Merge changes from topic "protobuf-3.9.1"

* changes:
  Convert statsd_test and statsd_benchmark to proto lite
  Adapt to google::protobuf::uint64 type change
diff --git a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
index 4b7a7f6..32107b4 100644
--- a/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
+++ b/apct-tests/perftests/multiuser/src/android/multiuser/UserLifecycleTests.java
@@ -119,9 +119,20 @@
     }
 
     @Test
+    public void createUser() {
+        while (mRunner.keepRunning()) {
+            final int userId = createUserNoFlags();
+
+            mRunner.pauseTiming();
+            removeUser(userId);
+            mRunner.resumeTiming();
+        }
+    }
+
+    @Test
     public void createAndStartUser() throws Exception {
         while (mRunner.keepRunning()) {
-            final int userId = createUser();
+            final int userId = createUserNoFlags();
 
             final CountDownLatch latch = new CountDownLatch(1);
             registerBroadcastReceiver(Intent.ACTION_USER_STARTED, latch, userId);
@@ -140,7 +151,7 @@
         while (mRunner.keepRunning()) {
             mRunner.pauseTiming();
             final int startUser = mAm.getCurrentUser();
-            final int userId = createUser();
+            final int userId = createUserNoFlags();
             mRunner.resumeTiming();
 
             switchUser(userId);
@@ -197,7 +208,7 @@
     public void stopUser() throws Exception {
         while (mRunner.keepRunning()) {
             mRunner.pauseTiming();
-            final int userId = createUser();
+            final int userId = createUserNoFlags();
             final CountDownLatch latch = new CountDownLatch(1);
             registerBroadcastReceiver(Intent.ACTION_USER_STARTED, latch, userId);
             mIam.startUserInBackground(userId);
@@ -217,7 +228,7 @@
         while (mRunner.keepRunning()) {
             mRunner.pauseTiming();
             final int startUser = mAm.getCurrentUser();
-            final int userId = createUser();
+            final int userId = createUserNoFlags();
             final CountDownLatch latch = new CountDownLatch(1);
             registerUserSwitchObserver(null, latch, userId);
             mRunner.resumeTiming();
@@ -237,7 +248,7 @@
         while (mRunner.keepRunning()) {
             mRunner.pauseTiming();
             final int startUser = mAm.getCurrentUser();
-            final int userId = createUser(UserInfo.FLAG_EPHEMERAL | UserInfo.FLAG_DEMO);
+            final int userId = createUserWithFlags(UserInfo.FLAG_EPHEMERAL | UserInfo.FLAG_DEMO);
             switchUser(userId);
             final CountDownLatch latch = new CountDownLatch(1);
             InstrumentationRegistry.getContext().registerReceiver(new BroadcastReceiver() {
@@ -396,7 +407,6 @@
     public void managedProfileCreateUnlockInstallAndLaunchApp() throws Exception {
         assumeTrue(mHasManagedUserFeature);
 
-        final String packageName = "perftests.multiuser.apps.dummyapp";
         while (mRunner.keepRunning()) {
             mRunner.pauseTiming();
             WindowManagerGlobal.getWindowManagerService().dismissKeyguard(null, null);
@@ -433,12 +443,12 @@
     }
 
     /** Creates a new user, returning its userId. */
-    private int createUser() {
-        return createUser(0);
+    private int createUserNoFlags() {
+        return createUserWithFlags(/* flags= */ 0);
     }
 
     /** Creates a new user with the given flags, returning its userId. */
-    private int createUser(int flags) {
+    private int createUserWithFlags(int flags) {
         int userId = mUm.createUser("TestUser", flags).id;
         mUsersToRemove.add(userId);
         return userId;
@@ -501,7 +511,7 @@
     private int initializeNewUserAndSwitchBack(boolean stopNewUser) throws Exception {
         final int origUser = mAm.getCurrentUser();
         // First, create and switch to testUser, waiting for its ACTION_USER_UNLOCKED
-        final int testUser = createUser();
+        final int testUser = createUserNoFlags();
         final CountDownLatch latch1 = new CountDownLatch(1);
         registerBroadcastReceiver(Intent.ACTION_USER_UNLOCKED, latch1, testUser);
         mAm.switchUser(testUser);
diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
index d0e38b4..65aaf20 100644
--- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
@@ -67,6 +67,7 @@
 import android.provider.Settings;
 import android.util.ArrayMap;
 import android.util.ArraySet;
+import android.util.AtomicFile;
 import android.util.KeyValueListParser;
 import android.util.MutableLong;
 import android.util.Pair;
@@ -79,7 +80,6 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.IBatteryStats;
-import com.android.internal.os.AtomicFile;
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.FastXmlSerializer;
diff --git a/api/current.txt b/api/current.txt
index 359ce3a..26c90df 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5320,10 +5320,12 @@
     ctor public Notification(android.os.Parcel);
     method public android.app.Notification clone();
     method public int describeContents();
+    method @Nullable public android.util.Pair<android.app.RemoteInput,android.app.Notification.Action> findRemoteInputActionPair(boolean);
     method public boolean getAllowSystemGeneratedContextualActions();
     method public int getBadgeIconType();
     method @Nullable public android.app.Notification.BubbleMetadata getBubbleMetadata();
     method public String getChannelId();
+    method @NonNull public java.util.List<android.app.Notification.Action> getContextualActions();
     method public String getGroup();
     method public int getGroupAlertBehavior();
     method public android.graphics.drawable.Icon getLargeIcon();
@@ -5719,6 +5721,7 @@
     method public String getDataMimeType();
     method public android.net.Uri getDataUri();
     method public android.os.Bundle getExtras();
+    method @NonNull public static java.util.List<android.app.Notification.MessagingStyle.Message> getMessagesFromBundleArray(@Nullable android.os.Parcelable[]);
     method @Deprecated public CharSequence getSender();
     method @Nullable public android.app.Person getSenderPerson();
     method public CharSequence getText();
@@ -5816,6 +5819,7 @@
     method public android.net.Uri getSound();
     method public long[] getVibrationPattern();
     method public boolean hasUserSetImportance();
+    method public boolean hasUserSetSound();
     method public void setAllowBubbles(boolean);
     method public void setBypassDnd(boolean);
     method public void setDescription(String);
@@ -41736,6 +41740,7 @@
     method public int getUid();
     method public android.os.UserHandle getUser();
     method @Deprecated public int getUserId();
+    method public boolean isAppGroup();
     method public boolean isClearable();
     method public boolean isGroup();
     method public boolean isOngoing();
diff --git a/api/system-current.txt b/api/system-current.txt
index 4ed9633..8ee6e63 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -521,8 +521,6 @@
   }
 
   public class Notification implements android.os.Parcelable {
-    method @Nullable public android.util.Pair<android.app.RemoteInput,android.app.Notification.Action> findRemoteInputActionPair(boolean);
-    method @NonNull public java.util.List<android.app.Notification.Action> getContextualActions();
     field public static final String CATEGORY_CAR_EMERGENCY = "car_emergency";
     field public static final String CATEGORY_CAR_INFORMATION = "car_information";
     field public static final String CATEGORY_CAR_WARNING = "car_warning";
@@ -531,10 +529,6 @@
     field public static final int FLAG_AUTOGROUP_SUMMARY = 1024; // 0x400
   }
 
-  public static final class Notification.MessagingStyle.Message {
-    method @Nullable public static android.app.Notification.MessagingStyle.Message getMessageFromBundle(@NonNull android.os.Bundle);
-  }
-
   public static final class Notification.TvExtender implements android.app.Notification.Extender {
     ctor public Notification.TvExtender();
     ctor public Notification.TvExtender(android.app.Notification);
@@ -3029,6 +3023,26 @@
     field public static final int STATUS_OK = 0; // 0x0
   }
 
+  public static final class SoundTrigger.ModuleProperties implements android.os.Parcelable {
+    method public int describeContents();
+    method public void writeToParcel(android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.hardware.soundtrigger.SoundTrigger.ModuleProperties> CREATOR;
+    field @NonNull public final String description;
+    field public final int id;
+    field @NonNull public final String implementor;
+    field public final int maxBufferMs;
+    field public final int maxKeyphrases;
+    field public final int maxSoundModels;
+    field public final int maxUsers;
+    field public final int powerConsumptionMw;
+    field public final int recognitionModes;
+    field public final boolean returnsTriggerInEvent;
+    field public final boolean supportsCaptureTransition;
+    field public final boolean supportsConcurrentCapture;
+    field @NonNull public final java.util.UUID uuid;
+    field public final int version;
+  }
+
   public static class SoundTrigger.RecognitionEvent {
     method @Nullable public android.media.AudioFormat getCaptureFormat();
     method public int getCaptureSession();
@@ -3771,6 +3785,7 @@
     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 @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) @Nullable public android.hardware.soundtrigger.SoundTrigger.ModuleProperties getModuleProperties();
     method @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER) public void updateModel(android.media.soundtrigger.SoundTriggerManager.Model);
   }
 
@@ -6689,10 +6704,6 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.service.notification.SnoozeCriterion> CREATOR;
   }
 
-  public class StatusBarNotification implements android.os.Parcelable {
-    method public boolean isAppGroup();
-  }
-
 }
 
 package android.service.oemlock {
@@ -8187,7 +8198,9 @@
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public long getPreferredNetworkTypeBitmask();
     method @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public int getRadioPowerState();
     method public int getSimApplicationState();
+    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getSimApplicationState(int);
     method public int getSimCardState();
+    method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getSimCardState(int);
     method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.Locale getSimLocale();
     method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public long getSupportedRadioAccessFamily();
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public java.util.List<android.telephony.TelephonyHistogram> getTelephonyHistograms();
diff --git a/api/test-current.txt b/api/test-current.txt
index 43b9086..6e28f67 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -97,8 +97,7 @@
     method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void removeStacksInWindowingModes(int[]) throws java.lang.SecurityException;
     method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void removeStacksWithActivityTypes(int[]) throws java.lang.SecurityException;
     method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void resizeDockedStack(android.graphics.Rect, android.graphics.Rect);
-    method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void resizeStack(int, android.graphics.Rect) throws java.lang.SecurityException;
-    method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void resizeStack(int, android.graphics.Rect, boolean);
+    method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void resizePinnedStack(int, android.graphics.Rect, boolean);
     method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void resizeTask(int, android.graphics.Rect);
     method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void setDisplayToSingleTaskInstance(int);
     method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS) public void setTaskWindowingMode(int, int, boolean) throws java.lang.SecurityException;
diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp
index 7244e74..c9a4b3b 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -65,7 +65,7 @@
         "src/condition/condition_util.cpp",
         "src/condition/SimpleConditionTracker.cpp",
         "src/condition/ConditionWizard.cpp",
-        "src/condition/StateTracker.cpp",
+        "src/condition/StateConditionTracker.cpp",
         "src/config/ConfigKey.cpp",
         "src/config/ConfigListener.cpp",
         "src/config/ConfigManager.cpp",
@@ -231,7 +231,7 @@
         "tests/FieldValue_test.cpp",
         "tests/condition/CombinationConditionTracker_test.cpp",
         "tests/condition/SimpleConditionTracker_test.cpp",
-        "tests/condition/StateTracker_test.cpp",
+        "tests/condition/StateConditionTracker_test.cpp",
         "tests/condition/ConditionTimer_test.cpp",
         "tests/metrics/OringDurationTracker_test.cpp",
         "tests/metrics/MaxDurationTracker_test.cpp",
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 2c8a556..ae751ce 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -334,6 +334,7 @@
         BackGesture back_gesture_reported_reported = 224;
         UpdateEngineUpdateAttemptReported update_engine_update_attempt_reported = 225;
         UpdateEngineSuccessfulUpdateReported update_engine_successful_update_reported = 226;
+        CameraActionEvent camera_action_event = 227;
     }
 
     // Pulled events will start at field 10000.
@@ -7164,3 +7165,29 @@
     // It's required that len(time_millis) == len(frame_count)
     repeated int64 frame_counts = 2;
 }
+
+
+/**
+ * Information about camera facing and API level usage.
+ * Logged from:
+ *   frameworks/base/services/core/java/com/android/server/camera/CameraServiceProxy.java
+ */
+message CameraActionEvent {
+    // Camera session duration
+    optional int64 duration = 1;
+
+    // Camera API level used
+    optional int32 api_level = 2;
+
+    // Name of client package
+    optional string package_name = 3;
+
+    // Camera facing
+    enum Facing {
+        UNKNOWN = 0;
+        BACK = 1;
+        FRONT = 2;
+        EXTERNAL = 3;
+    }
+    optional Facing facing = 4;
+}
diff --git a/cmds/statsd/src/condition/StateTracker.cpp b/cmds/statsd/src/condition/StateConditionTracker.cpp
similarity index 89%
rename from cmds/statsd/src/condition/StateTracker.cpp
rename to cmds/statsd/src/condition/StateConditionTracker.cpp
index 18c7178..7f3eedd 100644
--- a/cmds/statsd/src/condition/StateTracker.cpp
+++ b/cmds/statsd/src/condition/StateConditionTracker.cpp
@@ -16,7 +16,7 @@
 #define DEBUG false  // STOPSHIP if true
 #include "Log.h"
 
-#include "StateTracker.h"
+#include "StateConditionTracker.h"
 #include "guardrail/StatsdStats.h"
 
 namespace android {
@@ -27,7 +27,7 @@
 using std::unordered_set;
 using std::vector;
 
-StateTracker::StateTracker(const ConfigKey& key, const int64_t& id, const int index,
+StateConditionTracker::StateConditionTracker(const ConfigKey& key, const int64_t& id, const int index,
                            const SimplePredicate& simplePredicate,
                            const unordered_map<int64_t, int>& trackerNameIndexMap,
                            const vector<Matcher> primaryKeys)
@@ -69,19 +69,19 @@
     mInitialized = true;
 }
 
-StateTracker::~StateTracker() {
-    VLOG("~StateTracker()");
+StateConditionTracker::~StateConditionTracker() {
+    VLOG("~StateConditionTracker()");
 }
 
-bool StateTracker::init(const vector<Predicate>& allConditionConfig,
+bool StateConditionTracker::init(const vector<Predicate>& allConditionConfig,
                         const vector<sp<ConditionTracker>>& allConditionTrackers,
                         const unordered_map<int64_t, int>& conditionIdIndexMap,
                         vector<bool>& stack) {
     return mInitialized;
 }
 
-void StateTracker::dumpState() {
-    VLOG("StateTracker %lld DUMP:", (long long)mConditionId);
+void StateConditionTracker::dumpState() {
+    VLOG("StateConditionTracker %lld DUMP:", (long long)mConditionId);
     for (const auto& value : mSlicedState) {
         VLOG("\t%s -> %s", value.first.toString().c_str(), value.second.toString().c_str());
     }
@@ -95,7 +95,7 @@
     }
 }
 
-bool StateTracker::hitGuardRail(const HashableDimensionKey& newKey) {
+bool StateConditionTracker::hitGuardRail(const HashableDimensionKey& newKey) {
     if (mSlicedState.find(newKey) != mSlicedState.end()) {
         // if the condition is not sliced or the key is not new, we are good!
         return false;
@@ -114,7 +114,7 @@
     return false;
 }
 
-void StateTracker::evaluateCondition(const LogEvent& event,
+void StateConditionTracker::evaluateCondition(const LogEvent& event,
                                      const vector<MatchingState>& eventMatcherValues,
                                      const vector<sp<ConditionTracker>>& mAllConditions,
                                      vector<ConditionState>& conditionCache,
@@ -135,7 +135,7 @@
         return;
     }
 
-    VLOG("StateTracker evaluate event %s", event.ToString().c_str());
+    VLOG("StateConditionTracker evaluate event %s", event.ToString().c_str());
 
     // Primary key can exclusive fields must be simple fields. so there won't be more than
     // one keys matched.
@@ -151,7 +151,7 @@
     }
     hitGuardRail(primaryKey);
 
-    VLOG("StateTracker: key %s state %s", primaryKey.toString().c_str(), state.toString().c_str());
+    VLOG("StateConditionTracker: key %s state %s", primaryKey.toString().c_str(), state.toString().c_str());
 
     auto it = mSlicedState.find(primaryKey);
     if (it == mSlicedState.end()) {
@@ -176,7 +176,7 @@
     return;
 }
 
-void StateTracker::isConditionMet(
+void StateConditionTracker::isConditionMet(
         const ConditionKey& conditionParameters, const vector<sp<ConditionTracker>>& allConditions,
         const bool isPartialLink,
         vector<ConditionState>& conditionCache) const {
diff --git a/cmds/statsd/src/condition/StateTracker.h b/cmds/statsd/src/condition/StateConditionTracker.h
similarity index 89%
rename from cmds/statsd/src/condition/StateTracker.h
rename to cmds/statsd/src/condition/StateConditionTracker.h
index 5ae4441..0efe1fb 100644
--- a/cmds/statsd/src/condition/StateTracker.h
+++ b/cmds/statsd/src/condition/StateConditionTracker.h
@@ -25,14 +25,14 @@
 namespace os {
 namespace statsd {
 
-class StateTracker : public virtual ConditionTracker {
+class StateConditionTracker : public virtual ConditionTracker {
 public:
-    StateTracker(const ConfigKey& key, const int64_t& id, const int index,
+    StateConditionTracker(const ConfigKey& key, const int64_t& id, const int index,
                  const SimplePredicate& simplePredicate,
                  const std::unordered_map<int64_t, int>& trackerNameIndexMap,
                  const vector<Matcher> primaryKeys);
 
-    ~StateTracker();
+    ~StateConditionTracker();
 
     bool init(const std::vector<Predicate>& allConditionConfig,
               const std::vector<sp<ConditionTracker>>& allConditionTrackers,
@@ -46,8 +46,8 @@
                            std::vector<bool>& changedCache) override;
 
     /**
-     * Note: dimensionFields will be ignored in StateTracker, because we demand metrics
-     * must take the entire dimension fields from StateTracker. This is to make implementation
+     * Note: dimensionFields will be ignored in StateConditionTracker, because we demand metrics
+     * must take the entire dimension fields from StateConditionTracker. This is to make implementation
      * simple and efficient.
      *
      * For example: wakelock duration by uid process states:
@@ -109,7 +109,7 @@
     // maps from [primary_key] to [primary_key, exclusive_state].
     std::unordered_map<HashableDimensionKey, HashableDimensionKey> mSlicedState;
 
-    FRIEND_TEST(StateTrackerTest, TestStateChange);
+    FRIEND_TEST(StateConditionTrackerTest, TestStateChange);
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp
index 5cfb123..40484f4 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp
@@ -21,7 +21,7 @@
 
 #include "../condition/CombinationConditionTracker.h"
 #include "../condition/SimpleConditionTracker.h"
-#include "../condition/StateTracker.h"
+#include "../condition/StateConditionTracker.h"
 #include "../external/StatsPullerManager.h"
 #include "../matchers/CombinationLogMatchingTracker.h"
 #include "../matchers/SimpleLogMatchingTracker.h"
@@ -184,13 +184,13 @@
 }
 
 /**
- * A StateTracker is built from a SimplePredicate which has only "start", and no "stop"
+ * A StateConditionTracker is built from a SimplePredicate which has only "start", and no "stop"
  * or "stop_all". The start must be an atom matcher that matches a state atom. It must
  * have dimension, the dimension must be the state atom's primary fields plus exclusive state
- * field. For example, the StateTracker is used in tracking UidProcessState and ScreenState.
+ * field. For example, the StateConditionTracker is used in tracking UidProcessState and ScreenState.
  *
  */
-bool isStateTracker(const SimplePredicate& simplePredicate, vector<Matcher>* primaryKeys) {
+bool isStateConditionTracker(const SimplePredicate& simplePredicate, vector<Matcher>* primaryKeys) {
     // 1. must not have "stop". must have "dimension"
     if (!simplePredicate.has_stop() && simplePredicate.has_dimensions()) {
         auto it = android::util::AtomsInfo::kStateAtomsFieldOptions.find(
@@ -242,8 +242,8 @@
         switch (condition.contents_case()) {
             case Predicate::ContentsCase::kSimplePredicate: {
                 vector<Matcher> primaryKeys;
-                if (isStateTracker(condition.simple_predicate(), &primaryKeys)) {
-                    allConditionTrackers.push_back(new StateTracker(key, condition.id(), index,
+                if (isStateConditionTracker(condition.simple_predicate(), &primaryKeys)) {
+                    allConditionTrackers.push_back(new StateConditionTracker(key, condition.id(), index,
                                                                     condition.simple_predicate(),
                                                                     logTrackerMap, primaryKeys));
                 } else {
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.h b/cmds/statsd/src/metrics/metrics_manager_util.h
index 028231f..3704969 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.h
+++ b/cmds/statsd/src/metrics/metrics_manager_util.h
@@ -113,7 +113,7 @@
                       vector<int>& metricsWithActivation,
                       std::set<int64_t>& noReportMetricIds);
 
-bool isStateTracker(const SimplePredicate& simplePredicate, std::vector<Matcher>* primaryKeys);
+bool isStateConditionTracker(const SimplePredicate& simplePredicate, std::vector<Matcher>* primaryKeys);
 
 }  // namespace statsd
 }  // namespace os
diff --git a/cmds/statsd/tests/condition/StateTracker_test.cpp b/cmds/statsd/tests/condition/StateConditionTracker_test.cpp
similarity index 95%
rename from cmds/statsd/tests/condition/StateTracker_test.cpp
rename to cmds/statsd/tests/condition/StateConditionTracker_test.cpp
index 9a66254..fbf6efd 100644
--- a/cmds/statsd/tests/condition/StateTracker_test.cpp
+++ b/cmds/statsd/tests/condition/StateConditionTracker_test.cpp
@@ -12,7 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "src/condition/StateTracker.h"
+#include "src/condition/StateConditionTracker.h"
 #include "tests/statsd_test_util.h"
 
 #include <gmock/gmock.h>
@@ -50,7 +50,7 @@
     event->init();
 }
 
-TEST(StateTrackerTest, TestStateChange) {
+TEST(StateConditionTrackerTest, TestStateChange) {
     int uid1 = 111;
     int uid2 = 222;
 
@@ -60,7 +60,7 @@
     trackerNameIndexMap[StringToId("UidProcState")] = 0;
     vector<Matcher> primaryFields;
     primaryFields.push_back(getSimpleMatcher(kUidProcTag, 1));
-    StateTracker tracker(ConfigKey(12, 123), 123, 0, getUidProcStatePredicate(),
+    StateConditionTracker tracker(ConfigKey(12, 123), 123, 0, getUidProcStatePredicate(),
                          trackerNameIndexMap, primaryFields);
 
     LogEvent event(kUidProcTag, 0 /*timestamp*/);
diff --git a/config/hiddenapi-greylist.txt b/config/hiddenapi-greylist.txt
index 917ee6d..b4913c8 100644
--- a/config/hiddenapi-greylist.txt
+++ b/config/hiddenapi-greylist.txt
@@ -1150,8 +1150,6 @@
 Lcom/android/internal/statusbar/IStatusBarService$Stub;-><init>()V
 Lcom/android/internal/statusbar/IStatusBarService$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/statusbar/IStatusBarService;
 Lcom/android/internal/telecom/ITelecomService$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/telecom/ITelecomService;
-Lcom/android/internal/telephony/IIccPhoneBook$Stub$Proxy;->mRemote:Landroid/os/IBinder;
-Lcom/android/internal/telephony/IIccPhoneBook$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/telephony/IIccPhoneBook;
 Lcom/android/internal/telephony/IMms$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/telephony/IMms;
 Lcom/android/internal/telephony/IPhoneStateListener$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/telephony/IPhoneStateListener;
 Lcom/android/internal/telephony/IPhoneSubInfo$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
@@ -1159,7 +1157,6 @@
 Lcom/android/internal/telephony/IPhoneSubInfo$Stub;->TRANSACTION_getDeviceId:I
 Lcom/android/internal/telephony/ISms$Stub;-><init>()V
 Lcom/android/internal/telephony/ISms$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/telephony/ISms;
-Lcom/android/internal/telephony/ISub$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Lcom/android/internal/telephony/ISub$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/telephony/ISub;
 Lcom/android/internal/telephony/ITelephony$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
 Lcom/android/internal/telephony/ITelephony$Stub$Proxy;->mRemote:Landroid/os/IBinder;
@@ -1183,5 +1180,4 @@
 Lcom/android/server/ResettableTimeout$T;-><init>(Lcom/android/server/ResettableTimeout;)V
 Lcom/google/android/gles_jni/EGLImpl;-><init>()V
 Lcom/google/android/gles_jni/GLImpl;-><init>()V
-Lcom/google/android/mms/pdu/PduParser;->$assertionsDisabled:Z
 Lcom/google/android/util/AbstractMessageParser$Token$Type;->values()[Lcom/google/android/util/AbstractMessageParser$Token$Type;
diff --git a/core/java/android/app/ActivityTaskManager.java b/core/java/android/app/ActivityTaskManager.java
index 4ef554d..ac8c9f4 100644
--- a/core/java/android/app/ActivityTaskManager.java
+++ b/core/java/android/app/ActivityTaskManager.java
@@ -202,21 +202,6 @@
     }
 
     /**
-     * Resizes the input stack id to the given bounds.
-     * @param stackId Id of the stack to resize.
-     * @param bounds Bounds to resize the stack to or {@code null} for fullscreen.
-     */
-    @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
-    public void resizeStack(int stackId, Rect bounds) throws SecurityException {
-        try {
-            getService().resizeStack(stackId, bounds, false /* allowResizeInDockedMode */,
-                    false /* preserveWindows */, false /* animate */, -1 /* animationDuration */);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
      * Removes stacks in the windowing modes from the system if they are of activity type
      * ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED
      */
@@ -362,10 +347,13 @@
      * @param animate Whether we should play an animation for resizing stack.
      */
     @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
-    public void resizeStack(int stackId, Rect bounds, boolean animate) {
+    public void resizePinnedStack(int stackId, Rect bounds, boolean animate) {
         try {
-            getService().resizeStack(stackId, bounds, false, false, animate /* animate */,
-                    -1 /* animationDuration */);
+            if (animate) {
+                getService().animateResizePinnedStack(stackId, bounds, -1 /* animationDuration */);
+            } else {
+                getService().resizePinnedStack(bounds, null /* tempPinnedTaskBounds */);
+            }
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 356e0da..dca00a2 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -287,7 +287,8 @@
     void handleApplicationStrictModeViolation(in IBinder app, int penaltyMask,
             in StrictMode.ViolationInfo crashInfo);
     boolean isTopActivityImmersive();
-    void crashApplication(int uid, int initialPid, in String packageName, int userId, in String message);
+    void crashApplication(int uid, int initialPid, in String packageName, int userId,
+            in String message, boolean force);
     @UnsupportedAppUsage
     String getProviderMimeType(in Uri uri, int userId);
     // Cause the specified process to dump the specified heap.
@@ -405,23 +406,6 @@
     List<ActivityManager.StackInfo> getAllStackInfos();
     @UnsupportedAppUsage
     void moveTaskToStack(int taskId, int stackId, boolean toTop);
-    /**
-     * Resizes the input stack id to the given bounds.
-     *
-     * @param stackId Id of the stack to resize.
-     * @param bounds Bounds to resize the stack to or {@code null} for fullscreen.
-     * @param allowResizeInDockedMode True if the resize should be allowed when the docked stack is
-     *                                active.
-     * @param preserveWindows True if the windows of activities contained in the stack should be
-     *                        preserved.
-     * @param animate True if the stack resize should be animated.
-     * @param animationDuration The duration of the resize animation in milliseconds or -1 if the
-     *                          default animation duration should be used.
-     * @throws RemoteException
-     */
-    @UnsupportedAppUsage
-    void resizeStack(int stackId, in Rect bounds, boolean allowResizeInDockedMode,
-            boolean preserveWindows, boolean animate, int animationDuration);
     void setFocusedStack(int stackId);
     ActivityManager.StackInfo getFocusedStackInfo();
     @UnsupportedAppUsage
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index 47b784b..dda3bb5 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -234,21 +234,15 @@
     void setTaskWindowingMode(int taskId, int windowingMode, boolean toTop);
     void moveTaskToStack(int taskId, int stackId, boolean toTop);
     /**
-     * Resizes the input stack id to the given bounds.
+     * Resizes the input pinned stack to the given bounds with animation.
      *
-     * @param stackId Id of the stack to resize.
+     * @param stackId Id of the pinned stack to resize.
      * @param bounds Bounds to resize the stack to or {@code null} for fullscreen.
-     * @param allowResizeInDockedMode True if the resize should be allowed when the docked stack is
-     *                                active.
-     * @param preserveWindows True if the windows of activities contained in the stack should be
-     *                        preserved.
-     * @param animate True if the stack resize should be animated.
      * @param animationDuration The duration of the resize animation in milliseconds or -1 if the
      *                          default animation duration should be used.
      * @throws RemoteException
      */
-    void resizeStack(int stackId, in Rect bounds, boolean allowResizeInDockedMode,
-            boolean preserveWindows, boolean animate, int animationDuration);
+    void animateResizePinnedStack(int stackId, in Rect bounds, int animationDuration);
     boolean setTaskWindowingModeSplitScreenPrimary(int taskId, int createMode, boolean toTop,
             boolean animate, in Rect initialBounds, boolean showRecents);
     /**
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 8987b23..f0b3546 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -883,48 +883,6 @@
             }
         }
 
-        // /apex/com.android.art/lib, /vendor/lib, /odm/lib and /product/lib
-        // are added to the native lib search paths of the classloader.
-        // Note that this is done AFTER the classloader is
-        // created by ApplicationLoaders.getDefault().getClassLoader(...). The
-        // reason is because if we have added the paths when creating the classloader
-        // above, the paths are also added to the search path of the linker namespace
-        // 'classloader-namespace', which will allow ALL libs in the paths to apps.
-        // Since only the libs listed in <partition>/etc/public.libraries.txt can be
-        // available to apps, we shouldn't add the paths then.
-        //
-        // However, we need to add the paths to the classloader (Java) though. This
-        // is because when a native lib is requested via System.loadLibrary(), the
-        // classloader first tries to find the requested lib in its own native libs
-        // search paths. If a lib is not found in one of the paths, dlopen() is not
-        // called at all. This can cause a problem that a vendor public native lib
-        // is accessible when directly opened via dlopen(), but inaccesible via
-        // System.loadLibrary(). In order to prevent the problem, we explicitly
-        // add the paths only to the classloader, and not to the native loader
-        // (linker namespace).
-        List<String> extraLibPaths = new ArrayList<>(4);
-        String abiSuffix = VMRuntime.getRuntime().is64Bit() ? "64" : "";
-        if (!defaultSearchPaths.contains("/apex/com.android.art/lib")) {
-            extraLibPaths.add("/apex/com.android.art/lib" + abiSuffix);
-        }
-        if (!defaultSearchPaths.contains("/vendor/lib")) {
-            extraLibPaths.add("/vendor/lib" + abiSuffix);
-        }
-        if (!defaultSearchPaths.contains("/odm/lib")) {
-            extraLibPaths.add("/odm/lib" + abiSuffix);
-        }
-        if (!defaultSearchPaths.contains("/product/lib")) {
-            extraLibPaths.add("/product/lib" + abiSuffix);
-        }
-        if (!extraLibPaths.isEmpty()) {
-            StrictMode.ThreadPolicy oldPolicy = allowThreadDiskReads();
-            try {
-                ApplicationLoaders.getDefault().addNative(mDefaultClassLoader, extraLibPaths);
-            } finally {
-                setThreadPolicy(oldPolicy);
-            }
-        }
-
         if (addedPaths != null && addedPaths.size() > 0) {
             final String add = TextUtils.join(File.pathSeparator, addedPaths);
             ApplicationLoaders.getDefault().addPath(mDefaultClassLoader, add);
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index f065ff7..bb4e998 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -3274,11 +3274,8 @@
      *
      * @param requiresFreeform requires the remoteinput to allow freeform or not.
      * @return the result pair, {@code null} if no result is found.
-     *
-     * @hide
      */
     @Nullable
-    @SystemApi
     public Pair<RemoteInput, Action> findRemoteInputActionPair(boolean requiresFreeform) {
         if (actions == null) {
             return null;
@@ -3301,11 +3298,9 @@
     }
 
     /**
-     * Returns the actions that are contextual out of the actions in this notification.
-     *
-     * @hide
+     * Returns the actions that are contextual (that is, suggested because of the content of the
+     * notification) out of the actions in this notification.
      */
-    @SystemApi
     public @NonNull List<Notification.Action> getContextualActions() {
         if (actions == null) return Collections.emptyList();
 
@@ -7705,11 +7700,11 @@
             }
 
             /**
-             * @return A list of messages read from the bundles.
-             *
-             * @hide
+             * Returns a list of messages read from the given bundle list, e.g.
+             * {@link #EXTRA_MESSAGES} or {@link #EXTRA_HISTORIC_MESSAGES}.
              */
-            public static List<Message> getMessagesFromBundleArray(Parcelable[] bundles) {
+            @NonNull
+            public static List<Message> getMessagesFromBundleArray(@Nullable Parcelable[] bundles) {
                 if (bundles == null) {
                     return new ArrayList<>();
                 }
@@ -7726,13 +7721,12 @@
             }
 
             /**
-             * @return The message that is stored in the bundle or null if the message couldn't be
-             * resolved.
-             *
+             * Returns the message that is stored in the bundle (e.g. one of the values in the lists
+             * in {@link #EXTRA_MESSAGES} or {@link #EXTRA_HISTORIC_MESSAGES}) or null if the
+             * message couldn't be resolved.
              * @hide
              */
             @Nullable
-            @SystemApi
             public static Message getMessageFromBundle(@NonNull Bundle bundle) {
                 try {
                     if (!bundle.containsKey(KEY_TEXT) || !bundle.containsKey(KEY_TIMESTAMP)) {
diff --git a/core/java/android/app/NotificationChannel.java b/core/java/android/app/NotificationChannel.java
index 93e4ddc..20d977b 100644
--- a/core/java/android/app/NotificationChannel.java
+++ b/core/java/android/app/NotificationChannel.java
@@ -710,6 +710,14 @@
     }
 
     /**
+     * Returns whether the user has chosen the sound of this channel.
+     * @see #getSound()
+     */
+    public boolean hasUserSetSound() {
+        return (mUserLockedFields & USER_LOCKED_SOUND) != 0;
+    }
+
+    /**
      * @hide
      */
     public void populateFromXmlForRestore(XmlPullParser parser, Context context) {
diff --git a/core/java/android/content/pm/AndroidTelephonyCommonUpdater.java b/core/java/android/content/pm/AndroidTelephonyCommonUpdater.java
new file mode 100644
index 0000000..1a720d5
--- /dev/null
+++ b/core/java/android/content/pm/AndroidTelephonyCommonUpdater.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.content.pm;
+
+import static android.content.pm.SharedLibraryNames.ANDROID_TELEPHONY_COMMON;
+
+
+import com.android.internal.compat.IPlatformCompat;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
+import android.content.pm.PackageParser.Package;
+
+import android.os.Build.VERSION_CODES;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Log;
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * Updates a package to ensure that
+ * <ul>
+ * <li> if apps have target SDK < R, then telephony-common library is included by default to
+ * their class path. Even without <uses-library>.</li>
+ * <li> if apps with target SDK level >= R && have special permission (or Phone UID):
+ * apply <uses-library> on telephony-common should work.</li>
+ * <li> Otherwise not allow to use the lib.
+ * See {@link PackageSharedLibraryUpdater#removeLibrary(Package, String)}.</li>
+ * </ul>
+ *
+ * @hide
+ */
+@VisibleForTesting
+public class AndroidTelephonyCommonUpdater extends PackageSharedLibraryUpdater {
+
+    private static final String TAG = AndroidTelephonyCommonUpdater.class.getSimpleName();
+    /**
+     * Restrict telephony-common lib for apps having target SDK >= R
+     */
+    @ChangeId
+    @EnabledAfter(targetSdkVersion = VERSION_CODES.Q)
+    static final long RESTRICT_TELEPHONY_COMMON_CHANGE_ID = 139318877L;
+
+    private static boolean apkTargetsApiLevelLessThanROrCurrent(Package pkg) {
+        boolean shouldRestrict = false;
+        try {
+            IBinder b = ServiceManager.getService("platform_compat");
+            IPlatformCompat platformCompat = IPlatformCompat.Stub.asInterface(b);
+            shouldRestrict = platformCompat.isChangeEnabled(RESTRICT_TELEPHONY_COMMON_CHANGE_ID,
+                pkg.applicationInfo);
+        } catch (RemoteException ex) {
+            Log.e(TAG, ex.getMessage());
+        }
+        // TODO(b/139318877): remove version check for CUR_DEVELOPEMENT after clean up work.
+        return !shouldRestrict
+            || pkg.applicationInfo.targetSdkVersion == VERSION_CODES.CUR_DEVELOPMENT;
+    }
+
+    @Override
+    public void updatePackage(Package pkg) {
+        // for apps with targetSDKVersion < R include the library for backward compatibility.
+        if (apkTargetsApiLevelLessThanROrCurrent(pkg)) {
+            prefixRequiredLibrary(pkg, ANDROID_TELEPHONY_COMMON);
+        } else if (pkg.mSharedUserId == null || !pkg.mSharedUserId.equals("android.uid.phone")) {
+            // if apps target >= R
+            removeLibrary(pkg, ANDROID_TELEPHONY_COMMON);
+        }
+    }
+}
diff --git a/core/java/android/content/pm/PackageBackwardCompatibility.java b/core/java/android/content/pm/PackageBackwardCompatibility.java
index 4331bd4..797ba64b 100644
--- a/core/java/android/content/pm/PackageBackwardCompatibility.java
+++ b/core/java/android/content/pm/PackageBackwardCompatibility.java
@@ -51,6 +51,8 @@
 
         packageUpdaters.add(new AndroidHidlUpdater());
 
+        packageUpdaters.add(new AndroidTelephonyCommonUpdater());
+
         // Add this before adding AndroidTestBaseUpdater so that android.test.base comes before
         // android.test.mock.
         packageUpdaters.add(new AndroidTestRunnerSplitUpdater());
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index ef08bf5..4c970da 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -57,6 +57,7 @@
 import android.content.IntentFilter;
 import android.content.pm.PackageParserCacheHelper.ReadHelper;
 import android.content.pm.PackageParserCacheHelper.WriteHelper;
+import android.content.pm.permission.SplitPermissionInfoParcelable;
 import android.content.pm.split.DefaultSplitAssetLoader;
 import android.content.pm.split.SplitAssetDependencyLoader;
 import android.content.pm.split.SplitAssetLoader;
@@ -79,7 +80,6 @@
 import android.os.Trace;
 import android.os.UserHandle;
 import android.os.storage.StorageManager;
-import android.permission.PermissionManager;
 import android.system.ErrnoException;
 import android.system.OsConstants;
 import android.system.StructStat;
@@ -2528,11 +2528,17 @@
             Slog.i(TAG, newPermsMsg.toString());
         }
 
+        List<SplitPermissionInfoParcelable> splitPermissions;
 
-        final int NS = PermissionManager.SPLIT_PERMISSIONS.size();
-        for (int is=0; is<NS; is++) {
-            final PermissionManager.SplitPermissionInfo spi =
-                    PermissionManager.SPLIT_PERMISSIONS.get(is);
+        try {
+            splitPermissions = ActivityThread.getPermissionManager().getSplitPermissions();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+
+        final int listSize = splitPermissions.size();
+        for (int is = 0; is < listSize; is++) {
+            final SplitPermissionInfoParcelable spi = splitPermissions.get(is);
             if (pkg.applicationInfo.targetSdkVersion >= spi.getTargetSdk()
                     || !pkg.requestedPermissions.contains(spi.getSplitPermission())) {
                 continue;
@@ -8501,5 +8507,4 @@
             this.error = error;
         }
     }
-
 }
diff --git a/core/java/android/content/pm/SharedLibraryNames.java b/core/java/android/content/pm/SharedLibraryNames.java
index a607a9f..4c66fc0 100644
--- a/core/java/android/content/pm/SharedLibraryNames.java
+++ b/core/java/android/content/pm/SharedLibraryNames.java
@@ -33,4 +33,6 @@
     static final String ANDROID_TEST_RUNNER = "android.test.runner";
 
     public static final String ORG_APACHE_HTTP_LEGACY = "org.apache.http.legacy";
+
+    public static final String ANDROID_TELEPHONY_COMMON = "telephony-common";
 }
diff --git a/services/core/java/com/android/server/wm/SurfaceFactory.java b/core/java/android/content/pm/permission/SplitPermissionInfoParcelable.aidl
similarity index 62%
copy from services/core/java/com/android/server/wm/SurfaceFactory.java
copy to core/java/android/content/pm/permission/SplitPermissionInfoParcelable.aidl
index 076b7df..d84454c 100644
--- a/services/core/java/com/android/server/wm/SurfaceFactory.java
+++ b/core/java/android/content/pm/permission/SplitPermissionInfoParcelable.aidl
@@ -1,11 +1,11 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
+/**
+ * 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
+ *     http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
@@ -14,14 +14,6 @@
  * limitations under the License.
  */
 
-package com.android.server.wm;
+ package android.content.pm.permission;
 
-import android.view.Surface;
-
-/**
- * Helper class to inject custom {@link Surface} objects into window manager.
- */
-interface SurfaceFactory {
-    Surface make();
-};
-
+ parcelable SplitPermissionInfoParcelable;
\ No newline at end of file
diff --git a/core/java/android/content/pm/permission/SplitPermissionInfoParcelable.java b/core/java/android/content/pm/permission/SplitPermissionInfoParcelable.java
new file mode 100644
index 0000000..421ae49
--- /dev/null
+++ b/core/java/android/content/pm/permission/SplitPermissionInfoParcelable.java
@@ -0,0 +1,202 @@
+/*
+ * 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.content.pm.permission;
+
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.DataClass;
+import com.android.internal.util.Preconditions;
+
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Parcelable version of {@link android.permission.PermissionManager.SplitPermissionInfo}
+ * @hide
+ */
+@DataClass(genEqualsHashCode = true)
+public class SplitPermissionInfoParcelable implements Parcelable {
+
+    /**
+     * The permission that is split.
+     */
+    @NonNull
+    private final String mSplitPermission;
+
+    /**
+     * The permissions that are added.
+     */
+    @NonNull
+    private final List<String> mNewPermissions;
+
+    /**
+     * The target API level when the permission was split.
+     */
+    @IntRange(from = 0)
+    private final int mTargetSdk;
+
+    private void onConstructed() {
+        Preconditions.checkCollectionElementsNotNull(mNewPermissions, "newPermissions");
+    }
+
+
+
+    // Code below generated by codegen v1.0.0.
+    //
+    // DO NOT MODIFY!
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/permission/SplitPermissionInfoParcelable.java
+    //
+    // CHECKSTYLE:OFF Generated code
+
+    /**
+     * Creates a new SplitPermissionInfoParcelable.
+     *
+     * @param splitPermission
+     *   The permission that is split.
+     * @param newPermissions
+     *   The permissions that are added.
+     * @param targetSdk
+     *   The target API level when the permission was split.
+     */
+    @DataClass.Generated.Member
+    public SplitPermissionInfoParcelable(
+            @NonNull String splitPermission,
+            @NonNull List<String> newPermissions,
+            @IntRange(from = 0) int targetSdk) {
+        this.mSplitPermission = splitPermission;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mSplitPermission);
+        this.mNewPermissions = newPermissions;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mNewPermissions);
+        this.mTargetSdk = targetSdk;
+        com.android.internal.util.AnnotationValidations.validate(
+                IntRange.class, null, mTargetSdk,
+                "from", 0);
+
+        onConstructed();
+    }
+
+    /**
+     * The permission that is split.
+     */
+    @DataClass.Generated.Member
+    public @NonNull String getSplitPermission() {
+        return mSplitPermission;
+    }
+
+    /**
+     * The permissions that are added.
+     */
+    @DataClass.Generated.Member
+    public @NonNull List<String> getNewPermissions() {
+        return mNewPermissions;
+    }
+
+    /**
+     * The target API level when the permission was split.
+     */
+    @DataClass.Generated.Member
+    public @IntRange(from = 0) int getTargetSdk() {
+        return mTargetSdk;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public boolean equals(Object o) {
+        // You can override field equality logic by defining either of the methods like:
+        // boolean fieldNameEquals(SplitPermissionInfoParcelable other) { ... }
+        // boolean fieldNameEquals(FieldType otherValue) { ... }
+
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        @SuppressWarnings("unchecked")
+        SplitPermissionInfoParcelable that = (SplitPermissionInfoParcelable) o;
+        //noinspection PointlessBooleanExpression
+        return true
+                && Objects.equals(mSplitPermission, that.mSplitPermission)
+                && Objects.equals(mNewPermissions, that.mNewPermissions)
+                && mTargetSdk == that.mTargetSdk;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int hashCode() {
+        // You can override field hashCode logic by defining methods like:
+        // int fieldNameHashCode() { ... }
+
+        int _hash = 1;
+        _hash = 31 * _hash + Objects.hashCode(mSplitPermission);
+        _hash = 31 * _hash + Objects.hashCode(mNewPermissions);
+        _hash = 31 * _hash + mTargetSdk;
+        return _hash;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public void writeToParcel(Parcel dest, int flags) {
+        // You can override field parcelling by defining methods like:
+        // void parcelFieldName(Parcel dest, int flags) { ... }
+
+        dest.writeString(mSplitPermission);
+        dest.writeStringList(mNewPermissions);
+        dest.writeInt(mTargetSdk);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int describeContents() { return 0; }
+
+    @DataClass.Generated.Member
+    public static final @NonNull Parcelable.Creator<SplitPermissionInfoParcelable> CREATOR
+            = new Parcelable.Creator<SplitPermissionInfoParcelable>() {
+        @Override
+        public SplitPermissionInfoParcelable[] newArray(int size) {
+            return new SplitPermissionInfoParcelable[size];
+        }
+
+        @Override
+        @SuppressWarnings({"unchecked", "RedundantCast"})
+        public SplitPermissionInfoParcelable createFromParcel(Parcel in) {
+            // You can override field unparcelling by defining methods like:
+            // static FieldType unparcelFieldName(Parcel in) { ... }
+
+            String splitPermission = in.readString();
+            List<String> newPermissions = new java.util.ArrayList<>();
+            in.readStringList(newPermissions);
+            int targetSdk = in.readInt();
+            return new SplitPermissionInfoParcelable(
+                    splitPermission,
+                    newPermissions,
+                    targetSdk);
+        }
+    };
+
+    @DataClass.Generated(
+            time = 1567634390477L,
+            codegenVersion = "1.0.0",
+            sourceFile = "frameworks/base/core/java/android/content/pm/permission/SplitPermissionInfoParcelable.java",
+            inputSignatures = "private final @android.annotation.NonNull java.lang.String mSplitPermission\nprivate final @android.annotation.NonNull java.util.List<java.lang.String> mNewPermissions\nprivate final @android.annotation.IntRange(from=0L) int mTargetSdk\nprivate  void onConstructed()\nclass SplitPermissionInfoParcelable extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true)")
+    @Deprecated
+    private void __metadata() {}
+
+}
diff --git a/core/java/android/hardware/soundtrigger/SoundTrigger.java b/core/java/android/hardware/soundtrigger/SoundTrigger.java
index f96f47d..b1134e1 100644
--- a/core/java/android/hardware/soundtrigger/SoundTrigger.java
+++ b/core/java/android/hardware/soundtrigger/SoundTrigger.java
@@ -71,28 +71,27 @@
      * ID used to target any API call to this paricular module. Module
      * properties are returned by listModules() method.
      *
-     * @hide
      ****************************************************************************/
-    public static class ModuleProperties implements Parcelable {
+    public static final class ModuleProperties implements Parcelable {
         /** Unique module ID provided by the native service */
-        @UnsupportedAppUsage
         public final int id;
 
         /** human readable voice detection engine implementor */
+        @NonNull
         public final String implementor;
 
         /** human readable voice detection engine description */
+        @NonNull
         public final String description;
 
         /** Unique voice engine Id (changes with each version) */
-        @UnsupportedAppUsage
+        @NonNull
         public final UUID uuid;
 
         /** Voice detection engine version */
         public final int version;
 
         /** Maximum number of active sound models */
-        @UnsupportedAppUsage
         public final int maxSoundModels;
 
         /** Maximum number of key phrases */
@@ -120,7 +119,6 @@
          * recognition callback event */
         public final boolean returnsTriggerInEvent;
 
-        @UnsupportedAppUsage
         ModuleProperties(int id, String implementor, String description,
                 String uuid, int version, int maxSoundModels, int maxKeyphrases,
                 int maxUsers, int recognitionModes, boolean supportsCaptureTransition,
diff --git a/core/java/android/permission/IPermissionManager.aidl b/core/java/android/permission/IPermissionManager.aidl
index 9fa5f16..60c8811 100644
--- a/core/java/android/permission/IPermissionManager.aidl
+++ b/core/java/android/permission/IPermissionManager.aidl
@@ -19,6 +19,7 @@
 import android.content.pm.ParceledListSlice;
 import android.content.pm.PermissionGroupInfo;
 import android.content.pm.PermissionInfo;
+import android.content.pm.permission.SplitPermissionInfoParcelable;
 import android.permission.IOnPermissionsChangeListener;
 
 /**
@@ -97,4 +98,6 @@
             String packageName, int userId);
 
     boolean isPermissionRevokedByPolicy(String permName, String packageName, int userId);
+
+    List<SplitPermissionInfoParcelable> getSplitPermissions();
 }
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index 5e35958..6c4ee01 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -24,16 +24,18 @@
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.annotation.TestApi;
+import android.app.ActivityThread;
 import android.content.Context;
 import android.content.pm.IPackageManager;
+import android.content.pm.permission.SplitPermissionInfoParcelable;
 import android.os.RemoteException;
+import android.util.Slog;
 
 import com.android.internal.annotations.Immutable;
-import com.android.server.SystemConfig;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
-import java.util.Objects;
 
 /**
  * System level service for accessing the permission capabilities of the platform.
@@ -44,6 +46,8 @@
 @SystemApi
 @SystemService(Context.PERMISSION_SERVICE)
 public final class PermissionManager {
+    private static final String TAG = PermissionManager.class.getName();
+
     /** @hide */
     public static final String KILL_APP_REASON_PERMISSIONS_REVOKED =
             "permissions revoked";
@@ -51,19 +55,12 @@
     public static final String KILL_APP_REASON_GIDS_CHANGED =
             "permission grant or revoke changed gids";
 
-
-    /**
-     * {@link android.content.pm.PackageParser} needs access without having a {@link Context}.
-     *
-     * @hide
-     */
-    public static final ArrayList<SplitPermissionInfo> SPLIT_PERMISSIONS =
-            SystemConfig.getInstance().getSplitPermissions();
-
     private final @NonNull Context mContext;
 
     private final IPackageManager mPackageManager;
 
+    private List<SplitPermissionInfo> mSplitPermissionInfos;
+
     /**
      * Creates a new instance.
      *
@@ -131,7 +128,48 @@
      * @return All permissions that are split.
      */
     public @NonNull List<SplitPermissionInfo> getSplitPermissions() {
-        return SPLIT_PERMISSIONS;
+        if (mSplitPermissionInfos != null) {
+            return mSplitPermissionInfos;
+        }
+
+        List<SplitPermissionInfoParcelable> parcelableList;
+        try {
+            parcelableList = ActivityThread.getPermissionManager().getSplitPermissions();
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Error getting split permissions", e);
+            return Collections.emptyList();
+        }
+
+        mSplitPermissionInfos = splitPermissionInfoListToNonParcelableList(parcelableList);
+
+        return mSplitPermissionInfos;
+    }
+
+    private List<SplitPermissionInfo> splitPermissionInfoListToNonParcelableList(
+            List<SplitPermissionInfoParcelable> parcelableList) {
+        final int size = parcelableList.size();
+        List<SplitPermissionInfo> list = new ArrayList<>(size);
+        for (int i = 0; i < size; i++) {
+            list.add(new SplitPermissionInfo(parcelableList.get(i)));
+        }
+        return list;
+    }
+
+    /**
+     * Converts a {@link List} of {@link SplitPermissionInfo} into a List of
+     * {@link SplitPermissionInfoParcelable} and returns it.
+     * @hide
+     */
+    public static List<SplitPermissionInfoParcelable> splitPermissionInfoListToParcelableList(
+            List<SplitPermissionInfo> splitPermissionsList) {
+        final int size = splitPermissionsList.size();
+        List<SplitPermissionInfoParcelable> outList = new ArrayList<>(size);
+        for (int i = 0; i < size; i++) {
+            SplitPermissionInfo info = splitPermissionsList.get(i);
+            outList.add(new SplitPermissionInfoParcelable(
+                    info.getSplitPermission(), info.getNewPermissions(), info.getTargetSdk()));
+        }
+        return outList;
     }
 
     /**
@@ -140,44 +178,40 @@
      */
     @Immutable
     public static final class SplitPermissionInfo {
-        private final @NonNull String mSplitPerm;
-        private final @NonNull List<String> mNewPerms;
-        private final int mTargetSdk;
+        private @NonNull final SplitPermissionInfoParcelable mSplitPermissionInfoParcelable;
 
         @Override
         public boolean equals(@Nullable Object o) {
             if (this == o) return true;
             if (o == null || getClass() != o.getClass()) return false;
             SplitPermissionInfo that = (SplitPermissionInfo) o;
-            return mTargetSdk == that.mTargetSdk
-                    && mSplitPerm.equals(that.mSplitPerm)
-                    && mNewPerms.equals(that.mNewPerms);
+            return mSplitPermissionInfoParcelable.equals(that.mSplitPermissionInfoParcelable);
         }
 
         @Override
         public int hashCode() {
-            return Objects.hash(mSplitPerm, mNewPerms, mTargetSdk);
+            return mSplitPermissionInfoParcelable.hashCode();
         }
 
         /**
          * Get the permission that is split.
          */
         public @NonNull String getSplitPermission() {
-            return mSplitPerm;
+            return mSplitPermissionInfoParcelable.getSplitPermission();
         }
 
         /**
          * Get the permissions that are added.
          */
         public @NonNull List<String> getNewPermissions() {
-            return mNewPerms;
+            return mSplitPermissionInfoParcelable.getNewPermissions();
         }
 
         /**
          * Get the target API level when the permission was split.
          */
         public int getTargetSdk() {
-            return mTargetSdk;
+            return mSplitPermissionInfoParcelable.getTargetSdk();
         }
 
         /**
@@ -191,9 +225,11 @@
          */
         public SplitPermissionInfo(@NonNull String splitPerm, @NonNull List<String> newPerms,
                 int targetSdk) {
-            mSplitPerm = splitPerm;
-            mNewPerms = newPerms;
-            mTargetSdk = targetSdk;
+            this(new SplitPermissionInfoParcelable(splitPerm, newPerms, targetSdk));
+        }
+
+        private SplitPermissionInfo(@NonNull SplitPermissionInfoParcelable parcelable) {
+            mSplitPermissionInfoParcelable = parcelable;
         }
     }
 }
diff --git a/core/java/android/service/euicc/EuiccService.java b/core/java/android/service/euicc/EuiccService.java
index d2f22bf..ff8b135 100644
--- a/core/java/android/service/euicc/EuiccService.java
+++ b/core/java/android/service/euicc/EuiccService.java
@@ -29,7 +29,6 @@
 import android.telephony.euicc.DownloadableSubscription;
 import android.telephony.euicc.EuiccInfo;
 import android.telephony.euicc.EuiccManager.OtaStatus;
-import android.util.ArraySet;
 import android.util.Log;
 
 import java.lang.annotation.Retention;
@@ -252,18 +251,6 @@
     public static final int RESULT_FIRST_USER = 1;
 
     /**
-     * List of all valid resolution actions for validation purposes.
-     * @hide
-     */
-    public static final ArraySet<String> RESOLUTION_ACTIONS;
-    static {
-        RESOLUTION_ACTIONS = new ArraySet<>();
-        RESOLUTION_ACTIONS.add(EuiccService.ACTION_RESOLVE_DEACTIVATE_SIM);
-        RESOLUTION_ACTIONS.add(EuiccService.ACTION_RESOLVE_NO_PRIVILEGES);
-        RESOLUTION_ACTIONS.add(EuiccService.ACTION_RESOLVE_RESOLVABLE_ERRORS);
-    }
-
-    /**
      * Boolean extra for resolution actions indicating whether the user granted consent.
      * This is used and set by the implementation and used in {@code EuiccOperation}.
      */
diff --git a/core/java/android/service/notification/StatusBarNotification.java b/core/java/android/service/notification/StatusBarNotification.java
index 205df7e..b8378a3 100644
--- a/core/java/android/service/notification/StatusBarNotification.java
+++ b/core/java/android/service/notification/StatusBarNotification.java
@@ -170,10 +170,7 @@
 
     /**
      * Returns true if application asked that this notification be part of a group.
-     *
-     * @hide
      */
-    @SystemApi
     public boolean isAppGroup() {
         if (getNotification().getGroup() != null || getNotification().getSortKey() != null) {
             return true;
diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java
index bfab580..937990f7 100644
--- a/core/java/android/service/notification/ZenModeConfig.java
+++ b/core/java/android/service/notification/ZenModeConfig.java
@@ -1639,7 +1639,7 @@
         @UnsupportedAppUsage
         public String name;              // required for automatic
         @UnsupportedAppUsage
-        public int zenMode;
+        public int zenMode;             // ie: Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS
         @UnsupportedAppUsage
         public Uri conditionId;          // required for automatic
         public Condition condition;      // optional
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index 2efebf6..7e22dd9 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -18,6 +18,7 @@
 
 import static android.view.DisplayInfoProto.APP_HEIGHT;
 import static android.view.DisplayInfoProto.APP_WIDTH;
+import static android.view.DisplayInfoProto.FLAGS;
 import static android.view.DisplayInfoProto.LOGICAL_HEIGHT;
 import static android.view.DisplayInfoProto.LOGICAL_WIDTH;
 import static android.view.DisplayInfoProto.NAME;
@@ -708,6 +709,7 @@
         protoOutputStream.write(APP_WIDTH, appWidth);
         protoOutputStream.write(APP_HEIGHT, appHeight);
         protoOutputStream.write(NAME, name);
+        protoOutputStream.write(FLAGS, flags);
         protoOutputStream.end(token);
     }
 
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 8c8b9d3..7ee3973 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -5125,6 +5125,11 @@
     @Nullable
     private ContentCaptureSession mContentCaptureSession;
 
+    /**
+     * Whether {@link ContentCaptureSession} is cached, resets on {@link #invalidate()}.
+     */
+    private boolean mContentCaptureSessionCached;
+
     @LayoutRes
     private int mSourceLayoutId = ID_NULL;
 
@@ -5138,11 +5143,6 @@
     private int mExplicitStyle;
 
     /**
-     * Cached reference to the {@link ContentCaptureSession}, is reset on {@link #invalidate()}.
-     */
-    private ContentCaptureSession mCachedContentCaptureSession;
-
-    /**
      * Simple constructor to use when creating a view from code.
      *
      * @param context The Context the view is running in, through which it can
@@ -9560,18 +9560,21 @@
      */
     @Nullable
     public final ContentCaptureSession getContentCaptureSession() {
-        if (mCachedContentCaptureSession != null) {
-            return mCachedContentCaptureSession;
+        if (mContentCaptureSessionCached) {
+            return mContentCaptureSession;
         }
 
-        mCachedContentCaptureSession = getAndCacheContentCaptureSession();
-        return mCachedContentCaptureSession;
+        mContentCaptureSession = getAndCacheContentCaptureSession();
+        mContentCaptureSessionCached = true;
+        return mContentCaptureSession;
     }
 
     @Nullable
     private ContentCaptureSession getAndCacheContentCaptureSession() {
         // First try the session explicitly set by setContentCaptureSession()
-        if (mContentCaptureSession != null) return mContentCaptureSession;
+        if (mContentCaptureSession != null) {
+            return mContentCaptureSession;
+        }
 
         // Then the session explicitly set in an ancestor
         ContentCaptureSession session = null;
@@ -18094,7 +18097,7 @@
 
         // Reset content capture caches
         mPrivateFlags4 &= ~PFLAG4_CONTENT_CAPTURE_IMPORTANCE_MASK;
-        mCachedContentCaptureSession = null;
+        mContentCaptureSessionCached = false;
 
         if ((mPrivateFlags & (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)) == (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)
                 || (invalidateCache && (mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == PFLAG_DRAWING_CACHE_VALID)
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 3a51eaa..85aba3c 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -468,7 +468,6 @@
     private final UnhandledKeyManager mUnhandledKeyManager = new UnhandledKeyManager();
 
     boolean mWindowAttributesChanged = false;
-    int mWindowAttributesChangesFlag = 0;
 
     // These can be accessed by any thread, must be protected with a lock.
     // Surface can never be reassigned or cleared (use Surface.clear()).
@@ -865,7 +864,6 @@
 
                 mSoftInputMode = attrs.softInputMode;
                 mWindowAttributesChanged = true;
-                mWindowAttributesChangesFlag = WindowManager.LayoutParams.EVERYTHING_CHANGED;
                 mAttachInfo.mRootView = view;
                 mAttachInfo.mScalingRequired = mTranslator != null;
                 mAttachInfo.mApplicationScale =
@@ -1269,14 +1267,12 @@
             attrs.systemUiVisibility = mWindowAttributes.systemUiVisibility;
             attrs.subtreeSystemUiVisibility = mWindowAttributes.subtreeSystemUiVisibility;
 
-            mWindowAttributesChangesFlag = mWindowAttributes.copyFrom(attrs);
-            if ((mWindowAttributesChangesFlag
-                    & WindowManager.LayoutParams.TRANSLUCENT_FLAGS_CHANGED) != 0) {
+            final int changes = mWindowAttributes.copyFrom(attrs);
+            if ((changes & WindowManager.LayoutParams.TRANSLUCENT_FLAGS_CHANGED) != 0) {
                 // Recompute system ui visibility.
                 mAttachInfo.mRecomputeGlobalAttributes = true;
             }
-            if ((mWindowAttributesChangesFlag
-                    & WindowManager.LayoutParams.LAYOUT_CHANGED) != 0) {
+            if ((changes & WindowManager.LayoutParams.LAYOUT_CHANGED) != 0) {
                 // Request to update light center.
                 mAttachInfo.mNeedsUpdateLightCenter = true;
             }
@@ -2037,8 +2033,6 @@
             }
         }
 
-        mWindowAttributesChangesFlag = 0;
-
         Rect frame = mWinFrame;
         if (mFirst) {
             mFullRedrawNeeded = true;
@@ -3313,14 +3307,19 @@
     public void requestTransparentRegion(View child) {
         // the test below should not fail unless someone is messing with us
         checkThread();
-        if (mView == child) {
+        if (mView != child) {
+            return;
+        }
+
+        if ((mView.mPrivateFlags & View.PFLAG_REQUEST_TRANSPARENT_REGIONS) == 0) {
             mView.mPrivateFlags |= View.PFLAG_REQUEST_TRANSPARENT_REGIONS;
             // Need to make sure we re-evaluate the window attributes next
             // time around, to ensure the window has the correct format.
             mWindowAttributesChanged = true;
-            mWindowAttributesChangesFlag = 0;
-            requestLayout();
         }
+
+        // Always request layout to apply the latest transparent region.
+        requestLayout();
     }
 
     /**
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 9cd356f..b2d7032 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -2891,8 +2891,6 @@
         public static final int COLOR_MODE_CHANGED = 1 << 26;
         /** {@hide} */
         public static final int INSET_FLAGS_CHANGED = 1 << 27;
-        /** {@hide} */
-        public static final int EVERYTHING_CHANGED = 0xffffffff;
 
         // internal buffer to backup/restore parameters under compatibility mode.
         private int[] mCompatibilityParamsBackup = null;
diff --git a/core/java/com/android/internal/app/ISoundTriggerService.aidl b/core/java/com/android/internal/app/ISoundTriggerService.aidl
index 764c0cf..ea24d5f 100644
--- a/core/java/com/android/internal/app/ISoundTriggerService.aidl
+++ b/core/java/com/android/internal/app/ISoundTriggerService.aidl
@@ -54,4 +54,6 @@
     boolean isRecognitionActive(in ParcelUuid parcelUuid);
 
     int getModelState(in ParcelUuid soundModelId);
+
+    @nullable SoundTrigger.ModuleProperties getModuleProperties();
 }
diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
index 7c50337..5142d3c 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
@@ -50,10 +50,19 @@
     // Flags related to controls
 
     /**
-     * (boolean) Wether to have split behavior when opening QS
+     * (boolean) Whether to have split behavior when opening QS
      */
     public static final String QS_SPLIT_ENABLED = "qs_split_enabled";
 
+    /**
+     * (int) Open settings panels for WiFi and BT tiles
+     * 0 - default behavior, link to settings
+     * 1 - open panel on long press, click remains the same
+     * 2 - open panel on click, long press remains the same
+     * 3 - use details on long press
+     */
+    public static final String QS_USE_SETTINGS_PANELS = "qs_use_settings_panels";
+
     // Flags related to Smart Suggestions - these are read in SmartReplyConstants.
 
     /** (boolean) Whether to enable smart suggestions in notifications. */
diff --git a/core/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl
index b36c3fa..b626fc6 100644
--- a/core/java/com/android/internal/widget/ILockSettings.aidl
+++ b/core/java/com/android/internal/widget/ILockSettings.aidl
@@ -42,7 +42,7 @@
     long getLong(in String key, in long defaultValue, in int userId);
     @UnsupportedAppUsage
     String getString(in String key, in String defaultValue, in int userId);
-    void setLockCredential(in byte[] credential, int type, in byte[] savedCredential, int requestedQuality, int userId, boolean allowUntrustedChange);
+    boolean setLockCredential(in byte[] credential, int type, in byte[] savedCredential, int requestedQuality, int userId, boolean allowUntrustedChange);
     void resetKeyStore(int userId);
     VerifyCredentialResponse checkCredential(in byte[] credential, int type, int userId,
             in ICheckCredentialProgressCallback progressCallback);
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 32867a8..070121c 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -680,6 +680,15 @@
 
     /**
      * Clear any lock pattern or password.
+
+     * <p> This method will fail (returning {@code false}) if the previously
+     * saved password provided is incorrect, or if the lockscreen verification
+     * is still being throttled.
+     *
+     * @param savedCredential The previously saved credential
+     * @param userHandle the user whose pattern is to be saved.
+     * @return whether this was successful or not.
+     * @throws RuntimeException if password change encountered an unrecoverable error.
      */
     public boolean clearLock(byte[] savedCredential, int userHandle) {
         return clearLock(savedCredential, userHandle, false);
@@ -687,19 +696,27 @@
 
     /**
      * Clear any lock pattern or password, with the option to ignore incorrect existing credential.
+     * <p> This method will fail (returning {@code false}) if the previously
+     * saved password provided is incorrect, or if the lockscreen verification
+     * is still being throttled.
+     *
+     * @param savedCredential The previously saved credential
+     * @param userHandle the user whose pattern is to be saved.
+     * @return whether this was successful or not.
+     * @throws RuntimeException if password change encountered an unrecoverable error.
      */
     public boolean clearLock(byte[] savedCredential, int userHandle, boolean allowUntrustedChange) {
         final int currentQuality = getKeyguardStoredPasswordQuality(userHandle);
         setKeyguardStoredPasswordQuality(PASSWORD_QUALITY_UNSPECIFIED, userHandle);
 
-        try{
-            getLockSettings().setLockCredential(null, CREDENTIAL_TYPE_NONE,
-                    savedCredential, PASSWORD_QUALITY_UNSPECIFIED, userHandle,
-                    allowUntrustedChange);
-        } catch (Exception e) {
-            Log.e(TAG, "Failed to clear lock", e);
+        try {
+            if (!getLockSettings().setLockCredential(null, CREDENTIAL_TYPE_NONE, savedCredential,
+                    PASSWORD_QUALITY_UNSPECIFIED, userHandle, allowUntrustedChange)) {
+                return false;
+            }
+        } catch (RemoteException | RuntimeException e) {
             setKeyguardStoredPasswordQuality(currentQuality, userHandle);
-            return false;
+            throw new RuntimeException("Failed to clear lock", e);
         }
 
         if (userHandle == UserHandle.USER_SYSTEM) {
@@ -747,11 +764,15 @@
 
     /**
      * Save a lock pattern.
+     *
+     * <p> This method will fail (returning {@code false}) if the previously saved pattern provided
+     * is incorrect, or if the lockscreen verification is still being throttled.
+     *
      * @param pattern The new pattern to save.
      * @param savedPattern The previously saved pattern, converted to byte[] format
      * @param userId the user whose pattern is to be saved.
-     *
      * @return whether this was successful or not.
+     * @throws RuntimeException if password change encountered an unrecoverable error.
      */
     public boolean saveLockPattern(List<LockPatternView.Cell> pattern, byte[] savedPattern,
             int userId) {
@@ -760,13 +781,17 @@
 
     /**
      * Save a lock pattern.
+     *
+     * <p> This method will fail (returning {@code false}) if the previously saved pattern provided
+     * is incorrect, or if the lockscreen verification is still being throttled.
+     *
      * @param pattern The new pattern to save.
      * @param savedPattern The previously saved pattern, converted to byte[] format
      * @param userId the user whose pattern is to be saved.
      * @param allowUntrustedChange whether we want to allow saving a new password if the existing
      * password being provided is incorrect.
-     *
      * @return whether this was successful or not.
+     * @throws RuntimeException if password change encountered an unrecoverable error.
      */
     public boolean saveLockPattern(List<LockPatternView.Cell> pattern, byte[] savedPattern,
             int userId, boolean allowUntrustedChange) {
@@ -783,12 +808,13 @@
         final int currentQuality = getKeyguardStoredPasswordQuality(userId);
         setKeyguardStoredPasswordQuality(PASSWORD_QUALITY_SOMETHING, userId);
         try {
-            getLockSettings().setLockCredential(bytePattern, CREDENTIAL_TYPE_PATTERN, savedPattern,
-                    PASSWORD_QUALITY_SOMETHING, userId, allowUntrustedChange);
-        } catch (Exception e) {
-            Log.e(TAG, "Couldn't save lock pattern", e);
+            if (!getLockSettings().setLockCredential(bytePattern, CREDENTIAL_TYPE_PATTERN,
+                    savedPattern, PASSWORD_QUALITY_SOMETHING, userId, allowUntrustedChange)) {
+                return false;
+            }
+        } catch (RemoteException | RuntimeException e) {
             setKeyguardStoredPasswordQuality(currentQuality, userId);
-            return false;
+            throw new RuntimeException("Couldn't save lock pattern", e);
         }
         // Update the device encryption password.
         if (userId == UserHandle.USER_SYSTEM
@@ -906,14 +932,18 @@
      * Save a lock password.  Does not ensure that the password is as good
      * as the requested mode, but will adjust the mode to be as good as the
      * password.
+     *
+     * <p> This method will fail (returning {@code false}) if the previously
+     * saved password provided is incorrect, or if the lockscreen verification
+     * is still being throttled.
+     *
      * @param password The password to save
      * @param savedPassword The previously saved lock password, or null if none
      * @param requestedQuality {@see DevicePolicyManager#getPasswordQuality(
      * android.content.ComponentName)}
      * @param userHandle The userId of the user to change the password for
-     *
      * @return whether this was successful or not.
-     *
+     * @throws RuntimeException if password change encountered an unrecoverable error.
      * @deprecated Pass password as a byte array
      */
     @Deprecated
@@ -928,13 +958,18 @@
      * Save a lock password.  Does not ensure that the password is as good
      * as the requested mode, but will adjust the mode to be as good as the
      * password.
+     *
+     * <p> This method will fail (returning {@code false}) if the previously
+     * saved password provided is incorrect, or if the lockscreen verification
+     * is still being throttled.
+     *
      * @param password The password to save
      * @param savedPassword The previously saved lock password, or null if none
      * @param requestedQuality {@see DevicePolicyManager#getPasswordQuality(
      * android.content.ComponentName)}
      * @param userHandle The userId of the user to change the password for
-     *
      * @return whether this was successful or not.
+     * @throws RuntimeException if password change encountered an unrecoverable error.
      */
     public boolean saveLockPassword(byte[] password, byte[] savedPassword, int requestedQuality,
             int userHandle) {
@@ -946,6 +981,11 @@
      * Save a lock password.  Does not ensure that the password is as good
      * as the requested mode, but will adjust the mode to be as good as the
      * password.
+     *
+     * <p> This method will fail (returning {@code false}) if the previously
+     * saved password provided is incorrect, or if the lockscreen verification
+     * is still being throttled.
+     *
      * @param password The password to save
      * @param savedPassword The previously saved lock password, or null if none
      * @param requestedQuality {@see DevicePolicyManager#getPasswordQuality(
@@ -953,9 +993,9 @@
      * @param userHandle The userId of the user to change the password for
      * @param allowUntrustedChange whether we want to allow saving a new password if the existing
      * password being provided is incorrect.
-     *
      * @return whether this method saved the new password successfully or not. This flow will fail
      * and return false if the given credential is wrong and allowUntrustedChange is false.
+     * @throws RuntimeException if password change encountered an unrecoverable error.
      */
     public boolean saveLockPassword(byte[] password, byte[] savedPassword,
             int requestedQuality, int userHandle, boolean allowUntrustedChange) {
@@ -981,10 +1021,9 @@
         try {
             getLockSettings().setLockCredential(password, CREDENTIAL_TYPE_PASSWORD, savedPassword,
                     requestedQuality, userHandle, allowUntrustedChange);
-        } catch (Exception e) {
-            Log.e(TAG, "Unable to save lock password", e);
+        } catch (RemoteException | RuntimeException e) {
             setKeyguardStoredPasswordQuality(currentQuality, userHandle);
-            return false;
+            throw new RuntimeException("Unable to save lock password", e);
         }
 
         updateEncryptionPasswordIfNeeded(password, passwordQuality, userHandle);
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index 364278d..7cd3e95 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -53,6 +53,8 @@
 
 /**
  * Loads global system configuration info.
+ * Note: Initializing this class hits the disk and is slow.  This class should generally only be
+ * accessed by the system_server process.
  */
 public class SystemConfig {
     static final String TAG = "SystemConfig";
@@ -208,6 +210,11 @@
     private final ArraySet<String> mBugreportWhitelistedPackages = new ArraySet<>();
 
     public static SystemConfig getInstance() {
+        if (!isSystemProcess()) {
+            Slog.wtf(TAG, "SystemConfig is being accessed by a process other than "
+                    + "system_server.");
+        }
+
         synchronized (SystemConfig.class) {
             if (sInstance == null) {
                 sInstance = new SystemConfig();
@@ -1161,4 +1168,8 @@
             mSplitPermissions.add(new SplitPermissionInfo(splitPerm, newPermissions, targetSdk));
         }
     }
+
+    private static boolean isSystemProcess() {
+        return Process.myUid() == Process.SYSTEM_UID;
+    }
 }
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 4c54539..7779f55 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -308,6 +308,7 @@
                 "android_os_MessageQueue.cpp",
                 "android_os_Trace.cpp",
                 "android_util_AssetManager.cpp",
+                "android_util_FileObserver.cpp",
                 "android_util_StringBlock.cpp",
                 "android_util_XmlBlock.cpp",
             ],
diff --git a/core/jni/LayoutlibLoader.cpp b/core/jni/LayoutlibLoader.cpp
index 549fd4d..6c0680f 100644
--- a/core/jni/LayoutlibLoader.cpp
+++ b/core/jni/LayoutlibLoader.cpp
@@ -70,6 +70,7 @@
 extern int register_android_graphics_fonts_FontFamily(JNIEnv* env);
 extern int register_android_graphics_text_LineBreaker(JNIEnv* env);
 extern int register_android_graphics_text_MeasuredText(JNIEnv* env);
+extern int register_android_os_FileObserver(JNIEnv* env);
 extern int register_android_os_MessageQueue(JNIEnv* env);
 extern int register_android_os_SystemClock(JNIEnv* env);
 extern int register_android_os_SystemProperties(JNIEnv* env);
@@ -128,6 +129,7 @@
     {"android.graphics.text.LineBreaker", REG_JNI(register_android_graphics_text_LineBreaker)},
     {"android.graphics.text.MeasuredText", REG_JNI(register_android_graphics_text_MeasuredText)},
 #ifdef __linux__
+    {"android.os.FileObserver", REG_JNI(register_android_os_FileObserver)},
     {"android.os.MessageQueue", REG_JNI(register_android_os_MessageQueue)},
 #endif
     {"android.os.SystemClock", REG_JNI(register_android_os_SystemClock)},
diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp
index a52a7ed..4cb2d15 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -658,6 +658,12 @@
     return si.totalram;
 }
 
+/*
+ * The outFields array is initialized to -1 to allow the caller to identify
+ * when the status file (and therefore the process) they specified is invalid.
+ * This array should not be overwritten or cleared before we know that the
+ * status file can be read.
+ */
 void android_os_Process_readProcLines(JNIEnv* env, jobject clazz, jstring fileStr,
                                       jobjectArray reqFields, jlongArray outFields)
 {
@@ -706,14 +712,14 @@
         return;
     }
 
-    //ALOGI("Clearing %" PRId32 " sizes", count);
-    for (i=0; i<count; i++) {
-        sizesArray[i] = 0;
-    }
-
     int fd = open(file.string(), O_RDONLY | O_CLOEXEC);
 
     if (fd >= 0) {
+        //ALOGI("Clearing %" PRId32 " sizes", count);
+        for (i=0; i<count; i++) {
+            sizesArray[i] = 0;
+        }
+
         const size_t BUFFER_SIZE = 4096;
         char* buffer = (char*)malloc(BUFFER_SIZE);
         int len = read(fd, buffer, BUFFER_SIZE-1);
diff --git a/core/proto/android/view/displayinfo.proto b/core/proto/android/view/displayinfo.proto
index 49c0a29..984be2a 100644
--- a/core/proto/android/view/displayinfo.proto
+++ b/core/proto/android/view/displayinfo.proto
@@ -33,4 +33,5 @@
     // The human-readable name of the display.
     // Eg: "Built-in Screen"
     optional string name = 5;
+    optional int32 flags = 6;
 }
diff --git a/core/tests/coretests/src/android/content/pm/AndroidTelephonyCommonUpdaterTest.java b/core/tests/coretests/src/android/content/pm/AndroidTelephonyCommonUpdaterTest.java
new file mode 100644
index 0000000..8ab9ddb
--- /dev/null
+++ b/core/tests/coretests/src/android/content/pm/AndroidTelephonyCommonUpdaterTest.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm;
+
+import static android.content.pm.PackageBuilder.builder;
+import static android.content.pm.SharedLibraryNames.ANDROID_HIDL_BASE;
+import static android.content.pm.SharedLibraryNames.ANDROID_HIDL_MANAGER;
+import static android.content.pm.SharedLibraryNames.ANDROID_TELEPHONY_COMMON;
+
+import android.os.Build;
+import androidx.test.filters.SmallTest;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/**
+ * Test for {@link AndroidHidlUpdater}
+ */
+@SmallTest
+@RunWith(JUnit4.class)
+public class AndroidTelephonyCommonUpdaterTest extends PackageSharedLibraryUpdaterTest {
+
+    private static final String OTHER_LIBRARY = "other.library";
+    private static final String PHONE_UID = "android.uid.phone";
+
+    @Test
+    public void targeted_at_Q() {
+        PackageBuilder before = builder()
+                .targetSdkVersion(Build.VERSION_CODES.Q);
+
+        PackageBuilder after = builder().targetSdkVersion(Build.VERSION_CODES.Q)
+            .requiredLibraries(ANDROID_TELEPHONY_COMMON);
+
+        // Should add telephony-common libraries
+        checkBackwardsCompatibility(before, after);
+    }
+
+    @Test
+    public void targeted_at_Q_phoneUID() {
+        PackageBuilder before = builder().setSharedUid(PHONE_UID)
+                .targetSdkVersion(Build.VERSION_CODES.Q);
+
+        // Should add telephony-common libraries
+        PackageBuilder after = builder().setSharedUid(PHONE_UID)
+                .targetSdkVersion(Build.VERSION_CODES.Q)
+                .requiredLibraries(ANDROID_TELEPHONY_COMMON);
+
+        checkBackwardsCompatibility(before, after);
+    }
+
+    @Test
+    public void targeted_at_Q_not_empty_usesLibraries() {
+        PackageBuilder before = builder()
+                .targetSdkVersion(Build.VERSION_CODES.Q)
+                .requiredLibraries(OTHER_LIBRARY);
+
+        // no change
+        checkBackwardsCompatibility(before, before);
+    }
+
+    @Test
+    public void targeted_at_Q_not_empty_usesLibraries_phoneUID() {
+        PackageBuilder before = builder().setSharedUid(PHONE_UID)
+                .targetSdkVersion(Build.VERSION_CODES.Q)
+                .requiredLibraries(OTHER_LIBRARY);
+
+        // The telephony-common jars should be added at the start of the list because it
+        // is not on the bootclasspath and the package targets pre-R.
+        PackageBuilder after = builder().setSharedUid(PHONE_UID)
+                .targetSdkVersion(Build.VERSION_CODES.Q)
+                .requiredLibraries(ANDROID_TELEPHONY_COMMON, OTHER_LIBRARY);
+
+        checkBackwardsCompatibility(before, after);
+    }
+
+    @Test
+    public void targeted_at_R_in_usesLibraries() {
+        PackageBuilder before = builder()
+                .targetSdkVersion(Build.VERSION_CODES.Q + 1)
+                .requiredLibraries(ANDROID_TELEPHONY_COMMON);
+
+        PackageBuilder after = builder()
+                .targetSdkVersion(Build.VERSION_CODES.Q + 1);
+
+        // Libraries are removed because they are not available for apps target >= R and not run
+        // on phone-uid
+        checkBackwardsCompatibility(before, after);
+    }
+
+    @Test
+    public void targeted_at_Q_in_usesLibraries() {
+        PackageBuilder before = builder().asSystemApp()
+                .targetSdkVersion(Build.VERSION_CODES.Q)
+                .requiredLibraries(ANDROID_TELEPHONY_COMMON);
+
+        // No change is required because the package explicitly requests the telephony libraries
+        // and is targeted at the current version so does not need backwards compatibility.
+        checkBackwardsCompatibility(before, before);
+    }
+
+
+    @Test
+    public void targeted_at_R_in_usesOptionalLibraries() {
+        PackageBuilder before = builder().targetSdkVersion(Build.VERSION_CODES.Q + 1)
+            .optionalLibraries(ANDROID_TELEPHONY_COMMON);
+
+        // Dependency is removed, it is not available.
+        PackageBuilder after = builder().targetSdkVersion(Build.VERSION_CODES.Q + 1);
+
+        // Libraries are removed because they are not available for apps targeting Q+
+        checkBackwardsCompatibility(before, after);
+    }
+
+    @Test
+    public void targeted_at_R() {
+        PackageBuilder before = builder()
+            .targetSdkVersion(Build.VERSION_CODES.Q + 1);
+
+        // no change
+        checkBackwardsCompatibility(before, before);
+    }
+
+    private void checkBackwardsCompatibility(PackageBuilder before, PackageBuilder after) {
+        checkBackwardsCompatibility(before, after, AndroidTelephonyCommonUpdater::new);
+    }
+}
diff --git a/core/tests/coretests/src/android/content/pm/PackageBuilder.java b/core/tests/coretests/src/android/content/pm/PackageBuilder.java
index f7544af..f3a56e2 100644
--- a/core/tests/coretests/src/android/content/pm/PackageBuilder.java
+++ b/core/tests/coretests/src/android/content/pm/PackageBuilder.java
@@ -37,6 +37,8 @@
 
     private ArrayList<String> mOptionalLibraries;
 
+    private String mSharedUid;
+
     public static PackageBuilder builder() {
         return new PackageBuilder();
     }
@@ -47,6 +49,7 @@
         pkg.applicationInfo.flags = mFlags;
         pkg.usesLibraries = mRequiredLibraries;
         pkg.usesOptionalLibraries = mOptionalLibraries;
+        pkg.mSharedUserId = mSharedUid;
         return pkg;
     }
 
@@ -55,6 +58,11 @@
         return this;
     }
 
+    PackageBuilder setSharedUid(String uid) {
+        this.mSharedUid = uid;
+        return this;
+    }
+
     PackageBuilder asSystemApp() {
         this.mFlags |= ApplicationInfo.FLAG_SYSTEM;
         return this;
diff --git a/core/tests/coretests/src/android/os/ProcessTest.java b/core/tests/coretests/src/android/os/ProcessTest.java
index 9de4501..ae4edb9 100644
--- a/core/tests/coretests/src/android/os/ProcessTest.java
+++ b/core/tests/coretests/src/android/os/ProcessTest.java
@@ -17,13 +17,12 @@
 
 package android.os;
 
-import androidx.test.filters.MediumTest;
-
 import junit.framework.TestCase;
 
 public class ProcessTest extends TestCase {
 
-    @MediumTest
+    private static final int BAD_PID = 0;
+
     public void testProcessGetUidFromName() throws Exception {
         assertEquals(android.os.Process.SYSTEM_UID, Process.getUidForName("system"));
         assertEquals(Process.BLUETOOTH_UID, Process.getUidForName("bluetooth"));
@@ -37,7 +36,6 @@
                 Process.getUidForName("u3_a100"));
     }
 
-    @MediumTest
     public void testProcessGetUidFromNameFailure() throws Exception {
         // Failure cases
         assertEquals(-1, Process.getUidForName("u2a_foo"));
@@ -49,4 +47,29 @@
         assertEquals(-1, Process.getUidForName("u2jhsajhfkjhsafkhskafhkashfkjashfkjhaskjfdhakj3"));
     }
 
+    /**
+     * Tests getUidForPid() by ensuring that it returns the correct value when the process queried
+     * doesn't exist.
+     */
+    public void testGetUidForPidInvalidPid() {
+        assertEquals(-1, Process.getUidForPid(BAD_PID));
+    }
+
+    /**
+     * Tests getParentPid() by ensuring that it returns the correct value when the process queried
+     * doesn't exist.
+     */
+    public void testGetParentPidInvalidPid() {
+        assertEquals(-1, Process.getParentPid(BAD_PID));
+    }
+
+    /**
+     * Tests getThreadGroupLeader() by ensuring that it returns the correct value when the
+     * thread queried doesn't exist.
+     */
+    public void testGetThreadGroupLeaderInvalidTid() {
+        // This function takes a TID instead of a PID but BAD_PID should also be a bad TID.
+        assertEquals(-1, Process.getThreadGroupLeader(BAD_PID));
+    }
+
 }
diff --git a/core/tests/coretests/src/android/service/notification/StatusBarNotificationTest.java b/core/tests/coretests/src/android/service/notification/StatusBarNotificationTest.java
index 6161108..a5a98a9 100644
--- a/core/tests/coretests/src/android/service/notification/StatusBarNotificationTest.java
+++ b/core/tests/coretests/src/android/service/notification/StatusBarNotificationTest.java
@@ -19,6 +19,8 @@
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertNull;
 
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
@@ -181,6 +183,22 @@
                 logMaker.getTaggedData(MetricsEvent.FIELD_NOTIFICATION_STYLE));
     }
 
+    @Test
+    public void testIsAppGroup() {
+        StatusBarNotification sbn = getNotification(PKG, GROUP_ID_1, CHANNEL_ID);
+        assertTrue(sbn.isAppGroup());
+
+        sbn = getNotification(PKG, null, CHANNEL_ID);
+        assertFalse(sbn.isAppGroup());
+
+        Notification.Builder nb = getNotificationBuilder(null, CHANNEL_ID)
+                .setSortKey("something");
+
+        sbn = getNotification(PKG, nb);
+        assertTrue(sbn.isAppGroup());
+
+    }
+
     private StatusBarNotification getNotification(String pkg, String group, String channelId) {
         return getNotification(pkg, getNotificationBuilder(group, channelId));
     }
diff --git a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
index bdd3038..be2c4e96 100644
--- a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
+++ b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
@@ -86,7 +86,7 @@
     }
 
     @AfterClass
-    public static void tearDownOnce() throws Exception {
+    public static void tearDownOnce() {
         sInsetsModeSession.close();
     }
 
diff --git a/data/etc/car/com.android.car.settings.xml b/data/etc/car/com.android.car.settings.xml
index 5f7e1c1..291ce80 100644
--- a/data/etc/car/com.android.car.settings.xml
+++ b/data/etc/car/com.android.car.settings.xml
@@ -50,7 +50,6 @@
         <permission name="android.permission.USE_RESERVED_DISK"/>
         <permission name="android.permission.USER_ACTIVITY"/>
         <permission name="android.permission.WRITE_APN_SETTINGS"/>
-        <permission name="android.permission.WRITE_MEDIA_STORAGE"/>
         <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
         <permission name="android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS" />
     </privapp-permissions>
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index 0960f78..d66930a 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -225,6 +225,8 @@
     <library name="android.hidl.manager-V1.0-java"
             file="/system/framework/android.hidl.manager-V1.0-java.jar"
             dependency="android.hidl.base-V1.0-java" />
+    <library name="telephony-common"
+        file="/system/framework/telephony-common.jar" />
 
     <!-- These are the standard packages that are white-listed to always have internet
          access while in power save mode, even if they aren't in the foreground. -->
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index aad15ab..0ad050d 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -15,7 +15,17 @@
  */
 
 #include "CanvasContext.h"
+
 #include <GpuMemoryTracker.h>
+#include <apex/window.h>
+#include <fcntl.h>
+#include <strings.h>
+#include <sys/stat.h>
+
+#include <algorithm>
+#include <cstdint>
+#include <cstdlib>
+#include <functional>
 
 #include "../Properties.h"
 #include "AnimationContext.h"
@@ -32,16 +42,6 @@
 #include "utils/TimeUtils.h"
 #include "utils/TraceUtils.h"
 
-#include <strings.h>
-
-#include <fcntl.h>
-#include <sys/stat.h>
-#include <algorithm>
-
-#include <cstdint>
-#include <cstdlib>
-#include <functional>
-
 #define TRIM_MEMORY_COMPLETE 80
 #define TRIM_MEMORY_UI_HIDDEN 20
 
@@ -484,16 +484,16 @@
         swap.swapCompletedTime = systemTime(SYSTEM_TIME_MONOTONIC);
         swap.vsyncTime = mRenderThread.timeLord().latestVsync();
         if (didDraw) {
-            int durationUs;
             nsecs_t dequeueStart = mNativeSurface->getLastDequeueStartTime();
             if (dequeueStart < mCurrentFrameInfo->get(FrameInfoIndex::SyncStart)) {
                 // Ignoring dequeue duration as it happened prior to frame render start
                 // and thus is not part of the frame.
                 swap.dequeueDuration = 0;
             } else {
-                mNativeSurface->query(NATIVE_WINDOW_LAST_DEQUEUE_DURATION, &durationUs);
-                swap.dequeueDuration = us2ns(durationUs);
+                swap.dequeueDuration =
+                        us2ns(ANativeWindow_getLastDequeueDuration(mNativeSurface.get()));
             }
+            int durationUs;
             mNativeSurface->query(NATIVE_WINDOW_LAST_QUEUE_DURATION, &durationUs);
             swap.queueDuration = us2ns(durationUs);
         } else {
diff --git a/media/java/android/media/soundtrigger/SoundTriggerManager.java b/media/java/android/media/soundtrigger/SoundTriggerManager.java
index ada77c5..dc400ad 100644
--- a/media/java/android/media/soundtrigger/SoundTriggerManager.java
+++ b/media/java/android/media/soundtrigger/SoundTriggerManager.java
@@ -383,4 +383,20 @@
             throw e.rethrowFromSystemServer();
         }
     }
+
+    /**
+     * Get the hardware sound trigger module properties currently loaded.
+     *
+     * @return The properties currently loaded. Returns null if no supported hardware loaded.
+     */
+    @RequiresPermission(android.Manifest.permission.MANAGE_SOUND_TRIGGER)
+    @Nullable
+    public SoundTrigger.ModuleProperties getModuleProperties() {
+
+        try {
+            return mSoundTriggerService.getModuleProperties();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
 }
diff --git a/packages/BackupEncryption/Android.bp b/packages/BackupEncryption/Android.bp
new file mode 100644
index 0000000..50dbcdb
--- /dev/null
+++ b/packages/BackupEncryption/Android.bp
@@ -0,0 +1,24 @@
+//
+// 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.
+//
+
+android_app {
+    name: "BackupEncryption",
+    srcs: ["src/**/*.java"],
+    optimize: { enabled: false },
+    platform_apis: true,
+    certificate: "platform",
+    privileged: true,
+}
\ No newline at end of file
diff --git a/packages/BackupEncryption/AndroidManifest.xml b/packages/BackupEncryption/AndroidManifest.xml
new file mode 100644
index 0000000..a705df5
--- /dev/null
+++ b/packages/BackupEncryption/AndroidManifest.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (c) 2016 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.server.backup.encryption"
+    android:sharedUserId="android.uid.system" >
+
+    <application android:allowBackup="false" />
+</manifest>
diff --git a/packages/BackupEncryption/proguard.flags b/packages/BackupEncryption/proguard.flags
new file mode 100644
index 0000000..851ce8c
--- /dev/null
+++ b/packages/BackupEncryption/proguard.flags
@@ -0,0 +1 @@
+-keep class com.android.server.backup.encryption
diff --git a/services/backup/java/com/android/server/backup/encryption/chunk/Chunk.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunk/Chunk.java
similarity index 73%
rename from services/backup/java/com/android/server/backup/encryption/chunk/Chunk.java
rename to packages/BackupEncryption/src/com/android/server/backup/encryption/chunk/Chunk.java
index 5bec1a9..ba32860 100644
--- a/services/backup/java/com/android/server/backup/encryption/chunk/Chunk.java
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunk/Chunk.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
 package com.android.server.backup.encryption.chunk;
 
 import android.util.proto.ProtoInputStream;
@@ -51,4 +67,4 @@
     public byte[] getHash() {
         return mHash;
     }
-}
\ No newline at end of file
+}
diff --git a/services/backup/java/com/android/server/backup/encryption/chunk/ChunkHash.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunk/ChunkHash.java
similarity index 98%
rename from services/backup/java/com/android/server/backup/encryption/chunk/ChunkHash.java
rename to packages/BackupEncryption/src/com/android/server/backup/encryption/chunk/ChunkHash.java
index 1ae598e..1630eb8 100644
--- a/services/backup/java/com/android/server/backup/encryption/chunk/ChunkHash.java
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunk/ChunkHash.java
@@ -11,12 +11,13 @@
  * 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.backup.encryption.chunk;
 
 import com.android.internal.util.Preconditions;
+
 import java.util.Arrays;
 import java.util.Base64;
 
diff --git a/services/backup/java/com/android/server/backup/encryption/chunk/ChunkListingMap.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunk/ChunkListingMap.java
similarity index 100%
rename from services/backup/java/com/android/server/backup/encryption/chunk/ChunkListingMap.java
rename to packages/BackupEncryption/src/com/android/server/backup/encryption/chunk/ChunkListingMap.java
diff --git a/services/backup/java/com/android/server/backup/encryption/chunk/ChunkOrderingType.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunk/ChunkOrderingType.java
similarity index 97%
rename from services/backup/java/com/android/server/backup/encryption/chunk/ChunkOrderingType.java
rename to packages/BackupEncryption/src/com/android/server/backup/encryption/chunk/ChunkOrderingType.java
index df36c94..8cb028e 100644
--- a/services/backup/java/com/android/server/backup/encryption/chunk/ChunkOrderingType.java
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunk/ChunkOrderingType.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.backup.encryption.chunk;
@@ -21,6 +21,7 @@
 import static com.android.server.backup.encryption.chunk.ChunksMetadataProto.INLINE_LENGTHS;
 
 import android.annotation.IntDef;
+
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 
diff --git a/services/backup/java/com/android/server/backup/encryption/chunk/EncryptedChunkOrdering.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunk/EncryptedChunkOrdering.java
similarity index 96%
rename from services/backup/java/com/android/server/backup/encryption/chunk/EncryptedChunkOrdering.java
rename to packages/BackupEncryption/src/com/android/server/backup/encryption/chunk/EncryptedChunkOrdering.java
index 3a6d1f6..edf1b9a 100644
--- a/services/backup/java/com/android/server/backup/encryption/chunk/EncryptedChunkOrdering.java
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunk/EncryptedChunkOrdering.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.backup.encryption.chunk;
@@ -38,6 +38,7 @@
 
     private final byte[] mEncryptedChunkOrdering;
 
+    /** Get the encrypted chunk ordering */
     public byte[] encryptedChunkOrdering() {
         return mEncryptedChunkOrdering;
     }
diff --git a/services/backup/java/com/android/server/backup/encryption/chunking/BackupWriter.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/BackupWriter.java
similarity index 97%
rename from services/backup/java/com/android/server/backup/encryption/chunking/BackupWriter.java
rename to packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/BackupWriter.java
index 68d9d14..baa820c 100644
--- a/services/backup/java/com/android/server/backup/encryption/chunking/BackupWriter.java
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/BackupWriter.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.backup.encryption.chunking;
diff --git a/services/backup/java/com/android/server/backup/encryption/chunking/ByteRange.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/ByteRange.java
similarity index 100%
rename from services/backup/java/com/android/server/backup/encryption/chunking/ByteRange.java
rename to packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/ByteRange.java
diff --git a/services/backup/java/com/android/server/backup/encryption/chunking/ChunkEncryptor.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/ChunkEncryptor.java
similarity index 98%
rename from services/backup/java/com/android/server/backup/encryption/chunking/ChunkEncryptor.java
rename to packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/ChunkEncryptor.java
index 812cfbd..48abc8c 100644
--- a/services/backup/java/com/android/server/backup/encryption/chunking/ChunkEncryptor.java
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/ChunkEncryptor.java
@@ -11,16 +11,18 @@
  * 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.backup.encryption.chunking;
 
 import com.android.server.backup.encryption.chunk.ChunkHash;
+
 import java.security.InvalidAlgorithmParameterException;
 import java.security.InvalidKeyException;
 import java.security.NoSuchAlgorithmException;
 import java.security.SecureRandom;
+
 import javax.crypto.BadPaddingException;
 import javax.crypto.Cipher;
 import javax.crypto.IllegalBlockSizeException;
diff --git a/services/backup/java/com/android/server/backup/encryption/chunking/ChunkHasher.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/ChunkHasher.java
similarity index 97%
rename from services/backup/java/com/android/server/backup/encryption/chunking/ChunkHasher.java
rename to packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/ChunkHasher.java
index 145b7bf..02d498c 100644
--- a/services/backup/java/com/android/server/backup/encryption/chunking/ChunkHasher.java
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/ChunkHasher.java
@@ -11,14 +11,16 @@
  * 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.backup.encryption.chunking;
 
 import com.android.server.backup.encryption.chunk.ChunkHash;
+
 import java.security.InvalidKeyException;
 import java.security.NoSuchAlgorithmException;
+
 import javax.crypto.Mac;
 import javax.crypto.SecretKey;
 
diff --git a/services/backup/java/com/android/server/backup/encryption/chunking/Chunker.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/Chunker.java
similarity index 97%
rename from services/backup/java/com/android/server/backup/encryption/chunking/Chunker.java
rename to packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/Chunker.java
index b91913e..c9a6293 100644
--- a/services/backup/java/com/android/server/backup/encryption/chunking/Chunker.java
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/Chunker.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.backup.encryption.chunking;
diff --git a/services/backup/java/com/android/server/backup/encryption/chunking/DecryptedChunkFileOutput.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/DecryptedChunkFileOutput.java
similarity index 100%
rename from services/backup/java/com/android/server/backup/encryption/chunking/DecryptedChunkFileOutput.java
rename to packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/DecryptedChunkFileOutput.java
diff --git a/services/backup/java/com/android/server/backup/encryption/chunking/DiffScriptBackupWriter.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/DiffScriptBackupWriter.java
similarity index 100%
rename from services/backup/java/com/android/server/backup/encryption/chunking/DiffScriptBackupWriter.java
rename to packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/DiffScriptBackupWriter.java
diff --git a/services/backup/java/com/android/server/backup/encryption/chunking/DiffScriptWriter.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/DiffScriptWriter.java
similarity index 100%
rename from services/backup/java/com/android/server/backup/encryption/chunking/DiffScriptWriter.java
rename to packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/DiffScriptWriter.java
diff --git a/services/backup/java/com/android/server/backup/encryption/chunking/EncryptedChunk.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/EncryptedChunk.java
similarity index 98%
rename from services/backup/java/com/android/server/backup/encryption/chunking/EncryptedChunk.java
rename to packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/EncryptedChunk.java
index 1f936eb..cde59fa 100644
--- a/services/backup/java/com/android/server/backup/encryption/chunking/EncryptedChunk.java
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/EncryptedChunk.java
@@ -11,13 +11,14 @@
  * 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.backup.encryption.chunking;
 
 import com.android.internal.util.Preconditions;
 import com.android.server.backup.encryption.chunk.ChunkHash;
+
 import java.util.Arrays;
 import java.util.Objects;
 
diff --git a/services/backup/java/com/android/server/backup/encryption/chunking/EncryptedChunkEncoder.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/EncryptedChunkEncoder.java
similarity index 97%
rename from services/backup/java/com/android/server/backup/encryption/chunking/EncryptedChunkEncoder.java
rename to packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/EncryptedChunkEncoder.java
index eaf701c..16beda3 100644
--- a/services/backup/java/com/android/server/backup/encryption/chunking/EncryptedChunkEncoder.java
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/EncryptedChunkEncoder.java
@@ -11,12 +11,13 @@
  * 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.backup.encryption.chunking;
 
 import com.android.server.backup.encryption.chunk.ChunkOrderingType;
+
 import java.io.IOException;
 
 /** Encodes an {@link EncryptedChunk} as bytes to write to the encrypted backup file. */
diff --git a/services/backup/java/com/android/server/backup/encryption/chunking/InlineLengthsEncryptedChunkEncoder.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/InlineLengthsEncryptedChunkEncoder.java
similarity index 98%
rename from services/backup/java/com/android/server/backup/encryption/chunking/InlineLengthsEncryptedChunkEncoder.java
rename to packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/InlineLengthsEncryptedChunkEncoder.java
index 5c902ca..7b38dd4 100644
--- a/services/backup/java/com/android/server/backup/encryption/chunking/InlineLengthsEncryptedChunkEncoder.java
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/InlineLengthsEncryptedChunkEncoder.java
@@ -11,13 +11,14 @@
  * 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.backup.encryption.chunking;
 
 import com.android.server.backup.encryption.chunk.ChunkOrderingType;
 import com.android.server.backup.encryption.chunk.ChunksMetadataProto;
+
 import java.io.IOException;
 
 /**
diff --git a/services/backup/java/com/android/server/backup/encryption/chunking/LengthlessEncryptedChunkEncoder.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/LengthlessEncryptedChunkEncoder.java
similarity index 97%
rename from services/backup/java/com/android/server/backup/encryption/chunking/LengthlessEncryptedChunkEncoder.java
rename to packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/LengthlessEncryptedChunkEncoder.java
index 4b84981..567f75d 100644
--- a/services/backup/java/com/android/server/backup/encryption/chunking/LengthlessEncryptedChunkEncoder.java
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/LengthlessEncryptedChunkEncoder.java
@@ -11,13 +11,14 @@
  * 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.backup.encryption.chunking;
 
 import com.android.server.backup.encryption.chunk.ChunkOrderingType;
 import com.android.server.backup.encryption.chunk.ChunksMetadataProto;
+
 import java.io.IOException;
 
 /**
diff --git a/services/backup/java/com/android/server/backup/encryption/chunking/OutputStreamWrapper.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/OutputStreamWrapper.java
similarity index 100%
rename from services/backup/java/com/android/server/backup/encryption/chunking/OutputStreamWrapper.java
rename to packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/OutputStreamWrapper.java
diff --git a/services/backup/java/com/android/server/backup/encryption/chunking/RawBackupWriter.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/RawBackupWriter.java
similarity index 82%
rename from services/backup/java/com/android/server/backup/encryption/chunking/RawBackupWriter.java
rename to packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/RawBackupWriter.java
index 839dc7c..b211b0f 100644
--- a/services/backup/java/com/android/server/backup/encryption/chunking/RawBackupWriter.java
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/RawBackupWriter.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.backup.encryption.chunking;
@@ -21,18 +21,18 @@
 
 /** Writes data straight to an output stream. */
 public class RawBackupWriter implements BackupWriter {
-    private final OutputStream outputStream;
-    private long bytesWritten;
+    private final OutputStream mOutputStream;
+    private long mBytesWritten;
 
     /** Constructs a new writer which writes bytes to the given output stream. */
     public RawBackupWriter(OutputStream outputStream) {
-        this.outputStream = outputStream;
+        this.mOutputStream = outputStream;
     }
 
     @Override
     public void writeBytes(byte[] bytes) throws IOException {
-        outputStream.write(bytes);
-        bytesWritten += bytes.length;
+        mOutputStream.write(bytes);
+        mBytesWritten += bytes.length;
     }
 
     @Override
@@ -42,11 +42,11 @@
 
     @Override
     public long getBytesWritten() {
-        return bytesWritten;
+        return mBytesWritten;
     }
 
     @Override
     public void flush() throws IOException {
-        outputStream.flush();
+        mOutputStream.flush();
     }
 }
diff --git a/services/backup/java/com/android/server/backup/encryption/chunking/SingleStreamDiffScriptWriter.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/SingleStreamDiffScriptWriter.java
similarity index 100%
rename from services/backup/java/com/android/server/backup/encryption/chunking/SingleStreamDiffScriptWriter.java
rename to packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/SingleStreamDiffScriptWriter.java
diff --git a/services/backup/java/com/android/server/backup/encryption/chunking/cdc/ContentDefinedChunker.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/cdc/ContentDefinedChunker.java
similarity index 100%
rename from services/backup/java/com/android/server/backup/encryption/chunking/cdc/ContentDefinedChunker.java
rename to packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/cdc/ContentDefinedChunker.java
diff --git a/services/backup/java/com/android/server/backup/encryption/chunking/cdc/FingerprintMixer.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/cdc/FingerprintMixer.java
similarity index 100%
rename from services/backup/java/com/android/server/backup/encryption/chunking/cdc/FingerprintMixer.java
rename to packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/cdc/FingerprintMixer.java
diff --git a/services/backup/java/com/android/server/backup/encryption/chunking/cdc/Hkdf.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/cdc/Hkdf.java
similarity index 100%
rename from services/backup/java/com/android/server/backup/encryption/chunking/cdc/Hkdf.java
rename to packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/cdc/Hkdf.java
diff --git a/services/backup/java/com/android/server/backup/encryption/chunking/cdc/IsChunkBreakpoint.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/cdc/IsChunkBreakpoint.java
similarity index 100%
rename from services/backup/java/com/android/server/backup/encryption/chunking/cdc/IsChunkBreakpoint.java
rename to packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/cdc/IsChunkBreakpoint.java
diff --git a/services/backup/java/com/android/server/backup/encryption/chunking/cdc/RabinFingerprint64.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/cdc/RabinFingerprint64.java
similarity index 100%
rename from services/backup/java/com/android/server/backup/encryption/chunking/cdc/RabinFingerprint64.java
rename to packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/cdc/RabinFingerprint64.java
diff --git a/services/backup/java/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKey.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKey.java
similarity index 100%
rename from services/backup/java/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKey.java
rename to packages/BackupEncryption/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKey.java
diff --git a/services/backup/java/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyManager.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyManager.java
similarity index 100%
rename from services/backup/java/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyManager.java
rename to packages/BackupEncryption/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyManager.java
diff --git a/services/backup/java/com/android/server/backup/encryption/keys/TertiaryKeyGenerator.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/keys/TertiaryKeyGenerator.java
similarity index 100%
rename from services/backup/java/com/android/server/backup/encryption/keys/TertiaryKeyGenerator.java
rename to packages/BackupEncryption/src/com/android/server/backup/encryption/keys/TertiaryKeyGenerator.java
diff --git a/services/backup/java/com/android/server/backup/encryption/keys/TertiaryKeyRotationTracker.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/keys/TertiaryKeyRotationTracker.java
similarity index 100%
rename from services/backup/java/com/android/server/backup/encryption/keys/TertiaryKeyRotationTracker.java
rename to packages/BackupEncryption/src/com/android/server/backup/encryption/keys/TertiaryKeyRotationTracker.java
diff --git a/services/backup/java/com/android/server/backup/encryption/storage/BackupEncryptionDb.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/storage/BackupEncryptionDb.java
similarity index 100%
rename from services/backup/java/com/android/server/backup/encryption/storage/BackupEncryptionDb.java
rename to packages/BackupEncryption/src/com/android/server/backup/encryption/storage/BackupEncryptionDb.java
diff --git a/services/backup/java/com/android/server/backup/encryption/storage/BackupEncryptionDbContract.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/storage/BackupEncryptionDbContract.java
similarity index 100%
rename from services/backup/java/com/android/server/backup/encryption/storage/BackupEncryptionDbContract.java
rename to packages/BackupEncryption/src/com/android/server/backup/encryption/storage/BackupEncryptionDbContract.java
diff --git a/services/backup/java/com/android/server/backup/encryption/storage/BackupEncryptionDbHelper.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/storage/BackupEncryptionDbHelper.java
similarity index 100%
rename from services/backup/java/com/android/server/backup/encryption/storage/BackupEncryptionDbHelper.java
rename to packages/BackupEncryption/src/com/android/server/backup/encryption/storage/BackupEncryptionDbHelper.java
diff --git a/services/backup/java/com/android/server/backup/encryption/storage/EncryptionDbException.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/storage/EncryptionDbException.java
similarity index 100%
rename from services/backup/java/com/android/server/backup/encryption/storage/EncryptionDbException.java
rename to packages/BackupEncryption/src/com/android/server/backup/encryption/storage/EncryptionDbException.java
diff --git a/services/backup/java/com/android/server/backup/encryption/storage/TertiaryKey.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/storage/TertiaryKey.java
similarity index 100%
rename from services/backup/java/com/android/server/backup/encryption/storage/TertiaryKey.java
rename to packages/BackupEncryption/src/com/android/server/backup/encryption/storage/TertiaryKey.java
diff --git a/services/backup/java/com/android/server/backup/encryption/storage/TertiaryKeysTable.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/storage/TertiaryKeysTable.java
similarity index 100%
rename from services/backup/java/com/android/server/backup/encryption/storage/TertiaryKeysTable.java
rename to packages/BackupEncryption/src/com/android/server/backup/encryption/storage/TertiaryKeysTable.java
diff --git a/services/backup/java/com/android/server/backup/encryption/tasks/BackupEncrypter.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/BackupEncrypter.java
similarity index 100%
rename from services/backup/java/com/android/server/backup/encryption/tasks/BackupEncrypter.java
rename to packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/BackupEncrypter.java
diff --git a/services/backup/java/com/android/server/backup/encryption/tasks/BackupStreamEncrypter.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/BackupStreamEncrypter.java
similarity index 100%
rename from services/backup/java/com/android/server/backup/encryption/tasks/BackupStreamEncrypter.java
rename to packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/BackupStreamEncrypter.java
diff --git a/services/backup/java/com/android/server/backup/encryption/tasks/DecryptedChunkOutput.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/DecryptedChunkOutput.java
similarity index 100%
rename from services/backup/java/com/android/server/backup/encryption/tasks/DecryptedChunkOutput.java
rename to packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/DecryptedChunkOutput.java
diff --git a/services/backup/java/com/android/server/backup/encryption/tasks/EncryptedRestoreException.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/EncryptedRestoreException.java
similarity index 100%
rename from services/backup/java/com/android/server/backup/encryption/tasks/EncryptedRestoreException.java
rename to packages/BackupEncryption/src/com/android/server/backup/encryption/tasks/EncryptedRestoreException.java
diff --git a/packages/BackupEncryption/test/robolectric/Android.bp b/packages/BackupEncryption/test/robolectric/Android.bp
new file mode 100644
index 0000000..6d1abbb
--- /dev/null
+++ b/packages/BackupEncryption/test/robolectric/Android.bp
@@ -0,0 +1,32 @@
+// Copyright (C) 2018 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+android_robolectric_test {
+    name: "BackupEncryptionRoboTests",
+    srcs: [
+        "src/**/*.java",
+        ":FrameworksServicesRoboShadows",
+    ],
+    java_resource_dirs: ["config"],
+    libs: [
+        "platform-test-annotations",
+        "testng",
+    ],
+    instrumentation_for: "BackupEncryption",
+}
+
+filegroup {
+    name: "BackupEncryptionRoboShadows",
+    srcs: ["src/com/android/server/testing/shadows/**/*.java"],
+}
diff --git a/packages/BackupEncryption/test/robolectric/AndroidManifest.xml b/packages/BackupEncryption/test/robolectric/AndroidManifest.xml
new file mode 100644
index 0000000..ae5cdd9
--- /dev/null
+++ b/packages/BackupEncryption/test/robolectric/AndroidManifest.xml
@@ -0,0 +1,24 @@
+<?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.
+  -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          coreApp="true"
+          package="com.android.server.backup.encryption.robotests">
+
+    <application/>
+
+</manifest>
diff --git a/packages/BackupEncryption/test/robolectric/config/robolectric.properties b/packages/BackupEncryption/test/robolectric/config/robolectric.properties
new file mode 100644
index 0000000..26fceb3
--- /dev/null
+++ b/packages/BackupEncryption/test/robolectric/config/robolectric.properties
@@ -0,0 +1,17 @@
+#
+# 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.
+#
+
+sdk=NEWEST_SDK
diff --git a/services/robotests/backup/src/com/android/server/backup/encryption/chunk/ChunkHashTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunk/ChunkHashTest.java
similarity index 98%
rename from services/robotests/backup/src/com/android/server/backup/encryption/chunk/ChunkHashTest.java
rename to packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunk/ChunkHashTest.java
index 3f57240..c12464c 100644
--- a/services/robotests/backup/src/com/android/server/backup/encryption/chunk/ChunkHashTest.java
+++ b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunk/ChunkHashTest.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.backup.encryption.chunk;
diff --git a/services/robotests/backup/src/com/android/server/backup/encryption/chunk/ChunkListingMapTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunk/ChunkListingMapTest.java
similarity index 100%
rename from services/robotests/backup/src/com/android/server/backup/encryption/chunk/ChunkListingMapTest.java
rename to packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunk/ChunkListingMapTest.java
diff --git a/services/robotests/backup/src/com/android/server/backup/encryption/chunk/ChunkTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunk/ChunkTest.java
similarity index 98%
rename from services/robotests/backup/src/com/android/server/backup/encryption/chunk/ChunkTest.java
rename to packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunk/ChunkTest.java
index 17c9a86..1796f56 100644
--- a/services/robotests/backup/src/com/android/server/backup/encryption/chunk/ChunkTest.java
+++ b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunk/ChunkTest.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.backup.encryption.chunk;
diff --git a/services/robotests/backup/src/com/android/server/backup/encryption/chunk/EncryptedChunkOrderingTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunk/EncryptedChunkOrderingTest.java
similarity index 98%
rename from services/robotests/backup/src/com/android/server/backup/encryption/chunk/EncryptedChunkOrderingTest.java
rename to packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunk/EncryptedChunkOrderingTest.java
index 0bf1417..c6b29b7 100644
--- a/services/robotests/backup/src/com/android/server/backup/encryption/chunk/EncryptedChunkOrderingTest.java
+++ b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunk/EncryptedChunkOrderingTest.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.backup.encryption.chunk;
diff --git a/services/robotests/backup/src/com/android/server/backup/encryption/chunking/ByteRangeTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/ByteRangeTest.java
similarity index 100%
rename from services/robotests/backup/src/com/android/server/backup/encryption/chunking/ByteRangeTest.java
rename to packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/ByteRangeTest.java
diff --git a/services/robotests/backup/src/com/android/server/backup/encryption/chunking/ChunkEncryptorTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/ChunkEncryptorTest.java
similarity index 86%
rename from services/robotests/backup/src/com/android/server/backup/encryption/chunking/ChunkEncryptorTest.java
rename to packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/ChunkEncryptorTest.java
index d0e5fb3..19e3b28 100644
--- a/services/robotests/backup/src/com/android/server/backup/encryption/chunking/ChunkEncryptorTest.java
+++ b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/ChunkEncryptorTest.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.backup.encryption.chunking;
@@ -78,22 +78,22 @@
 
         // Return NONCE_1, then NONCE_2 for invocations of mSecureRandomMock.nextBytes().
         doAnswer(
-                        new Answer<Void>() {
-                            private int mInvocation = 0;
+                    new Answer<Void>() {
+                        private int mInvocation = 0;
 
-                            @Override
-                            public Void answer(InvocationOnMock invocation) {
-                                byte[] nonceDestination = invocation.getArgument(0);
-                                System.arraycopy(
-                                        NONCES[this.mInvocation],
-                                        0,
-                                        nonceDestination,
-                                        0,
-                                        GCM_NONCE_LENGTH_BYTES);
-                                this.mInvocation++;
-                                return null;
-                            }
-                        })
+                        @Override
+                        public Void answer(InvocationOnMock invocation) {
+                            byte[] nonceDestination = invocation.getArgument(0);
+                            System.arraycopy(
+                                    NONCES[this.mInvocation],
+                                    0,
+                                    nonceDestination,
+                                    0,
+                                    GCM_NONCE_LENGTH_BYTES);
+                            this.mInvocation++;
+                            return null;
+                        }
+                    })
                 .when(mSecureRandomMock)
                 .nextBytes(any(byte[].class));
     }
diff --git a/services/robotests/backup/src/com/android/server/backup/encryption/chunking/ChunkHasherTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/ChunkHasherTest.java
similarity index 98%
rename from services/robotests/backup/src/com/android/server/backup/encryption/chunking/ChunkHasherTest.java
rename to packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/ChunkHasherTest.java
index 2bbbf28..72a927d 100644
--- a/services/robotests/backup/src/com/android/server/backup/encryption/chunking/ChunkHasherTest.java
+++ b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/ChunkHasherTest.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.backup.encryption.chunking;
diff --git a/services/robotests/backup/src/com/android/server/backup/encryption/chunking/DecryptedChunkFileOutputTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/DecryptedChunkFileOutputTest.java
similarity index 100%
rename from services/robotests/backup/src/com/android/server/backup/encryption/chunking/DecryptedChunkFileOutputTest.java
rename to packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/DecryptedChunkFileOutputTest.java
diff --git a/services/robotests/backup/src/com/android/server/backup/encryption/chunking/DiffScriptBackupWriterTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/DiffScriptBackupWriterTest.java
similarity index 100%
rename from services/robotests/backup/src/com/android/server/backup/encryption/chunking/DiffScriptBackupWriterTest.java
rename to packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/DiffScriptBackupWriterTest.java
diff --git a/services/robotests/backup/src/com/android/server/backup/encryption/chunking/EncryptedChunkTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/EncryptedChunkTest.java
similarity index 98%
rename from services/robotests/backup/src/com/android/server/backup/encryption/chunking/EncryptedChunkTest.java
rename to packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/EncryptedChunkTest.java
index 8e801a1..325b601 100644
--- a/services/robotests/backup/src/com/android/server/backup/encryption/chunking/EncryptedChunkTest.java
+++ b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/EncryptedChunkTest.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.backup.encryption.chunking;
diff --git a/services/robotests/backup/src/com/android/server/backup/encryption/chunking/InlineLengthsEncryptedChunkEncoderTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/InlineLengthsEncryptedChunkEncoderTest.java
similarity index 98%
rename from services/robotests/backup/src/com/android/server/backup/encryption/chunking/InlineLengthsEncryptedChunkEncoderTest.java
rename to packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/InlineLengthsEncryptedChunkEncoderTest.java
index 2f872be..634acdc 100644
--- a/services/robotests/backup/src/com/android/server/backup/encryption/chunking/InlineLengthsEncryptedChunkEncoderTest.java
+++ b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/InlineLengthsEncryptedChunkEncoderTest.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.backup.encryption.chunking;
diff --git a/services/robotests/backup/src/com/android/server/backup/encryption/chunking/LengthlessEncryptedChunkEncoderTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/LengthlessEncryptedChunkEncoderTest.java
similarity index 98%
rename from services/robotests/backup/src/com/android/server/backup/encryption/chunking/LengthlessEncryptedChunkEncoderTest.java
rename to packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/LengthlessEncryptedChunkEncoderTest.java
index 978bddb..d231603 100644
--- a/services/robotests/backup/src/com/android/server/backup/encryption/chunking/LengthlessEncryptedChunkEncoderTest.java
+++ b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/LengthlessEncryptedChunkEncoderTest.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.backup.encryption.chunking;
diff --git a/services/robotests/backup/src/com/android/server/backup/encryption/chunking/RawBackupWriterTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/RawBackupWriterTest.java
similarity index 98%
rename from services/robotests/backup/src/com/android/server/backup/encryption/chunking/RawBackupWriterTest.java
rename to packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/RawBackupWriterTest.java
index 19ef8fb..966d3e2 100644
--- a/services/robotests/backup/src/com/android/server/backup/encryption/chunking/RawBackupWriterTest.java
+++ b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/RawBackupWriterTest.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.backup.encryption.chunking;
diff --git a/services/robotests/backup/src/com/android/server/backup/encryption/chunking/SingleStreamDiffScriptWriterTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/SingleStreamDiffScriptWriterTest.java
similarity index 100%
rename from services/robotests/backup/src/com/android/server/backup/encryption/chunking/SingleStreamDiffScriptWriterTest.java
rename to packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/SingleStreamDiffScriptWriterTest.java
diff --git a/services/robotests/backup/src/com/android/server/backup/encryption/chunking/cdc/ContentDefinedChunkerTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/cdc/ContentDefinedChunkerTest.java
similarity index 100%
rename from services/robotests/backup/src/com/android/server/backup/encryption/chunking/cdc/ContentDefinedChunkerTest.java
rename to packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/cdc/ContentDefinedChunkerTest.java
diff --git a/services/robotests/backup/src/com/android/server/backup/encryption/chunking/cdc/FingerprintMixerTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/cdc/FingerprintMixerTest.java
similarity index 100%
rename from services/robotests/backup/src/com/android/server/backup/encryption/chunking/cdc/FingerprintMixerTest.java
rename to packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/cdc/FingerprintMixerTest.java
diff --git a/services/robotests/backup/src/com/android/server/backup/encryption/chunking/cdc/HkdfTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/cdc/HkdfTest.java
similarity index 100%
rename from services/robotests/backup/src/com/android/server/backup/encryption/chunking/cdc/HkdfTest.java
rename to packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/cdc/HkdfTest.java
diff --git a/services/robotests/backup/src/com/android/server/backup/encryption/chunking/cdc/IsChunkBreakpointTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/cdc/IsChunkBreakpointTest.java
similarity index 100%
rename from services/robotests/backup/src/com/android/server/backup/encryption/chunking/cdc/IsChunkBreakpointTest.java
rename to packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/cdc/IsChunkBreakpointTest.java
diff --git a/services/robotests/backup/src/com/android/server/backup/encryption/chunking/cdc/RabinFingerprint64Test.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/cdc/RabinFingerprint64Test.java
similarity index 100%
rename from services/robotests/backup/src/com/android/server/backup/encryption/chunking/cdc/RabinFingerprint64Test.java
rename to packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/chunking/cdc/RabinFingerprint64Test.java
diff --git a/services/robotests/backup/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyManagerTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyManagerTest.java
similarity index 100%
rename from services/robotests/backup/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyManagerTest.java
rename to packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyManagerTest.java
diff --git a/services/robotests/backup/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyTest.java
similarity index 100%
rename from services/robotests/backup/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyTest.java
rename to packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/keys/RecoverableKeyStoreSecondaryKeyTest.java
diff --git a/services/robotests/backup/src/com/android/server/backup/encryption/keys/TertiaryKeyGeneratorTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/keys/TertiaryKeyGeneratorTest.java
similarity index 100%
rename from services/robotests/backup/src/com/android/server/backup/encryption/keys/TertiaryKeyGeneratorTest.java
rename to packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/keys/TertiaryKeyGeneratorTest.java
diff --git a/services/robotests/backup/src/com/android/server/backup/encryption/keys/TertiaryKeyRotationTrackerTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/keys/TertiaryKeyRotationTrackerTest.java
similarity index 100%
rename from services/robotests/backup/src/com/android/server/backup/encryption/keys/TertiaryKeyRotationTrackerTest.java
rename to packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/keys/TertiaryKeyRotationTrackerTest.java
diff --git a/services/robotests/backup/src/com/android/server/backup/encryption/storage/BackupEncryptionDbTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/storage/BackupEncryptionDbTest.java
similarity index 100%
rename from services/robotests/backup/src/com/android/server/backup/encryption/storage/BackupEncryptionDbTest.java
rename to packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/storage/BackupEncryptionDbTest.java
diff --git a/services/robotests/backup/src/com/android/server/backup/encryption/storage/TertiaryKeysTableTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/storage/TertiaryKeysTableTest.java
similarity index 100%
rename from services/robotests/backup/src/com/android/server/backup/encryption/storage/TertiaryKeysTableTest.java
rename to packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/storage/TertiaryKeysTableTest.java
diff --git a/services/robotests/backup/src/com/android/server/backup/encryption/tasks/BackupStreamEncrypterTest.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/BackupStreamEncrypterTest.java
similarity index 100%
rename from services/robotests/backup/src/com/android/server/backup/encryption/tasks/BackupStreamEncrypterTest.java
rename to packages/BackupEncryption/test/robolectric/src/com/android/server/backup/encryption/tasks/BackupStreamEncrypterTest.java
diff --git a/services/robotests/backup/src/com/android/server/backup/testing/RandomInputStream.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/backup/testing/RandomInputStream.java
similarity index 100%
rename from services/robotests/backup/src/com/android/server/backup/testing/RandomInputStream.java
rename to packages/BackupEncryption/test/robolectric/src/com/android/server/backup/testing/RandomInputStream.java
diff --git a/services/robotests/backup/src/com/android/server/backup/testing/CryptoTestUtils.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/testing/CryptoTestUtils.java
similarity index 97%
rename from services/robotests/backup/src/com/android/server/backup/testing/CryptoTestUtils.java
rename to packages/BackupEncryption/test/robolectric/src/com/android/server/testing/CryptoTestUtils.java
index 0428796..3f3494d 100644
--- a/services/robotests/backup/src/com/android/server/backup/testing/CryptoTestUtils.java
+++ b/packages/BackupEncryption/test/robolectric/src/com/android/server/testing/CryptoTestUtils.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.backup.testing;
diff --git a/services/robotests/src/com/android/server/testing/shadows/ShadowInternalRecoveryServiceException.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/testing/shadows/ShadowInternalRecoveryServiceException.java
similarity index 100%
rename from services/robotests/src/com/android/server/testing/shadows/ShadowInternalRecoveryServiceException.java
rename to packages/BackupEncryption/test/robolectric/src/com/android/server/testing/shadows/ShadowInternalRecoveryServiceException.java
diff --git a/services/robotests/src/com/android/server/testing/shadows/ShadowRecoveryController.java b/packages/BackupEncryption/test/robolectric/src/com/android/server/testing/shadows/ShadowRecoveryController.java
similarity index 100%
rename from services/robotests/src/com/android/server/testing/shadows/ShadowRecoveryController.java
rename to packages/BackupEncryption/test/robolectric/src/com/android/server/testing/shadows/ShadowRecoveryController.java
diff --git a/packages/CarSystemUI/res/layout/super_status_bar.xml b/packages/CarSystemUI/res/layout/super_status_bar.xml
index 728a239a..e1bcc2e 100644
--- a/packages/CarSystemUI/res/layout/super_status_bar.xml
+++ b/packages/CarSystemUI/res/layout/super_status_bar.xml
@@ -42,6 +42,14 @@
     </com.android.systemui.statusbar.BackDropView>
 
     <com.android.systemui.statusbar.ScrimView
+        android:id="@+id/scrim_for_bubble"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:importantForAccessibility="no"
+        sysui:ignoreRightInset="true"
+    />
+
+    <com.android.systemui.statusbar.ScrimView
         android:id="@+id/scrim_behind"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
diff --git a/packages/CarSystemUI/res/layout/trust_agent_unlock_dialog.xml b/packages/CarSystemUI/res/layout/trust_agent_unlock_dialog.xml
index 2d9901c..9df78f1 100644
--- a/packages/CarSystemUI/res/layout/trust_agent_unlock_dialog.xml
+++ b/packages/CarSystemUI/res/layout/trust_agent_unlock_dialog.xml
@@ -16,12 +16,13 @@
   -->
 
 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/layout"
+    android:id="@+id/unlock_dialog_parent"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:gravity="center">
 
     <LinearLayout
+        android:id="@+id/unlock_dialog"
         android:layout_width="@dimen/unlock_dialog_width"
         android:layout_height="wrap_content"
         android:gravity="center"
diff --git a/packages/CarSystemUI/res/values/integers_car.xml b/packages/CarSystemUI/res/values/integers_car.xml
index 862ba75..fb67b30 100644
--- a/packages/CarSystemUI/res/values/integers_car.xml
+++ b/packages/CarSystemUI/res/values/integers_car.xml
@@ -32,6 +32,6 @@
     at will result in remaining closed the panel if released-->
     <integer name="notification_settle_close_percentage">80</integer>
     <!-- The delay before the unlock dialog pops up -->
-    <integer name="unlock_dialog_delay_ms">3000</integer>
+    <integer name="unlock_dialog_delay_ms">0</integer>
 
 </resources>
diff --git a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIRootComponent.java b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIRootComponent.java
index d6b766b..27146fb 100644
--- a/packages/CarSystemUI/src/com/android/systemui/CarSystemUIRootComponent.java
+++ b/packages/CarSystemUI/src/com/android/systemui/CarSystemUIRootComponent.java
@@ -26,6 +26,7 @@
                 DependencyProvider.class,
                 DependencyBinder.class,
                 ServiceBinder.class,
+                SystemUIBinder.class,
                 SystemUIFactory.ContextHolder.class,
                 SystemUIModule.class,
                 CarSystemUIModule.class
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarTrustAgentUnlockDialogHelper.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarTrustAgentUnlockDialogHelper.java
index 013c63b..ec72ee7 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarTrustAgentUnlockDialogHelper.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarTrustAgentUnlockDialogHelper.java
@@ -52,7 +52,7 @@
      * Not using Dialog because context passed from {@link FullscreenUserSwitcher} is not an
      * activity.
      */
-    private final View mUnlockDialog;
+    private final View mUnlockDialogLayout;
     private final TextView mUnlockingText;
     private final Button mButton;
     private final IntentFilter mFilter;
@@ -70,14 +70,25 @@
         mParams.packageName = mContext.getPackageName();
         mParams.setTitle(mContext.getString(R.string.unlock_dialog_title));
 
-        mUnlockDialog = LayoutInflater.from(mContext).inflate(
+        mUnlockDialogLayout = LayoutInflater.from(mContext).inflate(
             R.layout.trust_agent_unlock_dialog, null);
-        mUnlockDialog.setLayoutParams(mParams);
+        mUnlockDialogLayout.setLayoutParams(mParams);
 
-        mUnlockingText = mUnlockDialog.findViewById(R.id.unlocking_text);
-        mButton = mUnlockDialog.findViewById(R.id.enter_pin_button);
+        View dialogParent = mUnlockDialogLayout.findViewById(R.id.unlock_dialog_parent);
+        dialogParent.setOnTouchListener((v, event)-> {
+            hideUnlockDialog(/* dismissUserSwitcher= */ false);
+            return true;
+        });
+        View unlockDialog = mUnlockDialogLayout.findViewById(R.id.unlock_dialog);
+        unlockDialog.setOnTouchListener((v, event) -> {
+            // If the person taps inside the unlock dialog, the touch event will be intercepted here
+            // and the dialog will not exit
+            return true;
+        });
+        mUnlockingText = mUnlockDialogLayout.findViewById(R.id.unlocking_text);
+        mButton = mUnlockDialogLayout.findViewById(R.id.enter_pin_button);
         mButton.setOnClickListener(v -> {
-            hideUnlockDialog(/* notifyOnHideListener= */true);
+            hideUnlockDialog(/* dismissUserSwitcher= */true);
             // TODO(b/138250105) Stop unlock advertising
         });
 
@@ -133,7 +144,7 @@
                 if (!mUserManager.isUserUnlocked(uid)) {
                     logd("Showed unlock dialog for user: " + uid + " after " + delayMillis
                             + " delay.");
-                    mWindowManager.addView(mUnlockDialog, mParams);
+                    mWindowManager.addView(mUnlockDialogLayout, mParams);
                 }
             }, delayMillis);
         }
@@ -142,22 +153,22 @@
 
     private void setUid(int uid) {
         mUid = uid;
-        TextView userName = mUnlockDialog.findViewById(R.id.user_name);
+        TextView userName = mUnlockDialogLayout.findViewById(R.id.user_name);
         userName.setText(mUserManager.getUserInfo(mUid).name);
-        ImageView avatar = mUnlockDialog.findViewById(R.id.avatar);
+        ImageView avatar = mUnlockDialogLayout.findViewById(R.id.avatar);
         avatar.setImageBitmap(mUserManager.getUserIcon(mUid));
         setButtonText();
     }
 
-    private void hideUnlockDialog(boolean notifyOnHideListener) {
+    private void hideUnlockDialog(boolean dismissUserSwitcher) {
         if (!mIsDialogShowing) {
             return;
         }
-        mWindowManager.removeView(mUnlockDialog);
+        mWindowManager.removeView(mUnlockDialogLayout);
         logd("Receiver unregistered");
         mContext.unregisterReceiver(this);
-        if (notifyOnHideListener && mOnHideListener != null) {
-            mOnHideListener.onHide();
+        if (mOnHideListener != null) {
+            mOnHideListener.onHide(dismissUserSwitcher);
         }
         mIsDialogShowing = false;
     }
@@ -240,6 +251,6 @@
      * Listener used to notify when the dialog is hidden
      */
     interface OnHideListener {
-        void onHide();
+        void onHide(boolean dismissUserSwitcher);
     }
 }
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java
index 7cd6adb..0f7c1ee 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/FullscreenUserSwitcher.java
@@ -34,6 +34,7 @@
 import androidx.recyclerview.widget.GridLayoutManager;
 
 import com.android.systemui.R;
+import com.android.systemui.statusbar.car.CarTrustAgentUnlockDialogHelper.OnHideListener;
 import com.android.systemui.statusbar.car.UserGridRecyclerView.UserRecord;
 
 /**
@@ -116,7 +117,7 @@
         // Show unlock dialog for initial user
         if (hasTrustedDevice(initialUser)) {
             mUnlockDialogHelper.showUnlockDialogAfterDelay(initialUser,
-                    () -> dismissUserSwitcher());
+                    mOnHideListener);
         }
     }
 
@@ -162,7 +163,7 @@
     private void onUserSelected(UserGridRecyclerView.UserRecord record) {
         mSelectedUser = record;
         if (hasTrustedDevice(record.mInfo.id)) {
-            mUnlockDialogHelper.showUnlockDialog(record.mInfo.id, () -> dismissUserSwitcher());
+            mUnlockDialogHelper.showUnlockDialog(record.mInfo.id, mOnHideListener);
             return;
         }
         if (Log.isLoggable(TAG, Log.DEBUG)) {
@@ -202,4 +203,18 @@
     private boolean hasTrustedDevice(int uid) {
         return !mEnrollmentManager.getEnrolledDeviceInfoForUser(uid).isEmpty();
     }
+
+    private OnHideListener mOnHideListener = new OnHideListener() {
+        @Override
+        public void onHide(boolean dismissUserSwitcher) {
+            if (dismissUserSwitcher) {
+                dismissUserSwitcher();
+            } else {
+                // Re-draw the parent view, otherwise the unlock dialog will not be removed from
+                // the screen immediately.
+                mParent.invalidate();
+            }
+
+        }
+    };
 }
diff --git a/packages/SystemUI/docs/plugin_hooks.md b/packages/SystemUI/docs/plugin_hooks.md
index 9fe2e18..2fb0c99 100644
--- a/packages/SystemUI/docs/plugin_hooks.md
+++ b/packages/SystemUI/docs/plugin_hooks.md
@@ -56,6 +56,11 @@
 
 Use: Allows replacement of the keyguard main clock.
 
+### Action: com.android.systemui.action.PLUGIN_NPV
+Expected interface: [NPVPlugin](/packages/SystemUI/plugin/src/com/android/systemui/plugins/NPVPlugin.java)
+
+Use: Attach a view under QQS for prototyping.
+
 # Global plugin dependencies
 These classes can be accessed by any plugin using PluginDependency as long as they @Requires them.
 
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/NPVPlugin.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/NPVPlugin.java
new file mode 100644
index 0000000..1426266
--- /dev/null
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/NPVPlugin.java
@@ -0,0 +1,52 @@
+/*
+ * 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.plugins;
+
+import android.view.View;
+import android.widget.FrameLayout;
+
+import com.android.systemui.plugins.annotations.ProvidesInterface;
+
+/**
+ * Plugin to attach custom views under QQS.
+ *
+ * A parent view is provided to the plugin to which they can add Views.
+ * <br>
+ * The parent is a {@link FrameLayout} with same background as QS and 96dp height.
+ *
+ * {@see NPVPluginManager}
+ * {@see status_bar_expanded_plugin_frame}
+ */
+@ProvidesInterface(action = NPVPlugin.ACTION, version = NPVPlugin.VERSION)
+public interface NPVPlugin extends Plugin {
+    String ACTION = "com.android.systemui.action.PLUGIN_NPV";
+    int VERSION = 1;
+
+    /**
+     * Attach views to the parent.
+     *
+     * @param parent a {@link FrameLayout} to which to attach views. Preferably a root view.
+     * @return a view attached to parent.
+     */
+    View attachToRoot(FrameLayout parent);
+
+    /**
+     * Indicate to the plugin when it is listening (QS expanded)
+     * @param listening
+     */
+    default void setListening(boolean listening) {};
+}
diff --git a/packages/SystemUI/res/layout/auth_biometric_contents.xml b/packages/SystemUI/res/layout/auth_biometric_contents.xml
index aed200f..925e4fa 100644
--- a/packages/SystemUI/res/layout/auth_biometric_contents.xml
+++ b/packages/SystemUI/res/layout/auth_biometric_contents.xml
@@ -56,7 +56,7 @@
         android:scaleType="fitXY" />
 
     <TextView
-        android:id="@+id/error"
+        android:id="@+id/indicator"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:paddingHorizontal="24dp"
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index 7a94008..e7c6b25 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -111,4 +111,6 @@
         android:visibility="invisible"
         android:background="@drawable/qs_navbar_scrim" />
 
+    <include layout="@layout/status_bar_expanded_plugin_frame"/>
+
 </com.android.systemui.statusbar.phone.NotificationPanelView>
diff --git a/packages/SystemUI/res/layout/status_bar_expanded_plugin_frame.xml b/packages/SystemUI/res/layout/status_bar_expanded_plugin_frame.xml
new file mode 100644
index 0000000..4849dfb
--- /dev/null
+++ b/packages/SystemUI/res/layout/status_bar_expanded_plugin_frame.xml
@@ -0,0 +1,29 @@
+<?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.
+-->
+
+<FrameLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/plugin_frame"
+    android:theme="@style/qs_theme"
+    android:layout_width="@dimen/qs_panel_width"
+    android:layout_height="96dp"
+    android:layout_gravity="center_horizontal"
+    android:layout_marginTop="@*android:dimen/quick_qs_total_height"
+    android:layout_marginLeft="@dimen/notification_side_paddings"
+    android:layout_marginRight="@dimen/notification_side_paddings"
+    android:visibility="gone"
+    android:background="@drawable/qs_background_primary"/>
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/ContextComponentHelper.java b/packages/SystemUI/src/com/android/systemui/ContextComponentHelper.java
index 62826d8..5fe5792 100644
--- a/packages/SystemUI/src/com/android/systemui/ContextComponentHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/ContextComponentHelper.java
@@ -24,4 +24,7 @@
 public interface ContextComponentHelper {
     /** Turns a classname into an instance of the class or returns null. */
     Service resolveService(String className);
+
+    /** Turns a classname into an instance of the class or returns null. */
+    SystemUI resolveSystemUI(String className);
 }
diff --git a/packages/SystemUI/src/com/android/systemui/ContextComponentResolver.java b/packages/SystemUI/src/com/android/systemui/ContextComponentResolver.java
index e8f0196..cee21c1 100644
--- a/packages/SystemUI/src/com/android/systemui/ContextComponentResolver.java
+++ b/packages/SystemUI/src/com/android/systemui/ContextComponentResolver.java
@@ -22,17 +22,22 @@
 
 import javax.inject.Inject;
 import javax.inject.Provider;
+import javax.inject.Singleton;
 
 /**
  * Used during Service and Activity instantiation to make them injectable.
  */
+@Singleton
 public class ContextComponentResolver implements ContextComponentHelper {
     private final Map<Class<?>, Provider<Service>> mServiceCreators;
+    private final Map<Class<?>, Provider<SystemUI>> mSystemUICreators;
 
     @Inject
     ContextComponentResolver(
-            Map<Class<?>, Provider<Service>> serviceCreators) {
+            Map<Class<?>, Provider<Service>> serviceCreators,
+            Map<Class<?>, Provider<SystemUI>> systemUICreators) {
         mServiceCreators = serviceCreators;
+        mSystemUICreators = systemUICreators;
     }
 
     /**
@@ -43,6 +48,14 @@
         return resolve(className, mServiceCreators);
     }
 
+    /**
+     * Looks up the SystemUI class name to see if Dagger has an instance of it.
+     */
+    @Override
+    public SystemUI resolveSystemUI(String className) {
+        return resolve(className, mSystemUICreators);
+    }
+
     private <T> T resolve(String className, Map<Class<?>, Provider<T>> creators) {
         for (Map.Entry<Class<?>, Provider<T>> p : creators.entrySet()) {
             if (p.getKey().getName().equals(className)) {
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUI.java b/packages/SystemUI/src/com/android/systemui/SystemUI.java
index 78a1246..30fbef6 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUI.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUI.java
@@ -24,7 +24,6 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.Map;
-import java.util.function.Function;
 
 public abstract class SystemUI implements SysUiServiceProvider {
     public Context mContext;
@@ -62,7 +61,4 @@
 
         n.addExtras(extras);
     }
-
-    public interface Injector extends Function<Context, SystemUI> {
-    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index 1c210b1..56b5d08 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -54,6 +54,8 @@
     public static final String TAG = "SystemUIService";
     private static final boolean DEBUG = false;
 
+    private ContextComponentHelper mComponentHelper;
+
     /**
      * Hold a reference on the stuff we start.
      */
@@ -78,6 +80,8 @@
                 Trace.TRACE_TAG_APP);
         log.traceBegin("DependencyInjection");
         mContextAvailableCallback.onContextAvailable(this);
+        mComponentHelper = SystemUIFactory
+                .getInstance().getRootComponent().getContextComponentHelper();
         log.traceEnd();
 
         // Set the application theme that is inherited by all services. Note that setting the
@@ -186,14 +190,12 @@
             if (DEBUG) Log.d(TAG, "loading: " + clsName);
             log.traceBegin("StartServices" + clsName);
             long ti = System.currentTimeMillis();
-            Class cls;
             try {
-                cls = Class.forName(clsName);
-                Object o = cls.newInstance();
-                if (o instanceof SystemUI.Injector) {
-                    o = ((SystemUI.Injector) o).apply(this);
+                SystemUI obj = mComponentHelper.resolveSystemUI(clsName);
+                if (obj == null) {
+                    obj = (SystemUI) Class.forName(clsName).newInstance();
                 }
-                mServices[i] = (SystemUI) o;
+                mServices[i] = obj;
             } catch (ClassNotFoundException ex) {
                 throw new RuntimeException(ex);
             } catch (IllegalAccessException ex) {
@@ -211,7 +213,7 @@
             // Warn if initialization of component takes too long
             ti = System.currentTimeMillis() - ti;
             if (ti > 1000) {
-                Log.w(TAG, "Initialization of " + cls.getName() + " took " + ti + " ms");
+                Log.w(TAG, "Initialization of " + clsName + " took " + ti + " ms");
             }
             if (mBootCompleted) {
                 mServices[i].onBootCompleted();
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIBinder.java b/packages/SystemUI/src/com/android/systemui/SystemUIBinder.java
new file mode 100644
index 0000000..88d65903
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIBinder.java
@@ -0,0 +1,36 @@
+/*
+ * 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;
+
+import com.android.systemui.keyguard.KeyguardViewMediator;
+
+import dagger.Binds;
+import dagger.Module;
+import dagger.multibindings.ClassKey;
+import dagger.multibindings.IntoMap;
+
+/**
+ * Services and Activities that are injectable should go here.
+ */
+@Module
+public abstract class SystemUIBinder {
+    /** Inject into KeyguardViewMediator. */
+    @Binds
+    @IntoMap
+    @ClassKey(KeyguardViewMediator.class)
+    public abstract SystemUI bindKeyguardViewMediator(KeyguardViewMediator sysui);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIRootComponent.java b/packages/SystemUI/src/com/android/systemui/SystemUIRootComponent.java
index b77667e..6ce673b 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIRootComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIRootComponent.java
@@ -38,10 +38,18 @@
         DependencyProvider.class,
         DependencyBinder.class,
         ServiceBinder.class,
+        SystemUIBinder.class,
         SystemUIFactory.ContextHolder.class,
         SystemUIModule.class,
         SystemUIDefaultModule.class})
 public interface SystemUIRootComponent {
+
+    /**
+     * Creates a GarbageMonitor.
+     */
+    @Singleton
+    ContextComponentHelper getContextComponentHelper();
+
     /**
      * Main dependency providing module.
      */
@@ -66,7 +74,7 @@
     InjectionInflationController.ViewCreator createViewCreator();
 
     /**
-     * Creatse a GarbageMonitor.
+     * Creates a GarbageMonitor.
      */
     @Singleton
     GarbageMonitor createGarbageMonitor();
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceView.java
index 74cc9c3..a68b61e 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricFaceView.java
@@ -154,18 +154,18 @@
 
     @Override
     protected void handleResetAfterError() {
-        resetErrorView(mContext, mErrorView);
+        resetErrorView(mContext, mIndicatorView);
     }
 
     @Override
     protected void handleResetAfterHelp() {
-        resetErrorView(mContext, mErrorView);
+        resetErrorView(mContext, mIndicatorView);
     }
 
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
-        mIconController = new IconController(mContext, mIconView, mErrorView);
+        mIconController = new IconController(mContext, mIconView, mIndicatorView);
     }
 
     @Override
@@ -174,7 +174,7 @@
 
         if (newState == STATE_AUTHENTICATING_ANIMATING_IN ||
                 (newState == STATE_AUTHENTICATING && mSize == AuthDialog.SIZE_MEDIUM)) {
-            resetErrorView(mContext, mErrorView);
+            resetErrorView(mContext, mIndicatorView);
         }
 
         // Do this last since the state variable gets updated.
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
index f260839..df14a17 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthBiometricView.java
@@ -21,6 +21,8 @@
 import android.animation.AnimatorSet;
 import android.animation.ValueAnimator;
 import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.Context;
 import android.hardware.biometrics.BiometricPrompt;
 import android.os.Bundle;
@@ -127,8 +129,8 @@
             return mBiometricView.findViewById(R.id.description);
         }
 
-        public TextView getErrorView() {
-            return mBiometricView.findViewById(R.id.error);
+        public TextView getIndicatorView() {
+            return mBiometricView.findViewById(R.id.indicator);
         }
 
         public ImageView getIconView() {
@@ -150,7 +152,7 @@
     private TextView mSubtitleView;
     private TextView mDescriptionView;
     protected ImageView mIconView;
-    @VisibleForTesting protected TextView mErrorView;
+    @VisibleForTesting protected TextView mIndicatorView;
     @VisibleForTesting Button mNegativeButton;
     @VisibleForTesting Button mPositiveButton;
     @VisibleForTesting Button mTryAgainButton;
@@ -165,6 +167,7 @@
     private float mIconOriginalY;
 
     protected boolean mDialogSizeAnimating;
+    protected Bundle mSavedState;
 
     /**
      * Delay after authentication is confirmed, before the dialog should be animated away.
@@ -252,7 +255,7 @@
             mTitleView.setVisibility(View.GONE);
             mSubtitleView.setVisibility(View.GONE);
             mDescriptionView.setVisibility(View.GONE);
-            mErrorView.setVisibility(View.GONE);
+            mIndicatorView.setVisibility(View.GONE);
             mNegativeButton.setVisibility(View.GONE);
 
             final float iconPadding = getResources()
@@ -284,7 +287,7 @@
                 final float opacity = (float) animation.getAnimatedValue();
 
                 mTitleView.setAlpha(opacity);
-                mErrorView.setAlpha(opacity);
+                mIndicatorView.setAlpha(opacity);
                 mNegativeButton.setAlpha(opacity);
                 mTryAgainButton.setAlpha(opacity);
 
@@ -304,7 +307,7 @@
                 public void onAnimationStart(Animator animation) {
                     super.onAnimationStart(animation);
                     mTitleView.setVisibility(View.VISIBLE);
-                    mErrorView.setVisibility(View.VISIBLE);
+                    mIndicatorView.setVisibility(View.VISIBLE);
                     mNegativeButton.setVisibility(View.VISIBLE);
                     mTryAgainButton.setVisibility(View.VISIBLE);
 
@@ -339,6 +342,7 @@
 
     public void updateState(@BiometricState int newState) {
         Log.v(TAG, "newState: " + newState);
+
         switch (newState) {
             case STATE_AUTHENTICATING_ANIMATING_IN:
             case STATE_AUTHENTICATING:
@@ -353,7 +357,7 @@
                 if (mSize != AuthDialog.SIZE_SMALL) {
                     mPositiveButton.setVisibility(View.GONE);
                     mNegativeButton.setVisibility(View.GONE);
-                    mErrorView.setVisibility(View.INVISIBLE);
+                    mIndicatorView.setVisibility(View.INVISIBLE);
                 }
                 mHandler.postDelayed(() -> mCallback.onAction(Callback.ACTION_AUTHENTICATED),
                         getDelayAfterAuthenticatedDurationMs());
@@ -364,9 +368,10 @@
                 mNegativeButton.setText(R.string.cancel);
                 mNegativeButton.setContentDescription(getResources().getString(R.string.cancel));
                 mPositiveButton.setEnabled(true);
-                mErrorView.setTextColor(mTextColorHint);
-                mErrorView.setText(R.string.biometric_dialog_tap_confirm);
-                mErrorView.setVisibility(View.VISIBLE);
+                mPositiveButton.setVisibility(View.VISIBLE);
+                mIndicatorView.setTextColor(mTextColorHint);
+                mIndicatorView.setText(R.string.biometric_dialog_tap_confirm);
+                mIndicatorView.setVisibility(View.VISIBLE);
                 break;
 
             case STATE_ERROR:
@@ -409,6 +414,27 @@
         updateState(STATE_HELP);
     }
 
+    public void onSaveState(@NonNull Bundle outState) {
+        outState.putInt(AuthDialog.KEY_BIOMETRIC_TRY_AGAIN_VISIBILITY,
+                mTryAgainButton.getVisibility());
+        outState.putInt(AuthDialog.KEY_BIOMETRIC_STATE, mState);
+        outState.putString(AuthDialog.KEY_BIOMETRIC_INDICATOR_STRING,
+                mIndicatorView.getText().toString());
+        outState.putBoolean(AuthDialog.KEY_BIOMETRIC_INDICATOR_ERROR_SHOWING,
+                mHandler.hasCallbacks(mResetErrorRunnable));
+        outState.putBoolean(AuthDialog.KEY_BIOMETRIC_INDICATOR_HELP_SHOWING,
+                mHandler.hasCallbacks(mResetHelpRunnable));
+        outState.putInt(AuthDialog.KEY_BIOMETRIC_DIALOG_SIZE, mSize);
+    }
+
+    /**
+     * Invoked after inflation but before being attached to window.
+     * @param savedState
+     */
+    public void restoreState(@Nullable Bundle savedState) {
+        mSavedState = savedState;
+    }
+
     private void setTextOrHide(TextView view, String string) {
         if (TextUtils.isEmpty(string)) {
             view.setVisibility(View.GONE);
@@ -429,25 +455,28 @@
 
     private void showTemporaryMessage(String message, Runnable resetMessageRunnable) {
         removePendingAnimations();
-        mErrorView.setText(message);
-        mErrorView.setTextColor(mTextColorError);
-        mErrorView.setVisibility(View.VISIBLE);
+        mIndicatorView.setText(message);
+        mIndicatorView.setTextColor(mTextColorError);
+        mIndicatorView.setVisibility(View.VISIBLE);
         mHandler.postDelayed(resetMessageRunnable, BiometricPrompt.HIDE_DIALOG_DELAY);
     }
 
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
-        initializeViews();
+        onFinishInflateInternal();
     }
 
+    /**
+     * After inflation, but before things like restoreState, onAttachedToWindow, etc.
+     */
     @VisibleForTesting
-    void initializeViews() {
+    void onFinishInflateInternal() {
         mTitleView = mInjector.getTitleView();
         mSubtitleView = mInjector.getSubtitleView();
         mDescriptionView = mInjector.getDescriptionView();
         mIconView = mInjector.getIconView();
-        mErrorView = mInjector.getErrorView();
+        mIndicatorView = mInjector.getIndicatorView();
         mNegativeButton = mInjector.getNegativeButton();
         mPositiveButton = mInjector.getPositiveButton();
         mTryAgainButton = mInjector.getTryAgainButton();
@@ -474,13 +503,49 @@
     @Override
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
+        onAttachedToWindowInternal();
+    }
+
+    /**
+     * Contains all the testable logic that should be invoked when {@link #onAttachedToWindow()} is
+     * invoked.
+     */
+    @VisibleForTesting
+    void onAttachedToWindowInternal() {
         setText(mTitleView, mBundle.getString(BiometricPrompt.KEY_TITLE));
         setText(mNegativeButton, mBundle.getString(BiometricPrompt.KEY_NEGATIVE_TEXT));
 
         setTextOrHide(mSubtitleView, mBundle.getString(BiometricPrompt.KEY_SUBTITLE));
         setTextOrHide(mDescriptionView, mBundle.getString(BiometricPrompt.KEY_DESCRIPTION));
 
-        updateState(STATE_AUTHENTICATING_ANIMATING_IN);
+        if (mSavedState == null) {
+            updateState(STATE_AUTHENTICATING_ANIMATING_IN);
+        } else {
+            // Restore as much state as possible first
+            updateState(mSavedState.getInt(AuthDialog.KEY_BIOMETRIC_STATE));
+
+            // Restore positive button state
+            mTryAgainButton.setVisibility(
+                    mSavedState.getInt(AuthDialog.KEY_BIOMETRIC_TRY_AGAIN_VISIBILITY));
+
+            // Restore indicator text state
+            final String indicatorText =
+                    mSavedState.getString(AuthDialog.KEY_BIOMETRIC_INDICATOR_STRING);
+            if (mSavedState.getBoolean(AuthDialog.KEY_BIOMETRIC_INDICATOR_HELP_SHOWING)) {
+                onHelp(indicatorText);
+            } else if (mSavedState.getBoolean(AuthDialog.KEY_BIOMETRIC_INDICATOR_ERROR_SHOWING)) {
+                onAuthenticationFailed(indicatorText);
+            }
+        }
+    }
+
+    @Override
+    protected void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+
+        // Empty the handler, otherwise things like ACTION_AUTHENTICATED may be duplicated once
+        // the new dialog is restored.
+        mHandler.removeCallbacksAndMessages(null /* all */);
     }
 
     @Override
@@ -521,13 +586,24 @@
     @Override
     public void onLayout(boolean changed, int left, int top, int right, int bottom) {
         super.onLayout(changed, left, top, right, bottom);
+        onLayoutInternal();
+    }
 
+    /**
+     * Contains all the testable logic that should be invoked when
+     * {@link #onLayout(boolean, int, int, int, int)}, is invoked.
+     */
+    @VisibleForTesting
+    void onLayoutInternal() {
         // Start with initial size only once. Subsequent layout changes don't matter since we
         // only care about the initial icon position.
         if (mIconOriginalY == 0) {
             mIconOriginalY = mIconView.getY();
-            updateSize(mRequireConfirmation ? AuthDialog.SIZE_MEDIUM
-                    : AuthDialog.SIZE_SMALL);
+            if (mSavedState == null) {
+                updateSize(mRequireConfirmation ? AuthDialog.SIZE_MEDIUM : AuthDialog.SIZE_SMALL);
+            } else {
+                updateSize(mSavedState.getInt(AuthDialog.KEY_BIOMETRIC_DIALOG_SIZE));
+            }
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index 9cb5fcf..e198060 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -17,6 +17,8 @@
 package com.android.systemui.biometrics;
 
 import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.content.Context;
 import android.graphics.PixelFormat;
 import android.os.Binder;
@@ -269,7 +271,8 @@
     }
 
     @Override
-    public void show(WindowManager wm) {
+    public void show(WindowManager wm, @Nullable Bundle savedState) {
+        mBiometricView.restoreState(savedState);
         wm.addView(this, getLayoutParams(mWindowToken));
     }
 
@@ -308,13 +311,8 @@
     }
 
     @Override
-    public void onSaveState(Bundle outState) {
-
-    }
-
-    @Override
-    public void restoreState(Bundle savedState) {
-
+    public void onSaveState(@NonNull Bundle outState) {
+        mBiometricView.onSaveState(outState);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index dcd01c6..ab89034 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -272,11 +272,7 @@
                     + " type: " + type);
         }
 
-        if (savedState != null) {
-            // SavedState is only non-null if it's from onConfigurationChanged. Restore the state
-            // even though it may be removed / re-created again
-            newDialog.restoreState(savedState);
-        } else if (mCurrentDialog != null) {
+        if (mCurrentDialog != null) {
             // If somehow we're asked to show a dialog, the old one doesn't need to be animated
             // away. This can happen if the app cancels and re-starts auth during configuration
             // change. This is ugly because we also have to do things on onConfigurationChanged
@@ -286,7 +282,7 @@
 
         mReceiver = (IBiometricServiceReceiverInternal) args.arg2;
         mCurrentDialog = newDialog;
-        mCurrentDialog.show(mWindowManager);
+        mCurrentDialog.show(mWindowManager, savedState);
     }
 
     private void onDialogDismissed(@DismissedReason int reason) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialog.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialog.java
index a6a857ca..fb904231 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialog.java
@@ -17,7 +17,8 @@
 package com.android.systemui.biometrics;
 
 import android.annotation.IntDef;
-import android.hardware.biometrics.BiometricPrompt;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.os.Bundle;
 import android.view.WindowManager;
 
@@ -28,28 +29,12 @@
  * Interface for the biometric dialog UI.
  */
 public interface AuthDialog {
-
-    // TODO: Clean up save/restore state
-    String[] KEYS_TO_BACKUP = {
-            BiometricPrompt.KEY_TITLE,
-            BiometricPrompt.KEY_USE_DEFAULT_TITLE,
-            BiometricPrompt.KEY_SUBTITLE,
-            BiometricPrompt.KEY_DESCRIPTION,
-            BiometricPrompt.KEY_POSITIVE_TEXT,
-            BiometricPrompt.KEY_NEGATIVE_TEXT,
-            BiometricPrompt.KEY_REQUIRE_CONFIRMATION,
-            BiometricPrompt.KEY_ALLOW_DEVICE_CREDENTIAL,
-            BiometricPrompt.KEY_FROM_CONFIRM_DEVICE_CREDENTIAL,
-
-            BiometricDialogView.KEY_TRY_AGAIN_VISIBILITY,
-            BiometricDialogView.KEY_CONFIRM_VISIBILITY,
-            BiometricDialogView.KEY_CONFIRM_ENABLED,
-            BiometricDialogView.KEY_STATE,
-            BiometricDialogView.KEY_ERROR_TEXT_VISIBILITY,
-            BiometricDialogView.KEY_ERROR_TEXT_STRING,
-            BiometricDialogView.KEY_ERROR_TEXT_IS_TEMPORARY,
-            BiometricDialogView.KEY_ERROR_TEXT_COLOR,
-    };
+    String KEY_BIOMETRIC_TRY_AGAIN_VISIBILITY = "try_agian_visibility";
+    String KEY_BIOMETRIC_STATE = "state";
+    String KEY_BIOMETRIC_INDICATOR_STRING = "indicator_string"; // error / help / hint
+    String KEY_BIOMETRIC_INDICATOR_ERROR_SHOWING = "error_is_temporary";
+    String KEY_BIOMETRIC_INDICATOR_HELP_SHOWING = "hint_is_temporary";
+    String KEY_BIOMETRIC_DIALOG_SIZE = "size";
 
     int SIZE_UNKNOWN = 0;
     int SIZE_SMALL = 1;
@@ -68,7 +53,7 @@
      * Show the dialog.
      * @param wm
      */
-    void show(WindowManager wm);
+    void show(WindowManager wm, @Nullable Bundle savedState);
 
     /**
      * Dismiss the dialog without sending a callback.
@@ -107,13 +92,7 @@
      * Save the current state.
      * @param outState
      */
-    void onSaveState(Bundle outState);
-
-    /**
-     * Restore a previous state.
-     * @param savedState
-     */
-    void restoreState(Bundle savedState);
+    void onSaveState(@NonNull Bundle outState);
 
     /**
      * Get the client's package name
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java
index 89d08d7..b985e1c 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/BiometricDialogView.java
@@ -23,6 +23,7 @@
 import android.animation.AnimatorSet;
 import android.animation.ValueAnimator;
 import android.annotation.IntDef;
+import android.annotation.Nullable;
 import android.app.admin.DevicePolicyManager;
 import android.content.Context;
 import android.graphics.Outline;
@@ -738,7 +739,10 @@
     }
 
     @Override
-    public void show(WindowManager wm) {
+    public void show(WindowManager wm, @Nullable Bundle savedState) {
+        if (savedState != null) {
+            restoreState(savedState);
+        }
         wm.addView(this, getLayoutParams(mWindowToken));
     }
 
@@ -832,7 +836,6 @@
         bundle.putInt(KEY_DIALOG_SIZE, mSize);
     }
 
-    @Override
     public void restoreState(Bundle bundle) {
         mRestoredState = bundle;
 
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index 13d6470..cc250b4 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -168,7 +168,7 @@
      * Callback to run after the flyout hides. Also called if a new flyout is shown before the
      * previous one animates out.
      */
-    private Runnable mAfterFlyoutHides;
+    private Runnable mFlyoutOnHide;
 
     /** Layout change listener that moves the stack to the nearest valid position on rotation. */
     private OnLayoutChangeListener mOrientationChangedListener;
@@ -1366,111 +1366,106 @@
     @VisibleForTesting
     void animateInFlyoutForBubble(Bubble bubble) {
         final CharSequence updateMessage = bubble.getUpdateMessage(getContext());
-
         if (!bubble.showFlyoutForBubble()) {
             // In case flyout was suppressed for this update, reset now.
             bubble.setSuppressFlyout(false);
             return;
         }
-
         if (updateMessage == null
                 || isExpanded()
                 || mIsExpansionAnimating
                 || mIsGestureInProgress
-                || mBubbleToExpandAfterFlyoutCollapse != null) {
+                || mBubbleToExpandAfterFlyoutCollapse != null
+                || bubble.getIconView() == null) {
             // Skip the message if none exists, we're expanded or animating expansion, or we're
-            // about to expand a bubble from the previous tapped flyout.
+            // about to expand a bubble from the previous tapped flyout, or if bubble view is null.
             return;
         }
-
-        if (bubble.getIconView() != null) {
-            // Temporarily suppress the dot while the flyout is visible.
-            bubble.getIconView().setSuppressDot(
-                    true /* suppressDot */, false /* animate */);
-
-            mFlyout.removeCallbacks(mAnimateInFlyout);
-            mFlyoutDragDeltaX = 0f;
-
-            if (mAfterFlyoutHides != null) {
-                mAfterFlyoutHides.run();
+        mFlyoutDragDeltaX = 0f;
+        clearFlyoutOnHide();
+        mFlyoutOnHide = () -> {
+            resetDot(bubble);
+            if (mBubbleToExpandAfterFlyoutCollapse == null) {
+                return;
             }
+            mBubbleData.setSelectedBubble(mBubbleToExpandAfterFlyoutCollapse);
+            mBubbleData.setExpanded(true);
+            mBubbleToExpandAfterFlyoutCollapse = null;
+        };
+        mFlyout.setVisibility(INVISIBLE);
 
-            mAfterFlyoutHides = () -> {
-                final boolean suppressDot = !bubble.showBubbleDot();
-                // If we're going to suppress the dot, make it visible first so it'll
-                // visibly animate away.
-                if (suppressDot) {
-                    bubble.getIconView().setSuppressDot(
-                            false /* suppressDot */, false /* animate */);
-                }
-                // Reset dot suppression. If we're not suppressing due to DND, then
-                // stop suppressing it with no animation (since the flyout has
-                // transformed into the dot). If we are suppressing due to DND, animate
-                // it away.
-                bubble.getIconView().setSuppressDot(
-                        suppressDot /* suppressDot */,
-                        suppressDot /* animate */);
+        // Temporarily suppress the dot while the flyout is visible.
+        bubble.getIconView().setSuppressDot(
+                true /* suppressDot */, false /* animate */);
 
-                if (mBubbleToExpandAfterFlyoutCollapse != null) {
-                    mBubbleData.setSelectedBubble(mBubbleToExpandAfterFlyoutCollapse);
-                    mBubbleData.setExpanded(true);
-                    mBubbleToExpandAfterFlyoutCollapse = null;
-                }
-            };
-
-            mFlyout.setVisibility(INVISIBLE);
-
-            // Post in case layout isn't complete and getWidth returns 0.
-            post(() -> {
-                // An auto-expanding bubble could have been posted during the time it takes to
-                // layout.
-                if (isExpanded()) {
-                    return;
-                }
-
-                final Runnable afterShow = () -> {
-                    mAnimateInFlyout = () -> {
-                        mFlyout.setVisibility(VISIBLE);
-                        bubble.getIconView().setSuppressDot(
-                                true /* suppressDot */, false /* animate */);
-                        mFlyoutDragDeltaX =
-                                mStackAnimationController.isStackOnLeftSide()
-                                        ? -mFlyout.getWidth()
-                                        : mFlyout.getWidth();
-                        animateFlyoutCollapsed(false /* collapsed */, 0 /* velX */);
-                        mFlyout.postDelayed(mHideFlyout, FLYOUT_HIDE_AFTER);
-                    };
-
-                    mFlyout.postDelayed(mAnimateInFlyout, 200);
+        // Start flyout expansion. Post in case layout isn't complete and getWidth returns 0.
+        post(() -> {
+            // An auto-expanding bubble could have been posted during the time it takes to
+            // layout.
+            if (isExpanded()) {
+                return;
+            }
+            final Runnable expandFlyoutAfterDelay = () -> {
+                mAnimateInFlyout = () -> {
+                    mFlyout.setVisibility(VISIBLE);
+                    mFlyoutDragDeltaX =
+                            mStackAnimationController.isStackOnLeftSide()
+                                    ? -mFlyout.getWidth()
+                                    : mFlyout.getWidth();
+                    animateFlyoutCollapsed(false /* collapsed */, 0 /* velX */);
+                    mFlyout.postDelayed(mHideFlyout, FLYOUT_HIDE_AFTER);
                 };
-
-                mFlyout.setupFlyoutStartingAsDot(
-                        updateMessage, mStackAnimationController.getStackPosition(), getWidth(),
-                        mStackAnimationController.isStackOnLeftSide(),
-                        bubble.getIconView().getBadgeColor(),
-                        afterShow,
-                        mAfterFlyoutHides,
-                        bubble.getIconView().getDotCenter());
-                mFlyout.bringToFront();
-            });
-        }
-
+                mFlyout.postDelayed(mAnimateInFlyout, 200);
+            };
+            mFlyout.setupFlyoutStartingAsDot(
+                    updateMessage, mStackAnimationController.getStackPosition(), getWidth(),
+                    mStackAnimationController.isStackOnLeftSide(),
+                    bubble.getIconView().getBadgeColor() /* dotColor */,
+                    expandFlyoutAfterDelay /* onLayoutComplete */,
+                    mFlyoutOnHide,
+                    bubble.getIconView().getDotCenter());
+            mFlyout.bringToFront();
+        });
         mFlyout.removeCallbacks(mHideFlyout);
         mFlyout.postDelayed(mHideFlyout, FLYOUT_HIDE_AFTER);
         logBubbleEvent(bubble, StatsLog.BUBBLE_UICHANGED__ACTION__FLYOUT);
     }
 
+    private void resetDot(Bubble bubble) {
+        final boolean suppressDot = !bubble.showBubbleDot();
+        // If we're going to suppress the dot, make it visible first so it'll
+        // visibly animate away.
+
+        if (suppressDot) {
+            bubble.getIconView().setSuppressDot(
+                    false /* suppressDot */, false /* animate */);
+        }
+        // Reset dot suppression. If we're not suppressing due to DND, then
+        // stop suppressing it with no animation (since the flyout has
+        // transformed into the dot). If we are suppressing due to DND, animate
+        // it away.
+        bubble.getIconView().setSuppressDot(
+                suppressDot /* suppressDot */,
+                suppressDot /* animate */);
+    }
+
     /** Hide the flyout immediately and cancel any pending hide runnables. */
     private void hideFlyoutImmediate() {
-        if (mAfterFlyoutHides != null) {
-            mAfterFlyoutHides.run();
-        }
-
+        clearFlyoutOnHide();
         mFlyout.removeCallbacks(mAnimateInFlyout);
         mFlyout.removeCallbacks(mHideFlyout);
         mFlyout.hideFlyout();
     }
 
+    private void clearFlyoutOnHide() {
+        mFlyout.removeCallbacks(mAnimateInFlyout);
+        if (mFlyoutOnHide == null) {
+            return;
+        }
+        mFlyoutOnHide.run();
+        mFlyoutOnHide = null;
+    }
+
     @Override
     public void getBoundsOnScreen(Rect outRect) {
         if (!mIsExpanded) {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
index 603c416..4512aa8 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
@@ -61,7 +61,7 @@
     // mBubbleIconFactory cannot be static because it depends on Context.
     private BubbleIconFactory mBubbleIconFactory;
 
-    private boolean mSuppressDot = false;
+    private boolean mSuppressDot;
 
     private Bubble mBubble;
 
@@ -140,6 +140,7 @@
     public void setAppIcon(Drawable appIcon) {
         mUserBadgedAppIcon = appIcon;
     }
+
     /**
      * @return the {@link ExpandableNotificationRow} view to display notification content when the
      * bubble is expanded.
@@ -154,7 +155,6 @@
         updateDotVisibility(animate, null /* after */);
     }
 
-
     /**
      * Sets whether or not to hide the dot even if we'd otherwise show it. This is used while the
      * flyout is visible or animating, to hide the dot until the flyout visually transforms into it.
@@ -166,7 +166,7 @@
 
     /** Sets the position of the 'new' dot, animating it out and back in if requested. */
     void setDotPosition(boolean onLeft, boolean animate) {
-        if (animate && onLeft != mBadgedImageView.getDotOnLeft() && !mSuppressDot) {
+        if (animate && onLeft != mBadgedImageView.getDotOnLeft() && shouldShowDot()) {
             animateDot(false /* showDot */, () -> {
                 mBadgedImageView.setDotOnLeft(onLeft);
                 animateDot(true /* showDot */, null);
@@ -190,12 +190,12 @@
      * after animation if requested.
      */
     private void updateDotVisibility(boolean animate, Runnable after) {
-        boolean showDot = mBubble.showBubbleDot() && !mSuppressDot;
-
+        final boolean showDot = shouldShowDot();
         if (animate) {
             animateDot(showDot, after);
         } else {
             mBadgedImageView.setShowDot(showDot);
+            mBadgedImageView.setDotScale(showDot ? 1f : 0f);
         }
     }
 
@@ -203,27 +203,25 @@
      * Animates the badge to show or hide.
      */
     private void animateDot(boolean showDot, Runnable after) {
-        if (mBadgedImageView.isShowingDot() != showDot) {
-            if (showDot) {
-                mBadgedImageView.setShowDot(true);
-            }
-            mBadgedImageView.clearAnimation();
-            mBadgedImageView.animate().setDuration(200)
-                    .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
-                    .setUpdateListener((valueAnimator) -> {
-                        float fraction = valueAnimator.getAnimatedFraction();
-                        fraction = showDot ? fraction : 1f - fraction;
-                        mBadgedImageView.setDotScale(fraction);
-                    }).withEndAction(() -> {
-                        if (!showDot) {
-                            mBadgedImageView.setShowDot(false);
-                        }
-
-                        if (after != null) {
-                            after.run();
-                        }
-            }).start();
+        if (mBadgedImageView.isShowingDot() == showDot) {
+            return;
         }
+        // Do NOT wait until after animation ends to setShowDot
+        // to avoid overriding more recent showDot states.
+        mBadgedImageView.setShowDot(showDot);
+        mBadgedImageView.clearAnimation();
+        mBadgedImageView.animate().setDuration(200)
+                .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
+                .setUpdateListener((valueAnimator) -> {
+                    float fraction = valueAnimator.getAnimatedFraction();
+                    fraction = showDot ? fraction : 1f - fraction;
+                    mBadgedImageView.setDotScale(fraction);
+                }).withEndAction(() -> {
+            mBadgedImageView.setDotScale(showDot ? 1f : 0f);
+            if (after != null) {
+                after.run();
+            }
+        }).start();
     }
 
     void updateViews() {
@@ -273,7 +271,11 @@
         iconPath.transform(matrix);
         mBadgedImageView.drawDot(iconPath);
 
-        animateDot(mBubble.showBubbleDot() /* showDot */, null /* after */);
+        animateDot(shouldShowDot(), null /* after */);
+    }
+
+    boolean shouldShowDot() {
+        return mBubble.showBubbleDot() && !mSuppressDot;
     }
 
     int getBadgeColor() {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index e696880..d2a9c75 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -99,6 +99,8 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 
+import javax.inject.Inject;
+
 /**
  * Mediates requests related to the keyguard.  This includes queries about the
  * state of the keyguard, power management events that effect whether the keyguard
@@ -216,6 +218,7 @@
     private boolean mBootSendUserPresent;
     private boolean mShuttingDown;
     private boolean mDozing;
+    private final FalsingManager mFalsingManager;
 
     /** High level access to the power manager for WakeLocks */
     private PowerManager mPM;
@@ -678,6 +681,13 @@
         }
     };
 
+    @Inject
+    public KeyguardViewMediator(FalsingManager falsingManager) {
+        super();
+
+        mFalsingManager = falsingManager;
+    }
+
     public void userActivity() {
         mPM.userActivity(SystemClock.uptimeMillis(), false);
     }
@@ -1603,7 +1613,7 @@
                     Trace.beginSection("KeyguardViewMediator#handleMessage START_KEYGUARD_EXIT_ANIM");
                     StartKeyguardExitAnimParams params = (StartKeyguardExitAnimParams) msg.obj;
                     handleStartKeyguardExitAnimation(params.startTime, params.fadeoutDuration);
-                    Dependency.get(FalsingManager.class).onSucccessfulUnlock();
+                    mFalsingManager.onSucccessfulUnlock();
                     Trace.endSection();
                     break;
                 case KEYGUARD_DONE_PENDING_TIMEOUT:
@@ -2075,11 +2085,10 @@
     public StatusBarKeyguardViewManager registerStatusBar(StatusBar statusBar,
             ViewGroup container, NotificationPanelView panelView,
             BiometricUnlockController biometricUnlockController, ViewGroup lockIconContainer,
-            View notificationContainer, KeyguardBypassController bypassController,
-            FalsingManager falsingManager) {
+            View notificationContainer, KeyguardBypassController bypassController) {
         mStatusBarKeyguardViewManager.registerStatusBar(statusBar, container, panelView,
                 biometricUnlockController, mDismissCallbackRegistry, lockIconContainer,
-                notificationContainer, bypassController, falsingManager);
+                notificationContainer, bypassController, mFalsingManager);
         return mStatusBarKeyguardViewManager;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java b/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java
new file mode 100644
index 0000000..30be775
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipBoundsHandler.java
@@ -0,0 +1,375 @@
+/*
+ * 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.pip;
+
+import static android.util.TypedValue.COMPLEX_UNIT_DIP;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.os.RemoteException;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.util.Size;
+import android.util.TypedValue;
+import android.view.DisplayInfo;
+import android.view.Gravity;
+import android.view.IPinnedStackController;
+import android.view.IWindowManager;
+import android.view.WindowManagerGlobal;
+
+import com.android.internal.policy.PipSnapAlgorithm;
+
+import java.io.PrintWriter;
+
+/**
+ * Handles bounds calculation for PIP on Phone and other form factors, it keeps tracking variant
+ * state changes originated from Window Manager and is the source of truth for PiP window bounds.
+ */
+public class PipBoundsHandler {
+
+    private static final String TAG = PipBoundsHandler.class.getSimpleName();
+    private static final float INVALID_SNAP_FRACTION = -1f;
+
+    // System.identityHashCode guarantees zero for null object.
+    private static final int INVALID_SYSTEM_IDENTITY_TOKEN = 0;
+
+    private final Context mContext;
+    private final IWindowManager mWindowManager;
+    private final PipSnapAlgorithm mSnapAlgorithm;
+    private final DisplayInfo mDisplayInfo = new DisplayInfo();
+    private final Rect mStableInsets = new Rect();
+    private final Rect mTmpInsets = new Rect();
+    private final Point mTmpDisplaySize = new Point();
+
+    private IPinnedStackController mPinnedStackController;
+    private int mLastPipToken;
+    private float mReentrySnapFraction = INVALID_SNAP_FRACTION;
+
+    private float mDefaultAspectRatio;
+    private float mMinAspectRatio;
+    private float mMaxAspectRatio;
+    private float mAspectRatio;
+    private int mDefaultStackGravity;
+    private int mDefaultMinSize;
+    private Point mScreenEdgeInsets;
+    private int mCurrentMinSize;
+
+    private boolean mIsMinimized;
+    private boolean mIsImeShowing;
+    private int mImeHeight;
+    private boolean mIsShelfShowing;
+    private int mShelfHeight;
+
+    public PipBoundsHandler(Context context) {
+        mContext = context;
+        mSnapAlgorithm = new PipSnapAlgorithm(context);
+        mWindowManager = WindowManagerGlobal.getWindowManagerService();
+        mAspectRatio = mDefaultAspectRatio;
+        reloadResources();
+    }
+
+    /**
+     * TODO: move the resources to SysUI package.
+     */
+    private void reloadResources() {
+        final Resources res = mContext.getResources();
+        mDefaultAspectRatio = res.getFloat(
+                com.android.internal.R.dimen.config_pictureInPictureDefaultAspectRatio);
+        mDefaultStackGravity = res.getInteger(
+                com.android.internal.R.integer.config_defaultPictureInPictureGravity);
+        mDefaultMinSize = res.getDimensionPixelSize(
+                com.android.internal.R.dimen.default_minimal_size_pip_resizable_task);
+        mCurrentMinSize = mDefaultMinSize;
+        final String screenEdgeInsetsDpString = res.getString(
+                com.android.internal.R.string.config_defaultPictureInPictureScreenEdgeInsets);
+        final Size screenEdgeInsetsDp = !screenEdgeInsetsDpString.isEmpty()
+                ? Size.parseSize(screenEdgeInsetsDpString)
+                : null;
+        mScreenEdgeInsets = screenEdgeInsetsDp == null ? new Point()
+                : new Point(dpToPx(screenEdgeInsetsDp.getWidth(), res.getDisplayMetrics()),
+                        dpToPx(screenEdgeInsetsDp.getHeight(), res.getDisplayMetrics()));
+        mMinAspectRatio = res.getFloat(
+                com.android.internal.R.dimen.config_pictureInPictureMinAspectRatio);
+        mMaxAspectRatio = res.getFloat(
+                com.android.internal.R.dimen.config_pictureInPictureMaxAspectRatio);
+    }
+
+    public void setPinnedStackController(IPinnedStackController controller) {
+        mPinnedStackController = controller;
+    }
+
+    public void setMinEdgeSize(int minEdgeSize) {
+        mCurrentMinSize = minEdgeSize;
+    }
+
+    /**
+     * Responds to IPinnedStackListener on IME visibility change.
+     */
+    public void onImeVisibilityChanged(boolean imeVisible, int imeHeight) {
+        mIsImeShowing = imeVisible;
+        mImeHeight = imeHeight;
+    }
+
+    /**
+     * Responds to IPinnedStackListener on shelf visibility change.
+     */
+    public void onShelfVisibilityChanged(boolean shelfVisible, int shelfHeight) {
+        mIsShelfShowing = shelfVisible;
+        mShelfHeight = shelfHeight;
+    }
+
+    /**
+     * Responds to IPinnedStackListener on minimized state change.
+     */
+    public void onMinimizedStateChanged(boolean minimized) {
+        mIsMinimized = minimized;
+    }
+
+    /**
+     * Responds to IPinnedStackListener on movement bounds change.
+     * Note that both inset and normal bounds will be calculated here rather than in the caller.
+     */
+    public void onMovementBoundsChanged(Rect insetBounds, Rect normalBounds,
+            Rect animatingBounds, DisplayInfo displayInfo) {
+        getInsetBounds(insetBounds);
+        final Rect defaultBounds = getDefaultBounds(INVALID_SNAP_FRACTION);
+        normalBounds.set(defaultBounds);
+        if (animatingBounds.isEmpty()) {
+            animatingBounds.set(defaultBounds);
+        }
+        if (isValidPictureInPictureAspectRatio(mAspectRatio)) {
+            transformBoundsToAspectRatio(normalBounds, mAspectRatio,
+                    false /* useCurrentMinEdgeSize */);
+        }
+        displayInfo.copyFrom(mDisplayInfo);
+    }
+
+    /**
+     * Responds to IPinnedStackListener on saving reentry snap fraction for a given token.
+     * Token should be generated via {@link System#identityHashCode(Object)}
+     */
+    public void onSaveReentrySnapFraction(int token, Rect stackBounds) {
+        mReentrySnapFraction = getSnapFraction(stackBounds);
+        mLastPipToken = token;
+    }
+
+    /**
+     * Responds to IPinnedStackListener on resetting reentry snap fraction for a given token.
+     * Token should be generated via {@link System#identityHashCode(Object)}
+     */
+    public void onResetReentrySnapFraction(int token) {
+        if (mLastPipToken == token) {
+            onResetReentrySnapFractionUnchecked();
+        }
+    }
+
+    private void onResetReentrySnapFractionUnchecked() {
+        mReentrySnapFraction = INVALID_SNAP_FRACTION;
+        mLastPipToken = INVALID_SYSTEM_IDENTITY_TOKEN;
+    }
+
+    /**
+     * Responds to IPinnedStackListener on {@link DisplayInfo} change.
+     * It will normally follow up with a
+     * {@link #onMovementBoundsChanged(Rect, Rect, Rect, DisplayInfo)} callback.
+     */
+    public void onDisplayInfoChanged(DisplayInfo displayInfo) {
+        mDisplayInfo.copyFrom(displayInfo);
+    }
+
+    /**
+     * Responds to IPinnedStackListener on configuration change.
+     */
+    public void onConfigurationChanged() {
+        reloadResources();
+    }
+
+    /**
+     * Responds to IPinnedStackListener on resetting aspect ratio for the pinned window.
+     * It will normally follow up with a
+     * {@link #onMovementBoundsChanged(Rect, Rect, Rect, DisplayInfo)} callback.
+     */
+    public void onAspectRatioChanged(float aspectRatio) {
+        mAspectRatio = aspectRatio;
+    }
+
+    /**
+     * Responds to IPinnedStackListener on preparing the pinned stack animation.
+     */
+    public void onPrepareAnimation(Rect sourceRectHint, float aspectRatio, Rect stackBounds) {
+        final Rect targetStackBounds;
+        if (stackBounds == null) {
+            targetStackBounds = getDefaultBounds(mReentrySnapFraction);
+        } else {
+            targetStackBounds = new Rect();
+            targetStackBounds.set(stackBounds);
+        }
+        if (isValidPictureInPictureAspectRatio(aspectRatio)) {
+            transformBoundsToAspectRatio(targetStackBounds, aspectRatio,
+                    true /* useCurrentMinEdgeSize */);
+        }
+        if (targetStackBounds.equals(stackBounds)) {
+            return;
+        }
+        mAspectRatio = aspectRatio;
+        onResetReentrySnapFractionUnchecked();
+        // TODO: callback Window Manager on starting animation with calculated bounds
+    }
+
+    /**
+     * @return whether the given {@param aspectRatio} is valid.
+     */
+    private boolean isValidPictureInPictureAspectRatio(float aspectRatio) {
+        return Float.compare(mMinAspectRatio, aspectRatio) <= 0
+                && Float.compare(aspectRatio, mMaxAspectRatio) <= 0;
+    }
+
+    /**
+     * Set the current bounds (or the default bounds if there are no current bounds) with the
+     * specified aspect ratio.
+     */
+    private void transformBoundsToAspectRatio(Rect stackBounds, float aspectRatio,
+            boolean useCurrentMinEdgeSize) {
+        // Save the snap fraction, calculate the aspect ratio based on screen size
+        final float snapFraction = mSnapAlgorithm.getSnapFraction(stackBounds,
+                getMovementBounds(stackBounds));
+
+        final int minEdgeSize = useCurrentMinEdgeSize ? mCurrentMinSize : mDefaultMinSize;
+        final Size size = mSnapAlgorithm.getSizeForAspectRatio(aspectRatio, minEdgeSize,
+                mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight);
+        final int left = (int) (stackBounds.centerX() - size.getWidth() / 2f);
+        final int top = (int) (stackBounds.centerY() - size.getHeight() / 2f);
+        stackBounds.set(left, top, left + size.getWidth(), top + size.getHeight());
+        mSnapAlgorithm.applySnapFraction(stackBounds, getMovementBounds(stackBounds), snapFraction);
+        if (mIsMinimized) {
+            applyMinimizedOffset(stackBounds, getMovementBounds(stackBounds));
+        }
+    }
+
+    /**
+     * @return the default bounds to show the PIP, if a {@param snapFraction} is provided, then it
+     * will apply the default bounds to the provided snap fraction.
+     */
+    private Rect getDefaultBounds(float snapFraction) {
+        final Rect insetBounds = new Rect();
+        getInsetBounds(insetBounds);
+
+        final Rect defaultBounds = new Rect();
+        final Size size = mSnapAlgorithm.getSizeForAspectRatio(mDefaultAspectRatio,
+                mDefaultMinSize, mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight);
+        if (snapFraction != INVALID_SNAP_FRACTION) {
+            defaultBounds.set(0, 0, size.getWidth(), size.getHeight());
+            final Rect movementBounds = getMovementBounds(defaultBounds);
+            mSnapAlgorithm.applySnapFraction(defaultBounds, movementBounds, snapFraction);
+        } else {
+            Gravity.apply(mDefaultStackGravity, size.getWidth(), size.getHeight(), insetBounds,
+                    0, Math.max(mIsImeShowing ? mImeHeight : 0,
+                            mIsShelfShowing ? mShelfHeight : 0),
+                    defaultBounds);
+        }
+        return defaultBounds;
+    }
+
+    /**
+     * Populates the bounds on the screen that the PIP can be visible in.
+     */
+    private void getInsetBounds(Rect outRect) {
+        try {
+            mWindowManager.getStableInsets(mContext.getDisplayId(), mTmpInsets);
+            outRect.set(mTmpInsets.left + mScreenEdgeInsets.x,
+                    mTmpInsets.top + mScreenEdgeInsets.y,
+                    mDisplayInfo.logicalWidth - mTmpInsets.right - mScreenEdgeInsets.x,
+                    mDisplayInfo.logicalHeight - mTmpInsets.bottom - mScreenEdgeInsets.y);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Failed to get stable insets from WM", e);
+        }
+    }
+
+    /**
+     * @return the movement bounds for the given {@param stackBounds} and the current state of the
+     *         controller.
+     */
+    private Rect getMovementBounds(Rect stackBounds) {
+        return getMovementBounds(stackBounds, true /* adjustForIme */);
+    }
+
+    /**
+     * @return the movement bounds for the given {@param stackBounds} and the current state of the
+     *         controller.
+     */
+    private Rect getMovementBounds(Rect stackBounds, boolean adjustForIme) {
+        final Rect movementBounds = new Rect();
+        getInsetBounds(movementBounds);
+
+        // Apply the movement bounds adjustments based on the current state.
+        mSnapAlgorithm.getMovementBounds(stackBounds, movementBounds, movementBounds,
+                (adjustForIme && mIsImeShowing) ? mImeHeight : 0);
+        return movementBounds;
+    }
+
+    /**
+     * Applies the minimized offsets to the given stack bounds.
+     */
+    private void applyMinimizedOffset(Rect stackBounds, Rect movementBounds) {
+        mTmpDisplaySize.set(mDisplayInfo.logicalWidth, mDisplayInfo.logicalHeight);
+        try {
+            mWindowManager.getStableInsets(mContext.getDisplayId(), mStableInsets);
+            mSnapAlgorithm.applyMinimizedOffset(stackBounds, movementBounds, mTmpDisplaySize,
+                    mStableInsets);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Failed to get stable insets from WM", e);
+        }
+    }
+
+    /**
+     * @return the default snap fraction to apply instead of the default gravity when calculating
+     *         the default stack bounds when first entering PiP.
+     */
+    private float getSnapFraction(Rect stackBounds) {
+        return mSnapAlgorithm.getSnapFraction(stackBounds, getMovementBounds(stackBounds));
+    }
+
+    /**
+     * @return the pixels for a given dp value.
+     */
+    private int dpToPx(float dpValue, DisplayMetrics dm) {
+        return (int) TypedValue.applyDimension(COMPLEX_UNIT_DIP, dpValue, dm);
+    }
+
+    /**
+     * Dumps internal states.
+     */
+    public void dump(PrintWriter pw, String prefix) {
+        final String innerPrefix = prefix + "  ";
+        pw.println(prefix + TAG);
+        pw.println(innerPrefix + "mReentrySnapFraction=" + mReentrySnapFraction);
+        pw.println(innerPrefix + "mDisplayInfo=" + mDisplayInfo);
+        pw.println(innerPrefix + "mDefaultAspectRatio=" + mDefaultAspectRatio);
+        pw.println(innerPrefix + "mMinAspectRatio=" + mMinAspectRatio);
+        pw.println(innerPrefix + "mMaxAspectRatio=" + mMaxAspectRatio);
+        pw.println(innerPrefix + "mAspectRatio=" + mAspectRatio);
+        pw.println(innerPrefix + "mDefaultStackGravity=" + mDefaultStackGravity);
+        pw.println(innerPrefix + "mIsMinimized=" + mIsMinimized);
+        pw.println(innerPrefix + "mIsImeShowing=" + mIsImeShowing);
+        pw.println(innerPrefix + "mImeHeight=" + mImeHeight);
+        pw.println(innerPrefix + "mIsShelfShowing=" + mIsShelfShowing);
+        pw.println(innerPrefix + "mShelfHeight=" + mShelfHeight);
+        mSnapAlgorithm.dump(pw, innerPrefix);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
index 9c65994..fa60477 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
@@ -551,9 +551,8 @@
                         return true;
                     }
 
-                    mActivityTaskManager.resizeStack(stackInfo.stackId, toBounds,
-                            false /* allowResizeInDockedMode */, true /* preserveWindows */,
-                            true /* animate */, duration);
+                    mActivityTaskManager.animateResizePinnedStack(stackInfo.stackId, toBounds,
+                            duration);
                     mBounds.set(toBounds);
                 } catch (RemoteException e) {
                     Log.e(TAG, "Could not animate resize pinned stack to bounds: " + toBounds, e);
diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
index 5f2614e..918af4f 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java
@@ -441,8 +441,8 @@
         }
         try {
             int animationDurationMs = -1;
-            mActivityTaskManager.resizeStack(mPinnedStackId, mCurrentPipBounds,
-                    true, true, true, animationDurationMs);
+            mActivityTaskManager.animateResizePinnedStack(mPinnedStackId, mCurrentPipBounds,
+                    animationDurationMs);
         } catch (RemoteException e) {
             Log.e(TAG, "resizeStack failed", e);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
index 37113cf..1211e13 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java
@@ -107,11 +107,6 @@
     }
 
     @Override
-    protected boolean shouldShowDetail() {
-        return !mExpanded;
-    }
-
-    @Override
     protected void drawTile(TileRecord r, State state) {
         if (state instanceof SignalState) {
             SignalState copy = new SignalState();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
index 83b000d..38153ec 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/QSCustomizer.java
@@ -38,8 +38,8 @@
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto;
-import com.android.systemui.Dependency;
 import com.android.systemui.R;
+import com.android.systemui.keyguard.ScreenLifecycle;
 import com.android.systemui.plugins.qs.QS;
 import com.android.systemui.plugins.qs.QSTile;
 import com.android.systemui.qs.QSDetailClipper;
@@ -52,6 +52,8 @@
 import java.util.ArrayList;
 import java.util.List;
 
+import javax.inject.Inject;
+
 /**
  * Allows full-screen customization of QS, through show() and hide().
  *
@@ -66,6 +68,8 @@
 
     private final QSDetailClipper mClipper;
     private final LightBarController mLightBarController;
+    private KeyguardMonitor mKeyguardMonitor;
+    private final ScreenLifecycle mScreenLifecycle;
     private final TileQueryHelper mTileQueryHelper;
     private final View mTransparentView;
 
@@ -82,7 +86,11 @@
     private boolean mOpening;
     private boolean mIsShowingNavBackdrop;
 
-    public QSCustomizer(Context context, AttributeSet attrs) {
+    @Inject
+    public QSCustomizer(Context context, AttributeSet attrs,
+            LightBarController lightBarController,
+            KeyguardMonitor keyguardMonitor,
+            ScreenLifecycle screenLifecycle) {
         super(new ContextThemeWrapper(context, R.style.edit_theme), attrs);
 
         LayoutInflater.from(getContext()).inflate(R.layout.qs_customize_panel_content, this);
@@ -115,7 +123,9 @@
         DefaultItemAnimator animator = new DefaultItemAnimator();
         animator.setMoveDuration(TileAdapter.MOVE_DURATION);
         mRecyclerView.setItemAnimator(animator);
-        mLightBarController = Dependency.get(LightBarController.class);
+        mLightBarController = lightBarController;
+        mKeyguardMonitor = keyguardMonitor;
+        mScreenLifecycle = screenLifecycle;
         updateNavBackDrop(getResources().getConfiguration());
     }
 
@@ -177,7 +187,7 @@
             queryTiles();
             mNotifQsContainer.setCustomizerAnimating(true);
             mNotifQsContainer.setCustomizerShowing(true);
-            Dependency.get(KeyguardMonitor.class).addCallback(mKeyguardCallback);
+            mKeyguardMonitor.addCallback(mKeyguardCallback);
             updateNavColors();
         }
     }
@@ -193,7 +203,7 @@
             queryTiles();
             mNotifQsContainer.setCustomizerAnimating(false);
             mNotifQsContainer.setCustomizerShowing(true);
-            Dependency.get(KeyguardMonitor.class).addCallback(mKeyguardCallback);
+            mKeyguardMonitor.addCallback(mKeyguardCallback);
             updateNavColors();
         }
     }
@@ -203,16 +213,21 @@
     }
 
     public void hide() {
+        final boolean animate = mScreenLifecycle.getScreenState() != ScreenLifecycle.SCREEN_OFF;
         if (isShown) {
             MetricsLogger.hidden(getContext(), MetricsProto.MetricsEvent.QS_EDIT);
             isShown = false;
             mToolbar.dismissPopupMenus();
             setCustomizing(false);
             save();
-            mClipper.animateCircularClip(mX, mY, false, mCollapseAnimationListener);
-            mNotifQsContainer.setCustomizerAnimating(true);
+            if (animate) {
+                mClipper.animateCircularClip(mX, mY, false, mCollapseAnimationListener);
+            } else {
+                setVisibility(View.GONE);
+            }
+            mNotifQsContainer.setCustomizerAnimating(animate);
             mNotifQsContainer.setCustomizerShowing(false);
-            Dependency.get(KeyguardMonitor.class).removeCallback(mKeyguardCallback);
+            mKeyguardMonitor.removeCallback(mKeyguardCallback);
             updateNavColors();
         }
     }
@@ -268,7 +283,7 @@
 
     public void saveInstanceState(Bundle outState) {
         if (isShown) {
-            Dependency.get(KeyguardMonitor.class).removeCallback(mKeyguardCallback);
+            mKeyguardMonitor.removeCallback(mKeyguardCallback);
         }
         outState.putBoolean(EXTRA_QS_CUSTOMIZING, mCustomizing);
     }
@@ -300,7 +315,7 @@
         @Override
         public void onKeyguardShowingChanged() {
             if (!isAttachedToWindow()) return;
-            if (Dependency.get(KeyguardMonitor.class).isShowing() && !mOpening) {
+            if (mKeyguardMonitor.isShowing() && !mOpening) {
                 hide();
             }
         }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
index b135f7b..effea6a 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
@@ -159,8 +159,11 @@
             mBindTryCount++;
             try {
                 mIsBound = mContext.bindServiceAsUser(mIntent, this,
-                        Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE
-                        | Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS, mUser);
+                        Context.BIND_AUTO_CREATE
+                                | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE
+                                | Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS
+                                | Context.BIND_WAIVE_PRIORITY,
+                        mUser);
             } catch (SecurityException e) {
                 Log.e(TAG, "Failed to bind to service", e);
                 mIsBound = false;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
index 9045539..3d6ee4c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
@@ -63,6 +63,8 @@
 import com.android.systemui.qs.PagedTileLayout.TilePage;
 import com.android.systemui.qs.QSHost;
 import com.android.systemui.qs.QuickStatusBarHeader;
+import com.android.systemui.qs.tiles.QSSettingsControllerKt;
+import com.android.systemui.qs.tiles.QSSettingsPanel;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -121,9 +123,16 @@
      */
     abstract public int getMetricsCategory();
 
+    /**
+     * Experimental option on whether to use settings panels. Only loaded on creation, so the tile
+     * needs to be removed and added for this to take effect.
+     */
+    protected final QSSettingsPanel mQSSettingsPanelOption;
+
     protected QSTileImpl(QSHost host) {
         mHost = host;
         mContext = host.getContext();
+        mQSSettingsPanelOption = QSSettingsControllerKt.getQSSettingsPanelOption();
     }
 
     @NonNull
@@ -288,6 +297,10 @@
     }
 
     protected void handleLongClick() {
+        if (mQSSettingsPanelOption == QSSettingsPanel.USE_DETAIL) {
+            showDetail(true);
+            return;
+        }
         Dependency.get(ActivityStarter.class).postStartActivityDismissingKeyguard(
                 getLongClickIntent(), 0);
     }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/QSSettingsController.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/QSSettingsController.kt
new file mode 100644
index 0000000..c7ef0be
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/QSSettingsController.kt
@@ -0,0 +1,36 @@
+/*
+ * 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.qs.tiles
+
+import android.provider.DeviceConfig
+import com.android.internal.config.sysui.SystemUiDeviceConfigFlags
+
+enum class QSSettingsPanel {
+    DEFAULT,
+    OPEN_LONG_PRESS,
+    OPEN_CLICK,
+    USE_DETAIL
+}
+
+fun getQSSettingsPanelOption(): QSSettingsPanel =
+        when (DeviceConfig.getInt(DeviceConfig.NAMESPACE_SYSTEMUI,
+                SystemUiDeviceConfigFlags.QS_USE_SETTINGS_PANELS, 0)) {
+            1 -> QSSettingsPanel.OPEN_LONG_PRESS
+            2 -> QSSettingsPanel.OPEN_CLICK
+            3 -> QSSettingsPanel.USE_DETAIL
+            else -> QSSettingsPanel.DEFAULT
+        }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
index d6dad68..187a3bd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UserDetailView.java
@@ -76,7 +76,7 @@
                 UserSwitcherController.UserRecord item) {
             UserDetailItemView v = UserDetailItemView.convertOrInflate(
                     mContext, convertView, parent);
-            if ((v != convertView && !item.isCurrent) || item.isGuest) {
+            if (!item.isCurrent || item.isGuest) {
                 v.setOnClickListener(this);
             } else {
                 v.setOnClickListener(null);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
index 0e7362c3..b79b662 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WifiTile.java
@@ -56,6 +56,7 @@
 /** Quick settings tile: Wifi **/
 public class WifiTile extends QSTileImpl<SignalState> {
     private static final Intent WIFI_SETTINGS = new Intent(Settings.ACTION_WIFI_SETTINGS);
+    private static final Intent WIFI_PANEL = new Intent(Settings.Panel.ACTION_WIFI);
 
     protected final NetworkController mController;
     private final AccessPointController mWifiController;
@@ -112,11 +113,16 @@
 
     @Override
     public Intent getLongClickIntent() {
-        return WIFI_SETTINGS;
+        if (mQSSettingsPanelOption == QSSettingsPanel.OPEN_LONG_PRESS) return WIFI_PANEL;
+        else return WIFI_SETTINGS;
     }
 
     @Override
     protected void handleClick() {
+        if (mQSSettingsPanelOption == QSSettingsPanel.OPEN_CLICK) {
+            mActivityStarter.postStartActivityDismissingKeyguard(WIFI_PANEL, 0);
+            return;
+        }
         // Secondary clicks are header clicks, just toggle.
         mState.copyTo(mStateBeforeClick);
         boolean wifiEnabled = mState.value;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index 2964889..6f5fe8b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -31,6 +31,7 @@
 
 import android.annotation.NonNull;
 import android.app.Notification;
+import android.app.Notification.MessagingStyle.Message;
 import android.app.NotificationChannel;
 import android.app.NotificationManager.Policy;
 import android.app.Person;
@@ -89,7 +90,6 @@
     public StatusBarNotification notification;
     private Ranking mRanking;
 
-    public NotificationChannel channel;
     public long lastAudiblyAlertedMs;
     public boolean noisy;
     public boolean ambient;
@@ -243,7 +243,6 @@
     public void setRanking(@NonNull Ranking ranking) {
         mRanking = ranking;
 
-        channel = ranking.getChannel();
         lastAudiblyAlertedMs = ranking.getLastAudiblyAlertedMillis();
         importance = ranking.getImportance();
         ambient = ranking.isAmbient();
@@ -259,6 +258,10 @@
         canBubble = ranking.canBubble();
     }
 
+    public NotificationChannel getChannel() {
+        return mRanking.getChannel();
+    }
+
     public void setInterruption() {
         interruption = true;
     }
@@ -545,21 +548,18 @@
         if (!ArrayUtils.isEmpty(replyTexts)) {
             return true;
         }
-        Parcelable[] messages = extras.getParcelableArray(Notification.EXTRA_MESSAGES);
-        if (messages != null && messages.length > 0) {
-            Parcelable message = messages[messages.length - 1];
-            if (message instanceof Bundle) {
-                Notification.MessagingStyle.Message lastMessage =
-                        Notification.MessagingStyle.Message.getMessageFromBundle(
-                                (Bundle) message);
-                if (lastMessage != null) {
-                    Person senderPerson = lastMessage.getSenderPerson();
-                    if (senderPerson == null) {
-                        return true;
-                    }
-                    Person user = extras.getParcelable(Notification.EXTRA_MESSAGING_PERSON);
-                    return Objects.equals(user, senderPerson);
+        List<Message> messages = Message.getMessagesFromBundleArray(
+                extras.getParcelableArray(Notification.EXTRA_MESSAGES));
+        if (messages != null && !messages.isEmpty()) {
+            Message lastMessage = messages.get(messages.size() -1);
+
+            if (lastMessage != null) {
+                Person senderPerson = lastMessage.getSenderPerson();
+                if (senderPerson == null) {
+                    return true;
                 }
+                Person user = extras.getParcelable(Notification.EXTRA_MESSAGING_PERSON);
+                return Objects.equals(user, senderPerson);
             }
         }
         return false;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 12d537d..0f6ce21 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -520,7 +520,7 @@
     public boolean getIsNonblockable() {
         boolean isNonblockable = Dependency.get(NotificationBlockingHelperManager.class)
                 .isNonblockable(mStatusBarNotification.getPackageName(),
-                        mEntry.channel.getId());
+                        mEntry.getChannel().getId());
 
         // If the SystemNotifAsyncTask hasn't finished running or retrieved a value, we'll try once
         // again, but in-place on the main thread this time. This should rarely ever get called.
@@ -532,13 +532,13 @@
             mEntry.mIsSystemNotification = isSystemNotification(mContext, mStatusBarNotification);
         }
 
-        isNonblockable |= mEntry.channel.isImportanceLockedByOEM();
-        isNonblockable |= mEntry.channel.isImportanceLockedByCriticalDeviceFunction();
+        isNonblockable |= mEntry.getChannel().isImportanceLockedByOEM();
+        isNonblockable |= mEntry.getChannel().isImportanceLockedByCriticalDeviceFunction();
 
         if (!isNonblockable && mEntry != null && mEntry.mIsSystemNotification != null) {
             if (mEntry.mIsSystemNotification) {
-                if (mEntry.channel != null
-                        && !mEntry.channel.isBlockableSystem()) {
+                if (mEntry.getChannel() != null
+                        && !mEntry.getChannel().isBlockableSystem()) {
                     isNonblockable = true;
                 }
             }
@@ -2389,7 +2389,7 @@
     public ArraySet<NotificationChannel> getUniqueChannels() {
         ArraySet<NotificationChannel> channels = new ArraySet<>();
 
-        channels.add(mEntry.channel);
+        channels.add(mEntry.getChannel());
 
         // If this is a summary, then add in the children notification channels for the
         // same user and pkg.
@@ -2398,7 +2398,7 @@
             final int numChildren = childrenRows.size();
             for (int i = 0; i < numChildren; i++) {
                 final ExpandableNotificationRow childRow = childrenRows.get(i);
-                final NotificationChannel childChannel = childRow.getEntry().channel;
+                final NotificationChannel childChannel = childRow.getEntry().getChannel();
                 final StatusBarNotification childSbn = childRow.getStatusBarNotification();
                 if (childSbn.getUser().equals(mStatusBarNotification.getUser()) &&
                         childSbn.getPackageName().equals(mStatusBarNotification.getPackageName())) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
index 8f7671a..3e8825d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java
@@ -316,7 +316,7 @@
                 iNotificationManager,
                 mVisualStabilityManager,
                 packageName,
-                row.getEntry().channel,
+                row.getEntry().getChannel(),
                 row.getUniqueChannels(),
                 sbn,
                 mCheckSaveListener,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NPVPluginManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NPVPluginManager.kt
new file mode 100644
index 0000000..7dcc2fc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NPVPluginManager.kt
@@ -0,0 +1,92 @@
+/*
+ * 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.content.Context
+import android.view.View
+import android.widget.FrameLayout
+import com.android.systemui.plugins.NPVPlugin
+import com.android.systemui.plugins.PluginListener
+import com.android.systemui.qs.TouchAnimator
+import com.android.systemui.shared.plugins.PluginManager
+
+/**
+ * Manages the NPVPlugin view and state
+ *
+ * Abstracts NPVPlugin from NPV and helps animate on expansion and respond to changes in Config.
+ */
+class NPVPluginManager(
+    var parent: FrameLayout,
+    val pluginManager: PluginManager
+) : PluginListener<NPVPlugin> {
+
+    private var plugin: NPVPlugin? = null
+    private var animator = createAnimator()
+
+    private fun createAnimator() = TouchAnimator.Builder()
+            .addFloat(parent, "alpha", 1f, 0f)
+            .addFloat(parent, "scaleY", 1f, 0f)
+            .build()
+
+    init {
+        pluginManager.addPluginListener(NPVPlugin.ACTION, this, NPVPlugin::class.java, false)
+        parent.pivotY = 0f
+    }
+
+    override fun onPluginConnected(plugin: NPVPlugin, pluginContext: Context) {
+        parent.removeAllViews()
+        plugin.attachToRoot(parent)
+        this.plugin = plugin
+        parent.visibility = View.VISIBLE
+    }
+
+    fun changeVisibility(visibility: Int) {
+        parent.visibility = if (plugin != null) visibility else View.GONE
+    }
+
+    fun destroy() {
+        plugin?.onDestroy()
+        pluginManager.removePluginListener(this)
+    }
+
+    override fun onPluginDisconnected(plugin: NPVPlugin) {
+        if (this.plugin == plugin) {
+            this.plugin = null
+            parent.removeAllViews()
+            parent.visibility = View.GONE
+        }
+    }
+
+    fun setListening(listening: Boolean) {
+        plugin?.setListening(listening)
+    }
+
+    fun setExpansion(expansion: Float, headerTranslation: Float, heightDiff: Float) {
+        parent.setTranslationY(expansion * heightDiff + headerTranslation)
+        if (!expansion.isNaN()) animator.setPosition(expansion)
+    }
+
+    fun replaceFrameLayout(newParent: FrameLayout) {
+        newParent.visibility = parent.visibility
+        parent.removeAllViews()
+        plugin?.attachToRoot(newParent)
+        parent = newParent
+        animator = createAnimator()
+    }
+
+    fun getHeight() = if (plugin != null) parent.height else 0
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index a58ba85..f91b03e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -393,6 +393,10 @@
     private boolean mAllowExpandForSmallExpansion;
     private Runnable mExpandAfterLayoutRunnable;
 
+    private PluginManager mPluginManager;
+    private FrameLayout mPluginFrame;
+    private NPVPluginManager mNPVPluginManager;
+
     @Inject
     public NotificationPanelView(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs,
             InjectionInflationController injectionInflationController,
@@ -400,7 +404,8 @@
             PulseExpansionHandler pulseExpansionHandler,
             DynamicPrivacyController dynamicPrivacyController,
             KeyguardBypassController bypassController,
-            FalsingManager falsingManager) {
+            FalsingManager falsingManager,
+            PluginManager pluginManager) {
         super(context, attrs);
         setWillNotDraw(!DEBUG);
         mInjectionInflationController = injectionInflationController;
@@ -431,6 +436,7 @@
         });
         mBottomAreaShadeAlphaAnimator.setDuration(160);
         mBottomAreaShadeAlphaAnimator.setInterpolator(Interpolators.ALPHA_OUT);
+        mPluginManager = pluginManager;
     }
 
     /**
@@ -465,6 +471,9 @@
         mKeyguardBottomArea = findViewById(R.id.keyguard_bottom_area);
         mQsNavbarScrim = findViewById(R.id.qs_navbar_scrim);
         mLastOrientation = getResources().getConfiguration().orientation;
+        mPluginFrame = findViewById(R.id.plugin_frame);
+        mNPVPluginManager = new NPVPluginManager(mPluginFrame, mPluginManager);
+
 
         initBottomArea();
 
@@ -584,6 +593,19 @@
             lp.gravity = panelGravity;
             mNotificationStackScroller.setLayoutParams(lp);
         }
+        int sideMargin = res.getDimensionPixelOffset(R.dimen.notification_side_paddings);
+        int topMargin =
+                res.getDimensionPixelOffset(com.android.internal.R.dimen.quick_qs_total_height);
+        lp = (FrameLayout.LayoutParams) mPluginFrame.getLayoutParams();
+        if (lp.width != qsWidth || lp.gravity != panelGravity || lp.leftMargin != sideMargin
+                || lp.rightMargin != sideMargin || lp.topMargin != topMargin) {
+            lp.width = qsWidth;
+            lp.gravity = panelGravity;
+            lp.leftMargin = sideMargin;
+            lp.rightMargin = sideMargin;
+            lp.topMargin = topMargin;
+            mPluginFrame.setLayoutParams(lp);
+        }
     }
 
     @Override
@@ -650,6 +672,43 @@
         if (mOnReinflationListener != null) {
             mOnReinflationListener.run();
         }
+        reinflatePluginContainer();
+    }
+
+    @Override
+    public void onUiModeChanged() {
+        reinflatePluginContainer();
+    }
+
+    private void reinflatePluginContainer() {
+        int index = indexOfChild(mPluginFrame);
+        removeView(mPluginFrame);
+        mPluginFrame = (FrameLayout) mInjectionInflationController
+                .injectable(LayoutInflater.from(mContext)).inflate(
+                        R.layout.status_bar_expanded_plugin_frame,
+                        this,
+                        false);
+        addView(mPluginFrame, index);
+
+        Resources res = getResources();
+        int qsWidth = res.getDimensionPixelSize(R.dimen.qs_panel_width);
+        int panelGravity = getResources().getInteger(R.integer.notification_panel_layout_gravity);
+        FrameLayout.LayoutParams lp;
+        int sideMargin = res.getDimensionPixelOffset(R.dimen.notification_side_paddings);
+        int topMargin =
+                res.getDimensionPixelOffset(com.android.internal.R.dimen.quick_qs_total_height);
+        lp = (FrameLayout.LayoutParams) mPluginFrame.getLayoutParams();
+        if (lp.width != qsWidth || lp.gravity != panelGravity || lp.leftMargin != sideMargin
+                || lp.rightMargin != sideMargin || lp.topMargin != topMargin) {
+            lp.width = qsWidth;
+            lp.gravity = panelGravity;
+            lp.leftMargin = sideMargin;
+            lp.rightMargin = sideMargin;
+            lp.topMargin = topMargin;
+            mPluginFrame.setLayoutParams(lp);
+        }
+
+        mNPVPluginManager.replaceFrameLayout(mPluginFrame);
     }
 
     private void initBottomArea() {
@@ -679,6 +738,7 @@
         int oldMaxHeight = mQsMaxExpansionHeight;
         if (mQs != null) {
             mQsMinExpansionHeight = mKeyguardShowing ? 0 : mQs.getQsMinExpansionHeight();
+            mQsMinExpansionHeight += mNPVPluginManager.getHeight();
             mQsMaxExpansionHeight = mQs.getDesiredHeight();
             mNotificationStackScroller.setMaxTopPadding(
                     mQsMaxExpansionHeight + mQsNotificationTopPadding);
@@ -1784,6 +1844,9 @@
                 mBarState != StatusBarState.KEYGUARD && (!mQsExpanded
                         || mQsExpansionFromOverscroll));
         updateEmptyShadeView();
+        mNPVPluginManager.changeVisibility((mBarState != StatusBarState.KEYGUARD)
+                ? View.VISIBLE
+                : View.INVISIBLE);
         mQsNavbarScrim.setVisibility(mBarState == StatusBarState.SHADE && mQsExpanded
                 && !mStackScrollerOverscrolling && mQsScrimEnabled
                 ? View.VISIBLE
@@ -1798,7 +1861,8 @@
     private void setQsExpansion(float height) {
         height = Math.min(Math.max(height, mQsMinExpansionHeight), mQsMaxExpansionHeight);
         mQsFullyExpanded = height == mQsMaxExpansionHeight && mQsMaxExpansionHeight != 0;
-        if (height > mQsMinExpansionHeight && !mQsExpanded && !mStackScrollerOverscrolling) {
+        if (height > mQsMinExpansionHeight && !mQsExpanded && !mStackScrollerOverscrolling
+                && !mDozing) {
             setQsExpanded(true);
         } else if (height <= mQsMinExpansionHeight && mQsExpanded) {
             setQsExpanded(false);
@@ -1839,6 +1903,8 @@
         if (mQs == null) return;
         float qsExpansionFraction = getQsExpansionFraction();
         mQs.setQsExpansion(qsExpansionFraction, getHeaderTranslation());
+        int heightDiff = mQs.getDesiredHeight() - mQs.getQsMinExpansionHeight();
+        mNPVPluginManager.setExpansion(qsExpansionFraction, getHeaderTranslation(), heightDiff);
         mNotificationStackScroller.setQsExpansionFraction(qsExpansionFraction);
     }
 
@@ -2259,6 +2325,7 @@
                 appearAmount = mNotificationStackScroller.calculateAppearFractionBypass();
             }
             startHeight = -mQs.getQsMinExpansionHeight();
+            startHeight -= mNPVPluginManager.getHeight();
         }
         float translation = MathUtils.lerp(startHeight, 0,
                 Math.min(1.0f, appearAmount))
@@ -2399,6 +2466,7 @@
         mKeyguardStatusBar.setListening(listening);
         if (mQs == null) return;
         mQs.setListening(listening);
+        mNPVPluginManager.setListening(listening);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index fe96ef7..6b12c61 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -1263,7 +1263,7 @@
         mStatusBarKeyguardViewManager = keyguardViewMediator.registerStatusBar(this,
                 getBouncerContainer(), mNotificationPanel, mBiometricUnlockController,
                 mStatusBarWindow.findViewById(R.id.lock_icon_container), mStackScroller,
-                mKeyguardBypassController, mFalsingManager);
+                mKeyguardBypassController);
         mKeyguardIndicationController
                 .setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager);
         mBiometricUnlockController.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitor.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitor.java
index e1ef809..6dc90b9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitor.java
@@ -19,7 +19,6 @@
 public interface KeyguardMonitor extends CallbackController<Callback> {
 
     boolean isSecure();
-    boolean canSkipBouncer();
     boolean isShowing();
     boolean isOccluded();
     boolean isKeyguardFadingAway();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitorImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitorImpl.java
index a950626..e8b0f9b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitorImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardMonitorImpl.java
@@ -17,14 +17,12 @@
 package com.android.systemui.statusbar.policy;
 
 import android.annotation.NonNull;
-import android.app.ActivityManager;
 import android.content.Context;
 
 import com.android.internal.util.Preconditions;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.keyguard.KeyguardUpdateMonitorCallback;
 import com.android.systemui.Dependency;
-import com.android.systemui.settings.CurrentUserTracker;
 
 import java.util.ArrayList;
 
@@ -40,14 +38,11 @@
     private final ArrayList<Callback> mCallbacks = new ArrayList<>();
 
     private final Context mContext;
-    private final CurrentUserTracker mUserTracker;
     private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
 
-    private int mCurrentUser;
     private boolean mShowing;
     private boolean mSecure;
     private boolean mOccluded;
-    private boolean mCanSkipBouncer;
 
     private boolean mListening;
     private boolean mKeyguardFadingAway;
@@ -63,13 +58,6 @@
     public KeyguardMonitorImpl(Context context) {
         mContext = context;
         mKeyguardUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class);
-        mUserTracker = new CurrentUserTracker(mContext) {
-            @Override
-            public void onUserSwitched(int newUserId) {
-                mCurrentUser = newUserId;
-                updateCanSkipBouncerState();
-            }
-        };
     }
 
     @Override
@@ -78,10 +66,7 @@
         mCallbacks.add(callback);
         if (mCallbacks.size() != 0 && !mListening) {
             mListening = true;
-            mCurrentUser = ActivityManager.getCurrentUser();
-            updateCanSkipBouncerState();
             mKeyguardUpdateMonitor.registerCallback(this);
-            mUserTracker.startTracking();
         }
     }
 
@@ -91,7 +76,6 @@
         if (mCallbacks.remove(callback) && mCallbacks.size() == 0 && mListening) {
             mListening = false;
             mKeyguardUpdateMonitor.removeCallback(this);
-            mUserTracker.stopTracking();
         }
     }
 
@@ -110,11 +94,6 @@
         return mOccluded;
     }
 
-    @Override
-    public boolean canSkipBouncer() {
-        return mCanSkipBouncer;
-    }
-
     public void notifyKeyguardState(boolean showing, boolean secure, boolean occluded) {
         if (mShowing == showing && mSecure == secure && mOccluded == occluded) return;
         mShowing = showing;
@@ -125,7 +104,6 @@
 
     @Override
     public void onTrustChanged(int userId) {
-        updateCanSkipBouncerState();
         notifyKeyguardChanged();
     }
 
@@ -133,10 +111,6 @@
         return mKeyguardUpdateMonitor.isDeviceInteractive();
     }
 
-    private void updateCanSkipBouncerState() {
-        mCanSkipBouncer = mKeyguardUpdateMonitor.getUserCanSkipBouncer(mCurrentUser);
-    }
-
     private void notifyKeyguardChanged() {
         // Copy the list to allow removal during callback.
         new ArrayList<>(mCallbacks).forEach(Callback::onKeyguardShowingChanged);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
index 2e1e520..4fa4b6c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -62,6 +62,7 @@
 import com.android.systemui.plugins.qs.DetailAdapter;
 import com.android.systemui.qs.tiles.UserDetailView;
 import com.android.systemui.statusbar.phone.SystemUIDialog;
+import com.android.systemui.statusbar.phone.UnlockMethodCache;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -597,17 +598,19 @@
 
         final UserSwitcherController mController;
         private final KeyguardMonitor mKeyguardMonitor;
+        private final UnlockMethodCache mUnlockMethodCache;
 
         protected BaseUserAdapter(UserSwitcherController controller) {
             mController = controller;
             mKeyguardMonitor = controller.mKeyguardMonitor;
+            mUnlockMethodCache = UnlockMethodCache.getInstance(controller.mContext);
             controller.addAdapter(new WeakReference<>(this));
         }
 
         public int getUserCount() {
             boolean secureKeyguardShowing = mKeyguardMonitor.isShowing()
                     && mKeyguardMonitor.isSecure()
-                    && !mKeyguardMonitor.canSkipBouncer();
+                    && !mUnlockMethodCache.canSkipBouncer();
             if (!secureKeyguardShowing) {
                 return mController.getUsers().size();
             }
@@ -629,7 +632,7 @@
         public int getCount() {
             boolean secureKeyguardShowing = mKeyguardMonitor.isShowing()
                     && mKeyguardMonitor.isSecure()
-                    && !mKeyguardMonitor.canSkipBouncer();
+                    && !mUnlockMethodCache.canSkipBouncer();
             if (!secureKeyguardShowing) {
                 return mController.getUsers().size();
             }
diff --git a/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java b/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java
index 9b264c4..e44e58a 100644
--- a/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java
+++ b/packages/SystemUI/src/com/android/systemui/util/InjectionInflationController.java
@@ -32,6 +32,7 @@
 import com.android.systemui.qs.QSPanel;
 import com.android.systemui.qs.QuickQSPanel;
 import com.android.systemui.qs.QuickStatusBarHeader;
+import com.android.systemui.qs.customize.QSCustomizer;
 import com.android.systemui.statusbar.NotificationShelf;
 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout;
 import com.android.systemui.statusbar.phone.LockIcon;
@@ -172,6 +173,11 @@
          * Creates the QuickQSPanel.
          */
         QuickQSPanel createQuickQSPanel();
+
+        /**
+         * Creates the QSCustomizer.
+         */
+        QSCustomizer createQSCustomizer();
     }
 
     /**
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
index 94ca9e9..819a7f6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
@@ -29,6 +29,7 @@
 
 import androidx.test.InstrumentationRegistry;
 
+import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.systemui.classifier.FalsingManagerFake;
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.util.Assert;
@@ -78,6 +79,7 @@
         // A lot of tests get the FalsingManager, often via several layers of indirection.
         // None of them actually need it.
         mDependency.injectTestDependency(FalsingManager.class, new FalsingManagerFake());
+        mDependency.injectMockDependency(KeyguardUpdateMonitor.class);
     }
 
     @After
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFaceViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFaceViewTest.java
index 128e819..b907cdb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFaceViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricFaceViewTest.java
@@ -63,7 +63,7 @@
         mFaceView.mNegativeButton = mNegativeButton;
         mFaceView.mPositiveButton = mPositiveButton;
         mFaceView.mTryAgainButton = mTryAgainButton;
-        mFaceView.mErrorView = mErrorView;
+        mFaceView.mIndicatorView = mErrorView;
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricViewTest.java
index ffcb293..fc18707 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthBiometricViewTest.java
@@ -17,12 +17,15 @@
 package com.android.systemui.biometrics;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 
 import android.content.Context;
+import android.hardware.biometrics.BiometricPrompt;
+import android.os.Bundle;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableLooper.RunWithLooper;
@@ -55,10 +58,10 @@
     @Mock private TextView mTitleView;
     @Mock private TextView mSubtitleView;
     @Mock private TextView mDescriptionView;
-    @Mock private TextView mErrorView;
+    @Mock private TextView mIndicatorView;
     @Mock private ImageView mIconView;
 
-    TestableBiometricView mBiometricView;
+    private TestableBiometricView mBiometricView;
 
     @Before
     public void setup() {
@@ -87,8 +90,8 @@
         verify(mCallback, never()).onAction(anyInt());
         verify(mBiometricView.mNegativeButton).setText(eq(R.string.cancel));
         verify(mBiometricView.mPositiveButton).setEnabled(eq(true));
-        verify(mErrorView).setText(eq(R.string.biometric_dialog_tap_confirm));
-        verify(mErrorView).setVisibility(eq(View.VISIBLE));
+        verify(mIndicatorView).setText(eq(R.string.biometric_dialog_tap_confirm));
+        verify(mIndicatorView).setVisibility(eq(View.VISIBLE));
     }
 
     @Test
@@ -193,11 +196,88 @@
         verify(mCallback, never()).onAction(eq(AuthBiometricView.Callback.ACTION_USER_CANCELED));
     }
 
+    @Test
+    public void testRestoresState() {
+        final boolean requireConfirmation = true; // set/init from AuthController
+
+        Button tryAgainButton = new Button(mContext);
+        TextView indicatorView = new TextView(mContext);
+        initDialog(mContext, mCallback, new MockInjector() {
+            @Override
+            public Button getTryAgainButton() {
+                return tryAgainButton;
+            }
+            @Override
+            public TextView getIndicatorView() {
+                return indicatorView;
+            }
+        });
+
+        final String failureMessage = "testFailureMessage";
+        mBiometricView.setRequireConfirmation(requireConfirmation);
+        mBiometricView.onAuthenticationFailed(failureMessage);
+        waitForIdleSync();
+
+        Bundle state = new Bundle();
+        mBiometricView.onSaveState(state);
+
+        assertEquals(View.VISIBLE, tryAgainButton.getVisibility());
+        assertEquals(View.VISIBLE, state.getInt(AuthDialog.KEY_BIOMETRIC_TRY_AGAIN_VISIBILITY));
+
+        assertEquals(AuthBiometricView.STATE_ERROR, mBiometricView.mState);
+        assertEquals(AuthBiometricView.STATE_ERROR, state.getInt(AuthDialog.KEY_BIOMETRIC_STATE));
+
+        assertEquals(View.VISIBLE, mBiometricView.mIndicatorView.getVisibility());
+        assertTrue(state.getBoolean(AuthDialog.KEY_BIOMETRIC_INDICATOR_ERROR_SHOWING));
+
+        assertEquals(failureMessage, mBiometricView.mIndicatorView.getText());
+        assertEquals(failureMessage, state.getString(AuthDialog.KEY_BIOMETRIC_INDICATOR_STRING));
+
+        // TODO: Test dialog size. Should move requireConfirmation to buildBiometricPromptBundle
+
+        // Create new dialog and restore the previous state into it
+        Button tryAgainButton2 = new Button(mContext);
+        TextView indicatorView2 = new TextView(mContext);
+        initDialog(mContext, mCallback, state, new MockInjector() {
+            @Override
+            public Button getTryAgainButton() {
+                return tryAgainButton2;
+            }
+            @Override
+            public TextView getIndicatorView() {
+                return indicatorView2;
+            }
+        });
+        mBiometricView.setRequireConfirmation(requireConfirmation);
+        waitForIdleSync();
+
+        // Test restored state
+        assertEquals(View.VISIBLE, tryAgainButton.getVisibility());
+        assertEquals(AuthBiometricView.STATE_ERROR, mBiometricView.mState);
+        assertEquals(View.VISIBLE, mBiometricView.mIndicatorView.getVisibility());
+        assertEquals(failureMessage, mBiometricView.mIndicatorView.getText());
+    }
+
+    private Bundle buildBiometricPromptBundle() {
+        Bundle bundle = new Bundle();
+        bundle.putCharSequence(BiometricPrompt.KEY_TITLE, "Title");
+        bundle.putCharSequence(BiometricPrompt.KEY_NEGATIVE_TEXT, "Negative");
+        return bundle;
+    }
+
+    private void initDialog(Context context, AuthBiometricView.Callback callback,
+            Bundle savedState, MockInjector injector) {
+        mBiometricView = new TestableBiometricView(context, null, injector);
+        mBiometricView.setBiometricPromptBundle(buildBiometricPromptBundle());
+        mBiometricView.setCallback(callback);
+        mBiometricView.restoreState(savedState);
+        mBiometricView.onFinishInflateInternal();
+        mBiometricView.onAttachedToWindowInternal();
+    }
+
     private void initDialog(Context context, AuthBiometricView.Callback callback,
             MockInjector injector) {
-        mBiometricView = new TestableBiometricView(context, null, injector);
-        mBiometricView.setCallback(callback);
-        mBiometricView.initializeViews();
+        initDialog(context, callback, null /* savedState */, injector);
     }
 
     private class MockInjector extends AuthBiometricView.Injector {
@@ -232,8 +312,8 @@
         }
 
         @Override
-        public TextView getErrorView() {
-            return mErrorView;
+        public TextView getIndicatorView() {
+            return mIndicatorView;
         }
 
         @Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
index a5e468e..eb7be4f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
@@ -147,7 +147,7 @@
     public void testShowInvoked_whenSystemRequested()
             throws Exception {
         showDialog(BiometricPrompt.TYPE_FACE);
-        verify(mDialog1).show(any());
+        verify(mDialog1).show(any(), any());
     }
 
     @Test
@@ -215,7 +215,7 @@
     @Test
     public void testShowNewDialog_beforeOldDialogDismissed_SkipsAnimations() throws Exception {
         showDialog(BiometricPrompt.TYPE_FACE);
-        verify(mDialog1).show(any());
+        verify(mDialog1).show(any(), any());
 
         showDialog(BiometricPrompt.TYPE_FACE);
 
@@ -223,13 +223,13 @@
         verify(mDialog1).dismissWithoutCallback(eq(false) /* animate */);
 
         // Second dialog should be shown without animation
-        verify(mDialog2).show(any());
+        verify(mDialog2).show(any(), any());
     }
 
     @Test
     public void testConfigurationPersists_whenOnConfigurationChanged() throws Exception {
         showDialog(BiometricPrompt.TYPE_FACE);
-        verify(mDialog1).show(any());
+        verify(mDialog1).show(any(), any());
 
         mBiometricDialogImpl.onConfigurationChanged(new Configuration());
 
@@ -241,10 +241,7 @@
 
         // Saved state is restored into new dialog
         ArgumentCaptor<Bundle> captor2 = ArgumentCaptor.forClass(Bundle.class);
-        verify(mDialog2).restoreState(captor2.capture());
-
-        // Dialog for new configuration skips intro
-        verify(mDialog2).show(any());
+        verify(mDialog2).show(any(), captor2.capture());
 
         // TODO: This should check all values we want to save/restore
         assertEquals(captor.getValue(), captor2.getValue());
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 ba434d4..448c80e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
@@ -71,8 +71,8 @@
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.phone.DozeParameters;
-import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.phone.KeyguardBypassController;
+import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.phone.StatusBarWindowController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -165,7 +165,8 @@
         // Return non-null notification data from the NEM
         when(mNotificationEntryManager.getNotificationData()).thenReturn(mNotificationData);
         when(mNotificationData.get(mRow.getEntry().key)).thenReturn(mRow.getEntry());
-        when(mNotificationData.getChannel(mRow.getEntry().key)).thenReturn(mRow.getEntry().channel);
+        when(mNotificationData.getChannel(mRow.getEntry().key)).thenReturn(
+                mRow.getEntry().getChannel());
 
         mZenModeConfig.suppressedVisualEffects = 0;
         when(mZenModeController.getConfig()).thenReturn(mZenModeConfig);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
index f2292fd..07fbbcf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
@@ -97,6 +97,8 @@
                 mLooper.getLooper(),
                 mPluginManager, mTunerService, mAutoTiles, mDumpController);
         setUpTileFactory();
+        Settings.Secure.putStringForUser(mContext.getContentResolver(), QSTileHost.TILES_SETTING,
+                "", ActivityManager.getCurrentUser());
     }
 
     private void setUpTileFactory() {
@@ -188,7 +190,8 @@
             // changed
             String newSetting = Settings.Secure.getStringForUser(getContext().getContentResolver(),
                     TILES_SETTING, ActivityManager.getCurrentUser());
-            if (!previousSetting.equals(newSetting)) {
+            // newSetting is not null, as it has just been set.
+            if (!newSetting.equals(previousSetting)) {
                 onTuningChanged(TILES_SETTING, newSetting);
             }
         }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/UserDetailViewAdapterTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/UserDetailViewAdapterTest.kt
new file mode 100644
index 0000000..b51e716
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/UserDetailViewAdapterTest.kt
@@ -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.systemui.qs.tiles
+
+import android.content.Context
+import android.content.pm.UserInfo
+import android.graphics.Bitmap
+import android.testing.AndroidTestingRunner
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.policy.UserSwitcherController
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.anyBoolean
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.Mock
+import org.mockito.Mockito.`when`
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class UserDetailViewAdapterTest : SysuiTestCase() {
+
+    @Mock private lateinit var mUserSwitcherController: UserSwitcherController
+    @Mock private lateinit var mParent: ViewGroup
+    @Mock private lateinit var mUserDetailItemView: UserDetailItemView
+    @Mock private lateinit var mOtherView: View
+    @Mock private lateinit var mInflatedUserDetailItemView: UserDetailItemView
+    @Mock private lateinit var mUserInfo: UserInfo
+    @Mock private lateinit var mPicture: Bitmap
+    @Mock private lateinit var mLayoutInflater: LayoutInflater
+    private lateinit var adapter: UserDetailView.Adapter
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+
+        mContext.addMockSystemService(Context.LAYOUT_INFLATER_SERVICE, mLayoutInflater)
+        `when`(mLayoutInflater.inflate(anyInt(), any(ViewGroup::class.java), anyBoolean()))
+                .thenReturn(mInflatedUserDetailItemView)
+        adapter = UserDetailView.Adapter(mContext, mUserSwitcherController)
+    }
+
+    private fun clickableTest(
+        current: Boolean,
+        guest: Boolean,
+        convertView: View,
+        shouldBeClickable: Boolean
+    ) {
+        val user = createUserRecord(current, guest)
+        val v = adapter.createUserDetailItemView(convertView, mParent, user)
+        if (shouldBeClickable) {
+            verify(v).setOnClickListener(adapter)
+        } else {
+            verify(v).setOnClickListener(null)
+        }
+    }
+
+    @Test
+    fun testGuestIsClickable_differentViews_notCurrent() {
+        clickableTest(false, true, mOtherView, true)
+    }
+
+    @Test
+    fun testGuestIsClickable_differentViews_Current() {
+        clickableTest(true, true, mOtherView, true)
+    }
+
+    @Test
+    fun testGuestIsClickable_sameView_notCurrent() {
+        clickableTest(false, true, mUserDetailItemView, true)
+    }
+
+    @Test
+    fun testGuestIsClickable_sameView_Current() {
+        clickableTest(true, true, mUserDetailItemView, true)
+    }
+
+    @Test
+    fun testNotGuestCurrentUserIsNotClickable_otherView() {
+        clickableTest(true, false, mOtherView, false)
+    }
+
+    @Test
+    fun testNotGuestCurrentUserIsNotClickable_sameView() {
+        clickableTest(true, false, mUserDetailItemView, false)
+    }
+
+    @Test
+    fun testNotGuestNotCurrentUserIsClickable_otherView() {
+        clickableTest(false, false, mOtherView, true)
+    }
+
+    @Test
+    fun testNotGuestNotCurrentUserIsClickable_sameView() {
+        clickableTest(false, false, mUserDetailItemView, true)
+    }
+
+    private fun createUserRecord(current: Boolean, guest: Boolean) =
+            UserSwitcherController.UserRecord(
+                    mUserInfo,
+                    mPicture,
+                    guest,
+                    current,
+                    false /* isAddUser */,
+                    false /* isRestricted */,
+                    true /* isSwitchToEnabled */)
+}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
index 264a5400..7cd5819 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationTestHelper.java
@@ -306,12 +306,21 @@
                 userHandle,
                 null /* overrideGroupKey */,
                 System.currentTimeMillis());
-        NotificationEntry entry = NotificationEntry.buildForTest(sbn);
+        final NotificationChannel channel =
+                new NotificationChannel(
+                        notification.getChannelId(),
+                        notification.getChannelId(),
+                        importance);
+        channel.setBlockableSystem(true);
+
+        NotificationEntry entry = new NotificationEntry(
+                sbn,
+                new RankingBuilder()
+                        .setKey(sbn.getKey())
+                        .setChannel(channel)
+                        .build());
         entry.setRow(row);
         entry.createIcons(mContext, sbn);
-        entry.channel = new NotificationChannel(
-                notification.getChannelId(), notification.getChannelId(), importance);
-        entry.channel.setBlockableSystem(true);
         row.setEntry(entry);
         row.getNotificationInflater().addInflationFlags(extraInflationFlags);
         NotificationContentInflaterTest.runThenWaitForInflation(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/RankingBuilder.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/RankingBuilder.java
new file mode 100644
index 0000000..bbdc4b7
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/RankingBuilder.java
@@ -0,0 +1,105 @@
+/*
+ * 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;
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.service.notification.NotificationListenerService.Ranking;
+import android.service.notification.SnoozeCriterion;
+
+import java.util.ArrayList;
+
+/**
+ * Standard builder class for Ranking objects. For use in tests that need to craft the underlying
+ * Ranking object of a NotificationEntry.
+ */
+public class RankingBuilder {
+    private String mKey = "test_key";
+    private int mRank = 0;
+    private boolean mMatchesInterruptionFilter = false;
+    private int mVisibilityOverride = 0;
+    private int mSuppressedVisualEffects = 0;
+    private int mImportance = 0;
+    private CharSequence mExplanation = "test_explanation";
+    private String mOverrideGroupKey = null;
+    private NotificationChannel mChannel = null;
+    private ArrayList<String> mOverridePeople = null;
+    private ArrayList<SnoozeCriterion> mSnoozeCriteria = null;
+    private boolean mShowBadge = false;
+    private int mUserSentiment = 0;
+    private boolean mHidden = false;
+    private long mLastAudiblyAlertedMs = 0;
+    private boolean mNoisy = false;
+    private ArrayList<Notification.Action> mSmartActions = null;
+    private ArrayList<CharSequence> mSmartReplies = null;
+    private boolean mCanBubble = false;
+
+    public RankingBuilder setKey(String key) {
+        mKey = key;
+        return this;
+    }
+
+    public RankingBuilder setImportance(int importance) {
+        mImportance = importance;
+        return this;
+    }
+
+    public RankingBuilder setUserSentiment(int userSentiment) {
+        mUserSentiment = userSentiment;
+        return this;
+    }
+
+    public RankingBuilder setChannel(NotificationChannel channel) {
+        mChannel = channel;
+        return this;
+    }
+
+    public RankingBuilder setSmartActions(ArrayList<Notification.Action> smartActions) {
+        mSmartActions = smartActions;
+        return this;
+    }
+
+    public RankingBuilder setSmartReplies(ArrayList<CharSequence> smartReplies) {
+        mSmartReplies = smartReplies;
+        return this;
+    }
+
+    public Ranking build() {
+        final Ranking ranking = new Ranking();
+        ranking.populate(
+                mKey,
+                mRank,
+                mMatchesInterruptionFilter,
+                mVisibilityOverride,
+                mSuppressedVisualEffects,
+                mImportance,
+                mExplanation,
+                mOverrideGroupKey,
+                mChannel,
+                mOverridePeople,
+                mSnoozeCriteria,
+                mShowBadge,
+                mUserSentiment,
+                mHidden,
+                mLastAudiblyAlertedMs,
+                mNoisy,
+                mSmartActions,
+                mSmartReplies,
+                mCanBubble);
+        return ranking;
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java
index ed719d9..45173a2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationDataTest.java
@@ -145,7 +145,7 @@
         override.putParcelable(OVERRIDE_CHANNEL, NOTIFICATION_CHANNEL);
         mNotificationData.rankingOverrides.put(mRow.getEntry().key, override);
         mNotificationData.add(mRow.getEntry());
-        assertEquals(NOTIFICATION_CHANNEL, mRow.getEntry().channel);
+        assertEquals(NOTIFICATION_CHANNEL, mRow.getEntry().getChannel());
     }
 
     @Test
@@ -343,7 +343,7 @@
                 new NotificationEntry(mMockStatusBarNotification, ranking);
 
         assertEquals(systemGeneratedSmartActions, entry.systemGeneratedSmartActions);
-        assertEquals(NOTIFICATION_CHANNEL, entry.channel);
+        assertEquals(NOTIFICATION_CHANNEL, entry.getChannel());
         assertEquals(Ranking.USER_SENTIMENT_NEGATIVE, entry.userSentiment);
         assertEquals(snoozeCriterions, entry.snoozeCriteria);
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
index d526d10..a14557b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java
@@ -51,6 +51,7 @@
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
 import com.android.systemui.statusbar.NotificationTestHelper;
+import com.android.systemui.statusbar.RankingBuilder;
 import com.android.systemui.statusbar.notification.AboveShelfChangedListener;
 import com.android.systemui.statusbar.notification.stack.NotificationChildrenContainer;
 
@@ -327,8 +328,11 @@
         // Give each child a unique channel id/name.
         int i = 0;
         for (ExpandableNotificationRow childRow : childRows) {
-            childRow.getEntry().channel =
-                    new NotificationChannel("id" + i, "dinnertime" + i, IMPORTANCE_DEFAULT);
+            childRow.getEntry().setRanking(new RankingBuilder()
+                    .setChannel(
+                            new NotificationChannel(
+                                    "id" + i, "dinnertime" + i, IMPORTANCE_DEFAULT))
+                    .build());
             i++;
         }
 
@@ -364,7 +368,7 @@
     public void testGetIsNonblockable_oemLocked() throws Exception {
         ExpandableNotificationRow row =
                 mNotificationTestHelper.createRow(mNotificationTestHelper.createNotification());
-        row.getEntry().channel.setImportanceLockedByOEM(true);
+        row.getEntry().getChannel().setImportanceLockedByOEM(true);
 
         assertTrue(row.getIsNonblockable());
     }
@@ -373,7 +377,7 @@
     public void testGetIsNonblockable_criticalDeviceFunction() throws Exception {
         ExpandableNotificationRow row =
                 mNotificationTestHelper.createRow(mNotificationTestHelper.createNotification());
-        row.getEntry().channel.setImportanceLockedByCriticalDeviceFunction(true);
+        row.getEntry().getChannel().setImportanceLockedByCriticalDeviceFunction(true);
 
         assertTrue(row.getIsNonblockable());
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManagerTest.java
index 3c91b3f..a26cdbd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationBlockingHelperManagerTest.java
@@ -41,8 +41,10 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.bubbles.BubbleController;
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
 import com.android.systemui.statusbar.NotificationTestHelper;
+import com.android.systemui.statusbar.RankingBuilder;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
 import com.android.systemui.util.Assert;
 
@@ -73,6 +75,7 @@
     public void setUp() {
         Assert.sMainLooper = TestableLooper.get(this).getLooper();
         MockitoAnnotations.initMocks(this);
+        mDependency.injectMockDependency(BubbleController.class);
         when(mGutsManager.openGuts(
                 any(View.class),
                 anyInt(),
@@ -82,6 +85,7 @@
         when(mMenuRow.getLongpressMenuItem(any(Context.class))).thenReturn(mMenuItem);
         mDependency.injectTestDependency(NotificationGutsManager.class, mGutsManager);
         mDependency.injectTestDependency(NotificationEntryManager.class, mEntryManager);
+        mDependency.injectMockDependency(BubbleController.class);
 
         mHelper = new NotificationTestHelper(mContext);
 
@@ -138,8 +142,11 @@
         ExpandableNotificationRow groupRow = createBlockableGroupRowSpy(10);
         int i = 0;
         for (ExpandableNotificationRow childRow : groupRow.getNotificationChildren()) {
-            childRow.getEntry().channel =
-                    new NotificationChannel(Integer.toString(i++), "", IMPORTANCE_DEFAULT);
+            childRow.getEntry().setRanking(new RankingBuilder()
+                    .setChannel(
+                            new NotificationChannel(
+                                    Integer.toString(i++), "", IMPORTANCE_DEFAULT))
+                    .build());
         }
 
         groupRow.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
index 7959484..09c4179 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationGutsManagerTest.java
@@ -62,6 +62,7 @@
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
 import com.android.systemui.statusbar.NotificationPresenter;
 import com.android.systemui.statusbar.NotificationTestHelper;
+import com.android.systemui.statusbar.RankingBuilder;
 import com.android.systemui.statusbar.notification.NotificationActivityStarter;
 import com.android.systemui.statusbar.notification.VisualStabilityManager;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
@@ -498,7 +499,10 @@
 
         try {
             ExpandableNotificationRow row = mHelper.createRow(nb.build());
-            row.getEntry().channel = mTestNotificationChannel;
+            row.getEntry().setRanking(
+                    new RankingBuilder()
+                            .setChannel(mTestNotificationChannel)
+                            .build());
             return row;
         } catch (Exception e) {
             fail();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java
index 98e1692..096acf9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java
@@ -40,6 +40,7 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
+import com.android.systemui.statusbar.RankingBuilder;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 import com.android.systemui.utils.leaks.LeakCheckedTest;
 
@@ -60,9 +61,11 @@
     public void setup() {
         injectLeakCheckedDependencies(ALL_SUPPORTED_CLASSES);
         mRow = mock(ExpandableNotificationRow.class);
-        NotificationEntry entry = NotificationEntry.buildForTest(
-                mock(StatusBarNotification.class));
-        entry.channel = mock(NotificationChannel.class);
+        NotificationEntry entry = new NotificationEntry(
+                mock(StatusBarNotification.class),
+                new RankingBuilder()
+                        .setChannel(mock(NotificationChannel.class))
+                        .build());
         when(mRow.getEntry()).thenReturn(entry);
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
index a96efd7..86ab3a7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewTest.java
@@ -38,6 +38,7 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.plugins.FalsingManager;
 import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.shared.plugins.PluginManager;
 import com.android.systemui.statusbar.KeyguardAffordanceView;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.NotificationShelf;
@@ -195,7 +196,7 @@
                             SystemUIFactory.getInstance().getRootComponent()),
                     coordinator, expansionHandler, mock(DynamicPrivacyController.class),
                     bypassController,
-                    mFalsingManager);
+                    mFalsingManager, mock(PluginManager.class));
             mNotificationStackScroller = mNotificationStackScrollLayout;
             mKeyguardStatusView = NotificationPanelViewTest.this.mKeyguardStatusView;
             mKeyguardStatusBar = NotificationPanelViewTest.this.mKeyguardStatusBar;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeKeyguardMonitor.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeKeyguardMonitor.java
index 95c7a4d..2fb0e0e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeKeyguardMonitor.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeKeyguardMonitor.java
@@ -80,9 +80,4 @@
     public long calculateGoingToFullShadeDelay() {
         return 0;
     }
-
-    @Override
-    public boolean canSkipBouncer() {
-        return false;
-    }
 }
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 2a2dc3d..18a8148 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -2165,7 +2165,11 @@
         }
     }
 
-    void systemReady() {
+    /**
+     * Called when the system is ready and ConnectivityService can initialize remaining components.
+     */
+    @VisibleForTesting
+    public void systemReady() {
         mProxyTracker.loadGlobalProxy();
         registerNetdEventCallback();
         mTethering.systemReady();
diff --git a/services/core/java/com/android/server/NetIdManager.java b/services/core/java/com/android/server/NetIdManager.java
index 11533be..097fb3a 100644
--- a/services/core/java/com/android/server/NetIdManager.java
+++ b/services/core/java/com/android/server/NetIdManager.java
@@ -20,6 +20,7 @@
 import android.util.SparseBooleanArray;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 
 /**
  * Class used to reserve and release net IDs.
@@ -38,14 +39,25 @@
     @GuardedBy("mNetIdInUse")
     private int mLastNetId = MIN_NET_ID - 1;
 
+    private final int mMaxNetId;
+
+    public NetIdManager() {
+        this(MAX_NET_ID);
+    }
+
+    @VisibleForTesting
+    NetIdManager(int maxNetId) {
+        mMaxNetId = maxNetId;
+    }
+
     /**
      * Get the first netId that follows the provided lastId and is available.
      */
-    private static int getNextAvailableNetIdLocked(
+    private int getNextAvailableNetIdLocked(
             int lastId, @NonNull SparseBooleanArray netIdInUse) {
         int netId = lastId;
-        for (int i = MIN_NET_ID; i <= MAX_NET_ID; i++) {
-            netId = netId < MAX_NET_ID ? netId + 1 : MIN_NET_ID;
+        for (int i = MIN_NET_ID; i <= mMaxNetId; i++) {
+            netId = netId < mMaxNetId ? netId + 1 : MIN_NET_ID;
             if (!netIdInUse.get(netId)) {
                 return netId;
             }
diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/services/core/java/com/android/server/PackageWatchdog.java
index 2ab8e03..5672a13 100644
--- a/services/core/java/com/android/server/PackageWatchdog.java
+++ b/services/core/java/com/android/server/PackageWatchdog.java
@@ -116,6 +116,10 @@
     private final AtomicFile mPolicyFile;
     private final ExplicitHealthCheckController mHealthCheckController;
     private final ConnectivityModuleConnector mConnectivityModuleConnector;
+    private final Runnable mSyncRequests = this::syncRequests;
+    private final Runnable mSyncStateWithScheduledReason = this::syncStateWithScheduledReason;
+    private final Runnable mSaveToFile = this::saveToFile;
+    private final SystemClock mSystemClock;
     @GuardedBy("mLock")
     private boolean mIsPackagesReady;
     // Flag to control whether explicit health checks are supported or not
@@ -130,18 +134,12 @@
     @GuardedBy("mLock")
     private long mUptimeAtLastStateSync;
 
-    private final Runnable mSyncRequests = this::syncRequests;
-    private final Runnable mSyncStateWithScheduledReason = this::syncStateWithScheduledReason;
-    private final Runnable mSaveToFile = this::saveToFile;
-
     @FunctionalInterface
     @VisibleForTesting
     interface SystemClock {
         long uptimeMillis();
     }
 
-    private final SystemClock mSystemClock;
-
     private PackageWatchdog(Context context) {
         // Needs to be constructed inline
         this(context, new AtomicFile(
@@ -286,28 +284,6 @@
     }
 
     /**
-     * Returns packages observed by {@code observer}
-     *
-     * @return an empty set if {@code observer} has some packages observerd from a previous boot
-     * but has not registered itself in the current boot to receive notifications. Returns null
-     * if there are no active packages monitored from any boot.
-     */
-    @Nullable
-    public Set<String> getPackages(PackageHealthObserver observer) {
-        synchronized (mLock) {
-            for (int i = 0; i < mAllObservers.size(); i++) {
-                if (observer.getName().equals(mAllObservers.keyAt(i))) {
-                    if (observer.equals(mAllObservers.valueAt(i).mRegisteredObserver)) {
-                        return mAllObservers.valueAt(i).mPackages.keySet();
-                    }
-                    return Collections.emptySet();
-                }
-            }
-        }
-        return null;
-    }
-
-    /**
      * Called when a process fails either due to a crash or ANR.
      *
      * <p>For each package contained in the process, one registered observer with the least user
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 8eb5d9b..1675b94 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -829,6 +829,15 @@
         }
     }
 
+    void killMisbehavingService(ServiceRecord r,
+            int appUid, int appPid, String localPackageName) {
+        synchronized (mAm) {
+            stopServiceLocked(r);
+            mAm.crashApplication(appUid, appPid, localPackageName, -1,
+                    "Bad notification for startForeground", true /*force*/);
+        }
+    }
+
     IBinder peekServiceLocked(Intent service, String resolvedType, String callingPackage) {
         ServiceLookupResult r = retrieveServiceLocked(service, null, resolvedType, callingPackage,
                 Binder.getCallingPid(), Binder.getCallingUid(),
@@ -3930,7 +3939,7 @@
     void serviceForegroundCrash(ProcessRecord app, CharSequence serviceRecord) {
         mAm.crashApplication(app.uid, app.pid, app.info.packageName, app.userId,
                 "Context.startForegroundService() did not then call Service.startForeground(): "
-                    + serviceRecord);
+                    + serviceRecord, false /*force*/);
     }
 
     void scheduleServiceTimeoutLocked(ProcessRecord proc) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 088d19d..fbd7757 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -3568,7 +3568,7 @@
 
     @Override
     public void crashApplication(int uid, int initialPid, String packageName, int userId,
-            String message) {
+            String message, boolean force) {
         if (checkCallingPermission(android.Manifest.permission.FORCE_STOP_PACKAGES)
                 != PackageManager.PERMISSION_GRANTED) {
             String msg = "Permission Denial: crashApplication() from pid="
@@ -3580,7 +3580,8 @@
         }
 
         synchronized(this) {
-            mAppErrors.scheduleAppCrashLocked(uid, initialPid, packageName, userId, message);
+            mAppErrors.scheduleAppCrashLocked(uid, initialPid, packageName, userId,
+                    message, force);
         }
     }
 
@@ -6323,13 +6324,6 @@
     }
 
     @Override
-    public void resizeStack(int stackId, Rect destBounds, boolean allowResizeInDockedMode,
-            boolean preserveWindows, boolean animate, int animationDuration) {
-        mActivityTaskManager.resizeStack(stackId, destBounds, allowResizeInDockedMode,
-                preserveWindows, animate, animationDuration);
-    }
-
-    @Override
     public ParceledListSlice<ActivityManager.RecentTaskInfo> getRecentTasks(int maxNum, int flags,
             int userId) {
         return mActivityTaskManager.getRecentTasks(maxNum, flags, userId);
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index a0900b6..d46c626 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -1070,7 +1070,7 @@
         } catch (NumberFormatException e) {
             packageName = arg;
         }
-        mInterface.crashApplication(-1, pid, packageName, userId, "shell-induced crash");
+        mInterface.crashApplication(-1, pid, packageName, userId, "shell-induced crash", false);
         return 0;
     }
 
@@ -2481,8 +2481,6 @@
         switch (op) {
             case "move-task":
                 return runStackMoveTask(pw);
-            case "resize":
-                return runStackResize(pw);
             case "resize-animated":
                 return runStackResizeAnimated(pw);
             case "resize-docked-stack":
@@ -2561,17 +2559,6 @@
         return 0;
     }
 
-    int runStackResize(PrintWriter pw) throws RemoteException {
-        String stackIdStr = getNextArgRequired();
-        int stackId = Integer.parseInt(stackIdStr);
-        final Rect bounds = getBounds();
-        if (bounds == null) {
-            getErrPrintWriter().println("Error: invalid input bounds");
-            return -1;
-        }
-        return resizeStack(stackId, bounds, 0);
-    }
-
     int runStackResizeAnimated(PrintWriter pw) throws RemoteException {
         String stackIdStr = getNextArgRequired();
         int stackId = Integer.parseInt(stackIdStr);
@@ -2585,16 +2572,7 @@
                 return -1;
             }
         }
-        return resizeStackUnchecked(stackId, bounds, 0, true);
-    }
-
-    int resizeStackUnchecked(int stackId, Rect bounds, int delayMs, boolean animate)
-            throws RemoteException {
-        try {
-            mTaskInterface.resizeStack(stackId, bounds, false, false, animate, -1);
-            Thread.sleep(delayMs);
-        } catch (InterruptedException e) {
-        }
+        mTaskInterface.animateResizePinnedStack(stackId, bounds, -1);
         return 0;
     }
 
@@ -2609,14 +2587,6 @@
         return 0;
     }
 
-    int resizeStack(int stackId, Rect bounds, int delayMs) throws RemoteException {
-        if (bounds == null) {
-            getErrPrintWriter().println("Error: invalid input bounds");
-            return -1;
-        }
-        return resizeStackUnchecked(stackId, bounds, delayMs, false);
-    }
-
     int runStackPositionTask(PrintWriter pw) throws RemoteException {
         String taskIdStr = getNextArgRequired();
         int taskId = Integer.parseInt(taskIdStr);
@@ -3195,8 +3165,6 @@
             pw.println("       move-task <TASK_ID> <STACK_ID> [true|false]");
             pw.println("           Move <TASK_ID> from its current stack to the top (true) or");
             pw.println("           bottom (false) of <STACK_ID>.");
-            pw.println("       resize <STACK_ID> <LEFT,TOP,RIGHT,BOTTOM>");
-            pw.println("           Change <STACK_ID> size and position to <LEFT,TOP,RIGHT,BOTTOM>.");
             pw.println("       resize-animated <STACK_ID> <LEFT,TOP,RIGHT,BOTTOM>");
             pw.println("           Same as resize, but allow animation.");
             pw.println("       resize-docked-stack <LEFT,TOP,RIGHT,BOTTOM> [<TASK_LEFT,TASK_TOP,TASK_RIGHT,TASK_BOTTOM>]");
diff --git a/services/core/java/com/android/server/am/AppErrors.java b/services/core/java/com/android/server/am/AppErrors.java
index 1ff6f4d..6a29c75 100644
--- a/services/core/java/com/android/server/am/AppErrors.java
+++ b/services/core/java/com/android/server/am/AppErrors.java
@@ -314,20 +314,24 @@
     }
 
     void killAppAtUserRequestLocked(ProcessRecord app, Dialog fromDialog) {
-        app.setCrashing(false);
-        app.crashingReport = null;
-        app.setNotResponding(false);
-        app.notRespondingReport = null;
         if (app.anrDialog == fromDialog) {
             app.anrDialog = null;
         }
         if (app.waitDialog == fromDialog) {
             app.waitDialog = null;
         }
+        killAppImmediateLocked(app, "user-terminated", "user request after error");
+    }
+
+    private void killAppImmediateLocked(ProcessRecord app, String reason, String killReason) {
+        app.setCrashing(false);
+        app.crashingReport = null;
+        app.setNotResponding(false);
+        app.notRespondingReport = null;
         if (app.pid > 0 && app.pid != MY_PID) {
-            handleAppCrashLocked(app, "user-terminated" /*reason*/,
+            handleAppCrashLocked(app, reason,
                     null /*shortMsg*/, null /*longMsg*/, null /*stackTrace*/, null /*data*/);
-            app.kill("user request after error", true);
+            app.kill(killReason, true);
         }
     }
 
@@ -341,7 +345,7 @@
      * @param message
      */
     void scheduleAppCrashLocked(int uid, int initialPid, String packageName, int userId,
-            String message) {
+            String message, boolean force) {
         ProcessRecord proc = null;
 
         // Figure out which process to kill.  We don't trust that initialPid
@@ -374,6 +378,14 @@
         }
 
         proc.scheduleCrash(message);
+        if (force) {
+            // If the app is responsive, the scheduled crash will happen as expected
+            // and then the delayed summary kill will be a no-op.
+            final ProcessRecord p = proc;
+            mService.mHandler.postDelayed(
+                    () -> killAppImmediateLocked(p, "forced", "killed for invalid state"),
+                    5000L);
+        }
     }
 
     /**
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 7a3d3c2..3c2aee4 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -1576,7 +1576,7 @@
         }
 
         if (adj == ProcessList.SERVICE_ADJ) {
-            if (doingAll) {
+            if (doingAll && !cycleReEval) {
                 app.serviceb = mNewNumAServiceProcs > (mNumServiceProcs/3);
                 mNewNumServiceProcs++;
                 //Slog.i(TAG, "ADJ " + app + " serviceb=" + app.serviceb);
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index 8c038aa..cc4b160 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -803,6 +803,7 @@
             final String localPackageName = packageName;
             final int localForegroundId = foregroundId;
             final Notification _foregroundNoti = foregroundNoti;
+            final ServiceRecord record = this;
             ams.mHandler.post(new Runnable() {
                 public void run() {
                     NotificationManagerInternal nm = LocalServices.getService(
@@ -901,10 +902,8 @@
                         Slog.w(TAG, "Error showing notification for service", e);
                         // If it gave us a garbage notification, it doesn't
                         // get to be foreground.
-                        ams.setServiceForeground(instanceName, ServiceRecord.this,
-                                0, null, 0, 0);
-                        ams.crashApplication(appUid, appPid, localPackageName, -1,
-                                "Bad notification for startForeground: " + e);
+                        ams.mServices.killMisbehavingService(record,
+                                appUid, appPid, localPackageName);
                     }
                 }
             });
diff --git a/services/core/java/com/android/server/camera/CameraServiceProxy.java b/services/core/java/com/android/server/camera/CameraServiceProxy.java
index 8de2595..09f5286 100644
--- a/services/core/java/com/android/server/camera/CameraServiceProxy.java
+++ b/services/core/java/com/android/server/camera/CameraServiceProxy.java
@@ -36,6 +36,7 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.Slog;
+import android.util.StatsLog;
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
@@ -44,6 +45,9 @@
 import com.android.server.SystemService;
 import com.android.server.wm.WindowManagerInternal;
 
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
@@ -102,6 +106,9 @@
 
     private final boolean mNotifyNfc;
 
+    private ScheduledThreadPoolExecutor mLogWriterService = new ScheduledThreadPoolExecutor(
+            /*corePoolSize*/ 1);
+
     /**
      * Structure to track camera usage
      */
@@ -204,6 +211,9 @@
 
         mNotifyNfc = SystemProperties.getInt(NFC_NOTIFICATION_PROP, 0) > 0;
         if (DEBUG) Slog.v(TAG, "Notify NFC behavior is " + (mNotifyNfc ? "active" : "disabled"));
+        // Don't keep any extra logging threads if not needed
+        mLogWriterService.setKeepAliveTime(1, TimeUnit.SECONDS);
+        mLogWriterService.allowCoreThreadTimeOut(true);
     }
 
     @Override
@@ -279,6 +289,51 @@
         }
     }
 
+    private class EventWriterTask implements Runnable {
+        private ArrayList<CameraUsageEvent> mEventList;
+        private static final long WRITER_SLEEP_MS = 100;
+
+        public EventWriterTask(ArrayList<CameraUsageEvent> eventList) {
+            mEventList = eventList;
+        }
+
+        @Override
+        public void run() {
+            if (mEventList != null) {
+                for (CameraUsageEvent event : mEventList) {
+                    logCameraUsageEvent(event);
+                    try {
+                        Thread.sleep(WRITER_SLEEP_MS);
+                    } catch (InterruptedException e) {}
+                }
+                mEventList.clear();
+            }
+        }
+
+        /**
+         * Write camera usage events to stats log.
+         * Package-private
+         */
+        private void logCameraUsageEvent(CameraUsageEvent e) {
+            int facing = StatsLog.CAMERA_ACTION_EVENT__FACING__UNKNOWN;
+            switch(e.mCameraFacing) {
+                case ICameraServiceProxy.CAMERA_FACING_BACK:
+                    facing = StatsLog.CAMERA_ACTION_EVENT__FACING__BACK;
+                    break;
+                case ICameraServiceProxy.CAMERA_FACING_FRONT:
+                    facing = StatsLog.CAMERA_ACTION_EVENT__FACING__FRONT;
+                    break;
+                case ICameraServiceProxy.CAMERA_FACING_EXTERNAL:
+                    facing = StatsLog.CAMERA_ACTION_EVENT__FACING__EXTERNAL;
+                    break;
+                default:
+                    Slog.w(TAG, "Unknown camera facing: " + e.mCameraFacing);
+            }
+            StatsLog.write(StatsLog.CAMERA_ACTION_EVENT, e.getDuration(), e.mAPILevel,
+                    e.mClientName, facing);
+        }
+    }
+
     /**
      * Dump camera usage events to log.
      * Package-private
@@ -315,6 +370,10 @@
                         .setPackageName(e.mClientName);
                 mLogger.write(l);
             }
+
+            mLogWriterService.execute(new EventWriterTask(
+                        new ArrayList<CameraUsageEvent>(mCameraUsageHistory)));
+
             mCameraUsageHistory.clear();
         }
         final long ident = Binder.clearCallingIdentity();
diff --git a/services/core/java/com/android/server/firewall/IntentFirewall.java b/services/core/java/com/android/server/firewall/IntentFirewall.java
index 376a864..c2af29c 100644
--- a/services/core/java/com/android/server/firewall/IntentFirewall.java
+++ b/services/core/java/com/android/server/firewall/IntentFirewall.java
@@ -32,10 +32,12 @@
 import android.util.ArrayMap;
 import android.util.Slog;
 import android.util.Xml;
+
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.XmlUtils;
 import com.android.server.EventLogTags;
 import com.android.server.IntentResolver;
+
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
@@ -562,7 +564,7 @@
 
         @Override
         public void onEvent(int event, String path) {
-            if (path.endsWith(".xml")) {
+            if (path != null && path.endsWith(".xml")) {
                 // we wait 250ms before taking any action on an event, in order to dedup multiple
                 // events. E.g. a delete event followed by a create event followed by a subsequent
                 // write+close event
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index dacf372..9a85f952 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -1635,6 +1635,10 @@
         removeAction(SystemAudioAutoInitiationAction.class);
         removeAction(SystemAudioStatusAction.class);
         removeAction(VolumeControlAction.class);
+
+        if (!mService.isControlEnabled()) {
+            setSystemAudioMode(false);
+        }
     }
 
     @ServiceThreadOnly
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index ccc0d4b..b7eca29 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -1461,7 +1461,7 @@
     // This method should be called by LockPatternUtil only, all internal methods in this class
     // should call setLockCredentialInternal.
     @Override
-    public void setLockCredential(byte[] credential, int type,
+    public boolean setLockCredential(byte[] credential, int type,
             byte[] savedCredential, int requestedQuality, int userId,
             boolean allowUntrustedChange) {
 
@@ -1471,8 +1471,10 @@
         }
         checkWritePermission(userId);
         synchronized (mSeparateChallengeLock) {
-            setLockCredentialInternal(credential, type, savedCredential, requestedQuality, userId,
-                    allowUntrustedChange, /* isLockTiedToParent= */ false);
+            if (!setLockCredentialInternal(credential, type, savedCredential, requestedQuality,
+                    userId, allowUntrustedChange, /* isLockTiedToParent= */ false)) {
+                return false;
+            }
             setSeparateProfileChallengeEnabledLocked(userId, true, null);
             notifyPasswordChanged(userId);
         }
@@ -1481,13 +1483,14 @@
             setDeviceUnlockedForUser(userId);
         }
         notifySeparateProfileChallengeChanged(userId);
+        return true;
     }
 
     /**
      * @param isLockTiedToParent is {@code true} if {@code userId} is a profile and its new
      *     credentials are being tied to its parent's credentials.
      */
-    private void setLockCredentialInternal(byte[] credential, @CredentialType int credentialType,
+    private boolean setLockCredentialInternal(byte[] credential, @CredentialType int credentialType,
             byte[] savedCredential, int requestedQuality, int userId, boolean allowUntrustedChange,
             boolean isLockTiedToParent) {
         // Normalize savedCredential and credential such that empty string is always represented
@@ -1500,9 +1503,9 @@
         }
         synchronized (mSpManager) {
             if (isSyntheticPasswordBasedCredentialLocked(userId)) {
-                spBasedSetLockCredentialInternalLocked(credential, credentialType, savedCredential,
-                        requestedQuality, userId, allowUntrustedChange, isLockTiedToParent);
-                return;
+                return spBasedSetLockCredentialInternalLocked(credential, credentialType,
+                        savedCredential, requestedQuality, userId, allowUntrustedChange,
+                        isLockTiedToParent);
             }
         }
 
@@ -1519,7 +1522,7 @@
             setUserPasswordMetrics(CREDENTIAL_TYPE_NONE, null, userId);
             sendCredentialsOnChangeIfRequired(
                     credentialType, credential, userId, isLockTiedToParent);
-            return;
+            return true;
         }
         if (credential == null) {
             throw new IllegalArgumentException("Null credential with mismatched credential type");
@@ -1552,37 +1555,38 @@
             if (shouldMigrateToSyntheticPasswordLocked(userId)) {
                 initializeSyntheticPasswordLocked(currentHandle.hash, savedCredential,
                         currentHandle.type, requestedQuality, userId);
-                spBasedSetLockCredentialInternalLocked(credential, credentialType, savedCredential,
-                        requestedQuality, userId, allowUntrustedChange, isLockTiedToParent);
-                return;
+                return spBasedSetLockCredentialInternalLocked(credential, credentialType,
+                        savedCredential, requestedQuality, userId, allowUntrustedChange,
+                        isLockTiedToParent);
             }
         }
         if (DEBUG) Slog.d(TAG, "setLockCredentialInternal: user=" + userId);
         byte[] enrolledHandle = enrollCredential(currentHandle.hash, savedCredential, credential,
                 userId);
-        if (enrolledHandle != null) {
-            CredentialHash willStore = CredentialHash.create(enrolledHandle, credentialType);
-            mStorage.writeCredentialHash(willStore, userId);
-            // push new secret and auth token to vold
-            GateKeeperResponse gkResponse;
-            try {
-                gkResponse = getGateKeeperService().verifyChallenge(userId, 0, willStore.hash,
-                        credential);
-            } catch (RemoteException e) {
-                throw new IllegalStateException("Failed to verify current credential", e);
-            }
-            setUserKeyProtection(userId, credential, convertResponse(gkResponse));
-            fixateNewestUserKeyAuth(userId);
-            // Refresh the auth token
-            doVerifyCredential(credential, credentialType, CHALLENGE_FROM_CALLER, 0, userId,
-                    null /* progressCallback */);
-            synchronizeUnifiedWorkChallengeForProfiles(userId, null);
-            sendCredentialsOnChangeIfRequired(
-                    credentialType, credential, userId, isLockTiedToParent);
-        } else {
-            throw new IllegalStateException(String.format("Failed to enroll %s",
+        if (enrolledHandle == null) {
+            Slog.w(TAG, String.format("Failed to enroll %s: incorrect credential",
                     credentialType == CREDENTIAL_TYPE_PASSWORD ? "password" : "pattern"));
+            return false;
         }
+        CredentialHash willStore = CredentialHash.create(enrolledHandle, credentialType);
+        mStorage.writeCredentialHash(willStore, userId);
+        // push new secret and auth token to vold
+        GateKeeperResponse gkResponse;
+        try {
+            gkResponse = getGateKeeperService().verifyChallenge(userId, 0, willStore.hash,
+                    credential);
+        } catch (RemoteException e) {
+            throw new IllegalStateException("Failed to verify current credential", e);
+        }
+        setUserKeyProtection(userId, credential, convertResponse(gkResponse));
+        fixateNewestUserKeyAuth(userId);
+        // Refresh the auth token
+        doVerifyCredential(credential, credentialType, CHALLENGE_FROM_CALLER, 0, userId,
+                null /* progressCallback */);
+        synchronizeUnifiedWorkChallengeForProfiles(userId, null);
+        sendCredentialsOnChangeIfRequired(
+                credentialType, credential, userId, isLockTiedToParent);
+        return true;
     }
 
     private VerifyCredentialResponse convertResponse(GateKeeperResponse gateKeeperResponse) {
@@ -2711,7 +2715,7 @@
     }
 
     @GuardedBy("mSpManager")
-    private void spBasedSetLockCredentialInternalLocked(byte[] credential, int credentialType,
+    private boolean spBasedSetLockCredentialInternalLocked(byte[] credential, int credentialType,
             byte[] savedCredential, int requestedQuality, int userId,
             boolean allowUntrustedChange, boolean isLockTiedToParent) {
         if (DEBUG) Slog.d(TAG, "spBasedSetLockCredentialInternalLocked: user=" + userId);
@@ -2736,8 +2740,9 @@
 
         // If existing credential is provided, the existing credential must match.
         if (savedCredential != null && auth == null) {
-            throw new IllegalStateException(String.format("Failed to enroll %s",
+            Slog.w(TAG, String.format("Failed to enroll %s: incorrect credential",
                     credentialType == CREDENTIAL_TYPE_PASSWORD ? "password" : "pattern"));
+            return false;
         }
         boolean untrustedReset = false;
         if (auth != null) {
@@ -2757,7 +2762,8 @@
             }
             untrustedReset = true;
         } else /* responseCode == VerifyCredentialResponse.RESPONSE_RETRY */ {
-            throw new IllegalStateException("Rate limit exceeded, so password was not changed.");
+            Slog.w(TAG, "Rate limit exceeded, so password was not changed.");
+            return false;
         }
 
         if (auth != null) {
@@ -2779,6 +2785,7 @@
             // synthetic password. That would invalidate existing escrow tokens though.
         }
         sendCredentialsOnChangeIfRequired(credentialType, credential, userId, isLockTiedToParent);
+        return true;
     }
 
     /**
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
index fe12a94..5594614 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
@@ -47,6 +47,7 @@
 import java.io.DataInputStream;
 import java.io.DataOutputStream;
 import java.io.File;
+import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.RandomAccessFile;
 import java.nio.channels.FileChannel;
@@ -325,23 +326,16 @@
             version = mCache.getVersion();
         }
 
-        RandomAccessFile raf = null;
         byte[] stored = null;
-        try {
-            raf = new RandomAccessFile(name, "r");
+        try (RandomAccessFile raf = new RandomAccessFile(name, "r")) {
             stored = new byte[(int) raf.length()];
             raf.readFully(stored, 0, stored.length);
             raf.close();
+        } catch (FileNotFoundException suppressed) {
+            // readFile() is also called by hasFile() to check the existence of files, in this
+            // case FileNotFoundException is expected.
         } catch (IOException e) {
             Slog.e(TAG, "Cannot read file " + e);
-        } finally {
-            if (raf != null) {
-                try {
-                    raf.close();
-                } catch (IOException e) {
-                    Slog.e(TAG, "Error closing file " + e);
-                }
-            }
         }
         mCache.putFileIfUnchanged(name, stored, version);
         return stored;
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index f04d459..f71b362 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -457,7 +457,7 @@
     private static final int MY_UID = Process.myUid();
     private static final int MY_PID = Process.myPid();
     private static final IBinder WHITELIST_TOKEN = new Binder();
-    private RankingHandler mRankingHandler;
+    protected RankingHandler mRankingHandler;
     private long mLastOverRateLogTime;
     private float mMaxPackageEnqueueRate = DEFAULT_MAX_NOTIFICATION_ENQUEUE_RATE;
 
@@ -926,7 +926,7 @@
                         () -> mAm.crashApplication(uid, initialPid, pkg, -1,
                             "Bad notification(tag=" + tag + ", id=" + id + ") posted from package "
                                 + pkg + ", crashing app(uid=" + uid + ", pid=" + initialPid + "): "
-                                + message));
+                                + message, true /* force */));
             }
         }
 
@@ -1550,10 +1550,12 @@
 
     @VisibleForTesting
     void clearNotifications() {
-        mEnqueuedNotifications.clear();
-        mNotificationList.clear();
-        mNotificationsByKey.clear();
-        mSummaryByGroupKey.clear();
+        synchronized (mNotificationList) {
+            mEnqueuedNotifications.clear();
+            mNotificationList.clear();
+            mNotificationsByKey.clear();
+            mSummaryByGroupKey.clear();
+        }
     }
 
     @VisibleForTesting
@@ -1605,11 +1607,6 @@
     void setPreferencesHelper(PreferencesHelper prefHelper) { mPreferencesHelper = prefHelper; }
 
     @VisibleForTesting
-    void setRankingHandler(RankingHandler rankingHandler) {
-        mRankingHandler = rankingHandler;
-    }
-
-    @VisibleForTesting
     void setZenHelper(ZenModeHelper zenHelper) {
         mZenModeHelper = zenHelper;
     }
@@ -1641,7 +1638,7 @@
 
     // TODO: All tests should use this init instead of the one-off setters above.
     @VisibleForTesting
-    void init(Looper looper, IPackageManager packageManager,
+    void init(Looper looper, RankingHandler rankingHandler, IPackageManager packageManager,
             PackageManager packageManagerClient,
             LightsManager lightsManager, NotificationListeners notificationListeners,
             NotificationAssistants notificationAssistants, ConditionProviders conditionProviders,
@@ -1675,7 +1672,6 @@
         mUm = userManager;
 
         mHandler = new WorkerHandler(looper);
-        mRankingThread.start();
         String[] extractorNames;
         try {
             extractorNames = resources.getStringArray(R.array.config_notificationSignalExtractors);
@@ -1684,7 +1680,7 @@
         }
         mUsageStats = usageStats;
         mMetricsLogger = new MetricsLogger();
-        mRankingHandler = new RankingHandlerWorker(mRankingThread.getLooper());
+        mRankingHandler = rankingHandler;
         mConditionProviders = conditionProviders;
         mZenModeHelper = new ZenModeHelper(getContext(), mHandler.getLooper(), mConditionProviders);
         mZenModeHelper.addCallback(new ZenModeHelper.Callback() {
@@ -1829,8 +1825,9 @@
         }, mUserProfiles);
 
         final File systemDir = new File(Environment.getDataDirectory(), "system");
+        mRankingThread.start();
 
-        init(Looper.myLooper(),
+        init(Looper.myLooper(), new RankingHandlerWorker(mRankingThread.getLooper()),
                 AppGlobals.getPackageManager(), getContext().getPackageManager(),
                 getLocalService(LightsManager.class),
                 new NotificationListeners(AppGlobals.getPackageManager()),
diff --git a/services/core/java/com/android/server/notification/ZenLog.java b/services/core/java/com/android/server/notification/ZenLog.java
index c6af756..8f05636 100644
--- a/services/core/java/com/android/server/notification/ZenLog.java
+++ b/services/core/java/com/android/server/notification/ZenLog.java
@@ -179,6 +179,7 @@
             case TYPE_SUPPRESSOR_CHANGED: return "suppressor_changed";
             case TYPE_LISTENER_HINTS_CHANGED: return "listener_hints_changed";
             case TYPE_SET_NOTIFICATION_POLICY: return "set_notification_policy";
+            case TYPE_SET_CONSOLIDATED_ZEN_POLICY: return "set_consolidated_policy";
             default: return "unknown";
         }
     }
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index ee948b2..f63aa52 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -954,12 +954,11 @@
     }
 
     private void applyCustomPolicy(ZenPolicy policy, ZenRule rule) {
-        if (rule.zenMode == NotificationManager.INTERRUPTION_FILTER_NONE) {
+        if (rule.zenMode == Global.ZEN_MODE_NO_INTERRUPTIONS) {
             policy.apply(new ZenPolicy.Builder()
                     .disallowAllSounds()
                     .build());
-        } else if (rule.zenMode
-                == NotificationManager.INTERRUPTION_FILTER_ALARMS) {
+        } else if (rule.zenMode == Global.ZEN_MODE_ALARMS) {
             policy.apply(new ZenPolicy.Builder()
                     .disallowAllSounds()
                     .allowAlarms(true)
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index af1a095..fe529a1 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -276,7 +276,7 @@
                 case "get-harmful-app-warning":
                     return runGetHarmfulAppWarning();
                 case "get-stagedsessions":
-                    return getStagedSessions();
+                    return runListStagedSessions();
                 case "uninstall-system-updates":
                     return uninstallSystemUpdates();
                 case "rollback-app":
@@ -348,53 +348,6 @@
         return 1;
     }
 
-    private int getStagedSessions() {
-        final IndentingPrintWriter pw = new IndentingPrintWriter(
-                getOutPrintWriter(), /* singleIndent */ "  ", /* wrapLength */ 120);
-        try {
-            List<SessionInfo> stagedSessions =
-                    mInterface.getPackageInstaller().getStagedSessions().getList();
-            final SparseArray<SessionInfo> sessionById = new SparseArray<>(stagedSessions.size());
-            for (SessionInfo session : stagedSessions) {
-                sessionById.put(session.getSessionId(), session);
-            }
-            for (SessionInfo session: stagedSessions) {
-                if (session.getParentSessionId() != SessionInfo.INVALID_ID) {
-                    continue;
-                }
-                printStagedSession(session, pw);
-                if (session.isMultiPackage()) {
-                    pw.increaseIndent();
-                    final int[] childIds = session.getChildSessionIds();
-                    for (int i = 0; i < childIds.length; i++) {
-                        final SessionInfo childSession = sessionById.get(childIds[i]);
-                        if (childSession == null) {
-                            pw.println("sessionId = " + childIds[i] + "; not found");
-                        } else {
-                            printStagedSession(childSession, pw);
-                        }
-                    }
-                    pw.decreaseIndent();
-                }
-            }
-        } catch (RemoteException e) {
-            pw.println("Failure ["
-                    + e.getClass().getName() + " - "
-                    + e.getMessage() + "]");
-            return 0;
-        }
-        return 1;
-    }
-
-    private static void printStagedSession(SessionInfo session, PrintWriter pw) {
-        pw.println("sessionId = " + session.getSessionId()
-                + "; appPackageName = " + session.getAppPackageName()
-                + "; isStaged = " + session.isStaged()
-                + "; isReady = " + session.isStagedSessionReady()
-                + "; isApplied = " + session.isStagedSessionApplied()
-                + "; isFailed = " + session.isStagedSessionFailed() + ";");
-    }
-
     private int uninstallSystemUpdates() {
         final PrintWriter pw = getOutPrintWriter();
         List<String> failedUninstalls = new LinkedList<>();
@@ -567,6 +520,8 @@
                 return runListPermissionGroups();
             case "permissions":
                 return runListPermissions();
+            case "staged-sessions":
+                return runListStagedSessions();
             case "users":
                 ServiceManager.getService("user").shellCommand(
                         getInFileDescriptor(), getOutFileDescriptor(), getErrFileDescriptor(),
@@ -904,6 +859,103 @@
         return 0;
     }
 
+    private static class SessionDump {
+        boolean onlyParent; // Show parent sessions only
+        boolean onlyReady; // Show only staged sessions that are in ready state
+        boolean onlySessionId; // Show sessionId only
+    }
+
+    // Returns true if the provided flag is a session flag and given SessionDump was updated
+    private boolean setSessionFlag(String flag, SessionDump sessionDump) {
+        switch (flag) {
+            case "--only-parent":
+                sessionDump.onlyParent = true;
+                break;
+            case "--only-ready":
+                sessionDump.onlyReady = true;
+                break;
+            case "--only-sessionid":
+                sessionDump.onlySessionId = true;
+                break;
+            default:
+                return false;
+        }
+        return true;
+    }
+
+    private int runListStagedSessions() {
+        final IndentingPrintWriter pw = new IndentingPrintWriter(
+                getOutPrintWriter(), /* singleIndent */ "  ", /* wrapLength */ 120);
+
+        SessionDump sessionDump = new SessionDump();
+        String opt;
+        while ((opt = getNextOption()) != null) {
+            if (!setSessionFlag(opt, sessionDump)) {
+                pw.println("Error: Unknown option: " + opt);
+                return -1;
+            }
+        }
+
+        try {
+            List<SessionInfo> stagedSessions =
+                    mInterface.getPackageInstaller().getStagedSessions().getList();
+            printSessionList(pw, stagedSessions, sessionDump);
+        } catch (RemoteException e) {
+            pw.println("Failure ["
+                    + e.getClass().getName() + " - "
+                    + e.getMessage() + "]");
+            return -1;
+        }
+        return 1;
+    }
+
+    private void printSessionList(IndentingPrintWriter pw, List<SessionInfo> stagedSessions,
+            SessionDump sessionDump) {
+        final SparseArray<SessionInfo> sessionById = new SparseArray<>(stagedSessions.size());
+        for (SessionInfo session : stagedSessions) {
+            sessionById.put(session.getSessionId(), session);
+        }
+        for (SessionInfo session: stagedSessions) {
+            if (sessionDump.onlyReady && !session.isStagedSessionReady()) {
+                continue;
+            }
+            if (session.getParentSessionId() != SessionInfo.INVALID_ID) {
+                continue;
+            }
+            printSession(pw, session, sessionDump);
+            if (session.isMultiPackage() && !sessionDump.onlyParent) {
+                pw.increaseIndent();
+                final int[] childIds = session.getChildSessionIds();
+                for (int i = 0; i < childIds.length; i++) {
+                    final SessionInfo childSession = sessionById.get(childIds[i]);
+                    if (childSession == null) {
+                        if (sessionDump.onlySessionId) {
+                            pw.println(childIds[i]);
+                        } else {
+                            pw.println("sessionId = " + childIds[i] + "; not found");
+                        }
+                    } else {
+                        printSession(pw, childSession, sessionDump);
+                    }
+                }
+                pw.decreaseIndent();
+            }
+        }
+    }
+
+    private static void printSession(PrintWriter pw, SessionInfo session, SessionDump sessionDump) {
+        if (sessionDump.onlySessionId) {
+            pw.println(session.getSessionId());
+            return;
+        }
+        pw.println("sessionId = " + session.getSessionId()
+                + "; appPackageName = " + session.getAppPackageName()
+                + "; isStaged = " + session.isStaged()
+                + "; isReady = " + session.isStagedSessionReady()
+                + "; isApplied = " + session.isStagedSessionApplied()
+                + "; isFailed = " + session.isStagedSessionFailed() + ";");
+    }
+
     private Intent parseIntentAndUser() throws URISyntaxException {
         mTargetUser = UserHandle.USER_CURRENT;
         mBrief = false;
@@ -3108,6 +3160,12 @@
         pw.println("      -d: only list dangerous permissions");
         pw.println("      -u: list only the permissions users will see");
         pw.println("");
+        pw.println("  list staged-sessions [--only-ready] [--only-sessionid] [--only-parent]");
+        pw.println("    Displays list of all staged sessions on device.");
+        pw.println("      --only-ready: show only staged sessions that are ready");
+        pw.println("      --only-sessionid: show only sessionId of each session");
+        pw.println("      --only-parent: hide all children sessions");
+        pw.println("");
         pw.println("  resolve-activity [--brief] [--components] [--query-flags FLAGS]");
         pw.println("       [--user USER_ID] INTENT");
         pw.println("    Prints the activity that resolves to the given INTENT.");
@@ -3345,7 +3403,7 @@
         pw.println("  uninstall-system-updates");
         pw.println("    Remove updates to all system applications and fall back to their /system " +
                 "version.");
-        pw.println();
+        pw.println("");
         pw.println("  get-moduleinfo [--all | --installed] [module-name]");
         pw.println("    Displays module info. If module-name is specified only that info is shown");
         pw.println("    By default, without any argument only installed modules are shown.");
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index 8d1ed43..3f2cadeb 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -39,6 +39,8 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
 import android.os.ParcelFileDescriptor;
 import android.os.PowerManager;
 import android.os.RemoteException;
@@ -58,6 +60,7 @@
 import java.util.List;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
 import java.util.function.Predicate;
 import java.util.stream.Collectors;
 
@@ -72,7 +75,7 @@
     private final PackageInstallerService mPi;
     private final ApexManager mApexManager;
     private final PowerManager mPowerManager;
-    private final Handler mBgHandler;
+    private final PreRebootVerificationHandler mPreRebootVerificationHandler;
 
     @GuardedBy("mStagedSessions")
     private final SparseArray<PackageInstallerSession> mStagedSessions = new SparseArray<>();
@@ -81,7 +84,8 @@
         mPi = pi;
         mApexManager = am;
         mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
-        mBgHandler = BackgroundThread.getHandler();
+        mPreRebootVerificationHandler = new PreRebootVerificationHandler(
+                BackgroundThread.get().getLooper());
     }
 
     private void updateStoredSession(@NonNull PackageInstallerSession sessionInfo) {
@@ -249,75 +253,6 @@
         return (session.params.installFlags & PackageManager.INSTALL_APEX) != 0;
     }
 
-    private void preRebootVerification(@NonNull PackageInstallerSession session) {
-        Slog.d(TAG, "Starting preRebootVerification for session " + session.sessionId);
-        final boolean hasApex = sessionContainsApex(session);
-        // APEX checks. For single-package sessions, check if they contain an APEX. For
-        // multi-package sessions, find all the child sessions that contain an APEX.
-        if (hasApex) {
-            try {
-                final List<PackageInfo> apexPackages = submitSessionToApexService(session);
-                for (PackageInfo apexPackage : apexPackages) {
-                    validateApexSignature(apexPackage, session.params.installFlags);
-                }
-            } catch (PackageManagerException e) {
-                session.setStagedSessionFailed(e.error, e.getMessage());
-                return;
-            }
-        }
-
-        if (sessionContainsApk(session)) {
-            try {
-                Slog.d(TAG, "Running a pre-reboot verification for APKs in session "
-                        + session.sessionId + " by performing a dry-run install");
-                installApksInSession(session, /* preReboot */ true);
-                // TODO(b/118865310): abort the session on apexd.
-            } catch (PackageManagerException e) {
-                session.setStagedSessionFailed(e.error, e.getMessage());
-                return;
-            }
-        }
-
-        if ((session.params.installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0) {
-            // If rollback is enabled for this session, we call through to the RollbackManager
-            // with the list of sessions it must enable rollback for. Note that notifyStagedSession
-            // is a synchronous operation.
-            final IRollbackManager rm = IRollbackManager.Stub.asInterface(
-                    ServiceManager.getService(Context.ROLLBACK_SERVICE));
-            try {
-                // NOTE: To stay consistent with the non-staged install flow, we don't fail the
-                // entire install if rollbacks can't be enabled.
-                if (!rm.notifyStagedSession(session.sessionId)) {
-                    Slog.e(TAG, "Unable to enable rollback for session: " + session.sessionId);
-                }
-            } catch (RemoteException re) {
-                // Cannot happen, the rollback manager is in the same process.
-            }
-        }
-
-        // Proactively mark session as ready before calling apexd. Although this call order looks
-        // counter-intuitive, this is the easiest way to ensure that session won't end up in the
-        // inconsistent state:
-        //  - If device gets rebooted right before call to apexd, then apexd will never activate
-        //      apex files of this staged session. This will result in StagingManager failing the
-        //      session.
-        // On the other hand, if the order of the calls was inverted (first call apexd, then mark
-        // session as ready), then if a device gets rebooted right after the call to apexd, only
-        // apex part of the train will be applied, leaving device in an inconsistent state.
-        Slog.d(TAG, "Marking session " + session.sessionId + " as ready");
-        session.setStagedSessionReady();
-        if (!hasApex) {
-            // Session doesn't contain apex, nothing to do.
-            return;
-        }
-        try {
-            mApexManager.markStagedSessionReady(session.sessionId);
-        } catch (PackageManagerException e) {
-            session.setStagedSessionFailed(e.error, e.getMessage());
-        }
-    }
-
-
     private boolean sessionContains(@NonNull PackageInstallerSession session,
                                     Predicate<PackageInstallerSession> filter) {
         if (!session.isMultiPackage()) {
@@ -366,7 +301,7 @@
                 // Greedily re-trigger the pre-reboot verification.
                 Slog.d(TAG, "Found pending staged session " + session.sessionId + " still to be "
                         + "verified, resuming pre-reboot verification");
-                mBgHandler.post(() -> preRebootVerification(session));
+                mPreRebootVerificationHandler.startPreRebootVerification(session);
                 return;
             }
             if (!apexSessionInfo.isActivated && !apexSessionInfo.isSuccess) {
@@ -476,34 +411,52 @@
     }
 
     private void commitApkSession(@NonNull PackageInstallerSession apkSession,
-            int originalSessionId, boolean preReboot) throws PackageManagerException {
+            PackageInstallerSession originalSession, boolean preReboot)
+            throws PackageManagerException {
         final int errorCode = preReboot ? SessionInfo.STAGED_SESSION_VERIFICATION_FAILED
                 : SessionInfo.STAGED_SESSION_ACTIVATION_FAILED;
-        if (!preReboot) {
-            if ((apkSession.params.installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0) {
-                // If rollback is available for this session, notify the rollback
-                // manager of the apk session so it can properly enable rollback.
-                final IRollbackManager rm = IRollbackManager.Stub.asInterface(
-                        ServiceManager.getService(Context.ROLLBACK_SERVICE));
-                try {
-                    rm.notifyStagedApkSession(originalSessionId, apkSession.sessionId);
-                } catch (RemoteException re) {
-                    // Cannot happen, the rollback manager is in the same process.
-                }
+        if (preReboot) {
+            final LocalIntentReceiverAsync receiver = new LocalIntentReceiverAsync(
+                    (Intent result) -> {
+                        int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
+                                PackageInstaller.STATUS_FAILURE);
+                        if (status != PackageInstaller.STATUS_SUCCESS) {
+                            final String errorMessage = result.getStringExtra(
+                                    PackageInstaller.EXTRA_STATUS_MESSAGE);
+                            Slog.e(TAG, "Failure to install APK staged session "
+                                    + originalSession.sessionId + " [" + errorMessage + "]");
+                            originalSession.setStagedSessionFailed(errorCode, errorMessage);
+                            return;
+                        }
+                        mPreRebootVerificationHandler.notifyPreRebootVerification_Apk_Complete(
+                                originalSession);
+                    });
+            apkSession.commit(receiver.getIntentSender(), false);
+            return;
+        }
+
+        if ((apkSession.params.installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0) {
+            // If rollback is available for this session, notify the rollback
+            // manager of the apk session so it can properly enable rollback.
+            final IRollbackManager rm = IRollbackManager.Stub.asInterface(
+                    ServiceManager.getService(Context.ROLLBACK_SERVICE));
+            try {
+                rm.notifyStagedApkSession(originalSession.sessionId, apkSession.sessionId);
+            } catch (RemoteException re) {
+                // Cannot happen, the rollback manager is in the same process.
             }
         }
 
-        final LocalIntentReceiver receiver = new LocalIntentReceiver();
+        final LocalIntentReceiverSync receiver = new LocalIntentReceiverSync();
         apkSession.commit(receiver.getIntentSender(), false);
         final Intent result = receiver.getResult();
         final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
                 PackageInstaller.STATUS_FAILURE);
         if (status != PackageInstaller.STATUS_SUCCESS) {
-
             final String errorMessage = result.getStringExtra(
                     PackageInstaller.EXTRA_STATUS_MESSAGE);
-            Slog.e(TAG, "Failure to install APK staged session " + originalSessionId + " ["
-                    + errorMessage + "]");
+            Slog.e(TAG, "Failure to install APK staged session "
+                    + originalSession.sessionId + " [" + errorMessage + "]");
             throw new PackageManagerException(errorCode, errorMessage);
         }
     }
@@ -515,7 +468,7 @@
         if (!session.isMultiPackage() && !isApexSession(session)) {
             // APK single-packaged staged session. Do a regular install.
             PackageInstallerSession apkSession = createAndWriteApkSession(session, preReboot);
-            commitApkSession(apkSession, session.sessionId, preReboot);
+            commitApkSession(apkSession, session, preReboot);
         } else if (session.isMultiPackage()) {
             // For multi-package staged sessions containing APKs, we identify which child sessions
             // contain an APK, and with those then create a new multi-package group of sessions,
@@ -565,14 +518,14 @@
                             "Failed to add a child session " + apkChildSession.sessionId);
                 }
             }
-            commitApkSession(apkParentSession, session.sessionId, preReboot);
+            commitApkSession(apkParentSession, session, preReboot);
         }
         // APEX single-package staged session, nothing to do.
     }
 
     void commitSession(@NonNull PackageInstallerSession session) {
         updateStoredSession(session);
-        mBgHandler.post(() -> preRebootVerification(session));
+        mPreRebootVerificationHandler.startPreRebootVerification(session);
     }
 
     @Nullable
@@ -699,7 +652,7 @@
         if (!session.isStagedSessionReady()) {
             // The framework got restarted before the pre-reboot verification could complete,
             // restart the verification.
-            mBgHandler.post(() -> preRebootVerification(session));
+            mPreRebootVerificationHandler.startPreRebootVerification(session);
         } else {
             // Session had already being marked ready. Start the checks to verify if there is any
             // follow-up work.
@@ -707,14 +660,34 @@
         }
     }
 
-    private static class LocalIntentReceiver {
+    private static class LocalIntentReceiverAsync {
+        final Consumer<Intent> mConsumer;
+
+        LocalIntentReceiverAsync(Consumer<Intent> consumer) {
+            mConsumer = consumer;
+        }
+
+        private IIntentSender.Stub mLocalSender = new IIntentSender.Stub() {
+            @Override
+            public void send(int code, Intent intent, String resolvedType, IBinder whitelistToken,
+                    IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
+                mConsumer.accept(intent);
+            }
+        };
+
+        public IntentSender getIntentSender() {
+            return new IntentSender((IIntentSender) mLocalSender);
+        }
+    }
+
+    private static class LocalIntentReceiverSync {
         private final LinkedBlockingQueue<Intent> mResult = new LinkedBlockingQueue<>();
 
         private IIntentSender.Stub mLocalSender = new IIntentSender.Stub() {
             @Override
             public void send(int code, Intent intent, String resolvedType, IBinder whitelistToken,
-                             IIntentReceiver finishedReceiver, String requiredPermission,
-                             Bundle options) {
+                    IIntentReceiver finishedReceiver, String requiredPermission,
+                    Bundle options) {
                 try {
                     mResult.offer(intent, 5, TimeUnit.SECONDS);
                 } catch (InterruptedException e) {
@@ -735,4 +708,179 @@
             }
         }
     }
+
+    private final class PreRebootVerificationHandler extends Handler {
+
+        PreRebootVerificationHandler(Looper looper) {
+            super(looper);
+        }
+
+        /**
+         * Handler for states of pre reboot verification. The states are arranged linearly (shown
+         * below) with each state either calling the next state, or calling some other method that
+         * eventually calls the next state.
+         *
+         * <p><ul>
+         *     <li>MSG_PRE_REBOOT_VERIFICATION_START</li>
+         *     <li>MSG_PRE_REBOOT_VERIFICATION_APEX</li>
+         *     <li>MSG_PRE_REBOOT_VERIFICATION_APK</li>
+         *     <li>MSG_PRE_REBOOT_VERIFICATION_END</li>
+         * </ul></p>
+         *
+         * Details about each of state can be found in corresponding handler of node.
+         */
+        private static final int MSG_PRE_REBOOT_VERIFICATION_START = 1;
+        private static final int MSG_PRE_REBOOT_VERIFICATION_APEX = 2;
+        private static final int MSG_PRE_REBOOT_VERIFICATION_APK = 3;
+        private static final int MSG_PRE_REBOOT_VERIFICATION_END = 4;
+
+        @Override
+        public void handleMessage(Message msg) {
+            PackageInstallerSession session = (PackageInstallerSession) msg.obj;
+            switch (msg.what) {
+                case MSG_PRE_REBOOT_VERIFICATION_START:
+                    handlePreRebootVerification_Start(session);
+                    break;
+                case MSG_PRE_REBOOT_VERIFICATION_APEX:
+                    handlePreRebootVerification_Apex(session);
+                    break;
+                case MSG_PRE_REBOOT_VERIFICATION_APK:
+                    handlePreRebootVerification_Apk(session);
+                    break;
+                case MSG_PRE_REBOOT_VERIFICATION_END:
+                    handlePreRebootVerification_End(session);
+                    break;
+            }
+        }
+
+        // Method for starting the pre-reboot verification
+        private void startPreRebootVerification(PackageInstallerSession session) {
+            obtainMessage(MSG_PRE_REBOOT_VERIFICATION_START, session).sendToTarget();
+        }
+
+        private void notifyPreRebootVerification_Start_Complete(PackageInstallerSession session) {
+            obtainMessage(MSG_PRE_REBOOT_VERIFICATION_APEX, session).sendToTarget();
+        }
+
+        private void notifyPreRebootVerification_Apex_Complete(PackageInstallerSession session) {
+            obtainMessage(MSG_PRE_REBOOT_VERIFICATION_APK, session).sendToTarget();
+        }
+
+        private void notifyPreRebootVerification_Apk_Complete(PackageInstallerSession session) {
+            obtainMessage(MSG_PRE_REBOOT_VERIFICATION_END, session).sendToTarget();
+        }
+
+        /**
+         * A dummy state for starting the pre reboot verification.
+         *
+         * See {@link PreRebootVerificationHandler} to see all nodes of pre reboot verification
+         */
+        private void handlePreRebootVerification_Start(@NonNull PackageInstallerSession session) {
+            Slog.d(TAG, "Starting preRebootVerification for session " + session.sessionId);
+            notifyPreRebootVerification_Start_Complete(session);
+        }
+
+        /**
+         * Pre-reboot verification state for apex files:
+         *
+         * <p><ul>
+         *     <li>submits session to apex service</li>
+         *     <li>validates signatures of apex files</li>
+         * </ul></p>
+         */
+        private void handlePreRebootVerification_Apex(@NonNull PackageInstallerSession session) {
+            final boolean hasApex = sessionContainsApex(session);
+
+            // APEX checks. For single-package sessions, check if they contain an APEX. For
+            // multi-package sessions, find all the child sessions that contain an APEX.
+            if (hasApex) {
+                try {
+                    final List<PackageInfo> apexPackages =
+                            submitSessionToApexService(session);
+                    for (PackageInfo apexPackage : apexPackages) {
+                        validateApexSignature(
+                                apexPackage, session.params.installFlags);
+                    }
+                } catch (PackageManagerException e) {
+                    session.setStagedSessionFailed(e.error, e.getMessage());
+                    return;
+                }
+            }
+
+            notifyPreRebootVerification_Apex_Complete(session);
+        }
+
+        /**
+         * Pre-reboot verification state for apk files:
+         *   <p><ul>
+         *       <li>performs a dry-run install of apk</li>
+         *   </ul></p>
+         */
+        private void handlePreRebootVerification_Apk(@NonNull PackageInstallerSession session) {
+            if (!sessionContainsApk(session)) {
+                notifyPreRebootVerification_Apk_Complete(session);
+                return;
+            }
+
+            try {
+                Slog.d(TAG, "Running a pre-reboot verification for APKs in session "
+                        + session.sessionId + " by performing a dry-run install");
+
+                // installApksInSession will notify the handler when APK verification is complete
+                installApksInSession(session, /* preReboot */ true);
+                // TODO(b/118865310): abort the session on apexd.
+            } catch (PackageManagerException e) {
+                session.setStagedSessionFailed(e.error, e.getMessage());
+            }
+        }
+
+        /**
+         * Pre-reboot verification state for wrapping up:
+         * <p><ul>
+         *     <li>enables rollback if required</li>
+         *     <li>marks session as ready</li>
+         * </ul></p>
+         */
+        private void handlePreRebootVerification_End(@NonNull PackageInstallerSession session) {
+            if ((session.params.installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0) {
+                // If rollback is enabled for this session, we call through to the RollbackManager
+                // with the list of sessions it must enable rollback for. Note that
+                // notifyStagedSession is a synchronous operation.
+                final IRollbackManager rm = IRollbackManager.Stub.asInterface(
+                        ServiceManager.getService(Context.ROLLBACK_SERVICE));
+                try {
+                    // NOTE: To stay consistent with the non-staged install flow, we don't fail the
+                    // entire install if rollbacks can't be enabled.
+                    if (!rm.notifyStagedSession(session.sessionId)) {
+                        Slog.e(TAG, "Unable to enable rollback for session: "
+                                + session.sessionId);
+                    }
+                } catch (RemoteException re) {
+                    // Cannot happen, the rollback manager is in the same process.
+                }
+            }
+
+            // Proactively mark session as ready before calling apexd. Although this call order
+            // looks counter-intuitive, this is the easiest way to ensure that session won't end up
+            // in the inconsistent state:
+            //  - If device gets rebooted right before call to apexd, then apexd will never activate
+            //      apex files of this staged session. This will result in StagingManager failing
+            //      the session.
+            // On the other hand, if the order of the calls was inverted (first call apexd, then
+            // mark session as ready), then if a device gets rebooted right after the call to apexd,
+            // only apex part of the train will be applied, leaving device in an inconsistent state.
+            Slog.d(TAG, "Marking session " + session.sessionId + " as ready");
+            session.setStagedSessionReady();
+            final boolean hasApex = sessionContainsApex(session);
+            if (!hasApex) {
+                // Session doesn't contain apex, nothing to do.
+                return;
+            }
+            try {
+                mApexManager.markStagedSessionReady(session.sessionId);
+            } catch (PackageManagerException e) {
+                session.setStagedSessionFailed(e.error, e.getMessage());
+            }
+        }
+    }
 }
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 72c8b22..a9e3f04 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -57,6 +57,7 @@
 import android.app.ApplicationPackageManager;
 import android.app.IActivityManager;
 import android.content.Context;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.PermissionGroupInfoFlags;
 import android.content.pm.PackageManager.PermissionInfoFlags;
@@ -67,6 +68,7 @@
 import android.content.pm.ParceledListSlice;
 import android.content.pm.PermissionGroupInfo;
 import android.content.pm.PermissionInfo;
+import android.content.pm.permission.SplitPermissionInfoParcelable;
 import android.metrics.LogMaker;
 import android.os.Binder;
 import android.os.Build;
@@ -2348,10 +2350,11 @@
                         // or has updated its target SDK and AR is no longer implicit to it.
                         // This is a compatibility workaround for apps when AR permission was
                         // split in Q.
-                        int numSplitPerms = PermissionManager.SPLIT_PERMISSIONS.size();
+                        final List<SplitPermissionInfoParcelable> permissionList =
+                                getSplitPermissions();
+                        int numSplitPerms = permissionList.size();
                         for (int splitPermNum = 0; splitPermNum < numSplitPerms; splitPermNum++) {
-                            PermissionManager.SplitPermissionInfo sp =
-                                    PermissionManager.SPLIT_PERMISSIONS.get(splitPermNum);
+                            SplitPermissionInfoParcelable sp = permissionList.get(splitPermNum);
                             String splitPermName = sp.getSplitPermission();
                             if (sp.getNewPermissions().contains(permName)
                                     && origPermissions.hasInstallPermission(splitPermName)) {
@@ -2920,10 +2923,10 @@
         String pkgName = pkg.packageName;
         ArrayMap<String, ArraySet<String>> newToSplitPerms = new ArrayMap<>();
 
-        int numSplitPerms = PermissionManager.SPLIT_PERMISSIONS.size();
+        final List<SplitPermissionInfoParcelable> permissionList = getSplitPermissions();
+        int numSplitPerms = permissionList.size();
         for (int splitPermNum = 0; splitPermNum < numSplitPerms; splitPermNum++) {
-            PermissionManager.SplitPermissionInfo spi =
-                    PermissionManager.SPLIT_PERMISSIONS.get(splitPermNum);
+            SplitPermissionInfoParcelable spi = permissionList.get(splitPermNum);
 
             List<String> newPerms = spi.getNewPermissions();
             int numNewPerms = newPerms.size();
@@ -2991,6 +2994,12 @@
         return updatedUserIds;
     }
 
+    @Override
+    public List<SplitPermissionInfoParcelable> getSplitPermissions() {
+        return PermissionManager.splitPermissionInfoListToParcelableList(
+                SystemConfig.getInstance().getSplitPermissions());
+    }
+
     private boolean isNewPlatformPermissionForPackage(String perm, PackageParser.Package pkg) {
         boolean allowed = false;
         final int NP = PackageParser.NEW_PERMISSIONS.length;
@@ -3742,7 +3751,7 @@
         // Make sure all dynamic permissions have been assigned to a package,
         // and make sure there are no dangling permissions.
         boolean permissionSourcePackageChanged = updatePermissionSourcePackage(changingPkgName,
-                changingPkg);
+                changingPkg, callback);
 
         if (permissionTreesSourcePackageChanged | permissionSourcePackageChanged) {
             // Permission ownership has changed. This e.g. changes which packages can get signature
@@ -3795,7 +3804,8 @@
      * @return {@code true} if a permission source package might have changed
      */
     private boolean updatePermissionSourcePackage(@Nullable String packageName,
-            @Nullable PackageParser.Package pkg) {
+            @Nullable PackageParser.Package pkg,
+            final @Nullable PermissionCallback callback) {
         boolean changed = false;
 
         Set<BasePermission> needsUpdate = null;
@@ -3811,6 +3821,45 @@
                         && (pkg == null || !hasPermission(pkg, bp.getName()))) {
                         Slog.i(TAG, "Removing permission " + bp.getName()
                                 + " that used to be declared by " + bp.getSourcePackageName());
+                        if (bp.isRuntime()) {
+                            final int[] userIds = mUserManagerInt.getUserIds();
+                            final int numUserIds = userIds.length;
+                            for (int userIdNum = 0; userIdNum < numUserIds; userIdNum++) {
+                                final int userId = userIds[userIdNum];
+
+                                mPackageManagerInt.forEachPackage((Package p) -> {
+                                    final String pName = p.packageName;
+                                    final ApplicationInfo appInfo =
+                                            mPackageManagerInt.getApplicationInfo(pName, 0,
+                                                    Process.SYSTEM_UID, UserHandle.USER_SYSTEM);
+                                    if (appInfo != null
+                                            && appInfo.targetSdkVersion < Build.VERSION_CODES.M) {
+                                        return;
+                                    }
+
+                                    final String permissionName = bp.getName();
+                                    if (checkPermissionImpl(permissionName, pName, userId)
+                                            == PackageManager.PERMISSION_GRANTED) {
+                                        try {
+                                            revokeRuntimePermissionInternal(
+                                                    permissionName,
+                                                    pName,
+                                                    false,
+                                                    Process.SYSTEM_UID,
+                                                    userId,
+                                                    callback);
+                                        } catch (IllegalArgumentException e) {
+                                            Slog.e(TAG,
+                                                    "Failed to revoke "
+                                                            + permissionName
+                                                            + " from "
+                                                            + pName,
+                                                    e);
+                                        }
+                                    }
+                                });
+                            }
+                        }
                         changed = true;
                         it.remove();
                     }
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index a671e5f..3147bc6 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -568,6 +568,10 @@
     }
 
     void onUnlockUser(int userId) {
+        // In order to ensure that no package begins running while a backup or restore is taking
+        // place, onUnlockUser must remain blocked until all pending backups and restores have
+        // completed.
+        CountDownLatch latch = new CountDownLatch(1);
         getHandler().post(() -> {
             final List<Rollback> rollbacks;
             synchronized (mLock) {
@@ -580,7 +584,14 @@
             for (Rollback rollback : changed) {
                 saveRollback(rollback);
             }
+            latch.countDown();
         });
+
+        try {
+            latch.await();
+        } catch (InterruptedException ie) {
+            throw new IllegalStateException("RollbackManagerHandlerThread interrupted");
+        }
     }
 
     private void updateRollbackLifetimeDurationInMillis() {
diff --git a/services/core/java/com/android/server/webkit/WebViewUpdater.java b/services/core/java/com/android/server/webkit/WebViewUpdater.java
index a460040..3b58af2 100644
--- a/services/core/java/com/android/server/webkit/WebViewUpdater.java
+++ b/services/core/java/com/android/server/webkit/WebViewUpdater.java
@@ -87,19 +87,6 @@
                         newPackage = findPreferredWebViewPackage();
                         if (mCurrentWebViewPackage != null) {
                             oldProviderName = mCurrentWebViewPackage.packageName;
-                            if (changedState == WebViewUpdateService.PACKAGE_CHANGED
-                                    && newPackage.packageName.equals(oldProviderName)) {
-                                // If we don't change package name we should only rerun the
-                                // preparation phase if the current package has been replaced
-                                // (not if it has been enabled/disabled).
-                                return;
-                            }
-                            if (newPackage.packageName.equals(oldProviderName)
-                                    && (newPackage.lastUpdateTime
-                                        == mCurrentWebViewPackage.lastUpdateTime)) {
-                                // If the chosen package hasn't been updated, then early-out
-                                return;
-                            }
                         }
                         // Only trigger update actions if the updated package is the one
                         // that will be used, or the one that was in use before the
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 9cfa50e..ad340fe 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -814,7 +814,7 @@
                 private final Paint mPaint = new Paint();
 
                 private final SurfaceControl mSurfaceControl;
-                private final Surface mSurface = new Surface();
+                private final Surface mSurface = mService.mSurfaceFactory.get();
 
                 private final AnimationController mAnimationController;
 
@@ -967,7 +967,7 @@
                 }
 
                 public void releaseSurface() {
-                    mService.mTransactionFactory.make().remove(mSurfaceControl).apply();
+                    mService.mTransactionFactory.get().remove(mSurfaceControl).apply();
                     mSurface.release();
                 }
 
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index f5f6625..b92625f 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -33,6 +33,7 @@
 import static android.content.pm.ActivityInfo.CONFIG_SCREEN_LAYOUT;
 import static android.content.pm.ActivityInfo.FLAG_RESUME_WHILE_PAUSING;
 import static android.content.pm.ActivityInfo.FLAG_SHOW_FOR_ALL_USERS;
+import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.Display.FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD;
 import static android.view.Display.INVALID_DISPLAY;
@@ -134,6 +135,7 @@
 import android.os.Message;
 import android.os.RemoteException;
 import android.os.SystemClock;
+import android.os.Trace;
 import android.os.UserHandle;
 import android.service.voice.IVoiceInteractionSession;
 import android.util.ArraySet;
@@ -635,11 +637,15 @@
             display.onStackWindowingModeChanged(this);
         }
         if (hasNewOverrideBounds) {
-            // Note the resizeStack may enter onConfigurationChanged recursively, so we make a copy
-            // of the temporary bounds (newBounds is mTmpRect) to avoid it being modified.
-            mRootActivityContainer.resizeStack(this, new Rect(newBounds), null /* tempTaskBounds */,
-                    null /* tempTaskInsetBounds */, PRESERVE_WINDOWS,
-                    true /* allowResizeInDockedMode */, true /* deferResume */);
+            if (inSplitScreenPrimaryWindowingMode()) {
+                mStackSupervisor.resizeDockedStackLocked(new Rect(newBounds),
+                        null /* tempTaskBounds */, null /* tempTaskInsetBounds */,
+                        null /* tempOtherTaskBounds */, null /* tempOtherTaskInsetBounds */,
+                        PRESERVE_WINDOWS, true /* deferResume */);
+            } else {
+                resize(new Rect(newBounds), null /* tempTaskBounds */,
+                        null /* tempTaskInsetBounds */, PRESERVE_WINDOWS, true /* deferResume */);
+            }
         }
         if (prevIsAlwaysOnTop != isAlwaysOnTop()) {
             // Since always on top is only on when the stack is freeform or pinned, the state
@@ -818,7 +824,8 @@
             }
 
             if (!Objects.equals(getRequestedOverrideBounds(), mTmpRect2)) {
-                resize(mTmpRect2, null /* tempTaskBounds */, null /* tempTaskInsetBounds */);
+                resize(mTmpRect2, null /* tempTaskBounds */, null /* tempTaskInsetBounds */,
+                        false /* preserveWindows */, true /* deferResume */);
             }
         } finally {
             if (showRecents && !alreadyInSplitScreenMode && mDisplayId == DEFAULT_DISPLAY
@@ -2208,7 +2215,7 @@
      * Returns true if this stack should be resized to match the bounds specified by
      * {@link ActivityOptions#setLaunchBounds} when launching an activity into the stack.
      */
-    boolean resizeStackWithLaunchBounds() {
+    boolean shouldResizeStackWithLaunchBounds() {
         return inPinnedWindowingMode();
     }
 
@@ -4344,31 +4351,42 @@
         }
     }
 
-    // TODO: Figure-out a way to consolidate with resize() method below.
-    void requestResize(Rect bounds) {
-        mService.resizeStack(mStackId, bounds,
-                true /* allowResizeInDockedMode */, false /* preserveWindows */,
-                false /* animate */, -1 /* animationDuration */);
-    }
-
     // TODO: Can only be called from special methods in ActivityStackSupervisor.
     // Need to consolidate those calls points into this resize method so anyone can call directly.
-    void resize(Rect bounds, Rect tempTaskBounds, Rect tempTaskInsetBounds) {
+    void resize(Rect bounds, Rect tempTaskBounds, Rect tempTaskInsetBounds,
+            boolean preserveWindows, boolean deferResume) {
         if (!updateBoundsAllowed(bounds)) {
             return;
         }
 
-        // Update override configurations of all tasks in the stack.
-        final Rect taskBounds = tempTaskBounds != null ? tempTaskBounds : bounds;
-
-        for (int i = mTaskHistory.size() - 1; i >= 0; i--) {
-            final TaskRecord task = mTaskHistory.get(i);
-            if (task.isResizeable()) {
-                task.updateOverrideConfiguration(taskBounds, tempTaskInsetBounds);
+        Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "stack.resize_" + mStackId);
+        mWindowManager.deferSurfaceLayout();
+        try {
+            // Update override configurations of all tasks in the stack.
+            final Rect taskBounds = tempTaskBounds != null ? tempTaskBounds : bounds;
+            for (int i = mTaskHistory.size() - 1; i >= 0; i--) {
+                final TaskRecord task = mTaskHistory.get(i);
+                if (task.isResizeable()) {
+                    if (tempTaskInsetBounds != null && !tempTaskInsetBounds.isEmpty()) {
+                        task.setDisplayedBounds(taskBounds);
+                        task.setBounds(tempTaskInsetBounds);
+                    } else {
+                        task.setDisplayedBounds(null);
+                        task.setBounds(taskBounds);
+                    }
+                }
             }
-        }
 
-        setBounds(bounds);
+            setBounds(bounds);
+
+            if (!deferResume) {
+                ensureVisibleActivitiesConfigurationLocked(
+                        topRunningActivityLocked(), preserveWindows);
+            }
+        } finally {
+            mWindowManager.continueSurfaceLayout();
+            Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+        }
     }
 
     void onPipAnimationEndResize() {
@@ -4497,18 +4515,27 @@
      *         then skip running tasks that match those types.
      */
     void getRunningTasks(List<TaskRecord> tasksOut, @ActivityType int ignoreActivityType,
-            @WindowingMode int ignoreWindowingMode, int callingUid, boolean allowed) {
+            @WindowingMode int ignoreWindowingMode, int callingUid, boolean allowed,
+            boolean crossUser) {
         boolean focusedStack = mRootActivityContainer.getTopDisplayFocusedStack() == this;
         boolean topTask = true;
+        int userId = UserHandle.getUserId(callingUid);
         for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
             final TaskRecord task = mTaskHistory.get(taskNdx);
             if (task.getTopActivity() == null) {
                 // Skip if there are no activities in the task
                 continue;
             }
-            if (!allowed && !task.isActivityTypeHome() && task.effectiveUid != callingUid) {
-                // Skip if the caller can't fetch this task
-                continue;
+            if (task.effectiveUid != callingUid) {
+                if (task.userId != userId && !crossUser) {
+                    // Skip if the caller does not have cross user permission
+                    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) {
@@ -4774,7 +4801,7 @@
         if (!mStackSupervisor.getLaunchParamsController()
                 .layoutTask(task, info.windowLayout, activity, source, options)
                 && !matchParentBounds() && task.isResizeable() && !isLockscreenShown) {
-            task.updateOverrideConfiguration(getRequestedOverrideBounds());
+            task.setBounds(getRequestedOverrideBounds());
         }
         task.createTask(toTop, (info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0);
         return task;
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index a480fb8..7a3f022 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -1398,7 +1398,7 @@
         boolean reparented = false;
         if (task.isResizeable() && canUseActivityOptionsLaunchBounds(options)) {
             final Rect bounds = options.getLaunchBounds();
-            task.updateOverrideConfiguration(bounds);
+            task.setBounds(bounds);
 
             ActivityStack stack =
                     mRootActivityContainer.getLaunchStack(null, options, task, ON_TOP);
@@ -1412,10 +1412,9 @@
                 // task.reparent() should already placed the task on top,
                 // still need moveTaskToFrontLocked() below for any transition settings.
             }
-            if (stack.resizeStackWithLaunchBounds()) {
-                mRootActivityContainer.resizeStack(stack, bounds, null /* tempTaskBounds */,
-                        null /* tempTaskInsetBounds */, !PRESERVE_WINDOWS,
-                        true /* allowResizeInDockedMode */, !DEFER_RESUME);
+            if (stack.shouldResizeStackWithLaunchBounds()) {
+                stack.resize(bounds, null /* tempTaskBounds */, null /* tempTaskInsetBounds */,
+                        !PRESERVE_WINDOWS, !DEFER_RESUME);
             } else {
                 // WM resizeTask must be done after the task is moved to the correct stack,
                 // because Task's setBounds() also updates dim layer's bounds, but that has
@@ -1636,7 +1635,8 @@
             // Don't allow re-entry while resizing. E.g. due to docked stack detaching.
             mAllowDockedStackResize = false;
             ActivityRecord r = stack.topRunningActivityLocked();
-            stack.resize(dockedBounds, tempDockedTaskBounds, tempDockedTaskInsetBounds);
+            stack.resize(dockedBounds, tempDockedTaskBounds, tempDockedTaskInsetBounds,
+                    !PRESERVE_WINDOWS, DEFER_RESUME);
 
             // TODO: Checking for isAttached might not be needed as if the user passes in null
             // dockedBounds then they want the docked stack to be dismissed.
@@ -1674,11 +1674,19 @@
                             tempRect /* outStackBounds */,
                             otherTaskRect /* outTempTaskBounds */);
 
-                    mRootActivityContainer.resizeStack(current,
-                            !tempRect.isEmpty() ? tempRect : null,
+                    if (tempRect.isEmpty()) {
+                        // If this scenario is hit, it means something is not working right.
+                        // Empty/null bounds implies fullscreen. In the event that this stack
+                        // *should* be fullscreen, its mode should be set explicitly in a form
+                        // of setWindowingMode so that other parts of the system are updated
+                        // properly.
+                        throw new IllegalArgumentException("Trying to set null bounds on a"
+                                + " non-fullscreen stack");
+                    }
+
+                    current.resize(tempRect,
                             !otherTaskRect.isEmpty() ? otherTaskRect : tempOtherTaskBounds,
-                            tempOtherTaskInsetBounds, preserveWindows,
-                            true /* allowResizeInDockedMode */, deferResume);
+                            tempOtherTaskInsetBounds, preserveWindows, deferResume);
                 }
             }
             if (!deferResume) {
@@ -1728,8 +1736,8 @@
                 // transitioning it from fullscreen into a floating state.
                 stack.onPipAnimationEndResize();
             }
-            stack.resize(pinnedBounds, tempPinnedTaskBounds, insetBounds);
-            stack.ensureVisibleActivitiesConfigurationLocked(r, false);
+            stack.resize(pinnedBounds, tempPinnedTaskBounds, insetBounds, !PRESERVE_WINDOWS,
+                    !DEFER_RESUME);
         } finally {
             mWindowManager.continueSurfaceLayout();
             Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 5717e2f..641b00a 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -2400,11 +2400,10 @@
         }
 
         final ActivityStack stack = task.getStack();
-        if (stack != null && stack.resizeStackWithLaunchBounds()) {
-            mService.resizeStack(
-                    stack.mStackId, bounds, true, !PRESERVE_WINDOWS, ANIMATE, -1);
+        if (stack != null && stack.inPinnedWindowingMode()) {
+            mService.animateResizePinnedStack(stack.mStackId, bounds, -1);
         } else {
-            task.updateOverrideConfiguration(bounds);
+            task.setBounds(bounds);
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index ee56c09..d97f0f5 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -19,6 +19,8 @@
 import static android.Manifest.permission.BIND_VOICE_INTERACTION;
 import static android.Manifest.permission.CHANGE_CONFIGURATION;
 import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS;
+import static android.Manifest.permission.INTERACT_ACROSS_USERS;
+import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
 import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
 import static android.Manifest.permission.MANAGE_ACTIVITY_STACKS;
 import static android.Manifest.permission.READ_FRAME_BUFFER;
@@ -2508,15 +2510,16 @@
             @WindowConfiguration.ActivityType int ignoreActivityType,
             @WindowConfiguration.WindowingMode int ignoreWindowingMode) {
         final int callingUid = Binder.getCallingUid();
+        final int callingPid = Binder.getCallingPid();
+        final boolean crossUser = isCrossUserAllowed(callingPid, callingUid);
         ArrayList<ActivityManager.RunningTaskInfo> list = new ArrayList<>();
 
         synchronized (mGlobalLock) {
             if (DEBUG_ALL) Slog.v(TAG, "getTasks: max=" + maxNum);
 
-            final boolean allowed = isGetTasksAllowed("getTasks", Binder.getCallingPid(),
-                    callingUid);
+            final boolean allowed = isGetTasksAllowed("getTasks", callingPid, callingUid);
             mRootActivityContainer.getRunningTasks(maxNum, list, ignoreActivityType,
-                    ignoreWindowingMode, callingUid, allowed);
+                    ignoreWindowingMode, callingUid, allowed, crossUser);
         }
 
         return list;
@@ -2582,35 +2585,23 @@
     }
 
     @Override
-    public void resizeStack(int stackId, Rect destBounds, boolean allowResizeInDockedMode,
-            boolean preserveWindows, boolean animate, int animationDuration) {
-        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "resizeStack()");
+    public void animateResizePinnedStack(int stackId, Rect destBounds, int animationDuration) {
+        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_STACKS, "animateResizePinnedStack()");
 
         final long ident = Binder.clearCallingIdentity();
         try {
             synchronized (mGlobalLock) {
-                if (animate) {
-                    final ActivityStack stack = mRootActivityContainer.getStack(stackId);
-                    if (stack == null) {
-                        Slog.w(TAG, "resizeStack: stackId " + stackId + " not found.");
-                        return;
-                    }
-                    if (stack.getWindowingMode() != WINDOWING_MODE_PINNED) {
-                        throw new IllegalArgumentException("Stack: " + stackId
-                                + " doesn't support animated resize.");
-                    }
-                    stack.animateResizePinnedStack(null /* sourceHintBounds */, destBounds,
-                            animationDuration, false /* fromFullscreen */);
-                } else {
-                    final ActivityStack stack = mRootActivityContainer.getStack(stackId);
-                    if (stack == null) {
-                        Slog.w(TAG, "resizeStack: stackId " + stackId + " not found.");
-                        return;
-                    }
-                    mRootActivityContainer.resizeStack(stack, destBounds,
-                            null /* tempTaskBounds */, null /* tempTaskInsetBounds */,
-                            preserveWindows, allowResizeInDockedMode, !DEFER_RESUME);
+                final ActivityStack stack = mRootActivityContainer.getStack(stackId);
+                if (stack == null) {
+                    Slog.w(TAG, "resizeStack: stackId " + stackId + " not found.");
+                    return;
                 }
+                if (stack.getWindowingMode() != WINDOWING_MODE_PINNED) {
+                    throw new IllegalArgumentException("Stack: " + stackId
+                        + " doesn't support animated resize.");
+                }
+                stack.animateResizePinnedStack(null /* sourceHintBounds */, destBounds,
+                        animationDuration, false /* fromFullscreen */);
             }
         } finally {
             Binder.restoreCallingIdentity(ident);
@@ -3546,6 +3537,11 @@
         return allowed;
     }
 
+    boolean isCrossUserAllowed(int pid, int uid) {
+        return checkPermission(INTERACT_ACROSS_USERS, pid, uid) == PERMISSION_GRANTED
+                || checkPermission(INTERACT_ACROSS_USERS_FULL, pid, uid) == PERMISSION_GRANTED;
+    }
+
     private PendingAssistExtras enqueueAssistContext(int requestType, Intent intent, String hint,
             IAssistDataReceiver receiver, Bundle receiverExtras, IBinder activityToken,
             boolean focused, boolean newSessionId, int userHandle, Bundle args, long timeout,
diff --git a/services/core/java/com/android/server/wm/AppWindowThumbnail.java b/services/core/java/com/android/server/wm/AppWindowThumbnail.java
index 4ceae72..b52ade4 100644
--- a/services/core/java/com/android/server/wm/AppWindowThumbnail.java
+++ b/services/core/java/com/android/server/wm/AppWindowThumbnail.java
@@ -41,6 +41,8 @@
 
 import com.android.server.wm.SurfaceAnimator.Animatable;
 
+import java.util.function.Supplier;
+
 /**
  * Represents a surface that is displayed over an {@link AppWindowToken}
  */
@@ -55,8 +57,9 @@
     private final int mHeight;
     private final boolean mRelative;
 
-    AppWindowThumbnail(Transaction t, AppWindowToken appToken, GraphicBuffer thumbnailHeader) {
-        this(t, appToken, thumbnailHeader, false /* relative */);
+    AppWindowThumbnail(Supplier<Surface> surfaceFactory, Transaction t, AppWindowToken appToken,
+            GraphicBuffer thumbnailHeader) {
+        this(surfaceFactory, t, appToken, thumbnailHeader, false /* relative */);
     }
 
     /**
@@ -66,9 +69,9 @@
      * @param relative Whether this thumbnail will be a child of appToken (and thus positioned
      *                 relative to it) or not.
      */
-    AppWindowThumbnail(Transaction t, AppWindowToken appToken, GraphicBuffer thumbnailHeader,
-            boolean relative) {
-        this(t, appToken, thumbnailHeader, relative, new Surface(), null);
+    AppWindowThumbnail(Supplier<Surface> surfaceFactory, Transaction t, AppWindowToken appToken,
+            GraphicBuffer thumbnailHeader, boolean relative) {
+        this(t, appToken, thumbnailHeader, relative, surfaceFactory.get(), null);
     }
 
     AppWindowThumbnail(Transaction t, AppWindowToken appToken, GraphicBuffer thumbnailHeader,
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 7e0d9a0..ffd9021 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -1793,8 +1793,8 @@
                     mWmService.mTaskSnapshotController.createTaskSnapshot(
                             task, 1 /* scaleFraction */);
             if (snapshot != null) {
-                mThumbnail = new AppWindowThumbnail(t, this, snapshot.getGraphicBuffer(),
-                        true /* relative */);
+                mThumbnail = new AppWindowThumbnail(mWmService.mSurfaceFactory, t, this,
+                        snapshot.getGraphicBuffer(), true /* relative */);
             }
         }
     }
@@ -2033,7 +2033,8 @@
         final boolean needsLetterbox = surfaceReady && w.isLetterboxedAppWindow() && fillsParent();
         if (needsLetterbox) {
             if (mLetterbox == null) {
-                mLetterbox = new Letterbox(() -> makeChildSurface(null));
+                mLetterbox = new Letterbox(() -> makeChildSurface(null),
+                        mWmService.mTransactionFactory);
                 mLetterbox.attachInput(w);
             }
             getPosition(mTmpPoint);
@@ -2981,7 +2982,8 @@
             return;
         }
         clearThumbnail();
-        mThumbnail = new AppWindowThumbnail(getPendingTransaction(), this, thumbnailHeader);
+        mThumbnail = new AppWindowThumbnail(mWmService.mSurfaceFactory, getPendingTransaction(),
+                this, thumbnailHeader);
         mThumbnail.startAnimation(getPendingTransaction(), loadThumbnailAnimation(thumbnailHeader));
     }
 
@@ -3009,7 +3011,8 @@
         if (thumbnail == null) {
             return;
         }
-        mThumbnail = new AppWindowThumbnail(getPendingTransaction(), this, thumbnail);
+        mThumbnail = new AppWindowThumbnail(mWmService.mSurfaceFactory,
+                getPendingTransaction(), this, thumbnail);
         final Animation animation =
                 getDisplayContent().mAppTransition.createCrossProfileAppsThumbnailAnimationLocked(
                         win.getFrameLw());
diff --git a/services/core/java/com/android/server/wm/BarController.java b/services/core/java/com/android/server/wm/BarController.java
index 90bb494..5c4332d 100644
--- a/services/core/java/com/android/server/wm/BarController.java
+++ b/services/core/java/com/android/server/wm/BarController.java
@@ -27,6 +27,7 @@
 import android.util.Slog;
 import android.util.proto.ProtoOutputStream;
 import android.view.View;
+import android.view.ViewRootImpl;
 import android.view.WindowManager;
 
 import com.android.server.LocalServices;
@@ -89,6 +90,10 @@
     }
 
     void setWindow(WindowState win) {
+        if (ViewRootImpl.sNewInsetsMode == ViewRootImpl.NEW_INSETS_MODE_FULL) {
+            // BarController gets replaced with InsetsPolicy in the full insets mode.
+            return;
+        }
         mWin = win;
     }
 
diff --git a/services/core/java/com/android/server/wm/BlackFrame.java b/services/core/java/com/android/server/wm/BlackFrame.java
index 7fc17e1..7557271a 100644
--- a/services/core/java/com/android/server/wm/BlackFrame.java
+++ b/services/core/java/com/android/server/wm/BlackFrame.java
@@ -27,6 +27,7 @@
 import android.view.SurfaceControl;
 
 import java.io.PrintWriter;
+import java.util.function.Supplier;
 
 /**
  * Four black surfaces put together to make a black frame.
@@ -97,7 +98,7 @@
     final BlackSurface[] mBlackSurfaces = new BlackSurface[4];
 
     final boolean mForceDefaultOrientation;
-    private final TransactionFactory mTransactionFactory;
+    private final Supplier<SurfaceControl.Transaction> mTransactionFactory;
 
     public void printTo(String prefix, PrintWriter pw) {
         pw.print(prefix); pw.print("Outer: "); mOuterRect.printShortString(pw);
@@ -112,8 +113,8 @@
         }
     }
 
-    public BlackFrame(TransactionFactory factory, SurfaceControl.Transaction t, Rect outer,
-            Rect inner, int layer, DisplayContent dc, boolean forceDefaultOrientation)
+    public BlackFrame(Supplier<SurfaceControl.Transaction> factory, SurfaceControl.Transaction t,
+            Rect outer, Rect inner, int layer, DisplayContent dc, boolean forceDefaultOrientation)
             throws OutOfResourcesException {
         boolean success = false;
 
@@ -151,7 +152,7 @@
 
     public void kill() {
         if (mBlackSurfaces != null) {
-            SurfaceControl.Transaction t = mTransactionFactory.make();
+            SurfaceControl.Transaction t = mTransactionFactory.get();
             for (int i=0; i<mBlackSurfaces.length; i++) {
                 if (mBlackSurfaces[i] != null) {
                     if (SHOW_TRANSACTIONS || SHOW_SURFACE_ALLOC) Slog.i(TAG_WM,
diff --git a/services/core/java/com/android/server/wm/CircularDisplayMask.java b/services/core/java/com/android/server/wm/CircularDisplayMask.java
index c3d6211..c1ca816 100644
--- a/services/core/java/com/android/server/wm/CircularDisplayMask.java
+++ b/services/core/java/com/android/server/wm/CircularDisplayMask.java
@@ -34,6 +34,8 @@
 import android.view.Surface.OutOfResourcesException;
 import android.view.SurfaceControl;
 
+import java.util.function.Supplier;
+
 class CircularDisplayMask {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "CircularDisplayMask" : TAG_WM;
 
@@ -43,7 +45,7 @@
     private Point mScreenSize;
 
     private final SurfaceControl mSurfaceControl;
-    private final Surface mSurface = new Surface();
+    private final Surface mSurface;
     private int mLastDW;
     private int mLastDH;
     private boolean mDrawNeeded;
@@ -53,10 +55,10 @@
     private boolean mDimensionsUnequal = false;
     private int mMaskThickness;
 
-    public CircularDisplayMask(DisplayContent dc, int zOrder,
+    CircularDisplayMask(Supplier<Surface> surfaceFactory, DisplayContent dc, int zOrder,
             int screenOffset, int maskThickness) {
         final Display display = dc.getDisplay();
-
+        mSurface = surfaceFactory.get();
         mScreenSize = new Point();
         display.getSize(mScreenSize);
         if (mScreenSize.x != mScreenSize.y + screenOffset) {
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 1cffce0..5ec167e 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -549,6 +549,7 @@
     private final PointerEventDispatcher mPointerEventDispatcher;
 
     private final InsetsStateController mInsetsStateController;
+    private final InsetsPolicy mInsetsPolicy;
 
     /** @see #getParentWindow() */
     private WindowState mParentWindow;
@@ -968,6 +969,7 @@
         mWmService.mAnimator.addDisplayLocked(mDisplayId);
         mInputMonitor = new InputMonitor(service, mDisplayId);
         mInsetsStateController = new InsetsStateController(this);
+        mInsetsPolicy = new InsetsPolicy(mInsetsStateController, this);
     }
 
     boolean isReady() {
@@ -1126,6 +1128,10 @@
         return mInsetsStateController;
     }
 
+    InsetsPolicy getInsetsPolicy() {
+        return mInsetsPolicy;
+    }
+
     @Surface.Rotation
     int getRotation() {
         return mDisplayRotation.getRotation();
@@ -4394,7 +4400,7 @@
                         .show(mSplitScreenDividerAnchor);
                 scheduleAnimation();
             } else {
-                mWmService.mTransactionFactory.make()
+                mWmService.mTransactionFactory.get()
                         .remove(mAppAnimationLayer)
                         .remove(mBoostedAppAnimationLayer)
                         .remove(mHomeAppAnimationLayer)
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 8328770..aecbca3 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -1029,6 +1029,14 @@
                 displayFrames.mDisplayCutoutSafe.top);
     }
 
+    WindowState getStatusBar() {
+        return mStatusBar;
+    }
+
+    WindowState getNavigationBar() {
+        return mNavigationBar;
+    }
+
     /**
      * Control the animation to run when a window's state changes.  Return a
      * non-0 number to force the animation to a specific resource ID, or 0
@@ -3108,8 +3116,7 @@
             return 0;
         }
 
-        mDisplayContent.getInsetsStateController().onBarControllingWindowChanged(
-                mTopFullscreenOpaqueWindowState);
+        mDisplayContent.getInsetsPolicy().updateBarControlTarget(win);
 
         int tmpVisibility = PolicyControl.getSystemUiVisibility(win, null)
                 & ~mResettingSystemUiFlags
diff --git a/services/core/java/com/android/server/wm/DragState.java b/services/core/java/com/android/server/wm/DragState.java
index c48f07c..17daabf 100644
--- a/services/core/java/com/android/server/wm/DragState.java
+++ b/services/core/java/com/android/server/wm/DragState.java
@@ -140,7 +140,7 @@
         mFlags = flags;
         mLocalWin = localWin;
         mNotifiedWindows = new ArrayList<WindowState>();
-        mTransaction = service.mTransactionFactory.make();
+        mTransaction = service.mTransactionFactory.get();
     }
 
     boolean isClosing() {
@@ -695,7 +695,8 @@
             implements ValueAnimator.AnimatorUpdateListener, Animator.AnimatorListener {
         @Override
         public void onAnimationUpdate(ValueAnimator animation) {
-            try (final SurfaceControl.Transaction transaction = new SurfaceControl.Transaction()) {
+            try (SurfaceControl.Transaction transaction =
+                         mService.mTransactionFactory.get()) {
                 transaction.setPosition(
                         mSurfaceControl,
                         (float) animation.getAnimatedValue(ANIMATED_PROPERTY_X),
diff --git a/services/core/java/com/android/server/wm/EmulatorDisplayOverlay.java b/services/core/java/com/android/server/wm/EmulatorDisplayOverlay.java
index 7cb4a43..f64592f 100644
--- a/services/core/java/com/android/server/wm/EmulatorDisplayOverlay.java
+++ b/services/core/java/com/android/server/wm/EmulatorDisplayOverlay.java
@@ -32,6 +32,8 @@
 import android.view.Surface.OutOfResourcesException;
 import android.view.SurfaceControl;
 
+import java.util.function.Supplier;
+
 class EmulatorDisplayOverlay {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "EmulatorDisplayOverlay" : TAG_WM;
 
@@ -39,7 +41,7 @@
     private Point mScreenSize;
 
     private final SurfaceControl mSurfaceControl;
-    private final Surface mSurface = new Surface();
+    private final Surface mSurface;
     private int mLastDW;
     private int mLastDH;
     private boolean mDrawNeeded;
@@ -47,8 +49,9 @@
     private int mRotation;
     private boolean mVisible;
 
-    public EmulatorDisplayOverlay(Context context, DisplayContent dc,
+    EmulatorDisplayOverlay(Supplier<Surface> surfaceFactory, Context context, DisplayContent dc,
             int zOrder) {
+        mSurface = surfaceFactory.get();
         final Display display = dc.getDisplay();
         mScreenSize = new Point();
         display.getSize(mScreenSize);
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index 2eec926..dd9000e 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -153,7 +153,7 @@
         mService = service;
         mDisplayContent = mService.mRoot.getDisplayContent(displayId);
         mDisplayId = displayId;
-        mInputTransaction = mService.mTransactionFactory.make();
+        mInputTransaction = mService.mTransactionFactory.get();
         mHandler = AnimationThread.getHandler();
         mUpdateInputForAllWindowsConsumer = new UpdateInputForAllWindowsConsumer();
     }
diff --git a/services/core/java/com/android/server/wm/SurfaceFactory.java b/services/core/java/com/android/server/wm/InsetsControlTarget.java
similarity index 80%
rename from services/core/java/com/android/server/wm/SurfaceFactory.java
rename to services/core/java/com/android/server/wm/InsetsControlTarget.java
index 076b7df..3db6dcf 100644
--- a/services/core/java/com/android/server/wm/SurfaceFactory.java
+++ b/services/core/java/com/android/server/wm/InsetsControlTarget.java
@@ -16,12 +16,9 @@
 
 package com.android.server.wm;
 
-import android.view.Surface;
-
 /**
- * Helper class to inject custom {@link Surface} objects into window manager.
+ * Generalization of an object that can control insets state.
  */
-interface SurfaceFactory {
-    Surface make();
-};
-
+interface InsetsControlTarget {
+    void notifyInsetsControlChanged();
+}
diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
new file mode 100644
index 0000000..2dc50d8
--- /dev/null
+++ b/services/core/java/com/android/server/wm/InsetsPolicy.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_STATUS_BAR_VISIBLE_TRANSPARENT;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION;
+
+import android.annotation.Nullable;
+
+/**
+ * Policy that implements who gets control over the windows generating insets.
+ */
+class InsetsPolicy {
+
+    private final InsetsStateController mStateController;
+    private final DisplayContent mDisplayContent;
+    private final DisplayPolicy mPolicy;
+
+    InsetsPolicy(InsetsStateController stateController, DisplayContent displayContent) {
+        mStateController = stateController;
+        mDisplayContent = displayContent;
+        mPolicy = displayContent.getDisplayPolicy();
+    }
+
+    /** Updates the target which can control system bars. */
+    void updateBarControlTarget(@Nullable WindowState focusedWin) {
+        mStateController.onBarControlTargetChanged(getTopControlTarget(focusedWin),
+                getNavControlTarget(focusedWin));
+    }
+
+    private @Nullable InsetsControlTarget getTopControlTarget(@Nullable WindowState focusedWin) {
+        if (areSystemBarsForciblyVisible() || isStatusBarForciblyVisible()) {
+            return null;
+        }
+        return focusedWin;
+    }
+
+    private @Nullable InsetsControlTarget getNavControlTarget(@Nullable WindowState focusedWin) {
+        if (areSystemBarsForciblyVisible() || isNavBarForciblyVisible()) {
+            return null;
+        }
+        return focusedWin;
+    }
+
+    private boolean isStatusBarForciblyVisible() {
+        final WindowState statusBar = mPolicy.getStatusBar();
+        if (statusBar == null) {
+            return false;
+        }
+        final int privateFlags = statusBar.mAttrs.privateFlags;
+
+        // TODO: Pretend to the app that it's still able to control it?
+        if ((privateFlags & PRIVATE_FLAG_FORCE_STATUS_BAR_VISIBLE_TRANSPARENT) != 0) {
+            return true;
+        }
+        if ((privateFlags & PRIVATE_FLAG_KEYGUARD) != 0) {
+            return true;
+        }
+        return false;
+    }
+
+    private boolean isNavBarForciblyVisible() {
+        final WindowState statusBar = mPolicy.getStatusBar();
+        if (statusBar == null) {
+            return false;
+        }
+        if ((statusBar.mAttrs.privateFlags & PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION) != 0) {
+            return true;
+        }
+        return false;
+    }
+
+    private boolean areSystemBarsForciblyVisible() {
+        final boolean isDockedStackVisible =
+                mDisplayContent.isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+        final boolean isFreeformStackVisible =
+                mDisplayContent.isStackVisible(WINDOWING_MODE_FREEFORM);
+        final boolean isResizing = mDisplayContent.getDockedDividerController().isResizing();
+
+        // We need to force system bars when the docked stack is visible, when the freeform stack
+        // is visible but also when we are resizing for the transitions when docked stack
+        // visibility changes.
+        return isDockedStackVisible || isFreeformStackVisible || isResizing;
+    }
+
+}
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index b7835aa..1b7b92b 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -52,7 +52,7 @@
     private final DisplayContent mDisplayContent;
     private final InsetsStateController mStateController;
     private @Nullable InsetsSourceControl mControl;
-    private @Nullable WindowState mControllingWin;
+    private @Nullable InsetsControlTarget mControlTarget;
     private @Nullable ControlAdapter mAdapter;
     private WindowState mWin;
     private TriConsumer<DisplayFrames, WindowState, Rect> mFrameProvider;
@@ -119,8 +119,8 @@
             mSource.setFrame(new Rect());
         } else {
             mWin.setInsetProvider(this);
-            if (mControllingWin != null) {
-                updateControlForTarget(mControllingWin, true /* force */);
+            if (mControlTarget != null) {
+                updateControlForTarget(mControlTarget, true /* force */);
             }
         }
     }
@@ -143,19 +143,19 @@
         if (mControl != null) {
             final Rect frame = mWin.getWindowFrames().mFrame;
             if (mControl.setSurfacePosition(frame.left, frame.top)) {
-                mStateController.notifyControlChanged(mControllingWin);
+                mStateController.notifyControlChanged(mControlTarget);
             }
         }
         setServerVisible(mWin.wouldBeVisibleIfPolicyIgnored() && mWin.isVisibleByPolicy()
                 && !mWin.mGivenInsetsPending);
     }
 
-    void updateControlForTarget(@Nullable WindowState target, boolean force) {
+    void updateControlForTarget(@Nullable InsetsControlTarget target, boolean force) {
         if (mWin == null) {
-            mControllingWin = target;
+            mControlTarget = target;
             return;
         }
-        if (target == mControllingWin && !force) {
+        if (target == mControlTarget && !force) {
             return;
         }
         if (target == null) {
@@ -167,13 +167,13 @@
         setClientVisible(InsetsState.getDefaultVisibility(mSource.getType()));
         mWin.startAnimation(mDisplayContent.getPendingTransaction(), mAdapter,
                 !mClientVisible /* hidden */);
-        mControllingWin = target;
+        mControlTarget = target;
         mControl = new InsetsSourceControl(mSource.getType(), mAdapter.mCapturedLeash,
                 new Point(mWin.getWindowFrames().mFrame.left, mWin.getWindowFrames().mFrame.top));
     }
 
     boolean onInsetsModified(WindowState caller, InsetsSource modifiedSource) {
-        if (mControllingWin != caller || modifiedSource.isVisible() == mClientVisible) {
+        if (mControlTarget != caller || modifiedSource.isVisible() == mClientVisible) {
             return false;
         }
         setClientVisible(modifiedSource.isVisible());
@@ -232,10 +232,10 @@
         @Override
         public void onAnimationCancelled(SurfaceControl animationLeash) {
             if (mAdapter == this) {
-                mStateController.notifyControlRevoked(mControllingWin, InsetsSourceProvider.this);
+                mStateController.notifyControlRevoked(mControlTarget, InsetsSourceProvider.this);
                 setClientVisible(InsetsState.getDefaultVisibility(mSource.getType()));
                 mControl = null;
-                mControllingWin = null;
+                mControlTarget = null;
                 mAdapter = null;
             }
         }
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index a1b52f4..bb70495 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -16,12 +16,10 @@
 
 package com.android.server.wm;
 
+import static android.view.InsetsState.InternalInsetType;
 import static android.view.InsetsState.TYPE_IME;
 import static android.view.InsetsState.TYPE_NAVIGATION_BAR;
 import static android.view.InsetsState.TYPE_TOP_BAR;
-import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL;
-import static android.view.ViewRootImpl.NEW_INSETS_MODE_NONE;
-import static android.view.ViewRootImpl.sNewInsetsMode;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -31,7 +29,6 @@
 import android.view.InsetsSource;
 import android.view.InsetsSourceControl;
 import android.view.InsetsState;
-import android.view.ViewRootImpl;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -46,10 +43,11 @@
     private final InsetsState mState = new InsetsState();
     private final DisplayContent mDisplayContent;
 
-    private final ArrayMap<Integer, InsetsSourceProvider> mControllers = new ArrayMap<>();
-    private final ArrayMap<WindowState, ArrayList<Integer>> mWinControlTypeMap = new ArrayMap<>();
-    private final SparseArray<WindowState> mTypeWinControlMap = new SparseArray<>();
-    private final ArraySet<WindowState> mPendingControlChanged = new ArraySet<>();
+    private final ArrayMap<Integer, InsetsSourceProvider> mProviders = new ArrayMap<>();
+    private final ArrayMap<InsetsControlTarget, ArrayList<Integer>> mControlTargetTypeMap =
+            new ArrayMap<>();
+    private final SparseArray<InsetsControlTarget> mTypeControlTargetMap = new SparseArray<>();
+    private final ArraySet<InsetsControlTarget> mPendingControlChanged = new ArraySet<>();
 
     private final Consumer<WindowState> mDispatchInsetsChanged = w -> {
         if (w.isVisible()) {
@@ -87,15 +85,15 @@
         return state;
     }
 
-    @Nullable InsetsSourceControl[] getControlsForDispatch(WindowState target) {
-        ArrayList<Integer> controlled = mWinControlTypeMap.get(target);
+    @Nullable InsetsSourceControl[] getControlsForDispatch(InsetsControlTarget target) {
+        ArrayList<Integer> controlled = mControlTargetTypeMap.get(target);
         if (controlled == null) {
             return null;
         }
         final int size = controlled.size();
         final InsetsSourceControl[] result = new InsetsSourceControl[size];
         for (int i = 0; i < size; i++) {
-            result[i] = mControllers.get(controlled.get(i)).getControl();
+            result[i] = mProviders.get(controlled.get(i)).getControl();
         }
         return result;
     }
@@ -103,8 +101,8 @@
     /**
      * @return The provider of a specific type.
      */
-    InsetsSourceProvider getSourceProvider(int type) {
-        return mControllers.computeIfAbsent(type,
+    InsetsSourceProvider getSourceProvider(@InternalInsetType int type) {
+        return mProviders.computeIfAbsent(type,
                 key -> new InsetsSourceProvider(mState.getSource(key), this, mDisplayContent));
     }
 
@@ -113,8 +111,8 @@
      */
     void onPostLayout() {
         mState.setDisplayFrame(mDisplayContent.getBounds());
-        for (int i = mControllers.size() - 1; i>= 0; i--) {
-            mControllers.valueAt(i).onPostLayout();
+        for (int i = mProviders.size() - 1; i >= 0; i--) {
+            mProviders.valueAt(i).onPostLayout();
         }
         if (!mLastState.equals(mState)) {
             mLastState.set(mState, true /* copySources */);
@@ -126,7 +124,7 @@
         boolean changed = false;
         for (int i = state.getSourcesCount() - 1; i >= 0; i--) {
             final InsetsSource source = state.sourceAt(i);
-            final InsetsSourceProvider provider = mControllers.get(source.getType());
+            final InsetsSourceProvider provider = mProviders.get(source.getType());
             if (provider == null) {
                 continue;
             }
@@ -137,75 +135,77 @@
         }
     }
 
-    void onImeTargetChanged(@Nullable WindowState imeTarget) {
+    void onImeTargetChanged(@Nullable InsetsControlTarget imeTarget) {
         onControlChanged(TYPE_IME, imeTarget);
         notifyPendingInsetsControlChanged();
     }
 
     /**
-     * Called when the top opaque fullscreen window that is able to control the system bars changes.
+     * Called when the focused window that is able to control the system bars changes.
      *
-     * @param controllingWindow The window that is now able to control the system bars appearance
-     *                          and visibility.
+     * @param topControlling The target that is now able to control the top bar appearance
+     *                       and visibility.
+     * @param navControlling The target that is now able to control the nav bar appearance
+     *                       and visibility.
      */
-    void onBarControllingWindowChanged(@Nullable WindowState controllingWindow) {
-        // TODO: Apply policy that determines whether controllingWindow is able to control system
-        // bars
-
-        // TODO: Depending on the form factor, mapping is different
-        onControlChanged(TYPE_TOP_BAR, controllingWindow);
-        onControlChanged(TYPE_NAVIGATION_BAR, controllingWindow);
+    void onBarControlTargetChanged(@Nullable InsetsControlTarget topControlling,
+            @Nullable InsetsControlTarget navControlling) {
+        onControlChanged(TYPE_TOP_BAR, topControlling);
+        onControlChanged(TYPE_NAVIGATION_BAR, navControlling);
         notifyPendingInsetsControlChanged();
     }
 
-    void notifyControlRevoked(@NonNull WindowState previousControllingWin,
+    void notifyControlRevoked(@NonNull InsetsControlTarget previousControlTarget,
             InsetsSourceProvider provider) {
-        removeFromControlMaps(previousControllingWin, provider.getSource().getType());
+        removeFromControlMaps(previousControlTarget, provider.getSource().getType());
     }
 
-    private void onControlChanged(int type, @Nullable WindowState win) {
-        final WindowState previous = mTypeWinControlMap.get(type);
-        if (win == previous) {
+    private void onControlChanged(@InternalInsetType int type,
+            @Nullable InsetsControlTarget target) {
+        final InsetsControlTarget previous = mTypeControlTargetMap.get(type);
+        if (target == previous) {
             return;
         }
-        final InsetsSourceProvider controller = getSourceProvider(type);
-        if (controller == null) {
+        final InsetsSourceProvider provider = getSourceProvider(type);
+        if (provider == null) {
             return;
         }
-        if (!controller.isControllable()) {
+        if (!provider.isControllable()) {
             return;
         }
-        controller.updateControlForTarget(win, false /* force */);
+        provider.updateControlForTarget(target, false /* force */);
         if (previous != null) {
             removeFromControlMaps(previous, type);
             mPendingControlChanged.add(previous);
         }
-        if (win != null) {
-            addToControlMaps(win, type);
-            mPendingControlChanged.add(win);
+        if (target != null) {
+            addToControlMaps(target, type);
+            mPendingControlChanged.add(target);
         }
     }
 
-    private void removeFromControlMaps(@NonNull WindowState win, int type) {
-        final ArrayList<Integer> array = mWinControlTypeMap.get(win);
+    private void removeFromControlMaps(@NonNull InsetsControlTarget target,
+            @InternalInsetType int type) {
+        final ArrayList<Integer> array = mControlTargetTypeMap.get(target);
         if (array == null) {
             return;
         }
         array.remove((Integer) type);
         if (array.isEmpty()) {
-            mWinControlTypeMap.remove(win);
+            mControlTargetTypeMap.remove(target);
         }
-        mTypeWinControlMap.remove(type);
+        mTypeControlTargetMap.remove(type);
     }
 
-    private void addToControlMaps(@NonNull WindowState win, int type) {
-        final ArrayList<Integer> array = mWinControlTypeMap.computeIfAbsent(win,
+    private void addToControlMaps(@NonNull InsetsControlTarget target,
+            @InternalInsetType int type) {
+        final ArrayList<Integer> array = mControlTargetTypeMap.computeIfAbsent(target,
                 key -> new ArrayList<>());
         array.add(type);
-        mTypeWinControlMap.put(type, win);
+        mTypeControlTargetMap.put(type, target);
     }
 
-    void notifyControlChanged(WindowState target) {
+    void notifyControlChanged(InsetsControlTarget target) {
         mPendingControlChanged.add(target);
         notifyPendingInsetsControlChanged();
     }
@@ -216,8 +216,8 @@
         }
         mDisplayContent.mWmService.mAnimator.addAfterPrepareSurfacesRunnable(() -> {
             for (int i = mPendingControlChanged.size() - 1; i >= 0; i--) {
-                final WindowState controllingWin = mPendingControlChanged.valueAt(i);
-                controllingWin.notifyInsetsControlChanged();
+                final InsetsControlTarget controlTarget = mPendingControlChanged.valueAt(i);
+                controlTarget.notifyInsetsControlChanged();
             }
             mPendingControlChanged.clear();
         });
@@ -231,10 +231,10 @@
         pw.println(prefix + "WindowInsetsStateController");
         mState.dump(prefix + "  ", pw);
         pw.println(prefix + "  " + "Control map:");
-        for (int i = mTypeWinControlMap.size() - 1; i >= 0; i--) {
+        for (int i = mTypeControlTargetMap.size() - 1; i >= 0; i--) {
             pw.print(prefix + "  ");
-            pw.println(InsetsState.typeToString(mTypeWinControlMap.keyAt(i)) + " -> "
-                    + mTypeWinControlMap.valueAt(i));
+            pw.println(InsetsState.typeToString(mTypeControlTargetMap.keyAt(i)) + " -> "
+                    + mTypeControlTargetMap.valueAt(i));
         }
     }
 }
diff --git a/services/core/java/com/android/server/wm/LaunchParamsController.java b/services/core/java/com/android/server/wm/LaunchParamsController.java
index 59c02f7..59df09b 100644
--- a/services/core/java/com/android/server/wm/LaunchParamsController.java
+++ b/services/core/java/com/android/server/wm/LaunchParamsController.java
@@ -152,7 +152,7 @@
 
             if (task.getStack().inFreeformWindowingMode()) {
                 // Only set bounds if it's in freeform mode.
-                task.updateOverrideConfiguration(mTmpParams.mBounds);
+                task.setBounds(mTmpParams.mBounds);
                 return true;
             }
 
diff --git a/services/core/java/com/android/server/wm/Letterbox.java b/services/core/java/com/android/server/wm/Letterbox.java
index bb035d5..1bd2493 100644
--- a/services/core/java/com/android/server/wm/Letterbox.java
+++ b/services/core/java/com/android/server/wm/Letterbox.java
@@ -41,7 +41,8 @@
     private static final Rect EMPTY_RECT = new Rect();
     private static final Point ZERO_POINT = new Point(0, 0);
 
-    private final Supplier<SurfaceControl.Builder> mFactory;
+    private final Supplier<SurfaceControl.Builder> mSurfaceControlFactory;
+    private final Supplier<SurfaceControl.Transaction> mTransactionFactory;
     private final Rect mOuter = new Rect();
     private final Rect mInner = new Rect();
     private final LetterboxSurface mTop = new LetterboxSurface("top");
@@ -55,8 +56,10 @@
      *
      * @param surfaceControlFactory a factory for creating the managed {@link SurfaceControl}s
      */
-    public Letterbox(Supplier<SurfaceControl.Builder> surfaceControlFactory) {
-        mFactory = surfaceControlFactory;
+    public Letterbox(Supplier<SurfaceControl.Builder> surfaceControlFactory,
+            Supplier<SurfaceControl.Transaction> transactionFactory) {
+        mSurfaceControlFactory = surfaceControlFactory;
+        mTransactionFactory = transactionFactory;
     }
 
     /**
@@ -245,7 +248,7 @@
         }
 
         private void createSurface() {
-            mSurface = mFactory.get().setName("Letterbox - " + mType)
+            mSurface = mSurfaceControlFactory.get().setName("Letterbox - " + mType)
                     .setFlags(HIDDEN).setColorLayer().build();
             mSurface.setLayer(-1);
             mSurface.setColor(new float[]{0, 0, 0});
@@ -261,7 +264,7 @@
 
         public void remove() {
             if (mSurface != null) {
-                new SurfaceControl.Transaction().remove(mSurface).apply();
+                mTransactionFactory.get().remove(mSurface).apply();
                 mSurface = null;
             }
             if (mInputInterceptor != null) {
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index 8752f37..f4280a2 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -592,7 +592,7 @@
             return null;
         }
 
-        final TaskScreenshotAnimatable animatable = new TaskScreenshotAnimatable(task,
+        final TaskScreenshotAnimatable animatable = new TaskScreenshotAnimatable(mService.mSurfaceControlFactory, task,
                 new SurfaceControl.ScreenshotGraphicBuffer(taskSnapshot.getSnapshot(),
                         taskSnapshot.getColorSpace(), false /* containsSecureLayers */));
         mRecentScreenshotAnimator = new SurfaceAnimator(
diff --git a/services/core/java/com/android/server/wm/RootActivityContainer.java b/services/core/java/com/android/server/wm/RootActivityContainer.java
index eb5d096..50b5902 100644
--- a/services/core/java/com/android/server/wm/RootActivityContainer.java
+++ b/services/core/java/com/android/server/wm/RootActivityContainer.java
@@ -27,12 +27,10 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
-import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
 import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_INSTANCE;
 import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK;
-import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.Display.INVALID_DISPLAY;
 import static android.view.WindowManager.TRANSIT_SHOW_SINGLE_TASK_DISPLAY;
@@ -93,7 +91,6 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.SystemClock;
-import android.os.Trace;
 import android.os.UserHandle;
 import android.os.storage.StorageManager;
 import android.provider.Settings;
@@ -881,48 +878,6 @@
         }
     }
 
-    void resizeStack(ActivityStack stack, Rect bounds, Rect tempTaskBounds,
-            Rect tempTaskInsetBounds, boolean preserveWindows, boolean allowResizeInDockedMode,
-            boolean deferResume) {
-
-        if (stack.inSplitScreenPrimaryWindowingMode()) {
-            mStackSupervisor.resizeDockedStackLocked(bounds, tempTaskBounds,
-                    tempTaskInsetBounds, null, null, preserveWindows, deferResume);
-            return;
-        }
-
-        final boolean splitScreenActive = getDefaultDisplay().hasSplitScreenPrimaryStack();
-        if (!allowResizeInDockedMode
-                && !stack.getWindowConfiguration().tasksAreFloating() && splitScreenActive) {
-            // If the docked stack exists, don't resize non-floating stacks independently of the
-            // size computed from the docked stack size (otherwise they will be out of sync)
-            return;
-        }
-
-        Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizeStack_" + stack.mStackId);
-        mWindowManager.deferSurfaceLayout();
-        try {
-            if (stack.affectedBySplitScreenResize()) {
-                if (bounds == null && stack.inSplitScreenWindowingMode()) {
-                    // null bounds = fullscreen windowing mode...at least for now.
-                    stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
-                } else if (splitScreenActive) {
-                    // If we are in split-screen mode and this stack support split-screen, then
-                    // it should be split-screen secondary mode. i.e. adjacent to the docked stack.
-                    stack.setWindowingMode(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
-                }
-            }
-            stack.resize(bounds, tempTaskBounds, tempTaskInsetBounds);
-            if (!deferResume) {
-                stack.ensureVisibleActivitiesConfigurationLocked(
-                        stack.topRunningActivityLocked(), preserveWindows);
-            }
-        } finally {
-            mWindowManager.continueSurfaceLayout();
-            Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
-        }
-    }
-
     /**
      * Move stack with all its existing content to specified display.
      * @param stackId Id of stack to move.
@@ -1014,9 +969,8 @@
             // Resize the pinned stack to match the current size of the task the activity we are
             // going to be moving is currently contained in. We do this to have the right starting
             // animation bounds for the pinned stack to the desired bounds the caller wants.
-            resizeStack(stack, task.getRequestedOverrideBounds(), null /* tempTaskBounds */,
-                    null /* tempTaskInsetBounds */, !PRESERVE_WINDOWS,
-                    true /* allowResizeInDockedMode */, !DEFER_RESUME);
+            stack.resize(task.getRequestedOverrideBounds(), null /* tempTaskBounds */,
+                    null /* tempTaskInsetBounds */, !PRESERVE_WINDOWS, !DEFER_RESUME);
 
             if (task.mActivities.size() == 1) {
                 // Defer resume until below, and do not schedule PiP changes until we animate below
@@ -2260,9 +2214,9 @@
     void getRunningTasks(int maxNum, List<ActivityManager.RunningTaskInfo> list,
             @WindowConfiguration.ActivityType int ignoreActivityType,
             @WindowConfiguration.WindowingMode int ignoreWindowingMode, int callingUid,
-            boolean allowed) {
+            boolean allowed, boolean crossUser) {
         mStackSupervisor.getRunningTasks().getTasks(maxNum, list, ignoreActivityType,
-                ignoreWindowingMode, mActivityDisplays, callingUid, allowed);
+                ignoreWindowingMode, mActivityDisplays, callingUid, allowed, crossUser);
     }
 
     void sendPowerHintForLaunchStartIfNeeded(boolean forceSend, ActivityRecord targetActivity) {
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 968d02b..d0b6fc8 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -134,7 +134,7 @@
 
     // Only a separate transaction until we separate the apply surface changes
     // transaction from the global transaction.
-    private final SurfaceControl.Transaction mDisplayTransaction = new SurfaceControl.Transaction();
+    private final SurfaceControl.Transaction mDisplayTransaction;
 
     private final Consumer<WindowState> mCloseSystemDialogsConsumer = w -> {
         if (w.mHasSurface) {
@@ -154,6 +154,7 @@
 
     RootWindowContainer(WindowManagerService service) {
         super(service);
+        mDisplayTransaction = service.mTransactionFactory.get();
         mHandler = new MyHandler(service.mH.getLooper());
     }
 
diff --git a/services/core/java/com/android/server/wm/RunningTasks.java b/services/core/java/com/android/server/wm/RunningTasks.java
index 3bf437d..22a9c32 100644
--- a/services/core/java/com/android/server/wm/RunningTasks.java
+++ b/services/core/java/com/android/server/wm/RunningTasks.java
@@ -40,7 +40,7 @@
 
     void getTasks(int maxNum, List<RunningTaskInfo> list, @ActivityType int ignoreActivityType,
             @WindowingMode int ignoreWindowingMode, ArrayList<ActivityDisplay> activityDisplays,
-            int callingUid, boolean allowed) {
+            int callingUid, boolean allowed, boolean crossUser) {
         // Return early if there are no tasks to fetch
         if (maxNum <= 0) {
             return;
@@ -55,7 +55,7 @@
                 final ActivityStack stack = display.getChildAt(stackNdx);
                 mTmpStackTasks.clear();
                 stack.getRunningTasks(mTmpStackTasks, ignoreActivityType, ignoreWindowingMode,
-                        callingUid, allowed);
+                        callingUid, allowed, crossUser);
                 mTmpSortedSet.addAll(mTmpStackTasks);
             }
         }
diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
index 82dde0d..cbaf098 100644
--- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -127,7 +127,7 @@
         mOriginalWidth = originalWidth;
         mOriginalHeight = originalHeight;
 
-        final SurfaceControl.Transaction t = mService.mTransactionFactory.make();
+        final SurfaceControl.Transaction t = mService.mTransactionFactory.get();
         try {
             mSurfaceControl = displayContent.makeOverlay()
                     .setName("ScreenshotSurface")
@@ -137,13 +137,13 @@
 
             // In case display bounds change, screenshot buffer and surface may mismatch so set a
             // scaling mode.
-            SurfaceControl.Transaction t2 = mService.mTransactionFactory.make();
+            SurfaceControl.Transaction t2 = mService.mTransactionFactory.get();
             t2.setOverrideScalingMode(mSurfaceControl, Surface.SCALING_MODE_SCALE_TO_WINDOW);
             t2.apply(true /* sync */);
 
             // Capture a screenshot into the surface we just created.
             final int displayId = display.getDisplayId();
-            final Surface surface = mService.mSurfaceFactory.make();
+            final Surface surface = mService.mSurfaceFactory.get();
             surface.copyFrom(mSurfaceControl);
             SurfaceControl.ScreenshotGraphicBuffer gb =
                     mService.mDisplayManagerInternal.screenshot(displayId);
@@ -427,7 +427,7 @@
                 Slog.i(TAG_WM,
                         "  FREEZE " + mSurfaceControl + ": DESTROY");
             }
-            mService.mTransactionFactory.make().remove(mSurfaceControl).apply();
+            mService.mTransactionFactory.get().remove(mSurfaceControl).apply();
             mSurfaceControl = null;
         }
         if (mExitingBlackFrame != null) {
diff --git a/services/core/java/com/android/server/wm/StrictModeFlash.java b/services/core/java/com/android/server/wm/StrictModeFlash.java
index 82f2ad8..9e5d9ca 100644
--- a/services/core/java/com/android/server/wm/StrictModeFlash.java
+++ b/services/core/java/com/android/server/wm/StrictModeFlash.java
@@ -27,17 +27,20 @@
 import android.view.Surface.OutOfResourcesException;
 import android.view.SurfaceControl;
 
+import java.util.function.Supplier;
+
 class StrictModeFlash {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "StrictModeFlash" : TAG_WM;
 
     private final SurfaceControl mSurfaceControl;
-    private final Surface mSurface = new Surface();
+    private final Surface mSurface;
     private int mLastDW;
     private int mLastDH;
     private boolean mDrawNeeded;
     private final int mThickness = 20;
 
-    public StrictModeFlash(DisplayContent dc) {
+    StrictModeFlash(Supplier<Surface> surfaceFactory, DisplayContent dc) {
+        mSurface = surfaceFactory.get();
         SurfaceControl ctrl = null;
         try {
             ctrl = dc.makeOverlay()
diff --git a/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java b/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
index 85176be..bbd986f 100644
--- a/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
+++ b/services/core/java/com/android/server/wm/SurfaceAnimationRunner.java
@@ -39,6 +39,8 @@
 import com.android.server.AnimationThread;
 import com.android.server.wm.LocalAnimationAdapter.AnimationSpec;
 
+import java.util.function.Supplier;
+
 /**
  * Class to run animations without holding the window manager lock.
  */
@@ -73,9 +75,10 @@
     @GuardedBy("mLock")
     private boolean mAnimationStartDeferred;
 
-    SurfaceAnimationRunner(PowerManagerInternal powerManagerInternal) {
-        this(null /* callbackProvider */, null /* animatorFactory */, new Transaction(),
-                powerManagerInternal);
+    SurfaceAnimationRunner(Supplier<Transaction> transactionFactory,
+            PowerManagerInternal powerManagerInternal) {
+        this(null /* callbackProvider */, null /* animatorFactory */,
+                transactionFactory.get(), powerManagerInternal);
     }
 
     @VisibleForTesting
diff --git a/services/core/java/com/android/server/wm/SurfaceBuilderFactory.java b/services/core/java/com/android/server/wm/SurfaceBuilderFactory.java
deleted file mode 100644
index 5390e5a..0000000
--- a/services/core/java/com/android/server/wm/SurfaceBuilderFactory.java
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.wm;
-
-import android.view.SurfaceSession;
-import android.view.SurfaceControl;
-
-interface SurfaceBuilderFactory {
-    SurfaceControl.Builder make(SurfaceSession s);
-};
-
diff --git a/services/core/java/com/android/server/wm/TaskRecord.java b/services/core/java/com/android/server/wm/TaskRecord.java
index e25ca73..d3f3981 100644
--- a/services/core/java/com/android/server/wm/TaskRecord.java
+++ b/services/core/java/com/android/server/wm/TaskRecord.java
@@ -512,7 +512,7 @@
         if (!getWindowConfiguration().persistTaskBounds()) {
             // Reset current bounds for task whose bounds shouldn't be persisted so it uses
             // default configuration the next time it launches.
-            updateOverrideConfiguration(null);
+            setBounds(null);
         }
         mService.getTaskChangeNotificationController().notifyTaskRemoved(taskId);
     }
@@ -565,7 +565,7 @@
                 // Task doesn't exist in window manager yet (e.g. was restored from recents).
                 // All we can do for now is update the bounds so it can be used when the task is
                 // added to window manager.
-                updateOverrideConfiguration(bounds);
+                setBounds(bounds);
                 if (!inFreeformWindowingMode()) {
                     // re-restore the task so it can have the proper stack association.
                     mService.mStackSupervisor.restoreRecentTaskLocked(this, null, !ON_TOP);
@@ -584,7 +584,11 @@
 
             Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizeTask_" + taskId);
 
-            final boolean updatedConfig = updateOverrideConfiguration(bounds);
+            boolean updatedConfig = false;
+            mTmpConfig.setTo(getResolvedOverrideConfiguration());
+            if (setBounds(bounds) != BOUNDS_CHANGE_NONE) {
+                updatedConfig = !mTmpConfig.equals(getResolvedOverrideConfiguration());
+            }
             // This variable holds information whether the configuration didn't change in a significant
 
             // way and the activity was kept the way it was. If it's false, it means the activity
@@ -1803,15 +1807,6 @@
         }
     }
 
-    /**
-     * Update task's override configuration based on the bounds.
-     * @param bounds The bounds of the task.
-     * @return True if the override configuration was updated.
-     */
-    boolean updateOverrideConfiguration(Rect bounds) {
-        return updateOverrideConfiguration(bounds, null /* insetBounds */);
-    }
-
     void setLastNonFullscreenBounds(Rect bounds) {
         if (mLastNonFullscreenBounds == null) {
             mLastNonFullscreenBounds = new Rect(bounds);
@@ -1821,32 +1816,6 @@
     }
 
     /**
-     * Update task's override configuration based on the bounds.
-     * @param bounds The bounds of the task.
-     * @param insetBounds The bounds used to calculate the system insets, which is used here to
-     *                    subtract the navigation bar/status bar size from the screen size reported
-     *                    to the application. See {@link IActivityTaskManager#resizeDockedStack}.
-     * @return True if the override configuration was updated.
-     */
-    boolean updateOverrideConfiguration(Rect bounds, @Nullable Rect insetBounds) {
-        final boolean hasSetDisplayedBounds = (insetBounds != null && !insetBounds.isEmpty());
-        if (hasSetDisplayedBounds) {
-            setDisplayedBounds(bounds);
-        } else {
-            setDisplayedBounds(null);
-        }
-        // "steady" bounds do not include any temporary offsets from animation or interaction.
-        Rect steadyBounds = hasSetDisplayedBounds ? insetBounds : bounds;
-        if (equivalentRequestedOverrideBounds(steadyBounds)) {
-            return false;
-        }
-
-        mTmpConfig.setTo(getResolvedOverrideConfiguration());
-        setBounds(steadyBounds);
-        return !mTmpConfig.equals(getResolvedOverrideConfiguration());
-    }
-
-    /**
      * This should be called when an child activity changes state. This should only
      * be called from
      * {@link ActivityRecord#setState(ActivityState, String)} .
@@ -2297,7 +2266,7 @@
 
     Rect updateOverrideConfigurationFromLaunchBounds() {
         final Rect bounds = getLaunchBounds();
-        updateOverrideConfiguration(bounds);
+        setBounds(bounds);
         if (bounds != null && !bounds.isEmpty()) {
             // TODO: Review if we actually want to do this - we are setting the launch bounds
             // directly here.
@@ -2322,12 +2291,12 @@
                 return;
             }
             if (mLastNonFullscreenBounds != null) {
-                updateOverrideConfiguration(mLastNonFullscreenBounds);
+                setBounds(mLastNonFullscreenBounds);
             } else {
                 mService.mStackSupervisor.getLaunchParamsController().layoutTask(this, null);
             }
         } else {
-            updateOverrideConfiguration(inStack.getRequestedOverrideBounds());
+            setBounds(inStack.getRequestedOverrideBounds());
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/TaskScreenshotAnimatable.java b/services/core/java/com/android/server/wm/TaskScreenshotAnimatable.java
index d6c2f66..d36ebf0 100644
--- a/services/core/java/com/android/server/wm/TaskScreenshotAnimatable.java
+++ b/services/core/java/com/android/server/wm/TaskScreenshotAnimatable.java
@@ -23,6 +23,8 @@
 import android.view.SurfaceControl;
 import android.view.SurfaceSession;
 
+import java.util.function.Function;
+
 /**
  * Class used by {@link RecentsAnimationController} to create a surface control with taking
  * screenshot of task when canceling recents animation.
@@ -36,7 +38,7 @@
     private int mWidth;
     private int mHeight;
 
-    TaskScreenshotAnimatable(Task task,
+    TaskScreenshotAnimatable(Function<SurfaceSession, SurfaceControl.Builder> surfaceControlFactory, Task task,
             SurfaceControl.ScreenshotGraphicBuffer screenshotBuffer) {
         GraphicBuffer buffer = screenshotBuffer == null
                 ? null : screenshotBuffer.getGraphicBuffer();
@@ -47,7 +49,7 @@
             Slog.d(TAG, "Creating TaskScreenshotAnimatable: task: " + task
                     + "width: " + mWidth + "height: " + mHeight);
         }
-        mSurfaceControl = new SurfaceControl.Builder(new SurfaceSession())
+        mSurfaceControl = surfaceControlFactory.apply(new SurfaceSession())
                 .setName("RecentTaskScreenshotSurface")
                 .setBufferSize(mWidth, mHeight)
                 .build();
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
index 1b3e4c1..7456f0d 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
@@ -237,7 +237,7 @@
             int sysUiVis, int windowFlags, int windowPrivateFlags, Rect taskBounds,
             int currentOrientation) {
         mService = service;
-        mSurface = new Surface();
+        mSurface = service.mSurfaceFactory.get();
         mHandler = new Handler(mService.mH.getLooper());
         mSession = WindowManagerGlobal.getWindowSession();
         mWindow = window;
@@ -325,13 +325,13 @@
                 - ((float) mFrame.width() / mFrame.height())) > 0.01f;
 
         // Keep a reference to it such that it doesn't get destroyed when finalized.
-        mChildSurfaceControl = new SurfaceControl.Builder(session)
+        mChildSurfaceControl = mService.mSurfaceControlFactory.apply(session)
                 .setName(mTitle + " - task-snapshot-surface")
                 .setBufferSize(buffer.getWidth(), buffer.getHeight())
                 .setFormat(buffer.getFormat())
                 .setParent(mSurfaceControl)
                 .build();
-        Surface surface = new Surface();
+        Surface surface = mService.mSurfaceFactory.get();
         surface.copyFrom(mChildSurfaceControl);
 
         final Rect frame;
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index cc2112e..bef6a37 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -859,7 +859,7 @@
         // When the home stack is resizable, should always have the same stack and task bounds
         if (isActivityTypeHome()) {
             final Task homeTask = findHomeTask();
-            if (homeTask != null && homeTask.isResizeable()) {
+            if (homeTask == null || homeTask.isResizeable()) {
                 // Calculate the home stack bounds when in docked mode and the home stack is
                 // resizeable.
                 getDisplayContent().mDividerControllerLocked
@@ -965,7 +965,7 @@
     }
 
     void resetDockedStackToMiddle() {
-        if (inSplitScreenPrimaryWindowingMode()) {
+        if (!inSplitScreenPrimaryWindowingMode()) {
             throw new IllegalStateException("Not a docked stack=" + this);
         }
 
@@ -973,12 +973,12 @@
 
         final Rect bounds = new Rect();
         final Rect tempBounds = new Rect();
-        TaskStack dockedStack = mDisplayContent.getSplitScreenPrimaryStackIgnoringVisibility();
-        Rect dockedBounds =
-                (dockedStack == null || dockedStack == this) ? null : dockedStack.getRawBounds();
-        getStackDockedModeBoundsLocked(mDisplayContent.getConfiguration(), dockedBounds,
+        getStackDockedModeBoundsLocked(mDisplayContent.getConfiguration(), null /* dockedBounds */,
                 null /* currentTempTaskBounds */, bounds, tempBounds);
-        mActivityStack.requestResize(bounds);
+        mActivityStack.mStackSupervisor.resizeDockedStackLocked(bounds, null /* tempTaskBounds */,
+                null /* tempTaskInsetBounds */, null /* tempOtherTaskBounds */,
+                null /* tempOtherTaskInsetBounds */, false /* preserveWindows */,
+                false /* deferResume */);
     }
 
     @Override
@@ -1009,7 +1009,7 @@
         EventLog.writeEvent(EventLogTags.WM_STACK_REMOVED, mStackId);
 
         if (mAnimationBackgroundSurface != null) {
-            mWmService.mTransactionFactory.make().remove(mAnimationBackgroundSurface).apply();
+            mWmService.mTransactionFactory.get().remove(mAnimationBackgroundSurface).apply();
             mAnimationBackgroundSurface = null;
         }
 
diff --git a/services/core/java/com/android/server/wm/TransactionFactory.java b/services/core/java/com/android/server/wm/TransactionFactory.java
deleted file mode 100644
index 067f083..0000000
--- a/services/core/java/com/android/server/wm/TransactionFactory.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.server.wm;
-
-import android.view.SurfaceControl.Transaction;
-
-/**
- * Helper class to inject custom transaction objects into window manager.
- */
-interface TransactionFactory {
-    Transaction make();
-};
-
diff --git a/services/core/java/com/android/server/wm/Watermark.java b/services/core/java/com/android/server/wm/Watermark.java
index e6ac059..729cfc0 100644
--- a/services/core/java/com/android/server/wm/Watermark.java
+++ b/services/core/java/com/android/server/wm/Watermark.java
@@ -33,6 +33,8 @@
 import android.view.Surface.OutOfResourcesException;
 import android.view.SurfaceControl;
 
+import java.util.function.Supplier;
+
 /**
  * Displays a watermark on top of the window manager's windows.
  */
@@ -47,19 +49,20 @@
     private final int mDeltaY;
 
     private final SurfaceControl mSurfaceControl;
-    private final Surface mSurface = new Surface();
+    private final Surface mSurface;
     private int mLastDW;
     private int mLastDH;
     private boolean mDrawNeeded;
 
-    Watermark(DisplayContent dc, DisplayMetrics dm, String[] tokens) {
+    Watermark(Supplier<Surface> surfaceFactory, DisplayContent dc, DisplayMetrics dm,
+            String[] tokens) {
         if (false) {
             Log.i(TAG_WM, "*********************** WATERMARK");
             for (int i=0; i<tokens.length; i++) {
                 Log.i(TAG_WM, "  TOKEN #" + i + ": " + tokens[i]);
             }
         }
-
+        mSurface = surfaceFactory.get();
         mDisplay = dc.getDisplay();
         mTokens = tokens;
 
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index b8db98b..4fce46b 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -83,12 +83,13 @@
     private final ArrayList<Runnable> mAfterPrepareSurfacesRunnables = new ArrayList<>();
     private boolean mInExecuteAfterPrepareSurfacesRunnables;
 
-    private final SurfaceControl.Transaction mTransaction = new SurfaceControl.Transaction();
+    private final SurfaceControl.Transaction mTransaction;
 
     WindowAnimator(final WindowManagerService service) {
         mService = service;
         mContext = service.mContext;
         mPolicy = service.mPolicy;
+        mTransaction = service.mTransactionFactory.get();
         AnimationThread.getHandler().runWithScissors(
                 () -> mChoreographer = Choreographer.getSfInstance(), 0 /* timeout */);
 
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 29d232f..e280a663 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -139,7 +139,7 @@
 
     WindowContainer(WindowManagerService wms) {
         mWmService = wms;
-        mPendingTransaction = wms.mTransactionFactory.make();
+        mPendingTransaction = wms.mTransactionFactory.get();
         mSurfaceAnimator = new SurfaceAnimator(this, this::onAnimationFinished, wms);
     }
 
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index fa1ace6..8d3a107 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -286,6 +286,8 @@
 import java.util.Arrays;
 import java.util.Date;
 import java.util.List;
+import java.util.function.Function;
+import java.util.function.Supplier;
 
 /** {@hide} */
 public class WindowManagerService extends IWindowManager.Stub
@@ -911,9 +913,9 @@
     static WindowManagerThreadPriorityBooster sThreadPriorityBooster =
             new WindowManagerThreadPriorityBooster();
 
-    SurfaceBuilderFactory mSurfaceBuilderFactory = SurfaceControl.Builder::new;
-    TransactionFactory mTransactionFactory = SurfaceControl.Transaction::new;
-    SurfaceFactory mSurfaceFactory = Surface::new;
+    Function<SurfaceSession, SurfaceControl.Builder> mSurfaceControlFactory;
+    Supplier<SurfaceControl.Transaction> mTransactionFactory;
+    final Supplier<Surface> mSurfaceFactory;
 
     private final SurfaceControl.Transaction mTransaction;
 
@@ -1013,20 +1015,22 @@
             final boolean showBootMsgs, final boolean onlyCore, WindowManagerPolicy policy,
             ActivityTaskManagerService atm) {
         return main(context, im, showBootMsgs, onlyCore, policy, atm,
-                SurfaceControl.Transaction::new);
+                SurfaceControl.Transaction::new, Surface::new, SurfaceControl.Builder::new);
     }
 
     /**
      * Creates and returns an instance of the WindowManagerService. This call allows the caller
-     * to override the {@link TransactionFactory} to stub functionality under test.
+     * to override factories that can be used to stub native calls during test.
      */
     @VisibleForTesting
     public static WindowManagerService main(final Context context, final InputManagerService im,
             final boolean showBootMsgs, final boolean onlyCore, WindowManagerPolicy policy,
-            ActivityTaskManagerService atm, TransactionFactory transactionFactory) {
+            ActivityTaskManagerService atm, Supplier<SurfaceControl.Transaction> transactionFactory,
+            Supplier<Surface> surfaceFactory,
+            Function<SurfaceSession, SurfaceControl.Builder> surfaceControlFactory) {
         DisplayThread.getHandler().runWithScissors(() ->
                 sInstance = new WindowManagerService(context, im, showBootMsgs, onlyCore, policy,
-                        atm, transactionFactory), 0);
+                        atm, transactionFactory, surfaceFactory, surfaceControlFactory), 0);
         return sInstance;
     }
 
@@ -1048,7 +1052,9 @@
 
     private WindowManagerService(Context context, InputManagerService inputManager,
             boolean showBootMsgs, boolean onlyCore, WindowManagerPolicy policy,
-            ActivityTaskManagerService atm, TransactionFactory transactionFactory) {
+            ActivityTaskManagerService atm, Supplier<SurfaceControl.Transaction> transactionFactory,
+            Supplier<Surface> surfaceFactory,
+            Function<SurfaceSession, SurfaceControl.Builder> surfaceControlFactory) {
         installLock(this, INDEX_WINDOW);
         mGlobalLock = atm.getGlobalLock();
         mAtmService = atm;
@@ -1076,10 +1082,13 @@
                 com.android.internal.R.bool.config_lowRamTaskSnapshotsAndRecents);
         mInputManager = inputManager; // Must be before createDisplayContentLocked.
         mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
-        mDisplayWindowSettings = new DisplayWindowSettings(this);
 
+        mSurfaceControlFactory = surfaceControlFactory;
         mTransactionFactory = transactionFactory;
-        mTransaction = mTransactionFactory.make();
+        mSurfaceFactory = surfaceFactory;
+        mTransaction = mTransactionFactory.get();
+
+        mDisplayWindowSettings = new DisplayWindowSettings(this);
         mPolicy = policy;
         mAnimator = new WindowAnimator(this);
         mRoot = new RootWindowContainer(this);
@@ -1183,7 +1192,8 @@
                 PowerManager.SCREEN_BRIGHT_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE, TAG_WM);
         mHoldingScreenWakeLock.setReferenceCounted(false);
 
-        mSurfaceAnimationRunner = new SurfaceAnimationRunner(mPowerManagerInternal);
+        mSurfaceAnimationRunner = new SurfaceAnimationRunner(mTransactionFactory,
+                mPowerManagerInternal);
 
         mAllowTheaterModeWakeFromLayout = context.getResources().getBoolean(
                 com.android.internal.R.bool.config_allowTheaterModeWakeFromWindowLayout);
@@ -3460,7 +3470,7 @@
                         int maskThickness = mContext.getResources().getDimensionPixelSize(
                                 com.android.internal.R.dimen.circular_display_mask_thickness);
 
-                        mCircularDisplayMask = new CircularDisplayMask(
+                        mCircularDisplayMask = new CircularDisplayMask(mSurfaceFactory,
                                 getDefaultDisplayContentLocked(),
                                 mPolicy.getWindowLayerFromTypeLw(
                                         WindowManager.LayoutParams.TYPE_POINTER)
@@ -3488,6 +3498,7 @@
             try {
                 if (mEmulatorDisplayOverlay == null) {
                     mEmulatorDisplayOverlay = new EmulatorDisplayOverlay(
+                            mSurfaceFactory,
                             mContext,
                             getDefaultDisplayContentLocked(),
                             mPolicy.getWindowLayerFromTypeLw(
@@ -3536,7 +3547,7 @@
             try {
                 // TODO(multi-display): support multiple displays
                 if (mStrictModeFlash == null) {
-                    mStrictModeFlash = new StrictModeFlash(
+                    mStrictModeFlash = new StrictModeFlash(mSurfaceFactory,
                             getDefaultDisplayContentLocked());
                 }
                 mStrictModeFlash.setVisibility(on);
@@ -5553,7 +5564,8 @@
                 if (toks != null && toks.length > 0) {
                     // TODO(multi-display): Show watermarks on secondary displays.
                     final DisplayContent displayContent = getDefaultDisplayContentLocked();
-                    mWatermark = new Watermark(displayContent, displayContent.mRealDisplayMetrics,
+                    mWatermark = new Watermark(mSurfaceFactory, displayContent,
+                            displayContent.mRealDisplayMetrics,
                             toks);
                 }
             }
@@ -7544,7 +7556,7 @@
     }
 
     SurfaceControl.Builder makeSurfaceBuilder(SurfaceSession s) {
-        return mSurfaceBuilderFactory.make(s);
+        return mSurfaceControlFactory.apply(s);
     }
 
     /**
@@ -7616,7 +7628,8 @@
             mRoot.forAllDisplays(displayContent ->
                     displayContent.getInputMonitor().updateInputWindowsImmediately());
         }
-        new SurfaceControl.Transaction().syncInputWindows().apply(true);
+
+        mTransactionFactory.get().syncInputWindows().apply(true);
     }
 
     private void waitForAnimationsToComplete() {
@@ -7738,7 +7751,7 @@
         h.replaceTouchableRegionWithCrop(null);
 
         SurfaceSession s = new SurfaceSession();
-        SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+        SurfaceControl.Transaction t = mTransactionFactory.get();
         t.setInputWindowInfo(surface, h);
         t.apply();
 
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 4900869..cbb0b3a 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -217,7 +217,8 @@
 import java.util.function.Predicate;
 
 /** A window in the window manager. */
-class WindowState extends WindowContainer<WindowState> implements WindowManagerPolicy.WindowState {
+class WindowState extends WindowContainer<WindowState> implements WindowManagerPolicy.WindowState,
+        InsetsControlTarget {
     static final String TAG = TAG_WITH_CLASS_NAME ? "WindowState" : TAG_WM;
 
     // The minimal size of a window within the usable area of the freeform stack.
@@ -3329,7 +3330,8 @@
         }
     }
 
-    void notifyInsetsControlChanged() {
+    @Override
+    public void notifyInsetsControlChanged() {
         final InsetsStateController stateController =
                 getDisplayContent().getInsetsStateController();
         try {
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index ae3a10a..8e679d4 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -159,7 +159,7 @@
      * window is first added or shown, cleared when the callback has been made. */
     boolean mEnteringAnimation;
 
-    private final SurfaceControl.Transaction mTmpTransaction = new SurfaceControl.Transaction();
+    private final SurfaceControl.Transaction mTmpTransaction;
 
     /** The pixel format of the underlying SurfaceControl */
     int mSurfaceFormat;
@@ -246,6 +246,7 @@
         final WindowManagerService service = win.mWmService;
 
         mService = service;
+        mTmpTransaction = service.mTransactionFactory.get();
         mAnimator = service.mAnimator;
         mPolicy = service.mPolicy;
         mContext = service.mContext;
diff --git a/services/core/java/com/android/server/wm/WindowSurfaceController.java b/services/core/java/com/android/server/wm/WindowSurfaceController.java
index bcefa8f..8cede6c 100644
--- a/services/core/java/com/android/server/wm/WindowSurfaceController.java
+++ b/services/core/java/com/android/server/wm/WindowSurfaceController.java
@@ -81,7 +81,7 @@
     private final int mWindowType;
     private final Session mWindowSession;
 
-    private final SurfaceControl.Transaction mTmpTransaction = new SurfaceControl.Transaction();
+    private final SurfaceControl.Transaction mTmpTransaction;
 
     public WindowSurfaceController(SurfaceSession s, String name, int w, int h, int format,
             int flags, WindowStateAnimator animator, int windowType, int ownerUid) {
@@ -96,6 +96,7 @@
         final WindowState win = animator.mWin;
         mWindowType = windowType;
         mWindowSession = win.mSession;
+        mTmpTransaction = mService.mTransactionFactory.get();
 
         Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "new SurfaceControl");
         final SurfaceControl.Builder b = win.makeSurface()
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 6218498..7274d17 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -62,6 +62,17 @@
         "frameworks/native/services",
         "system/gatekeeper/include",
     ],
+
+    product_variables: {
+        arc: {
+            exclude_srcs: [
+                "com_android_server_AlarmManagerService.cpp",
+            ],
+            srcs: [
+                ":arctimersrcs",
+            ],
+        }
+    }
 }
 
 cc_defaults {
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 9670899..1685b04 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
@@ -1486,6 +1486,45 @@
                 SCHED_GROUP_DEFAULT);
     }
 
+    @SuppressWarnings("GuardedBy")
+    @Test
+    public void testUpdateOomAdj_DoAll_ServiceB() {
+        ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
+                MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
+        ProcessRecord app2 = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
+                MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
+        long now = SystemClock.uptimeMillis();
+        ServiceRecord s = bindService(app, app2, null, 0, mock(IBinder.class));
+        s.startRequested = true;
+        s.lastActivity = now;
+        s = bindService(app2, app, null, 0, mock(IBinder.class));
+        s.startRequested = true;
+        s.lastActivity = now;
+        ProcessRecord app3 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID,
+                MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
+        s = mock(ServiceRecord.class);
+        s.app = app3;
+        setFieldValue(ServiceRecord.class, s, "connections",
+                new ArrayMap<IBinder, ArrayList<ConnectionRecord>>());
+        app3.services.add(s);
+        doCallRealMethod().when(s).getConnections();
+        s.startRequested = true;
+        s.lastActivity = now;
+        ArrayList<ProcessRecord> lru = sService.mProcessList.mLruProcesses;
+        lru.clear();
+        lru.add(app3);
+        lru.add(app2);
+        lru.add(app);
+        sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
+        sService.mOomAdjuster.mNumServiceProcs = 3;
+        sService.mOomAdjuster.updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_NONE);
+        lru.clear();
+
+        assertEquals(SERVICE_B_ADJ, app3.setAdj);
+        assertEquals(SERVICE_ADJ, app2.setAdj);
+        assertEquals(SERVICE_ADJ, app.setAdj);
+    }
+
     private ProcessRecord makeDefaultProcessRecord(int pid, int uid, String processName,
             String packageName, boolean hasShownUi) {
         long now = SystemClock.uptimeMillis();
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java
index 67d6eda..7354ad4 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java
@@ -92,24 +92,18 @@
 
     public void testChangePasswordFailPrimaryUser() throws RemoteException {
         final long sid = 1234;
-        final String FAILED_MESSAGE = "Failed to enroll password";
         initializeStorageWithCredential(PRIMARY_USER_ID, "password", CREDENTIAL_TYPE_PASSWORD, sid);
 
-        try {
-            mService.setLockCredential("newpwd".getBytes(), CREDENTIAL_TYPE_PASSWORD,
-                    "badpwd".getBytes(), PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID, false);
-            fail("Did not fail when enrolling using incorrect credential");
-        } catch (IllegalStateException expected) {
-            assertTrue(expected.getMessage().equals(FAILED_MESSAGE));
-        }
+        assertFalse(mService.setLockCredential("newpwd".getBytes(), CREDENTIAL_TYPE_PASSWORD,
+                    "badpwd".getBytes(), PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID, false));
         assertVerifyCredentials(PRIMARY_USER_ID, "password", CREDENTIAL_TYPE_PASSWORD, sid);
     }
 
     public void testClearPasswordPrimaryUser() throws RemoteException {
         final String PASSWORD = "password";
         initializeStorageWithCredential(PRIMARY_USER_ID, PASSWORD, CREDENTIAL_TYPE_PASSWORD, 1234);
-        mService.setLockCredential(null, CREDENTIAL_TYPE_NONE, PASSWORD.getBytes(),
-                PASSWORD_QUALITY_UNSPECIFIED, PRIMARY_USER_ID, false);
+        assertTrue(mService.setLockCredential(null, CREDENTIAL_TYPE_NONE, PASSWORD.getBytes(),
+                PASSWORD_QUALITY_UNSPECIFIED, PRIMARY_USER_ID, false));
         assertFalse(mService.havePassword(PRIMARY_USER_ID));
         assertFalse(mService.havePattern(PRIMARY_USER_ID));
         assertEquals(0, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
@@ -118,9 +112,9 @@
     public void testManagedProfileUnifiedChallenge() throws RemoteException {
         final String firstUnifiedPassword = "testManagedProfileUnifiedChallenge-pwd-1";
         final String secondUnifiedPassword = "testManagedProfileUnifiedChallenge-pwd-2";
-        mService.setLockCredential(firstUnifiedPassword.getBytes(),
+        assertTrue(mService.setLockCredential(firstUnifiedPassword.getBytes(),
                 LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
-                null, PASSWORD_QUALITY_COMPLEX, PRIMARY_USER_ID, false);
+                null, PASSWORD_QUALITY_COMPLEX, PRIMARY_USER_ID, false));
         mService.setSeparateProfileChallengeEnabled(MANAGED_PROFILE_USER_ID, false, null);
         final long primarySid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID);
         final long profileSid = mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID);
@@ -154,17 +148,17 @@
          */
         mStorageManager.setIgnoreBadUnlock(true);
         // Change primary password and verify that profile SID remains
-        mService.setLockCredential(secondUnifiedPassword.getBytes(),
+        assertTrue(mService.setLockCredential(secondUnifiedPassword.getBytes(),
                 LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, firstUnifiedPassword.getBytes(),
-                PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID, false);
+                PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID, false));
         mStorageManager.setIgnoreBadUnlock(false);
         assertEquals(profileSid, mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID));
         assertNull(mGateKeeperService.getAuthToken(TURNED_OFF_PROFILE_USER_ID));
 
         // Clear unified challenge
-        mService.setLockCredential(null, LockPatternUtils.CREDENTIAL_TYPE_NONE,
+        assertTrue(mService.setLockCredential(null, LockPatternUtils.CREDENTIAL_TYPE_NONE,
                 secondUnifiedPassword.getBytes(), PASSWORD_QUALITY_UNSPECIFIED, PRIMARY_USER_ID,
-                false);
+                false));
         assertEquals(0, mGateKeeperService.getSecureUserId(PRIMARY_USER_ID));
         assertEquals(0, mGateKeeperService.getSecureUserId(MANAGED_PROFILE_USER_ID));
         assertEquals(0, mGateKeeperService.getSecureUserId(TURNED_OFF_PROFILE_USER_ID));
@@ -173,17 +167,17 @@
     public void testManagedProfileSeparateChallenge() throws RemoteException {
         final String primaryPassword = "testManagedProfileSeparateChallenge-primary";
         final String profilePassword = "testManagedProfileSeparateChallenge-profile";
-        mService.setLockCredential(primaryPassword.getBytes(),
+        assertTrue(mService.setLockCredential(primaryPassword.getBytes(),
                 LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
-                PASSWORD_QUALITY_COMPLEX, PRIMARY_USER_ID, false);
+                PASSWORD_QUALITY_COMPLEX, PRIMARY_USER_ID, false));
         /* Currently in LockSettingsService.setLockCredential, unlockUser() is called with the new
          * credential as part of verifyCredential() before the new credential is committed in
          * StorageManager. So we relax the check in our mock StorageManager to allow that.
          */
         mStorageManager.setIgnoreBadUnlock(true);
-        mService.setLockCredential(profilePassword.getBytes(),
+        assertTrue(mService.setLockCredential(profilePassword.getBytes(),
                 LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
-                PASSWORD_QUALITY_COMPLEX, MANAGED_PROFILE_USER_ID, false);
+                PASSWORD_QUALITY_COMPLEX, MANAGED_PROFILE_USER_ID, false));
         mStorageManager.setIgnoreBadUnlock(false);
 
         final long primarySid = mGateKeeperService.getSecureUserId(PRIMARY_USER_ID);
@@ -209,8 +203,9 @@
 
         // Change primary credential and make sure we don't affect profile
         mStorageManager.setIgnoreBadUnlock(true);
-        mService.setLockCredential("pwd".getBytes(), LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
-                primaryPassword.getBytes(), PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID, false);
+        assertTrue(mService.setLockCredential("pwd".getBytes(),
+                LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
+                primaryPassword.getBytes(), PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID, false));
         mStorageManager.setIgnoreBadUnlock(false);
         assertEquals(VerifyCredentialResponse.RESPONSE_OK, mService.verifyCredential(
                 profilePassword.getBytes(), LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, 0,
@@ -221,13 +216,13 @@
     public void testSetLockCredential_forPrimaryUser_sendsCredentials() throws Exception {
         final byte[] password = "password".getBytes();
 
-        mService.setLockCredential(
+        assertTrue(mService.setLockCredential(
                 password,
                 CREDENTIAL_TYPE_PASSWORD,
                 null,
                 PASSWORD_QUALITY_ALPHABETIC,
                 PRIMARY_USER_ID,
-                false);
+                false));
 
         verify(mRecoverableKeyStoreManager)
                 .lockScreenSecretChanged(CREDENTIAL_TYPE_PASSWORD, password, PRIMARY_USER_ID);
@@ -237,13 +232,13 @@
             throws Exception {
         final byte[] pattern = "12345".getBytes();
 
-        mService.setLockCredential(
+        assertTrue(mService.setLockCredential(
                 pattern,
                 CREDENTIAL_TYPE_PATTERN,
                 null,
                 PASSWORD_QUALITY_SOMETHING,
                 MANAGED_PROFILE_USER_ID,
-                false);
+                false));
 
         verify(mRecoverableKeyStoreManager)
                 .lockScreenSecretChanged(CREDENTIAL_TYPE_PATTERN, pattern, MANAGED_PROFILE_USER_ID);
@@ -259,13 +254,13 @@
                 CREDENTIAL_TYPE_PATTERN,
                 PASSWORD_QUALITY_SOMETHING);
 
-        mService.setLockCredential(
+        assertTrue(mService.setLockCredential(
                 newCredential,
                 CREDENTIAL_TYPE_PASSWORD,
                 oldCredential.getBytes(),
                 PASSWORD_QUALITY_ALPHABETIC,
                 MANAGED_PROFILE_USER_ID,
-                false);
+                false));
 
         verify(mRecoverableKeyStoreManager)
                 .lockScreenSecretChanged(
@@ -276,13 +271,13 @@
             throws Exception {
         mService.setSeparateProfileChallengeEnabled(MANAGED_PROFILE_USER_ID, false, null);
 
-        mService.setLockCredential(
+        assertTrue(mService.setLockCredential(
                 "12345".getBytes(),
                 CREDENTIAL_TYPE_PATTERN,
                 null,
                 PASSWORD_QUALITY_SOMETHING,
                 PRIMARY_USER_ID,
-                false);
+                false));
 
         verify(mRecoverableKeyStoreManager, never())
                 .lockScreenSecretChanged(
@@ -298,13 +293,13 @@
                 PRIMARY_USER_ID, oldCredential, CREDENTIAL_TYPE_PASSWORD, 1234);
         mService.setSeparateProfileChallengeEnabled(MANAGED_PROFILE_USER_ID, false, null);
 
-        mService.setLockCredential(
+        assertTrue(mService.setLockCredential(
                 newCredential,
                 CREDENTIAL_TYPE_PASSWORD,
                 oldCredential.getBytes(),
                 PASSWORD_QUALITY_ALPHABETIC,
                 PRIMARY_USER_ID,
-                false);
+                false));
 
         verify(mRecoverableKeyStoreManager)
                 .lockScreenSecretChanged(CREDENTIAL_TYPE_PASSWORD, newCredential, PRIMARY_USER_ID);
@@ -321,13 +316,13 @@
                 PRIMARY_USER_ID, oldCredential, CREDENTIAL_TYPE_PASSWORD, 1234);
         mService.setSeparateProfileChallengeEnabled(MANAGED_PROFILE_USER_ID, false, null);
 
-        mService.setLockCredential(
+        assertTrue(mService.setLockCredential(
                 null,
                 CREDENTIAL_TYPE_NONE,
                 oldCredential.getBytes(),
                 PASSWORD_QUALITY_UNSPECIFIED,
                 PRIMARY_USER_ID,
-                false);
+                false));
 
         verify(mRecoverableKeyStoreManager)
                 .lockScreenSecretChanged(CREDENTIAL_TYPE_NONE, null, PRIMARY_USER_ID);
@@ -343,13 +338,13 @@
                 PRIMARY_USER_ID, parentPassword, CREDENTIAL_TYPE_PASSWORD, 1234);
         mService.setSeparateProfileChallengeEnabled(MANAGED_PROFILE_USER_ID, false, null);
 
-        mService.setLockCredential(
+        assertTrue(mService.setLockCredential(
                 profilePassword,
                 CREDENTIAL_TYPE_PASSWORD,
                 null,
                 PASSWORD_QUALITY_ALPHABETIC,
                 MANAGED_PROFILE_USER_ID,
-                false);
+                false));
 
         verify(mRecoverableKeyStoreManager)
                 .lockScreenSecretChanged(
@@ -395,13 +390,13 @@
     public void testVerifyCredential_forProfileWithSeparateChallenge_sendsCredentials()
             throws Exception {
         final byte[] pattern = "12345".getBytes();
-        mService.setLockCredential(
+        assertTrue(mService.setLockCredential(
                 pattern,
                 CREDENTIAL_TYPE_PATTERN,
                 null,
                 PASSWORD_QUALITY_SOMETHING,
                 MANAGED_PROFILE_USER_ID,
-                false);
+                false));
         reset(mRecoverableKeyStoreManager);
 
         mService.verifyCredential(pattern, CREDENTIAL_TYPE_PATTERN, 1, MANAGED_PROFILE_USER_ID);
@@ -436,8 +431,8 @@
 
     private void testCreateCredential(int userId, String credential, int type, int quality)
             throws RemoteException {
-        mService.setLockCredential(credential.getBytes(), type, null, quality,
-                userId, false);
+        assertTrue(mService.setLockCredential(credential.getBytes(), type, null, quality,
+                userId, false));
         assertVerifyCredentials(userId, credential, type, -1);
     }
 
@@ -461,8 +456,8 @@
             String oldCredential, int oldType, int quality) throws RemoteException {
         final long sid = 1234;
         initializeStorageWithCredential(userId, oldCredential, oldType, sid);
-        mService.setLockCredential(newCredential.getBytes(), newType, oldCredential.getBytes(),
-                quality, userId, false);
+        assertTrue(mService.setLockCredential(newCredential.getBytes(), newType,
+                oldCredential.getBytes(), quality, userId, false));
         assertVerifyCredentials(userId, newCredential, newType, sid);
     }
 
diff --git a/services/tests/uiservicestests/AndroidManifest.xml b/services/tests/uiservicestests/AndroidManifest.xml
index 7453c48..180deb5 100644
--- a/services/tests/uiservicestests/AndroidManifest.xml
+++ b/services/tests/uiservicestests/AndroidManifest.xml
@@ -31,6 +31,7 @@
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
     <uses-permission android:name="android.permission.OBSERVE_ROLE_HOLDERS" />
     <uses-permission android:name="android.permission.GET_INTENT_SENDER_INTENT"/>
+    <uses-permission android:name="android.permission.WRITE_DEVICE_CONFIG" />
 
     <application android:debuggable="true">
         <uses-library android:name="android.test.runner" />
diff --git a/services/tests/uiservicestests/src/com/android/server/UiServiceTestCase.java b/services/tests/uiservicestests/src/com/android/server/UiServiceTestCase.java
index 14b71ec..3c2d550 100644
--- a/services/tests/uiservicestests/src/com/android/server/UiServiceTestCase.java
+++ b/services/tests/uiservicestests/src/com/android/server/UiServiceTestCase.java
@@ -27,9 +27,11 @@
 
 import com.android.server.uri.UriGrantsManagerInternal;
 
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Rule;
 import org.mockito.Mock;
+import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 
 public class UiServiceTestCase {
@@ -77,4 +79,9 @@
         when(mUgmInternal.checkGrantUriPermission(
                 anyInt(), anyString(), any(Uri.class), anyInt(), anyInt())).thenReturn(-1);
     }
+
+    @After
+    public final void cleanUpMockito() {
+        Mockito.framework().clearInlineMocks();
+    }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index be638a9..d11995a 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -62,6 +62,7 @@
 import static org.mockito.Matchers.eq;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.mock;
@@ -209,9 +210,10 @@
     private AudioManager mAudioManager;
     @Mock
     ActivityManager mActivityManager;
-    NotificationManagerService.WorkerHandler mHandler;
     @Mock
     Resources mResources;
+    @Mock
+    RankingHandler mRankingHandler;
 
     private NotificationChannel mTestNotificationChannel = new NotificationChannel(
             TEST_CHANNEL_ID, TEST_CHANNEL_ID, IMPORTANCE_DEFAULT);
@@ -342,7 +344,6 @@
 
         // Use this testable looper.
         mTestableLooper = TestableLooper.get(this);
-        mHandler = mService.new WorkerHandler(mTestableLooper.getLooper());
         // MockPackageManager - default returns ApplicationInfo with matching calling UID
         mContext.setMockPackageManager(mPackageManagerClient);
 
@@ -391,7 +392,7 @@
 
         when(mAssistants.isAdjustmentAllowed(anyString())).thenReturn(true);
 
-        mService.init(mTestableLooper.getLooper(),
+        mService.init(mTestableLooper.getLooper(), mRankingHandler,
                 mPackageManager, mPackageManagerClient, mockLightsManager,
                 mListeners, mAssistants, mConditionProviders,
                 mCompanionMgr, mSnoozeHelper, mUsageStats, mPolicyFile, mActivityManager,
@@ -410,6 +411,7 @@
                 PKG, new ParceledListSlice(Arrays.asList(mTestNotificationChannel)));
         assertNotNull(mBinderService.getNotificationChannel(
                 PKG, mContext.getUserId(), PKG, TEST_CHANNEL_ID));
+        clearInvocations(mRankingHandler);
     }
 
     @After
@@ -461,7 +463,8 @@
         Notification.Builder nb = new Notification.Builder(mContext, "a")
                 .setContentTitle("foo")
                 .setSmallIcon(android.R.drawable.sym_def_app_icon);
-        StatusBarNotification sbn = new StatusBarNotification(pkg, pkg, uid, "tag", uid, 0,
+        StatusBarNotification sbn = new StatusBarNotification(pkg, pkg, uid,
+                "tag" + System.currentTimeMillis(), uid, 0,
                 nb.build(), new UserHandle(userId), null, postTime);
         return sbn;
     }
@@ -482,7 +485,9 @@
         if (isBubble) {
             nb.setBubbleMetadata(getBasicBubbleMetadataBuilder().build());
         }
-        StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, id, "tag", mUid, 0,
+
+        StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, id,
+                "tag" + System.currentTimeMillis(), mUid, 0,
                 nb.build(), new UserHandle(mUid), null, 0);
         return new NotificationRecord(mContext, sbn, channel);
     }
@@ -510,7 +515,7 @@
         if (isBubble) {
             nb.setBubbleMetadata(getBasicBubbleMetadataBuilder().build());
         }
-        StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, "tag", mUid, 0,
+        StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0,
                 nb.build(), new UserHandle(mUid), null, 0);
         return new NotificationRecord(mContext, sbn, channel);
     }
@@ -588,7 +593,7 @@
         when(mActivityManager.getPackageImportance(nrBubble.sbn.getPackageName())).thenReturn(
                 IMPORTANCE_FOREGROUND);
 
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, nrBubble.sbn.getTag(),
                 nrBubble.sbn.getId(), nrBubble.sbn.getNotification(), nrBubble.sbn.getUserId());
         waitForIdle();
 
@@ -600,7 +605,7 @@
         // Plain notification without bubble metadata
         NotificationRecord nrPlain = generateNotificationRecord(mTestNotificationChannel, 2,
                 "BUBBLE_GROUP", false /* isSummary */, false /* isBubble */);
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, nrPlain.sbn.getTag(),
                 nrPlain.sbn.getId(), nrPlain.sbn.getNotification(), nrPlain.sbn.getUserId());
         waitForIdle();
 
@@ -613,7 +618,7 @@
         if (summaryAutoCancel) {
             nrSummary.getNotification().flags |= FLAG_AUTO_CANCEL;
         }
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, nrSummary.sbn.getTag(),
                 nrSummary.sbn.getId(), nrSummary.sbn.getNotification(), nrSummary.sbn.getUserId());
         waitForIdle();
 
@@ -757,7 +762,8 @@
         mBinderService.createNotificationChannels(
                 PKG, new ParceledListSlice(Arrays.asList(channel)));
         final StatusBarNotification sbn = generateNotificationRecord(channel).sbn;
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
+        mBinderService.enqueueNotificationWithTag(PKG, PKG,
+                "testBlockedNotifications_blockedChannel",
                 sbn.getId(), sbn.getNotification(), sbn.getUserId());
         waitForIdle();
         assertEquals(0, mBinderService.getActiveNotifications(sbn.getPackageName()).length);
@@ -775,7 +781,7 @@
 
         final StatusBarNotification sbn = generateNotificationRecord(channel).sbn;
         sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, sbn.getTag(),
                 sbn.getId(), sbn.getNotification(), sbn.getUserId());
         waitForIdle();
         assertEquals(1, mBinderService.getActiveNotifications(sbn.getPackageName()).length);
@@ -804,7 +810,7 @@
 
         StatusBarNotification sbn = generateNotificationRecord(channel).sbn;
         sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, sbn.getTag(),
                 sbn.getId(), sbn.getNotification(), sbn.getUserId());
         waitForIdle();
         // The first time a foreground service notification is shown, we allow the channel
@@ -826,7 +832,8 @@
 
         sbn = generateNotificationRecord(channel).sbn;
         sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
+        mBinderService.enqueueNotificationWithTag(PKG, PKG,
+                "testEnqueuedBlockedNotifications_userBlockedChannelForegroundService",
                 sbn.getId(), sbn.getNotification(), sbn.getUserId());
         waitForIdle();
         // The second time it is shown, we keep the user's preference.
@@ -840,7 +847,8 @@
     public void testBlockedNotifications_blockedChannelGroup() throws Exception {
         when(mPackageManager.isPackageSuspendedForUser(anyString(), anyInt())).thenReturn(false);
         mService.setPreferencesHelper(mPreferencesHelper);
-        when(mPreferencesHelper.isGroupBlocked(anyString(), anyInt(), anyString())).thenReturn(true);
+        when(mPreferencesHelper.isGroupBlocked(anyString(), anyInt(), anyString())).
+                thenReturn(true);
 
         NotificationChannel channel = new NotificationChannel("id", "name",
                 NotificationManager.IMPORTANCE_HIGH);
@@ -857,7 +865,8 @@
         mBinderService.setNotificationsEnabledForPackage(PKG, mUid, false);
 
         final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
+        mBinderService.enqueueNotificationWithTag(PKG, PKG,
+                "testEnqueuedBlockedNotifications_blockedApp",
                 sbn.getId(), sbn.getNotification(), sbn.getUserId());
         waitForIdle();
         assertEquals(0, mBinderService.getActiveNotifications(sbn.getPackageName()).length);
@@ -871,7 +880,8 @@
 
         final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
         sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
+        mBinderService.enqueueNotificationWithTag(PKG, PKG,
+                "testEnqueuedBlockedNotifications_blockedAppForegroundService",
                 sbn.getId(), sbn.getNotification(), sbn.getUserId());
         waitForIdle();
         assertEquals(0, mBinderService.getActiveNotifications(sbn.getPackageName()).length);
@@ -893,7 +903,8 @@
             final StatusBarNotification sbn =
                     generateNotificationRecord(mTestNotificationChannel, ++id, "", false).sbn;
             sbn.getNotification().category = category;
-            mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
+            mBinderService.enqueueNotificationWithTag(PKG, PKG,
+                    "testEnqueuedRestrictedNotifications_asSystem",
                     sbn.getId(), sbn.getNotification(), sbn.getUserId());
         }
         waitForIdle();
@@ -917,7 +928,8 @@
             final StatusBarNotification sbn =
                     generateNotificationRecord(mTestNotificationChannel, ++id, "", false).sbn;
             sbn.getNotification().category = category;
-            mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
+            mBinderService.enqueueNotificationWithTag(PKG, PKG,
+                    "testEnqueuedRestrictedNotifications_notAutomotive",
                     sbn.getId(), sbn.getNotification(), sbn.getUserId());
         }
         waitForIdle();
@@ -940,7 +952,8 @@
             final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
             sbn.getNotification().category = category;
             try {
-                mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
+                mBinderService.enqueueNotificationWithTag(PKG, PKG,
+                        "testEnqueuedRestrictedNotifications_badUser",
                         sbn.getId(), sbn.getNotification(), sbn.getUserId());
                 fail("Calls from non system apps should not allow use of restricted categories");
             } catch (SecurityException e) {
@@ -977,7 +990,8 @@
 
     @Test
     public void testEnqueueNotificationWithTag_PopulatesGetActiveNotifications() throws Exception {
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag", 0,
+        mBinderService.enqueueNotificationWithTag(PKG, PKG,
+                "testEnqueueNotificationWithTag_PopulatesGetActiveNotifications", 0,
                 generateNotificationRecord(null).getNotification(), 0);
         waitForIdle();
         StatusBarNotification[] notifs = mBinderService.getActiveNotifications(PKG);
@@ -987,9 +1001,11 @@
 
     @Test
     public void testCancelNotificationImmediatelyAfterEnqueue() throws Exception {
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag", 0,
+        mBinderService.enqueueNotificationWithTag(PKG, PKG,
+                "testCancelNotificationImmediatelyAfterEnqueue", 0,
                 generateNotificationRecord(null).getNotification(), 0);
-        mBinderService.cancelNotificationWithTag(PKG, PKG, "tag", 0, 0);
+        mBinderService.cancelNotificationWithTag(PKG, PKG,
+                "testCancelNotificationImmediatelyAfterEnqueue", 0, 0);
         waitForIdle();
         StatusBarNotification[] notifs =
                 mBinderService.getActiveNotifications(PKG);
@@ -999,12 +1015,15 @@
 
     @Test
     public void testCancelNotificationWhilePostedAndEnqueued() throws Exception {
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag", 0,
+        mBinderService.enqueueNotificationWithTag(PKG, PKG,
+                "testCancelNotificationWhilePostedAndEnqueued", 0,
                 generateNotificationRecord(null).getNotification(), 0);
         waitForIdle();
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag", 0,
+        mBinderService.enqueueNotificationWithTag(PKG, PKG,
+                "testCancelNotificationWhilePostedAndEnqueued", 0,
                 generateNotificationRecord(null).getNotification(), 0);
-        mBinderService.cancelNotificationWithTag(PKG, PKG, "tag", 0, 0);
+        mBinderService.cancelNotificationWithTag(PKG, PKG,
+                "testCancelNotificationWhilePostedAndEnqueued", 0, 0);
         waitForIdle();
         StatusBarNotification[] notifs =
                 mBinderService.getActiveNotifications(PKG);
@@ -1019,7 +1038,8 @@
     public void testCancelNotificationsFromListenerImmediatelyAfterEnqueue() throws Exception {
         NotificationRecord r = generateNotificationRecord(null);
         final StatusBarNotification sbn = r.sbn;
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
+        mBinderService.enqueueNotificationWithTag(PKG, PKG,
+                "testCancelNotificationsFromListenerImmediatelyAfterEnqueue",
                 sbn.getId(), sbn.getNotification(), sbn.getUserId());
         mBinderService.cancelNotificationsFromListener(null, null);
         waitForIdle();
@@ -1032,7 +1052,8 @@
     @Test
     public void testCancelAllNotificationsImmediatelyAfterEnqueue() throws Exception {
         final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
+        mBinderService.enqueueNotificationWithTag(PKG, PKG,
+                "testCancelAllNotificationsImmediatelyAfterEnqueue",
                 sbn.getId(), sbn.getNotification(), sbn.getUserId());
         mBinderService.cancelAllNotifications(PKG, sbn.getUserId());
         waitForIdle();
@@ -1047,7 +1068,8 @@
         final NotificationRecord n = generateNotificationRecord(
                 mTestNotificationChannel, 1, "group", true);
 
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
+        mBinderService.enqueueNotificationWithTag(PKG, PKG,
+                "testUserInitiatedClearAll_noLeak",
                 n.sbn.getId(), n.sbn.getNotification(), n.sbn.getUserId());
         waitForIdle();
 
@@ -1070,9 +1092,11 @@
         final NotificationRecord child = generateNotificationRecord(
                 mTestNotificationChannel, 2, "group1", false);
 
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
+        mBinderService.enqueueNotificationWithTag(PKG, PKG,
+                "testCancelAllNotificationsCancelsChildren",
                 parent.sbn.getId(), parent.sbn.getNotification(), parent.sbn.getUserId());
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
+        mBinderService.enqueueNotificationWithTag(PKG, PKG,
+                "testCancelAllNotificationsCancelsChildren",
                 child.sbn.getId(), child.sbn.getNotification(), child.sbn.getUserId());
         waitForIdle();
 
@@ -1085,7 +1109,8 @@
     public void testCancelAllNotificationsMultipleEnqueuedDoesNotCrash() throws Exception {
         final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
         for (int i = 0; i < 10; i++) {
-            mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
+            mBinderService.enqueueNotificationWithTag(PKG, PKG,
+                    "testCancelAllNotificationsMultipleEnqueuedDoesNotCrash",
                     sbn.getId(), sbn.getNotification(), sbn.getUserId());
         }
         mBinderService.cancelAllNotifications(PKG, sbn.getUserId());
@@ -1104,17 +1129,20 @@
                 mTestNotificationChannel, 2, "group1", false);
 
         // fully post parent notification
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
+        mBinderService.enqueueNotificationWithTag(PKG, PKG,
+                "testCancelGroupSummaryMultipleEnqueuedChildrenDoesNotCrash",
                 parent.sbn.getId(), parent.sbn.getNotification(), parent.sbn.getUserId());
         waitForIdle();
 
         // enqueue the child several times
         for (int i = 0; i < 10; i++) {
-            mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
+            mBinderService.enqueueNotificationWithTag(PKG, PKG,
+                    "testCancelGroupSummaryMultipleEnqueuedChildrenDoesNotCrash",
                     child.sbn.getId(), child.sbn.getNotification(), child.sbn.getUserId());
         }
         // make the parent a child, which will cancel the child notification
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
+        mBinderService.enqueueNotificationWithTag(PKG, PKG,
+                "testCancelGroupSummaryMultipleEnqueuedChildrenDoesNotCrash",
                 parentAsChild.sbn.getId(), parentAsChild.sbn.getNotification(),
                 parentAsChild.sbn.getUserId());
         waitForIdle();
@@ -1126,7 +1154,8 @@
     public void testCancelAllNotifications_IgnoreForegroundService() throws Exception {
         final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
         sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
+        mBinderService.enqueueNotificationWithTag(PKG, PKG,
+                "testCancelAllNotifications_IgnoreForegroundService",
                 sbn.getId(), sbn.getNotification(), sbn.getUserId());
         mBinderService.cancelAllNotifications(PKG, sbn.getUserId());
         waitForIdle();
@@ -1140,7 +1169,8 @@
     public void testCancelAllNotifications_IgnoreOtherPackages() throws Exception {
         final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
         sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
+        mBinderService.enqueueNotificationWithTag(PKG, PKG,
+                "testCancelAllNotifications_IgnoreOtherPackages",
                 sbn.getId(), sbn.getNotification(), sbn.getUserId());
         mBinderService.cancelAllNotifications("other_pkg_name", sbn.getUserId());
         waitForIdle();
@@ -1153,7 +1183,8 @@
     @Test
     public void testCancelAllNotifications_NullPkgRemovesAll() throws Exception {
         final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
+        mBinderService.enqueueNotificationWithTag(PKG, PKG,
+                "testCancelAllNotifications_NullPkgRemovesAll",
                 sbn.getId(), sbn.getNotification(), sbn.getUserId());
         mBinderService.cancelAllNotifications(null, sbn.getUserId());
         waitForIdle();
@@ -1166,7 +1197,8 @@
     @Test
     public void testCancelAllNotifications_NullPkgIgnoresUserAllNotifications() throws Exception {
         final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
+        mBinderService.enqueueNotificationWithTag(PKG, PKG,
+                "testCancelAllNotifications_NullPkgIgnoresUserAllNotifications",
                 sbn.getId(), sbn.getNotification(), UserHandle.USER_ALL);
         // Null pkg is how we signal a user switch.
         mBinderService.cancelAllNotifications(null, sbn.getUserId());
@@ -1181,7 +1213,8 @@
     public void testAppInitiatedCancelAllNotifications_CancelsNoClearFlag() throws Exception {
         final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
         sbn.getNotification().flags |= Notification.FLAG_NO_CLEAR;
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
+        mBinderService.enqueueNotificationWithTag(PKG, PKG,
+                "testAppInitiatedCancelAllNotifications_CancelsNoClearFlag",
                 sbn.getId(), sbn.getNotification(), sbn.getUserId());
         mBinderService.cancelAllNotifications(PKG, sbn.getUserId());
         waitForIdle();
@@ -1266,7 +1299,12 @@
 
     @Test
     public void testRemoveForegroundServiceFlag_ImmediatelyAfterEnqueue() throws Exception {
-        final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
+        Notification n =
+                new Notification.Builder(mContext, mTestNotificationChannel.getId())
+                        .setSmallIcon(android.R.drawable.sym_def_app_icon)
+                        .build();
+        StatusBarNotification sbn = new StatusBarNotification("a", "a", 0, null, mUid, 0,
+                n, new UserHandle(mUid), null, 0);
         sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
         mBinderService.enqueueNotificationWithTag(PKG, PKG, null,
                 sbn.getId(), sbn.getNotification(), sbn.getUserId());
@@ -1283,12 +1321,13 @@
         final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
         sbn.getNotification().flags =
                 Notification.FLAG_ONGOING_EVENT | FLAG_FOREGROUND_SERVICE;
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, sbn.getTag(),
                 sbn.getId(), sbn.getNotification(), sbn.getUserId());
         sbn.getNotification().flags = Notification.FLAG_ONGOING_EVENT;
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, sbn.getTag(),
                 sbn.getId(), sbn.getNotification(), sbn.getUserId());
-        mBinderService.cancelNotificationWithTag(PKG, PKG, "tag", sbn.getId(), sbn.getUserId());
+        mBinderService.cancelNotificationWithTag(PKG, PKG, sbn.getTag(), sbn.getId(),
+                sbn.getUserId());
         waitForIdle();
         assertEquals(0, mBinderService.getActiveNotifications(sbn.getPackageName()).length);
         assertEquals(0, mService.getNotificationRecordCount());
@@ -1376,21 +1415,22 @@
         // should not be returned
         final NotificationRecord group2 = generateNotificationRecord(
                 mTestNotificationChannel, 2, "group2", true);
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, null,
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, "testFindGroupNotificationsLocked",
                 group2.sbn.getId(), group2.sbn.getNotification(), group2.sbn.getUserId());
         waitForIdle();
 
         // should not be returned
         final NotificationRecord nonGroup = generateNotificationRecord(
                 mTestNotificationChannel, 3, null, false);
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, null,
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, "testFindGroupNotificationsLocked",
                 nonGroup.sbn.getId(), nonGroup.sbn.getNotification(), nonGroup.sbn.getUserId());
         waitForIdle();
 
         // same group, child, should be returned
         final NotificationRecord group1Child = generateNotificationRecord(
                 mTestNotificationChannel, 4, "group1", false);
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, null, group1Child.sbn.getId(),
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, "testFindGroupNotificationsLocked",
+                group1Child.sbn.getId(),
                 group1Child.sbn.getNotification(), group1Child.sbn.getUserId());
         waitForIdle();
 
@@ -1447,7 +1487,8 @@
     public void testAppInitiatedCancelAllNotifications_CancelsOnGoingFlag() throws Exception {
         final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
         sbn.getNotification().flags |= Notification.FLAG_ONGOING_EVENT;
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
+        mBinderService.enqueueNotificationWithTag(PKG, PKG,
+                "testAppInitiatedCancelAllNotifications_CancelsOnGoingFlag",
                 sbn.getId(), sbn.getNotification(), sbn.getUserId());
         mBinderService.cancelAllNotifications(PKG, sbn.getUserId());
         waitForIdle();
@@ -1564,7 +1605,7 @@
                         new NotificationChannel("foo", "foo", IMPORTANCE_HIGH));
 
         Notification.TvExtender tv = new Notification.TvExtender().setChannelId("foo");
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag", 0,
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, "testTvExtenderChannelOverride_onTv", 0,
                 generateNotificationRecord(null, tv).getNotification(), 0);
         verify(mPreferencesHelper, times(1)).getNotificationChannel(
                 anyString(), anyInt(), eq("foo"), anyBoolean());
@@ -1579,8 +1620,8 @@
                 mTestNotificationChannel);
 
         Notification.TvExtender tv = new Notification.TvExtender().setChannelId("foo");
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag", 0,
-                generateNotificationRecord(null, tv).getNotification(), 0);
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, "testTvExtenderChannelOverride_notOnTv",
+                0, generateNotificationRecord(null, tv).getNotification(), 0);
         verify(mPreferencesHelper, times(1)).getNotificationChannel(
                 anyString(), anyInt(), eq(mTestNotificationChannel.getId()), anyBoolean());
     }
@@ -2220,7 +2261,7 @@
         final NotificationRecord child = generateNotificationRecord(
                 mTestNotificationChannel, 2, "group", false);
 
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, null,
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, "testPostNonGroup_noUnsnoozing",
                 child.sbn.getId(), child.sbn.getNotification(), child.sbn.getUserId());
         waitForIdle();
 
@@ -2233,7 +2274,7 @@
         final NotificationRecord record = generateNotificationRecord(
                 mTestNotificationChannel, 2, null, false);
 
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, null,
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, "testPostNonGroup_noUnsnoozing",
                 record.sbn.getId(), record.sbn.getNotification(), record.sbn.getUserId());
         waitForIdle();
 
@@ -2245,7 +2286,7 @@
         final NotificationRecord parent = generateNotificationRecord(
                 mTestNotificationChannel, 2, "group", true);
 
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, null,
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, "testPostGroupSummary_noUnsnoozing",
                 parent.sbn.getId(), parent.sbn.getNotification(), parent.sbn.getUserId());
         waitForIdle();
 
@@ -2659,31 +2700,37 @@
                 .setColorized(true)
                 .setFlag(Notification.FLAG_CAN_COLORIZE, true)
                 .setSmallIcon(android.R.drawable.sym_def_app_icon);
-        StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, "tag", mUid, 0,
+        StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1,
+                "testNoFakeColorizedPermission", mUid, 0,
                 nb.build(), new UserHandle(mUid), null, 0);
         NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
 
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, null,
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, sbn.getTag(),
                 nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
         waitForIdle();
 
         NotificationRecord posted = mService.findNotificationLocked(
-                PKG, null, nr.sbn.getId(), nr.sbn.getUserId());
+                PKG, nr.sbn.getTag(), nr.sbn.getId(), nr.sbn.getUserId());
 
         assertFalse(posted.getNotification().isColorized());
     }
 
     @Test
-    public void testGetNotificationCountLocked() throws Exception {
+    public void testGetNotificationCountLocked() {
+        String sampleTagToExclude = null;
+        int sampleIdToExclude = 0;
         for (int i = 0; i < 20; i++) {
             NotificationRecord r =
                     generateNotificationRecord(mTestNotificationChannel, i, null, false);
             mService.addEnqueuedNotification(r);
+
         }
         for (int i = 0; i < 20; i++) {
             NotificationRecord r =
                     generateNotificationRecord(mTestNotificationChannel, i, null, false);
             mService.addNotification(r);
+            sampleTagToExclude = r.sbn.getTag();
+            sampleIdToExclude = i;
         }
 
         // another package
@@ -2700,55 +2747,49 @@
         mService.addNotification(otherPackage);
 
         // Same notifications are enqueued as posted, everything counts b/c id and tag don't match
+        // anything that's currently enqueued or posted
         int userId = new UserHandle(mUid).getIdentifier();
         assertEquals(40,
                 mService.getNotificationCountLocked(PKG, userId, 0, null));
         assertEquals(40,
                 mService.getNotificationCountLocked(PKG, userId, 0, "tag2"));
+
+        // return all for package "a" - "banana" tag isn't used
         assertEquals(2,
                 mService.getNotificationCountLocked("a", userId, 0, "banana"));
 
         // exclude a known notification - it's excluded from only the posted list, not enqueued
-        assertEquals(39,
-                mService.getNotificationCountLocked(PKG, userId, 0, "tag"));
+        assertEquals(39, mService.getNotificationCountLocked(
+                PKG, userId, sampleIdToExclude, sampleTagToExclude));
     }
 
     @Test
     public void testAddAutogroup_requestsSort() throws Exception {
-        RankingHandler rh = mock(RankingHandler.class);
-        mService.setRankingHandler(rh);
-
         final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
         mService.addNotification(r);
         mService.addAutogroupKeyLocked(r.getKey());
 
-        verify(rh, times(1)).requestSort();
+        verify(mRankingHandler, times(1)).requestSort();
     }
 
     @Test
     public void testRemoveAutogroup_requestsSort() throws Exception {
-        RankingHandler rh = mock(RankingHandler.class);
-        mService.setRankingHandler(rh);
-
         final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
         r.setOverrideGroupKey("TEST");
         mService.addNotification(r);
         mService.removeAutogroupKeyLocked(r.getKey());
 
-        verify(rh, times(1)).requestSort();
+        verify(mRankingHandler, times(1)).requestSort();
     }
 
     @Test
     public void testReaddAutogroup_noSort() throws Exception {
-        RankingHandler rh = mock(RankingHandler.class);
-        mService.setRankingHandler(rh);
-
         final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
         r.setOverrideGroupKey("TEST");
         mService.addNotification(r);
         mService.addAutogroupKeyLocked(r.getKey());
 
-        verify(rh, never()).requestSort();
+        verify(mRankingHandler, never()).requestSort();
     }
 
     @Test
@@ -2922,7 +2963,8 @@
                 .setFlag(FLAG_FOREGROUND_SERVICE, true)
                 .setPriority(Notification.PRIORITY_MIN);
 
-        StatusBarNotification sbn = new StatusBarNotification(preOPkg, preOPkg, 9, "tag",
+        StatusBarNotification sbn = new StatusBarNotification(preOPkg, preOPkg, 9,
+                "testBumpFGImportance_noChannelChangePreOApp",
                 Binder.getCallingUid(), 0, nb.build(), new UserHandle(Binder.getCallingUid()), null, 0);
 
         mBinderService.enqueueNotificationWithTag(sbn.getPackageName(), sbn.getOpPkg(),
@@ -2938,10 +2980,12 @@
                 .setFlag(FLAG_FOREGROUND_SERVICE, true)
                 .setPriority(Notification.PRIORITY_MIN);
 
-        sbn = new StatusBarNotification(preOPkg, preOPkg, 9, "tag", Binder.getCallingUid(),
+        sbn = new StatusBarNotification(preOPkg, preOPkg, 9,
+                "testBumpFGImportance_noChannelChangePreOApp", Binder.getCallingUid(),
                 0, nb.build(), new UserHandle(Binder.getCallingUid()), null, 0);
 
-        mBinderService.enqueueNotificationWithTag(preOPkg, preOPkg, "tag",
+        mBinderService.enqueueNotificationWithTag(preOPkg, preOPkg,
+                "testBumpFGImportance_noChannelChangePreOApp",
                 sbn.getId(), sbn.getNotification(), sbn.getUserId());
         waitForIdle();
         assertEquals(IMPORTANCE_LOW,
@@ -3154,9 +3198,6 @@
     public void testUserSentimentChangeTriggersUpdate() throws Exception {
         final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
         mService.addNotification(r);
-        NotificationManagerService.WorkerHandler handler = mock(
-                NotificationManagerService.WorkerHandler.class);
-        mService.setHandler(handler);
         when(mAssistants.isSameUser(eq(null), anyInt())).thenReturn(true);
 
         Bundle signals = new Bundle();
@@ -3168,16 +3209,13 @@
 
         waitForIdle();
 
-        verify(handler, timeout(300).times(1)).scheduleSendRankingUpdate();
+        verify(mRankingHandler, timeout(300).times(1)).requestSort();
     }
 
     @Test
     public void testTooLateAdjustmentTriggersUpdate() throws Exception {
         final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
         mService.addNotification(r);
-        NotificationManagerService.WorkerHandler handler = mock(
-                NotificationManagerService.WorkerHandler.class);
-        mService.setHandler(handler);
         when(mAssistants.isSameUser(eq(null), anyInt())).thenReturn(true);
 
         Bundle signals = new Bundle();
@@ -3189,16 +3227,13 @@
 
         waitForIdle();
 
-        verify(handler, timeout(300).times(1)).scheduleSendRankingUpdate();
+        verify(mRankingHandler, times(1)).requestSort();
     }
 
     @Test
     public void testEnqueuedAdjustmentAppliesAdjustments() throws Exception {
         final NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
         mService.addEnqueuedNotification(r);
-        NotificationManagerService.WorkerHandler handler = mock(
-                NotificationManagerService.WorkerHandler.class);
-        mService.setHandler(handler);
         when(mAssistants.isSameUser(eq(null), anyInt())).thenReturn(true);
 
         Bundle signals = new Bundle();
@@ -3208,8 +3243,7 @@
                 r.sbn.getPackageName(), r.getKey(), signals, "", r.getUser().getIdentifier());
         mBinderService.applyEnqueuedAdjustmentFromAssistant(null, adjustment);
 
-        assertEquals(USER_SENTIMENT_NEGATIVE,
-                r.getUserSentiment());
+        assertEquals(USER_SENTIMENT_NEGATIVE, r.getUserSentiment());
     }
 
     @Test
@@ -4099,7 +4133,8 @@
 
         final StatusBarNotification sbn = generateNotificationRecord(null).sbn;
         try {
-            mInternalService.enqueueNotification(notReal, "android", 0, 0, "tag",
+            mInternalService.enqueueNotification(notReal, "android", 0, 0,
+                    "testPostFromAndroidForNonExistentPackage",
                     sbn.getId(), sbn.getNotification(), sbn.getUserId());
             fail("can't post notifications for nonexistent packages, even if you exist");
         } catch (SecurityException e) {
@@ -4373,7 +4408,8 @@
         NotificationRecord r = generateNotificationRecord(mTestNotificationChannel);
         mService.addNotification(r);
 
-        StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, "tag", mUid, 0,
+        StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, r.sbn.getId(),
+                r.sbn.getTag(), mUid, 0,
                 new Notification.Builder(mContext, mTestNotificationChannel.getId()).build(),
                 new UserHandle(mUid), null, 0);
         NotificationRecord update = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
@@ -4502,7 +4538,7 @@
         when(mActivityManager.getPackageImportance(nr.sbn.getPackageName())).thenReturn(
                 IMPORTANCE_FOREGROUND);
 
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.sbn.getTag(),
                 nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
         waitForIdle();
 
@@ -4526,7 +4562,7 @@
         when(mActivityManager.getPackageImportance(nr.sbn.getPackageName())).thenReturn(
                 IMPORTANCE_FOREGROUND);
 
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.sbn.getTag(),
                 nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
         waitForIdle();
 
@@ -4550,8 +4586,8 @@
         when(mActivityManager.getPackageImportance(nr.sbn.getPackageName())).thenReturn(
                 IMPORTANCE_FOREGROUND);
 
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
-                nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
+        mBinderService.enqueueNotificationWithTag(PKG, PKG,
+                nr.sbn.getTag(), nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
         waitForIdle();
 
         // yes allowed, yes foreground, yes bubble
@@ -4572,7 +4608,7 @@
         when(mActivityManager.getPackageImportance(nr.sbn.getPackageName())).thenReturn(
                 IMPORTANCE_VISIBLE);
 
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.sbn.getTag(),
                 nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
         waitForIdle();
 
@@ -4593,7 +4629,7 @@
         // Send notif when we're foreground
         when(mActivityManager.getPackageImportance(nr1.sbn.getPackageName())).thenReturn(
                 IMPORTANCE_FOREGROUND);
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, nr1.sbn.getTag(),
                 nr1.sbn.getId(), nr1.sbn.getNotification(), nr1.sbn.getUserId());
         waitForIdle();
 
@@ -4607,7 +4643,7 @@
 
         when(mActivityManager.getPackageImportance(nr2.sbn.getPackageName())).thenReturn(
                 IMPORTANCE_VISIBLE);
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, nr2.sbn.getTag(),
                 nr2.sbn.getId(), nr2.sbn.getNotification(), nr2.sbn.getUserId());
         waitForIdle();
 
@@ -4633,7 +4669,7 @@
         // Send notif when we're foreground
         when(mActivityManager.getPackageImportance(nr1.sbn.getPackageName())).thenReturn(
                 IMPORTANCE_FOREGROUND);
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, nr1.sbn.getTag(),
                 nr1.sbn.getId(), nr1.sbn.getNotification(), nr1.sbn.getUserId());
         waitForIdle();
 
@@ -4642,7 +4678,7 @@
                 nr1.sbn.getKey()).getNotification().isBubbleNotification());
 
         // Remove the bubble
-        mBinderService.cancelNotificationWithTag(PKG, PKG, "tag", nr1.sbn.getId(),
+        mBinderService.cancelNotificationWithTag(PKG, PKG, nr1.sbn.getTag(), nr1.sbn.getId(),
                 nr1.sbn.getUserId());
         waitForIdle();
 
@@ -4656,7 +4692,7 @@
 
         when(mActivityManager.getPackageImportance(nr2.sbn.getPackageName())).thenReturn(
                 IMPORTANCE_VISIBLE);
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, nr2.sbn.getTag(),
                 nr2.sbn.getId(), nr2.sbn.getNotification(), nr2.sbn.getUserId());
         waitForIdle();
 
@@ -4702,11 +4738,12 @@
                 .setActions(replyAction)
                 .setSmallIcon(android.R.drawable.sym_def_app_icon);
 
-        StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, null, mUid, 0,
+        StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1,
+                "testFlagBubbleNotifs_flag_messaging", mUid, 0,
                 nb.build(), new UserHandle(mUid), null, 0);
         NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
 
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, null,
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.sbn.getTag(),
                 nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
         waitForIdle();
 
@@ -4735,13 +4772,14 @@
                 .setBubbleMetadata(data)
                 .setSmallIcon(android.R.drawable.sym_def_app_icon);
 
-        StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, null, mUid, 0,
+        StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1,
+                "testFlagBubbleNotifs_flag_phonecall", mUid, 0,
                 nb.build(), new UserHandle(mUid), null, 0);
         // Make sure it has foreground service
         sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
         NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
 
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, null,
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.sbn.getTag(),
                 nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
         waitForIdle();
 
@@ -4774,7 +4812,7 @@
                 nb.build(), new UserHandle(mUid), null, 0);
         NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
 
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, null,
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.sbn.getTag(),
                 nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
         waitForIdle();
 
@@ -4798,13 +4836,14 @@
                 .setBubbleMetadata(data)
                 .setSmallIcon(android.R.drawable.sym_def_app_icon);
 
-        StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, null, mUid, 0,
+        StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1,
+                "testFlagBubbleNotifs_noFlag_phonecall_noPerson", mUid, 0,
                 nb.build(), new UserHandle(mUid), null, 0);
         // Make sure it has foreground service
         sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
         NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
 
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, null,
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.sbn.getTag(),
                 nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
         waitForIdle();
 
@@ -4832,13 +4871,14 @@
                 .setBubbleMetadata(data)
                 .setSmallIcon(android.R.drawable.sym_def_app_icon);
 
-        StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, null, mUid, 0,
+        StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1,
+                "testFlagBubbleNotifs_noFlag_phonecall_noCategory", mUid, 0,
                 nb.build(), new UserHandle(mUid), null, 0);
         // Make sure it has foreground service
         sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
         NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
 
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, null,
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.sbn.getTag(),
                 nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
         waitForIdle();
 
@@ -4872,12 +4912,13 @@
                 )
                 .setSmallIcon(android.R.drawable.sym_def_app_icon);
 
-        StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, null, mUid, 0,
+        StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1,
+                "testFlagBubbleNotifs_noFlag_messaging_appNotAllowed", mUid, 0,
                 nb.build(), new UserHandle(mUid), null, 0);
         NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
 
         // Post the notification
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, null,
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.sbn.getTag(),
                 nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
         waitForIdle();
 
@@ -4895,7 +4936,7 @@
         NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel);
 
         // Post the notification
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.sbn.getTag(),
                 nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
         waitForIdle();
 
@@ -4929,12 +4970,13 @@
                 )
                 .setSmallIcon(android.R.drawable.sym_def_app_icon);
 
-        StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, null, mUid, 0,
+        StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1,
+                "testFlagBubbleNotifs_noFlag_messaging_channelNotAllowed", mUid, 0,
                 nb.build(), new UserHandle(mUid), null, 0);
         NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
 
         // Post the notification
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, null,
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, sbn.getTag(),
                 nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
         waitForIdle();
 
@@ -4963,13 +5005,14 @@
                 .setBubbleMetadata(data)
                 .setSmallIcon(android.R.drawable.sym_def_app_icon);
 
-        StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, null, mUid, 0,
+        StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1,
+                "testFlagBubbleNotifs_noFlag_phonecall_notAllowed", mUid, 0,
                 nb.build(), new UserHandle(mUid), null, 0);
         // Make sure it has foreground service
         sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
         NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
 
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, null,
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, sbn.getTag(),
                 nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
         waitForIdle();
 
@@ -4998,13 +5041,14 @@
                 .setBubbleMetadata(data)
                 .setSmallIcon(android.R.drawable.sym_def_app_icon);
 
-        StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, null, mUid, 0,
+        StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1,
+                "testFlagBubbleNotifs_noFlag_phonecall_channelNotAllowed", mUid, 0,
                 nb.build(), new UserHandle(mUid), null, 0);
         // Make sure it has foreground service
         sbn.getNotification().flags |= FLAG_FOREGROUND_SERVICE;
         NotificationRecord nr = new NotificationRecord(mContext, sbn, mTestNotificationChannel);
 
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, null,
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, sbn.getTag(),
                 nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
         waitForIdle();
 
@@ -5033,7 +5077,8 @@
         nrBubble.sbn.getNotification().flags |= FLAG_BUBBLE;
 
         // Post the notification
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, null,
+        mBinderService.enqueueNotificationWithTag(PKG, PKG,
+                "testAppCancelNotifications_cancelsBubbles",
                 nrBubble.sbn.getId(), nrBubble.sbn.getNotification(), nrBubble.sbn.getUserId());
         waitForIdle();
 
@@ -5041,7 +5086,8 @@
         assertEquals(1, notifs.length);
         assertEquals(1, mService.getNotificationRecordCount());
 
-        mBinderService.cancelNotificationWithTag(PKG, PKG, null, nrBubble.sbn.getId(),
+        mBinderService.cancelNotificationWithTag(PKG, PKG,
+                "testAppCancelNotifications_cancelsBubbles", nrBubble.sbn.getId(),
                 nrBubble.sbn.getUserId());
         waitForIdle();
 
@@ -5196,7 +5242,7 @@
         when(mActivityManager.getPackageImportance(nr.sbn.getPackageName())).thenReturn(
                 IMPORTANCE_FOREGROUND);
 
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.sbn.getTag(),
                 nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
         waitForIdle();
 
@@ -5226,7 +5272,7 @@
         // Plain notification that has bubble metadata
         NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel,
                 null /* tvExtender */, true /* isBubble */);
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.sbn.getTag(),
                 nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
         waitForIdle();
 
@@ -5260,7 +5306,7 @@
         // Notif that is not a bubble
         NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel,
                 null /* tvExtender */, true /* isBubble */);
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.sbn.getTag(),
                 nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
         waitForIdle();
 
@@ -5290,7 +5336,7 @@
         // Plain notification that has bubble metadata
         NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel,
                 null /* tvExtender */, true /* isBubble */);
-        mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, nr.sbn.getTag(),
                 nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
         waitForIdle();
 
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java b/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java
index f37ff11..7f9f489 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java
@@ -129,7 +129,7 @@
         mRoleObserver = mService.new RoleObserver(mRoleManager, mPm, mExecutor);
 
         try {
-            mService.init(mock(Looper.class),
+            mService.init(mock(Looper.class), mock(RankingHandler.class),
                     mock(IPackageManager.class), mock(PackageManager.class),
                     mock(LightsManager.class),
                     mock(NotificationListeners.class), mock(NotificationAssistants.class),
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 d2af18e..34cc0c7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -147,8 +147,8 @@
         assertThat((Object) task2.getStack()).isInstanceOf(ActivityStack.class);
         mStarter.updateBounds(task2, bounds);
 
-        verify(mService, times(1)).resizeStack(eq(task2.getStack().mStackId),
-                eq(bounds), anyBoolean(), anyBoolean(), anyBoolean(), anyInt());
+        verify(mService, times(1)).animateResizePinnedStack(eq(task2.getStack().mStackId),
+                eq(bounds), anyInt());
 
         // In the case of no animation, the stack and task bounds should be set immediately.
         if (!ANIMATE) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
new file mode 100644
index 0000000..f3a8e1a
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsPolicyTest.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_STATUS_BAR_VISIBLE_TRANSPARENT;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
+import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import android.platform.test.annotations.Presubmit;
+import android.view.InsetsSourceControl;
+import android.view.test.InsetsModeSession;
+
+import androidx.test.filters.FlakyTest;
+import androidx.test.filters.SmallTest;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@FlakyTest(detail = "Promote to pre-submit once confirmed stable.")
+@Presubmit
+@RunWith(WindowTestRunner.class)
+public class InsetsPolicyTest extends WindowTestsBase {
+    private static InsetsModeSession sInsetsModeSession;
+
+    @BeforeClass
+    public static void setUpOnce() {
+        // To let the insets provider control the insets visibility, the insets mode has to be
+        // NEW_INSETS_MODE_FULL.
+        sInsetsModeSession = new InsetsModeSession(NEW_INSETS_MODE_FULL);
+    }
+
+    @AfterClass
+    public static void tearDownOnce() {
+        sInsetsModeSession.close();
+    }
+
+    @Test
+    public void testControlsForDispatch_regular() {
+        addWindow(TYPE_STATUS_BAR, "topBar");
+        addWindow(TYPE_NAVIGATION_BAR, "navBar");
+
+        final InsetsSourceControl[] controls = addAppWindowAndGetControlsForDispatch();
+
+        // The app can control both system bars.
+        assertNotNull(controls);
+        assertEquals(2, controls.length);
+    }
+
+    @Test
+    public void testControlsForDispatch_dockedStackVisible() {
+        addWindow(TYPE_STATUS_BAR, "topBar");
+        addWindow(TYPE_NAVIGATION_BAR, "navBar");
+
+        final WindowState win = createWindowOnStack(null, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY,
+                ACTIVITY_TYPE_STANDARD, TYPE_APPLICATION, mDisplayContent, "app");
+        final InsetsSourceControl[] controls = addWindowAndGetControlsForDispatch(win);
+
+        // The app must not control any system bars.
+        assertNull(controls);
+    }
+
+    @Test
+    public void testControlsForDispatch_freeformStackVisible() {
+        addWindow(TYPE_STATUS_BAR, "topBar");
+        addWindow(TYPE_NAVIGATION_BAR, "navBar");
+
+        final WindowState win = createWindowOnStack(null, WINDOWING_MODE_FREEFORM,
+                ACTIVITY_TYPE_STANDARD, TYPE_APPLICATION, mDisplayContent, "app");
+        final InsetsSourceControl[] controls = addWindowAndGetControlsForDispatch(win);
+
+        // The app must not control any bars.
+        assertNull(controls);
+    }
+
+    @Test
+    public void testControlsForDispatch_dockedDividerControllerResizing() {
+        addWindow(TYPE_STATUS_BAR, "topBar");
+        addWindow(TYPE_NAVIGATION_BAR, "navBar");
+        mDisplayContent.getDockedDividerController().setResizing(true);
+
+        final InsetsSourceControl[] controls = addAppWindowAndGetControlsForDispatch();
+
+        // The app must not control any system bars.
+        assertNull(controls);
+    }
+
+    @Test
+    public void testControlsForDispatch_keyguard() {
+        addWindow(TYPE_STATUS_BAR, "topBar").mAttrs.privateFlags |= PRIVATE_FLAG_KEYGUARD;
+        addWindow(TYPE_NAVIGATION_BAR, "navBar");
+
+        final InsetsSourceControl[] controls = addAppWindowAndGetControlsForDispatch();
+
+        // The app must not control the top bar.
+        assertNotNull(controls);
+        assertEquals(1, controls.length);
+    }
+
+    // TODO: adjust this test if we pretend to the app that it's still able to control it.
+    @Test
+    public void testControlsForDispatch_forceStatusBarVisibleTransparent() {
+        addWindow(TYPE_STATUS_BAR, "topBar").mAttrs.privateFlags |=
+                PRIVATE_FLAG_FORCE_STATUS_BAR_VISIBLE_TRANSPARENT;
+        addWindow(TYPE_NAVIGATION_BAR, "navBar");
+
+        final InsetsSourceControl[] controls = addAppWindowAndGetControlsForDispatch();
+
+        // The app must not control the top bar.
+        assertNotNull(controls);
+        assertEquals(1, controls.length);
+    }
+
+    @Test
+    public void testControlsForDispatch_statusBarForceShowNavigation() {
+        addWindow(TYPE_STATUS_BAR, "topBar").mAttrs.privateFlags |=
+                PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION;
+        addWindow(TYPE_NAVIGATION_BAR, "navBar");
+
+        final InsetsSourceControl[] controls = addAppWindowAndGetControlsForDispatch();
+
+        // The app must not control the navigation bar.
+        assertNotNull(controls);
+        assertEquals(1, controls.length);
+    }
+
+    private WindowState addWindow(int type, String name) {
+        final WindowState win = createWindow(null, type, name);
+        mDisplayContent.getDisplayPolicy().addWindowLw(win, win.mAttrs);
+        return win;
+    }
+
+    private InsetsSourceControl[] addAppWindowAndGetControlsForDispatch() {
+        return addWindowAndGetControlsForDispatch(addWindow(TYPE_APPLICATION, "app"));
+    }
+
+    private InsetsSourceControl[] addWindowAndGetControlsForDispatch(WindowState win) {
+        mDisplayContent.getInsetsPolicy().updateBarControlTarget(win);
+        return mDisplayContent.getInsetsStateController().getControlsForDispatch(win);
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
index 9fce78b..81ea32b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
@@ -23,13 +23,15 @@
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 
 import android.platform.test.annotations.Presubmit;
+import android.view.InsetsSource;
 import android.view.InsetsSourceControl;
 import android.view.InsetsState;
-import android.view.ViewRootImpl;
+import android.view.test.InsetsModeSession;
 
 import androidx.test.filters.FlakyTest;
 import androidx.test.filters.SmallTest;
@@ -37,90 +39,91 @@
 import org.junit.AfterClass;
 import org.junit.BeforeClass;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 
 @SmallTest
+@FlakyTest(detail = "Promote to pre-submit once confirmed stable.")
 @Presubmit
+@RunWith(WindowTestRunner.class)
 public class InsetsStateControllerTest extends WindowTestsBase {
-    private static int sPreviousNewInsetsMode;
+    private static InsetsModeSession sInsetsModeSession;
 
     @BeforeClass
     public static void setUpOnce() {
-        // TODO: Make use of SettingsSession when it becomes feasible for this.
-        sPreviousNewInsetsMode = ViewRootImpl.sNewInsetsMode;
         // To let the insets provider control the insets visibility, the insets mode has to be
         // NEW_INSETS_MODE_FULL.
-        ViewRootImpl.sNewInsetsMode = NEW_INSETS_MODE_FULL;
+        sInsetsModeSession = new InsetsModeSession(NEW_INSETS_MODE_FULL);
     }
 
     @AfterClass
     public static void tearDownOnce() {
-        ViewRootImpl.sNewInsetsMode = sPreviousNewInsetsMode;
+        sInsetsModeSession.close();
     }
 
     @Test
     @FlakyTest(bugId = 131005232)
     public void testStripForDispatch_notOwn() {
-        final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow");
-        final WindowState app = createWindow(null, TYPE_APPLICATION, "parentWindow");
+        final WindowState topBar = createWindow(null, TYPE_APPLICATION, "topBar");
+        final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
         getController().getSourceProvider(TYPE_TOP_BAR).setWindow(topBar, null);
         topBar.setInsetProvider(getController().getSourceProvider(TYPE_TOP_BAR));
         assertNotNull(getController().getInsetsForDispatch(app).getSource(TYPE_TOP_BAR));
     }
 
-    @FlakyTest(detail = "Promote to pre-submit once confirmed stable.")
     @Test
     public void testStripForDispatch_own() {
-        final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow");
+        final WindowState topBar = createWindow(null, TYPE_APPLICATION, "topBar");
         mDisplayContent.getInsetsStateController().getSourceProvider(TYPE_TOP_BAR)
                 .setWindow(topBar, null);
         topBar.setInsetProvider(getController().getSourceProvider(TYPE_TOP_BAR));
-        assertEquals(new InsetsState(), getController().getInsetsForDispatch(topBar));
+        final InsetsState state = getController().getInsetsForDispatch(topBar);
+        for (int i = state.getSourcesCount() - 1; i >= 0; i--) {
+            final InsetsSource source = state.sourceAt(i);
+            assertNotEquals(TYPE_TOP_BAR, source.getType());
+        }
     }
 
-    @FlakyTest(detail = "Promote to pre-submit once confirmed stable.")
     @Test
     public void testStripForDispatch_navBar() {
-        final WindowState navBar = createWindow(null, TYPE_APPLICATION, "parentWindow");
-        final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow");
-        final WindowState ime = createWindow(null, TYPE_APPLICATION, "parentWindow");
+        final WindowState navBar = createWindow(null, TYPE_APPLICATION, "navBar");
+        final WindowState topBar = createWindow(null, TYPE_APPLICATION, "topBar");
+        final WindowState ime = createWindow(null, TYPE_APPLICATION, "ime");
         getController().getSourceProvider(TYPE_TOP_BAR).setWindow(topBar, null);
         getController().getSourceProvider(TYPE_NAVIGATION_BAR).setWindow(navBar, null);
         getController().getSourceProvider(TYPE_IME).setWindow(ime, null);
-        assertEquals(new InsetsState(), getController().getInsetsForDispatch(navBar));
+        assertEquals(0, getController().getInsetsForDispatch(navBar).getSourcesCount());
     }
 
-    @FlakyTest(detail = "Promote to pre-submit once confirmed stable.")
     @Test
     public void testBarControllingWinChanged() {
-        final WindowState navBar = createWindow(null, TYPE_APPLICATION, "parentWindow");
-        final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow");
-        final WindowState app = createWindow(null, TYPE_APPLICATION, "parentWindow");
+        final WindowState navBar = createWindow(null, TYPE_APPLICATION, "navBar");
+        final WindowState topBar = createWindow(null, TYPE_APPLICATION, "topBar");
+        final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
         getController().getSourceProvider(TYPE_TOP_BAR).setWindow(topBar, null);
         getController().getSourceProvider(TYPE_NAVIGATION_BAR).setWindow(navBar, null);
-        getController().onBarControllingWindowChanged(app);
+        getController().onBarControlTargetChanged(app, app);
         InsetsSourceControl[] controls = getController().getControlsForDispatch(app);
         assertEquals(2, controls.length);
     }
 
-    @FlakyTest(detail = "Promote to pre-submit once confirmed stable.")
     @Test
     public void testControlRevoked() {
-        final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow");
-        final WindowState app = createWindow(null, TYPE_APPLICATION, "parentWindow");
+        final WindowState topBar = createWindow(null, TYPE_APPLICATION, "topBar");
+        final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
         getController().getSourceProvider(TYPE_TOP_BAR).setWindow(topBar, null);
-        getController().onBarControllingWindowChanged(app);
+        getController().onBarControlTargetChanged(app, null);
         assertNotNull(getController().getControlsForDispatch(app));
-        getController().onBarControllingWindowChanged(null);
+        getController().onBarControlTargetChanged(null, null);
         assertNull(getController().getControlsForDispatch(app));
     }
 
     @FlakyTest(bugId = 124088319)
     @Test
     public void testControlRevoked_animation() {
-        final WindowState topBar = createWindow(null, TYPE_APPLICATION, "parentWindow");
-        final WindowState app = createWindow(null, TYPE_APPLICATION, "parentWindow");
+        final WindowState topBar = createWindow(null, TYPE_APPLICATION, "topBar");
+        final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
         getController().getSourceProvider(TYPE_TOP_BAR).setWindow(topBar, null);
-        getController().onBarControllingWindowChanged(app);
+        getController().onBarControlTargetChanged(app, null);
         assertNotNull(getController().getControlsForDispatch(app));
         topBar.cancelAnimation();
         assertNull(getController().getControlsForDispatch(app));
diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
index c52c8d7..2d0416d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
@@ -48,7 +48,7 @@
     @Before
     public void setUp() throws Exception {
         mSurfaces = new SurfaceControlMocker();
-        mLetterbox = new Letterbox(mSurfaces);
+        mLetterbox = new Letterbox(mSurfaces, () -> mock(SurfaceControl.Transaction.class));
         mTransaction = mock(SurfaceControl.Transaction.class);
     }
 
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 fb4e330..55011fb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -1031,7 +1031,7 @@
         assertSecurityException(expectCallable,
                 () -> mService.moveTopActivityToPinnedStack(INVALID_STACK_ID, new Rect()));
         assertSecurityException(expectCallable,
-                () -> mService.resizeStack(INVALID_STACK_ID, new Rect(), true, true, true, 0));
+                () -> mService.animateResizePinnedStack(INVALID_STACK_ID, new Rect(), -1));
         assertSecurityException(expectCallable,
                 () -> mService.resizeDockedStack(new Rect(), new Rect(), new Rect(), new Rect(),
                         new Rect()));
@@ -1289,10 +1289,10 @@
         @Override
         void getTasks(int maxNum, List<RunningTaskInfo> list, int ignoreActivityType,
                 int ignoreWindowingMode, ArrayList<ActivityDisplay> activityDisplays,
-                int callingUid, boolean allowed) {
+                int callingUid, boolean allowed, boolean crossUser) {
             mLastAllowed = allowed;
             super.getTasks(maxNum, list, ignoreActivityType, ignoreWindowingMode, activityDisplays,
-                    callingUid, allowed);
+                    callingUid, allowed, crossUser);
         }
     }
 }
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 dc96480..cdd4c24 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java
@@ -77,7 +77,7 @@
         final int numFetchTasks = 5;
         ArrayList<RunningTaskInfo> tasks = new ArrayList<>();
         mRunningTasks.getTasks(5, tasks, ACTIVITY_TYPE_UNDEFINED, WINDOWING_MODE_UNDEFINED,
-                displays, -1 /* callingUid */, true /* allowed */);
+                displays, -1 /* callingUid */, true /* allowed */, true /*crossUser */);
         assertThat(tasks).hasSize(numFetchTasks);
         for (int i = 0; i < numFetchTasks; i++) {
             assertEquals(numTasks - i - 1, tasks.get(i).id);
@@ -87,7 +87,7 @@
         // and does not crash
         tasks.clear();
         mRunningTasks.getTasks(100, tasks, ACTIVITY_TYPE_UNDEFINED, WINDOWING_MODE_UNDEFINED,
-                displays, -1 /* callingUid */, true /* allowed */);
+                displays, -1 /* callingUid */, true /* allowed */, true /* crossUser */);
         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/SystemServicesTestRule.java b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
index 21ed285..43c6b35 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SystemServicesTestRule.java
@@ -257,17 +257,14 @@
         // Suppress StrictMode violation (DisplayWindowSettings) to avoid log flood.
         DisplayThread.getHandler().post(StrictMode::allowThreadDiskWritesMask);
         mWmService = WindowManagerService.main(
-                mContext, mImService, false, false, mWMPolicy, mAtmService, StubTransaction::new);
+                mContext, mImService, false, false, mWMPolicy, mAtmService, StubTransaction::new,
+                () -> mock(Surface.class), (unused) -> new MockSurfaceControlBuilder());
         spyOn(mWmService);
 
         // Setup factory classes to prevent calls to native code.
         mTransaction = spy(StubTransaction.class);
         // Return a spied Transaction class than can be used to verify calls.
         mWmService.mTransactionFactory = () -> mTransaction;
-        // Return a SurfaceControl.Builder class that creates mocked SurfaceControl instances.
-        mWmService.mSurfaceBuilderFactory = (unused) -> new MockSurfaceControlBuilder();
-        // Return mocked Surface instances.
-        mWmService.mSurfaceFactory = () -> mock(Surface.class);
         mWmService.mSurfaceAnimationRunner = new SurfaceAnimationRunner(
                 null, null, mTransaction, mWmService.mPowerManagerInternal);
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index d5e8440..f41f126 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -403,7 +403,7 @@
         assertTrue(topBar.isVisible());
         mDisplayContent.getInsetsStateController().getSourceProvider(TYPE_TOP_BAR)
                 .setWindow(topBar, null);
-        mDisplayContent.getInsetsStateController().onBarControllingWindowChanged(app);
+        mDisplayContent.getInsetsStateController().onBarControlTargetChanged(app, app);
         mDisplayContent.getInsetsStateController().getSourceProvider(TYPE_TOP_BAR)
                 .onInsetsModified(app, new InsetsSource(TYPE_TOP_BAR));
         waitUntilHandlersIdle();
diff --git a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
index 7d7c3985..2105ab0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ZOrderingTests.java
@@ -51,6 +51,7 @@
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.LinkedList;
+import java.util.function.Function;
 
 /**
  * Tests for the {@link DisplayContent#assignChildLayers(SurfaceControl.Transaction)} method.
@@ -131,7 +132,8 @@
         }
     }
 
-    private static class HierarchyRecordingBuilderFactory implements SurfaceBuilderFactory {
+    private static class HierarchyRecordingBuilderFactory implements Function<SurfaceSession,
+                SurfaceControl.Builder> {
         private LayerRecordingTransaction mTransaction;
 
         HierarchyRecordingBuilderFactory(LayerRecordingTransaction transaction) {
@@ -139,7 +141,7 @@
         }
 
         @Override
-        public SurfaceControl.Builder make(SurfaceSession s) {
+        public SurfaceControl.Builder apply(SurfaceSession s) {
             final LayerRecordingTransaction transaction = mTransaction;
             return new HierarchyRecorder(s, transaction);
         }
@@ -153,7 +155,7 @@
         // which is after construction of the DisplayContent, meaning the HierarchyRecorder
         // would miss construction of the top-level layers.
         mTransaction = new LayerRecordingTransaction();
-        mWm.mSurfaceBuilderFactory = new HierarchyRecordingBuilderFactory(mTransaction);
+        mWm.mSurfaceControlFactory = new HierarchyRecordingBuilderFactory(mTransaction);
         mWm.mTransactionFactory = () -> mTransaction;
     }
 
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index fc464a1..090623e 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -121,7 +121,7 @@
     private static final long TEN_SECONDS = 10 * 1000;
     private static final long TWENTY_MINUTES = 20 * 60 * 1000;
     private static final long FLUSH_INTERVAL = COMPRESS_TIME ? TEN_SECONDS : TWENTY_MINUTES;
-    private static final long TIME_CHANGE_THRESHOLD_MILLIS = 2 * 1000; // Two seconds.
+    static final long TIME_CHANGE_THRESHOLD_MILLIS = 2 * 1000; // Two seconds.
 
     private static final boolean ENABLE_KERNEL_UPDATES = true;
     private static final File KERNEL_COUNTER_FILE = new File("/proc/uid_procstat/set");
@@ -156,8 +156,6 @@
     private final SparseArray<UserUsageStatsService> mUserState = new SparseArray<>();
     private final SparseBooleanArray mUserUnlockedStates = new SparseBooleanArray();
     private final SparseIntArray mUidToKernelCounter = new SparseIntArray();
-    long mRealTimeSnapshot;
-    long mSystemTimeSnapshot;
     int mUsageSource;
 
     /** Manages the standby state of apps. */
@@ -253,9 +251,6 @@
         getContext().registerReceiverAsUser(new UserActionsReceiver(), UserHandle.ALL, filter,
                 null, mHandler);
 
-        mRealTimeSnapshot = SystemClock.elapsedRealtime();
-        mSystemTimeSnapshot = System.currentTimeMillis();
-
         publishLocalService(UsageStatsManagerInternal.class, new LocalService());
         publishBinderService(Context.USAGE_STATS_SERVICE, new BinderService());
     }
@@ -345,7 +340,7 @@
             mUserUnlockedStates.put(userId, true);
             final UserUsageStatsService userService = getUserDataAndInitializeIfNeededLocked(
                     userId, System.currentTimeMillis());
-            userService.userUnlocked(checkAndGetTimeLocked());
+            userService.userUnlocked(System.currentTimeMillis());
             // Process all the pending reported events
             while (pendingEvents.peek() != null) {
                 reportEvent(pendingEvents.poll(), userId);
@@ -569,37 +564,6 @@
     }
 
     /**
-     * This should be the only way to get the time from the system.
-     */
-    private long checkAndGetTimeLocked() {
-        final long actualSystemTime = System.currentTimeMillis();
-        final long actualRealtime = SystemClock.elapsedRealtime();
-        final long expectedSystemTime = (actualRealtime - mRealTimeSnapshot) + mSystemTimeSnapshot;
-        final long diffSystemTime = actualSystemTime - expectedSystemTime;
-        if (Math.abs(diffSystemTime) > TIME_CHANGE_THRESHOLD_MILLIS
-                && ENABLE_TIME_CHANGE_CORRECTION) {
-            // The time has changed.
-            Slog.i(TAG, "Time changed in UsageStats by " + (diffSystemTime / 1000) + " seconds");
-            final int userCount = mUserState.size();
-            for (int i = 0; i < userCount; i++) {
-                final UserUsageStatsService service = mUserState.valueAt(i);
-                service.onTimeChanged(expectedSystemTime, actualSystemTime);
-            }
-            mRealTimeSnapshot = actualRealtime;
-            mSystemTimeSnapshot = actualSystemTime;
-        }
-        return actualSystemTime;
-    }
-
-    /**
-     * Assuming the event's timestamp is measured in milliseconds since boot,
-     * convert it to a system wall time.
-     */
-    private void convertToSystemTimeLocked(Event event) {
-        event.mTimeStamp = Math.max(0, event.mTimeStamp - mRealTimeSnapshot) + mSystemTimeSnapshot;
-    }
-
-    /**
      * Called by the Binder stub
      */
     void shutdown() {
@@ -718,9 +682,8 @@
                 return;
             }
 
-            final long timeNow = checkAndGetTimeLocked();
+            final long timeNow = System.currentTimeMillis();
             final long elapsedRealtime = SystemClock.elapsedRealtime();
-            convertToSystemTimeLocked(event);
 
             if (event.mPackage != null
                     && mPackageManagerInternal.isPackageEphemeral(userId, event.mPackage)) {
@@ -876,13 +839,8 @@
                 return null;
             }
 
-            final long timeNow = checkAndGetTimeLocked();
-            if (!validRange(timeNow, beginTime, endTime)) {
-                return null;
-            }
-
             final UserUsageStatsService service =
-                    getUserDataAndInitializeIfNeededLocked(userId, timeNow);
+                    getUserDataAndInitializeIfNeededLocked(userId, System.currentTimeMillis());
             List<UsageStats> list = service.queryUsageStats(bucketType, beginTime, endTime);
             if (list == null) {
                 return null;
@@ -913,13 +871,8 @@
                 return null;
             }
 
-            final long timeNow = checkAndGetTimeLocked();
-            if (!validRange(timeNow, beginTime, endTime)) {
-                return null;
-            }
-
             final UserUsageStatsService service =
-                    getUserDataAndInitializeIfNeededLocked(userId, timeNow);
+                    getUserDataAndInitializeIfNeededLocked(userId, System.currentTimeMillis());
             return service.queryConfigurationStats(bucketType, beginTime, endTime);
         }
     }
@@ -935,13 +888,8 @@
                 return null;
             }
 
-            final long timeNow = checkAndGetTimeLocked();
-            if (!validRange(timeNow, beginTime, endTime)) {
-                return null;
-            }
-
             final UserUsageStatsService service =
-                    getUserDataAndInitializeIfNeededLocked(userId, timeNow);
+                    getUserDataAndInitializeIfNeededLocked(userId, System.currentTimeMillis());
             return service.queryEventStats(bucketType, beginTime, endTime);
         }
     }
@@ -957,13 +905,8 @@
                 return null;
             }
 
-            final long timeNow = checkAndGetTimeLocked();
-            if (!validRange(timeNow, beginTime, endTime)) {
-                return null;
-            }
-
             final UserUsageStatsService service =
-                    getUserDataAndInitializeIfNeededLocked(userId, timeNow);
+                    getUserDataAndInitializeIfNeededLocked(userId, System.currentTimeMillis());
             return service.queryEvents(beginTime, endTime, shouldObfuscateInstantApps);
         }
     }
@@ -979,21 +922,12 @@
                 return null;
             }
 
-            final long timeNow = checkAndGetTimeLocked();
-            if (!validRange(timeNow, beginTime, endTime)) {
-                return null;
-            }
-
             final UserUsageStatsService service =
-                    getUserDataAndInitializeIfNeededLocked(userId, timeNow);
+                    getUserDataAndInitializeIfNeededLocked(userId, System.currentTimeMillis());
             return service.queryEventsForPackage(beginTime, endTime, packageName, includeTaskRoot);
         }
     }
 
-    private static boolean validRange(long currentTime, long beginTime, long endTime) {
-        return beginTime <= currentTime && beginTime < endTime;
-    }
-
     private String buildFullToken(String packageName, String token) {
         final StringBuilder sb = new StringBuilder(packageName.length() + token.length() + 1);
         sb.append(packageName);
@@ -2050,8 +1984,8 @@
 
                 // Check to ensure that only user 0's data is b/r for now
                 if (user == UserHandle.USER_SYSTEM) {
-                    final UserUsageStatsService userStats =
-                            getUserDataAndInitializeIfNeededLocked(user, checkAndGetTimeLocked());
+                    final UserUsageStatsService userStats = getUserDataAndInitializeIfNeededLocked(
+                            user, System.currentTimeMillis());
                     return userStats.getBackupPayload(key);
                 } else {
                     return null;
@@ -2068,8 +2002,8 @@
                 }
 
                 if (user == UserHandle.USER_SYSTEM) {
-                    final UserUsageStatsService userStats =
-                            getUserDataAndInitializeIfNeededLocked(user, checkAndGetTimeLocked());
+                    final UserUsageStatsService userStats = getUserDataAndInitializeIfNeededLocked(
+                            user, System.currentTimeMillis());
                     userStats.applyRestoredPayload(key, payload);
                 }
             }
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index 6fbd882..1560b9e 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -76,6 +76,8 @@
     private final String mLogPrefix;
     private String mLastBackgroundedPackage;
     private final int mUserId;
+    private long mRealTimeSnapshot;
+    private long mSystemTimeSnapshot;
 
     private static final long[] INTERVAL_LENGTH = new long[] {
             UnixCalendar.DAY_IN_MILLIS, UnixCalendar.WEEK_IN_MILLIS,
@@ -101,6 +103,8 @@
         mListener = listener;
         mLogPrefix = "User[" + Integer.toString(userId) + "] ";
         mUserId = userId;
+        mRealTimeSnapshot = SystemClock.elapsedRealtime();
+        mSystemTimeSnapshot = System.currentTimeMillis();
     }
 
     void init(final long currentTimeMillis) {
@@ -165,12 +169,41 @@
         persistActiveStats();
     }
 
-    void onTimeChanged(long oldTime, long newTime) {
+    private void onTimeChanged(long oldTime, long newTime) {
         persistActiveStats();
         mDatabase.onTimeChanged(newTime - oldTime);
         loadActiveStats(newTime);
     }
 
+    /**
+     * This should be the only way to get the time from the system.
+     */
+    private long checkAndGetTimeLocked() {
+        final long actualSystemTime = System.currentTimeMillis();
+        if (!UsageStatsService.ENABLE_TIME_CHANGE_CORRECTION) {
+            return actualSystemTime;
+        }
+        final long actualRealtime = SystemClock.elapsedRealtime();
+        final long expectedSystemTime = (actualRealtime - mRealTimeSnapshot) + mSystemTimeSnapshot;
+        final long diffSystemTime = actualSystemTime - expectedSystemTime;
+        if (Math.abs(diffSystemTime) > UsageStatsService.TIME_CHANGE_THRESHOLD_MILLIS) {
+            // The time has changed.
+            Slog.i(TAG, mLogPrefix + "Time changed in by " + (diffSystemTime / 1000) + " seconds");
+            onTimeChanged(expectedSystemTime, actualSystemTime);
+            mRealTimeSnapshot = actualRealtime;
+            mSystemTimeSnapshot = actualSystemTime;
+        }
+        return actualSystemTime;
+    }
+
+    /**
+     * Assuming the event's timestamp is measured in milliseconds since boot,
+     * convert it to a system wall time.
+     */
+    private void convertToSystemTimeLocked(Event event) {
+        event.mTimeStamp = Math.max(0, event.mTimeStamp - mRealTimeSnapshot) + mSystemTimeSnapshot;
+    }
+
     void reportEvent(Event event) {
         if (DEBUG) {
             Slog.d(TAG, mLogPrefix + "Got usage event for " + event.mPackage
@@ -178,6 +211,9 @@
                     + eventToString(event.mEventType));
         }
 
+        checkAndGetTimeLocked();
+        convertToSystemTimeLocked(event);
+
         if (event.mTimeStamp >= mDailyExpiryDate.getTimeInMillis()) {
             // Need to rollover
             rolloverStats(event.mTimeStamp);
@@ -298,6 +334,10 @@
                 }
             };
 
+    private static boolean validRange(long currentTime, long beginTime, long endTime) {
+        return beginTime <= currentTime && beginTime < endTime;
+    }
+
     /**
      * Generic query method that selects the appropriate IntervalStats for the specified time range
      * and bucket, then calls the {@link com.android.server.usage.UsageStatsDatabase.StatCombiner}
@@ -370,19 +410,31 @@
     }
 
     List<UsageStats> queryUsageStats(int bucketType, long beginTime, long endTime) {
+        if (!validRange(checkAndGetTimeLocked(), beginTime, endTime)) {
+            return null;
+        }
         return queryStats(bucketType, beginTime, endTime, sUsageStatsCombiner);
     }
 
     List<ConfigurationStats> queryConfigurationStats(int bucketType, long beginTime, long endTime) {
+        if (!validRange(checkAndGetTimeLocked(), beginTime, endTime)) {
+            return null;
+        }
         return queryStats(bucketType, beginTime, endTime, sConfigStatsCombiner);
     }
 
     List<EventStats> queryEventStats(int bucketType, long beginTime, long endTime) {
+        if (!validRange(checkAndGetTimeLocked(), beginTime, endTime)) {
+            return null;
+        }
         return queryStats(bucketType, beginTime, endTime, sEventStatsCombiner);
     }
 
     UsageEvents queryEvents(final long beginTime, final long endTime,
                             boolean obfuscateInstantApps) {
+        if (!validRange(checkAndGetTimeLocked(), beginTime, endTime)) {
+            return null;
+        }
         final ArraySet<String> names = new ArraySet<>();
         List<Event> results = queryStats(INTERVAL_DAILY,
                 beginTime, endTime, new StatCombiner<Event>() {
@@ -428,6 +480,9 @@
 
     UsageEvents queryEventsForPackage(final long beginTime, final long endTime,
             final String packageName, boolean includeTaskRoot) {
+        if (!validRange(checkAndGetTimeLocked(), beginTime, endTime)) {
+            return null;
+        }
         final ArraySet<String> names = new ArraySet<>();
         names.add(packageName);
         final List<Event> results = queryStats(INTERVAL_DAILY,
@@ -1030,10 +1085,12 @@
     }
 
     byte[] getBackupPayload(String key){
+        checkAndGetTimeLocked();
         return mDatabase.getBackupPayload(key);
     }
 
     void applyRestoredPayload(String key, byte[] payload){
+        checkAndGetTimeLocked();
         mDatabase.applyRestoredPayload(key, payload);
     }
 }
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbConfigDescriptor.java b/services/usb/java/com/android/server/usb/descriptors/UsbConfigDescriptor.java
index de2dd10..3f2d8c8 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbConfigDescriptor.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbConfigDescriptor.java
@@ -41,6 +41,8 @@
                                  //     D4..0 Reserved, set to 0.
     private int mMaxPower;       // 8:1 Maximum Power Consumption in 2mA units
 
+    private boolean mBlockAudio; // leave it off for now. We be replace with a "Developer Option"
+
     private ArrayList<UsbInterfaceDescriptor> mInterfaceDescriptors =
             new ArrayList<UsbInterfaceDescriptor>();
 
@@ -77,21 +79,35 @@
         mInterfaceDescriptors.add(interfaceDesc);
     }
 
+    private boolean isAudioInterface(UsbInterfaceDescriptor descriptor) {
+        return descriptor.getUsbClass() == UsbDescriptor.CLASSID_AUDIO
+                && descriptor.getUsbSubclass() == UsbDescriptor.AUDIO_AUDIOSTREAMING;
+    }
+
     UsbConfiguration toAndroid(UsbDescriptorParser parser) {
         if (UsbDescriptorParser.DEBUG) {
             Log.d(TAG, "  toAndroid()");
         }
+
+        // NOTE - This code running in the server process.
+        //TODO (pmclean@) - remove this
+//        int pid = android.os.Process.myPid();
+//        int uid = android.os.Process.myUid();
+//        Log.d(TAG, "  ---- pid:" + pid + " uid:" + uid);
+
         String name = parser.getDescriptorString(mConfigIndex);
         UsbConfiguration config = new
                 UsbConfiguration(mConfigValue, name, mAttribs, mMaxPower);
-        UsbInterface[] interfaces = new UsbInterface[mInterfaceDescriptors.size()];
-        if (UsbDescriptorParser.DEBUG) {
-            Log.d(TAG, "    " + mInterfaceDescriptors.size() + " interfaces.");
+
+        ArrayList<UsbInterface> filteredInterfaces = new ArrayList<UsbInterface>();
+        for (UsbInterfaceDescriptor descriptor : mInterfaceDescriptors) {
+            if (!mBlockAudio || !isAudioInterface(descriptor)) {
+                filteredInterfaces.add(descriptor.toAndroid(parser));
+            }
         }
-        for (int index = 0; index < mInterfaceDescriptors.size(); index++) {
-            interfaces[index] = mInterfaceDescriptors.get(index).toAndroid(parser);
-        }
-        config.setInterfaces(interfaces);
+        UsbInterface[] interfaceArray = new UsbInterface[0];
+        interfaceArray = filteredInterfaces.toArray(interfaceArray);
+        config.setInterfaces(interfaceArray);
         return config;
     }
 
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
index 4f62d5a..6ffbd43 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbDescriptorParser.java
@@ -26,7 +26,7 @@
  */
 public final class UsbDescriptorParser {
     private static final String TAG = "UsbDescriptorParser";
-    public static final boolean DEBUG = false;
+    public static final boolean DEBUG = true;
 
     private final String mDeviceAddr;
 
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbEndpointDescriptor.java b/services/usb/java/com/android/server/usb/descriptors/UsbEndpointDescriptor.java
index 5eb0a2f..4d0cfea 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbEndpointDescriptor.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbEndpointDescriptor.java
@@ -31,7 +31,7 @@
     public static final int MASK_ENDPOINT_ADDRESS = 0b000000000001111;
     public static final int MASK_ENDPOINT_DIRECTION = (byte) 0b0000000010000000;
     public static final int DIRECTION_OUTPUT = 0x0000;
-    public static final int DIRECTION_INPUT = (byte) 0x0080;
+    public static final int DIRECTION_INPUT = 0x0080;
 
     public static final int MASK_ATTRIBS_TRANSTYPE = 0b00000011;
     public static final int TRANSTYPE_CONTROL = 0x00;
@@ -85,7 +85,7 @@
     }
 
     public int getEndpointAddress() {
-        return mEndpointAddress;
+        return mEndpointAddress & MASK_ENDPOINT_ADDRESS;
     }
 
     public int getAttributes() {
@@ -108,6 +108,10 @@
         return mSyncAddress;
     }
 
+    public int getDirection() {
+        return mEndpointAddress & UsbEndpointDescriptor.MASK_ENDPOINT_DIRECTION;
+    }
+
     /* package */ UsbEndpoint toAndroid(UsbDescriptorParser parser) {
         if (UsbDescriptorParser.DEBUG) {
             Log.d(TAG, "toAndroid() type:"
@@ -137,11 +141,9 @@
 
         canvas.openList();
 
-        int address = getEndpointAddress();
         canvas.writeListItem("Address: "
-                + ReportCanvas.getHexString(address & UsbEndpointDescriptor.MASK_ENDPOINT_ADDRESS)
-                + ((address & UsbEndpointDescriptor.MASK_ENDPOINT_DIRECTION)
-                == UsbEndpointDescriptor.DIRECTION_OUTPUT ? " [out]" : " [in]"));
+                + ReportCanvas.getHexString(getEndpointAddress())
+                + (getDirection() == UsbEndpointDescriptor.DIRECTION_OUTPUT ? " [out]" : " [in]"));
 
         int attributes = getAttributes();
         canvas.openListItem();
diff --git a/services/usb/java/com/android/server/usb/descriptors/UsbInterfaceDescriptor.java b/services/usb/java/com/android/server/usb/descriptors/UsbInterfaceDescriptor.java
index 1dc6069..64dbd97 100644
--- a/services/usb/java/com/android/server/usb/descriptors/UsbInterfaceDescriptor.java
+++ b/services/usb/java/com/android/server/usb/descriptors/UsbInterfaceDescriptor.java
@@ -31,7 +31,6 @@
  */
 public class UsbInterfaceDescriptor extends UsbDescriptor {
     private static final String TAG = "UsbInterfaceDescriptor";
-
     protected int mInterfaceNumber;   // 2:1 Number of Interface
     protected byte mAlternateSetting; // 3:1 Value used to select alternative setting
     protected byte mNumEndpoints;     // 4:1 Number of Endpoints used for this interface
@@ -73,6 +72,19 @@
         return mNumEndpoints;
     }
 
+    /**
+     * @param index Index of desired UsbEndpointDescriptor.
+     * @return the UsbEndpointDescriptor descriptor at the specified index, or
+     *  null if an invalid index.
+     */
+    public UsbEndpointDescriptor getEndpointDescriptor(int index) {
+        if (index < 0 || index >= mEndpointDescriptors.size()) {
+            return null;
+        }
+
+        return mEndpointDescriptors.get(index);
+    }
+
     public int getUsbClass() {
         return mUsbClass;
     }
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
index a8cafb3..1dd3972 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
@@ -666,6 +666,23 @@
                 return ret;
             }
         }
+
+        @Override
+        @Nullable
+        public ModuleProperties getModuleProperties() {
+            enforceCallingPermission(Manifest.permission.MANAGE_SOUND_TRIGGER);
+            if (!isInitialized()) return null;
+            if (DEBUG) {
+                Slog.i(TAG, "getModuleProperties()");
+            }
+
+            synchronized (mLock) {
+                ModuleProperties properties = mSoundTriggerHelper.getModuleProperties();
+                sEventLogger.log(new SoundTriggerLogger.StringEvent(
+                        "getModuleProperties(): " + properties.toString()));
+                return properties;
+            }
+        }
     }
 
     /**
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
index f16dec6..0ff9273 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
@@ -194,7 +194,7 @@
             if (!mFullyBound) {
                 mFullyBound = mContext.bindServiceAsUser(mBindIntent, mFullConnection,
                         Context.BIND_AUTO_CREATE | Context.BIND_TREAT_LIKE_ACTIVITY
-                                | Context.BIND_FOREGROUND_SERVICE
+                                | Context.BIND_SCHEDULE_LIKE_TOP_APP
                                 | Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS,
                         new UserHandle(mUser));
             }
diff --git a/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java b/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java
index 31bd341..902da4c 100644
--- a/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java
+++ b/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java
@@ -275,7 +275,8 @@
             Log.e(TAG, "connectToRemoteAndConfigure - null iorap remote. check for Log.wtf?");
             return false;
         }
-        invokeRemote( () -> mIorapRemote.setTaskListener(new RemoteTaskListener()) );
+        invokeRemote(mIorapRemote,
+            (IIorap remote) -> remote.setTaskListener(new RemoteTaskListener()) );
         registerInProcessListenersLocked();
 
         return true;
@@ -323,8 +324,9 @@
                         mSequenceId, intent));
             }
 
-            invokeRemote(() ->
-                    mIorapRemote.onAppLaunchEvent(RequestId.nextValueForSequence(),
+            invokeRemote(mIorapRemote,
+                (IIorap remote) ->
+                    remote.onAppLaunchEvent(RequestId.nextValueForSequence(),
                         new AppLaunchEvent.IntentStarted(mSequenceId, intent))
             );
         }
@@ -335,8 +337,9 @@
                 Log.v(TAG, String.format("AppLaunchObserver#onIntentFailed(%d)", mSequenceId));
             }
 
-            invokeRemote(() ->
-                    mIorapRemote.onAppLaunchEvent(RequestId.nextValueForSequence(),
+            invokeRemote(mIorapRemote,
+                (IIorap remote) ->
+                    remote.onAppLaunchEvent(RequestId.nextValueForSequence(),
                         new AppLaunchEvent.IntentFailed(mSequenceId))
             );
         }
@@ -349,8 +352,9 @@
                         mSequenceId, activity, temperature));
             }
 
-            invokeRemote(() ->
-                    mIorapRemote.onAppLaunchEvent(RequestId.nextValueForSequence(),
+            invokeRemote(mIorapRemote,
+                (IIorap remote) ->
+                    remote.onAppLaunchEvent(RequestId.nextValueForSequence(),
                             new AppLaunchEvent.ActivityLaunched(mSequenceId, activity, temperature))
             );
         }
@@ -362,8 +366,9 @@
                         mSequenceId, activity));
             }
 
-            invokeRemote(() ->
-                    mIorapRemote.onAppLaunchEvent(RequestId.nextValueForSequence(),
+            invokeRemote(mIorapRemote,
+                (IIorap remote) ->
+                    remote.onAppLaunchEvent(RequestId.nextValueForSequence(),
                             new AppLaunchEvent.ActivityLaunchCancelled(mSequenceId,
                                     activity)));
         }
@@ -375,8 +380,9 @@
                         mSequenceId, activity));
             }
 
-            invokeRemote(() ->
-                mIorapRemote.onAppLaunchEvent(RequestId.nextValueForSequence(),
+            invokeRemote(mIorapRemote,
+                (IIorap remote) ->
+                    remote.onAppLaunchEvent(RequestId.nextValueForSequence(),
                         new AppLaunchEvent.ActivityLaunchFinished(mSequenceId, activity))
             );
         }
@@ -501,8 +507,8 @@
                 mRunningJobs.put(request, params);
             }
 
-            if (!invokeRemote( () ->
-                    mIorapRemote.onJobScheduledEvent(request,
+            if (!invokeRemote(mIorapRemote, (IIorap remote) ->
+                    remote.onJobScheduledEvent(request,
                             JobScheduledEvent.createIdleMaintenance(
                                     JobScheduledEvent.TYPE_START_JOB,
                                     params))
@@ -539,8 +545,8 @@
 
             // Notify iorapd to stop (abort) the job.
             if (wasTracking) {
-                invokeRemote(() ->
-                        mIorapRemote.onJobScheduledEvent(RequestId.nextValueForSequence(),
+                invokeRemote(mIorapRemote, (IIorap remote) ->
+                        remote.onJobScheduledEvent(RequestId.nextValueForSequence(),
                                 JobScheduledEvent.createIdleMaintenance(
                                         JobScheduledEvent.TYPE_STOP_JOB,
                                         params))
@@ -632,12 +638,17 @@
     /** Allow passing lambdas to #invokeRemote */
     private interface RemoteRunnable {
         // TODO: run(RequestId) ?
-        void run() throws RemoteException;
+        void run(IIorap iorap) throws RemoteException;
     }
 
-    private static boolean invokeRemote(RemoteRunnable r) {
+    // Always pass in the iorap directly here to avoid data race.
+    private static boolean invokeRemote(IIorap iorap, RemoteRunnable r) {
+       if (iorap == null) {
+         Log.w(TAG, "IIorap went to null in this thread, drop invokeRemote.");
+         return false;
+       }
        try {
-           r.run();
+           r.run(iorap);
            return true;
        } catch (RemoteException e) {
            // This could be a logic error (remote side returning error), which we need to fix.
diff --git a/startop/view_compiler/dex_builder_test/Android.bp b/startop/view_compiler/dex_builder_test/Android.bp
index 1214538..f783aa6 100644
--- a/startop/view_compiler/dex_builder_test/Android.bp
+++ b/startop/view_compiler/dex_builder_test/Android.bp
@@ -37,15 +37,21 @@
 android_test {
     name: "dex-builder-test",
     srcs: [
+        "src/android/startop/test/ApkLayoutCompilerTest.java",
         "src/android/startop/test/DexBuilderTest.java",
         "src/android/startop/test/LayoutCompilerTest.java",
         "src/android/startop/test/TestClass.java",
     ],
     sdk_version: "current",
-    data: [":generate_dex_testcases", ":generate_compiled_layout1", ":generate_compiled_layout2"],
+    data: [
+        ":generate_dex_testcases",
+        ":generate_compiled_layout1",
+        ":generate_compiled_layout2",
+    ],
     static_libs: [
-        "androidx.test.rules",
-        "guava",
+        "androidx.test.core",
+        "androidx.test.runner",
+        "junit",
     ],
     manifest: "AndroidManifest.xml",
     resource_dirs: ["res"],
diff --git a/startop/view_compiler/dex_builder_test/src/android/startop/test/ApkLayoutCompilerTest.java b/startop/view_compiler/dex_builder_test/src/android/startop/test/ApkLayoutCompilerTest.java
new file mode 100644
index 0000000..230e8df
--- /dev/null
+++ b/startop/view_compiler/dex_builder_test/src/android/startop/test/ApkLayoutCompilerTest.java
@@ -0,0 +1,57 @@
+/*
+ * 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.startop.test;
+
+import android.content.Context;
+import androidx.test.InstrumentationRegistry;
+import android.view.View;
+import dalvik.system.PathClassLoader;
+import java.lang.reflect.Method;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+public class ApkLayoutCompilerTest {
+    static ClassLoader loadDexFile() throws Exception {
+        Context context = InstrumentationRegistry.getTargetContext();
+        return new PathClassLoader(context.getCodeCacheDir() + "/compiled_view.dex",
+                ClassLoader.getSystemClassLoader());
+    }
+
+    @BeforeClass
+    public static void setup() throws Exception {
+        // ensure PackageManager has compiled the layouts.
+        Process pm = Runtime.getRuntime().exec("pm compile --compile-layouts android.startop.test");
+        pm.waitFor();
+    }
+
+    @Test
+    public void loadAndInflateLayout1() throws Exception {
+        ClassLoader dex_file = loadDexFile();
+        Class compiled_view = dex_file.loadClass("android.startop.test.CompiledView");
+        Method layout1 = compiled_view.getMethod("layout1", Context.class, int.class);
+        Context context = InstrumentationRegistry.getTargetContext();
+        layout1.invoke(null, context, R.layout.layout1);
+    }
+
+    @Test
+    public void loadAndInflateLayout2() throws Exception {
+        ClassLoader dex_file = loadDexFile();
+        Class compiled_view = dex_file.loadClass("android.startop.test.CompiledView");
+        Method layout2 = compiled_view.getMethod("layout2", Context.class, int.class);
+        Context context = InstrumentationRegistry.getTargetContext();
+        layout2.invoke(null, context, R.layout.layout2);
+    }
+}
diff --git a/startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java b/startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java
index d1fe588..6af01f6f 100644
--- a/startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java
+++ b/startop/view_compiler/dex_builder_test/src/android/startop/test/DexBuilderTest.java
@@ -14,8 +14,11 @@
 
 package android.startop.test;
 
+import android.content.Context;
+import androidx.test.InstrumentationRegistry;
 import dalvik.system.PathClassLoader;
-
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
 import org.junit.Assert;
 import org.junit.Test;
 
diff --git a/startop/view_compiler/dex_builder_test/src/android/startop/test/LayoutCompilerTest.java b/startop/view_compiler/dex_builder_test/src/android/startop/test/LayoutCompilerTest.java
index 3dfb20c..b0cf91d 100644
--- a/startop/view_compiler/dex_builder_test/src/android/startop/test/LayoutCompilerTest.java
+++ b/startop/view_compiler/dex_builder_test/src/android/startop/test/LayoutCompilerTest.java
@@ -15,11 +15,11 @@
 package android.startop.test;
 
 import android.content.Context;
-
 import androidx.test.InstrumentationRegistry;
-
+import android.view.View;
 import dalvik.system.PathClassLoader;
-
+import java.lang.reflect.Method;
+import org.junit.Assert;
 import org.junit.Test;
 
 import java.lang.reflect.Method;
diff --git a/telephony/java/android/provider/Telephony.java b/telephony/java/android/provider/Telephony.java
index c1b8479..67b252e 100644
--- a/telephony/java/android/provider/Telephony.java
+++ b/telephony/java/android/provider/Telephony.java
@@ -3962,6 +3962,13 @@
         public static final Uri CONTENT_URI = Uri.parse("content://cellbroadcasts");
 
         /**
+         * The id of the subscription which received this cell broadcast message.
+         * <P>Type: INTEGER</P>
+         * @hide
+         */
+        public static final String SUB_ID = "sub_id";
+
+        /**
          * Message geographical scope. Valid values are:
          * <ul>
          * <li>{@link android.telephony.SmsCbMessage#GEOGRAPHICAL_SCOPE_CELL_WIDE}. meaning the
@@ -4183,6 +4190,18 @@
         public static final String GEOMETRIES = "geometries";
 
         /**
+         * Geo-Fencing Maximum Wait Time in second. The range of the time is [0, 255]. A device
+         * shall allow to determine its position meeting operator policy. If the device is unable to
+         * determine its position meeting operator policy within the GeoFencing Maximum Wait Time,
+         * it shall present the alert to the user and discontinue further positioning determination
+         * for the alert.
+         *
+         * <P>Type: INTEGER</P>
+         * @hide
+         */
+        public static final String MAXIMUM_WAIT_TIME = "maximum_wait_time";
+
+        /**
          * Query columns for instantiating {@link android.telephony.CellBroadcastMessage} objects.
          * @hide
          */
@@ -4235,7 +4254,8 @@
                 CMAS_CERTAINTY,
                 RECEIVED_TIME,
                 MESSAGE_BROADCASTED,
-                GEOMETRIES
+                GEOMETRIES,
+                MAXIMUM_WAIT_TIME
         };
     }
 
diff --git a/telephony/java/android/telephony/SmsCbMessage.java b/telephony/java/android/telephony/SmsCbMessage.java
index 77231d1..c078764 100644
--- a/telephony/java/android/telephony/SmsCbMessage.java
+++ b/telephony/java/android/telephony/SmsCbMessage.java
@@ -139,6 +139,12 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface MessagePriority {}
 
+    /**
+     * ATIS-0700041 Section 5.2.8 WAC Geo-Fencing Maximum Wait Time Table 12.
+     * @hide
+     */
+    public static final int MAXIMUM_WAIT_TIME_NOT_SET = 255;
+
     /** Format of this message (for interpretation of service category values). */
     private final int mMessageFormat;
 
@@ -187,6 +193,14 @@
     @Nullable
     private final SmsCbCmasInfo mCmasWarningInfo;
 
+    /**
+     * Geo-Fencing Maximum Wait Time in second, a device shall allow to determine its position
+     * meeting operator policy. If the device is unable to determine its position meeting operator
+     * policy within the GeoFencing Maximum Wait Time, it shall present the alert to the user and
+     * discontinue further positioning determination for the alert.
+     */
+    private final int mMaximumWaitTimeSec;
+
     /** UNIX timestamp of when the message was received. */
     private final long mReceivedTimeMillis;
 
@@ -202,8 +216,8 @@
             @Nullable SmsCbCmasInfo cmasWarningInfo) {
 
         this(messageFormat, geographicalScope, serialNumber, location, serviceCategory, language,
-                body, priority, etwsWarningInfo, cmasWarningInfo, null /* geometries */,
-                System.currentTimeMillis());
+                body, priority, etwsWarningInfo, cmasWarningInfo, 0 /* maximumWaitingTime */,
+                null /* geometries */, System.currentTimeMillis());
     }
 
     /**
@@ -213,7 +227,7 @@
     public SmsCbMessage(int messageFormat, int geographicalScope, int serialNumber,
             SmsCbLocation location, int serviceCategory, String language, String body,
             int priority, SmsCbEtwsInfo etwsWarningInfo, SmsCbCmasInfo cmasWarningInfo,
-            List<Geometry> geometries, long receivedTimeMillis) {
+            int maximumWaitTimeSec, List<Geometry> geometries, long receivedTimeMillis) {
         mMessageFormat = messageFormat;
         mGeographicalScope = geographicalScope;
         mSerialNumber = serialNumber;
@@ -226,6 +240,7 @@
         mCmasWarningInfo = cmasWarningInfo;
         mReceivedTimeMillis = receivedTimeMillis;
         mGeometries = geometries;
+        mMaximumWaitTimeSec = maximumWaitTimeSec;
     }
 
     /**
@@ -262,6 +277,7 @@
         mReceivedTimeMillis = in.readLong();
         String geoStr = in.readString();
         mGeometries = geoStr != null ? CbGeoUtils.parseGeometriesFromString(geoStr) : null;
+        mMaximumWaitTimeSec = in.readInt();
     }
 
     /**
@@ -295,6 +311,7 @@
         dest.writeLong(mReceivedTimeMillis);
         dest.writeString(
                 mGeometries != null ? CbGeoUtils.encodeGeometriesToString(mGeometries) : null);
+        dest.writeInt(mMaximumWaitTimeSec);
     }
 
     @NonNull
@@ -389,6 +406,15 @@
     }
 
     /**
+     * Get the Geo-Fencing Maximum Wait Time.
+     * @return the time in second.
+     * @hide
+     */
+    public int getMaximumWaitingTime() {
+        return mMaximumWaitTimeSec;
+    }
+
+    /**
      * Get the time when this message was received.
      * @return the time in millisecond
      */
@@ -475,6 +501,7 @@
                 + ", priority=" + mPriority
                 + (mEtwsWarningInfo != null ? (", " + mEtwsWarningInfo.toString()) : "")
                 + (mCmasWarningInfo != null ? (", " + mCmasWarningInfo.toString()) : "")
+                + ", maximumWaitingTime = " + mMaximumWaitTimeSec
                 + ", geo=" + (mGeometries != null
                 ? CbGeoUtils.encodeGeometriesToString(mGeometries) : "null")
                 + '}';
@@ -535,6 +562,8 @@
             cv.put(CellBroadcasts.GEOMETRIES, (String) null);
         }
 
+        cv.put(CellBroadcasts.MAXIMUM_WAIT_TIME, mMaximumWaitTimeSec);
+
         return cv;
     }
 
@@ -644,17 +673,21 @@
         List<Geometry> geometries =
                 geoStr != null ? CbGeoUtils.parseGeometriesFromString(geoStr) : null;
 
-        long receivedTimeSec = cursor.getLong(
+        long receivedTimeMillis = cursor.getLong(
                 cursor.getColumnIndexOrThrow(CellBroadcasts.RECEIVED_TIME));
 
+        int maximumWaitTimeSec = cursor.getInt(
+                cursor.getColumnIndexOrThrow(CellBroadcasts.MAXIMUM_WAIT_TIME));
+
         return new SmsCbMessage(format, geoScope, serialNum, location, category,
-                language, body, priority, etwsInfo, cmasInfo, geometries, receivedTimeSec);
+                language, body, priority, etwsInfo, cmasInfo, maximumWaitTimeSec, geometries,
+                receivedTimeMillis);
     }
 
     /**
      * @return {@code True} if this message needs geo-fencing check.
      */
     public boolean needGeoFencingCheck() {
-        return mGeometries != null;
+        return mMaximumWaitTimeSec > 0 && mGeometries != null;
     }
 }
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 475563d..f25b012 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -3132,19 +3132,62 @@
      */
     @SystemApi
     public int getSimCardState() {
-        int simCardState = getSimState();
-        switch (simCardState) {
+        int simState = getSimState();
+        return getSimCardStateFromSimState(simState);
+    }
+
+    /**
+     * Returns a constant indicating the state of the device SIM card in a physical slot.
+     *
+     * @param physicalSlotIndex physical slot index
+     *
+     * @see #SIM_STATE_UNKNOWN
+     * @see #SIM_STATE_ABSENT
+     * @see #SIM_STATE_CARD_IO_ERROR
+     * @see #SIM_STATE_CARD_RESTRICTED
+     * @see #SIM_STATE_PRESENT
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public int getSimCardState(int physicalSlotIndex) {
+        int simState = getSimState(getLogicalSlotIndex(physicalSlotIndex));
+        return getSimCardStateFromSimState(simState);
+    }
+
+    /**
+     * Converts SIM state to SIM card state.
+     * @param simState
+     * @return SIM card state
+     */
+    private int getSimCardStateFromSimState(int simState) {
+        switch (simState) {
             case SIM_STATE_UNKNOWN:
             case SIM_STATE_ABSENT:
             case SIM_STATE_CARD_IO_ERROR:
             case SIM_STATE_CARD_RESTRICTED:
-                return simCardState;
+                return simState;
             default:
                 return SIM_STATE_PRESENT;
         }
     }
 
     /**
+     * Converts a physical slot index to logical slot index.
+     * @param physicalSlotIndex physical slot index
+     * @return logical slot index
+     */
+    private int getLogicalSlotIndex(int physicalSlotIndex) {
+        UiccSlotInfo[] slotInfos = getUiccSlotsInfo();
+        if (slotInfos != null && physicalSlotIndex >= 0 && physicalSlotIndex < slotInfos.length) {
+            return slotInfos[physicalSlotIndex].getLogicalSlotIdx();
+        }
+
+        return SubscriptionManager.INVALID_SIM_SLOT_INDEX;
+    }
+
+    /**
      * Returns a constant indicating the state of the card applications on the default SIM card.
      *
      * @see #SIM_STATE_UNKNOWN
@@ -3159,8 +3202,41 @@
      */
     @SystemApi
     public int getSimApplicationState() {
-        int simApplicationState = getSimStateIncludingLoaded();
-        switch (simApplicationState) {
+        int simState = getSimStateIncludingLoaded();
+        return getSimApplicationStateFromSimState(simState);
+    }
+
+    /**
+     * Returns a constant indicating the state of the card applications on the device SIM card in
+     * a physical slot.
+     *
+     * @param physicalSlotIndex physical slot index
+     *
+     * @see #SIM_STATE_UNKNOWN
+     * @see #SIM_STATE_PIN_REQUIRED
+     * @see #SIM_STATE_PUK_REQUIRED
+     * @see #SIM_STATE_NETWORK_LOCKED
+     * @see #SIM_STATE_NOT_READY
+     * @see #SIM_STATE_PERM_DISABLED
+     * @see #SIM_STATE_LOADED
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public int getSimApplicationState(int physicalSlotIndex) {
+        int simState =
+                SubscriptionManager.getSimStateForSlotIndex(getLogicalSlotIndex(physicalSlotIndex));
+        return getSimApplicationStateFromSimState(simState);
+    }
+
+    /**
+     * Converts SIM state to SIM application state.
+     * @param simState
+     * @return SIM application state
+     */
+    private int getSimApplicationStateFromSimState(int simState) {
+        switch (simState) {
             case SIM_STATE_UNKNOWN:
             case SIM_STATE_ABSENT:
             case SIM_STATE_CARD_IO_ERROR:
@@ -3171,14 +3247,14 @@
                 // NOT_READY to either LOCKED or LOADED.
                 return SIM_STATE_NOT_READY;
             default:
-                return simApplicationState;
+                return simState;
         }
     }
 
     /**
-     * Returns a constant indicating the state of the device SIM card in a slot.
+     * Returns a constant indicating the state of the device SIM card in a logical slot.
      *
-     * @param slotIndex
+     * @param slotIndex logical slot index
      *
      * @see #SIM_STATE_UNKNOWN
      * @see #SIM_STATE_ABSENT
diff --git a/telephony/java/com/android/internal/telephony/CbGeoUtils.java b/telephony/java/com/android/internal/telephony/CbGeoUtils.java
index 73dd822..0b73252 100644
--- a/telephony/java/com/android/internal/telephony/CbGeoUtils.java
+++ b/telephony/java/com/android/internal/telephony/CbGeoUtils.java
@@ -299,7 +299,8 @@
      * @return the encoded string.
      */
     @NonNull
-    public static String encodeGeometriesToString(@NonNull List<Geometry> geometries) {
+    public static String encodeGeometriesToString(List<Geometry> geometries) {
+        if (geometries == null || geometries.isEmpty()) return "";
         return geometries.stream()
                 .map(geometry -> encodeGeometryToString(geometry))
                 .filter(encodedStr -> !TextUtils.isEmpty(encodedStr))
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmSmsCbMessage.java b/telephony/java/com/android/internal/telephony/gsm/GsmSmsCbMessage.java
index dca4e6b..6eea118 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmSmsCbMessage.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmSmsCbMessage.java
@@ -104,7 +104,7 @@
                     header.getSerialNumber(), location, header.getServiceCategory(), null,
                     getEtwsPrimaryMessage(context, header.getEtwsInfo().getWarningType()),
                     SmsCbMessage.MESSAGE_PRIORITY_EMERGENCY, header.getEtwsInfo(),
-                    header.getCmasInfo(), null /* geometries */, receivedTimeMillis);
+                    header.getCmasInfo(), 0, null /* geometries */, receivedTimeMillis);
         } else if (header.isUmtsFormat()) {
             // UMTS format has only 1 PDU
             byte[] pdu = pdus[0];
@@ -120,9 +120,13 @@
 
             // Has Warning Area Coordinates information
             List<Geometry> geometries = null;
+            int maximumWaitingTimeSec = 255;
             if (pdu.length > wacDataOffset) {
                 try {
-                    geometries = parseWarningAreaCoordinates(pdu, wacDataOffset);
+                    Pair<Integer, List<Geometry>> wac = parseWarningAreaCoordinates(pdu,
+                            wacDataOffset);
+                    maximumWaitingTimeSec = wac.first;
+                    geometries = wac.second;
                 } catch (Exception ex) {
                     // Catch the exception here, the message will be considered as having no WAC
                     // information which means the message will be broadcasted directly.
@@ -133,7 +137,8 @@
             return new SmsCbMessage(SmsCbMessage.MESSAGE_FORMAT_3GPP,
                     header.getGeographicalScope(), header.getSerialNumber(), location,
                     header.getServiceCategory(), language, body, priority,
-                    header.getEtwsInfo(), header.getCmasInfo(), geometries, receivedTimeMillis);
+                    header.getEtwsInfo(), header.getCmasInfo(), maximumWaitingTimeSec, geometries,
+                    receivedTimeMillis);
         } else {
             String language = null;
             StringBuilder sb = new StringBuilder();
@@ -148,7 +153,7 @@
             return new SmsCbMessage(SmsCbMessage.MESSAGE_FORMAT_3GPP,
                     header.getGeographicalScope(), header.getSerialNumber(), location,
                     header.getServiceCategory(), language, sb.toString(), priority,
-                    header.getEtwsInfo(), header.getCmasInfo(), null /* geometries */,
+                    header.getEtwsInfo(), header.getCmasInfo(), 0, null /* geometries */,
                     receivedTimeMillis);
         }
     }
@@ -197,7 +202,17 @@
         }
     }
 
-    private static List<Geometry> parseWarningAreaCoordinates(byte[] pdu, int wacOffset) {
+    /**
+     * Parse the broadcast area and maximum wait time from the Warning Area Coordinates TLV.
+     *
+     * @param pdu Warning Area Coordinates TLV.
+     * @param wacOffset the offset of Warning Area Coordinates TLV.
+     * @return a pair with the first element is maximum wait time and the second is the broadcast
+     * area. The default value of the maximum wait time is 255 which means use the device default
+     * value.
+     */
+    private static Pair<Integer, List<Geometry>> parseWarningAreaCoordinates(
+            byte[] pdu, int wacOffset) {
         // little-endian
         int wacDataLength = (pdu[wacOffset + 1] << 8) | pdu[wacOffset];
         int offset = wacOffset + 2;
@@ -209,6 +224,8 @@
 
         BitStreamReader bitReader = new BitStreamReader(pdu, offset);
 
+        int maximumWaitTimeSec = SmsCbMessage.MAXIMUM_WAIT_TIME_NOT_SET;
+
         List<Geometry> geo = new ArrayList<>();
         int remainedBytes = wacDataLength;
         while (remainedBytes > 0) {
@@ -220,8 +237,7 @@
 
             switch (type) {
                 case CbGeoUtils.GEO_FENCING_MAXIMUM_WAIT_TIME:
-                    // TODO: handle the maximum wait time in cell broadcast provider.
-                    int maximumWaitTimeSec = bitReader.read(8);
+                    maximumWaitTimeSec = bitReader.read(8);
                     break;
                 case CbGeoUtils.GEOMETRY_TYPE_POLYGON:
                     List<LatLng> latLngs = new ArrayList<>();
@@ -247,7 +263,7 @@
                     throw new IllegalArgumentException("Unsupported geoType = " + type);
             }
         }
-        return geo;
+        return new Pair(maximumWaitTimeSec, geo);
     }
 
     /**
diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
index da32c8c..28b331b 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java
@@ -1496,7 +1496,7 @@
      *
      * @return true if this is a USIM data download message; false otherwise
      */
-    boolean isUsimDataDownload() {
+    public boolean isUsimDataDownload() {
         return messageClass == MessageClass.CLASS_2 &&
                 (mProtocolIdentifier == 0x7f || mProtocolIdentifier == 0x7c);
     }
diff --git a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
index 47fefae..2d867f9 100644
--- a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
+++ b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
@@ -20,7 +20,6 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.anyInt;
@@ -65,8 +64,6 @@
 import java.util.concurrent.TimeUnit;
 import java.util.function.Consumer;
 
-// TODO: Write test without using PackageWatchdog#getPackages. Just rely on
-// behavior of observers receiving crash notifications or not to determine if it's registered
 // TODO: Use Truth in tests.
 /**
  * Test PackageWatchdog.
@@ -83,6 +80,7 @@
     private static final String OBSERVER_NAME_4 = "observer4";
     private static final long SHORT_DURATION = TimeUnit.SECONDS.toMillis(1);
     private static final long LONG_DURATION = TimeUnit.SECONDS.toMillis(5);
+    private final TestClock mTestClock = new TestClock();
     private TestLooper mTestLooper;
     private Context mSpyContext;
     @Mock
@@ -114,78 +112,109 @@
         dropShellPermissions();
     }
 
-    /**
-     * Test registration, unregistration, package expiry and duration reduction
-     */
     @Test
-    public void testRegistration() throws Exception {
+    public void testRegistration_singleObserver() {
+        PackageWatchdog watchdog = createWatchdog();
+        TestObserver observer = new TestObserver(OBSERVER_NAME_1);
+
+        watchdog.startObservingHealth(observer, Arrays.asList(APP_A), SHORT_DURATION);
+        raiseFatalFailure(watchdog, Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)));
+        mTestLooper.dispatchAll();
+
+        // The failed packages should be the same as the registered ones to ensure registration is
+        // done successfully
+        assertEquals(1, observer.mHealthCheckFailedPackages.size());
+        assertTrue(observer.mHealthCheckFailedPackages.contains(APP_A));
+    }
+
+    @Test
+    public void testRegistration_multiObservers() {
         PackageWatchdog watchdog = createWatchdog();
         TestObserver observer1 = new TestObserver(OBSERVER_NAME_1);
         TestObserver observer2 = new TestObserver(OBSERVER_NAME_2);
-        TestObserver observer3 = new TestObserver(OBSERVER_NAME_3);
 
-        // Start observing for observer1 which will be unregistered
         watchdog.startObservingHealth(observer1, Arrays.asList(APP_A), SHORT_DURATION);
-        // Start observing for observer2 which will expire
         watchdog.startObservingHealth(observer2, Arrays.asList(APP_A, APP_B), SHORT_DURATION);
-        // Start observing for observer3 which will have expiry duration reduced
-        watchdog.startObservingHealth(observer3, Arrays.asList(APP_A), LONG_DURATION);
-
-        // Verify packages observed at start
-        // 1
-        assertEquals(1, watchdog.getPackages(observer1).size());
-        assertTrue(watchdog.getPackages(observer1).contains(APP_A));
-        // 2
-        assertEquals(2, watchdog.getPackages(observer2).size());
-        assertTrue(watchdog.getPackages(observer2).contains(APP_A));
-        assertTrue(watchdog.getPackages(observer2).contains(APP_B));
-        // 3
-        assertEquals(1, watchdog.getPackages(observer3).size());
-        assertTrue(watchdog.getPackages(observer3).contains(APP_A));
-
-        // Then unregister observer1
-        watchdog.unregisterHealthObserver(observer1);
-
-        // Verify observer2 and observer3 left
-        // 1
-        assertNull(watchdog.getPackages(observer1));
-        // 2
-        assertEquals(2, watchdog.getPackages(observer2).size());
-        assertTrue(watchdog.getPackages(observer2).contains(APP_A));
-        assertTrue(watchdog.getPackages(observer2).contains(APP_B));
-        // 3
-        assertEquals(1, watchdog.getPackages(observer3).size());
-        assertTrue(watchdog.getPackages(observer3).contains(APP_A));
-
-        // Then advance time a little and run messages in Handlers so observer2 expires
-        Thread.sleep(SHORT_DURATION);
+        raiseFatalFailure(watchdog, Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE),
+                new VersionedPackage(APP_B, VERSION_CODE)));
         mTestLooper.dispatchAll();
 
-        // Verify observer3 left with reduced expiry duration
-        // 1
-        assertNull(watchdog.getPackages(observer1));
-        // 2
-        assertNull(watchdog.getPackages(observer2));
-        // 3
-        assertEquals(1, watchdog.getPackages(observer3).size());
-        assertTrue(watchdog.getPackages(observer3).contains(APP_A));
+        // The failed packages should be the same as the registered ones to ensure registration is
+        // done successfully
+        assertEquals(1, observer1.mHealthCheckFailedPackages.size());
+        assertEquals(2, observer2.mHealthCheckFailedPackages.size());
+        assertTrue(observer1.mHealthCheckFailedPackages.contains(APP_A));
+        assertTrue(observer2.mHealthCheckFailedPackages.contains(APP_A));
+        assertTrue(observer2.mHealthCheckFailedPackages.contains(APP_B));
+    }
 
-        // Then advance time some more and run messages in Handlers so observer3 expires
-        Thread.sleep(LONG_DURATION);
+    @Test
+    public void testUnregistration_singleObserver() {
+        PackageWatchdog watchdog = createWatchdog();
+        TestObserver observer = new TestObserver(OBSERVER_NAME_1);
+
+        watchdog.startObservingHealth(observer, Arrays.asList(APP_A), SHORT_DURATION);
+        watchdog.unregisterHealthObserver(observer);
+        raiseFatalFailure(watchdog, Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)));
         mTestLooper.dispatchAll();
 
-        // Verify observer3 expired
-        // 1
-        assertNull(watchdog.getPackages(observer1));
-        // 2
-        assertNull(watchdog.getPackages(observer2));
-        // 3
-        assertNull(watchdog.getPackages(observer3));
+        // We should have no failed packages to ensure unregistration is done successfully
+        assertEquals(0, observer.mHealthCheckFailedPackages.size());
+    }
+
+    @Test
+    public void testUnregistration_multiObservers() {
+        PackageWatchdog watchdog = createWatchdog();
+        TestObserver observer1 = new TestObserver(OBSERVER_NAME_1);
+        TestObserver observer2 = new TestObserver(OBSERVER_NAME_2);
+
+        watchdog.startObservingHealth(observer1, Arrays.asList(APP_A), SHORT_DURATION);
+        watchdog.startObservingHealth(observer2, Arrays.asList(APP_A), SHORT_DURATION);
+        watchdog.unregisterHealthObserver(observer2);
+        raiseFatalFailure(watchdog, Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)));
+        mTestLooper.dispatchAll();
+
+        // observer1 should receive failed packages as intended.
+        assertEquals(1, observer1.mHealthCheckFailedPackages.size());
+        // observer2 should have no failed packages to ensure unregistration is done successfully
+        assertEquals(0, observer2.mHealthCheckFailedPackages.size());
+    }
+
+    @Test
+    public void testExpiration_singleObserver() {
+        PackageWatchdog watchdog = createWatchdog();
+        TestObserver observer = new TestObserver(OBSERVER_NAME_1);
+
+        watchdog.startObservingHealth(observer, Arrays.asList(APP_A), SHORT_DURATION);
+        moveTimeForwardAndDispatch(SHORT_DURATION);
+        raiseFatalFailure(watchdog, Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)));
+        mTestLooper.dispatchAll();
+
+        // We should have no failed packages for the fatal failure is raised after expiration
+        assertEquals(0, observer.mHealthCheckFailedPackages.size());
+    }
+
+    @Test
+    public void testExpiration_multiObservers() {
+        PackageWatchdog watchdog = createWatchdog();
+        TestObserver observer1 = new TestObserver(OBSERVER_NAME_1);
+        TestObserver observer2 = new TestObserver(OBSERVER_NAME_2);
+
+        watchdog.startObservingHealth(observer1, Arrays.asList(APP_A), SHORT_DURATION);
+        watchdog.startObservingHealth(observer2, Arrays.asList(APP_A), LONG_DURATION);
+        moveTimeForwardAndDispatch(SHORT_DURATION);
+        raiseFatalFailure(watchdog, Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)));
+        mTestLooper.dispatchAll();
+
+        // We should have no failed packages for the fatal failure is raised after expiration
+        assertEquals(0, observer1.mHealthCheckFailedPackages.size());
+        // We should have failed packages since observer2 hasn't expired
+        assertEquals(1, observer2.mHealthCheckFailedPackages.size());
     }
 
     /** Observing already observed package extends the observation time. */
     @Test
-    public void testObserveAlreadyObservedPackage() throws Exception {
+    public void testObserveAlreadyObservedPackage() {
         PackageWatchdog watchdog = createWatchdog();
         TestObserver observer = new TestObserver(OBSERVER_NAME_1);
 
@@ -193,66 +222,51 @@
         watchdog.startObservingHealth(observer, Arrays.asList(APP_A), SHORT_DURATION);
 
         // Then advance time half-way
-        Thread.sleep(SHORT_DURATION / 2);
-        mTestLooper.dispatchAll();
+        moveTimeForwardAndDispatch(SHORT_DURATION / 2);
 
         // Start observing APP_A again
         watchdog.startObservingHealth(observer, Arrays.asList(APP_A), SHORT_DURATION);
 
         // Then advance time such that it should have expired were it not for the second observation
-        Thread.sleep((SHORT_DURATION / 2) + 1);
+        moveTimeForwardAndDispatch((SHORT_DURATION / 2) + 1);
+
+        raiseFatalFailure(watchdog, Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)));
         mTestLooper.dispatchAll();
 
-        // Verify that APP_A not expired since second observation extended the time
-        assertEquals(1, watchdog.getPackages(observer).size());
-        assertTrue(watchdog.getPackages(observer).contains(APP_A));
+        // Verify that we receive failed packages as expected for APP_A not expired
+        assertEquals(1, observer.mHealthCheckFailedPackages.size());
+        assertTrue(observer.mHealthCheckFailedPackages.contains(APP_A));
     }
 
     /**
      * Test package observers are persisted and loaded on startup
      */
     @Test
-    public void testPersistence() throws Exception {
+    public void testPersistence() {
         PackageWatchdog watchdog1 = createWatchdog();
         TestObserver observer1 = new TestObserver(OBSERVER_NAME_1);
         TestObserver observer2 = new TestObserver(OBSERVER_NAME_2);
 
         watchdog1.startObservingHealth(observer1, Arrays.asList(APP_A), SHORT_DURATION);
         watchdog1.startObservingHealth(observer2, Arrays.asList(APP_A, APP_B), SHORT_DURATION);
-
-        // Verify 2 observers are registered and saved internally
-        // 1
-        assertEquals(1, watchdog1.getPackages(observer1).size());
-        assertTrue(watchdog1.getPackages(observer1).contains(APP_A));
-        // 2
-        assertEquals(2, watchdog1.getPackages(observer2).size());
-        assertTrue(watchdog1.getPackages(observer2).contains(APP_A));
-        assertTrue(watchdog1.getPackages(observer2).contains(APP_B));
-
         // Then advance time and run IO Handler so file is saved
         mTestLooper.dispatchAll();
-
         // Then start a new watchdog
         PackageWatchdog watchdog2 = createWatchdog();
-
-        // Verify the new watchdog loads observers on startup but nothing registered
-        assertEquals(0, watchdog2.getPackages(observer1).size());
-        assertEquals(0, watchdog2.getPackages(observer2).size());
-        // Verify random observer not saved returns null
-        assertNull(watchdog2.getPackages(new TestObserver(OBSERVER_NAME_3)));
-
-        // Then register observer1
+        // Then resume observer1 and observer2
         watchdog2.registerHealthObserver(observer1);
         watchdog2.registerHealthObserver(observer2);
+        raiseFatalFailure(watchdog2, Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE),
+                new VersionedPackage(APP_B, VERSION_CODE)));
+        mTestLooper.dispatchAll();
 
-        // Verify 2 observers are registered after reload
-        // 1
-        assertEquals(1, watchdog1.getPackages(observer1).size());
-        assertTrue(watchdog1.getPackages(observer1).contains(APP_A));
-        // 2
-        assertEquals(2, watchdog1.getPackages(observer2).size());
-        assertTrue(watchdog1.getPackages(observer2).contains(APP_A));
-        assertTrue(watchdog1.getPackages(observer2).contains(APP_B));
+        // We should receive failed packages as expected to ensure observers are persisted and
+        // resumed correctly
+        assertEquals(1, observer1.mHealthCheckFailedPackages.size());
+        assertEquals(2, observer2.mHealthCheckFailedPackages.size());
+        assertTrue(observer1.mHealthCheckFailedPackages.contains(APP_A));
+        assertTrue(observer1.mHealthCheckFailedPackages.contains(APP_A));
+        assertTrue(observer2.mHealthCheckFailedPackages.contains(APP_B));
     }
 
     /**
@@ -276,8 +290,8 @@
         mTestLooper.dispatchAll();
 
         // Verify that observers are not notified
-        assertEquals(0, observer1.mFailedPackages.size());
-        assertEquals(0, observer2.mFailedPackages.size());
+        assertEquals(0, observer1.mMitigatedPackages.size());
+        assertEquals(0, observer2.mMitigatedPackages.size());
     }
 
     /**
@@ -295,16 +309,14 @@
         watchdog.startObservingHealth(observer1, Arrays.asList(APP_B), SHORT_DURATION);
 
         // Then fail APP_C (not observed) above the threshold
-        for (int i = 0; i < watchdog.getTriggerFailureCount(); i++) {
-            watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_C, VERSION_CODE)));
-        }
+        raiseFatalFailure(watchdog, Arrays.asList(new VersionedPackage(APP_C, VERSION_CODE)));
 
         // Run handler so package failures are dispatched to observers
         mTestLooper.dispatchAll();
 
         // Verify that observers are not notified
-        assertEquals(0, observer1.mFailedPackages.size());
-        assertEquals(0, observer2.mFailedPackages.size());
+        assertEquals(0, observer1.mMitigatedPackages.size());
+        assertEquals(0, observer2.mMitigatedPackages.size());
     }
 
     /**
@@ -329,16 +341,14 @@
         watchdog.startObservingHealth(observer, Arrays.asList(APP_A), SHORT_DURATION);
 
         // Then fail APP_A (different version) above the threshold
-        for (int i = 0; i < watchdog.getTriggerFailureCount(); i++) {
-            watchdog.onPackageFailure(Arrays.asList(
-                            new VersionedPackage(APP_A, differentVersionCode)));
-        }
+        raiseFatalFailure(watchdog,
+                Arrays.asList(new VersionedPackage(APP_A, differentVersionCode)));
 
         // Run handler so package failures are dispatched to observers
         mTestLooper.dispatchAll();
 
         // Verify that observers are not notified
-        assertEquals(0, observer.mFailedPackages.size());
+        assertEquals(0, observer.mMitigatedPackages.size());
     }
 
 
@@ -368,21 +378,19 @@
                 SHORT_DURATION);
 
         // Then fail all apps above the threshold
-        for (int i = 0; i < watchdog.getTriggerFailureCount(); i++) {
-            watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE),
-                    new VersionedPackage(APP_B, VERSION_CODE),
-                    new VersionedPackage(APP_C, VERSION_CODE),
-                    new VersionedPackage(APP_D, VERSION_CODE)));
-        }
+        raiseFatalFailure(watchdog, Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE),
+                new VersionedPackage(APP_B, VERSION_CODE),
+                new VersionedPackage(APP_C, VERSION_CODE),
+                new VersionedPackage(APP_D, VERSION_CODE)));
 
         // Run handler so package failures are dispatched to observers
         mTestLooper.dispatchAll();
 
         // Verify least impact observers are notifed of package failures
-        List<String> observerNonePackages = observerNone.mFailedPackages;
-        List<String> observerHighPackages = observerHigh.mFailedPackages;
-        List<String> observerMidPackages = observerMid.mFailedPackages;
-        List<String> observerLowPackages = observerLow.mFailedPackages;
+        List<String> observerNonePackages = observerNone.mMitigatedPackages;
+        List<String> observerHighPackages = observerHigh.mMitigatedPackages;
+        List<String> observerMidPackages = observerMid.mMitigatedPackages;
+        List<String> observerLowPackages = observerLow.mMitigatedPackages;
 
         // APP_D failure observed by only observerNone is not caught cos its impact is none
         assertEquals(0, observerNonePackages.size());
@@ -421,66 +429,58 @@
         watchdog.startObservingHealth(observerSecond, Arrays.asList(APP_A), LONG_DURATION);
 
         // Then fail APP_A above the threshold
-        for (int i = 0; i < watchdog.getTriggerFailureCount(); i++) {
-            watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)));
-        }
+        raiseFatalFailure(watchdog, Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)));
         // Run handler so package failures are dispatched to observers
         mTestLooper.dispatchAll();
 
         // Verify only observerFirst is notifed
-        assertEquals(1, observerFirst.mFailedPackages.size());
-        assertEquals(APP_A, observerFirst.mFailedPackages.get(0));
-        assertEquals(0, observerSecond.mFailedPackages.size());
+        assertEquals(1, observerFirst.mMitigatedPackages.size());
+        assertEquals(APP_A, observerFirst.mMitigatedPackages.get(0));
+        assertEquals(0, observerSecond.mMitigatedPackages.size());
 
         // After observerFirst handles failure, next action it has is high impact
         observerFirst.mImpact = PackageHealthObserverImpact.USER_IMPACT_HIGH;
-        observerFirst.mFailedPackages.clear();
-        observerSecond.mFailedPackages.clear();
+        observerFirst.mMitigatedPackages.clear();
+        observerSecond.mMitigatedPackages.clear();
 
         // Then fail APP_A again above the threshold
-        for (int i = 0; i < watchdog.getTriggerFailureCount(); i++) {
-            watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)));
-        }
+        raiseFatalFailure(watchdog, Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)));
         // Run handler so package failures are dispatched to observers
         mTestLooper.dispatchAll();
 
         // Verify only observerSecond is notifed cos it has least impact
-        assertEquals(1, observerSecond.mFailedPackages.size());
-        assertEquals(APP_A, observerSecond.mFailedPackages.get(0));
-        assertEquals(0, observerFirst.mFailedPackages.size());
+        assertEquals(1, observerSecond.mMitigatedPackages.size());
+        assertEquals(APP_A, observerSecond.mMitigatedPackages.get(0));
+        assertEquals(0, observerFirst.mMitigatedPackages.size());
 
         // After observerSecond handles failure, it has no further actions
         observerSecond.mImpact = PackageHealthObserverImpact.USER_IMPACT_NONE;
-        observerFirst.mFailedPackages.clear();
-        observerSecond.mFailedPackages.clear();
+        observerFirst.mMitigatedPackages.clear();
+        observerSecond.mMitigatedPackages.clear();
 
         // Then fail APP_A again above the threshold
-        for (int i = 0; i < watchdog.getTriggerFailureCount(); i++) {
-            watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)));
-        }
+        raiseFatalFailure(watchdog, Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)));
         // Run handler so package failures are dispatched to observers
         mTestLooper.dispatchAll();
 
         // Verify only observerFirst is notifed cos it has the only action
-        assertEquals(1, observerFirst.mFailedPackages.size());
-        assertEquals(APP_A, observerFirst.mFailedPackages.get(0));
-        assertEquals(0, observerSecond.mFailedPackages.size());
+        assertEquals(1, observerFirst.mMitigatedPackages.size());
+        assertEquals(APP_A, observerFirst.mMitigatedPackages.get(0));
+        assertEquals(0, observerSecond.mMitigatedPackages.size());
 
         // After observerFirst handles failure, it too has no further actions
         observerFirst.mImpact = PackageHealthObserverImpact.USER_IMPACT_NONE;
-        observerFirst.mFailedPackages.clear();
-        observerSecond.mFailedPackages.clear();
+        observerFirst.mMitigatedPackages.clear();
+        observerSecond.mMitigatedPackages.clear();
 
         // Then fail APP_A again above the threshold
-        for (int i = 0; i < watchdog.getTriggerFailureCount(); i++) {
-            watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)));
-        }
+        raiseFatalFailure(watchdog, Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)));
         // Run handler so package failures are dispatched to observers
         mTestLooper.dispatchAll();
 
         // Verify no observer is notified cos no actions left
-        assertEquals(0, observerFirst.mFailedPackages.size());
-        assertEquals(0, observerSecond.mFailedPackages.size());
+        assertEquals(0, observerFirst.mMitigatedPackages.size());
+        assertEquals(0, observerSecond.mMitigatedPackages.size());
     }
 
     /**
@@ -499,17 +499,15 @@
         watchdog.startObservingHealth(observer1, Arrays.asList(APP_A), SHORT_DURATION);
 
         // Then fail APP_A above the threshold
-        for (int i = 0; i < watchdog.getTriggerFailureCount(); i++) {
-            watchdog.onPackageFailure(Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)));
-        }
+        raiseFatalFailure(watchdog, Arrays.asList(new VersionedPackage(APP_A, VERSION_CODE)));
 
         // Run handler so package failures are dispatched to observers
         mTestLooper.dispatchAll();
 
         // Verify only one observer is notifed
-        assertEquals(1, observer1.mFailedPackages.size());
-        assertEquals(APP_A, observer1.mFailedPackages.get(0));
-        assertEquals(0, observer2.mFailedPackages.size());
+        assertEquals(1, observer1.mMitigatedPackages.size());
+        assertEquals(APP_A, observer1.mMitigatedPackages.get(0));
+        assertEquals(0, observer2.mMitigatedPackages.size());
     }
 
     /**
@@ -552,23 +550,21 @@
         watchdog.startObservingHealth(observer3, Arrays.asList(APP_A), SHORT_DURATION);
 
         // Then expire observers
-        Thread.sleep(SHORT_DURATION);
-        // Run handler so package failures are dispatched to observers
-        mTestLooper.dispatchAll();
+        moveTimeForwardAndDispatch(SHORT_DURATION);
 
         // Verify we cancelled all requests on expiry
         assertEquals(0, controller.getRequestedPackages().size());
 
         // Verify observer1 is not notified
-        assertEquals(0, observer1.mFailedPackages.size());
+        assertEquals(0, observer1.mMitigatedPackages.size());
 
         // Verify observer2 is notifed because health checks for APP_B never passed
-        assertEquals(1, observer2.mFailedPackages.size());
-        assertEquals(APP_B, observer2.mFailedPackages.get(0));
+        assertEquals(1, observer2.mMitigatedPackages.size());
+        assertEquals(APP_B, observer2.mMitigatedPackages.get(0));
 
         // Verify observer3 is notifed because health checks for APP_A did not pass before expiry
-        assertEquals(1, observer3.mFailedPackages.size());
-        assertEquals(APP_A, observer3.mFailedPackages.get(0));
+        assertEquals(1, observer3.mMitigatedPackages.size());
+        assertEquals(APP_A, observer3.mMitigatedPackages.get(0));
     }
 
     /**
@@ -609,11 +605,10 @@
         assertEquals(0, controller.getRequestedPackages().size());
 
         // Then expire APP_A
-        Thread.sleep(SHORT_DURATION);
-        mTestLooper.dispatchAll();
+        moveTimeForwardAndDispatch(SHORT_DURATION);
 
         // Verify APP_A is not failed (APP_B) is not expired yet
-        assertEquals(0, observer.mFailedPackages.size());
+        assertEquals(0, observer.mMitigatedPackages.size());
 
         // Re-enable explicit health checks
         setExplicitHealthCheckEnabled(true);
@@ -638,12 +633,11 @@
         assertEquals(APP_C, requestedPackages.get(0));
 
         // Then expire APP_A and APP_C
-        Thread.sleep(SHORT_DURATION);
-        mTestLooper.dispatchAll();
+        moveTimeForwardAndDispatch(SHORT_DURATION);
 
         // Verify only APP_C is failed because explicit health checks was not supported for APP_A
-        assertEquals(1, observer.mFailedPackages.size());
-        assertEquals(APP_C, observer.mFailedPackages.get(0));
+        assertEquals(1, observer.mMitigatedPackages.size());
+        assertEquals(APP_C, observer.mMitigatedPackages.get(0));
     }
 
     /**
@@ -664,21 +658,20 @@
         watchdog.startObservingHealth(observer, Arrays.asList(APP_A), LONG_DURATION);
 
         // Then APP_A has exceeded health check duration
-        Thread.sleep(SHORT_DURATION);
-        mTestLooper.dispatchAll();
+        moveTimeForwardAndDispatch(SHORT_DURATION);
 
         // Verify that health check is failed
-        assertEquals(1, observer.mFailedPackages.size());
-        assertEquals(APP_A, observer.mFailedPackages.get(0));
+        assertEquals(1, observer.mMitigatedPackages.size());
+        assertEquals(APP_A, observer.mMitigatedPackages.get(0));
 
         // Then clear failed packages and start observing a random package so requests are synced
         // and PackageWatchdog#onSupportedPackages is called and APP_A has a chance to fail again
         // this time due to package expiry.
-        observer.mFailedPackages.clear();
+        observer.mMitigatedPackages.clear();
         watchdog.startObservingHealth(observer, Arrays.asList(APP_B), LONG_DURATION);
 
         // Verify that health check failure is not notified again
-        assertTrue(observer.mFailedPackages.isEmpty());
+        assertTrue(observer.mMitigatedPackages.isEmpty());
     }
 
     /** Tests {@link MonitoredPackage} health check state transitions. */
@@ -742,8 +735,8 @@
         mTestLooper.dispatchAll();
 
         // Verify the NetworkStack observer is notified
-        assertEquals(1, observer.mFailedPackages.size());
-        assertEquals(APP_A, observer.mFailedPackages.get(0));
+        assertEquals(1, observer.mMitigatedPackages.size());
+        assertEquals(APP_A, observer.mMitigatedPackages.get(0));
     }
 
     private void adoptShellPermissions(String... permissions) {
@@ -772,6 +765,19 @@
         }
     }
 
+    private void moveTimeForwardAndDispatch(long milliSeconds) {
+        mTestClock.moveTimeForward(milliSeconds);
+        mTestLooper.moveTimeForward(milliSeconds);
+        mTestLooper.dispatchAll();
+    }
+
+    /** Trigger package failures above the threshold. */
+    private void raiseFatalFailure(PackageWatchdog watchdog, List<VersionedPackage> packages) {
+        for (int i = 0; i < watchdog.getTriggerFailureCount(); i++) {
+            watchdog.onPackageFailure(packages);
+        }
+    }
+
     private PackageWatchdog createWatchdog() {
         return createWatchdog(new TestController(), true /* withPackagesReady */);
     }
@@ -782,7 +788,7 @@
         Handler handler = new Handler(mTestLooper.getLooper());
         PackageWatchdog watchdog =
                 new PackageWatchdog(mSpyContext, policyFile, handler, handler, controller,
-                        mConnectivityModuleConnector, android.os.SystemClock::uptimeMillis);
+                        mConnectivityModuleConnector, mTestClock);
         // Verify controller is not automatically started
         assertFalse(controller.mIsEnabled);
         if (withPackagesReady) {
@@ -801,7 +807,8 @@
     private static class TestObserver implements PackageHealthObserver {
         private final String mName;
         private int mImpact;
-        final List<String> mFailedPackages = new ArrayList<>();
+        final List<String> mHealthCheckFailedPackages = new ArrayList<>();
+        final List<String> mMitigatedPackages = new ArrayList<>();
 
         TestObserver(String name) {
             mName = name;
@@ -814,11 +821,12 @@
         }
 
         public int onHealthCheckFailed(VersionedPackage versionedPackage) {
+            mHealthCheckFailedPackages.add(versionedPackage.getPackageName());
             return mImpact;
         }
 
         public boolean execute(VersionedPackage versionedPackage) {
-            mFailedPackages.add(versionedPackage.getPackageName());
+            mMitigatedPackages.add(versionedPackage.getPackageName());
             return true;
         }
 
@@ -888,4 +896,17 @@
             }
         }
     }
+
+    private static class TestClock implements PackageWatchdog.SystemClock {
+        // Note 0 is special to the internal clock of PackageWatchdog. We need to start from
+        // a non-zero value in order not to disrupt the logic of PackageWatchdog.
+        private long mUpTimeMillis = 1;
+        @Override
+        public long uptimeMillis() {
+            return mUpTimeMillis;
+        }
+        public void moveTimeForward(long milliSeconds) {
+            mUpTimeMillis += milliSeconds;
+        }
+    }
 }
diff --git a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
index b2e5a62..bc98f06 100644
--- a/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
+++ b/tests/RollbackTest/StagedRollbackTest/src/com/android/tests/rollback/host/StagedRollbackTest.java
@@ -28,6 +28,8 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.concurrent.TimeUnit;
+
 /**
  * Runs the staged rollback tests.
  */
@@ -99,9 +101,15 @@
         // crash system_server enough times to trigger a rollback
         crashProcess("system_server", NATIVE_CRASHES_THRESHOLD);
 
-        // Rollback should be committed automatically now
-        // Give time for rollback to be committed
-        assertTrue(getDevice().waitForDeviceNotAvailable(60000));
+        // Rollback should be committed automatically now.
+        // Give time for rollback to be committed. This could take a while,
+        // because we need all of the following to happen:
+        // 1. system_server comes back up and boot completes.
+        // 2. Rollback health observer detects updatable crashing signal.
+        // 3. Staged rollback session becomes ready.
+        // 4. Device actually reboots.
+        // So we give a generous timeout here.
+        assertTrue(getDevice().waitForDeviceNotAvailable(TimeUnit.MINUTES.toMillis(5)));
         getDevice().waitForDeviceAvailable();
 
         // verify rollback committed
diff --git a/tests/net/integration/Android.bp b/tests/net/integration/Android.bp
index 16a68d7..7d9b7b7 100644
--- a/tests/net/integration/Android.bp
+++ b/tests/net/integration/Android.bp
@@ -14,6 +14,39 @@
 // limitations under the License.
 //
 
+android_test {
+    name: "FrameworksNetIntegrationTests",
+    platform_apis: true,
+    certificate: "platform",
+    srcs: [
+        "src/**/*.kt",
+        "src/**/*.aidl",
+    ],
+    libs: [
+        "android.test.mock",
+    ],
+    static_libs: [
+        "TestNetworkStackLib",
+        "androidx.test.ext.junit",
+        "frameworks-net-integration-testutils",
+        "kotlin-reflect",
+        "mockito-target-extended-minus-junit4",
+        "net-tests-utils",
+        "services.core",
+        "services.net",
+        "testables",
+    ],
+    use_embedded_native_libs: true,
+    jni_libs: [
+        // For mockito extended
+        "libdexmakerjvmtiagent",
+        "libstaticjvmtiagent",
+        // android_library does not include JNI libs: include NetworkStack dependencies here
+        "libnativehelper_compat_libc++",
+        "libnetworkstackutilsjni",
+    ],
+}
+
 // Utilities for testing framework code both in integration and unit tests.
 java_library {
     name: "frameworks-net-integration-testutils",
diff --git a/tests/net/integration/AndroidManifest.xml b/tests/net/integration/AndroidManifest.xml
new file mode 100644
index 0000000..91b3cd9
--- /dev/null
+++ b/tests/net/integration/AndroidManifest.xml
@@ -0,0 +1,58 @@
+<?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.
+ */
+-->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          xmlns:tools="http://schemas.android.com/tools"
+          package="com.android.server.net.integrationtests">
+
+    <!-- For ConnectivityService registerReceiverAsUser (receiving broadcasts) -->
+    <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
+    <!-- PermissionMonitor sets network permissions for each user -->
+    <uses-permission android:name="android.permission.MANAGE_USERS" />
+    <!-- ConnectivityService sends notifications to BatteryStats -->
+    <uses-permission android:name="android.permission.UPDATE_DEVICE_STATS" />
+    <application android:debuggable="true">
+        <uses-library android:name="android.test.runner" />
+
+        <!-- This manifest is merged with the base manifest of the real NetworkStack app.
+             Remove the NetworkStackService from the base (real) manifest, and replace with a test
+             service that responds to the same intent -->
+        <service android:name="com.android.server.NetworkStackService" tools:node="remove"/>
+        <service android:name=".TestNetworkStackService"
+                 android:process="com.android.server.net.integrationtests.testnetworkstack">
+            <intent-filter>
+                <action android:name="android.net.INetworkStackConnector.Test"/>
+            </intent-filter>
+        </service>
+        <service android:name=".NetworkStackInstrumentationService"
+                 android:process="com.android.server.net.integrationtests.testnetworkstack">
+            <intent-filter>
+                <action android:name=".INetworkStackInstrumentation"/>
+            </intent-filter>
+        </service>
+        <service tools:replace="android:process"
+                 android:name="com.android.server.connectivity.ipmemorystore.RegularMaintenanceJobService"
+                 android:process="com.android.server.net.integrationtests.testnetworkstack"/>
+
+    </application>
+
+    <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+                     android:targetPackage="com.android.server.net.integrationtests"
+                     android:label="Frameworks Net Integration Tests" />
+
+</manifest>
diff --git a/tests/net/integration/res/values/config.xml b/tests/net/integration/res/values/config.xml
new file mode 100644
index 0000000..2c8046f
--- /dev/null
+++ b/tests/net/integration/res/values/config.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <!--
+    Override configuration for testing. The below settings use the config_ variants, which are
+    normally used by RROs to override the setting with highest priority. -->
+    <integer name="config_captive_portal_dns_probe_timeout">12500</integer>
+    <string name="config_captive_portal_http_url" translatable="false">http://test.android.com</string>
+    <string name="config_captive_portal_https_url" translatable="false">https://secure.test.android.com</string>
+    <string-array name="config_captive_portal_fallback_urls" translatable="false">
+        <item>http://fallback1.android.com</item>
+        <item>http://fallback2.android.com</item>
+    </string-array>
+    <string-array name="config_captive_portal_fallback_probe_specs" translatable="false">
+    </string-array>
+</resources>
diff --git a/tests/net/integration/src/android/net/TestNetworkStackClient.kt b/tests/net/integration/src/android/net/TestNetworkStackClient.kt
new file mode 100644
index 0000000..01eb514
--- /dev/null
+++ b/tests/net/integration/src/android/net/TestNetworkStackClient.kt
@@ -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 android.net
+
+import android.content.ComponentName
+import android.content.Context
+import android.content.Intent
+import android.os.IBinder
+import com.android.server.net.integrationtests.TestNetworkStackService
+import org.mockito.Mockito.any
+import org.mockito.Mockito.spy
+import org.mockito.Mockito.timeout
+import org.mockito.Mockito.verify
+import kotlin.test.fail
+
+const val TEST_ACTION_SUFFIX = ".Test"
+
+class TestNetworkStackClient(context: Context) : NetworkStackClient(TestDependencies(context)) {
+    // TODO: consider switching to TrackRecord for more expressive checks
+    private val lastCallbacks = HashMap<Network, INetworkMonitorCallbacks>()
+
+    private class TestDependencies(private val context: Context) : Dependencies {
+        override fun addToServiceManager(service: IBinder) = Unit
+        override fun checkCallerUid() = Unit
+
+        override fun getConnectivityModuleConnector(): ConnectivityModuleConnector {
+            return ConnectivityModuleConnector { _, _, _, inSystemProcess ->
+                getNetworkStackIntent(inSystemProcess)
+            }.also { it.init(context) }
+        }
+
+        private fun getNetworkStackIntent(inSystemProcess: Boolean): Intent? {
+            // Simulate out-of-system-process config: in-process service not found (null intent)
+            if (inSystemProcess) return null
+            val intent = Intent(INetworkStackConnector::class.qualifiedName + TEST_ACTION_SUFFIX)
+            val serviceName = TestNetworkStackService::class.qualifiedName
+                    ?: fail("TestNetworkStackService name not found")
+            intent.component = ComponentName(context.packageName, serviceName)
+            return intent
+        }
+    }
+
+    // base may be an instance of an inaccessible subclass, so non-spyable.
+    // Use a known open class that delegates to the original instance for all methods except
+    // asBinder. asBinder needs to use its own non-delegated implementation as otherwise it would
+    // return a binder token to a class that is not spied on.
+    open class NetworkMonitorCallbacksWrapper(private val base: INetworkMonitorCallbacks) :
+            INetworkMonitorCallbacks.Stub(), INetworkMonitorCallbacks by base {
+        // asBinder is implemented by both base class and delegate: specify explicitly
+        override fun asBinder(): IBinder {
+            return super.asBinder()
+        }
+    }
+
+    override fun makeNetworkMonitor(network: Network, name: String?, cb: INetworkMonitorCallbacks) {
+        val cbSpy = spy(NetworkMonitorCallbacksWrapper(cb))
+        lastCallbacks[network] = cbSpy
+        super.makeNetworkMonitor(network, name, cbSpy)
+    }
+
+    fun verifyNetworkMonitorCreated(network: Network, timeoutMs: Long) {
+        val cb = lastCallbacks[network]
+                ?: fail("NetworkMonitor for network $network not requested")
+        verify(cb, timeout(timeoutMs)).onNetworkMonitorCreated(any())
+    }
+}
\ No newline at end of file
diff --git a/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt b/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
new file mode 100644
index 0000000..334b26d
--- /dev/null
+++ b/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
@@ -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
+ */
+
+package com.android.server.net.integrationtests
+
+import android.content.ComponentName
+import android.content.Context
+import android.content.Context.BIND_AUTO_CREATE
+import android.content.Context.BIND_IMPORTANT
+import android.content.Intent
+import android.content.ServiceConnection
+import android.net.ConnectivityManager
+import android.net.IDnsResolver
+import android.net.INetd
+import android.net.INetworkPolicyManager
+import android.net.INetworkStatsService
+import android.net.LinkProperties
+import android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET
+import android.net.NetworkCapabilities.TRANSPORT_CELLULAR
+import android.net.NetworkRequest
+import android.net.TestNetworkStackClient
+import android.net.metrics.IpConnectivityLog
+import android.os.ConditionVariable
+import android.os.IBinder
+import android.os.INetworkManagementService
+import android.testing.TestableContext
+import android.util.Log
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.platform.app.InstrumentationRegistry
+import com.android.server.ConnectivityService
+import com.android.server.LocalServices
+import com.android.server.NetworkAgentWrapper
+import com.android.server.TestNetIdManager
+import com.android.server.connectivity.DefaultNetworkMetrics
+import com.android.server.connectivity.IpConnectivityMetrics
+import com.android.server.connectivity.MockableSystemProperties
+import com.android.server.connectivity.ProxyTracker
+import com.android.server.connectivity.Tethering
+import com.android.server.net.NetworkPolicyManagerInternal
+import com.android.testutils.TestableNetworkCallback
+import org.junit.After
+import org.junit.Before
+import org.junit.BeforeClass
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.any
+import org.mockito.Mockito.doNothing
+import org.mockito.Mockito.doReturn
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.spy
+import org.mockito.MockitoAnnotations
+import org.mockito.Spy
+import kotlin.test.assertEquals
+import kotlin.test.assertTrue
+import kotlin.test.fail
+
+const val SERVICE_BIND_TIMEOUT_MS = 5_000L
+const val TEST_TIMEOUT_MS = 1_000L
+
+/**
+ * Test that exercises an instrumented version of ConnectivityService against an instrumented
+ * NetworkStack in a different test process.
+ */
+@RunWith(AndroidJUnit4::class)
+class ConnectivityServiceIntegrationTest {
+    // lateinit used here for mocks as they need to be reinitialized between each test and the test
+    // should crash if they are used before being initialized.
+    @Mock
+    private lateinit var netManager: INetworkManagementService
+    @Mock
+    private lateinit var statsService: INetworkStatsService
+    @Mock
+    private lateinit var policyManager: INetworkPolicyManager
+    @Mock
+    private lateinit var log: IpConnectivityLog
+    @Mock
+    private lateinit var netd: INetd
+    @Mock
+    private lateinit var dnsResolver: IDnsResolver
+    @Mock
+    private lateinit var metricsLogger: IpConnectivityMetrics.Logger
+    @Mock
+    private lateinit var defaultMetrics: DefaultNetworkMetrics
+    @Spy
+    private var context = TestableContext(realContext)
+
+    // lateinit for these three classes under test, as they should be reset to a different instance
+    // for every test but should always be initialized before use (or the test should crash).
+    private lateinit var networkStackClient: TestNetworkStackClient
+    private lateinit var service: ConnectivityService
+    private lateinit var cm: ConnectivityManager
+
+    companion object {
+        // lateinit for this binder token, as it must be initialized before any test code is run
+        // and use of it before init should crash the test.
+        private lateinit var nsInstrumentation: INetworkStackInstrumentation
+        private val bindingCondition = ConditionVariable(false)
+
+        private val realContext get() = InstrumentationRegistry.getInstrumentation().context
+
+        private class InstrumentationServiceConnection : ServiceConnection {
+            override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
+                Log.i("TestNetworkStack", "Service connected")
+                try {
+                    if (service == null) fail("Error binding to NetworkStack instrumentation")
+                    if (::nsInstrumentation.isInitialized) fail("Service already connected")
+                    nsInstrumentation = INetworkStackInstrumentation.Stub.asInterface(service)
+                } finally {
+                    bindingCondition.open()
+                }
+            }
+
+            override fun onServiceDisconnected(name: ComponentName?) = Unit
+        }
+
+        @BeforeClass
+        @JvmStatic
+        fun setUpClass() {
+            val intent = Intent(realContext, NetworkStackInstrumentationService::class.java)
+            intent.action = INetworkStackInstrumentation::class.qualifiedName
+            assertTrue(realContext.bindService(intent, InstrumentationServiceConnection(),
+                    BIND_AUTO_CREATE or BIND_IMPORTANT),
+                    "Error binding to instrumentation service")
+            assertTrue(bindingCondition.block(SERVICE_BIND_TIMEOUT_MS),
+                    "Timed out binding to instrumentation service " +
+                            "after $SERVICE_BIND_TIMEOUT_MS ms")
+        }
+    }
+
+    @Before
+    fun setUp() {
+        MockitoAnnotations.initMocks(this)
+        doReturn(defaultMetrics).`when`(metricsLogger).defaultNetworkMetrics()
+        doNothing().`when`(context).sendStickyBroadcastAsUser(any(), any(), any())
+
+        networkStackClient = TestNetworkStackClient(realContext)
+        networkStackClient.init()
+        networkStackClient.start()
+
+        LocalServices.removeServiceForTest(NetworkPolicyManagerInternal::class.java)
+        LocalServices.addService(NetworkPolicyManagerInternal::class.java,
+                mock(NetworkPolicyManagerInternal::class.java))
+
+        service = TestConnectivityService(makeDependencies())
+        cm = ConnectivityManager(context, service)
+        context.addMockSystemService(Context.CONNECTIVITY_SERVICE, cm)
+
+        service.systemReady()
+    }
+
+    private inner class TestConnectivityService(deps: Dependencies) : ConnectivityService(
+            context, netManager, statsService, policyManager, dnsResolver, log, netd, deps)
+
+    private fun makeDependencies(): ConnectivityService.Dependencies {
+        val deps = spy(ConnectivityService.Dependencies())
+        doReturn(networkStackClient).`when`(deps).networkStack
+        doReturn(metricsLogger).`when`(deps).metricsLogger
+        doReturn(mock(Tethering::class.java)).`when`(deps).makeTethering(
+                any(), any(), any(), any(), any())
+        doReturn(mock(ProxyTracker::class.java)).`when`(deps).makeProxyTracker(any(), any())
+        doReturn(mock(MockableSystemProperties::class.java)).`when`(deps).systemProperties
+        doReturn(TestNetIdManager()).`when`(deps).makeNetIdManager()
+        return deps
+    }
+
+    @After
+    fun tearDown() {
+        nsInstrumentation.clearAllState()
+    }
+
+    @Test
+    fun testValidation() {
+        val request = NetworkRequest.Builder()
+                .clearCapabilities()
+                .addCapability(NET_CAPABILITY_INTERNET)
+                .build()
+        val testCallback = TestableNetworkCallback()
+
+        cm.registerNetworkCallback(request, testCallback)
+        nsInstrumentation.addHttpResponse(HttpResponse(
+                "http://test.android.com",
+                responseCode = 204, contentLength = 42, redirectUrl = null))
+        nsInstrumentation.addHttpResponse(HttpResponse(
+                "https://secure.test.android.com",
+                responseCode = 204, contentLength = 42, redirectUrl = null))
+
+        val na = NetworkAgentWrapper(TRANSPORT_CELLULAR, LinkProperties(), context)
+        networkStackClient.verifyNetworkMonitorCreated(na.network, TEST_TIMEOUT_MS)
+
+        na.addCapability(NET_CAPABILITY_INTERNET)
+        na.connect()
+
+        testCallback.expectAvailableThenValidatedCallbacks(na.network, TEST_TIMEOUT_MS)
+        assertEquals(2, nsInstrumentation.getRequestUrls().size)
+    }
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/wm/SurfaceFactory.java b/tests/net/integration/src/com/android/server/net/integrationtests/HttpResponse.aidl
similarity index 70%
copy from services/core/java/com/android/server/wm/SurfaceFactory.java
copy to tests/net/integration/src/com/android/server/net/integrationtests/HttpResponse.aidl
index 076b7df..9a2bcfe 100644
--- a/services/core/java/com/android/server/wm/SurfaceFactory.java
+++ b/tests/net/integration/src/com/android/server/net/integrationtests/HttpResponse.aidl
@@ -11,17 +11,9 @@
  * 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.wm;
+package com.android.server.net.integrationtests;
 
-import android.view.Surface;
-
-/**
- * Helper class to inject custom {@link Surface} objects into window manager.
- */
-interface SurfaceFactory {
-    Surface make();
-};
-
+parcelable HttpResponse;
\ No newline at end of file
diff --git a/tests/net/integration/src/com/android/server/net/integrationtests/HttpResponse.kt b/tests/net/integration/src/com/android/server/net/integrationtests/HttpResponse.kt
new file mode 100644
index 0000000..45073d8
--- /dev/null
+++ b/tests/net/integration/src/com/android/server/net/integrationtests/HttpResponse.kt
@@ -0,0 +1,44 @@
+/*
+ * 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.net.integrationtests
+
+import android.os.Parcel
+import android.os.Parcelable
+
+data class HttpResponse(
+    val requestUrl: String,
+    val responseCode: Int,
+    val contentLength: Long,
+    val redirectUrl: String?
+) : Parcelable {
+    constructor(p: Parcel): this(p.readString(), p.readInt(), p.readLong(), p.readString())
+
+    override fun writeToParcel(dest: Parcel, flags: Int) {
+        with(dest) {
+            writeString(requestUrl)
+            writeInt(responseCode)
+            writeLong(contentLength)
+            writeString(redirectUrl)
+        }
+    }
+
+    override fun describeContents() = 0
+    companion object CREATOR : Parcelable.Creator<HttpResponse> {
+        override fun createFromParcel(source: Parcel) = HttpResponse(source)
+        override fun newArray(size: Int) = arrayOfNulls<HttpResponse?>(size)
+    }
+}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/wm/SurfaceFactory.java b/tests/net/integration/src/com/android/server/net/integrationtests/INetworkStackInstrumentation.aidl
similarity index 65%
copy from services/core/java/com/android/server/wm/SurfaceFactory.java
copy to tests/net/integration/src/com/android/server/net/integrationtests/INetworkStackInstrumentation.aidl
index 076b7df..efc58ad 100644
--- a/services/core/java/com/android/server/wm/SurfaceFactory.java
+++ b/tests/net/integration/src/com/android/server/net/integrationtests/INetworkStackInstrumentation.aidl
@@ -11,17 +11,15 @@
  * 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.wm;
+package com.android.server.net.integrationtests;
 
-import android.view.Surface;
+import com.android.server.net.integrationtests.HttpResponse;
 
-/**
- * Helper class to inject custom {@link Surface} objects into window manager.
- */
-interface SurfaceFactory {
-    Surface make();
-};
-
+interface INetworkStackInstrumentation {
+    void clearAllState();
+    void addHttpResponse(in HttpResponse response);
+    List<String> getRequestUrls();
+}
\ No newline at end of file
diff --git a/tests/net/integration/src/com/android/server/net/integrationtests/NetworkStackInstrumentationService.kt b/tests/net/integration/src/com/android/server/net/integrationtests/NetworkStackInstrumentationService.kt
new file mode 100644
index 0000000..4827d29
--- /dev/null
+++ b/tests/net/integration/src/com/android/server/net/integrationtests/NetworkStackInstrumentationService.kt
@@ -0,0 +1,81 @@
+/*
+ * 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.net.integrationtests
+
+import android.app.Service
+import android.content.Intent
+import java.net.URL
+import java.util.Collections
+import java.util.concurrent.ConcurrentHashMap
+import java.util.concurrent.ConcurrentLinkedQueue
+import kotlin.collections.ArrayList
+import kotlin.test.fail
+
+/**
+ * An instrumentation interface for the NetworkStack that allows controlling behavior to
+ * facilitate integration tests.
+ */
+class NetworkStackInstrumentationService : Service() {
+    override fun onBind(intent: Intent) = InstrumentationConnector.asBinder()
+
+    object InstrumentationConnector : INetworkStackInstrumentation.Stub() {
+        private val httpResponses = ConcurrentHashMap<String, ConcurrentLinkedQueue<HttpResponse>>()
+                .run {
+                    withDefault { key -> getOrPut(key) { ConcurrentLinkedQueue() } }
+                }
+        private val httpRequestUrls = Collections.synchronizedList(ArrayList<String>())
+
+        /**
+         * Called when an HTTP request is being processed by NetworkMonitor. Returns the response
+         * that should be simulated.
+         */
+        fun processRequest(url: URL): HttpResponse {
+            val strUrl = url.toString()
+            httpRequestUrls.add(strUrl)
+            return httpResponses[strUrl]?.poll()
+                    ?: fail("No mocked response for request: $strUrl. " +
+                            "Mocked URL keys are: ${httpResponses.keys}")
+        }
+
+        /**
+         * Clear all state of this connector. This is intended for use between two tests, so all
+         * state should be reset as if the connector was just created.
+         */
+        override fun clearAllState() {
+            httpResponses.clear()
+            httpRequestUrls.clear()
+        }
+
+        /**
+         * Add a response to a future HTTP request.
+         *
+         * <p>For any subsequent HTTP/HTTPS query, the first response with a matching URL will be
+         * used to mock the query response.
+         */
+        override fun addHttpResponse(response: HttpResponse) {
+            httpResponses.getValue(response.requestUrl).add(response)
+        }
+
+        /**
+         * Get the ordered list of request URLs that have been sent by NetworkMonitor, and were
+         * answered based on mock responses.
+         */
+        override fun getRequestUrls(): List<String> {
+            return ArrayList(httpRequestUrls)
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/net/integration/src/com/android/server/net/integrationtests/TestNetworkStackService.kt b/tests/net/integration/src/com/android/server/net/integrationtests/TestNetworkStackService.kt
new file mode 100644
index 0000000..8e4a9dd
--- /dev/null
+++ b/tests/net/integration/src/com/android/server/net/integrationtests/TestNetworkStackService.kt
@@ -0,0 +1,95 @@
+/*
+ * 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.net.integrationtests
+
+import android.app.Service
+import android.content.Context
+import android.content.Intent
+import android.net.INetworkMonitorCallbacks
+import android.net.Network
+import android.net.metrics.IpConnectivityLog
+import android.net.util.SharedLog
+import android.os.IBinder
+import com.android.networkstack.metrics.DataStallStatsUtils
+import com.android.server.NetworkStackService.NetworkMonitorConnector
+import com.android.server.NetworkStackService.NetworkStackConnector
+import com.android.server.connectivity.NetworkMonitor
+import com.android.server.net.integrationtests.NetworkStackInstrumentationService.InstrumentationConnector
+import org.mockito.Mockito.doReturn
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.spy
+import java.net.HttpURLConnection
+import java.net.URL
+import java.net.URLConnection
+
+private const val TEST_NETID = 42
+
+/**
+ * Android service that can return an [android.net.INetworkStackConnector] which can be instrumented
+ * through [NetworkStackInstrumentationService].
+ * Useful in tests to create test instrumented NetworkStack components that can receive
+ * instrumentation commands through [NetworkStackInstrumentationService].
+ */
+class TestNetworkStackService : Service() {
+    override fun onBind(intent: Intent): IBinder = TestNetworkStackConnector(makeTestContext())
+
+    private fun makeTestContext() = spy(applicationContext).also {
+        doReturn(mock(IBinder::class.java)).`when`(it).getSystemService(Context.NETD_SERVICE)
+    }
+
+    private class TestPermissionChecker : NetworkStackConnector.PermissionChecker() {
+        override fun enforceNetworkStackCallingPermission() = Unit
+    }
+
+    private class NetworkMonitorDeps(private val privateDnsBypassNetwork: Network) :
+            NetworkMonitor.Dependencies() {
+        override fun getPrivateDnsBypassNetwork(network: Network?) = privateDnsBypassNetwork
+        override fun sendNetworkConditionsBroadcast(context: Context, broadcast: Intent) = Unit
+    }
+
+    private inner class TestNetworkStackConnector(context: Context) :
+            NetworkStackConnector(context, TestPermissionChecker()) {
+
+        private val network = Network(TEST_NETID)
+        private val privateDnsBypassNetwork = TestNetwork(TEST_NETID)
+
+        private inner class TestNetwork(netId: Int) : Network(netId) {
+            override fun openConnection(url: URL): URLConnection {
+                val response = InstrumentationConnector.processRequest(url)
+
+                val connection = mock(HttpURLConnection::class.java)
+                doReturn(response.responseCode).`when`(connection).responseCode
+                doReturn(response.contentLength).`when`(connection).contentLengthLong
+                doReturn(response.redirectUrl).`when`(connection).getHeaderField("location")
+                return connection
+            }
+        }
+
+        override fun makeNetworkMonitor(
+            network: Network,
+            name: String?,
+            cb: INetworkMonitorCallbacks
+        ) {
+            val nm = NetworkMonitor(this@TestNetworkStackService, cb,
+                    this.network,
+                    mock(IpConnectivityLog::class.java), mock(SharedLog::class.java),
+                    NetworkMonitorDeps(privateDnsBypassNetwork),
+                    mock(DataStallStatsUtils::class.java))
+            cb.onNetworkMonitorCreated(NetworkMonitorConnector(nm, TestPermissionChecker()))
+        }
+    }
+}
\ No newline at end of file
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index caa3203..f2f258a 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -2188,7 +2188,6 @@
     }
 
     @Test
-    @FlakyTest(bugId = 140306320)
     public void testPartialConnectivity() throws Exception {
         // Register network callback.
         NetworkRequest request = new NetworkRequest.Builder()
@@ -2261,9 +2260,8 @@
         // If user accepted partial connectivity network before,
         // NetworkMonitor#setAcceptPartialConnectivity() will be called in
         // ConnectivityService#updateNetworkInfo().
-        waitForIdle();
-        verify(mWiFiNetworkAgent.mNetworkMonitor, times(1)).setAcceptPartialConnectivity();
         callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+        verify(mWiFiNetworkAgent.mNetworkMonitor, times(1)).setAcceptPartialConnectivity();
         callback.expectCallback(CallbackRecord.LOSING, mCellNetworkAgent);
         nc = callback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
         assertFalse(nc.hasCapability(NET_CAPABILITY_PARTIAL_CONNECTIVITY));
@@ -2283,9 +2281,8 @@
         // If user accepted partial connectivity network before,
         // NetworkMonitor#setAcceptPartialConnectivity() will be called in
         // ConnectivityService#updateNetworkInfo().
-        waitForIdle();
-        verify(mWiFiNetworkAgent.mNetworkMonitor, times(1)).setAcceptPartialConnectivity();
         callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+        verify(mWiFiNetworkAgent.mNetworkMonitor, times(1)).setAcceptPartialConnectivity();
         callback.expectCallback(CallbackRecord.LOSING, mCellNetworkAgent);
         assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
         callback.expectCapabilitiesWith(NET_CAPABILITY_PARTIAL_CONNECTIVITY, mWiFiNetworkAgent);
@@ -2308,9 +2305,8 @@
         // valid, because ConnectivityService calls setAcceptPartialConnectivity before it calls
         // notifyNetworkConnected.
         mWiFiNetworkAgent.connectWithPartialValidConnectivity();
-        waitForIdle();
-        verify(mWiFiNetworkAgent.mNetworkMonitor, times(1)).setAcceptPartialConnectivity();
         callback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent);
+        verify(mWiFiNetworkAgent.mNetworkMonitor, times(1)).setAcceptPartialConnectivity();
         callback.expectCallback(CallbackRecord.LOSING, mCellNetworkAgent);
         callback.expectCapabilitiesWith(
                 NET_CAPABILITY_PARTIAL_CONNECTIVITY | NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent);
diff --git a/tests/net/java/com/android/server/NetIdManagerTest.kt b/tests/net/java/com/android/server/NetIdManagerTest.kt
new file mode 100644
index 0000000..045f89f
--- /dev/null
+++ b/tests/net/java/com/android/server/NetIdManagerTest.kt
@@ -0,0 +1,53 @@
+/*
+ * 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
+
+import androidx.test.filters.SmallTest
+import androidx.test.runner.AndroidJUnit4
+import com.android.server.NetIdManager.MIN_NET_ID
+import com.android.testutils.ExceptionUtils.ThrowingRunnable
+import com.android.testutils.assertThrows
+import org.junit.Test
+import org.junit.runner.RunWith
+import kotlin.test.assertEquals
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class NetIdManagerTest {
+    @Test
+    fun testReserveReleaseNetId() {
+        val manager = NetIdManager(MIN_NET_ID + 4)
+        assertEquals(MIN_NET_ID, manager.reserveNetId())
+        assertEquals(MIN_NET_ID + 1, manager.reserveNetId())
+        assertEquals(MIN_NET_ID + 2, manager.reserveNetId())
+        assertEquals(MIN_NET_ID + 3, manager.reserveNetId())
+
+        manager.releaseNetId(MIN_NET_ID + 1)
+        manager.releaseNetId(MIN_NET_ID + 3)
+        // IDs only loop once there is no higher ID available
+        assertEquals(MIN_NET_ID + 4, manager.reserveNetId())
+        assertEquals(MIN_NET_ID + 1, manager.reserveNetId())
+        assertEquals(MIN_NET_ID + 3, manager.reserveNetId())
+        assertThrows(IllegalStateException::class.java, ThrowingRunnable { manager.reserveNetId() })
+        manager.releaseNetId(MIN_NET_ID + 5)
+        // Still no ID available: MIN_NET_ID + 5 was not reserved
+        assertThrows(IllegalStateException::class.java, ThrowingRunnable { manager.reserveNetId() })
+        manager.releaseNetId(MIN_NET_ID + 2)
+        // Throwing an exception still leaves the manager in a working state
+        assertEquals(MIN_NET_ID + 2, manager.reserveNetId())
+    }
+}
\ No newline at end of file
diff --git a/tests/utils/testutils/java/android/view/test/InsetsModeSession.java b/tests/utils/testutils/java/android/view/test/InsetsModeSession.java
index c83dfa4..e05fdce 100644
--- a/tests/utils/testutils/java/android/view/test/InsetsModeSession.java
+++ b/tests/utils/testutils/java/android/view/test/InsetsModeSession.java
@@ -31,7 +31,7 @@
     }
 
     @Override
-    public void close() throws Exception {
+    public void close() {
         ViewRootImpl.sNewInsetsMode = mOldMode;
     }
 }
diff --git a/tools/aapt2/cmd/Compile.cpp b/tools/aapt2/cmd/Compile.cpp
index c7ac438..d50b1de 100644
--- a/tools/aapt2/cmd/Compile.cpp
+++ b/tools/aapt2/cmd/Compile.cpp
@@ -740,7 +740,6 @@
   }
 
   std::unique_ptr<io::IFileCollection> file_collection;
-  std::unique_ptr<IArchiveWriter> archive_writer;
 
   // Collect the resources files to compile
   if (options_.res_dir && options_.res_zip) {
@@ -761,8 +760,6 @@
       context.GetDiagnostics()->Error(DiagMessage(options_.res_dir.value()) << err);
       return 1;
     }
-
-    archive_writer = CreateZipFileArchiveWriter(context.GetDiagnostics(), options_.output_path);
   } else if (options_.res_zip) {
     if (!args.empty()) {
       context.GetDiagnostics()->Error(DiagMessage() << "files given but --zip specified");
@@ -777,8 +774,6 @@
       context.GetDiagnostics()->Error(DiagMessage(options_.res_zip.value()) << err);
       return 1;
     }
-
-    archive_writer = CreateZipFileArchiveWriter(context.GetDiagnostics(), options_.output_path);
   } else {
     auto collection = util::make_unique<io::FileCollection>();
 
@@ -791,7 +786,14 @@
     }
 
     file_collection = std::move(collection);
+  }
+
+  std::unique_ptr<IArchiveWriter> archive_writer;
+  file::FileType output_file_type = file::GetFileType(options_.output_path);
+  if (output_file_type == file::FileType::kDirectory) {
     archive_writer = CreateDirectoryArchiveWriter(context.GetDiagnostics(), options_.output_path);
+  } else {
+    archive_writer = CreateZipFileArchiveWriter(context.GetDiagnostics(), options_.output_path);
   }
 
   if (!archive_writer) {
diff --git a/tools/aapt2/link/XmlReferenceLinker.cpp b/tools/aapt2/link/XmlReferenceLinker.cpp
index f3be483..c3c16b9 100644
--- a/tools/aapt2/link/XmlReferenceLinker.cpp
+++ b/tools/aapt2/link/XmlReferenceLinker.cpp
@@ -83,6 +83,15 @@
     Attribute default_attribute(android::ResTable_map::TYPE_ANY);
     default_attribute.SetWeak(true);
 
+    // The default orientation of gradients in android Q is different than previous android
+    // versions. Set the android:angle attribute to "0" to ensure that the default gradient
+    // orientation will remain left-to-right in android Q.
+    if (el->name == "gradient" && context_->GetMinSdkVersion() <= SDK_Q) {
+      if (!el->FindAttribute(xml::kSchemaAndroid, "angle")) {
+        el->attributes.push_back(xml::Attribute{xml::kSchemaAndroid, "angle", "0"});
+      }
+    }
+
     const Source source = source_.WithLine(el->line_number);
     for (xml::Attribute& attr : el->attributes) {
       // If the attribute has no namespace, interpret values as if
diff --git a/tools/aapt2/link/XmlReferenceLinker_test.cpp b/tools/aapt2/link/XmlReferenceLinker_test.cpp
index ef99355..0ce2e50 100644
--- a/tools/aapt2/link/XmlReferenceLinker_test.cpp
+++ b/tools/aapt2/link/XmlReferenceLinker_test.cpp
@@ -47,6 +47,8 @@
                                             test::AttributeBuilder()
                                                 .SetTypeMask(android::ResTable_map::TYPE_STRING)
                                                 .Build())
+                           .AddPublicSymbol("android:attr/angle", ResourceId(0x01010004),
+                                            test::AttributeBuilder().Build())
 
                            // Add one real symbol that was introduces in v21
                            .AddPublicSymbol("android:attr/colorAccent", ResourceId(0x01010435),
@@ -75,7 +77,7 @@
   }
 
  protected:
-  std::unique_ptr<IAaptContext> context_;
+  std::unique_ptr<test::Context> context_;
 };
 
 TEST_F(XmlReferenceLinkerTest, LinkBasicAttributes) {
@@ -254,4 +256,63 @@
   EXPECT_EQ(make_value(ResourceId(0x7f030000)), ref->id);
 }
 
+
+TEST_F(XmlReferenceLinkerTest, AddAngleOnGradientForAndroidQ) {
+  std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDomForPackageName(context_.get(), R"(
+    <gradient />)");
+
+  XmlReferenceLinker linker;
+  ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
+
+  xml::Element* gradient_el = doc->root.get();
+  ASSERT_THAT(gradient_el, NotNull());
+
+  xml::Attribute* xml_attr = gradient_el->FindAttribute(xml::kSchemaAndroid, "angle");
+  ASSERT_THAT(xml_attr, NotNull());
+  ASSERT_TRUE(xml_attr->compiled_attribute);
+  EXPECT_EQ(make_value(ResourceId(0x01010004)), xml_attr->compiled_attribute.value().id);
+
+  BinaryPrimitive* value = ValueCast<BinaryPrimitive>(xml_attr->compiled_value.get());
+  ASSERT_THAT(value, NotNull());
+  EXPECT_EQ(value->value.dataType, android::Res_value::TYPE_INT_DEC);
+  EXPECT_EQ(value->value.data, 0U);
+}
+
+TEST_F(XmlReferenceLinkerTest, DoNotOverwriteAngleOnGradientForAndroidQ) {
+  std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDomForPackageName(context_.get(), R"(
+  <gradient xmlns:android="http://schemas.android.com/apk/res/android"
+      android:angle="90"/>)");
+
+  XmlReferenceLinker linker;
+  ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
+
+  xml::Element* gradient_el = doc->root.get();
+  ASSERT_THAT(gradient_el, NotNull());
+
+  xml::Attribute* xml_attr = gradient_el->FindAttribute(xml::kSchemaAndroid, "angle");
+  ASSERT_THAT(xml_attr, NotNull());
+  ASSERT_TRUE(xml_attr->compiled_attribute);
+  EXPECT_EQ(make_value(ResourceId(0x01010004)), xml_attr->compiled_attribute.value().id);
+
+  BinaryPrimitive* value = ValueCast<BinaryPrimitive>(xml_attr->compiled_value.get());
+  ASSERT_THAT(value, NotNull());
+  EXPECT_EQ(value->value.dataType, android::Res_value::TYPE_INT_DEC);
+  EXPECT_EQ(value->value.data, 90U);
+}
+
+TEST_F(XmlReferenceLinkerTest, DoNotOverwriteAngleOnGradientForPostAndroidQ) {
+  std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDomForPackageName(context_.get(), R"(
+  <gradient xmlns:android="http://schemas.android.com/apk/res/android" />)");
+  context_->SetMinSdkVersion(30);
+
+  XmlReferenceLinker linker;
+  ASSERT_TRUE(linker.Consume(context_.get(), doc.get()));
+
+  xml::Element* gradient_el = doc->root.get();
+  ASSERT_THAT(gradient_el, NotNull());
+
+  xml::Attribute* xml_attr = gradient_el->FindAttribute(xml::kSchemaAndroid, "angle");
+  ASSERT_THAT(xml_attr, IsNull());
+}
+
 }  // namespace aapt
diff --git a/tools/aapt2/test/Context.h b/tools/aapt2/test/Context.h
index 7e10a59..553c43e 100644
--- a/tools/aapt2/test/Context.h
+++ b/tools/aapt2/test/Context.h
@@ -81,6 +81,10 @@
     return min_sdk_version_;
   }
 
+  void SetMinSdkVersion(int min_sdk_version) {
+    min_sdk_version_ = min_sdk_version;
+  }
+
  const std::set<std::string>& GetSplitNameDependencies() override {
     return split_name_dependencies_;
   }
diff --git a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
index 9d4b837..f8c20111 100644
--- a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
+++ b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
@@ -15,6 +15,7 @@
  */
 package android.net.wifi;
 
+import android.annotation.IntDef;
 import android.annotation.Nullable;
 import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
@@ -23,6 +24,8 @@
 import android.text.TextUtils;
 import android.util.Log;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.nio.charset.StandardCharsets;
 import java.security.PrivateKey;
 import java.security.cert.X509Certificate;
@@ -111,6 +114,48 @@
     /** @hide */
     public static final String CA_CERT_ALIAS_DELIMITER = " ";
 
+    /**
+     * Do not use OCSP stapling (TLS certificate status extension)
+     * @hide
+     */
+    public static final int OCSP_NONE = 0;
+
+    /**
+     * Try to use OCSP stapling, but not require response
+     * @hide
+     */
+    public static final int OCSP_REQUEST_CERT_STATUS = 1;
+
+    /**
+     * Require valid OCSP stapling response
+     * @hide
+     */
+    public static final int OCSP_REQUIRE_CERT_STATUS = 2;
+
+    /**
+     * Require valid OCSP stapling response for all not-trusted certificates in the server
+     * certificate chain
+     * @hide
+     */
+    public static final int OCSP_REQUIRE_ALL_NON_TRUSTED_CERTS_STATUS = 3;
+
+    /** @hide */
+    @IntDef(prefix = {"OCSP_"}, value = {
+            OCSP_NONE,
+            OCSP_REQUEST_CERT_STATUS,
+            OCSP_REQUIRE_CERT_STATUS,
+            OCSP_REQUIRE_ALL_NON_TRUSTED_CERTS_STATUS
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Ocsp {
+    }
+
+    /**
+     * Whether to use/require OCSP (Online Certificate Status Protocol) to check server certificate.
+     * @hide
+     */
+    private @Ocsp int mOcsp = OCSP_NONE;
+
     // Fields to copy verbatim from wpa_supplicant.
     private static final String[] SUPPLICANT_CONFIG_KEYS = new String[] {
             IDENTITY_KEY,
@@ -185,6 +230,7 @@
         mPhase2Method = source.mPhase2Method;
         mIsAppInstalledDeviceKeyAndCert = source.mIsAppInstalledDeviceKeyAndCert;
         mIsAppInstalledCaCert = source.mIsAppInstalledCaCert;
+        mOcsp = source.mOcsp;
     }
 
     /**
@@ -230,6 +276,7 @@
         ParcelUtil.writeCertificates(dest, mClientCertificateChain);
         dest.writeBoolean(mIsAppInstalledDeviceKeyAndCert);
         dest.writeBoolean(mIsAppInstalledCaCert);
+        dest.writeInt(mOcsp);
     }
 
     public static final @android.annotation.NonNull Creator<WifiEnterpriseConfig> CREATOR =
@@ -251,6 +298,7 @@
                     enterpriseConfig.mClientCertificateChain = ParcelUtil.readCertificates(in);
                     enterpriseConfig.mIsAppInstalledDeviceKeyAndCert = in.readBoolean();
                     enterpriseConfig.mIsAppInstalledCaCert = in.readBoolean();
+                    enterpriseConfig.mOcsp = in.readInt();
                     return enterpriseConfig;
                 }
 
@@ -1141,6 +1189,7 @@
         if (mPhase2Method > 0 && mPhase2Method < Phase2.strings.length) {
             sb.append("phase2_method: ").append(Phase2.strings[mPhase2Method]).append("\n");
         }
+        sb.append(" ocsp: ").append(mOcsp).append("\n");
         return sb.toString();
     }
 
@@ -1190,4 +1239,28 @@
     public boolean isAppInstalledCaCert() {
         return mIsAppInstalledCaCert;
     }
+
+    /**
+     * Set the ocsp type.
+     * @param  ocsp is one {@link ##OCSP_NONE}, {@link #OCSP_REQUEST_CERT_STATUS},
+     *                   {@link #OCSP_REQUIRE_CERT_STATUS} or
+     *                   {@link #OCSP_REQUIRE_ALL_NON_TRUSTED_CERTS_STATUS}
+     * @hide
+     */
+    public void setOcsp(@Ocsp int ocsp) {
+        if (ocsp >= OCSP_NONE && ocsp <= OCSP_REQUIRE_ALL_NON_TRUSTED_CERTS_STATUS) {
+            mOcsp = ocsp;
+        } else {
+            throw new IllegalArgumentException("Invalid OCSP type.");
+        }
+    }
+
+    /**
+     * Get the ocsp type.
+     * @return ocsp type
+     * @hide
+     */
+    public @Ocsp int getOcsp() {
+        return mOcsp;
+    }
 }
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 6bf7bfb9..a37de00 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -52,12 +52,14 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.WorkSource;
+import android.text.TextUtils;
 import android.util.Log;
 import android.util.Pair;
 import android.util.SparseArray;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.AsyncChannel;
 import com.android.internal.util.Protocol;
 import com.android.server.net.NetworkPinner;
@@ -1821,6 +1823,7 @@
         try {
             IWifiManager iWifiManager = getIWifiManager();
             if (iWifiManager == null) {
+                if (config == null) return;
                 throw new RemoteException("Wifi service is not running");
             }
             if (!iWifiManager.addOrUpdatePasspointConfiguration(
@@ -1849,6 +1852,7 @@
         try {
             IWifiManager iWifiManager = getIWifiManager();
             if (iWifiManager == null) {
+                if (TextUtils.isEmpty(fqdn)) return;
                 throw new RemoteException("Wifi service is not running");
             }
             if (!iWifiManager.removePasspointConfiguration(
@@ -1899,6 +1903,7 @@
         try {
             IWifiManager iWifiManager = getIWifiManager();
             if (iWifiManager == null) {
+                if (bssid == 0L || TextUtils.isEmpty(fileName)) return;
                 throw new RemoteException("Wifi service is not running");
             }
             iWifiManager.queryPasspointIcon(bssid, fileName);
@@ -1928,6 +1933,8 @@
      * @param holdoff hold off time in milliseconds
      * @param ess set if the hold off pertains to an ESS rather than a BSS
      * @hide
+     *
+     * TODO (140167680): This needs to be removed, the implementation is empty!
      */
     public void deauthenticateNetwork(long holdoff, boolean ess) {
         try {
@@ -2504,6 +2511,7 @@
         try {
             IWifiManager iWifiManager = getIWifiManager();
             if (iWifiManager == null) {
+                if (TextUtils.isEmpty(country)) return;
                 throw new RemoteException("Wifi service is not running");
             }
             iWifiManager.setCountryCode(country);
@@ -2692,6 +2700,7 @@
         try {
             IWifiManager iWifiManager = getIWifiManager();
             if (iWifiManager == null) {
+                if (TextUtils.isEmpty(ifaceName) || mode == IFACE_IP_MODE_UNSPECIFIED) return;
                 throw new RemoteException("Wifi service is not running");
             }
             iWifiManager.updateInterfaceIpState(ifaceName, mode);
@@ -3044,6 +3053,7 @@
         try {
             IWifiManager iWifiManager = getIWifiManager();
             if (iWifiManager == null) {
+                if (remoteIPAddress == null || !enable) return;
                 throw new RemoteException("Wifi service is not running");
             }
             iWifiManager.enableTdls(remoteIPAddress.getHostAddress(), enable);
@@ -3062,6 +3072,7 @@
         try {
             IWifiManager iWifiManager = getIWifiManager();
             if (iWifiManager == null) {
+                if (TextUtils.isEmpty(remoteMacAddress) || !enable) return;
                 throw new RemoteException("Wifi service is not running");
             }
             iWifiManager.enableTdlsWithMacAddress(remoteMacAddress, enable);
@@ -3956,7 +3967,7 @@
             android.Manifest.permission.NETWORK_STACK
     })
     public void disableEphemeralNetwork(String SSID) {
-        if (SSID == null) throw new IllegalArgumentException("SSID cannot be null");
+        if (TextUtils.isEmpty(SSID)) throw new IllegalArgumentException("SSID cannot be null");
         try {
             IWifiManager iWifiManager = getIWifiManager();
             if (iWifiManager == null) {
@@ -4495,6 +4506,7 @@
         try {
             IWifiManager iWifiManager = getIWifiManager();
             if (iWifiManager == null) {
+                if (verbose == 0) return;
                 throw new RemoteException("Wifi service is not running");
             }
             iWifiManager.enableVerboseLogging(verbose);
@@ -4581,6 +4593,7 @@
         try {
             IWifiManager iWifiManager = getIWifiManager();
             if (iWifiManager == null) {
+                if (enabled) return;
                 throw new RemoteException("Wifi service is not running");
             }
             iWifiManager.enableWifiConnectivityManager(enabled);
@@ -4611,6 +4624,7 @@
         try {
             IWifiManager iWifiManager = getIWifiManager();
             if (iWifiManager == null) {
+                if (ArrayUtils.isEmpty(data)) return;
                 throw new RemoteException("Wifi service is not running");
             }
             iWifiManager.restoreBackupData(data);
@@ -4631,6 +4645,7 @@
         try {
             IWifiManager iWifiManager = getIWifiManager();
             if (iWifiManager == null) {
+                if (ArrayUtils.isEmpty(supplicantData) && ArrayUtils.isEmpty(ipConfigData)) return;
                 throw new RemoteException("Wifi service is not running");
             }
             iWifiManager.restoreSupplicantBackupData(supplicantData, ipConfigData);
@@ -4937,6 +4952,7 @@
         try {
             IWifiManager iWifiManager = getIWifiManager();
             if (iWifiManager == null) {
+                if (state == DEVICE_MOBILITY_STATE_UNKNOWN) return;
                 throw new RemoteException("Wifi service is not running");
             }
             iWifiManager.setDeviceMobilityState(state);
diff --git a/wifi/tests/src/android/net/wifi/WifiEnterpriseConfigTest.java b/wifi/tests/src/android/net/wifi/WifiEnterpriseConfigTest.java
index beed666..d8db4ce 100644
--- a/wifi/tests/src/android/net/wifi/WifiEnterpriseConfigTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiEnterpriseConfigTest.java
@@ -23,6 +23,7 @@
 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.net.wifi.WifiEnterpriseConfig.Eap;
 import android.net.wifi.WifiEnterpriseConfig.Phase2;
@@ -343,11 +344,13 @@
         enterpriseConfig.setPassword("*");
         enterpriseConfig.setEapMethod(Eap.TTLS);
         enterpriseConfig.setPhase2Method(Phase2.GTC);
+        enterpriseConfig.setOcsp(WifiEnterpriseConfig.OCSP_REQUIRE_CERT_STATUS);
         mEnterpriseConfig = new WifiEnterpriseConfig();
         mEnterpriseConfig.copyFromExternal(enterpriseConfig, "*");
         assertEquals("TTLS", getSupplicantEapMethod());
         assertEquals("\"autheap=GTC\"", getSupplicantPhase2Method());
         assertNotEquals("*", mEnterpriseConfig.getPassword());
+        assertEquals(enterpriseConfig.getOcsp(), mEnterpriseConfig.getOcsp());
     }
 
     /** Verfies that parceling a WifiEnterpriseConfig preseves method information. */
@@ -487,4 +490,35 @@
         assertFalse(mEnterpriseConfig.isAppInstalledDeviceKeyAndCert());
         assertTrue(mEnterpriseConfig.isAppInstalledCaCert());
     }
+
+    /** Verifies that OCSP value is set correctly. */
+    @Test
+    public void testOcspSetGet() throws Exception {
+        WifiEnterpriseConfig enterpriseConfig = new WifiEnterpriseConfig();
+
+        enterpriseConfig.setOcsp(WifiEnterpriseConfig.OCSP_NONE);
+        assertEquals(WifiEnterpriseConfig.OCSP_NONE, enterpriseConfig.getOcsp());
+
+        enterpriseConfig.setOcsp(WifiEnterpriseConfig.OCSP_REQUIRE_CERT_STATUS);
+        assertEquals(WifiEnterpriseConfig.OCSP_REQUIRE_CERT_STATUS, enterpriseConfig.getOcsp());
+
+        enterpriseConfig.setOcsp(WifiEnterpriseConfig.OCSP_REQUEST_CERT_STATUS);
+        assertEquals(WifiEnterpriseConfig.OCSP_REQUEST_CERT_STATUS, enterpriseConfig.getOcsp());
+
+        enterpriseConfig.setOcsp(WifiEnterpriseConfig.OCSP_REQUIRE_ALL_NON_TRUSTED_CERTS_STATUS);
+        assertEquals(WifiEnterpriseConfig.OCSP_REQUIRE_ALL_NON_TRUSTED_CERTS_STATUS,
+                enterpriseConfig.getOcsp());
+    }
+
+    /** Verifies that an exception is thrown when invalid OCSP is set. */
+    @Test
+    public void testInvalidOcspValue() {
+        WifiEnterpriseConfig enterpriseConfig = new WifiEnterpriseConfig();
+        try {
+            enterpriseConfig.setOcsp(-1);
+            fail("Should raise an IllegalArgumentException here.");
+        } catch (IllegalArgumentException e) {
+            // expected exception.
+        }
+    }
 }