Merge changes Ic0304c29,I7fdb41aa

* changes:
  Add trace based window manager perf test
  Rename StubActivity to PerfTestActivity
diff --git a/Android.bp b/Android.bp
index b64907a..0547481 100644
--- a/Android.bp
+++ b/Android.bp
@@ -414,6 +414,7 @@
         "libcore-platform-compat-config",
         "services-platform-compat-config",
         "media-provider-platform-compat-config",
+        "services-devicepolicy-platform-compat-config",
     ],
     sdk_version: "core_platform",
 }
@@ -1595,4 +1596,4 @@
         "core/java/com/android/internal/util/State.java",
         "core/java/com/android/internal/util/StateMachine.java",
     ],
-}
\ No newline at end of file
+}
diff --git a/api/current.txt b/api/current.txt
index 603dec2..40106b3 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5872,6 +5872,7 @@
     method public android.service.notification.StatusBarNotification[] getActiveNotifications();
     method public android.app.AutomaticZenRule getAutomaticZenRule(String);
     method public java.util.Map<java.lang.String,android.app.AutomaticZenRule> getAutomaticZenRules();
+    method @Nullable public android.app.NotificationManager.Policy getConsolidatedNotificationPolicy();
     method public final int getCurrentInterruptionFilter();
     method public int getImportance();
     method public android.app.NotificationChannel getNotificationChannel(String);
@@ -24651,6 +24652,7 @@
     field public static final int DolbyVisionLevelUhd30 = 64; // 0x40
     field public static final int DolbyVisionLevelUhd48 = 128; // 0x80
     field public static final int DolbyVisionLevelUhd60 = 256; // 0x100
+    field public static final int DolbyVisionProfileDvav110 = 1024; // 0x400
     field public static final int DolbyVisionProfileDvavPen = 2; // 0x2
     field public static final int DolbyVisionProfileDvavPer = 1; // 0x1
     field public static final int DolbyVisionProfileDvavSe = 512; // 0x200
@@ -38280,6 +38282,7 @@
     field public static final String COLUMN_MIME_TYPE = "mime_type";
     field public static final String COLUMN_SIZE = "_size";
     field public static final String COLUMN_SUMMARY = "summary";
+    field public static final int FLAG_DIR_BLOCKS_TREE = 32768; // 0x8000
     field public static final int FLAG_DIR_PREFERS_GRID = 16; // 0x10
     field public static final int FLAG_DIR_PREFERS_LAST_MODIFIED = 32; // 0x20
     field public static final int FLAG_DIR_SUPPORTS_CREATE = 8; // 0x8
@@ -38498,16 +38501,17 @@
 
   public static final class MediaStore.Audio {
     ctor public MediaStore.Audio();
-    method public static String keyFor(String);
+    method @Deprecated @Nullable public static String keyFor(@Nullable String);
   }
 
   public static interface MediaStore.Audio.AlbumColumns {
     field public static final String ALBUM = "album";
     field @Deprecated public static final String ALBUM_ART = "album_art";
     field public static final String ALBUM_ID = "album_id";
-    field public static final String ALBUM_KEY = "album_key";
+    field @Deprecated public static final String ALBUM_KEY = "album_key";
     field public static final String ARTIST = "artist";
     field public static final String ARTIST_ID = "artist_id";
+    field @Deprecated public static final String ARTIST_KEY = "artist_key";
     field public static final String FIRST_YEAR = "minyear";
     field public static final String LAST_YEAR = "maxyear";
     field public static final String NUMBER_OF_SONGS = "numsongs";
@@ -38526,7 +38530,7 @@
 
   public static interface MediaStore.Audio.ArtistColumns {
     field public static final String ARTIST = "artist";
-    field public static final String ARTIST_KEY = "artist_key";
+    field @Deprecated public static final String ARTIST_KEY = "artist_key";
     field public static final String NUMBER_OF_ALBUMS = "number_of_albums";
     field public static final String NUMBER_OF_TRACKS = "number_of_tracks";
   }
@@ -38549,19 +38553,23 @@
   public static interface MediaStore.Audio.AudioColumns extends android.provider.MediaStore.MediaColumns {
     field public static final String ALBUM = "album";
     field public static final String ALBUM_ID = "album_id";
-    field public static final String ALBUM_KEY = "album_key";
+    field @Deprecated public static final String ALBUM_KEY = "album_key";
     field public static final String ARTIST = "artist";
     field public static final String ARTIST_ID = "artist_id";
-    field public static final String ARTIST_KEY = "artist_key";
+    field @Deprecated public static final String ARTIST_KEY = "artist_key";
     field public static final String BOOKMARK = "bookmark";
     field public static final String COMPOSER = "composer";
+    field public static final String GENRE = "genre";
+    field public static final String GENRE_ID = "genre_id";
+    field @Deprecated public static final String GENRE_KEY = "genre_key";
     field public static final String IS_ALARM = "is_alarm";
     field public static final String IS_AUDIOBOOK = "is_audiobook";
     field public static final String IS_MUSIC = "is_music";
     field public static final String IS_NOTIFICATION = "is_notification";
     field public static final String IS_PODCAST = "is_podcast";
     field public static final String IS_RINGTONE = "is_ringtone";
-    field public static final String TITLE_KEY = "title_key";
+    field @Deprecated public static final String TITLE_KEY = "title_key";
+    field public static final String TITLE_RESOURCE_URI = "title_resource_uri";
     field public static final String TRACK = "track";
     field public static final String YEAR = "year";
   }
@@ -44250,6 +44258,7 @@
     field public static final String KEY_DEFAULT_VM_NUMBER_STRING = "default_vm_number_string";
     field public static final String KEY_DIAL_STRING_REPLACE_STRING_ARRAY = "dial_string_replace_string_array";
     field public static final String KEY_DISABLE_CDMA_ACTIVATION_CODE_BOOL = "disable_cdma_activation_code_bool";
+    field public static final String KEY_DISCONNECT_CAUSE_PLAY_BUSYTONE_INT_ARRAY = "disconnect_cause_play_busytone_int_array";
     field public static final String KEY_DISPLAY_HD_AUDIO_PROPERTY_BOOL = "display_hd_audio_property_bool";
     field public static final String KEY_DROP_VIDEO_CALL_WHEN_ANSWERING_AUDIO_CALL_BOOL = "drop_video_call_when_answering_audio_call_bool";
     field public static final String KEY_DTMF_TYPE_ENABLED_BOOL = "dtmf_type_enabled_bool";
@@ -44595,6 +44604,7 @@
     method public int describeContents();
     method public int getAsuLevel();
     method public int getDbm();
+    method public int getEcNo();
     method @IntRange(from=android.telephony.CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN, to=android.telephony.CellSignalStrength.SIGNAL_STRENGTH_GREAT) public int getLevel();
     method public void writeToParcel(android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CellSignalStrengthWcdma> CREATOR;
@@ -45100,6 +45110,7 @@
     method public long getDataLimitBytes();
     method public long getDataUsageBytes();
     method public long getDataUsageTime();
+    method @Nullable public int[] getNetworkTypes();
     method @Nullable public CharSequence getSummary();
     method @Nullable public CharSequence getTitle();
     method public void writeToParcel(android.os.Parcel, int);
@@ -45119,6 +45130,7 @@
     method public static android.telephony.SubscriptionPlan.Builder createRecurring(java.time.ZonedDateTime, java.time.Period);
     method public android.telephony.SubscriptionPlan.Builder setDataLimit(long, int);
     method public android.telephony.SubscriptionPlan.Builder setDataUsage(long, long);
+    method @NonNull public android.telephony.SubscriptionPlan.Builder setNetworkTypes(@Nullable int[]);
     method public android.telephony.SubscriptionPlan.Builder setSummary(@Nullable CharSequence);
     method public android.telephony.SubscriptionPlan.Builder setTitle(@Nullable CharSequence);
   }
diff --git a/api/system-current.txt b/api/system-current.txt
index a0071ad..46413d3 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1359,7 +1359,7 @@
 
   public abstract class Context {
     method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public boolean bindServiceAsUser(@RequiresPermission android.content.Intent, android.content.ServiceConnection, int, android.os.UserHandle);
-    method @NonNull public android.content.Context createContextAsUser(@NonNull android.os.UserHandle);
+    method @NonNull public android.content.Context createContextAsUser(@NonNull android.os.UserHandle, int);
     method public abstract android.content.Context createCredentialProtectedStorageContext();
     method @NonNull public android.content.Context createPackageContextAsUser(@NonNull String, int, @NonNull android.os.UserHandle) throws android.content.pm.PackageManager.NameNotFoundException;
     method @Nullable public abstract java.io.File getPreloadsFileCache();
@@ -1756,6 +1756,7 @@
     field public static final int PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER = 65536; // 0x10000
     field public static final int PROTECTION_FLAG_TELEPHONY = 4194304; // 0x400000
     field public static final int PROTECTION_FLAG_WELLBEING = 131072; // 0x20000
+    field public static final int PROTECTION_FLAG_WIFI = 8388608; // 0x800000
     field @Nullable public final String backgroundPermission;
     field @StringRes public int requestRes;
   }
@@ -7315,7 +7316,7 @@
   public abstract class CellBroadcastService extends android.app.Service {
     ctor public CellBroadcastService();
     method @CallSuper public android.os.IBinder onBind(android.content.Intent);
-    method public abstract void onCdmaCellBroadcastSms(int, byte[]);
+    method public abstract void onCdmaCellBroadcastSms(int, byte[], int);
     method public abstract void onGsmCellBroadcastSms(int, byte[]);
     field public static final String CELL_BROADCAST_SERVICE_INTERFACE = "android.telephony.CellBroadcastService";
   }
@@ -9761,9 +9762,10 @@
     method public final long getUserActivityTimeout();
     method public final void setUserActivityTimeout(long);
     field @RequiresPermission(android.Manifest.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS) public static final int SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS = 524288; // 0x80000
+    field @RequiresPermission(android.Manifest.permission.INTERNAL_SYSTEM_WINDOW) public static final int SYSTEM_FLAG_SHOW_FOR_ALL_USERS = 16; // 0x10
   }
 
-  @IntDef(flag=true, prefix={"SYSTEM_FLAG_"}, value={android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface WindowManager.LayoutParams.SystemFlags {
+  @IntDef(flag=true, prefix={"SYSTEM_FLAG_"}, value={android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS, android.view.WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface WindowManager.LayoutParams.SystemFlags {
   }
 
 }
diff --git a/api/test-current.txt b/api/test-current.txt
index 2ed88ad..9b85b96 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -659,7 +659,7 @@
   }
 
   public abstract class Context {
-    method @NonNull public android.content.Context createContextAsUser(@NonNull android.os.UserHandle);
+    method @NonNull public android.content.Context createContextAsUser(@NonNull android.os.UserHandle, int);
     method @NonNull public android.content.Context createPackageContextAsUser(@NonNull String, int, @NonNull android.os.UserHandle) throws android.content.pm.PackageManager.NameNotFoundException;
     method public abstract android.view.Display getDisplay();
     method public abstract int getDisplayId();
@@ -773,6 +773,7 @@
     field public static final int PROTECTION_FLAG_TELEPHONY = 4194304; // 0x400000
     field public static final int PROTECTION_FLAG_VENDOR_PRIVILEGED = 32768; // 0x8000
     field public static final int PROTECTION_FLAG_WELLBEING = 131072; // 0x20000
+    field public static final int PROTECTION_FLAG_WIFI = 8388608; // 0x800000
     field @Nullable public final String backgroundPermission;
   }
 
diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp
index cb27325..8af925a 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -230,6 +230,7 @@
         "tests/e2e/Anomaly_duration_sum_e2e_test.cpp",
         "tests/e2e/Attribution_e2e_test.cpp",
         "tests/e2e/ConfigTtl_e2e_test.cpp",
+        "tests/e2e/CountMetric_e2e_test.cpp",
         "tests/e2e/DurationMetric_e2e_test.cpp",
         "tests/e2e/GaugeMetric_e2e_pull_test.cpp",
         "tests/e2e/GaugeMetric_e2e_push_test.cpp",
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index ff7416c..6c3dff2 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -16,24 +16,26 @@
 
 #define DEBUG false  // STOPSHIP if true
 #include "Log.h"
-#include "statslog.h"
+
+#include "StatsLogProcessor.h"
 
 #include <android-base/file.h>
 #include <dirent.h>
 #include <frameworks/base/cmds/statsd/src/active_config_list.pb.h>
-#include "StatsLogProcessor.h"
+#include <log/log_event_list.h>
+#include <utils/Errors.h>
+#include <utils/SystemClock.h>
+
 #include "android-base/stringprintf.h"
 #include "external/StatsPullerManager.h"
 #include "guardrail/StatsdStats.h"
 #include "metrics/CountMetricProducer.h"
+#include "state/StateManager.h"
 #include "stats_log_util.h"
 #include "stats_util.h"
+#include "statslog.h"
 #include "storage/StorageManager.h"
 
-#include <log/log_event_list.h>
-#include <utils/Errors.h>
-#include <utils/SystemClock.h>
-
 using namespace android;
 using android::base::StringPrintf;
 using android::util::FIELD_COUNT_REPEATED;
@@ -218,6 +220,8 @@
         onIsolatedUidChangedEventLocked(*event);
     }
 
+    StateManager::getInstance().onLogEvent(*event);
+
     if (mMetricsManagers.empty()) {
         return;
     }
diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h
index 3d002d2..8292a3a 100644
--- a/cmds/statsd/src/StatsLogProcessor.h
+++ b/cmds/statsd/src/StatsLogProcessor.h
@@ -262,6 +262,10 @@
     FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithSameDeactivation);
     FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations);
 
+    FRIEND_TEST(CountMetricE2eTest, TestWithSimpleState);
+    FRIEND_TEST(CountMetricE2eTest, TestWithMappedState);
+    FRIEND_TEST(CountMetricE2eTest, TestWithMultipleStates);
+
     FRIEND_TEST(DurationMetricE2eTest, TestOneBucket);
     FRIEND_TEST(DurationMetricE2eTest, TestTwoBuckets);
     FRIEND_TEST(DurationMetricE2eTest, TestWithActivation);
diff --git a/cmds/statsd/src/anomaly/subscriber_util.cpp b/cmds/statsd/src/anomaly/subscriber_util.cpp
index e09d575..4c30c4c 100644
--- a/cmds/statsd/src/anomaly/subscriber_util.cpp
+++ b/cmds/statsd/src/anomaly/subscriber_util.cpp
@@ -40,7 +40,7 @@
 
     for (const Subscription& subscription : subscriptions) {
         if (subscription.probability_of_informing() < 1
-                && ((float)rand() / RAND_MAX) >= subscription.probability_of_informing()) {
+                && ((float)rand() / (float)RAND_MAX) >= subscription.probability_of_informing()) {
             // Note that due to float imprecision, 0.0 and 1.0 might not truly mean never/always.
             // The config writer was advised to use -0.1 and 1.1 for never/always.
             ALOGI("Fate decided that a subscriber would not be informed.");
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp
index b5c8e35..4a06387 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp
@@ -18,13 +18,15 @@
 #include "Log.h"
 
 #include "CountMetricProducer.h"
-#include "guardrail/StatsdStats.h"
-#include "stats_util.h"
-#include "stats_log_util.h"
 
+#include <inttypes.h>
 #include <limits.h>
 #include <stdlib.h>
 
+#include "guardrail/StatsdStats.h"
+#include "stats_log_util.h"
+#include "stats_util.h"
+
 using android::util::FIELD_COUNT_REPEATED;
 using android::util::FIELD_TYPE_BOOL;
 using android::util::FIELD_TYPE_FLOAT;
@@ -65,16 +67,16 @@
 const int FIELD_ID_START_BUCKET_ELAPSED_MILLIS = 5;
 const int FIELD_ID_END_BUCKET_ELAPSED_MILLIS = 6;
 
-CountMetricProducer::CountMetricProducer(const ConfigKey& key, const CountMetric& metric,
-                                         const int conditionIndex,
-                                         const sp<ConditionWizard>& wizard,
-                                         const int64_t timeBaseNs, const int64_t startTimeNs,
-                                         const unordered_map<int, shared_ptr<Activation>>&
-                                                 eventActivationMap,
-                                         const unordered_map<int, vector<shared_ptr<Activation>>>&
-                                                 eventDeactivationMap)
+CountMetricProducer::CountMetricProducer(
+        const ConfigKey& key, const CountMetric& metric, const int conditionIndex,
+        const sp<ConditionWizard>& wizard, const int64_t timeBaseNs, const int64_t startTimeNs,
+
+        const unordered_map<int, shared_ptr<Activation>>& eventActivationMap,
+        const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap,
+        const vector<int>& slicedStateAtoms,
+        const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap)
     : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, wizard, eventActivationMap,
-                     eventDeactivationMap) {
+                     eventDeactivationMap, slicedStateAtoms, stateGroupMap) {
     if (metric.has_bucket()) {
         mBucketSizeNs =
                 TimeUnitToBucketSizeInMillisGuardrailed(key.GetUid(), metric.bucket()) * 1000000;
@@ -100,6 +102,8 @@
         mConditionSliced = true;
     }
 
+    // TODO(tsaichristine): b/142124705 handle metric state links
+
     flushIfNeededLocked(startTimeNs);
     // Adjust start for partial bucket
     mCurrentBucketStartTimeNs = startTimeNs;
@@ -112,6 +116,12 @@
     VLOG("~CountMetricProducer() called");
 }
 
+void CountMetricProducer::onStateChanged(int atomId, const HashableDimensionKey& primaryKey,
+                                         int oldState, int newState) {
+    VLOG("CountMetric %lld onStateChanged State%d, key %s, %d -> %d", (long long)mMetricId, atomId,
+         primaryKey.toString().c_str(), oldState, newState);
+}
+
 void CountMetricProducer::dumpStatesLocked(FILE* out, bool verbose) const {
     if (mCurrentSlicedCounter == nullptr ||
         mCurrentSlicedCounter->size() == 0) {
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.h b/cmds/statsd/src/metrics/CountMetricProducer.h
index 61913c7..61e0892 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.h
+++ b/cmds/statsd/src/metrics/CountMetricProducer.h
@@ -17,15 +17,16 @@
 #ifndef COUNT_METRIC_PRODUCER_H
 #define COUNT_METRIC_PRODUCER_H
 
-#include <unordered_map>
-
 #include <android/util/ProtoOutputStream.h>
 #include <gtest/gtest_prod.h>
-#include "../anomaly/AnomalyTracker.h"
-#include "../condition/ConditionTracker.h"
-#include "../matchers/matcher_util.h"
+
+#include <unordered_map>
+
 #include "MetricProducer.h"
+#include "anomaly/AnomalyTracker.h"
+#include "condition/ConditionTracker.h"
 #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+#include "matchers/matcher_util.h"
 #include "stats_util.h"
 
 namespace android {
@@ -40,16 +41,20 @@
 
 class CountMetricProducer : public MetricProducer {
 public:
-    CountMetricProducer(const ConfigKey& key, const CountMetric& countMetric,
-                        const int conditionIndex, const sp<ConditionWizard>& wizard,
-                        const int64_t timeBaseNs, const int64_t startTimeNs,
-                        const std::unordered_map<int, std::shared_ptr<Activation>>&
-                                eventActivationMap = {},
-                        const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>&
-                                eventDeactivationMap = {});
+    CountMetricProducer(
+            const ConfigKey& key, const CountMetric& countMetric, const int conditionIndex,
+            const sp<ConditionWizard>& wizard, const int64_t timeBaseNs, const int64_t startTimeNs,
+            const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap = {},
+            const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>&
+                    eventDeactivationMap = {},
+            const vector<int>& slicedStateAtoms = {},
+            const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap = {});
 
     virtual ~CountMetricProducer();
 
+    void onStateChanged(int atomId, const HashableDimensionKey& primaryKey, int oldState,
+                        int newState) override;
+
 protected:
     void onMatchedLogEventInternalLocked(
             const size_t matcherIndex, const MetricDimensionKey& eventKey,
@@ -106,6 +111,7 @@
     FRIEND_TEST(CountMetricProducerTest, TestEventWithAppUpgrade);
     FRIEND_TEST(CountMetricProducerTest, TestEventWithAppUpgradeInNextBucket);
     FRIEND_TEST(CountMetricProducerTest, TestFirstBucket);
+    FRIEND_TEST(CountMetricProducerTest, TestOneWeekTimeUnit);
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index 31b90f3..ab2a1c3 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -69,9 +69,11 @@
         const bool nesting, const sp<ConditionWizard>& wizard,
         const FieldMatcher& internalDimensions, const int64_t timeBaseNs, const int64_t startTimeNs,
         const unordered_map<int, shared_ptr<Activation>>& eventActivationMap,
-        const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap)
+        const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap,
+        const vector<int>& slicedStateAtoms,
+        const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap)
     : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, wizard, eventActivationMap,
-                     eventDeactivationMap),
+                     eventDeactivationMap, slicedStateAtoms, stateGroupMap),
       mAggregationType(metric.aggregation_type()),
       mStartIndex(startIndex),
       mStopIndex(stopIndex),
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h
index 0592b18..7457d7f 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.h
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.h
@@ -38,16 +38,16 @@
 
 class DurationMetricProducer : public MetricProducer {
 public:
-    DurationMetricProducer(const ConfigKey& key, const DurationMetric& durationMetric,
-                           const int conditionIndex, const size_t startIndex,
-                           const size_t stopIndex, const size_t stopAllIndex, const bool nesting,
-                           const sp<ConditionWizard>& wizard,
-                           const FieldMatcher& internalDimensions, const int64_t timeBaseNs,
-                           const int64_t startTimeNs,
-                           const unordered_map<int, shared_ptr<Activation>>&
-                                   eventActivationMap = {},
-                           const unordered_map<int, vector<shared_ptr<Activation>>>&
-                                   eventDeactivationMap = {});
+    DurationMetricProducer(
+            const ConfigKey& key, const DurationMetric& durationMetric, const int conditionIndex,
+            const size_t startIndex, const size_t stopIndex, const size_t stopAllIndex,
+            const bool nesting, const sp<ConditionWizard>& wizard,
+            const FieldMatcher& internalDimensions, const int64_t timeBaseNs,
+            const int64_t startTimeNs,
+            const unordered_map<int, shared_ptr<Activation>>& eventActivationMap = {},
+            const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap = {},
+            const vector<int>& slicedStateAtoms = {},
+            const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap = {});
 
     virtual ~DurationMetricProducer();
 
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp
index a60a916..32eb077 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp
@@ -52,16 +52,15 @@
 const int FIELD_ID_ELAPSED_TIMESTAMP_NANOS = 1;
 const int FIELD_ID_ATOMS = 2;
 
-EventMetricProducer::EventMetricProducer(const ConfigKey& key, const EventMetric& metric,
-                                         const int conditionIndex,
-                                         const sp<ConditionWizard>& wizard,
-                                         const int64_t startTimeNs,
-                                         const unordered_map<int, shared_ptr<Activation>>&
-                                                 eventActivationMap,
-                                         const unordered_map<int, vector<shared_ptr<Activation>>>&
-                                                 eventDeactivationMap)
+EventMetricProducer::EventMetricProducer(
+        const ConfigKey& key, const EventMetric& metric, const int conditionIndex,
+        const sp<ConditionWizard>& wizard, const int64_t startTimeNs,
+        const unordered_map<int, shared_ptr<Activation>>& eventActivationMap,
+        const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap,
+        const vector<int>& slicedStateAtoms,
+        const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap)
     : MetricProducer(metric.id(), key, startTimeNs, conditionIndex, wizard, eventActivationMap,
-                     eventDeactivationMap) {
+                     eventDeactivationMap, slicedStateAtoms, stateGroupMap) {
     if (metric.links().size() > 0) {
         for (const auto& link : metric.links()) {
             Metric2Condition mc;
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.h b/cmds/statsd/src/metrics/EventMetricProducer.h
index aab53c8..dca37e8 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.h
+++ b/cmds/statsd/src/metrics/EventMetricProducer.h
@@ -33,13 +33,14 @@
 
 class EventMetricProducer : public MetricProducer {
 public:
-    EventMetricProducer(const ConfigKey& key, const EventMetric& eventMetric,
-                        const int conditionIndex, const sp<ConditionWizard>& wizard,
-                        const int64_t startTimeNs,
-                        const std::unordered_map<int, std::shared_ptr<Activation>>&
-                                eventActivationMap = {},
-                        const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>&
-                                eventDeactivationMap = {});
+    EventMetricProducer(
+            const ConfigKey& key, const EventMetric& eventMetric, const int conditionIndex,
+            const sp<ConditionWizard>& wizard, const int64_t startTimeNs,
+            const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap = {},
+            const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>&
+                    eventDeactivationMap = {},
+            const vector<int>& slicedStateAtoms = {},
+            const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap = {});
 
     virtual ~EventMetricProducer();
 
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index e409b6fb..d0f88a8 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -74,9 +74,11 @@
         const int atomId, const int64_t timeBaseNs, const int64_t startTimeNs,
         const sp<StatsPullerManager>& pullerManager,
         const unordered_map<int, shared_ptr<Activation>>& eventActivationMap,
-        const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap)
+        const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap,
+        const vector<int>& slicedStateAtoms,
+        const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap)
     : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, wizard, eventActivationMap,
-            eventDeactivationMap),
+                     eventDeactivationMap, slicedStateAtoms, stateGroupMap),
       mWhatMatcherIndex(whatMatcherIndex),
       mEventMatcherWizard(matcherWizard),
       mPullerManager(pullerManager),
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h
index dfe1d56..640a02a 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.h
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h
@@ -56,16 +56,17 @@
 // producer always reports the guage at the earliest time of the bucket when the condition is met.
 class GaugeMetricProducer : public virtual MetricProducer, public virtual PullDataReceiver {
 public:
-    GaugeMetricProducer(const ConfigKey& key, const GaugeMetric& gaugeMetric,
-                        const int conditionIndex, const sp<ConditionWizard>& conditionWizard,
-                        const int whatMatcherIndex,const sp<EventMatcherWizard>& matcherWizard,
-                        const int pullTagId, const int triggerAtomId, const int atomId,
-                        const int64_t timeBaseNs, const int64_t startTimeNs,
-                        const sp<StatsPullerManager>& pullerManager,
-                        const std::unordered_map<int, std::shared_ptr<Activation>>&
-                                eventActivationMap = {},
-                        const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>&
-                                eventDeactivationMap = {});
+    GaugeMetricProducer(
+            const ConfigKey& key, const GaugeMetric& gaugeMetric, const int conditionIndex,
+            const sp<ConditionWizard>& conditionWizard, const int whatMatcherIndex,
+            const sp<EventMatcherWizard>& matcherWizard, const int pullTagId,
+            const int triggerAtomId, const int atomId, const int64_t timeBaseNs,
+            const int64_t startTimeNs, const sp<StatsPullerManager>& pullerManager,
+            const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap = {},
+            const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>&
+                    eventDeactivationMap = {},
+            const vector<int>& slicedStateAtoms = {},
+            const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap = {});
 
     virtual ~GaugeMetricProducer();
 
diff --git a/cmds/statsd/src/metrics/MetricProducer.cpp b/cmds/statsd/src/metrics/MetricProducer.cpp
index 3426a19..2a700ef 100644
--- a/cmds/statsd/src/metrics/MetricProducer.cpp
+++ b/cmds/statsd/src/metrics/MetricProducer.cpp
@@ -45,23 +45,27 @@
         const int conditionIndex, const sp<ConditionWizard>& wizard,
         const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap,
         const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>&
-                eventDeactivationMap)
-        : mMetricId(metricId),
-          mConfigKey(key),
-          mTimeBaseNs(timeBaseNs),
-          mCurrentBucketStartTimeNs(timeBaseNs),
-          mCurrentBucketNum(0),
-          mCondition(initialCondition(conditionIndex)),
-          mConditionTrackerIndex(conditionIndex),
-          mConditionSliced(false),
-          mWizard(wizard),
-          mContainANYPositionInDimensionsInWhat(false),
-          mSliceByPositionALL(false),
-          mHasLinksToAllConditionDimensionsInTracker(false),
-          mEventActivationMap(eventActivationMap),
-          mEventDeactivationMap(eventDeactivationMap),
-          mIsActive(mEventActivationMap.empty()) {
-    }
+                eventDeactivationMap,
+        const vector<int>& slicedStateAtoms,
+        const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap)
+    : mMetricId(metricId),
+      mConfigKey(key),
+      mTimeBaseNs(timeBaseNs),
+      mCurrentBucketStartTimeNs(timeBaseNs),
+      mCurrentBucketNum(0),
+      mCondition(initialCondition(conditionIndex)),
+      mConditionTrackerIndex(conditionIndex),
+      mConditionSliced(false),
+      mWizard(wizard),
+      mContainANYPositionInDimensionsInWhat(false),
+      mSliceByPositionALL(false),
+      mHasLinksToAllConditionDimensionsInTracker(false),
+      mEventActivationMap(eventActivationMap),
+      mEventDeactivationMap(eventDeactivationMap),
+      mIsActive(mEventActivationMap.empty()),
+      mSlicedStateAtoms(slicedStateAtoms),
+      mStateGroupMap(stateGroupMap) {
+}
 
 void MetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, const LogEvent& event) {
     if (!mIsActive) {
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index 1e1eb69..a72de22 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -17,19 +17,19 @@
 #ifndef METRIC_PRODUCER_H
 #define METRIC_PRODUCER_H
 
-#include <shared_mutex>
-
 #include <frameworks/base/cmds/statsd/src/active_config_list.pb.h>
+#include <log/logprint.h>
+#include <utils/RefBase.h>
+
+#include <unordered_map>
+
 #include "HashableDimensionKey.h"
 #include "anomaly/AnomalyTracker.h"
 #include "condition/ConditionWizard.h"
 #include "config/ConfigKey.h"
 #include "matchers/matcher_util.h"
 #include "packages/PackageInfoListener.h"
-
-#include <log/logprint.h>
-#include <utils/RefBase.h>
-#include <unordered_map>
+#include "state/StateListener.h"
 
 namespace android {
 namespace os {
@@ -86,13 +86,15 @@
 // writing the report to dropbox. MetricProducers should respond to package changes as required in
 // PackageInfoListener, but if none of the metrics are slicing by package name, then the update can
 // be a no-op.
-class MetricProducer : public virtual PackageInfoListener {
+class MetricProducer : public virtual PackageInfoListener, public virtual StateListener {
 public:
     MetricProducer(const int64_t& metricId, const ConfigKey& key, const int64_t timeBaseNs,
                    const int conditionIndex, const sp<ConditionWizard>& wizard,
                    const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap,
                    const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>&
-                           eventDeactivationMap);
+                           eventDeactivationMap,
+                   const vector<int>& slicedStateAtoms,
+                   const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap);
 
     virtual ~MetricProducer(){};
 
@@ -151,6 +153,9 @@
         return mConditionSliced;
     };
 
+    void onStateChanged(int atomId, const HashableDimensionKey& primaryKey, int oldState,
+                        int newState){};
+
     // Output the metrics data to [protoOutput]. All metrics reports end with the same timestamp.
     // This method clears all the past buckets.
     void onDumpReport(const int64_t dumpTimeNs,
@@ -230,6 +235,11 @@
         return mBucketSizeNs;
     }
 
+    inline const std::vector<int> getSlicedStateAtoms() {
+        std::lock_guard<std::mutex> lock(mMutex);
+        return mSlicedStateAtoms;
+    }
+
     /* If alert is valid, adds an AnomalyTracker and returns it. If invalid, returns nullptr. */
     virtual sp<AnomalyTracker> addAnomalyTracker(const Alert &alert,
                                                  const sp<AlarmMonitor>& anomalyAlarmMonitor) {
@@ -381,6 +391,16 @@
 
     bool mIsActive;
 
+    // The slice_by_state atom ids defined in statsd_config.
+    std::vector<int> mSlicedStateAtoms;
+
+    // Maps atom ids and state values to group_ids (<atom_id, <value, group_id>>).
+    std::unordered_map<int, std::unordered_map<int, int64_t>> mStateGroupMap;
+
+    FRIEND_TEST(CountMetricE2eTest, TestWithSimpleState);
+    FRIEND_TEST(CountMetricE2eTest, TestWithMappedState);
+    FRIEND_TEST(CountMetricE2eTest, TestWithMultipleStates);
+
     FRIEND_TEST(DurationMetricE2eTest, TestOneBucket);
     FRIEND_TEST(DurationMetricE2eTest, TestTwoBuckets);
     FRIEND_TEST(DurationMetricE2eTest, TestWithActivation);
diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp
index 963205e..7bae4b9 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -15,8 +15,12 @@
  */
 #define DEBUG false  // STOPSHIP if true
 #include "Log.h"
+
 #include "MetricsManager.h"
-#include "statslog.h"
+
+#include <log/logprint.h>
+#include <private/android_filesystem_config.h>
+#include <utils/SystemClock.h>
 
 #include "CountMetricProducer.h"
 #include "condition/CombinationConditionTracker.h"
@@ -25,12 +29,10 @@
 #include "matchers/CombinationLogMatchingTracker.h"
 #include "matchers/SimpleLogMatchingTracker.h"
 #include "metrics_manager_util.h"
-#include "stats_util.h"
+#include "state/StateManager.h"
 #include "stats_log_util.h"
-
-#include <log/logprint.h>
-#include <private/android_filesystem_config.h>
-#include <utils/SystemClock.h>
+#include "stats_util.h"
+#include "statslog.h"
 
 using android::util::FIELD_COUNT_REPEATED;
 using android::util::FIELD_TYPE_INT32;
@@ -149,6 +151,12 @@
 }
 
 MetricsManager::~MetricsManager() {
+    for (auto it : mAllMetricProducers) {
+        for (int atomId : it->getSlicedStateAtoms()) {
+            StateManager::getInstance().unregisterListener(atomId, it);
+        }
+    }
+
     VLOG("~MetricsManager()");
 }
 
diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h
index 3dad614..d184121 100644
--- a/cmds/statsd/src/metrics/MetricsManager.h
+++ b/cmds/statsd/src/metrics/MetricsManager.h
@@ -282,6 +282,10 @@
             TestActivationOnBootMultipleActivationsDifferentActivationTypes);
     FRIEND_TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart);
 
+    FRIEND_TEST(CountMetricE2eTest, TestWithSimpleState);
+    FRIEND_TEST(CountMetricE2eTest, TestWithMappedState);
+    FRIEND_TEST(CountMetricE2eTest, TestWithMultipleStates);
+
     FRIEND_TEST(DurationMetricE2eTest, TestOneBucket);
     FRIEND_TEST(DurationMetricE2eTest, TestTwoBuckets);
     FRIEND_TEST(DurationMetricE2eTest, TestWithActivation);
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index 7fe5a83..6fd0327 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -83,9 +83,11 @@
         const sp<EventMatcherWizard>& matcherWizard, const int pullTagId, const int64_t timeBaseNs,
         const int64_t startTimeNs, const sp<StatsPullerManager>& pullerManager,
         const unordered_map<int, shared_ptr<Activation>>& eventActivationMap,
-        const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap)
+        const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap,
+        const vector<int>& slicedStateAtoms,
+        const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap)
     : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, conditionWizard,
-                     eventActivationMap, eventDeactivationMap),
+                     eventActivationMap, eventDeactivationMap, slicedStateAtoms, stateGroupMap),
       mWhatMatcherIndex(whatMatcherIndex),
       mEventMatcherWizard(matcherWizard),
       mPullerManager(pullerManager),
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h
index d7cd397..206e602 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.h
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.h
@@ -52,15 +52,17 @@
 // - an alarm set to the end of the bucket
 class ValueMetricProducer : public virtual MetricProducer, public virtual PullDataReceiver {
 public:
-    ValueMetricProducer(const ConfigKey& key, const ValueMetric& valueMetric,
-                        const int conditionIndex, const sp<ConditionWizard>& conditionWizard,
-                        const int whatMatcherIndex, const sp<EventMatcherWizard>& matcherWizard,
-                        const int pullTagId, const int64_t timeBaseNs, const int64_t startTimeNs,
-                        const sp<StatsPullerManager>& pullerManager,
-                        const std::unordered_map<int, std::shared_ptr<Activation>>&
-                                eventActivationMap = {},
-                        const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>&
-                                eventDeactivationMap = {});
+    ValueMetricProducer(
+            const ConfigKey& key, const ValueMetric& valueMetric, const int conditionIndex,
+            const sp<ConditionWizard>& conditionWizard, const int whatMatcherIndex,
+            const sp<EventMatcherWizard>& matcherWizard, const int pullTagId,
+            const int64_t timeBaseNs, const int64_t startTimeNs,
+            const sp<StatsPullerManager>& pullerManager,
+            const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap = {},
+            const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>&
+                    eventDeactivationMap = {},
+            const vector<int>& slicedStateAtoms = {},
+            const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap = {});
 
     virtual ~ValueMetricProducer();
 
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp
index 0fee71e..33e162e 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp
@@ -20,24 +20,24 @@
 #include "metrics_manager_util.h"
 #include "MetricProducer.h"
 
-#include "../condition/CombinationConditionTracker.h"
-#include "../condition/SimpleConditionTracker.h"
-#include "../condition/StateConditionTracker.h"
-#include "../external/StatsPullerManager.h"
-#include "../matchers/CombinationLogMatchingTracker.h"
-#include "../matchers/SimpleLogMatchingTracker.h"
-#include "../matchers/EventMatcherWizard.h"
-#include "../metrics/CountMetricProducer.h"
-#include "../metrics/DurationMetricProducer.h"
-#include "../metrics/EventMetricProducer.h"
-#include "../metrics/GaugeMetricProducer.h"
-#include "../metrics/ValueMetricProducer.h"
+#include <inttypes.h>
 
+#include "condition/CombinationConditionTracker.h"
+#include "condition/SimpleConditionTracker.h"
+#include "condition/StateConditionTracker.h"
+#include "external/StatsPullerManager.h"
+#include "matchers/CombinationLogMatchingTracker.h"
+#include "matchers/EventMatcherWizard.h"
+#include "matchers/SimpleLogMatchingTracker.h"
+#include "metrics/CountMetricProducer.h"
+#include "metrics/DurationMetricProducer.h"
+#include "metrics/EventMetricProducer.h"
+#include "metrics/GaugeMetricProducer.h"
+#include "metrics/ValueMetricProducer.h"
+#include "state/StateManager.h"
 #include "stats_util.h"
 #include "statslog.h"
 
-#include <inttypes.h>
-
 using std::set;
 using std::string;
 using std::unordered_map;
@@ -138,6 +138,41 @@
     return true;
 }
 
+// Initializes state data structures for a metric.
+// input:
+// [config]: the input config
+// [stateIds]: the slice_by_state ids for this metric
+// [stateAtomIdMap]: this map contains the mapping from all state ids to atom ids
+// [allStateGroupMaps]: this map contains the mapping from state ids and state
+//                      values to state group ids for all states
+// output:
+// [slicedStateAtoms]: a vector of atom ids of all the slice_by_states
+// [stateGroupMap]: this map should contain the mapping from states ids and state
+//                      values to state group ids for all states that this metric
+//                      is interested in
+bool handleMetricWithStates(
+        const StatsdConfig& config, const ::google::protobuf::RepeatedField<int64_t>& stateIds,
+        const unordered_map<int64_t, int>& stateAtomIdMap,
+        const unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps,
+        vector<int>& slicedStateAtoms,
+        unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap) {
+    for (const auto& stateId : stateIds) {
+        auto it = stateAtomIdMap.find(stateId);
+        if (it == stateAtomIdMap.end()) {
+            ALOGW("cannot find State %" PRId64 " in the config", stateId);
+            return false;
+        }
+        int atomId = it->second;
+        slicedStateAtoms.push_back(atomId);
+
+        auto stateIt = allStateGroupMaps.find(stateId);
+        if (stateIt != allStateGroupMaps.end()) {
+            stateGroupMap[atomId] = stateIt->second;
+        }
+    }
+    return true;
+}
+
 // Validates a metricActivation and populates state.
 // EventActivationMap and EventDeactivationMap are supplied to a MetricProducer
 //      to provide the producer with state about its activators and deactivators.
@@ -342,12 +377,32 @@
     return true;
 }
 
+bool initStates(const StatsdConfig& config, unordered_map<int64_t, int>& stateAtomIdMap,
+                unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps) {
+    for (int i = 0; i < config.state_size(); i++) {
+        const State& state = config.state(i);
+        const int64_t stateId = state.id();
+        stateAtomIdMap[stateId] = state.atom_id();
+
+        const StateMap& stateMap = state.map();
+        for (auto group : stateMap.group()) {
+            for (auto value : group.value()) {
+                allStateGroupMaps[stateId][value] = group.group_id();
+            }
+        }
+    }
+
+    return true;
+}
+
 bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseTimeNs,
                  const int64_t currentTimeNs, UidMap& uidMap,
                  const sp<StatsPullerManager>& pullerManager,
                  const unordered_map<int64_t, int>& logTrackerMap,
                  const unordered_map<int64_t, int>& conditionTrackerMap,
                  const vector<sp<LogMatchingTracker>>& allAtomMatchers,
+                 const unordered_map<int64_t, int>& stateAtomIdMap,
+                 const unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps,
                  vector<sp<ConditionTracker>>& allConditionTrackers,
                  vector<sp<MetricProducer>>& allMetricProducers,
                  unordered_map<int, vector<int>>& conditionToMetricMap,
@@ -398,10 +453,9 @@
 
         int conditionIndex = -1;
         if (metric.has_condition()) {
-            bool good = handleMetricWithConditions(
-                    metric.condition(), metricIndex, conditionTrackerMap, metric.links(),
-                    allConditionTrackers, conditionIndex, conditionToMetricMap);
-            if (!good) {
+            if (!handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap,
+                                            metric.links(), allConditionTrackers, conditionIndex,
+                                            conditionToMetricMap)) {
                 return false;
             }
         } else {
@@ -411,6 +465,18 @@
             }
         }
 
+        std::vector<int> slicedStateAtoms;
+        unordered_map<int, unordered_map<int, int64_t>> stateGroupMap;
+        if (metric.slice_by_state_size() > 0) {
+            if (!handleMetricWithStates(config, metric.slice_by_state(), stateAtomIdMap,
+                                        allStateGroupMaps, slicedStateAtoms, stateGroupMap)) {
+                return false;
+            }
+        }
+
+        // TODO(tsaichristine): add check for unequal number of MetricStateLinks
+        // and slice_by_states
+
         unordered_map<int, shared_ptr<Activation>> eventActivationMap;
         unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap;
         bool success = handleMetricActivation(config, metric.id(), metricIndex,
@@ -421,7 +487,7 @@
 
         sp<MetricProducer> countProducer = new CountMetricProducer(
                 key, metric, conditionIndex, wizard, timeBaseTimeNs, currentTimeNs,
-                eventActivationMap, eventDeactivationMap);
+                eventActivationMap, eventDeactivationMap, slicedStateAtoms, stateGroupMap);
         allMetricProducers.push_back(countProducer);
     }
 
@@ -721,6 +787,13 @@
     }
     for (const auto& it : allMetricProducers) {
         uidMap.addListener(it);
+
+        // Register metrics to StateTrackers
+        for (int atomId : it->getSlicedStateAtoms()) {
+            if (!StateManager::getInstance().registerListener(atomId, it)) {
+                return false;
+            }
+        }
     }
     return true;
 }
@@ -846,6 +919,8 @@
     unordered_map<int64_t, int> logTrackerMap;
     unordered_map<int64_t, int> conditionTrackerMap;
     unordered_map<int64_t, int> metricProducerMap;
+    unordered_map<int64_t, int> stateAtomIdMap;
+    unordered_map<int64_t, unordered_map<int, int64_t>> allStateGroupMaps;
 
     if (!initLogTrackers(config, uidMap, logTrackerMap, allAtomMatchers, allTagIds)) {
         ALOGE("initLogMatchingTrackers failed");
@@ -858,9 +933,13 @@
         ALOGE("initConditionTrackers failed");
         return false;
     }
-
+    if (!initStates(config, stateAtomIdMap, allStateGroupMaps)) {
+        ALOGE("initStates failed");
+        return false;
+    }
     if (!initMetrics(key, config, timeBaseNs, currentTimeNs, uidMap, pullerManager, logTrackerMap,
-                     conditionTrackerMap, allAtomMatchers, allConditionTrackers, allMetricProducers,
+                     conditionTrackerMap, allAtomMatchers, stateAtomIdMap, allStateGroupMaps,
+                     allConditionTrackers, allMetricProducers,
                      conditionToMetricMap, trackerToMetricMap, metricProducerMap,
                      noReportMetricIds, activationAtomTrackerToMetricMap,
                      deactivationAtomTrackerToMetricMap, metricsWithActivation)) {
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.h b/cmds/statsd/src/metrics/metrics_manager_util.h
index 3802948..95b2ab8 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.h
+++ b/cmds/statsd/src/metrics/metrics_manager_util.h
@@ -68,6 +68,17 @@
                     std::unordered_map<int, std::vector<int>>& trackerToConditionMap,
                     std::unordered_map<int, std::vector<MetricConditionLink>>& eventConditionLinks);
 
+// Initialize State maps using State protos in the config. These maps will
+// eventually be passed to MetricProducers to initialize their state info.
+// input:
+// [config]: the input config
+// output:
+// [stateAtomIdMap]: this map should contain the mapping from state ids to atom ids
+// [allStateGroupMaps]: this map should contain the mapping from states ids and state
+//                      values to state group ids for all states
+bool initStates(const StatsdConfig& config, unordered_map<int64_t, int>& stateAtomIdMap,
+                unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps);
+
 // Initialize MetricProducers.
 // input:
 // [key]: the config key that this config belongs to
@@ -75,6 +86,9 @@
 // [timeBaseSec]: start time base for all metrics
 // [logTrackerMap]: LogMatchingTracker name to index mapping from previous step.
 // [conditionTrackerMap]: condition name to index mapping
+// [stateAtomIdMap]: contains the mapping from state ids to atom ids
+// [allStateGroupMaps]: contains the mapping from atom ids and state values to
+//                      state group ids for all states
 // output:
 // [allMetricProducers]: contains the list of sp to the MetricProducers created.
 // [conditionToMetricMap]: contains the mapping from condition tracker index to
@@ -87,6 +101,8 @@
         const std::unordered_map<int64_t, int>& conditionTrackerMap,
         const std::unordered_map<int, std::vector<MetricConditionLink>>& eventConditionLinks,
         const vector<sp<LogMatchingTracker>>& allAtomMatchers,
+        const unordered_map<int64_t, int>& stateAtomIdMap,
+        const unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps,
         vector<sp<ConditionTracker>>& allConditionTrackers,
         std::vector<sp<MetricProducer>>& allMetricProducers,
         std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
diff --git a/cmds/statsd/src/state/StateManager.cpp b/cmds/statsd/src/state/StateManager.cpp
index a3059c5..95b2c76 100644
--- a/cmds/statsd/src/state/StateManager.cpp
+++ b/cmds/statsd/src/state/StateManager.cpp
@@ -35,26 +35,25 @@
     }
 }
 
-bool StateManager::registerListener(int stateAtomId, wp<StateListener> listener) {
+bool StateManager::registerListener(int atomId, wp<StateListener> listener) {
     std::lock_guard<std::mutex> lock(mMutex);
 
     // Check if state tracker already exists
-    if (mStateTrackers.find(stateAtomId) == mStateTrackers.end()) {
+    if (mStateTrackers.find(atomId) == mStateTrackers.end()) {
         // Create a new state tracker iff atom is a state atom
-        auto it = android::util::AtomsInfo::kStateAtomsFieldOptions.find(stateAtomId);
+        auto it = android::util::AtomsInfo::kStateAtomsFieldOptions.find(atomId);
         if (it != android::util::AtomsInfo::kStateAtomsFieldOptions.end()) {
-            mStateTrackers[stateAtomId] = new StateTracker(stateAtomId, it->second);
+            mStateTrackers[atomId] = new StateTracker(atomId, it->second);
         } else {
-            ALOGE("StateManager cannot register listener, Atom %d is not a state atom",
-                  stateAtomId);
+            ALOGE("StateManager cannot register listener, Atom %d is not a state atom", atomId);
             return false;
         }
     }
-    mStateTrackers[stateAtomId]->registerListener(listener);
+    mStateTrackers[atomId]->registerListener(listener);
     return true;
 }
 
-void StateManager::unregisterListener(int stateAtomId, wp<StateListener> listener) {
+void StateManager::unregisterListener(int atomId, wp<StateListener> listener) {
     std::unique_lock<std::mutex> lock(mMutex);
 
     // Hold the sp<> until the lock is released so that ~StateTracker() is
@@ -62,7 +61,7 @@
     sp<StateTracker> toRemove;
 
     // Unregister listener from correct StateTracker
-    auto it = mStateTrackers.find(stateAtomId);
+    auto it = mStateTrackers.find(atomId);
     if (it != mStateTrackers.end()) {
         it->second->unregisterListener(listener);
 
@@ -73,15 +72,15 @@
         }
     } else {
         ALOGE("StateManager cannot unregister listener, StateTracker for atom %d does not exist",
-              stateAtomId);
+              atomId);
     }
     lock.unlock();
 }
 
-int StateManager::getState(int stateAtomId, const HashableDimensionKey& key) {
+int StateManager::getStateValue(int atomId, const HashableDimensionKey& key) {
     std::lock_guard<std::mutex> lock(mMutex);
-    if (mStateTrackers.find(stateAtomId) != mStateTrackers.end()) {
-        return mStateTrackers[stateAtomId]->getState(key);
+    if (mStateTrackers.find(atomId) != mStateTrackers.end()) {
+        return mStateTrackers[atomId]->getStateValue(key);
     }
 
     return StateTracker::kStateUnknown;
diff --git a/cmds/statsd/src/state/StateManager.h b/cmds/statsd/src/state/StateManager.h
index ce60f14..89ee6c0 100644
--- a/cmds/statsd/src/state/StateManager.h
+++ b/cmds/statsd/src/state/StateManager.h
@@ -15,10 +15,11 @@
  */
 #pragma once
 
-//#include <utils/Log.h>
+#include <gtest/gtest_prod.h>
+#include <inttypes.h>
 #include <utils/RefBase.h>
-#include "HashableDimensionKey.h"
 
+#include "HashableDimensionKey.h"
 #include "state/StateListener.h"
 #include "state/StateTracker.h"
 
@@ -38,29 +39,29 @@
     // Notifies the correct StateTracker of an event.
     void onLogEvent(const LogEvent& event);
 
-    // Returns true if stateAtomId is the id of a state atom and notifies the
-    // correct StateTracker to register the listener. If the correct
-    // StateTracker does not exist, a new StateTracker is created.
-    bool registerListener(int stateAtomId, wp<StateListener> listener);
+    // Returns true if atomId is being tracked and is associated with a state
+    // atom. StateManager notifies the correct StateTracker to register listener.
+    // If the correct StateTracker does not exist, a new StateTracker is created.
+    bool registerListener(int atomId, wp<StateListener> listener);
 
     // Notifies the correct StateTracker to unregister a listener
     // and removes the tracker if it no longer has any listeners.
-    void unregisterListener(int stateAtomId, wp<StateListener> listener);
+    void unregisterListener(int atomId, wp<StateListener> listener);
 
-    // Queries the correct StateTracker for the state that is mapped to the given
-    // query key.
+    // Queries the correct StateTracker for the original/un-mapped state value
+    // that is mapped to the given query key.
     // If the StateTracker doesn't exist, returns StateTracker::kStateUnknown.
-    int getState(int stateAtomId, const HashableDimensionKey& queryKey);
+    int getStateValue(int atomId, const HashableDimensionKey& queryKey);
 
     inline int getStateTrackersCount() {
         std::lock_guard<std::mutex> lock(mMutex);
         return mStateTrackers.size();
     }
 
-    inline int getListenersCount(int stateAtomId) {
+    inline int getListenersCount(int atomId) {
         std::lock_guard<std::mutex> lock(mMutex);
-        if (mStateTrackers.find(stateAtomId) != mStateTrackers.end()) {
-            return mStateTrackers[stateAtomId]->getListenersCount();
+        if (mStateTrackers.find(atomId) != mStateTrackers.end()) {
+            return mStateTrackers[atomId]->getListenersCount();
         }
         return -1;
     }
diff --git a/cmds/statsd/src/state/StateTracker.cpp b/cmds/statsd/src/state/StateTracker.cpp
index 5a91950..323fc0e 100644
--- a/cmds/statsd/src/state/StateTracker.cpp
+++ b/cmds/statsd/src/state/StateTracker.cpp
@@ -30,13 +30,13 @@
   : mAtomId(atomId),
     mStateField(getSimpleMatcher(atomId, stateAtomInfo.exclusiveField)) {
     // create matcher for each primary field
-    // TODO(tsaichristine): handle when primary field is first uid in chain
+    // TODO(tsaichristine): b/142108433 handle when primary field is first uid in chain
     for (const auto& primary : stateAtomInfo.primaryFields) {
         Matcher matcher = getSimpleMatcher(atomId, primary);
         mPrimaryFields.push_back(matcher);
     }
 
-    // TODO(tsaichristine): set default state, reset state, and nesting
+    // TODO(tsaichristine): b/142108433 set default state, reset state, and nesting
 }
 
 void StateTracker::onLogEvent(const LogEvent& event) {
@@ -96,7 +96,7 @@
     mListeners.erase(listener);
 }
 
-int StateTracker::getState(const HashableDimensionKey& queryKey) const {
+int StateTracker::getStateValue(const HashableDimensionKey& queryKey) const {
     if (queryKey.getValues().size() == mPrimaryFields.size()) {
         auto it = mStateMap.find(queryKey);
         if (it != mStateMap.end()) {
diff --git a/cmds/statsd/src/state/StateTracker.h b/cmds/statsd/src/state/StateTracker.h
index f22706c..cfa9fd8 100644
--- a/cmds/statsd/src/state/StateTracker.h
+++ b/cmds/statsd/src/state/StateTracker.h
@@ -48,7 +48,7 @@
     // Returns the state value mapped to the given query key.
     // If the key isn't mapped to a state or the key size doesn't match the
     // primary key size, the default state is returned.
-    int getState(const HashableDimensionKey& queryKey) const;
+    int getStateValue(const HashableDimensionKey& queryKey) const;
 
     inline int getListenersCount() const {
         return mListeners.size();
diff --git a/cmds/statsd/src/stats_log_util.cpp b/cmds/statsd/src/stats_log_util.cpp
index 67625eb..c22e3cc 100644
--- a/cmds/statsd/src/stats_log_util.cpp
+++ b/cmds/statsd/src/stats_log_util.cpp
@@ -445,6 +445,8 @@
             return 12 * 60 * 60 * 1000LL;
         case ONE_DAY:
             return 24 * 60 * 60 * 1000LL;
+        case ONE_WEEK:
+            return 7 * 24 * 60 * 60 * 1000LL;
         case CTS:
             return 1000;
         case TIME_UNIT_UNSPECIFIED:
diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto
index c107397..c98b2cf 100644
--- a/cmds/statsd/src/statsd_config.proto
+++ b/cmds/statsd/src/statsd_config.proto
@@ -44,6 +44,7 @@
   SIX_HOURS = 7;
   TWELVE_HOURS = 8;
   ONE_DAY = 9;
+  ONE_WEEK = 10;
   CTS = 1000;
 }
 
diff --git a/cmds/statsd/tests/e2e/CountMetric_e2e_test.cpp b/cmds/statsd/tests/e2e/CountMetric_e2e_test.cpp
new file mode 100644
index 0000000..6591d69
--- /dev/null
+++ b/cmds/statsd/tests/e2e/CountMetric_e2e_test.cpp
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include "src/StatsLogProcessor.h"
+#include "src/state/StateManager.h"
+#include "tests/statsd_test_util.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+#ifdef __ANDROID__
+
+TEST(CountMetricE2eTest, TestWithSimpleState) {
+    // Initialize config
+    StatsdConfig config;
+    config.add_allowed_log_source("AID_ROOT");  // LogEvent defaults to UID of root.
+
+    auto syncStartMatcher = CreateSyncStartAtomMatcher();
+    *config.add_atom_matcher() = syncStartMatcher;
+
+    auto state = CreateScreenState();
+    *config.add_state() = state;
+
+    // Create count metric that slices by screen state
+    int64_t metricId = 123456;
+    auto countMetric = config.add_count_metric();
+    countMetric->set_id(metricId);
+    countMetric->set_what(syncStartMatcher.id());
+    countMetric->set_bucket(TimeUnit::ONE_MINUTE);
+    countMetric->add_slice_by_state(state.id());
+
+    // Initialize StatsLogProcessor
+    const int64_t baseTimeNs = 0;                                   // 0:00
+    const int64_t configAddedTimeNs = baseTimeNs + 1 * NS_PER_SEC;  // 0:01
+    const int64_t bucketSizeNs =
+            TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000LL * 1000LL;
+
+    int uid = 12345;
+    int64_t cfgId = 98765;
+    ConfigKey cfgKey(uid, cfgId);
+
+    auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey);
+
+    // Check that StateTrackers were properly initialized.
+    EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
+    EXPECT_EQ(1,
+              StateManager::getInstance().getListenersCount(android::util::SCREEN_STATE_CHANGED));
+
+    // Check that CountMetricProducer was initialized correctly.
+    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+    sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
+    EXPECT_TRUE(metricsManager->isConfigValid());
+    EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1);
+    sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
+    EXPECT_EQ(metricProducer->mSlicedStateAtoms.size(), 1);
+    EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(0), android::util::SCREEN_STATE_CHANGED);
+    EXPECT_EQ(metricProducer->mStateGroupMap.size(), 0);
+}
+
+TEST(CountMetricE2eTest, TestWithMappedState) {
+    // Initialize config
+    StatsdConfig config;
+    config.add_allowed_log_source("AID_ROOT");  // LogEvent defaults to UID of root.
+
+    auto syncStartMatcher = CreateSyncStartAtomMatcher();
+    *config.add_atom_matcher() = syncStartMatcher;
+
+    auto state = CreateScreenStateWithOnOffMap();
+    *config.add_state() = state;
+
+    // Create count metric that slices by screen state with on/off map
+    int64_t metricId = 123456;
+    auto countMetric = config.add_count_metric();
+    countMetric->set_id(metricId);
+    countMetric->set_what(syncStartMatcher.id());
+    countMetric->set_bucket(TimeUnit::ONE_MINUTE);
+    countMetric->add_slice_by_state(state.id());
+
+    // Initialize StatsLogProcessor
+    const int64_t baseTimeNs = 0;                                   // 0:00
+    const int64_t configAddedTimeNs = baseTimeNs + 1 * NS_PER_SEC;  // 0:01
+    const int64_t bucketSizeNs =
+            TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000LL * 1000LL;
+
+    int uid = 12345;
+    int64_t cfgId = 98765;
+    ConfigKey cfgKey(uid, cfgId);
+
+    auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey);
+
+    // Check that StateTrackers were properly initialized.
+    EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
+    EXPECT_EQ(1,
+              StateManager::getInstance().getListenersCount(android::util::SCREEN_STATE_CHANGED));
+
+    // Check that CountMetricProducer was initialized correctly.
+    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+    sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
+    EXPECT_TRUE(metricsManager->isConfigValid());
+    EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1);
+    sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
+    EXPECT_EQ(metricProducer->mSlicedStateAtoms.size(), 1);
+    EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(0), android::util::SCREEN_STATE_CHANGED);
+    EXPECT_EQ(metricProducer->mStateGroupMap.size(), 1);
+
+    StateMap map = state.map();
+    for (auto group : map.group()) {
+        for (auto value : group.value()) {
+            EXPECT_EQ(metricProducer->mStateGroupMap[android::util::SCREEN_STATE_CHANGED][value],
+                      group.group_id());
+        }
+    }
+}
+
+TEST(CountMetricE2eTest, TestWithMultipleStates) {
+    // Initialize config
+    StatsdConfig config;
+    config.add_allowed_log_source("AID_ROOT");  // LogEvent defaults to UID of root.
+
+    auto syncStartMatcher = CreateSyncStartAtomMatcher();
+    *config.add_atom_matcher() = syncStartMatcher;
+
+    auto state1 = CreateScreenStateWithOnOffMap();
+    *config.add_state() = state1;
+    auto state2 = CreateUidProcessState();
+    *config.add_state() = state2;
+
+    // Create count metric that slices by screen state with on/off map
+    int64_t metricId = 123456;
+    auto countMetric = config.add_count_metric();
+    countMetric->set_id(metricId);
+    countMetric->set_what(syncStartMatcher.id());
+    countMetric->set_bucket(TimeUnit::ONE_MINUTE);
+    countMetric->add_slice_by_state(state1.id());
+    countMetric->add_slice_by_state(state2.id());
+
+    // Initialize StatsLogProcessor
+    const int64_t baseTimeNs = 0;                                   // 0:00
+    const int64_t configAddedTimeNs = baseTimeNs + 1 * NS_PER_SEC;  // 0:01
+    const int64_t bucketSizeNs =
+            TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000LL * 1000LL;
+
+    int uid = 12345;
+    int64_t cfgId = 98765;
+    ConfigKey cfgKey(uid, cfgId);
+
+    auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey);
+
+    // Check that StateTrackers were properly initialized.
+    EXPECT_EQ(2, StateManager::getInstance().getStateTrackersCount());
+    EXPECT_EQ(1,
+              StateManager::getInstance().getListenersCount(android::util::SCREEN_STATE_CHANGED));
+    EXPECT_EQ(1, StateManager::getInstance().getListenersCount(
+                         android::util::UID_PROCESS_STATE_CHANGED));
+
+    // Check that CountMetricProducer was initialized correctly.
+    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+    sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
+    EXPECT_TRUE(metricsManager->isConfigValid());
+    EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1);
+    sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0];
+    EXPECT_EQ(metricProducer->mSlicedStateAtoms.size(), 2);
+    EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(0), android::util::SCREEN_STATE_CHANGED);
+    EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(1), android::util::UID_PROCESS_STATE_CHANGED);
+    EXPECT_EQ(metricProducer->mStateGroupMap.size(), 1);
+
+    StateMap map = state1.map();
+    for (auto group : map.group()) {
+        for (auto value : group.value()) {
+            EXPECT_EQ(metricProducer->mStateGroupMap[android::util::SCREEN_STATE_CHANGED][value],
+                      group.group_id());
+        }
+    }
+}
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
diff --git a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
index 839daa4..8915c73 100644
--- a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
@@ -43,8 +43,8 @@
     metric.set_bucket(ONE_MINUTE);
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
-    CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
-                                      5, 600 * NS_PER_SEC + NS_PER_SEC/2);
+    CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, 5,
+                                      600 * NS_PER_SEC + NS_PER_SEC / 2);
     EXPECT_EQ(600500000000, countProducer.mCurrentBucketStartTimeNs);
     EXPECT_EQ(10, countProducer.mCurrentBucketNum);
     EXPECT_EQ(660000000005, countProducer.getCurrentBucketEndTimeNs());
@@ -131,7 +131,8 @@
 
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
-    CountMetricProducer countProducer(kConfigKey, metric, 1, wizard, bucketStartTimeNs, bucketStartTimeNs);
+    CountMetricProducer countProducer(kConfigKey, metric, 1, wizard, bucketStartTimeNs,
+                                      bucketStartTimeNs);
 
     countProducer.onConditionChanged(true, bucketStartTimeNs);
     countProducer.onMatchedLogEvent(1 /*matcher index*/, event1);
@@ -396,6 +397,26 @@
             std::ceil(1.0 * event7.GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec));
 }
 
+TEST(CountMetricProducerTest, TestOneWeekTimeUnit) {
+    CountMetric metric;
+    metric.set_id(1);
+    metric.set_bucket(ONE_WEEK);
+
+    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+
+    int64_t oneDayNs = 24 * 60 * 60 * 1e9;
+    int64_t fiveWeeksNs = 5 * 7 * oneDayNs;
+
+    CountMetricProducer countProducer(
+            kConfigKey, metric, -1 /* meaning no condition */, wizard, oneDayNs, fiveWeeksNs);
+
+    int64_t fiveWeeksOneDayNs = fiveWeeksNs + oneDayNs;
+
+    EXPECT_EQ(fiveWeeksNs, countProducer.mCurrentBucketStartTimeNs);
+    EXPECT_EQ(4, countProducer.mCurrentBucketNum);
+    EXPECT_EQ(fiveWeeksOneDayNs, countProducer.getCurrentBucketEndTimeNs());
+}
+
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
diff --git a/cmds/statsd/tests/state/StateTracker_test.cpp b/cmds/statsd/tests/state/StateTracker_test.cpp
index c89ffea..8d38000 100644
--- a/cmds/statsd/tests/state/StateTracker_test.cpp
+++ b/cmds/statsd/tests/state/StateTracker_test.cpp
@@ -44,7 +44,7 @@
 
     std::vector<Update> updates;
 
-    void onStateChanged(int stateAtomId, const HashableDimensionKey& primaryKey, int oldState,
+    void onStateChanged(int atomId, const HashableDimensionKey& primaryKey, int oldState,
                         int newState) {
         updates.emplace_back(primaryKey, newState);
     }
@@ -82,6 +82,7 @@
     return event;
 }
 
+// Incorrect event - missing fields
 std::shared_ptr<LogEvent> buildIncorrectOverlayEvent(int uid, const std::string& packageName, int state) {
     std::shared_ptr<LogEvent> event =
             std::make_shared<LogEvent>(android::util::OVERLAY_STATE_CHANGED, 1000 /*timestamp*/);
@@ -91,6 +92,18 @@
     event->init();
     return event;
 }
+
+// Incorrect event - exclusive state has wrong type
+std::shared_ptr<LogEvent> buildOverlayEventBadStateType(int uid, const std::string& packageName) {
+    std::shared_ptr<LogEvent> event =
+            std::make_shared<LogEvent>(android::util::OVERLAY_STATE_CHANGED, 1000 /*timestamp*/);
+    event->write((int32_t)uid);
+    event->write(packageName);
+    event->write(true);
+    event->write("string");  // exclusive state: string instead of int
+    event->init();
+    return event;
+}
 // END: build event functions.
 
 // START: get primary key functions
@@ -148,22 +161,22 @@
 
     // Register listener to non-existing StateTracker
     EXPECT_EQ(0, mgr.getStateTrackersCount());
-    mgr.registerListener(android::util::SCREEN_STATE_CHANGED, listener1);
+    EXPECT_TRUE(mgr.registerListener(android::util::SCREEN_STATE_CHANGED, listener1));
     EXPECT_EQ(1, mgr.getStateTrackersCount());
     EXPECT_EQ(1, mgr.getListenersCount(android::util::SCREEN_STATE_CHANGED));
 
     // Register listener to existing StateTracker
-    mgr.registerListener(android::util::SCREEN_STATE_CHANGED, listener2);
+    EXPECT_TRUE(mgr.registerListener(android::util::SCREEN_STATE_CHANGED, listener2));
     EXPECT_EQ(1, mgr.getStateTrackersCount());
     EXPECT_EQ(2, mgr.getListenersCount(android::util::SCREEN_STATE_CHANGED));
 
     // Register already registered listener to existing StateTracker
-    mgr.registerListener(android::util::SCREEN_STATE_CHANGED, listener2);
+    EXPECT_TRUE(mgr.registerListener(android::util::SCREEN_STATE_CHANGED, listener2));
     EXPECT_EQ(1, mgr.getStateTrackersCount());
     EXPECT_EQ(2, mgr.getListenersCount(android::util::SCREEN_STATE_CHANGED));
 
     // Register listener to non-state atom
-    mgr.registerListener(android::util::BATTERY_LEVEL_CHANGED, listener2);
+    EXPECT_FALSE(mgr.registerListener(android::util::BATTERY_LEVEL_CHANGED, listener2));
     EXPECT_EQ(1, mgr.getStateTrackersCount());
 }
 
@@ -227,12 +240,12 @@
 
     // check StateTracker was updated by querying for state
     HashableDimensionKey queryKey = DEFAULT_DIMENSION_KEY;
-    EXPECT_EQ(2, mgr.getState(android::util::SCREEN_STATE_CHANGED, queryKey));
+    EXPECT_EQ(2, mgr.getStateValue(android::util::SCREEN_STATE_CHANGED, queryKey));
 }
 
 /**
  * Test StateManager's onLogEvent and StateListener's onStateChanged correctly
- * updates listener for states with primary keys.
+ * updates listener for states with one primary key.
  */
 TEST(StateTrackerTest, TestStateChangeOnePrimaryField) {
     sp<TestStateListener> listener1 = new TestStateListener();
@@ -240,9 +253,8 @@
     mgr.registerListener(android::util::UID_PROCESS_STATE_CHANGED, listener1);
 
     // log event
-    std::shared_ptr<LogEvent> event = buildUidProcessEvent(
-            1000,
-            android::app::ProcessStateEnum::PROCESS_STATE_TOP);  //  state value: 1002
+    std::shared_ptr<LogEvent> event =
+            buildUidProcessEvent(1000 /* uid */, android::app::ProcessStateEnum::PROCESS_STATE_TOP);
     mgr.onLogEvent(*event);
 
     // check listener was updated
@@ -252,23 +264,33 @@
 
     // check StateTracker was updated by querying for state
     HashableDimensionKey queryKey;
-    getUidProcessKey(1000, &queryKey);
-    EXPECT_EQ(1002, mgr.getState(android::util::UID_PROCESS_STATE_CHANGED, queryKey));
+    getUidProcessKey(1000 /* uid */, &queryKey);
+    EXPECT_EQ(1002, mgr.getStateValue(android::util::UID_PROCESS_STATE_CHANGED, queryKey));
 }
 
+/**
+ * Test StateManager's onLogEvent and StateListener's onStateChanged correctly
+ * updates listener for states with multiple primary keys.
+ */
 TEST(StateTrackerTest, TestStateChangeMultiplePrimaryFields) {
     sp<TestStateListener> listener1 = new TestStateListener();
     StateManager mgr;
     mgr.registerListener(android::util::OVERLAY_STATE_CHANGED, listener1);
 
     // log event
-    std::shared_ptr<LogEvent> event = buildOverlayEvent(1000, "package1", 1);  // state: ENTERED
+    std::shared_ptr<LogEvent> event =
+            buildOverlayEvent(1000 /* uid */, "package1", 1);  // state: ENTERED
     mgr.onLogEvent(*event);
 
-    // check listener update
+    // check listener was updated
     EXPECT_EQ(1, listener1->updates.size());
     EXPECT_EQ(1000, listener1->updates[0].mKey.getValues()[0].mValue.int_value);
     EXPECT_EQ(1, listener1->updates[0].mState);
+
+    // check StateTracker was updated by querying for state
+    HashableDimensionKey queryKey;
+    getOverlayKey(1000 /* uid */, "package1", &queryKey);
+    EXPECT_EQ(1, mgr.getStateValue(android::util::OVERLAY_STATE_CHANGED, queryKey));
 }
 
 /**
@@ -282,11 +304,14 @@
     mgr.registerListener(android::util::OVERLAY_STATE_CHANGED, listener1);
 
     // log event
-    std::shared_ptr<LogEvent> event =
-            buildIncorrectOverlayEvent(1000, "package1", 1);  // state: ENTERED
-    mgr.onLogEvent(*event);
+    std::shared_ptr<LogEvent> event1 =
+            buildIncorrectOverlayEvent(1000 /* uid */, "package1", 1 /* state */);
+    std::shared_ptr<LogEvent> event2 = buildOverlayEventBadStateType(1001 /* uid */, "package2");
 
-    // check listener update
+    // check listener was updated
+    mgr.onLogEvent(*event1);
+    EXPECT_EQ(0, listener1->updates.size());
+    mgr.onLogEvent(*event2);
     EXPECT_EQ(0, listener1->updates.size());
 }
 
@@ -301,18 +326,19 @@
 
     std::shared_ptr<LogEvent> event1 = buildUidProcessEvent(
             1000,
-            android::app::ProcessStateEnum::PROCESS_STATE_TOP);  // state value: 1002
+            android::app::ProcessStateEnum::PROCESS_STATE_TOP);  //  state value: 1002
     std::shared_ptr<LogEvent> event2 = buildUidProcessEvent(
             1001,
-            android::app::ProcessStateEnum::PROCESS_STATE_FOREGROUND_SERVICE);  // state value: 1003
+            android::app::ProcessStateEnum::PROCESS_STATE_FOREGROUND_SERVICE);  //  state value:
+                                                                                //  1003
     std::shared_ptr<LogEvent> event3 = buildUidProcessEvent(
             1002,
-            android::app::ProcessStateEnum::PROCESS_STATE_PERSISTENT);  // state value: 1000
+            android::app::ProcessStateEnum::PROCESS_STATE_PERSISTENT);  //  state value: 1000
     std::shared_ptr<LogEvent> event4 = buildUidProcessEvent(
             1001,
-            android::app::ProcessStateEnum::PROCESS_STATE_TOP);  // state value: 1002
+            android::app::ProcessStateEnum::PROCESS_STATE_TOP);  //  state value: 1002
     std::shared_ptr<LogEvent> event5 =
-            buildScreenEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON);  // state value:
+            buildScreenEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON);
     std::shared_ptr<LogEvent> event6 = buildOverlayEvent(1000, "package1", 1);
     std::shared_ptr<LogEvent> event7 = buildOverlayEvent(1000, "package2", 2);
 
@@ -327,25 +353,25 @@
     // Query for UidProcessState of uid 1001
     HashableDimensionKey queryKey1;
     getUidProcessKey(1001, &queryKey1);
-    EXPECT_EQ(1003, mgr.getState(android::util::UID_PROCESS_STATE_CHANGED, queryKey1));
+    EXPECT_EQ(1003, mgr.getStateValue(android::util::UID_PROCESS_STATE_CHANGED, queryKey1));
 
     // Query for UidProcessState of uid 1004 - not in state map
     HashableDimensionKey queryKey2;
     getUidProcessKey(1004, &queryKey2);
-    EXPECT_EQ(-1,
-              mgr.getState(android::util::UID_PROCESS_STATE_CHANGED, queryKey2));  // default state
+    EXPECT_EQ(-1, mgr.getStateValue(android::util::UID_PROCESS_STATE_CHANGED,
+                                    queryKey2));  // default state
 
     // Query for UidProcessState of uid 1001 - after change in state
     mgr.onLogEvent(*event4);
-    EXPECT_EQ(1002, mgr.getState(android::util::UID_PROCESS_STATE_CHANGED, queryKey1));
+    EXPECT_EQ(1002, mgr.getStateValue(android::util::UID_PROCESS_STATE_CHANGED, queryKey1));
 
     // Query for ScreenState
-    EXPECT_EQ(2, mgr.getState(android::util::SCREEN_STATE_CHANGED, DEFAULT_DIMENSION_KEY));
+    EXPECT_EQ(2, mgr.getStateValue(android::util::SCREEN_STATE_CHANGED, DEFAULT_DIMENSION_KEY));
 
     // Query for OverlayState of uid 1000, package name "package2"
     HashableDimensionKey queryKey3;
     getOverlayKey(1000, "package2", &queryKey3);
-    EXPECT_EQ(2, mgr.getState(android::util::OVERLAY_STATE_CHANGED, queryKey3));
+    EXPECT_EQ(2, mgr.getStateValue(android::util::OVERLAY_STATE_CHANGED, queryKey3));
 }
 
 }  // namespace statsd
diff --git a/cmds/statsd/tests/statsd_test_util.cpp b/cmds/statsd/tests/statsd_test_util.cpp
index 2c4f3c7..38c22ab 100644
--- a/cmds/statsd/tests/statsd_test_util.cpp
+++ b/cmds/statsd/tests/statsd_test_util.cpp
@@ -251,6 +251,101 @@
     return predicate;
 }
 
+State CreateScreenState() {
+    State state;
+    state.set_id(StringToId("ScreenState"));
+    state.set_atom_id(29);
+    return state;
+}
+
+State CreateUidProcessState() {
+    State state;
+    state.set_id(StringToId("UidProcessState"));
+    state.set_atom_id(27);
+    return state;
+}
+
+State CreateOverlayState() {
+    State state;
+    state.set_id(StringToId("OverlayState"));
+    state.set_atom_id(59);
+    return state;
+}
+
+State CreateScreenStateWithOnOffMap() {
+    State state;
+    state.set_id(StringToId("ScreenStateOnOff"));
+    state.set_atom_id(29);
+
+    auto map = CreateScreenStateOnOffMap();
+    *state.mutable_map() = map;
+
+    return state;
+}
+
+State CreateScreenStateWithInDozeMap() {
+    State state;
+    state.set_id(StringToId("ScreenStateInDoze"));
+    state.set_atom_id(29);
+
+    auto map = CreateScreenStateInDozeMap();
+    *state.mutable_map() = map;
+
+    return state;
+}
+
+StateMap_StateGroup CreateScreenStateOnGroup() {
+    StateMap_StateGroup group;
+    group.set_group_id(StringToId("SCREEN_ON"));
+    group.add_value(2);
+    group.add_value(5);
+    group.add_value(6);
+    return group;
+}
+
+StateMap_StateGroup CreateScreenStateOffGroup() {
+    StateMap_StateGroup group;
+    group.set_group_id(StringToId("SCREEN_OFF"));
+    group.add_value(0);
+    group.add_value(1);
+    group.add_value(3);
+    group.add_value(4);
+    return group;
+}
+
+StateMap CreateScreenStateOnOffMap() {
+    StateMap map;
+    *map.add_group() = CreateScreenStateOnGroup();
+    *map.add_group() = CreateScreenStateOffGroup();
+    return map;
+}
+
+StateMap_StateGroup CreateScreenStateInDozeGroup() {
+    StateMap_StateGroup group;
+    group.set_group_id(StringToId("SCREEN_DOZE"));
+    group.add_value(3);
+    group.add_value(4);
+    return group;
+}
+
+StateMap_StateGroup CreateScreenStateNotDozeGroup() {
+    StateMap_StateGroup group;
+    group.set_group_id(StringToId("SCREEN_NOT_DOZE"));
+    group.add_value(0);
+    group.add_value(1);
+    group.add_value(2);
+    group.add_value(5);
+    group.add_value(6);
+    return group;
+}
+
+StateMap CreateScreenStateInDozeMap() {
+    StateMap map;
+    *map.add_group() = CreateScreenStateInDozeGroup();
+    *map.add_group() = CreateScreenStateNotDozeGroup();
+    return map;
+}
+
 void addPredicateToPredicateCombination(const Predicate& predicate,
                                         Predicate* combinationPredicate) {
     combinationPredicate->mutable_combination()->add_predicate(predicate.id());
diff --git a/cmds/statsd/tests/statsd_test_util.h b/cmds/statsd/tests/statsd_test_util.h
index 635c583..c026105 100644
--- a/cmds/statsd/tests/statsd_test_util.h
+++ b/cmds/statsd/tests/statsd_test_util.h
@@ -104,6 +104,37 @@
 // Create a Predicate proto for app is in background.
 Predicate CreateIsInBackgroundPredicate();
 
+// Create State proto for screen state atom.
+State CreateScreenState();
+
+// Create State proto for uid process state atom.
+State CreateUidProcessState();
+
+// Create State proto for overlay state atom.
+State CreateOverlayState();
+
+State CreateScreenStateWithOnOffMap();
+
+State CreateScreenStateWithInDozeMap();
+
+// Create StateGroup proto for ScreenState ON group
+StateMap_StateGroup CreateScreenStateOnGroup();
+
+// Create StateGroup proto for ScreenState OFF group
+StateMap_StateGroup CreateScreenStateOffGroup();
+
+// Create StateMap proto for ScreenState ON/OFF map
+StateMap CreateScreenStateOnOffMap();
+
+// Create StateGroup proto for ScreenState IN DOZE group
+StateMap_StateGroup CreateScreenStateInDozeGroup();
+
+// Create StateGroup proto for ScreenState NOT IN DOZE group
+StateMap_StateGroup CreateScreenStateNotDozeGroup();
+
+// Create StateMap proto for ScreenState IN DOZE map
+StateMap CreateScreenStateInDozeMap();
+
 // Add a predicate to the predicate combination.
 void addPredicateToPredicateCombination(const Predicate& predicate, Predicate* combination);
 
@@ -319,4 +350,4 @@
 }
 }  // namespace statsd
 }  // namespace os
-}  // namespace android
\ No newline at end of file
+}  // namespace android
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 39fab63..d5bc9b0 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -2212,9 +2212,9 @@
     }
 
     @Override
-    public Context createContextAsUser(UserHandle user) {
+    public Context createContextAsUser(UserHandle user, @CreatePackageOptions int flags) {
         try {
-            return createPackageContextAsUser(getPackageName(), mFlags, user);
+            return createPackageContextAsUser(getPackageName(), flags, user);
         } catch (NameNotFoundException e) {
             throw new IllegalStateException("Own package not found: package=" + getPackageName());
         }
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 316cab8..58f6741 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -997,8 +997,13 @@
     }
 
     /**
-     * @hide
+     * <p>
+     *  Gets the currently applied notification policy. If {@link #getCurrentInterruptionFilter}
+     * is equal to {@link #INTERRUPTION_FILTER_ALL}, then the consolidated notification policy
+     * will match the default notification policy returned by {@link #getNotificationPolicy}.
+     * </p>
      */
+    @Nullable
     public NotificationManager.Policy getConsolidatedNotificationPolicy() {
         INotificationManager service = getService();
         try {
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index 9e6054c..68ab89c 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -884,21 +884,24 @@
      * @param activityToken optional token to clean up Activity resources
      */
     private void cleanupReferences(IBinder activityToken) {
-        if (activityToken != null) {
-            ActivityResources activityResources = mActivityResourceReferences.get(activityToken);
-            if (activityResources != null) {
-                ArrayUtils.unstableRemoveIf(activityResources.activityResources,
-                        sEmptyReferencePredicate);
+        synchronized (this) {
+            if (activityToken != null) {
+                ActivityResources activityResources = mActivityResourceReferences.get(
+                        activityToken);
+                if (activityResources != null) {
+                    ArrayUtils.unstableRemoveIf(activityResources.activityResources,
+                            sEmptyReferencePredicate);
+                }
+            } else {
+                ArrayUtils.unstableRemoveIf(mResourceReferences, sEmptyReferencePredicate);
             }
-        } else {
-            ArrayUtils.unstableRemoveIf(mResourceReferences, sEmptyReferencePredicate);
-        }
 
-        for (int index = mResourcesWithLoaders.size() - 1; index >= 0; index--) {
-            ResourcesWithLoaders resourcesWithLoaders = mResourcesWithLoaders.get(index);
-            Resources resources = resourcesWithLoaders.resources();
-            if (resources == null) {
-                mResourcesWithLoaders.remove(index);
+            for (int index = mResourcesWithLoaders.size() - 1; index >= 0; index--) {
+                ResourcesWithLoaders resourcesWithLoaders = mResourcesWithLoaders.get(index);
+                Resources resources = resourcesWithLoaders.resources();
+                if (resources == null) {
+                    mResourcesWithLoaders.remove(index);
+                }
             }
         }
     }
diff --git a/core/java/android/app/admin/PasswordMetrics.java b/core/java/android/app/admin/PasswordMetrics.java
index 9929855..464f75c 100644
--- a/core/java/android/app/admin/PasswordMetrics.java
+++ b/core/java/android/app/admin/PasswordMetrics.java
@@ -27,9 +27,6 @@
 import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
 import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
 
-import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD;
-import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN;
-
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.app.admin.DevicePolicyManager.PasswordComplexity;
@@ -37,8 +34,7 @@
 import android.os.Parcelable;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.Preconditions;
-import com.android.internal.widget.LockPatternUtils.CredentialType;
+import com.android.internal.widget.LockscreenCredential;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -226,21 +222,21 @@
     };
 
     /**
-     * Returnsthe {@code PasswordMetrics} for a given credential.
+     * Returns the {@code PasswordMetrics} for a given credential.
      *
      * If the credential is a pin or a password, equivalent to {@link #computeForPassword(byte[])}.
      * {@code credential} cannot be null when {@code type} is
      * {@link com.android.internal.widget.LockPatternUtils#CREDENTIAL_TYPE_PASSWORD}.
      */
-    public static PasswordMetrics computeForCredential(
-            @CredentialType int type, byte[] credential) {
-        if (type == CREDENTIAL_TYPE_PASSWORD) {
-            Preconditions.checkNotNull(credential, "credential cannot be null");
-            return PasswordMetrics.computeForPassword(credential);
-        } else if (type == CREDENTIAL_TYPE_PATTERN)  {
+    public static PasswordMetrics computeForCredential(LockscreenCredential credential) {
+        if (credential.isPassword()) {
+            return PasswordMetrics.computeForPassword(credential.getCredential());
+        } else if (credential.isPattern())  {
             return new PasswordMetrics(PASSWORD_QUALITY_SOMETHING);
-        } else /* if (type == CREDENTIAL_TYPE_NONE) */ {
+        } else if (credential.isNone()) {
             return new PasswordMetrics(PASSWORD_QUALITY_UNSPECIFIED);
+        } else {
+            throw new IllegalArgumentException("Unknown credential type " + credential.getType());
         }
     }
 
diff --git a/core/java/android/app/slice/SliceManager.java b/core/java/android/app/slice/SliceManager.java
index 955093d..90ecce2 100644
--- a/core/java/android/app/slice/SliceManager.java
+++ b/core/java/android/app/slice/SliceManager.java
@@ -390,6 +390,8 @@
             }
             Bundle extras = new Bundle();
             extras.putParcelable(SliceProvider.EXTRA_INTENT, intent);
+            extras.putParcelableArrayList(SliceProvider.EXTRA_SUPPORTED_SPECS,
+                    new ArrayList<>(supportedSpecs));
             final Bundle res = provider.call(SliceProvider.METHOD_MAP_INTENT, null, extras);
             if (res == null) {
                 return null;
diff --git a/core/java/android/app/timedetector/TimeSignal.java b/core/java/android/app/timedetector/TimeSignal.java
index da21794..b494260 100644
--- a/core/java/android/app/timedetector/TimeSignal.java
+++ b/core/java/android/app/timedetector/TimeSignal.java
@@ -56,8 +56,7 @@
 
     private static TimeSignal createFromParcel(Parcel in) {
         String sourceId = in.readString();
-        TimestampedValue<Long> utcTime =
-                TimestampedValue.readFromParcel(in, null /* classLoader */, Long.class);
+        TimestampedValue<Long> utcTime = in.readParcelable(null /* classLoader */);
         return new TimeSignal(sourceId, utcTime);
     }
 
@@ -69,7 +68,7 @@
     @Override
     public void writeToParcel(@NonNull Parcel dest, int flags) {
         dest.writeString(mSourceId);
-        TimestampedValue.writeToParcel(dest, mUtcTime);
+        dest.writeParcelable(mUtcTime, 0);
     }
 
     @NonNull
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 227684b..e7e278f 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -5244,7 +5244,7 @@
     @SystemApi
     @TestApi
     @NonNull
-    public Context createContextAsUser(@NonNull UserHandle user) {
+    public Context createContextAsUser(@NonNull UserHandle user, @CreatePackageOptions int flags) {
         if (Build.IS_ENG) {
             throw new IllegalStateException("createContextAsUser not overridden!");
         }
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index f7cd51e..7993ea1 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -885,8 +885,8 @@
 
     /** @hide */
     @Override
-    public Context createContextAsUser(UserHandle user) {
-        return mBase.createContextAsUser(user);
+    public Context createContextAsUser(UserHandle user, @CreatePackageOptions int flags) {
+        return mBase.createContextAsUser(user, flags);
     }
 
     /** @hide */
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index f302d59..7509065 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2858,6 +2858,14 @@
 
     /**
      * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
+     * The device does not have slices implementation.
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_SLICES_DISABLED = "android.software.slices_disabled";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
      * The device supports device-unique Keystore attestations.  Only available on devices that
      * also support {@link #FEATURE_STRONGBOX_KEYSTORE}, and can only be used by device owner
      * apps (see {@link android.app.admin.DevicePolicyManager#generateKeyPair}).
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 0b157fa..cf21e96 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -577,8 +577,6 @@
      */
     public interface Callback {
         boolean hasFeature(String feature);
-        String[] getOverlayPaths(String targetPackageName, String targetPath);
-        String[] getOverlayApks(String targetPackageName);
     }
 
     /**
@@ -595,14 +593,6 @@
         @Override public boolean hasFeature(String feature) {
             return mPm.hasSystemFeature(feature);
         }
-
-        @Override public String[] getOverlayPaths(String targetPackageName, String targetPath) {
-            return null;
-        }
-
-        @Override public String[] getOverlayApks(String targetPackageName) {
-            return null;
-        }
     }
 
     /**
@@ -1195,19 +1185,7 @@
             }
 
             final byte[] bytes = IoUtils.readFileAsByteArray(cacheFile.getAbsolutePath());
-            Package p = fromCacheEntry(bytes);
-            if (mCallback != null) {
-                String[] overlayApks = mCallback.getOverlayApks(p.packageName);
-                if (overlayApks != null && overlayApks.length > 0) {
-                    for (String overlayApk : overlayApks) {
-                        // If a static RRO is updated, return null.
-                        if (!isCacheUpToDate(new File(overlayApk), cacheFile)) {
-                            return null;
-                        }
-                    }
-                }
-            }
-            return p;
+            return fromCacheEntry(bytes);
         } catch (Throwable e) {
             Slog.w(TAG, "Error reading package cache: ", e);
 
@@ -1381,7 +1359,7 @@
             final Resources res = new Resources(assets, mMetrics, null);
 
             final String[] outError = new String[1];
-            final Package pkg = parseBaseApk(apkPath, res, parser, flags, outError);
+            final Package pkg = parseBaseApk(res, parser, flags, outError);
             if (pkg == null) {
                 throw new PackageParserException(mParseError,
                         apkPath + " (at " + parser.getPositionDescription() + "): " + outError[0]);
@@ -1944,7 +1922,6 @@
      * need to consider whether they should be supported by split APKs and child
      * packages.
      *
-     * @param apkPath The package apk file path
      * @param res The resources from which to resolve values
      * @param parser The manifest parser
      * @param flags Flags how to parse
@@ -1954,8 +1931,7 @@
      * @throws XmlPullParserException
      * @throws IOException
      */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
-    private Package parseBaseApk(String apkPath, Resources res, XmlResourceParser parser, int flags,
+    private Package parseBaseApk(Resources res, XmlResourceParser parser, int flags,
             String[] outError) throws XmlPullParserException, IOException {
         final String splitName;
         final String pkgName;
@@ -1975,15 +1951,6 @@
             return null;
         }
 
-        if (mCallback != null) {
-            String[] overlayPaths = mCallback.getOverlayPaths(pkgName, apkPath);
-            if (overlayPaths != null && overlayPaths.length > 0) {
-                for (String overlayPath : overlayPaths) {
-                    res.getAssets().addOverlayPath(overlayPath);
-                }
-            }
-        }
-
         final Package pkg = new Package(pkgName);
 
         TypedArray sa = res.obtainAttributes(parser,
diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java
index c77c53f..aa6f58e 100644
--- a/core/java/android/content/pm/PermissionInfo.java
+++ b/core/java/android/content/pm/PermissionInfo.java
@@ -248,6 +248,17 @@
     @TestApi
     public static final int PROTECTION_FLAG_TELEPHONY = 0x400000;
 
+    /**
+     * Additional flag for {@link #protectionLevel}, corresponding
+     * to the <code>wifi</code> value of
+     * {@link android.R.attr#protectionLevel}.
+     *
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    public static final int PROTECTION_FLAG_WIFI = 0x800000;
+
     /** @hide */
     @IntDef(flag = true, prefix = { "PROTECTION_FLAG_" }, value = {
             PROTECTION_FLAG_PRIVILEGED,
@@ -270,6 +281,7 @@
             PROTECTION_FLAG_INCIDENT_REPORT_APPROVER,
             PROTECTION_FLAG_APP_PREDICTOR,
             PROTECTION_FLAG_TELEPHONY,
+            PROTECTION_FLAG_WIFI,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface ProtectionFlags {}
@@ -516,6 +528,9 @@
         if ((level & PermissionInfo.PROTECTION_FLAG_TELEPHONY) != 0) {
             protLevel += "|telephony";
         }
+        if ((level & PermissionInfo.PROTECTION_FLAG_WIFI) != 0) {
+            protLevel += "|wifi";
+        }
         return protLevel;
     }
 
diff --git a/core/java/android/net/INetworkPolicyListener.aidl b/core/java/android/net/INetworkPolicyListener.aidl
index 106b7be..fe9141c 100644
--- a/core/java/android/net/INetworkPolicyListener.aidl
+++ b/core/java/android/net/INetworkPolicyListener.aidl
@@ -15,6 +15,7 @@
  */
 
 package android.net;
+import android.telephony.SubscriptionPlan;
 
 /** {@hide} */
 oneway interface INetworkPolicyListener {
@@ -22,5 +23,6 @@
     void onMeteredIfacesChanged(in String[] meteredIfaces);
     void onRestrictBackgroundChanged(boolean restrictBackground);
     void onUidPoliciesChanged(int uid, int uidPolicies);
-    void onSubscriptionOverride(int subId, int overrideMask, int overrideValue, long networkTypeMask);
+    void onSubscriptionOverride(int subId, int overrideMask, int overrideValue);
+    void onSubscriptionPlansChanged(int subId, in SubscriptionPlan[] plans);
 }
diff --git a/core/java/android/net/INetworkPolicyManager.aidl b/core/java/android/net/INetworkPolicyManager.aidl
index 90327663..385cb1d 100644
--- a/core/java/android/net/INetworkPolicyManager.aidl
+++ b/core/java/android/net/INetworkPolicyManager.aidl
@@ -76,7 +76,7 @@
     SubscriptionPlan[] getSubscriptionPlans(int subId, String callingPackage);
     void setSubscriptionPlans(int subId, in SubscriptionPlan[] plans, String callingPackage);
     String getSubscriptionPlansOwner(int subId);
-    void setSubscriptionOverride(int subId, int overrideMask, int overrideValue, long networkTypeMask, long timeoutMillis, String callingPackage);
+    void setSubscriptionOverride(int subId, int overrideMask, int overrideValue, long timeoutMillis, String callingPackage);
 
     void factoryReset(String subscriber);
 
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index 628dcd2..9150aae 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -31,6 +31,7 @@
 import android.os.Build;
 import android.os.RemoteException;
 import android.os.UserHandle;
+import android.telephony.SubscriptionPlan;
 import android.util.DebugUtils;
 import android.util.Pair;
 import android.util.Range;
@@ -380,7 +381,8 @@
         @Override public void onMeteredIfacesChanged(String[] meteredIfaces) { }
         @Override public void onRestrictBackgroundChanged(boolean restrictBackground) { }
         @Override public void onUidPoliciesChanged(int uid, int uidPolicies) { }
-        @Override public void onSubscriptionOverride(int subId, int overrideMask, int overrideValue,
-                long networkTypeMask) { }
+        @Override public void onSubscriptionOverride(int subId, int overrideMask,
+                int overrideValue) { }
+        @Override public void onSubscriptionPlansChanged(int subId, SubscriptionPlan[] plans) { }
     }
 }
diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java
index fd81178..eb09930 100644
--- a/core/java/android/provider/DocumentsContract.java
+++ b/core/java/android/provider/DocumentsContract.java
@@ -363,15 +363,22 @@
          * <p>
          * Type: INTEGER (int)
          *
-         * @see #FLAG_SUPPORTS_WRITE
-         * @see #FLAG_SUPPORTS_DELETE
-         * @see #FLAG_SUPPORTS_THUMBNAIL
+         * @see #FLAG_DIR_BLOCKS_TREE
          * @see #FLAG_DIR_PREFERS_GRID
          * @see #FLAG_DIR_PREFERS_LAST_MODIFIED
-         * @see #FLAG_VIRTUAL_DOCUMENT
+         * @see #FLAG_DIR_SUPPORTS_CREATE
+         * @see #FLAG_PARTIAL
          * @see #FLAG_SUPPORTS_COPY
+         * @see #FLAG_SUPPORTS_DELETE
+         * @see #FLAG_SUPPORTS_METADATA
          * @see #FLAG_SUPPORTS_MOVE
          * @see #FLAG_SUPPORTS_REMOVE
+         * @see #FLAG_SUPPORTS_RENAME
+         * @see #FLAG_SUPPORTS_SETTINGS
+         * @see #FLAG_SUPPORTS_THUMBNAIL
+         * @see #FLAG_SUPPORTS_WRITE
+         * @see #FLAG_VIRTUAL_DOCUMENT
+         * @see #FLAG_WEB_LINKABLE
          */
         public static final String COLUMN_FLAGS = "flags";
 
@@ -542,6 +549,23 @@
          * @see DocumentsContract#getDocumentMetadata(ContentInterface, Uri)
          */
         public static final int FLAG_SUPPORTS_METADATA = 1 << 14;
+
+        /**
+         * Flag indicating that a document is a directory that wants to block itself
+         * from being selected when the user launches an {@link Intent#ACTION_OPEN_DOCUMENT_TREE}
+         * intent. Only valid when {@link #COLUMN_MIME_TYPE} is {@link #MIME_TYPE_DIR}.
+         * <p>
+         * Note that this flag <em>only</em> applies to the single directory to which it is
+         * applied. It does <em>not</em> block the user from selecting either a parent or
+         * child directory during an {@link Intent#ACTION_OPEN_DOCUMENT_TREE} request.
+         * In particular, the only way to guarantee that a specific directory can never
+         * be granted via an {@link Intent#ACTION_OPEN_DOCUMENT_TREE} request is to ensure
+         * that both it and <em>all of its parent directories</em> have set this flag.
+         *
+         * @see Intent#ACTION_OPEN_DOCUMENT_TREE
+         * @see #COLUMN_FLAGS
+         */
+        public static final int FLAG_DIR_BLOCKS_TREE = 1 << 15;
     }
 
     /**
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index 493f9a2..a1333df 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -39,7 +39,6 @@
 import android.content.Intent;
 import android.content.UriPermission;
 import android.database.Cursor;
-import android.database.DatabaseUtils;
 import android.graphics.Bitmap;
 import android.graphics.BitmapFactory;
 import android.graphics.ImageDecoder;
@@ -70,15 +69,19 @@
 
 import com.android.internal.annotations.GuardedBy;
 
+import libcore.util.HexEncoding;
+
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.text.Collator;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
+import java.util.Locale;
 import java.util.Objects;
 import java.util.Set;
 import java.util.regex.Pattern;
@@ -2059,7 +2062,17 @@
             /**
              * A non human readable key calculated from the TITLE, used for
              * searching, sorting and grouping
+             *
+             * @see Audio#keyFor(String)
+             * @deprecated These keys are generated using
+             *             {@link java.util.Locale#ROOT}, which means they don't
+             *             reflect locale-specific sorting preferences. To apply
+             *             locale-specific sorting preferences, use
+             *             {@link ContentResolver#QUERY_ARG_SQL_SORT_ORDER} with
+             *             {@code COLLATE LOCALIZED}, or
+             *             {@link ContentResolver#QUERY_ARG_SORT_LOCALE}.
              */
+            @Deprecated
             @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String TITLE_KEY = "title_key";
 
@@ -2104,7 +2117,17 @@
             /**
              * A non human readable key calculated from the ARTIST, used for
              * searching, sorting and grouping
+             *
+             * @see Audio#keyFor(String)
+             * @deprecated These keys are generated using
+             *             {@link java.util.Locale#ROOT}, which means they don't
+             *             reflect locale-specific sorting preferences. To apply
+             *             locale-specific sorting preferences, use
+             *             {@link ContentResolver#QUERY_ARG_SQL_SORT_ORDER} with
+             *             {@code COLLATE LOCALIZED}, or
+             *             {@link ContentResolver#QUERY_ARG_SORT_LOCALE}.
              */
+            @Deprecated
             @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String ARTIST_KEY = "artist_key";
 
@@ -2129,7 +2152,17 @@
             /**
              * A non human readable key calculated from the ALBUM, used for
              * searching, sorting and grouping
+             *
+             * @see Audio#keyFor(String)
+             * @deprecated These keys are generated using
+             *             {@link java.util.Locale#ROOT}, which means they don't
+             *             reflect locale-specific sorting preferences. To apply
+             *             locale-specific sorting preferences, use
+             *             {@link ContentResolver#QUERY_ARG_SQL_SORT_ORDER} with
+             *             {@code COLLATE LOCALIZED}, or
+             *             {@link ContentResolver#QUERY_ARG_SORT_LOCALE}.
              */
+            @Deprecated
             @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String ALBUM_KEY = "album_key";
 
@@ -2186,91 +2219,89 @@
             public static final String IS_AUDIOBOOK = "is_audiobook";
 
             /**
-             * The genre of the audio file, if any
-             * Does not exist in the database - only used by the media scanner for inserts.
-             * @hide
+             * The id of the genre the audio file is from, if any
              */
-            @Deprecated
-            // @Column(Cursor.FIELD_TYPE_STRING)
+            @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
+            public static final String GENRE_ID = "genre_id";
+
+            /**
+             * The genre of the audio file, if any.
+             */
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String GENRE = "genre";
 
             /**
-             * The resource URI of a localized title, if any
+             * A non human readable key calculated from the GENRE, used for
+             * searching, sorting and grouping
+             *
+             * @see Audio#keyFor(String)
+             * @deprecated These keys are generated using
+             *             {@link java.util.Locale#ROOT}, which means they don't
+             *             reflect locale-specific sorting preferences. To apply
+             *             locale-specific sorting preferences, use
+             *             {@link ContentResolver#QUERY_ARG_SQL_SORT_ORDER} with
+             *             {@code COLLATE LOCALIZED}, or
+             *             {@link ContentResolver#QUERY_ARG_SORT_LOCALE}.
+             */
+            @Deprecated
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
+            public static final String GENRE_KEY = "genre_key";
+
+            /**
+             * The resource URI of a localized title, if any.
+             * <p>
              * Conforms to this pattern:
-             *   Scheme: {@link ContentResolver.SCHEME_ANDROID_RESOURCE}
-             *   Authority: Package Name of ringtone title provider
-             *   First Path Segment: Type of resource (must be "string")
-             *   Second Path Segment: Resource ID of title
-             * @hide
+             * <ul>
+             * <li>Scheme: {@link ContentResolver#SCHEME_ANDROID_RESOURCE}
+             * <li>Authority: Package Name of ringtone title provider
+             * <li>First Path Segment: Type of resource (must be "string")
+             * <li>Second Path Segment: Resource ID of title
+             * </ul>
              */
             @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String TITLE_RESOURCE_URI = "title_resource_uri";
         }
 
+        private static final Pattern PATTERN_TRIM_BEFORE = Pattern.compile(
+                "(?i)(^(the|an|a) |,\\s*(the|an|a)$|[^\\w\\s]|^\\s+|\\s+$)");
+        private static final Pattern PATTERN_TRIM_AFTER = Pattern.compile(
+                "(^(00)+|(00)+$)");
+
         /**
-         * Converts a name to a "key" that can be used for grouping, sorting
-         * and searching.
-         * The rules that govern this conversion are:
-         * - remove 'special' characters like ()[]'!?.,
-         * - remove leading/trailing spaces
-         * - convert everything to lowercase
-         * - remove leading "the ", "an " and "a "
-         * - remove trailing ", the|an|a"
-         * - remove accents. This step leaves us with CollationKey data,
-         *   which is not human readable
+         * Converts a user-visible string into a "key" that can be used for
+         * grouping, sorting, and searching.
          *
-         * @param name The artist or album name to convert
-         * @return The "key" for the given name.
+         * @return Opaque token that should not be parsed or displayed to users.
+         * @deprecated These keys are generated using
+         *             {@link java.util.Locale#ROOT}, which means they don't
+         *             reflect locale-specific sorting preferences. To apply
+         *             locale-specific sorting preferences, use
+         *             {@link ContentResolver#QUERY_ARG_SQL_SORT_ORDER} with
+         *             {@code COLLATE LOCALIZED}, or
+         *             {@link ContentResolver#QUERY_ARG_SORT_LOCALE}.
          */
-        public static String keyFor(String name) {
-            if (name != null)  {
-                boolean sortfirst = false;
-                if (name.equals(UNKNOWN_STRING)) {
-                    return "\001";
-                }
-                // Check if the first character is \001. We use this to
-                // force sorting of certain special files, like the silent ringtone.
-                if (name.startsWith("\001")) {
-                    sortfirst = true;
-                }
-                name = name.trim().toLowerCase();
-                if (name.startsWith("the ")) {
-                    name = name.substring(4);
-                }
-                if (name.startsWith("an ")) {
-                    name = name.substring(3);
-                }
-                if (name.startsWith("a ")) {
-                    name = name.substring(2);
-                }
-                if (name.endsWith(", the") || name.endsWith(",the") ||
-                    name.endsWith(", an") || name.endsWith(",an") ||
-                    name.endsWith(", a") || name.endsWith(",a")) {
-                    name = name.substring(0, name.lastIndexOf(','));
-                }
-                name = name.replaceAll("[\\[\\]\\(\\)\"'.,?!]", "").trim();
-                if (name.length() > 0) {
-                    // Insert a separator between the characters to avoid
-                    // matches on a partial character. If we ever change
-                    // to start-of-word-only matches, this can be removed.
-                    StringBuilder b = new StringBuilder();
-                    b.append('.');
-                    int nl = name.length();
-                    for (int i = 0; i < nl; i++) {
-                        b.append(name.charAt(i));
-                        b.append('.');
-                    }
-                    name = b.toString();
-                    String key = DatabaseUtils.getCollationKey(name);
-                    if (sortfirst) {
-                        key = "\001" + key;
-                    }
-                    return key;
-               } else {
-                    return "";
-                }
+        @Deprecated
+        public static @Nullable String keyFor(@Nullable String name) {
+            if (TextUtils.isEmpty(name)) return null;
+
+            if (UNKNOWN_STRING.equals(name)) {
+                return "01";
             }
-            return null;
+
+            final boolean sortFirst = name.startsWith("\001");
+
+            name = PATTERN_TRIM_BEFORE.matcher(name).replaceAll("");
+            if (TextUtils.isEmpty(name)) return null;
+
+            final Collator c = Collator.getInstance(Locale.ROOT);
+            c.setStrength(Collator.PRIMARY);
+            name = HexEncoding.encodeToString(c.getCollationKey(name).toByteArray(), false);
+
+            name = PATTERN_TRIM_AFTER.matcher(name).replaceAll("");
+            if (sortFirst) {
+                name = "01" + name;
+            }
+            return name;
         }
 
         public static final class Media implements AudioColumns {
@@ -2632,7 +2663,17 @@
             /**
              * A non human readable key calculated from the ARTIST, used for
              * searching, sorting and grouping
+             *
+             * @see Audio#keyFor(String)
+             * @deprecated These keys are generated using
+             *             {@link java.util.Locale#ROOT}, which means they don't
+             *             reflect locale-specific sorting preferences. To apply
+             *             locale-specific sorting preferences, use
+             *             {@link ContentResolver#QUERY_ARG_SQL_SORT_ORDER} with
+             *             {@code COLLATE LOCALIZED}, or
+             *             {@link ContentResolver#QUERY_ARG_SORT_LOCALE}.
              */
+            @Deprecated
             @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String ARTIST_KEY = "artist_key";
 
@@ -2736,6 +2777,23 @@
             public static final String ARTIST = "artist";
 
             /**
+             * A non human readable key calculated from the ARTIST, used for
+             * searching, sorting and grouping
+             *
+             * @see Audio#keyFor(String)
+             * @deprecated These keys are generated using
+             *             {@link java.util.Locale#ROOT}, which means they don't
+             *             reflect locale-specific sorting preferences. To apply
+             *             locale-specific sorting preferences, use
+             *             {@link ContentResolver#QUERY_ARG_SQL_SORT_ORDER} with
+             *             {@code COLLATE LOCALIZED}, or
+             *             {@link ContentResolver#QUERY_ARG_SORT_LOCALE}.
+             */
+            @Deprecated
+            @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
+            public static final String ARTIST_KEY = "artist_key";
+
+            /**
              * The number of songs on this album
              */
             @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true)
@@ -2770,7 +2828,17 @@
             /**
              * A non human readable key calculated from the ALBUM, used for
              * searching, sorting and grouping
+             *
+             * @see Audio#keyFor(String)
+             * @deprecated These keys are generated using
+             *             {@link java.util.Locale#ROOT}, which means they don't
+             *             reflect locale-specific sorting preferences. To apply
+             *             locale-specific sorting preferences, use
+             *             {@link ContentResolver#QUERY_ARG_SQL_SORT_ORDER} with
+             *             {@code COLLATE LOCALIZED}, or
+             *             {@link ContentResolver#QUERY_ARG_SORT_LOCALE}.
              */
+            @Deprecated
             @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true)
             public static final String ALBUM_KEY = "album_key";
 
diff --git a/core/java/android/util/DebugUtils.java b/core/java/android/util/DebugUtils.java
index 20e0d14..bc5edf8 100644
--- a/core/java/android/util/DebugUtils.java
+++ b/core/java/android/util/DebugUtils.java
@@ -255,7 +255,7 @@
                     if (value == 0 && flagsWasZero) {
                         return constNameWithoutPrefix(prefix, field);
                     }
-                    if ((flags & value) == value) {
+                    if (value != 0 && (flags & value) == value) {
                         flags &= ~value;
                         res.append(constNameWithoutPrefix(prefix, field)).append('|');
                     }
diff --git a/core/java/android/util/TimestampedValue.java b/core/java/android/util/TimestampedValue.java
index 1289e4d..4505673 100644
--- a/core/java/android/util/TimestampedValue.java
+++ b/core/java/android/util/TimestampedValue.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.os.Parcel;
+import android.os.Parcelable;
 import android.os.SystemClock;
 
 import java.util.Objects;
@@ -30,14 +31,14 @@
  * If a suitable clock is used the reference time can be used to identify the age of a value or
  * ordering between values.
  *
- * <p>To read and write a timestamped value from / to a Parcel see
- * {@link #readFromParcel(Parcel, ClassLoader, Class)} and
- * {@link #writeToParcel(Parcel, TimestampedValue)}.
+ * <p>This class implements {@link Parcelable} for convenience but instances will only actually be
+ * parcelable if the value type held is {@code null}, {@link Parcelable}, or one of the other types
+ * supported by {@link Parcel#writeValue(Object)} / {@link Parcel#readValue(ClassLoader)}.
  *
  * @param <T> the type of the value with an associated timestamp
  * @hide
  */
-public final class TimestampedValue<T> {
+public final class TimestampedValue<T> implements Parcelable {
     private final long mReferenceTimeMillis;
     private final T mValue;
 
@@ -81,57 +82,43 @@
     }
 
     /**
-     * Read a {@link TimestampedValue} from a parcel that was stored using
-     * {@link #writeToParcel(Parcel, TimestampedValue)}.
-     *
-     * <p>The marshalling/unmarshalling of the value relies upon {@link Parcel#writeValue(Object)}
-     * and {@link Parcel#readValue(ClassLoader)} and so this method can only be used with types
-     * supported by those methods.
-     *
-     * @param in the Parcel to read from
-     * @param classLoader the ClassLoader to pass to {@link Parcel#readValue(ClassLoader)}
-     * @param valueClass the expected type of the value, typically the same as {@code <T>} but can
-     *     also be a subclass
-     * @throws RuntimeException if the value read is not compatible with {@code valueClass} or the
-     *     object could not be read
-     */
-    @SuppressWarnings("unchecked")
-    @NonNull
-    public static <T> TimestampedValue<T> readFromParcel(
-            @NonNull Parcel in, @Nullable ClassLoader classLoader, Class<? extends T> valueClass) {
-        long referenceTimeMillis = in.readLong();
-        T value = (T) in.readValue(classLoader);
-        // Equivalent to static code: if (!(value.getClass() instanceof {valueClass})) {
-        if (value != null && !valueClass.isAssignableFrom(value.getClass())) {
-            throw new RuntimeException("Value was of type " + value.getClass()
-                    + " is not assignable to " + valueClass);
-        }
-        return new TimestampedValue<>(referenceTimeMillis, value);
-    }
-
-    /**
-     * Write a {@link TimestampedValue} to a parcel so that it can be read using
-     * {@link #readFromParcel(Parcel, ClassLoader, Class)}.
-     *
-     * <p>The marshalling/unmarshalling of the value relies upon {@link Parcel#writeValue(Object)}
-     * and {@link Parcel#readValue(ClassLoader)} and so this method can only be used with types
-     * supported by those methods.
-     *
-     * @param dest the Parcel
-     * @param timestampedValue the value
-     * @throws RuntimeException if the value could not be written to the Parcel
-     */
-    public static void writeToParcel(
-            @NonNull Parcel dest, @NonNull TimestampedValue<?> timestampedValue) {
-        dest.writeLong(timestampedValue.mReferenceTimeMillis);
-        dest.writeValue(timestampedValue.mValue);
-    }
-
-    /**
      * Returns the difference in milliseconds between two instance's reference times.
      */
     public static long referenceTimeDifference(
             @NonNull TimestampedValue<?> one, @NonNull TimestampedValue<?> two) {
         return one.mReferenceTimeMillis - two.mReferenceTimeMillis;
     }
+
+    public static final @NonNull Parcelable.Creator<TimestampedValue<?>> CREATOR =
+            new Parcelable.ClassLoaderCreator<TimestampedValue<?>>() {
+
+                @Override
+                public TimestampedValue<?> createFromParcel(@NonNull Parcel source) {
+                    return createFromParcel(source, null);
+                }
+
+                @Override
+                public TimestampedValue<?> createFromParcel(
+                        @NonNull Parcel source, @Nullable ClassLoader classLoader) {
+                    long referenceTimeMillis = source.readLong();
+                    Object value = source.readValue(classLoader);
+                    return new TimestampedValue<>(referenceTimeMillis, value);
+                }
+
+                @Override
+                public TimestampedValue[] newArray(int size) {
+                    return new TimestampedValue[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeLong(mReferenceTimeMillis);
+        dest.writeValue(mValue);
+    }
 }
diff --git a/core/java/android/view/IRecentsAnimationController.aidl b/core/java/android/view/IRecentsAnimationController.aidl
index 955be8d..762366e 100644
--- a/core/java/android/view/IRecentsAnimationController.aidl
+++ b/core/java/android/view/IRecentsAnimationController.aidl
@@ -120,4 +120,10 @@
      * @see IRecentsAnimationRunner#onCancelled
      */
     void setDeferCancelUntilNextTransition(boolean defer, boolean screenshot);
+
+    /**
+     * Sets a state for controller to decide which surface is the destination when the recents
+     * animation is cancelled through fail safe mechanism.
+     */
+    void setWillFinishToHome(boolean willFinishToHome);
 }
diff --git a/core/java/android/view/IWindow.aidl b/core/java/android/view/IWindow.aidl
index 4b872d3..8bf99ec 100644
--- a/core/java/android/view/IWindow.aidl
+++ b/core/java/android/view/IWindow.aidl
@@ -81,6 +81,14 @@
      */
     void showInsets(int types, boolean fromIme);
 
+    /**
+     * Called when a set of insets source window should be hidden by policy.
+     *
+     * @param types internal inset types (WindowInsets.Type.InsetType) to hide
+     * @param fromIme true if this request originated from IME (InputMethodService).
+     */
+    void hideInsets(int types, boolean fromIme);
+
     void moved(int newX, int newY);
     void dispatchAppVisibility(boolean visible);
     void dispatchGetNewSurface();
diff --git a/core/java/android/view/InputMonitor.java b/core/java/android/view/InputMonitor.java
index 1a1d7e6..ad1f201 100644
--- a/core/java/android/view/InputMonitor.java
+++ b/core/java/android/view/InputMonitor.java
@@ -40,8 +40,6 @@
     private static final boolean DEBUG = false;
 
     @NonNull
-    private final String mName;
-    @NonNull
     private final InputChannel mInputChannel;
     @NonNull
     private final IInputMonitorHost mHost;
@@ -81,23 +79,19 @@
 
 
 
-    // Code below generated by codegen v1.0.1.
+    // Code below generated by codegen v1.0.7.
     //
     // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
     //
     // To regenerate run:
     // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/view/InputMonitor.java
-    //
-    // CHECKSTYLE:OFF Generated code
+
 
     @DataClass.Generated.Member
     public InputMonitor(
-            @NonNull String name,
             @NonNull InputChannel inputChannel,
             @NonNull IInputMonitorHost host) {
-        this.mName = name;
-        com.android.internal.util.AnnotationValidations.validate(
-                NonNull.class, null, mName);
         this.mInputChannel = inputChannel;
         com.android.internal.util.AnnotationValidations.validate(
                 NonNull.class, null, mInputChannel);
@@ -109,11 +103,6 @@
     }
 
     @DataClass.Generated.Member
-    public @NonNull String getName() {
-        return mName;
-    }
-
-    @DataClass.Generated.Member
     public @NonNull InputChannel getInputChannel() {
         return mInputChannel;
     }
@@ -130,7 +119,6 @@
         // String fieldNameToString() { ... }
 
         return "InputMonitor { " +
-                "name = " + mName + ", " +
                 "inputChannel = " + mInputChannel + ", " +
                 "host = " + mHost +
         " }";
@@ -142,7 +130,6 @@
         // You can override field parcelling by defining methods like:
         // void parcelFieldName(Parcel dest, int flags) { ... }
 
-        dest.writeString(mName);
         dest.writeTypedObject(mInputChannel, flags);
         dest.writeStrongInterface(mHost);
     }
@@ -151,6 +138,26 @@
     @DataClass.Generated.Member
     public int describeContents() { return 0; }
 
+    /** @hide */
+    @SuppressWarnings({"unchecked", "RedundantCast"})
+    @DataClass.Generated.Member
+    /* package-private */ InputMonitor(Parcel in) {
+        // You can override field unparcelling by defining methods like:
+        // static FieldType unparcelFieldName(Parcel in) { ... }
+
+        InputChannel inputChannel = (InputChannel) in.readTypedObject(InputChannel.CREATOR);
+        IInputMonitorHost host = IInputMonitorHost.Stub.asInterface(in.readStrongBinder());
+
+        this.mInputChannel = inputChannel;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mInputChannel);
+        this.mHost = host;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mHost);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
     @DataClass.Generated.Member
     public static final @NonNull Parcelable.Creator<InputMonitor> CREATOR
             = new Parcelable.Creator<InputMonitor>() {
@@ -160,26 +167,16 @@
         }
 
         @Override
-        @SuppressWarnings({"unchecked", "RedundantCast"})
         public InputMonitor createFromParcel(Parcel in) {
-            // You can override field unparcelling by defining methods like:
-            // static FieldType unparcelFieldName(Parcel in) { ... }
-
-            String name = in.readString();
-            InputChannel inputChannel = (InputChannel) in.readTypedObject(InputChannel.CREATOR);
-            IInputMonitorHost host = IInputMonitorHost.Stub.asInterface(in.readStrongBinder());
-            return new InputMonitor(
-                    name,
-                    inputChannel,
-                    host);
+            return new InputMonitor(in);
         }
     };
 
     @DataClass.Generated(
-            time = 1569871940995L,
-            codegenVersion = "1.0.1",
+            time = 1571177265149L,
+            codegenVersion = "1.0.7",
             sourceFile = "frameworks/base/core/java/android/view/InputMonitor.java",
-            inputSignatures = "private static final  java.lang.String TAG\nprivate static final  boolean DEBUG\nprivate final @android.annotation.NonNull java.lang.String mName\nprivate final @android.annotation.NonNull android.view.InputChannel mInputChannel\nprivate final @android.annotation.NonNull android.view.IInputMonitorHost mHost\npublic  void pilferPointers()\npublic  void dispose()\nclass InputMonitor extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true)")
+            inputSignatures = "private static final  java.lang.String TAG\nprivate static final  boolean DEBUG\nprivate final @android.annotation.NonNull android.view.InputChannel mInputChannel\nprivate final @android.annotation.NonNull android.view.IInputMonitorHost mHost\npublic  void pilferPointers()\npublic  void dispose()\nclass InputMonitor extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/view/InsetsAnimationControlImpl.java b/core/java/android/view/InsetsAnimationControlImpl.java
index 341c214..e4deffa 100644
--- a/core/java/android/view/InsetsAnimationControlImpl.java
+++ b/core/java/android/view/InsetsAnimationControlImpl.java
@@ -229,6 +229,10 @@
             final InsetsSourceConsumer consumer = items.valueAt(i);
             final InsetsSource source = mInitialInsetsState.getSource(consumer.getType());
             final InsetsSourceControl control = consumer.getControl();
+            if (control == null) {
+                // Control may not be available for consumer yet or revoked.
+                continue;
+            }
             final SurfaceControl leash = consumer.getControl().getLeash();
 
             mTmpMatrix.setTranslate(control.getSurfacePosition().x, control.getSurfacePosition().y);
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 5a8636d..5bb4f63 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -251,6 +251,10 @@
 
     @Override
     public void hide(@InsetType int types) {
+        hide(types, false /* fromIme */);
+    }
+
+    void hide(@InsetType int types, boolean fromIme) {
         int typesReady = 0;
         final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types);
         for (int i = internalTypes.size() - 1; i >= 0; i--) {
@@ -265,7 +269,7 @@
             }
             typesReady |= InsetsState.toPublicType(consumer.getType());
         }
-        applyAnimation(typesReady, false /* show */, false /* fromIme */);
+        applyAnimation(typesReady, false /* show */, fromIme /* fromIme */);
     }
 
     @Override
@@ -331,42 +335,35 @@
         boolean isReady = true;
         for (int i = internalTypes.size() - 1; i >= 0; i--) {
             InsetsSourceConsumer consumer = getSourceConsumer(internalTypes.valueAt(i));
-            // Double check for IME that IME target window has focus.
-            if (consumer.getType() != TYPE_IME || consumer.hasWindowFocus()) {
-                boolean setVisible = !consumer.isVisible();
-                if (setVisible) {
-                    // Show request
-                    switch(consumer.requestShow(fromIme)) {
-                        case ShowResult.SHOW_IMMEDIATELY:
-                            typesReady |= InsetsState.toPublicType(consumer.getType());
-                            break;
-                        case ShowResult.SHOW_DELAYED:
-                            isReady = false;
-                            break;
-                        case ShowResult.SHOW_FAILED:
-                            // IME cannot be shown (since it didn't have focus), proceed
-                            // with animation of other types.
-                            if (mPendingTypesToShow != 0) {
-                                // remove IME from pending because view no longer has focus.
-                                mPendingTypesToShow &= ~InsetsState.toPublicType(TYPE_IME);
-                            }
-                            break;
-                    }
-                } else {
-                    // Hide request
-                    // TODO: Move notifyHidden() to beginning of the hide animation
-                    // (when visibility actually changes using hideDirectly()).
-                    consumer.notifyHidden();
-                    typesReady |= InsetsState.toPublicType(consumer.getType());
+            boolean setVisible = !consumer.isVisible();
+            if (setVisible) {
+                // Show request
+                switch(consumer.requestShow(fromIme)) {
+                    case ShowResult.SHOW_IMMEDIATELY:
+                        typesReady |= InsetsState.toPublicType(consumer.getType());
+                        break;
+                    case ShowResult.SHOW_DELAYED:
+                        isReady = false;
+                        break;
+                    case ShowResult.SHOW_FAILED:
+                        // IME cannot be shown (since it didn't have focus), proceed
+                        // with animation of other types.
+                        if (mPendingTypesToShow != 0) {
+                            // remove IME from pending because view no longer has focus.
+                            mPendingTypesToShow &= ~InsetsState.toPublicType(TYPE_IME);
+                        }
+                        break;
                 }
-                consumers.put(consumer.getType(), consumer);
             } else {
-                // window doesnt have focus, no-op.
-                isReady = false;
-                // TODO: Let the calling app know that window has lost focus and
-                //       show()/hide()/controlWindowInsetsAnimation requests will be ignored.
-                typesReady &= ~InsetsState.toPublicType(consumer.getType());
+                // Hide request
+                // TODO: Move notifyHidden() to beginning of the hide animation
+                // (when visibility actually changes using hideDirectly()).
+                if (!fromIme) {
+                    consumer.notifyHidden();
+                }
+                typesReady |= InsetsState.toPublicType(consumer.getType());
             }
+            consumers.put(consumer.getType(), consumer);
         }
         return new Pair<>(typesReady, isReady);
     }
diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java
index 8a1fd62..ad59ae5 100644
--- a/core/java/android/view/ViewDebug.java
+++ b/core/java/android/view/ViewDebug.java
@@ -32,6 +32,7 @@
 import android.os.Debug;
 import android.os.Handler;
 import android.os.Looper;
+import android.os.Message;
 import android.os.RemoteException;
 import android.util.DisplayMetrics;
 import android.util.Log;
@@ -46,6 +47,7 @@
 import java.io.IOException;
 import java.io.OutputStream;
 import java.io.OutputStreamWriter;
+import java.lang.annotation.Annotation;
 import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -53,12 +55,12 @@
 import java.lang.reflect.AccessibleObject;
 import java.lang.reflect.Field;
 import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Member;
 import java.lang.reflect.Method;
 import java.util.ArrayDeque;
-import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashMap;
 import java.util.concurrent.Callable;
-import java.util.concurrent.CancellationException;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Executor;
@@ -68,6 +70,7 @@
 import java.util.concurrent.atomic.AtomicReference;
 import java.util.concurrent.locks.ReentrantLock;
 import java.util.function.Function;
+import java.util.stream.Stream;
 
 /**
  * Various debugging/tracing tools related to {@link View} and the view hierarchy.
@@ -331,11 +334,83 @@
         public View findHierarchyView(String className, int hashCode);
     }
 
-    private static HashMap<Class<?>, Method[]> mCapturedViewMethodsForClasses = null;
-    private static HashMap<Class<?>, Field[]> mCapturedViewFieldsForClasses = null;
+    private abstract static class PropertyInfo<T extends Annotation,
+            R extends AccessibleObject & Member> {
+
+        public final R member;
+        public final T property;
+        public final String name;
+        public final Class<?> returnType;
+
+        public String entrySuffix = "";
+        public String valueSuffix = "";
+
+        PropertyInfo(Class<T> property, R member, Class<?> returnType) {
+            this.member = member;
+            this.name = member.getName();
+            this.property = member.getAnnotation(property);
+            this.returnType = returnType;
+        }
+
+        public abstract Object invoke(Object target) throws Exception;
+
+        static <T extends Annotation> PropertyInfo<T, ?> forMethod(Method method,
+                Class<T> property) {
+            // Ensure the method return and parameter types can be resolved.
+            try {
+                if ((method.getReturnType() == Void.class)
+                        || (method.getParameterTypes().length != 0)) {
+                    return null;
+                }
+            } catch (NoClassDefFoundError e) {
+                return null;
+            }
+            if (!method.isAnnotationPresent(property)) {
+                return null;
+            }
+            method.setAccessible(true);
+
+            PropertyInfo info = new MethodPI(method, property);
+            info.entrySuffix = "()";
+            info.valueSuffix = ";";
+            return info;
+        }
+
+        static <T extends Annotation> PropertyInfo<T, ?> forField(Field field, Class<T> property) {
+            if (!field.isAnnotationPresent(property)) {
+                return null;
+            }
+            field.setAccessible(true);
+            return new FieldPI<>(field, property);
+        }
+    }
+
+    private static class MethodPI<T extends Annotation> extends PropertyInfo<T, Method> {
+
+        MethodPI(Method method, Class<T> property) {
+            super(property, method, method.getReturnType());
+        }
+
+        @Override
+        public Object invoke(Object target) throws Exception {
+            return member.invoke(target);
+        }
+    }
+
+    private static class FieldPI<T extends Annotation> extends PropertyInfo<T, Field> {
+
+        FieldPI(Field field, Class<T> property) {
+            super(property, field, field.getType());
+        }
+
+        @Override
+        public Object invoke(Object target) throws Exception {
+            return member.get(target);
+        }
+    }
 
     // Maximum delay in ms after which we stop trying to capture a View's drawing
-    private static final int CAPTURE_TIMEOUT = 4000;
+    private static final int CAPTURE_TIMEOUT = 6000;
 
     private static final String REMOTE_COMMAND_CAPTURE = "CAPTURE";
     private static final String REMOTE_COMMAND_DUMP = "DUMP";
@@ -346,9 +421,9 @@
     private static final String REMOTE_COMMAND_CAPTURE_LAYERS = "CAPTURE_LAYERS";
     private static final String REMOTE_COMMAND_OUTPUT_DISPLAYLIST = "OUTPUT_DISPLAYLIST";
 
-    private static HashMap<Class<?>, Field[]> sFieldsForClasses;
-    private static HashMap<Class<?>, Method[]> sMethodsForClasses;
-    private static HashMap<AccessibleObject, ExportedProperty> sAnnotations;
+    private static HashMap<Class<?>, PropertyInfo<ExportedProperty, ?>[]> sExportProperties;
+    private static HashMap<Class<?>, PropertyInfo<CapturedViewProperty, ?>[]>
+            sCapturedViewProperties;
 
     /**
      * @deprecated This enum is now unused
@@ -1157,6 +1232,69 @@
 
     private static void dumpViewHierarchy(Context context, ViewGroup group,
             BufferedWriter out, int level, boolean skipChildren, boolean includeProperties) {
+        cacheExportedProperties(group.getClass());
+        if (!skipChildren) {
+            cacheExportedPropertiesForChildren(group);
+        }
+        // Try to use the handler provided by the view
+        Handler handler = group.getHandler();
+        // Fall back on using the main thread
+        if (handler == null) {
+            handler = new Handler(Looper.getMainLooper());
+        }
+
+        if (handler.getLooper() == Looper.myLooper()) {
+            dumpViewHierarchyOnUIThread(context, group, out, level, skipChildren,
+                    includeProperties);
+        } else {
+            FutureTask task = new FutureTask(() ->
+                    dumpViewHierarchyOnUIThread(context, group, out, level, skipChildren,
+                            includeProperties), null);
+            Message msg = Message.obtain(handler, task);
+            msg.setAsynchronous(true);
+            handler.sendMessage(msg);
+            while (true) {
+                try {
+                    task.get(CAPTURE_TIMEOUT, java.util.concurrent.TimeUnit.MILLISECONDS);
+                    return;
+                } catch (InterruptedException e) {
+                    // try again
+                } catch (ExecutionException | TimeoutException e) {
+                    // Something unexpected happened.
+                    throw new RuntimeException(e);
+                }
+            }
+        }
+    }
+
+    private static void cacheExportedPropertiesForChildren(ViewGroup group) {
+        final int count = group.getChildCount();
+        for (int i = 0; i < count; i++) {
+            final View view = group.getChildAt(i);
+            cacheExportedProperties(view.getClass());
+            if (view instanceof ViewGroup) {
+                cacheExportedPropertiesForChildren((ViewGroup) view);
+            }
+        }
+    }
+
+    private static void cacheExportedProperties(Class<?> klass) {
+        if (sExportProperties != null && sExportProperties.containsKey(klass)) {
+            return;
+        }
+        do {
+            for (PropertyInfo<ExportedProperty, ?> info : getExportedProperties(klass)) {
+                if (!info.returnType.isPrimitive() && info.property.deepExport()) {
+                    cacheExportedProperties(info.returnType);
+                }
+            }
+            klass = klass.getSuperclass();
+        } while (klass != Object.class);
+    }
+
+
+    private static void dumpViewHierarchyOnUIThread(Context context, ViewGroup group,
+            BufferedWriter out, int level, boolean skipChildren, boolean includeProperties) {
         if (!dumpView(context, group, out, level, includeProperties)) {
             return;
         }
@@ -1169,16 +1307,16 @@
         for (int i = 0; i < count; i++) {
             final View view = group.getChildAt(i);
             if (view instanceof ViewGroup) {
-                dumpViewHierarchy(context, (ViewGroup) view, out, level + 1, skipChildren,
-                        includeProperties);
+                dumpViewHierarchyOnUIThread(context, (ViewGroup) view, out, level + 1,
+                        skipChildren, includeProperties);
             } else {
                 dumpView(context, view, out, level + 1, includeProperties);
             }
             if (view.mOverlay != null) {
                 ViewOverlay overlay = view.getOverlay();
                 ViewGroup overlayContainer = overlay.mOverlayViewGroup;
-                dumpViewHierarchy(context, overlayContainer, out, level + 2, skipChildren,
-                        includeProperties);
+                dumpViewHierarchyOnUIThread(context, overlayContainer, out, level + 2,
+                        skipChildren, includeProperties);
             }
         }
         if (group instanceof HierarchyHandler) {
@@ -1212,81 +1350,28 @@
         return true;
     }
 
-    private static Field[] getExportedPropertyFields(Class<?> klass) {
-        if (sFieldsForClasses == null) {
-            sFieldsForClasses = new HashMap<Class<?>, Field[]>();
-        }
-        if (sAnnotations == null) {
-            sAnnotations = new HashMap<AccessibleObject, ExportedProperty>(512);
-        }
-
-        final HashMap<Class<?>, Field[]> map = sFieldsForClasses;
-
-        Field[] fields = map.get(klass);
-        if (fields != null) {
-            return fields;
-        }
-
-        try {
-            final Field[] declaredFields = klass.getDeclaredFieldsUnchecked(false);
-            final ArrayList<Field> foundFields = new ArrayList<Field>();
-            for (final Field field : declaredFields) {
-              // Fields which can't be resolved have a null type.
-              if (field.getType() != null && field.isAnnotationPresent(ExportedProperty.class)) {
-                  field.setAccessible(true);
-                  foundFields.add(field);
-                  sAnnotations.put(field, field.getAnnotation(ExportedProperty.class));
-              }
-            }
-            fields = foundFields.toArray(new Field[foundFields.size()]);
-            map.put(klass, fields);
-        } catch (NoClassDefFoundError e) {
-            throw new AssertionError(e);
-        }
-
-        return fields;
+    private static <T extends Annotation> PropertyInfo<T, ?>[] convertToPropertyInfos(
+            Method[] methods, Field[] fields, Class<T> property) {
+        return Stream.of(Arrays.stream(methods).map(m -> PropertyInfo.forMethod(m, property)),
+                Arrays.stream(fields).map(f -> PropertyInfo.forField(f, property)))
+                .flatMap(Function.identity())
+                .filter(i -> i != null)
+                .toArray(PropertyInfo[]::new);
     }
 
-    private static Method[] getExportedPropertyMethods(Class<?> klass) {
-        if (sMethodsForClasses == null) {
-            sMethodsForClasses = new HashMap<Class<?>, Method[]>(100);
+    private static PropertyInfo<ExportedProperty, ?>[] getExportedProperties(Class<?> klass) {
+        if (sExportProperties == null) {
+            sExportProperties = new HashMap<>();
         }
-        if (sAnnotations == null) {
-            sAnnotations = new HashMap<AccessibleObject, ExportedProperty>(512);
+        final HashMap<Class<?>, PropertyInfo<ExportedProperty, ?>[]> map = sExportProperties;
+        PropertyInfo<ExportedProperty, ?>[] properties = sExportProperties.get(klass);
+
+        if (properties == null) {
+            properties = convertToPropertyInfos(klass.getDeclaredMethodsUnchecked(false),
+                    klass.getDeclaredFieldsUnchecked(false), ExportedProperty.class);
+            map.put(klass, properties);
         }
-
-        final HashMap<Class<?>, Method[]> map = sMethodsForClasses;
-
-        Method[] methods = map.get(klass);
-        if (methods != null) {
-            return methods;
-        }
-
-        methods = klass.getDeclaredMethodsUnchecked(false);
-
-        final ArrayList<Method> foundMethods = new ArrayList<Method>();
-        for (final Method method : methods) {
-            // Ensure the method return and parameter types can be resolved.
-            try {
-                method.getReturnType();
-                method.getParameterTypes();
-            } catch (NoClassDefFoundError e) {
-                continue;
-            }
-
-            if (method.getParameterTypes().length == 0 &&
-                    method.isAnnotationPresent(ExportedProperty.class) &&
-                    method.getReturnType() != Void.class) {
-                method.setAccessible(true);
-                foundMethods.add(method);
-                sAnnotations.put(method, method.getAnnotation(ExportedProperty.class));
-            }
-        }
-
-        methods = foundMethods.toArray(new Method[foundMethods.size()]);
-        map.put(klass, methods);
-
-        return methods;
+        return properties;
     }
 
     private static void dumpViewProperties(Context context, Object view,
@@ -1305,233 +1390,97 @@
 
         Class<?> klass = view.getClass();
         do {
-            exportFields(context, view, out, klass, prefix);
-            exportMethods(context, view, out, klass, prefix);
+            writeExportedProperties(context, view, out, klass, prefix);
             klass = klass.getSuperclass();
         } while (klass != Object.class);
     }
 
-    private static Object callMethodOnAppropriateTheadBlocking(final Method method,
-            final Object object) throws IllegalAccessException, InvocationTargetException,
-            TimeoutException {
-        if (!(object instanceof View)) {
-            return method.invoke(object, (Object[]) null);
-        }
-
-        final View view = (View) object;
-        Callable<Object> callable = new Callable<Object>() {
-            @Override
-            public Object call() throws IllegalAccessException, InvocationTargetException {
-                return method.invoke(view, (Object[]) null);
-            }
-        };
-        FutureTask<Object> future = new FutureTask<Object>(callable);
-        // Try to use the handler provided by the view
-        Handler handler = view.getHandler();
-        // Fall back on using the main thread
-        if (handler == null) {
-            handler = new Handler(android.os.Looper.getMainLooper());
-        }
-        handler.post(future);
-        while (true) {
-            try {
-                return future.get(CAPTURE_TIMEOUT, java.util.concurrent.TimeUnit.MILLISECONDS);
-            } catch (ExecutionException e) {
-                Throwable t = e.getCause();
-                if (t instanceof IllegalAccessException) {
-                    throw (IllegalAccessException)t;
-                }
-                if (t instanceof InvocationTargetException) {
-                    throw (InvocationTargetException)t;
-                }
-                throw new RuntimeException("Unexpected exception", t);
-            } catch (InterruptedException e) {
-                // Call get again
-            } catch (CancellationException e) {
-                throw new RuntimeException("Unexpected cancellation exception", e);
-            }
-        }
-    }
-
     private static String formatIntToHexString(int value) {
         return "0x" + Integer.toHexString(value).toUpperCase();
     }
 
-    private static void exportMethods(Context context, Object view, BufferedWriter out,
+    private static void writeExportedProperties(Context context, Object view, BufferedWriter out,
             Class<?> klass, String prefix) throws IOException {
-
-        final Method[] methods = getExportedPropertyMethods(klass);
-        int count = methods.length;
-        for (int i = 0; i < count; i++) {
-            final Method method = methods[i];
+        for (PropertyInfo<ExportedProperty, ?> info : getExportedProperties(klass)) {
             //noinspection EmptyCatchBlock
+            Object value;
             try {
-                Object methodValue = callMethodOnAppropriateTheadBlocking(method, view);
-                final Class<?> returnType = method.getReturnType();
-                final ExportedProperty property = sAnnotations.get(method);
-                String categoryPrefix =
-                        property.category().length() != 0 ? property.category() + ":" : "";
-
-                if (returnType == int.class) {
-                    if (property.resolveId() && context != null) {
-                        final int id = (Integer) methodValue;
-                        methodValue = resolveId(context, id);
-                    } else {
-                        final FlagToString[] flagsMapping = property.flagMapping();
-                        if (flagsMapping.length > 0) {
-                            final int intValue = (Integer) methodValue;
-                            final String valuePrefix =
-                                    categoryPrefix + prefix + method.getName() + '_';
-                            exportUnrolledFlags(out, flagsMapping, intValue, valuePrefix);
-                        }
-
-                        final IntToString[] mapping = property.mapping();
-                        if (mapping.length > 0) {
-                            final int intValue = (Integer) methodValue;
-                            boolean mapped = false;
-                            int mappingCount = mapping.length;
-                            for (int j = 0; j < mappingCount; j++) {
-                                final IntToString mapper = mapping[j];
-                                if (mapper.from() == intValue) {
-                                    methodValue = mapper.to();
-                                    mapped = true;
-                                    break;
-                                }
-                            }
-
-                            if (!mapped) {
-                                methodValue = intValue;
-                            }
-                        }
-                    }
-                } else if (returnType == int[].class) {
-                    final int[] array = (int[]) methodValue;
-                    final String valuePrefix = categoryPrefix + prefix + method.getName() + '_';
-                    final String suffix = "()";
-
-                    exportUnrolledArray(context, out, property, array, valuePrefix, suffix);
-
-                    continue;
-                } else if (returnType == String[].class) {
-                    final String[] array = (String[]) methodValue;
-                    if (property.hasAdjacentMapping() && array != null) {
-                        for (int j = 0; j < array.length; j += 2) {
-                            if (array[j] != null) {
-                                writeEntry(out, categoryPrefix + prefix, array[j], "()",
-                                        array[j + 1] == null ? "null" : array[j + 1]);
-                            }
-
-                        }
-                    }
-
-                    continue;
-                } else if (!returnType.isPrimitive()) {
-                    if (property.deepExport()) {
-                        dumpViewProperties(context, methodValue, out, prefix + property.prefix());
-                        continue;
-                    }
-                }
-
-                writeEntry(out, categoryPrefix + prefix, method.getName(), "()", methodValue);
-            } catch (IllegalAccessException e) {
-            } catch (InvocationTargetException e) {
-            } catch (TimeoutException e) {
+                value = info.invoke(view);
+            } catch (Exception e) {
+                // ignore
+                continue;
             }
-        }
-    }
 
-    private static void exportFields(Context context, Object view, BufferedWriter out,
-            Class<?> klass, String prefix) throws IOException {
+            String categoryPrefix =
+                    info.property.category().length() != 0 ? info.property.category() + ":" : "";
 
-        final Field[] fields = getExportedPropertyFields(klass);
+            if (info.returnType == int.class || info.returnType == byte.class) {
+                if (info.property.resolveId() && context != null) {
+                    final int id = (Integer) value;
+                    value = resolveId(context, id);
 
-        int count = fields.length;
-        for (int i = 0; i < count; i++) {
-            final Field field = fields[i];
-
-            //noinspection EmptyCatchBlock
-            try {
-                Object fieldValue = null;
-                final Class<?> type = field.getType();
-                final ExportedProperty property = sAnnotations.get(field);
-                String categoryPrefix =
-                        property.category().length() != 0 ? property.category() + ":" : "";
-
-                if (type == int.class || type == byte.class) {
-                    if (property.resolveId() && context != null) {
-                        final int id = field.getInt(view);
-                        fieldValue = resolveId(context, id);
-                    } else {
-                        final FlagToString[] flagsMapping = property.flagMapping();
-                        if (flagsMapping.length > 0) {
-                            final int intValue = field.getInt(view);
-                            final String valuePrefix =
-                                    categoryPrefix + prefix + field.getName() + '_';
-                            exportUnrolledFlags(out, flagsMapping, intValue, valuePrefix);
-                        }
-
-                        final IntToString[] mapping = property.mapping();
-                        if (mapping.length > 0) {
-                            final int intValue = field.getInt(view);
-                            int mappingCount = mapping.length;
-                            for (int j = 0; j < mappingCount; j++) {
-                                final IntToString mapped = mapping[j];
-                                if (mapped.from() == intValue) {
-                                    fieldValue = mapped.to();
-                                    break;
-                                }
-                            }
-
-                            if (fieldValue == null) {
-                                fieldValue = intValue;
-                            }
-                        }
-
-                        if (property.formatToHexString()) {
-                            fieldValue = field.get(view);
-                            if (type == int.class) {
-                                fieldValue = formatIntToHexString((Integer) fieldValue);
-                            } else if (type == byte.class) {
-                                fieldValue = "0x"
-                                        + HexEncoding.encodeToString((Byte) fieldValue, true);
-                            }
-                        }
+                } else if (info.property.formatToHexString()) {
+                    if (info.returnType == int.class) {
+                        value = formatIntToHexString((Integer) value);
+                    } else if (info.returnType == byte.class) {
+                        value = "0x"
+                                + HexEncoding.encodeToString((Byte) value, true);
                     }
-                } else if (type == int[].class) {
-                    final int[] array = (int[]) field.get(view);
-                    final String valuePrefix = categoryPrefix + prefix + field.getName() + '_';
-                    final String suffix = "";
-
-                    exportUnrolledArray(context, out, property, array, valuePrefix, suffix);
-
-                    continue;
-                } else if (type == String[].class) {
-                    final String[] array = (String[]) field.get(view);
-                    if (property.hasAdjacentMapping() && array != null) {
-                        for (int j = 0; j < array.length; j += 2) {
-                            if (array[j] != null) {
-                                writeEntry(out, categoryPrefix + prefix, array[j], "",
-                                        array[j + 1] == null ? "null" : array[j + 1]);
-                            }
-                        }
+                } else {
+                    final ViewDebug.FlagToString[] flagsMapping = info.property.flagMapping();
+                    if (flagsMapping.length > 0) {
+                        final int intValue = (Integer) value;
+                        final String valuePrefix =
+                                categoryPrefix + prefix + info.name + '_';
+                        exportUnrolledFlags(out, flagsMapping, intValue, valuePrefix);
                     }
 
-                    continue;
-                } else if (!type.isPrimitive()) {
-                    if (property.deepExport()) {
-                        dumpViewProperties(context, field.get(view), out, prefix +
-                                property.prefix());
-                        continue;
+                    final ViewDebug.IntToString[] mapping = info.property.mapping();
+                    if (mapping.length > 0) {
+                        final int intValue = (Integer) value;
+                        boolean mapped = false;
+                        int mappingCount = mapping.length;
+                        for (int j = 0; j < mappingCount; j++) {
+                            final ViewDebug.IntToString mapper = mapping[j];
+                            if (mapper.from() == intValue) {
+                                value = mapper.to();
+                                mapped = true;
+                                break;
+                            }
+                        }
+
+                        if (!mapped) {
+                            value = intValue;
+                        }
+                    }
+                }
+            } else if (info.returnType == int[].class) {
+                final int[] array = (int[]) value;
+                final String valuePrefix = categoryPrefix + prefix + info.name + '_';
+                exportUnrolledArray(context, out, info.property, array, valuePrefix,
+                        info.entrySuffix);
+
+                continue;
+            } else if (info.returnType == String[].class) {
+                final String[] array = (String[]) value;
+                if (info.property.hasAdjacentMapping() && array != null) {
+                    for (int j = 0; j < array.length; j += 2) {
+                        if (array[j] != null) {
+                            writeEntry(out, categoryPrefix + prefix, array[j],
+                                    info.entrySuffix, array[j + 1] == null ? "null" : array[j + 1]);
+                        }
                     }
                 }
 
-                if (fieldValue == null) {
-                    fieldValue = field.get(view);
+                continue;
+            } else if (!info.returnType.isPrimitive()) {
+                if (info.property.deepExport()) {
+                    dumpViewProperties(context, value, out, prefix + info.property.prefix());
+                    continue;
                 }
-
-                writeEntry(out, categoryPrefix + prefix, field.getName(), "", fieldValue);
-            } catch (IllegalAccessException e) {
             }
+
+            writeEntry(out, categoryPrefix + prefix, info.name, info.entrySuffix, value);
         }
     }
 
@@ -1721,91 +1670,40 @@
         }
     }
 
-    private static Field[] capturedViewGetPropertyFields(Class<?> klass) {
-        if (mCapturedViewFieldsForClasses == null) {
-            mCapturedViewFieldsForClasses = new HashMap<Class<?>, Field[]>();
+    private static PropertyInfo<CapturedViewProperty, ?>[] getCapturedViewProperties(
+            Class<?> klass) {
+        if (sCapturedViewProperties == null) {
+            sCapturedViewProperties = new HashMap<>();
         }
-        final HashMap<Class<?>, Field[]> map = mCapturedViewFieldsForClasses;
+        final HashMap<Class<?>, PropertyInfo<CapturedViewProperty, ?>[]> map =
+                sCapturedViewProperties;
 
-        Field[] fields = map.get(klass);
-        if (fields != null) {
-            return fields;
+        PropertyInfo<CapturedViewProperty, ?>[] infos = map.get(klass);
+        if (infos == null) {
+            infos = convertToPropertyInfos(klass.getMethods(), klass.getFields(),
+                    CapturedViewProperty.class);
+            map.put(klass, infos);
         }
-
-        final ArrayList<Field> foundFields = new ArrayList<Field>();
-        fields = klass.getFields();
-
-        int count = fields.length;
-        for (int i = 0; i < count; i++) {
-            final Field field = fields[i];
-            if (field.isAnnotationPresent(CapturedViewProperty.class)) {
-                field.setAccessible(true);
-                foundFields.add(field);
-            }
-        }
-
-        fields = foundFields.toArray(new Field[foundFields.size()]);
-        map.put(klass, fields);
-
-        return fields;
+        return infos;
     }
 
-    private static Method[] capturedViewGetPropertyMethods(Class<?> klass) {
-        if (mCapturedViewMethodsForClasses == null) {
-            mCapturedViewMethodsForClasses = new HashMap<Class<?>, Method[]>();
-        }
-        final HashMap<Class<?>, Method[]> map = mCapturedViewMethodsForClasses;
-
-        Method[] methods = map.get(klass);
-        if (methods != null) {
-            return methods;
-        }
-
-        final ArrayList<Method> foundMethods = new ArrayList<Method>();
-        methods = klass.getMethods();
-
-        int count = methods.length;
-        for (int i = 0; i < count; i++) {
-            final Method method = methods[i];
-            if (method.getParameterTypes().length == 0 &&
-                    method.isAnnotationPresent(CapturedViewProperty.class) &&
-                    method.getReturnType() != Void.class) {
-                method.setAccessible(true);
-                foundMethods.add(method);
-            }
-        }
-
-        methods = foundMethods.toArray(new Method[foundMethods.size()]);
-        map.put(klass, methods);
-
-        return methods;
-    }
-
-    private static String capturedViewExportMethods(Object obj, Class<?> klass,
-            String prefix) {
-
+    private static String exportCapturedViewProperties(Object obj, Class<?> klass, String prefix) {
         if (obj == null) {
             return "null";
         }
 
         StringBuilder sb = new StringBuilder();
-        final Method[] methods = capturedViewGetPropertyMethods(klass);
 
-        int count = methods.length;
-        for (int i = 0; i < count; i++) {
-            final Method method = methods[i];
+        for (PropertyInfo<CapturedViewProperty, ?> pi : getCapturedViewProperties(klass)) {
             try {
-                Object methodValue = method.invoke(obj, (Object[]) null);
-                final Class<?> returnType = method.getReturnType();
+                Object methodValue = pi.invoke(obj);
 
-                CapturedViewProperty property = method.getAnnotation(CapturedViewProperty.class);
-                if (property.retrieveReturn()) {
+                if (pi.property.retrieveReturn()) {
                     //we are interested in the second level data only
-                    sb.append(capturedViewExportMethods(methodValue, returnType, method.getName() + "#"));
+                    sb.append(exportCapturedViewProperties(methodValue, pi.returnType,
+                            pi.name + "#"));
                 } else {
-                    sb.append(prefix);
-                    sb.append(method.getName());
-                    sb.append("()=");
+                    sb.append(prefix).append(pi.name).append(pi.entrySuffix).append("=");
 
                     if (methodValue != null) {
                         final String value = methodValue.toString().replace("\n", "\\n");
@@ -1813,47 +1711,10 @@
                     } else {
                         sb.append("null");
                     }
-                    sb.append("; ");
+                    sb.append(pi.valueSuffix).append(" ");
                 }
-            } catch (IllegalAccessException e) {
-                //Exception IllegalAccess, it is OK here
-                //we simply ignore this method
-            } catch (InvocationTargetException e) {
-                //Exception InvocationTarget, it is OK here
-                //we simply ignore this method
-            }
-        }
-        return sb.toString();
-    }
-
-    private static String capturedViewExportFields(Object obj, Class<?> klass, String prefix) {
-        if (obj == null) {
-            return "null";
-        }
-
-        StringBuilder sb = new StringBuilder();
-        final Field[] fields = capturedViewGetPropertyFields(klass);
-
-        int count = fields.length;
-        for (int i = 0; i < count; i++) {
-            final Field field = fields[i];
-            try {
-                Object fieldValue = field.get(obj);
-
-                sb.append(prefix);
-                sb.append(field.getName());
-                sb.append("=");
-
-                if (fieldValue != null) {
-                    final String value = fieldValue.toString().replace("\n", "\\n");
-                    sb.append(value);
-                } else {
-                    sb.append("null");
-                }
-                sb.append(' ');
-            } catch (IllegalAccessException e) {
-                //Exception IllegalAccess, it is OK here
-                //we simply ignore this field
+            } catch (Exception e) {
+                //It is OK here, we simply ignore this property
             }
         }
         return sb.toString();
@@ -1869,8 +1730,7 @@
     public static void dumpCapturedView(String tag, Object view) {
         Class<?> klass = view.getClass();
         StringBuilder sb = new StringBuilder(klass.getName() + ": ");
-        sb.append(capturedViewExportFields(view, klass, ""));
-        sb.append(capturedViewExportMethods(view, klass, ""));
+        sb.append(exportCapturedViewProperties(view, klass, ""));
         Log.d(tag, sb.toString());
     }
 
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 9ddd84f..20dc234 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -4570,6 +4570,7 @@
     private static final int MSG_SYSTEM_GESTURE_EXCLUSION_CHANGED = 32;
     private static final int MSG_LOCATION_IN_PARENT_DISPLAY_CHANGED = 33;
     private static final int MSG_SHOW_INSETS = 34;
+    private static final int MSG_HIDE_INSETS = 35;
 
 
     final class ViewRootHandler extends Handler {
@@ -4636,6 +4637,8 @@
                     return "MSG_LOCATION_IN_PARENT_DISPLAY_CHANGED";
                 case MSG_SHOW_INSETS:
                     return "MSG_SHOW_INSETS";
+                case MSG_HIDE_INSETS:
+                    return "MSG_HIDE_INSETS";
             }
             return super.getMessageName(message);
         }
@@ -4754,6 +4757,10 @@
                     mInsetsController.show(msg.arg1, msg.arg2 == 1);
                     break;
                 }
+                case MSG_HIDE_INSETS: {
+                    mInsetsController.hide(msg.arg1, msg.arg2 == 1);
+                    break;
+                }
                 case MSG_WINDOW_MOVED:
                     if (mAdded) {
                         final int w = mWinFrame.width();
@@ -7559,6 +7566,10 @@
         mHandler.obtainMessage(MSG_SHOW_INSETS, types, fromIme ? 1 : 0).sendToTarget();
     }
 
+    private void hideInsets(@InsetType int types, boolean fromIme) {
+        mHandler.obtainMessage(MSG_HIDE_INSETS, types, fromIme ? 1 : 0).sendToTarget();
+    }
+
     public void dispatchMoved(int newX, int newY) {
         if (DEBUG_LAYOUT) Log.v(mTag, "Window moved " + this + ": newX=" + newX + " newY=" + newY);
         if (mTranslator != null) {
@@ -8682,6 +8693,14 @@
         }
 
         @Override
+        public void hideInsets(@InsetType int types, boolean fromIme) {
+            final ViewRootImpl viewAncestor = mViewAncestor.get();
+            if (viewAncestor != null) {
+                viewAncestor.hideInsets(types, fromIme);
+            }
+        }
+
+        @Override
         public void moved(int newX, int newY) {
             final ViewRootImpl viewAncestor = mViewAncestor.get();
             if (viewAncestor != null) {
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 0b42cd9..db76bb6 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -1680,8 +1680,9 @@
          * to determine its default behavior.
          *
          * {@hide} */
-        @UnsupportedAppUsage
-        public static final int PRIVATE_FLAG_SHOW_FOR_ALL_USERS = 0x00000010;
+        @SystemApi
+        @RequiresPermission(permission.INTERNAL_SYSTEM_WINDOW)
+        public static final int SYSTEM_FLAG_SHOW_FOR_ALL_USERS = 0x00000010;
 
         /**
          * Never animate position changes of the window.
@@ -1842,6 +1843,7 @@
         @Retention(RetentionPolicy.SOURCE)
         @IntDef(flag = true, prefix = { "SYSTEM_FLAG_" }, value = {
                 SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS,
+                SYSTEM_FLAG_SHOW_FOR_ALL_USERS,
         })
         public @interface SystemFlags {}
 
@@ -1863,8 +1865,8 @@
                         equals = PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS,
                         name = "WANTS_OFFSET_NOTIFICATIONS"),
                 @ViewDebug.FlagToString(
-                        mask = PRIVATE_FLAG_SHOW_FOR_ALL_USERS,
-                        equals = PRIVATE_FLAG_SHOW_FOR_ALL_USERS,
+                        mask = SYSTEM_FLAG_SHOW_FOR_ALL_USERS,
+                        equals = SYSTEM_FLAG_SHOW_FOR_ALL_USERS,
                         name = "SHOW_FOR_ALL_USERS"),
                 @ViewDebug.FlagToString(
                         mask = PRIVATE_FLAG_NO_MOVE_ANIMATION,
diff --git a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
index 925a589..0b15cd0 100644
--- a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
+++ b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java
@@ -246,7 +246,7 @@
                 Toast warningToast = mFrameworkObjectProvider.makeToastFromText(
                         mContext, toastMessage, Toast.LENGTH_LONG);
                 warningToast.getWindowParams().privateFlags |=
-                        WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+                        WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
                 warningToast.show();
             }
 
diff --git a/core/java/com/android/internal/app/procstats/AssociationState.java b/core/java/com/android/internal/app/procstats/AssociationState.java
index 0ed2524..1d4239f 100644
--- a/core/java/com/android/internal/app/procstats/AssociationState.java
+++ b/core/java/com/android/internal/app/procstats/AssociationState.java
@@ -950,6 +950,14 @@
 
         proto.write(PackageAssociationProcessStatsProto.COMPONENT_NAME, mName);
 
+        proto.write(PackageAssociationProcessStatsProto.TOTAL_COUNT, mTotalCount);
+        proto.write(PackageAssociationProcessStatsProto.TOTAL_DURATION_MS, getTotalDuration(now));
+        if (mTotalActiveCount != 0) {
+            proto.write(PackageAssociationProcessStatsProto.ACTIVE_COUNT, mTotalActiveCount);
+            proto.write(PackageAssociationProcessStatsProto.ACTIVE_DURATION_MS,
+                    getActiveDuration(now));
+        }
+
         final int NSRC = mSources.size();
         for (int isrc = 0; isrc < NSRC; isrc++) {
             final SourceKey key = mSources.keyAt(isrc);
diff --git a/core/java/com/android/internal/content/FileSystemProvider.java b/core/java/com/android/internal/content/FileSystemProvider.java
index cdb79ab..f5708a5c 100644
--- a/core/java/com/android/internal/content/FileSystemProvider.java
+++ b/core/java/com/android/internal/content/FileSystemProvider.java
@@ -17,6 +17,7 @@
 package com.android.internal.content;
 
 import android.annotation.CallSuper;
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.ContentResolver;
 import android.content.Intent;
@@ -552,6 +553,11 @@
                     flags |= Document.FLAG_SUPPORTS_DELETE;
                     flags |= Document.FLAG_SUPPORTS_RENAME;
                     flags |= Document.FLAG_SUPPORTS_MOVE;
+
+                    if (shouldBlockFromTree(docId)) {
+                        flags |= Document.FLAG_DIR_BLOCKS_TREE;
+                    }
+
                 } else {
                     flags |= Document.FLAG_SUPPORTS_WRITE;
                     flags |= Document.FLAG_SUPPORTS_DELETE;
@@ -592,6 +598,10 @@
         return row;
     }
 
+    protected boolean shouldBlockFromTree(@NonNull String docId) {
+        return false;
+    }
+
     protected boolean typeSupportsMetadata(String mimeType) {
         return MetadataReader.isSupportedMimeType(mimeType)
                 || Document.MIME_TYPE_DIR.equals(mimeType);
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index a845b58..659134a 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -17,6 +17,7 @@
 package com.android.internal.statusbar;
 
 import android.app.Notification;
+import android.net.Uri;
 import android.content.ComponentName;
 import android.graphics.Rect;
 import android.os.Bundle;
@@ -77,6 +78,7 @@
     void onNotificationSettingsViewed(String key);
     void setSystemUiVisibility(int displayId, int vis, int mask, String cause);
     void onNotificationBubbleChanged(String key, boolean isBubble);
+    void grantInlineReplyUriPermission(String key, in Uri uri);
 
     void onGlobalActionsShown();
     void onGlobalActionsHidden();
diff --git a/core/java/com/android/internal/view/BaseIWindow.java b/core/java/com/android/internal/view/BaseIWindow.java
index 0e078dd..7e1f13a 100644
--- a/core/java/com/android/internal/view/BaseIWindow.java
+++ b/core/java/com/android/internal/view/BaseIWindow.java
@@ -80,6 +80,10 @@
     }
 
     @Override
+    public void hideInsets(@InsetType int types, boolean fromIme)  throws RemoteException {
+    }
+
+    @Override
     public void moved(int newX, int newY) {
     }
 
diff --git a/core/java/com/android/internal/widget/LockPatternChecker.java b/core/java/com/android/internal/widget/LockPatternChecker.java
index 09bc28c..85a45fd 100644
--- a/core/java/com/android/internal/widget/LockPatternChecker.java
+++ b/core/java/com/android/internal/widget/LockPatternChecker.java
@@ -1,13 +1,9 @@
 package com.android.internal.widget;
 
-import android.annotation.UnsupportedAppUsage;
 import android.os.AsyncTask;
 
 import com.android.internal.widget.LockPatternUtils.RequestThrottledException;
 
-import java.util.ArrayList;
-import java.util.List;
-
 /**
  * Helper class to check/verify PIN/Password/Pattern asynchronously.
  */
@@ -53,34 +49,28 @@
     }
 
     /**
-     * Verify a pattern asynchronously.
+     * Verify a lockscreen credential asynchronously.
      *
      * @param utils The LockPatternUtils instance to use.
-     * @param pattern The pattern to check.
-     * @param challenge The challenge to verify against the pattern.
-     * @param userId The user to check against the pattern.
+     * @param credential The credential to check.
+     * @param challenge The challenge to verify against the credential.
+     * @param userId The user to check against the credential.
      * @param callback The callback to be invoked with the verification result.
      */
-    public static AsyncTask<?, ?, ?> verifyPattern(final LockPatternUtils utils,
-            final List<LockPatternView.Cell> pattern,
+    public static AsyncTask<?, ?, ?> verifyCredential(final LockPatternUtils utils,
+            final LockscreenCredential credential,
             final long challenge,
             final int userId,
             final OnVerifyCallback callback) {
+        // Create a copy of the credential since checking credential is asynchrounous.
+        final LockscreenCredential credentialCopy = credential.duplicate();
         AsyncTask<Void, Void, byte[]> task = new AsyncTask<Void, Void, byte[]>() {
             private int mThrottleTimeout;
-            private List<LockPatternView.Cell> patternCopy;
-
-            @Override
-            protected void onPreExecute() {
-                // Make a copy of the pattern to prevent race conditions.
-                // No need to clone the individual cells because they are immutable.
-                patternCopy = new ArrayList(pattern);
-            }
 
             @Override
             protected byte[] doInBackground(Void... args) {
                 try {
-                    return utils.verifyPattern(patternCopy, challenge, userId);
+                    return utils.verifyCredential(credentialCopy, challenge, userId);
                 } catch (RequestThrottledException ex) {
                     mThrottleTimeout = ex.getTimeoutMs();
                     return null;
@@ -90,6 +80,12 @@
             @Override
             protected void onPostExecute(byte[] result) {
                 callback.onVerified(result, mThrottleTimeout);
+                credentialCopy.zeroize();
+            }
+
+            @Override
+            protected void onCancelled() {
+                credentialCopy.zeroize();
             }
         };
         task.execute();
@@ -97,32 +93,26 @@
     }
 
     /**
-     * Checks a pattern asynchronously.
+     * Checks a lockscreen credential asynchronously.
      *
      * @param utils The LockPatternUtils instance to use.
-     * @param pattern The pattern to check.
-     * @param userId The user to check against the pattern.
+     * @param credential The credential to check.
+     * @param userId The user to check against the credential.
      * @param callback The callback to be invoked with the check result.
      */
-    public static AsyncTask<?, ?, ?> checkPattern(final LockPatternUtils utils,
-            final List<LockPatternView.Cell> pattern,
+    public static AsyncTask<?, ?, ?> checkCredential(final LockPatternUtils utils,
+            final LockscreenCredential credential,
             final int userId,
             final OnCheckCallback callback) {
+        // Create a copy of the credential since checking credential is asynchrounous.
+        final LockscreenCredential credentialCopy = credential.duplicate();
         AsyncTask<Void, Void, Boolean> task = new AsyncTask<Void, Void, Boolean>() {
             private int mThrottleTimeout;
-            private List<LockPatternView.Cell> patternCopy;
-
-            @Override
-            protected void onPreExecute() {
-                // Make a copy of the pattern to prevent race conditions.
-                // No need to clone the individual cells because they are immutable.
-                patternCopy = new ArrayList(pattern);
-            }
 
             @Override
             protected Boolean doInBackground(Void... args) {
                 try {
-                    return utils.checkPattern(patternCopy, userId, callback::onEarlyMatched);
+                    return utils.checkCredential(credentialCopy, userId, callback::onEarlyMatched);
                 } catch (RequestThrottledException ex) {
                     mThrottleTimeout = ex.getTimeoutMs();
                     return false;
@@ -132,11 +122,13 @@
             @Override
             protected void onPostExecute(Boolean result) {
                 callback.onChecked(result, mThrottleTimeout);
+                credentialCopy.zeroize();
             }
 
             @Override
             protected void onCancelled() {
                 callback.onCancelled();
+                credentialCopy.zeroize();
             }
         };
         task.execute();
@@ -144,84 +136,29 @@
     }
 
     /**
-     * Verify a password asynchronously.
+     * Perform a lockscreen credential verification explicitly on a managed profile with unified
+     * challenge, using the parent user's credential.
      *
      * @param utils The LockPatternUtils instance to use.
-     * @param password The password to check.
-     * @param challenge The challenge to verify against the pattern.
-     * @param userId The user to check against the pattern.
-     * @param callback The callback to be invoked with the verification result.
-     *
-     * @deprecated Pass the password as a byte array.
-     */
-    @Deprecated
-    public static AsyncTask<?, ?, ?> verifyPassword(final LockPatternUtils utils,
-            final String password,
-            final long challenge,
-            final int userId,
-            final OnVerifyCallback callback) {
-        byte[] passwordBytes = password != null ? password.getBytes() : null;
-        return verifyPassword(utils, passwordBytes, challenge, userId, callback);
-    }
-
-    /**
-     * Verify a password asynchronously.
-     *
-     * @param utils The LockPatternUtils instance to use.
-     * @param password The password to check.
-     * @param challenge The challenge to verify against the pattern.
-     * @param userId The user to check against the pattern.
-     * @param callback The callback to be invoked with the verification result.
-     */
-    public static AsyncTask<?, ?, ?> verifyPassword(final LockPatternUtils utils,
-            final byte[] password,
-            final long challenge,
-            final int userId,
-            final OnVerifyCallback callback) {
-        AsyncTask<Void, Void, byte[]> task = new AsyncTask<Void, Void, byte[]>() {
-            private int mThrottleTimeout;
-
-            @Override
-            protected byte[] doInBackground(Void... args) {
-                try {
-                    return utils.verifyPassword(password, challenge, userId);
-                } catch (RequestThrottledException ex) {
-                    mThrottleTimeout = ex.getTimeoutMs();
-                    return null;
-                }
-            }
-
-            @Override
-            protected void onPostExecute(byte[] result) {
-                callback.onVerified(result, mThrottleTimeout);
-            }
-        };
-        task.execute();
-        return task;
-    }
-
-    /**
-     * Verify a password asynchronously.
-     *
-     * @param utils The LockPatternUtils instance to use.
-     * @param password The password to check.
-     * @param challenge The challenge to verify against the pattern.
-     * @param userId The user to check against the pattern.
+     * @param credential The credential to check.
+     * @param challenge The challenge to verify against the credential.
+     * @param userId The user to check against the credential.
      * @param callback The callback to be invoked with the verification result.
      */
     public static AsyncTask<?, ?, ?> verifyTiedProfileChallenge(final LockPatternUtils utils,
-            final byte[] password,
-            final boolean isPattern,
+            final LockscreenCredential credential,
             final long challenge,
             final int userId,
             final OnVerifyCallback callback) {
+        // Create a copy of the credential since checking credential is asynchrounous.
+        final LockscreenCredential credentialCopy = credential.duplicate();
         AsyncTask<Void, Void, byte[]> task = new AsyncTask<Void, Void, byte[]>() {
             private int mThrottleTimeout;
 
             @Override
             protected byte[] doInBackground(Void... args) {
                 try {
-                    return utils.verifyTiedProfileChallenge(password, isPattern, challenge, userId);
+                    return utils.verifyTiedProfileChallenge(credentialCopy, challenge, userId);
                 } catch (RequestThrottledException ex) {
                     mThrottleTimeout = ex.getTimeoutMs();
                     return null;
@@ -231,64 +168,12 @@
             @Override
             protected void onPostExecute(byte[] result) {
                 callback.onVerified(result, mThrottleTimeout);
-            }
-        };
-        task.execute();
-        return task;
-    }
-
-    /**
-     * Checks a password asynchronously.
-     *
-     * @param utils The LockPatternUtils instance to use.
-     * @param password The password to check.
-     * @param userId The user to check against the pattern.
-     * @param callback The callback to be invoked with the check result.
-     * @deprecated Pass passwords as byte[]
-     */
-    @UnsupportedAppUsage
-    @Deprecated
-    public static AsyncTask<?, ?, ?> checkPassword(final LockPatternUtils utils,
-            final String password,
-            final int userId,
-            final OnCheckCallback callback) {
-        byte[] passwordBytes = password != null ? password.getBytes() : null;
-        return checkPassword(utils, passwordBytes, userId, callback);
-    }
-
-    /**
-     * Checks a password asynchronously.
-     *
-     * @param utils The LockPatternUtils instance to use.
-     * @param passwordBytes The password to check.
-     * @param userId The user to check against the pattern.
-     * @param callback The callback to be invoked with the check result.
-     */
-    public static AsyncTask<?, ?, ?> checkPassword(final LockPatternUtils utils,
-            final byte[] passwordBytes,
-            final int userId,
-            final OnCheckCallback callback) {
-        AsyncTask<Void, Void, Boolean> task = new AsyncTask<Void, Void, Boolean>() {
-            private int mThrottleTimeout;
-
-            @Override
-            protected Boolean doInBackground(Void... args) {
-                try {
-                    return utils.checkPassword(passwordBytes, userId, callback::onEarlyMatched);
-                } catch (RequestThrottledException ex) {
-                    mThrottleTimeout = ex.getTimeoutMs();
-                    return false;
-                }
-            }
-
-            @Override
-            protected void onPostExecute(Boolean result) {
-                callback.onChecked(result, mThrottleTimeout);
+                credentialCopy.zeroize();
             }
 
             @Override
             protected void onCancelled() {
-                callback.onCancelled();
+                credentialCopy.zeroize();
             }
         };
         task.execute();
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index 070121c..1daa25a 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -26,10 +26,10 @@
 import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
 
 import android.annotation.IntDef;
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UnsupportedAppUsage;
 import android.app.admin.DevicePolicyManager;
-import android.app.admin.PasswordMetrics;
 import android.app.trust.IStrongAuthTracker;
 import android.app.trust.TrustManager;
 import android.content.ComponentName;
@@ -365,11 +365,24 @@
                 null /* componentName */, userId);
     }
 
-    private byte[] verifyCredential(byte[] credential, int type, long challenge, int userId)
-            throws RequestThrottledException {
+    /**
+     * Check to see if a credential matches the saved one.
+     * If credential matches, return an opaque attestation that the challenge was verified.
+     *
+     * @param credential The credential to check.
+     * @param challenge The challenge to verify against the credential
+     * @return the attestation that the challenge was verified, or null
+     * @param userId The user whose credential is being verified
+     * @throws RequestThrottledException if credential verification is being throttled due to
+     *         to many incorrect attempts.
+     * @throws IllegalStateException if called on the main thread.
+     */
+    public byte[] verifyCredential(@NonNull LockscreenCredential credential, long challenge,
+            int userId) throws RequestThrottledException {
+        throwIfCalledOnMainThread();
         try {
-            VerifyCredentialResponse response = getLockSettings().verifyCredential(credential,
-                    type, challenge, userId);
+            VerifyCredentialResponse response = getLockSettings().verifyCredential(
+                    credential.getCredential(), credential.getType(), challenge, userId);
             if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
                 return response.getPayload();
             } else if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_RETRY) {
@@ -382,11 +395,24 @@
         }
     }
 
-    private boolean checkCredential(byte[] credential, int type, int userId,
+    /**
+     * Check to see if a credential matches the saved one.
+     *
+     * @param credential The credential to check.
+     * @param userId The user whose credential is being checked
+     * @param progressCallback callback to deliver early signal that the credential matches
+     * @return {@code true} if credential matches, {@code false} otherwise
+     * @throws RequestThrottledException if credential verification is being throttled due to
+     *         to many incorrect attempts.
+     * @throws IllegalStateException if called on the main thread.
+     */
+    public boolean checkCredential(@NonNull LockscreenCredential credential, int userId,
             @Nullable CheckCredentialProgressCallback progressCallback)
             throws RequestThrottledException {
+        throwIfCalledOnMainThread();
         try {
-            VerifyCredentialResponse response = getLockSettings().checkCredential(credential, type,
+            VerifyCredentialResponse response = getLockSettings().checkCredential(
+                    credential.getCredential(), credential.getType(),
                     userId, wrapCallback(progressCallback));
 
             if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
@@ -402,79 +428,26 @@
     }
 
     /**
-     * Check to see if a pattern matches the saved pattern.
-     * If pattern matches, return an opaque attestation that the challenge
-     * was verified.
+     * Check if the credential of a managed profile with unified challenge matches. In this context,
+     * The credential should be the parent user's lockscreen password. If credential matches,
+     * return an opaque attestation associated with the managed profile that the challenge was
+     * verified.
      *
-     * @param pattern The pattern to check.
-     * @param challenge The challenge to verify against the pattern
-     * @return the attestation that the challenge was verified, or null.
+     * @param credential The parent user's credential to check.
+     * @param challenge The challenge to verify against the credential
+     * @return the attestation that the challenge was verified, or null
+     * @param userId The managed profile user id
+     * @throws RequestThrottledException if credential verification is being throttled due to
+     *         to many incorrect attempts.
+     * @throws IllegalStateException if called on the main thread.
      */
-    public byte[] verifyPattern(List<LockPatternView.Cell> pattern, long challenge, int userId)
-            throws RequestThrottledException {
-        throwIfCalledOnMainThread();
-        return verifyCredential(patternToByteArray(pattern), CREDENTIAL_TYPE_PATTERN, challenge,
-                userId);
-    }
-
-    /**
-     * Check to see if a pattern matches the saved pattern.  If no pattern exists,
-     * always returns true.
-     * @param pattern The pattern to check.
-     * @return Whether the pattern matches the stored one.
-     */
-    public boolean checkPattern(List<LockPatternView.Cell> pattern, int userId)
-            throws RequestThrottledException {
-        return checkPattern(pattern, userId, null /* progressCallback */);
-    }
-
-    /**
-     * Check to see if a pattern matches the saved pattern.  If no pattern exists,
-     * always returns true.
-     * @param pattern The pattern to check.
-     * @return Whether the pattern matches the stored one.
-     */
-    public boolean checkPattern(List<LockPatternView.Cell> pattern, int userId,
-            @Nullable CheckCredentialProgressCallback progressCallback)
-            throws RequestThrottledException {
-        throwIfCalledOnMainThread();
-        return checkCredential(patternToByteArray(pattern), CREDENTIAL_TYPE_PATTERN, userId,
-                progressCallback);
-    }
-
-    /**
-     * Check to see if a password matches the saved password.
-     * If password matches, return an opaque attestation that the challenge
-     * was verified.
-     *
-     * @param password The password to check.
-     * @param challenge The challenge to verify against the password
-     * @return the attestation that the challenge was verified, or null.
-     */
-    public byte[] verifyPassword(byte[] password, long challenge, int userId)
-            throws RequestThrottledException {
-        throwIfCalledOnMainThread();
-        return verifyCredential(password, CREDENTIAL_TYPE_PASSWORD, challenge, userId);
-    }
-
-
-    /**
-     * Check to see if a password matches the saved password.
-     * If password matches, return an opaque attestation that the challenge
-     * was verified.
-     *
-     * @param password The password to check.
-     * @param challenge The challenge to verify against the password
-     * @return the attestation that the challenge was verified, or null.
-     */
-    public byte[] verifyTiedProfileChallenge(byte[] password, boolean isPattern, long challenge,
-            int userId) throws RequestThrottledException {
+    public byte[] verifyTiedProfileChallenge(@NonNull LockscreenCredential credential,
+            long challenge, int userId) throws RequestThrottledException {
         throwIfCalledOnMainThread();
         try {
             VerifyCredentialResponse response =
-                    getLockSettings().verifyTiedProfileChallenge(password,
-                            isPattern ? CREDENTIAL_TYPE_PATTERN : CREDENTIAL_TYPE_PASSWORD, challenge,
-                            userId);
+                    getLockSettings().verifyTiedProfileChallenge(
+                            credential.getCredential(), credential.getType(), challenge, userId);
 
             if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) {
                 return response.getPayload();
@@ -489,61 +462,6 @@
     }
 
     /**
-     *
-     * Check to see if a password matches the saved password.  If no password exists,
-     * always returns true.
-     * @param password The password to check.
-     * @return Whether the password matches the stored one.
-     */
-    @UnsupportedAppUsage
-    public boolean checkPassword(String password, int userId) throws RequestThrottledException {
-        byte[] passwordBytes = password != null ? password.getBytes() : null;
-        return checkPassword(passwordBytes, userId, null /* progressCallback */);
-    }
-
-
-    /**
-     *
-     * Check to see if a password matches the saved password.  If no password exists,
-     * always returns true.
-     * @param password The password to check.
-     * @return Whether the password matches the stored one.
-     */
-    public boolean checkPassword(byte[] password, int userId) throws RequestThrottledException {
-        return checkPassword(password, userId, null /* progressCallback */);
-    }
-
-    // TODO(b/120484642): This method is necessary for vendor/qcom code and is a hidden api
-    /* *
-     * Check to see if a password matches the saved password.  If no password exists,
-     * always returns true.
-     * @param password The password to check.
-     * @return Whether the password matches the stored one.
-     */
-    public boolean checkPassword(String password, int userId,
-            @Nullable CheckCredentialProgressCallback progressCallback)
-            throws RequestThrottledException {
-        byte[] passwordBytes = password != null ? password.getBytes() : null;
-        throwIfCalledOnMainThread();
-        return checkCredential(passwordBytes, CREDENTIAL_TYPE_PASSWORD, userId, progressCallback);
-
-    }
-
-    /**
-     * Check to see if a password matches the saved password.  If no password exists,
-     * always returns true.
-     * @param password The password to check.
-     * @return Whether the password matches the stored one.
-     */
-
-    public boolean checkPassword(byte[] password, int userId,
-            @Nullable CheckCredentialProgressCallback progressCallback)
-            throws RequestThrottledException {
-        throwIfCalledOnMainThread();
-        return checkCredential(password, CREDENTIAL_TYPE_PASSWORD, userId, progressCallback);
-    }
-
-    /**
      * Check to see if vold already has the password.
      * Note that this also clears vold's copy of the password.
      * @return Whether the vold password matches or not.
@@ -560,9 +478,10 @@
      * Returns the password history hash factor, needed to check new password against password
      * history with {@link #checkPasswordHistory(byte[], byte[], int)}
      */
-    public byte[] getPasswordHistoryHashFactor(byte[] currentPassword, int userId) {
+    public byte[] getPasswordHistoryHashFactor(@NonNull LockscreenCredential currentPassword,
+            int userId) {
         try {
-            return getLockSettings().getHashFactor(currentPassword, userId);
+            return getLockSettings().getHashFactor(currentPassword.getCredential(), userId);
         } catch (RemoteException e) {
             Log.e(TAG, "failed to get hash factor", e);
             return null;
@@ -679,57 +598,6 @@
     }
 
     /**
-     * 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);
-    }
-
-    /**
-     * 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 {
-            if (!getLockSettings().setLockCredential(null, CREDENTIAL_TYPE_NONE, savedCredential,
-                    PASSWORD_QUALITY_UNSPECIFIED, userHandle, allowUntrustedChange)) {
-                return false;
-            }
-        } catch (RemoteException | RuntimeException e) {
-            setKeyguardStoredPasswordQuality(currentQuality, userHandle);
-            throw new RuntimeException("Failed to clear lock", e);
-        }
-
-        if (userHandle == UserHandle.USER_SYSTEM) {
-            // Set the encryption password to default.
-            updateEncryptionPassword(StorageManager.CRYPT_TYPE_DEFAULT, null);
-            setCredentialRequiredToDecrypt(false);
-        }
-
-        onAfterChangingPassword(userHandle);
-        return true;
-    }
-
-    /**
      * Disable showing lock screen at all for a given user.
      * This is only meaningful if pattern, pin or password are not set.
      *
@@ -762,75 +630,88 @@
                 || isDemoUser;
     }
 
-    /**
-     * 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) {
-        return saveLockPattern(pattern, savedPattern, userId, false);
+    /** Returns if the given quality maps to an alphabetic password */
+    public static boolean isQualityAlphabeticPassword(int quality) {
+        return quality >= PASSWORD_QUALITY_ALPHABETIC;
+    }
+
+    /** Returns if the given quality maps to an numeric pin */
+    public static boolean isQualityNumericPin(int quality) {
+        return quality == PASSWORD_QUALITY_NUMERIC || quality == PASSWORD_QUALITY_NUMERIC_COMPLEX;
     }
 
     /**
-     * Save a lock pattern.
+     * Save a new lockscreen credential.
      *
-     * <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.
+     * <p> This method will fail (returning {@code false}) if the previously saved credential
+     * 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.
+     * @param newCredential The new credential to save
+     * @param savedCredential The current credential
+     * @param userId the user whose lockscreen credential is to be changed
+     *
+     * @return whether this method saved the new password successfully or not. This flow will fail
+     * and return false if the given credential is wrong.
      * @throws RuntimeException if password change encountered an unrecoverable error.
      */
-    public boolean saveLockPattern(List<LockPatternView.Cell> pattern, byte[] savedPattern,
-            int userId, boolean allowUntrustedChange) {
+    public boolean setLockCredential(@NonNull LockscreenCredential newCredential,
+            @NonNull LockscreenCredential savedCredential, int userId) {
+        return setLockCredential(newCredential, savedCredential, userId, false);
+    }
+
+    /**
+     * Save a new lockscreen credential.
+     * <p> This method will fail (returning {@code false}) if the previously saved pattern provided
+     * is incorrect and allowUntrustedChange is false, or if the lockscreen verification is still
+     * being throttled.
+     * @param newCredential The new credential to save
+     * @param savedCredential The current credential
+     * @param userHandle the user whose lockscreen credential is to be changed
+     * @param allowUntrustedChange whether we want to allow saving a new pattern if the existing
+     * credentialt 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 setLockCredential(@NonNull LockscreenCredential newCredential,
+            @NonNull LockscreenCredential savedCredential, int userHandle,
+            boolean allowUntrustedChange) {
         if (!hasSecureLockScreen()) {
             throw new UnsupportedOperationException(
                     "This operation requires the lock screen feature.");
         }
-        if (pattern == null || pattern.size() < MIN_LOCK_PATTERN_SIZE) {
-            throw new IllegalArgumentException("pattern must not be null and at least "
-                    + MIN_LOCK_PATTERN_SIZE + " dots long.");
-        }
+        newCredential.checkLength();
 
-        final byte[] bytePattern = patternToByteArray(pattern);
-        final int currentQuality = getKeyguardStoredPasswordQuality(userId);
-        setKeyguardStoredPasswordQuality(PASSWORD_QUALITY_SOMETHING, userId);
+        final int currentQuality = getKeyguardStoredPasswordQuality(userHandle);
+        setKeyguardStoredPasswordQuality(newCredential.getQuality(), userHandle);
+
         try {
-            if (!getLockSettings().setLockCredential(bytePattern, CREDENTIAL_TYPE_PATTERN,
-                    savedPattern, PASSWORD_QUALITY_SOMETHING, userId, allowUntrustedChange)) {
+            if (!getLockSettings().setLockCredential(
+                    newCredential.getCredential(), newCredential.getType(),
+                    savedCredential.getCredential(),
+                    newCredential.getQuality(), userHandle, allowUntrustedChange)) {
+                setKeyguardStoredPasswordQuality(currentQuality, userHandle);
                 return false;
             }
         } catch (RemoteException | RuntimeException e) {
-            setKeyguardStoredPasswordQuality(currentQuality, userId);
-            throw new RuntimeException("Couldn't save lock pattern", e);
-        }
-        // Update the device encryption password.
-        if (userId == UserHandle.USER_SYSTEM
-                && LockPatternUtils.isDeviceEncryptionEnabled()) {
-            if (!shouldEncryptWithCredentials(true)) {
-                clearEncryptionPassword();
-            } else {
-                updateEncryptionPassword(StorageManager.CRYPT_TYPE_PATTERN, bytePattern);
-            }
+            setKeyguardStoredPasswordQuality(currentQuality, userHandle);
+            throw new RuntimeException("Unable to save lock password", e);
         }
 
-        reportPatternWasChosen(userId);
-        onAfterChangingPassword(userId);
+        onPostPasswordChanged(newCredential, userHandle);
         return true;
     }
 
+    private void onPostPasswordChanged(LockscreenCredential newCredential, int userHandle) {
+        updateEncryptionPasswordIfNeeded(newCredential, userHandle);
+        if (newCredential.isPattern()) {
+            reportPatternWasChosen(userHandle);
+        }
+        updatePasswordHistory(newCredential, userHandle);
+        reportEnabledTrustAgentsChanged(userHandle);
+    }
+
     private void updateCryptoUserInfo(int userId) {
         if (userId != UserHandle.USER_SYSTEM) {
             return;
@@ -929,149 +810,35 @@
     }
 
     /**
-     * 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
-    public boolean saveLockPassword(String password, String savedPassword, int requestedQuality,
-            int userHandle) {
-        byte[] passwordBytes = password != null ? password.getBytes() : null;
-        byte[] savedPasswordBytes = savedPassword != null ? savedPassword.getBytes() : null;
-        return saveLockPassword(passwordBytes, savedPasswordBytes, requestedQuality, userHandle);
-    }
-
-    /**
-     * 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) {
-        return saveLockPassword(password, savedPassword, requestedQuality,
-                userHandle, false);
-    }
-
-    /**
-     * 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
-     * @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) {
-        if (!hasSecureLockScreen()) {
-            throw new UnsupportedOperationException(
-                    "This operation requires the lock screen feature.");
-        }
-        if (password == null || password.length < MIN_LOCK_PASSWORD_SIZE) {
-            throw new IllegalArgumentException("password must not be null and at least "
-                    + "of length " + MIN_LOCK_PASSWORD_SIZE);
-        }
-
-        if (requestedQuality < PASSWORD_QUALITY_NUMERIC) {
-            throw new IllegalArgumentException("quality must be at least NUMERIC, but was "
-                    + requestedQuality);
-        }
-
-        final int currentQuality = getKeyguardStoredPasswordQuality(userHandle);
-        final int passwordQuality = PasswordMetrics.computeForPassword(password).quality;
-        final int newKeyguardQuality =
-                computeKeyguardQuality(CREDENTIAL_TYPE_PASSWORD, requestedQuality, passwordQuality);
-        setKeyguardStoredPasswordQuality(newKeyguardQuality, userHandle);
-        try {
-            getLockSettings().setLockCredential(password, CREDENTIAL_TYPE_PASSWORD, savedPassword,
-                    requestedQuality, userHandle, allowUntrustedChange);
-        } catch (RemoteException | RuntimeException e) {
-            setKeyguardStoredPasswordQuality(currentQuality, userHandle);
-            throw new RuntimeException("Unable to save lock password", e);
-        }
-
-        updateEncryptionPasswordIfNeeded(password, passwordQuality, userHandle);
-        updatePasswordHistory(password, userHandle);
-        onAfterChangingPassword(userHandle);
-        return true;
-    }
-
-    /**
-     * Compute keyguard credential quality to store in PASSWORD_TYPE_KEY by computing max between
-     * them so that digit-only password is distinguished from PIN.
-     *
-     * TODO: remove this method and make CREDENTIAL_TYPE distinguish between PIN and password, so
-     * that this quality is no longer needs to be persisted.
-     */
-    private int computeKeyguardQuality(
-            @CredentialType int credentialType, int requestedQuality, int passwordQuality) {
-        return credentialType == CREDENTIAL_TYPE_PASSWORD
-                ? Math.max(passwordQuality, requestedQuality) : passwordQuality;
-    }
-
-    /**
      * Update device encryption password if calling user is USER_SYSTEM and device supports
      * encryption.
      */
-    private void updateEncryptionPasswordIfNeeded(byte[] password, int quality, int userHandle) {
+    private void updateEncryptionPasswordIfNeeded(LockscreenCredential credential, int userHandle) {
         // Update the device encryption password.
-        if (userHandle == UserHandle.USER_SYSTEM
-                && LockPatternUtils.isDeviceEncryptionEnabled()) {
-            if (!shouldEncryptWithCredentials(true)) {
-                clearEncryptionPassword();
-            } else {
-                boolean numeric = quality == PASSWORD_QUALITY_NUMERIC;
-                boolean numericComplex = quality == PASSWORD_QUALITY_NUMERIC_COMPLEX;
-                int type = numeric || numericComplex ? StorageManager.CRYPT_TYPE_PIN
-                        : StorageManager.CRYPT_TYPE_PASSWORD;
-                updateEncryptionPassword(type, password);
-            }
+        if (userHandle != UserHandle.USER_SYSTEM || !isDeviceEncryptionEnabled()) {
+            return;
         }
+        if (!shouldEncryptWithCredentials(true)) {
+            updateEncryptionPassword(StorageManager.CRYPT_TYPE_DEFAULT, null);
+            return;
+        }
+        if (credential.isNone()) {
+            // Set the encryption password to default.
+            setCredentialRequiredToDecrypt(false);
+        }
+        updateEncryptionPassword(credential.getStorageCryptType(), credential.getCredential());
     }
 
     /**
      * Store the hash of the *current* password in the password history list, if device policy
      * enforces password history requirement.
      */
-    private void updatePasswordHistory(byte[] password, int userHandle) {
-        if (password == null || password.length == 0) {
-            Log.e(TAG, "checkPasswordHistory: empty password");
+    private void updatePasswordHistory(LockscreenCredential password, int userHandle) {
+        if (password.isNone()) {
+            return;
+        }
+        if (password.isPattern()) {
+            // Do not keep track of historical patterns
             return;
         }
         // Add the password to the password history. We assume all
@@ -1085,10 +852,10 @@
             passwordHistory = "";
         } else {
             final byte[] hashFactor = getPasswordHistoryHashFactor(password, userHandle);
-            String hash = passwordToHistoryHash(password, hashFactor, userHandle);
+            String hash = passwordToHistoryHash(password.getCredential(), hashFactor, userHandle);
             if (hash == null) {
                 Log.e(TAG, "Compute new style password hash failed, fallback to legacy style");
-                hash = legacyPasswordToHash(password, userHandle);
+                hash = legacyPasswordToHash(password.getCredential(), userHandle);
             }
             if (TextUtils.isEmpty(passwordHistory)) {
                 passwordHistory = hash;
@@ -1132,8 +899,8 @@
     }
 
     /**
-     * Retrieves the quality mode for {@param userHandle}.
-     * {@see DevicePolicyManager#getPasswordQuality(android.content.ComponentName)}
+     * Retrieves the quality mode for {@code userHandle}.
+     * @see DevicePolicyManager#getPasswordQuality(android.content.ComponentName)
      *
      * @return stored password quality
      */
@@ -1147,37 +914,37 @@
     }
 
     /**
-     * Enables/disables the Separate Profile Challenge for this {@param userHandle}. This is a no-op
+     * Enables/disables the Separate Profile Challenge for this {@code userHandle}. This is a no-op
      * for user handles that do not belong to a managed profile.
      *
      * @param userHandle Managed profile user id
      * @param enabled True if separate challenge is enabled
-     * @param managedUserPassword Managed profile previous password. Null when {@param enabled} is
+     * @param managedUserPassword Managed profile previous password. Null when {@code enabled} is
      *            true
      */
     public void setSeparateProfileChallengeEnabled(int userHandle, boolean enabled,
-            byte[] managedUserPassword) {
+            LockscreenCredential managedUserPassword) {
         if (!isManagedProfile(userHandle)) {
             return;
         }
         try {
             getLockSettings().setSeparateProfileChallengeEnabled(userHandle, enabled,
-                    managedUserPassword);
-            onAfterChangingPassword(userHandle);
+                    managedUserPassword.getCredential());
+            reportEnabledTrustAgentsChanged(userHandle);
         } catch (RemoteException e) {
             Log.e(TAG, "Couldn't update work profile challenge enabled");
         }
     }
 
     /**
-     * Returns true if {@param userHandle} is a managed profile with separate challenge.
+     * Returns true if {@code userHandle} is a managed profile with separate challenge.
      */
     public boolean isSeparateProfileChallengeEnabled(int userHandle) {
         return isManagedProfile(userHandle) && hasSeparateChallenge(userHandle);
     }
 
     /**
-     * Returns true if {@param userHandle} is a managed profile with unified challenge.
+     * Returns true if {@code userHandle} is a managed profile with unified challenge.
      */
     public boolean isManagedProfileWithUnifiedChallenge(int userHandle) {
         return isManagedProfile(userHandle) && !hasSeparateChallenge(userHandle);
@@ -1218,20 +985,6 @@
 
     /**
      * Deserialize a pattern.
-     * @param string The pattern serialized with {@link #patternToString}
-     * @return The pattern.
-     * @deprecated Pass patterns as byte[] and use byteArrayToPattern
-     */
-    @Deprecated
-    public static List<LockPatternView.Cell> stringToPattern(String string) {
-        if (string == null) {
-            return null;
-        }
-        return byteArrayToPattern(string.getBytes());
-    }
-
-    /**
-     * Deserialize a pattern.
      * @param  bytes The pattern serialized with {@link #patternToByteArray}
      * @return The pattern.
      */
@@ -1252,19 +1005,6 @@
     /**
      * Serialize a pattern.
      * @param pattern The pattern.
-     * @return The pattern in string form.
-     * @deprecated Use patternToByteArray instead.
-     */
-    @UnsupportedAppUsage
-    @Deprecated
-    public static String patternToString(List<LockPatternView.Cell> pattern) {
-        return new String(patternToByteArray(pattern));
-    }
-
-
-    /**
-     * Serialize a pattern.
-     * @param pattern The pattern.
      * @return The pattern in byte array form.
      */
     public static byte[] patternToByteArray(List<LockPatternView.Cell> pattern) {
@@ -1281,34 +1021,6 @@
         return res;
     }
 
-    /*
-     * Generate an SHA-1 hash for the pattern. Not the most secure, but it is
-     * at least a second level of protection. First level is that the file
-     * is in a location only readable by the system process.
-     * @param pattern the gesture pattern.
-     * @return the hash of the pattern in a byte array.
-     */
-    @UnsupportedAppUsage
-    public static byte[] patternToHash(List<LockPatternView.Cell> pattern) {
-        if (pattern == null) {
-            return null;
-        }
-
-        final int patternSize = pattern.size();
-        byte[] res = new byte[patternSize];
-        for (int i = 0; i < patternSize; i++) {
-            LockPatternView.Cell cell = pattern.get(i);
-            res[i] = (byte) (cell.getRow() * 3 + cell.getColumn());
-        }
-        try {
-            MessageDigest md = MessageDigest.getInstance("SHA-1");
-            byte[] hash = md.digest(res);
-            return hash;
-        } catch (NoSuchAlgorithmException nsa) {
-            return res;
-        }
-    }
-
     private String getSalt(int userId) {
         long salt = getLong(LOCK_PASSWORD_SALT_KEY, 0, userId);
         if (salt == 0) {
@@ -1332,6 +1044,7 @@
      * @param password the gesture pattern.
      *
      * @return the hash of the pattern in a byte array.
+     * TODO: move to LockscreenCredential class
      */
     public String legacyPasswordToHash(byte[] password, int userId) {
         if (password == null || password.length == 0) {
@@ -1362,6 +1075,7 @@
 
     /**
      * Hash the password for password history check purpose.
+     * TODO: move to LockscreenCredential class
      */
     private String passwordToHistoryHash(byte[] passwordToHash, byte[] hashFactor, int userId) {
         if (passwordToHash == null || passwordToHash.length == 0 || hashFactor == null) {
@@ -1629,7 +1343,7 @@
     }
 
     /**
-     * Disable trust until credentials have been entered for user {@param userId}.
+     * Disable trust until credentials have been entered for user {@code userId}.
      *
      * Requires the {@link android.Manifest.permission#ACCESS_KEYGUARD_SECURE_STORAGE} permission.
      *
@@ -1640,7 +1354,7 @@
     }
 
     /**
-     * Requests strong authentication for user {@param userId}.
+     * Requests strong authentication for user {@code userId}.
      *
      * Requires the {@link android.Manifest.permission#ACCESS_KEYGUARD_SECURE_STORAGE} permission.
      *
@@ -1657,7 +1371,7 @@
         }
     }
 
-    private void onAfterChangingPassword(int userHandle) {
+    private void reportEnabledTrustAgentsChanged(int userHandle) {
         getTrustManager().reportEnabledTrustAgentsChanged(userHandle);
     }
 
@@ -1823,54 +1537,36 @@
      * <p>This method is only available to code running in the system server process itself.
      *
      * @param credential The new credential to be set
-     * @param type Credential type: password / pattern / none.
-     * @param requestedQuality the requested password quality by DevicePolicyManager.
-     *        See {@link DevicePolicyManager#getPasswordQuality(android.content.ComponentName)}
      * @param tokenHandle Handle of the escrow token
      * @param token Escrow token
-     * @param userId The user who's lock credential to be changed
+     * @param userHandle The user who's lock credential to be changed
      * @return {@code true} if the operation is successful.
      */
-    public boolean setLockCredentialWithToken(byte[] credential, int type, int requestedQuality,
-            long tokenHandle, byte[] token, int userId) {
+    public boolean setLockCredentialWithToken(LockscreenCredential credential, long tokenHandle,
+            byte[] token, int userHandle) {
         if (!hasSecureLockScreen()) {
             throw new UnsupportedOperationException(
                     "This operation requires the lock screen feature.");
         }
+        credential.checkLength();
         LockSettingsInternal localService = getLockSettingsInternal();
-        if (type != CREDENTIAL_TYPE_NONE) {
-            if (credential == null || credential.length < MIN_LOCK_PASSWORD_SIZE) {
-                throw new IllegalArgumentException("password must not be null and at least "
-                        + "of length " + MIN_LOCK_PASSWORD_SIZE);
-            }
-            final int quality = PasswordMetrics.computeForCredential(type, credential).quality;
-            final int keyguardQuality = computeKeyguardQuality(type, quality, requestedQuality);
-            if (!localService.setLockCredentialWithToken(credential, type, tokenHandle, token,
-                    keyguardQuality, userId)) {
+
+        final int currentQuality = getKeyguardStoredPasswordQuality(userHandle);
+        setKeyguardStoredPasswordQuality(credential.getQuality(), userHandle);
+
+        try {
+            if (!localService.setLockCredentialWithToken(credential.getCredential(),
+                    credential.getType(),
+                    tokenHandle, token, credential.getType(), userHandle)) {
+                setKeyguardStoredPasswordQuality(currentQuality, userHandle);
                 return false;
             }
-            setKeyguardStoredPasswordQuality(quality, userId);
-
-            updateEncryptionPasswordIfNeeded(credential, quality, userId);
-            updatePasswordHistory(credential, userId);
-            onAfterChangingPassword(userId);
-        } else {
-            if (!(credential == null || credential.length == 0)) {
-                throw new IllegalArgumentException("password must be emtpy for NONE type");
-            }
-            if (!localService.setLockCredentialWithToken(null, CREDENTIAL_TYPE_NONE, tokenHandle,
-                    token, PASSWORD_QUALITY_UNSPECIFIED, userId)) {
-                return false;
-            }
-            setKeyguardStoredPasswordQuality(PASSWORD_QUALITY_UNSPECIFIED, userId);
-
-            if (userId == UserHandle.USER_SYSTEM) {
-                // Set the encryption password to default.
-                updateEncryptionPassword(StorageManager.CRYPT_TYPE_DEFAULT, null);
-                setCredentialRequiredToDecrypt(false);
-            }
+        } catch (RuntimeException e) {
+            setKeyguardStoredPasswordQuality(currentQuality, userHandle);
+            throw new RuntimeException("Unable to save lock credential", e);
         }
-        onAfterChangingPassword(userId);
+
+        onPostPasswordChanged(credential, userHandle);
         return true;
     }
 
@@ -1997,7 +1693,7 @@
         }
 
         /**
-         * @return true if unlocking with trust alone is allowed for {@param userId} by the current
+         * @return true if unlocking with trust alone is allowed for {@code userId} by the current
          * strong authentication requirements.
          */
         public boolean isTrustAllowedForUser(int userId) {
@@ -2005,7 +1701,7 @@
         }
 
         /**
-         * @return true if unlocking with a biometric method alone is allowed for {@param userId}
+         * @return true if unlocking with a biometric method alone is allowed for {@code userId}
          * by the current strong authentication requirements.
          */
         public boolean isBiometricAllowedForUser(int userId) {
@@ -2013,7 +1709,7 @@
         }
 
         /**
-         * Called when the strong authentication requirements for {@param userId} changed.
+         * Called when the strong authentication requirements for {@code userId} changed.
          */
         public void onStrongAuthRequiredChanged(int userId) {
         }
@@ -2102,22 +1798,4 @@
         return FRP_CREDENTIAL_ENABLED && context.getResources().getBoolean(
                 com.android.internal.R.bool.config_enableCredentialFactoryResetProtection);
     }
-
-    /**
-     * Converts a CharSequence to a byte array without requiring a toString(), which creates an
-     * additional copy.
-     *
-     * @param chars The CharSequence to convert
-     * @return A byte array representing the input
-     */
-    public static byte[] charSequenceToByteArray(CharSequence chars) {
-        if (chars == null) {
-            return null;
-        }
-        byte[] bytes = new byte[chars.length()];
-        for (int i = 0; i < chars.length(); i++) {
-            bytes[i] = (byte) chars.charAt(i);
-        }
-        return bytes;
-    }
 }
diff --git a/core/java/com/android/internal/widget/LockPatternView.java b/core/java/com/android/internal/widget/LockPatternView.java
index 3f6c4d4..74a0aa3 100644
--- a/core/java/com/android/internal/widget/LockPatternView.java
+++ b/core/java/com/android/internal/widget/LockPatternView.java
@@ -1318,7 +1318,7 @@
         super.onRestoreInstanceState(ss.getSuperState());
         setPattern(
                 DisplayMode.Correct,
-                LockPatternUtils.stringToPattern(ss.getSerializedPattern()));
+                LockPatternUtils.byteArrayToPattern(ss.getSerializedPattern().getBytes()));
         mPatternDisplayMode = DisplayMode.values()[ss.getDisplayMode()];
         mInputEnabled = ss.isInputEnabled();
         mInStealthMode = ss.isInStealthMode();
diff --git a/core/java/com/android/internal/widget/LockscreenCredential.java b/core/java/com/android/internal/widget/LockscreenCredential.java
new file mode 100644
index 0000000..19e6d97
--- /dev/null
+++ b/core/java/com/android/internal/widget/LockscreenCredential.java
@@ -0,0 +1,339 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.widget;
+
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
+import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
+
+import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE;
+import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD;
+import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.storage.StorageManager;
+import android.text.TextUtils;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * A class representing a lockscreen credential. It can be either an empty password, a pattern
+ * or a password (or PIN).
+ *
+ * <p> As required by some security certification, the framework tries its best to
+ * remove copies of the lockscreen credential bytes from memory. In this regard, this class
+ * abuses the {@link AutoCloseable} interface for sanitizing memory. This
+ * presents a nice syntax to auto-zeroize memory with the try-with-resource statement:
+ * <pre>
+ * try {LockscreenCredential credential = LockscreenCredential.createPassword(...) {
+ *     // Process the credential in some way
+ * }
+ * </pre>
+ * With this construct, we can garantee that there will be no copies of the password left in
+ * memory when the credential goes out of scope. This should help mitigate certain class of
+ * attacks where the attcker gains read-only access to full device memory (cold boot attack,
+ * unsecured software/hardware memory dumping interfaces such as JTAG).
+ */
+public class LockscreenCredential implements Parcelable, AutoCloseable {
+
+    private final int mType;
+    // Stores raw credential bytes, or null if credential has been zeroized. An empty password
+    // is represented as a byte array of length 0.
+    private byte[] mCredential;
+    // Store the quality of the password, this is used to distinguish between pin
+    // (PASSWORD_QUALITY_NUMERIC) and password (PASSWORD_QUALITY_ALPHABETIC).
+    private final int mQuality;
+
+    /**
+     * Private constructor, use static builder methods instead.
+     *
+     * <p> Builder methods should create a private copy of the credential bytes and pass in here.
+     * LockscreenCredential will only store the reference internally without copying. This is to
+     * minimize the number of extra copies introduced.
+     */
+    private LockscreenCredential(int type, int quality, byte[] credential) {
+        Preconditions.checkNotNull(credential);
+        if (type == CREDENTIAL_TYPE_NONE) {
+            Preconditions.checkArgument(credential.length == 0);
+        } else {
+            Preconditions.checkArgument(credential.length > 0);
+        }
+        mType = type;
+        mQuality = quality;
+        mCredential = credential;
+    }
+
+    /**
+     * Creates a LockscreenCredential object representing empty password.
+     */
+    public static LockscreenCredential createNone() {
+        return new LockscreenCredential(CREDENTIAL_TYPE_NONE, PASSWORD_QUALITY_UNSPECIFIED,
+                new byte[0]);
+    }
+
+    /**
+     * Creates a LockscreenCredential object representing the given pattern.
+     */
+    public static LockscreenCredential createPattern(@NonNull List<LockPatternView.Cell> pattern) {
+        return new LockscreenCredential(CREDENTIAL_TYPE_PATTERN,
+                PASSWORD_QUALITY_SOMETHING,
+                LockPatternUtils.patternToByteArray(pattern));
+    }
+
+    /**
+     * Creates a LockscreenCredential object representing the given alphabetic password.
+     */
+    public static LockscreenCredential createPassword(@NonNull CharSequence password) {
+        return new LockscreenCredential(CREDENTIAL_TYPE_PASSWORD,
+                PASSWORD_QUALITY_ALPHABETIC,
+                charSequenceToByteArray(password));
+    }
+
+    /**
+     * Creates a LockscreenCredential object representing the given numeric PIN.
+     */
+    public static LockscreenCredential createPin(@NonNull CharSequence pin) {
+        return new LockscreenCredential(CREDENTIAL_TYPE_PASSWORD,
+                PASSWORD_QUALITY_NUMERIC,
+                charSequenceToByteArray(pin));
+    }
+
+    /**
+     * Creates a LockscreenCredential object representing the given alphabetic password.
+     * If the supplied password is empty, create an empty credential object.
+     */
+    public static LockscreenCredential createPasswordOrNone(@Nullable CharSequence password) {
+        if (TextUtils.isEmpty(password)) {
+            return createNone();
+        } else {
+            return createPassword(password);
+        }
+    }
+
+    /**
+     * Creates a LockscreenCredential object representing the given numeric PIN.
+     * If the supplied password is empty, create an empty credential object.
+     */
+    public static LockscreenCredential createPinOrNone(@Nullable CharSequence pin) {
+        if (TextUtils.isEmpty(pin)) {
+            return createNone();
+        } else {
+            return createPin(pin);
+        }
+    }
+
+    /**
+     * Create a LockscreenCredential object based on raw credential and type
+     * TODO: Remove once LSS.setUserPasswordMetrics accepts a LockscreenCredential
+     */
+    public static LockscreenCredential createRaw(int type, byte[] credential) {
+        if (type == CREDENTIAL_TYPE_NONE) {
+            return createNone();
+        } else {
+            return new LockscreenCredential(type, PASSWORD_QUALITY_UNSPECIFIED, credential);
+        }
+    }
+
+    private void ensureNotZeroized() {
+        Preconditions.checkState(mCredential != null, "Credential is already zeroized");
+    }
+    /**
+     * Returns the type of this credential. Can be one of {@link #CREDENTIAL_TYPE_NONE},
+     * {@link #CREDENTIAL_TYPE_PATTERN} or {@link #CREDENTIAL_TYPE_PASSWORD}.
+     *
+     * TODO: Remove once credential type is internal. Callers should use {@link #isNone},
+     * {@link #isPattern} and {@link #isPassword} instead.
+     */
+    public int getType() {
+        ensureNotZeroized();
+        return mType;
+    }
+
+    /**
+     * Returns the quality type of the credential
+     */
+    public int getQuality() {
+        ensureNotZeroized();
+        return mQuality;
+    }
+
+    /**
+     * Returns the credential bytes. This is a direct reference of the internal field so
+     * callers should not modify it.
+     *
+     */
+    public byte[] getCredential() {
+        ensureNotZeroized();
+        return mCredential;
+    }
+
+    /**
+     *  Returns the credential type recognized by {@link StorageManager}. Can be one of
+     *  {@link StorageManager#CRYPT_TYPE_DEFAULT}, {@link StorageManager#CRYPT_TYPE_PATTERN},
+     *  {@link StorageManager#CRYPT_TYPE_PIN} or {@link StorageManager#CRYPT_TYPE_PASSWORD}.
+     */
+    public int getStorageCryptType() {
+        if (isNone()) {
+            return StorageManager.CRYPT_TYPE_DEFAULT;
+        }
+        if (isPattern()) {
+            return StorageManager.CRYPT_TYPE_PATTERN;
+        }
+        if (isPassword()) {
+            return mQuality == PASSWORD_QUALITY_NUMERIC
+                    ? StorageManager.CRYPT_TYPE_PIN : StorageManager.CRYPT_TYPE_PASSWORD;
+        }
+        throw new IllegalStateException("Unhandled credential type");
+    }
+
+    /** Returns whether this is an empty credential */
+    public boolean isNone() {
+        ensureNotZeroized();
+        return mType == CREDENTIAL_TYPE_NONE;
+    }
+
+    /** Returns whether this is a pattern credential */
+    public boolean isPattern() {
+        ensureNotZeroized();
+        return mType == CREDENTIAL_TYPE_PATTERN;
+    }
+
+    /** Returns whether this is a password credential */
+    public boolean isPassword() {
+        ensureNotZeroized();
+        return mType == CREDENTIAL_TYPE_PASSWORD;
+    }
+
+    /** Returns the length of the credential */
+    public int size() {
+        ensureNotZeroized();
+        return mCredential.length;
+    }
+
+    /** Create a copy of the credential */
+    public LockscreenCredential duplicate() {
+        return new LockscreenCredential(mType, mQuality,
+                mCredential != null ? Arrays.copyOf(mCredential, mCredential.length) : null);
+    }
+
+    /**
+     * Zeroize the credential bytes.
+     */
+    public void zeroize() {
+        if (mCredential != null) {
+            Arrays.fill(mCredential, (byte) 0);
+            mCredential = null;
+        }
+    }
+
+    /**
+     * Check if the credential meets minimal length requirement.
+     *
+     * @throws IllegalArgumentException if the credential is too short.
+     */
+    public void checkLength() {
+        if (isNone()) {
+            return;
+        }
+        if (isPattern()) {
+            if (size() < LockPatternUtils.MIN_LOCK_PATTERN_SIZE) {
+                throw new IllegalArgumentException("pattern must not be null and at least "
+                        + LockPatternUtils.MIN_LOCK_PATTERN_SIZE + " dots long.");
+            }
+            return;
+        }
+        if (isPassword()) {
+            if (size() < LockPatternUtils.MIN_LOCK_PASSWORD_SIZE) {
+                throw new IllegalArgumentException("password must not be null and at least "
+                        + "of length " + LockPatternUtils.MIN_LOCK_PASSWORD_SIZE);
+            }
+            return;
+        }
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mType);
+        dest.writeInt(mQuality);
+        dest.writeByteArray(mCredential);
+    }
+
+    public static final Parcelable.Creator<LockscreenCredential> CREATOR =
+            new Parcelable.Creator<LockscreenCredential>() {
+
+        @Override
+        public LockscreenCredential createFromParcel(Parcel source) {
+            return new LockscreenCredential(source.readInt(), source.readInt(),
+                    source.createByteArray());
+        }
+
+        @Override
+        public LockscreenCredential[] newArray(int size) {
+            return new LockscreenCredential[size];
+        }
+    };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void close() {
+        zeroize();
+    }
+
+    @Override
+    public int hashCode() {
+        // Effective Java — Item 9
+        return ((17 + mType) * 31 + mQuality) * 31 + mCredential.hashCode();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (o == this) return true;
+        if (!(o instanceof LockscreenCredential)) return false;
+        final LockscreenCredential other = (LockscreenCredential) o;
+        return mType == other.mType && mQuality == other.mQuality
+                && Arrays.equals(mCredential, other.mCredential);
+    }
+
+    /**
+     * Converts a CharSequence to a byte array without requiring a toString(), which creates an
+     * additional copy.
+     *
+     * @param chars The CharSequence to convert
+     * @return A byte array representing the input
+     */
+    private static byte[] charSequenceToByteArray(CharSequence chars) {
+        if (chars == null) {
+            return new byte[0];
+        }
+        byte[] bytes = new byte[chars.length()];
+        for (int i = 0; i < chars.length(); i++) {
+            bytes[i] = (byte) chars.charAt(i);
+        }
+        return bytes;
+    }
+}
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index 697825d..ea0389f 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -168,6 +168,10 @@
     // These are the permitted backup transport service components
     final ArraySet<ComponentName> mBackupTransportWhitelist = new ArraySet<>();
 
+    // These are packages mapped to maps of component class name to default enabled state.
+    final ArrayMap<String, ArrayMap<String, Boolean>> mPackageComponentEnabledState =
+            new ArrayMap<>();
+
     // Package names that are exempted from private API blacklisting
     final ArraySet<String> mHiddenApiPackageWhitelist = new ArraySet<>();
 
@@ -301,6 +305,10 @@
         return mBackupTransportWhitelist;
     }
 
+    public ArrayMap<String, Boolean> getComponentsEnabledStates(String packageName) {
+        return mPackageComponentEnabledState.get(packageName);
+    }
+
     public ArraySet<String> getDisabledUntilUsedPreinstalledCarrierApps() {
         return mDisabledUntilUsedPreinstalledCarrierApps;
     }
@@ -846,6 +854,14 @@
                         }
                         XmlUtils.skipCurrentTag(parser);
                     } break;
+                    case "component-override": {
+                        if (allowAppConfigs) {
+                            readComponentOverrides(parser, permFile);
+                        } else {
+                            logNotAllowedInPartition(name, permFile, parser);
+                        }
+                        XmlUtils.skipCurrentTag(parser);
+                    } break;
                     case "backup-transport-whitelisted-service": {
                         if (allowFeatures) {
                             String serviceName = parser.getAttributeValue(null, "service");
@@ -1269,6 +1285,54 @@
         }
     }
 
+    private void readComponentOverrides(XmlPullParser parser, File permFile)
+            throws IOException, XmlPullParserException {
+        String pkgname = parser.getAttributeValue(null, "package");
+        if (pkgname == null) {
+            Slog.w(TAG, "<component-override> without package in "
+                    + permFile + " at " + parser.getPositionDescription());
+            return;
+        }
+
+        pkgname = pkgname.intern();
+
+        final int depth = parser.getDepth();
+        while (XmlUtils.nextElementWithin(parser, depth)) {
+            String name = parser.getName();
+            if ("component".equals(name)) {
+                String clsname = parser.getAttributeValue(null, "class");
+                String enabled = parser.getAttributeValue(null, "enabled");
+                if (clsname == null) {
+                    Slog.w(TAG, "<component> without class in "
+                            + permFile + " at " + parser.getPositionDescription());
+                    return;
+                } else if (enabled == null) {
+                    Slog.w(TAG, "<component> without enabled in "
+                            + permFile + " at " + parser.getPositionDescription());
+                    return;
+                }
+
+                if (clsname.startsWith(".")) {
+                    clsname = pkgname + clsname;
+                }
+
+                clsname = clsname.intern();
+
+                ArrayMap<String, Boolean> componentEnabledStates =
+                        mPackageComponentEnabledState.get(pkgname);
+                if (componentEnabledStates == null) {
+                    componentEnabledStates = new ArrayMap<>();
+                    mPackageComponentEnabledState.put(pkgname,
+                            componentEnabledStates);
+                }
+
+                componentEnabledStates.put(clsname, !"false".equals(enabled));
+            } else {
+                XmlUtils.skipCurrentTag(parser);
+            }
+        }
+    }
+
     private static boolean isSystemProcess() {
         return Process.myUid() == Process.SYSTEM_UID;
     }
diff --git a/core/jni/android_view_InputChannel.cpp b/core/jni/android_view_InputChannel.cpp
index af34e7b7..bf1cea8 100644
--- a/core/jni/android_view_InputChannel.cpp
+++ b/core/jni/android_view_InputChannel.cpp
@@ -112,7 +112,9 @@
 }
 
 static jobject android_view_InputChannel_createInputChannel(JNIEnv* env,
-        std::unique_ptr<NativeInputChannel> nativeInputChannel) {
+        sp<InputChannel> inputChannel) {
+    std::unique_ptr<NativeInputChannel> nativeInputChannel =
+            std::make_unique<NativeInputChannel>(inputChannel);
     jobject inputChannelObj = env->NewObject(gInputChannelClassInfo.clazz,
             gInputChannelClassInfo.ctor);
     if (inputChannelObj) {
@@ -143,14 +145,12 @@
         return nullptr;
     }
 
-    jobject serverChannelObj = android_view_InputChannel_createInputChannel(env,
-            std::make_unique<NativeInputChannel>(serverChannel));
+    jobject serverChannelObj = android_view_InputChannel_createInputChannel(env, serverChannel);
     if (env->ExceptionCheck()) {
         return nullptr;
     }
 
-    jobject clientChannelObj = android_view_InputChannel_createInputChannel(env,
-            std::make_unique<NativeInputChannel>(clientChannel));
+    jobject clientChannelObj = android_view_InputChannel_createInputChannel(env, clientChannel);
     if (env->ExceptionCheck()) {
         return nullptr;
     }
diff --git a/core/proto/android/service/procstats.proto b/core/proto/android/service/procstats.proto
index f49a044..ad7299d 100644
--- a/core/proto/android/service/procstats.proto
+++ b/core/proto/android/service/procstats.proto
@@ -241,12 +241,25 @@
     repeated StateStats active_state_stats = 6;
 }
 
-// Next Tag: 3
+// Next Tag: 7
 message PackageAssociationProcessStatsProto {
     option (android.msg_privacy).dest = DEST_AUTOMATIC;
 
     // Name of the target component.
     optional string component_name = 1;
+
+    // Total count of the times this association appeared.
+    optional int32 total_count = 3;
+
+    // Millisecond uptime total duration this association was around.
+    optional int64 total_duration_ms = 4;
+
+    // Total count of the times this association became actively impacting its target process.
+    optional int32 active_count = 5;
+
+    // Millisecond uptime total duration this association was around.
+    optional int64 active_duration_ms = 6;
+
     // Information on one source in this association.
     repeated PackageAssociationSourceProcessStatsProto sources = 2;
 }
diff --git a/core/proto/android/stats/devicepolicy/device_policy_enums.proto b/core/proto/android/stats/devicepolicy/device_policy_enums.proto
index 0821d14..15813a1 100644
--- a/core/proto/android/stats/devicepolicy/device_policy_enums.proto
+++ b/core/proto/android/stats/devicepolicy/device_policy_enums.proto
@@ -113,9 +113,9 @@
   PROVISIONING_PREPROVISIONING_ACTIVITY_TIME_MS = 87;
   PROVISIONING_ENCRYPT_DEVICE_ACTIVITY_TIME_MS = 88;
   PROVISIONING_WEB_ACTIVITY_TIME_MS = 89;
-  PROVISIONING_TRAMPOLINE_ACTIVITY_TIME_MS = 90;
-  PROVISIONING_POST_ENCRYPTION_ACTIVITY_TIME_MS = 91;
-  PROVISIONING_FINALIZATION_ACTIVITY_TIME_MS = 92;
+  PROVISIONING_TRAMPOLINE_ACTIVITY_TIME_MS = 90 [deprecated=true];
+  PROVISIONING_POST_ENCRYPTION_ACTIVITY_TIME_MS = 91 [deprecated=true];
+  PROVISIONING_FINALIZATION_ACTIVITY_TIME_MS = 92 [deprecated=true];
   PROVISIONING_NETWORK_TYPE = 93;
   PROVISIONING_ACTION = 94;
   PROVISIONING_EXTRAS = 95;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index b3372a6..5c1e13b 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2955,7 +2955,7 @@
          @hide
     -->
     <permission android:name="android.permission.INTERNAL_SYSTEM_WINDOW"
-        android:protectionLevel="signature|telephony" />
+        android:protectionLevel="signature|telephony|wifi" />
 
     <!-- @SystemApi Allows an application to use
          {@link android.view.WindowManager.LayoutsParams#SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS}
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 166cde0..ffcfe43 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -298,6 +298,9 @@
         <!-- Additional flag from base permission type: this permission can be automatically
             granted to the system telephony apps -->
         <flag name="telephony" value="0x400000" />
+        <!-- Additional flag from base permission type: this permission can be automatically
+            granted to the system wifi app-->
+        <flag name="wifi" value="0x800000" />
     </attr>
 
     <!-- Flags indicating more context for a permission group. -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 531023f..5605246 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3687,7 +3687,6 @@
      -->
     <string name="config_defaultWellbeingPackage" translatable="false"></string>
 
-
     <!-- The package name for the system telephony apps.
          This package must be trusted, as it will be granted with permissions with special telephony
          protection level. Note, framework by default support multiple telephony apps, each package
@@ -3696,6 +3695,13 @@
      -->
     <string name="config_telephonyPackages" translatable="false">"com.android.phone,com.android.stk,com.android.providers.telephony,com.android.ons"</string>
 
+    <!-- The package name for the default system wifi app.
+         This package must be trusted, as it has the permissions to control wifi
+         connectivity on the device.
+         Example: "com.android.wifi"
+     -->
+    <string name="config_wifiPackage" translatable="false">"com.android.wifi"</string>
+
     <!-- The component name for the default system attention service.
          This service must be trusted, as it can be activated without explicit consent of the user.
          See android.attention.AttentionManagerService.
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 06fc5e3..42cd2cd 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3468,6 +3468,7 @@
   <java-symbol type="string" name="config_defaultTextClassifierPackage" />
   <java-symbol type="string" name="config_defaultWellbeingPackage" />
   <java-symbol type="string" name="config_telephonyPackages" />
+  <java-symbol type="string" name="config_wifiPackage" />
   <java-symbol type="string" name="config_defaultContentCaptureService" />
   <java-symbol type="string" name="config_defaultAugmentedAutofillService" />
   <java-symbol type="string" name="config_defaultAppPredictionService" />
diff --git a/core/tests/coretests/src/android/util/TimestampedValueTest.java b/core/tests/coretests/src/android/util/TimestampedValueTest.java
index 6e3ab79..6fc2400 100644
--- a/core/tests/coretests/src/android/util/TimestampedValueTest.java
+++ b/core/tests/coretests/src/android/util/TimestampedValueTest.java
@@ -55,12 +55,12 @@
         TimestampedValue<String> stringValue = new TimestampedValue<>(1000, "Hello");
         Parcel parcel = Parcel.obtain();
         try {
-            TimestampedValue.writeToParcel(parcel, stringValue);
+            parcel.writeParcelable(stringValue, 0);
 
             parcel.setDataPosition(0);
 
             TimestampedValue<String> stringValueCopy =
-                    TimestampedValue.readFromParcel(parcel, null /* classLoader */, String.class);
+                    parcel.readParcelable(null /* classLoader */);
             assertEquals(stringValue, stringValueCopy);
         } finally {
             parcel.recycle();
@@ -72,12 +72,12 @@
         TimestampedValue<String> stringValue = new TimestampedValue<>(1000, "Hello");
         Parcel parcel = Parcel.obtain();
         try {
-            TimestampedValue.writeToParcel(parcel, stringValue);
+            parcel.writeParcelable(stringValue, 0);
 
             parcel.setDataPosition(0);
 
-            TimestampedValue<Object> stringValueCopy =
-                    TimestampedValue.readFromParcel(parcel, null /* classLoader */, Object.class);
+            TimestampedValue<String> stringValueCopy =
+                    parcel.readParcelable(null /* classLoader */);
             assertEquals(stringValue, stringValueCopy);
         } finally {
             parcel.recycle();
@@ -85,15 +85,15 @@
     }
 
     @Test
-    public void testParceling_valueClassIncompatible() {
-        TimestampedValue<String> stringValue = new TimestampedValue<>(1000, "Hello");
+    public void testParceling_valueClassNotParcelable() {
+        // This class is not one supported by Parcel.writeValue().
+        class NotParcelable {}
+
+        TimestampedValue<NotParcelable> notParcelableValue =
+                new TimestampedValue<>(1000, new NotParcelable());
         Parcel parcel = Parcel.obtain();
         try {
-            TimestampedValue.writeToParcel(parcel, stringValue);
-
-            parcel.setDataPosition(0);
-
-            TimestampedValue.readFromParcel(parcel, null /* classLoader */, Double.class);
+            parcel.writeParcelable(notParcelableValue, 0);
             fail();
         } catch (RuntimeException expected) {
         } finally {
@@ -106,12 +106,11 @@
         TimestampedValue<String> nullValue = new TimestampedValue<>(1000, null);
         Parcel parcel = Parcel.obtain();
         try {
-            TimestampedValue.writeToParcel(parcel, nullValue);
+            parcel.writeParcelable(nullValue, 0);
 
             parcel.setDataPosition(0);
 
-            TimestampedValue<Object> nullValueCopy =
-                    TimestampedValue.readFromParcel(parcel, null /* classLoader */, String.class);
+            TimestampedValue<String> nullValueCopy = parcel.readParcelable(null /* classLoader */);
             assertEquals(nullValue, nullValueCopy);
         } finally {
             parcel.recycle();
diff --git a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
index abee1da2..7b405434 100644
--- a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
+++ b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java
@@ -345,9 +345,9 @@
         accessibilityShortcutController.performAccessibilityShortcut();
         accessibilityShortcutController.performAccessibilityShortcut();
         verify(mToast).show();
-        assertEquals(WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS,
+        assertEquals(WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS,
                 mLayoutParams.privateFlags
-                        & WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS);
+                        & WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS);
         verify(mAccessibilityManagerService, times(1)).performAccessibilityShortcut();
     }
 
diff --git a/core/tests/coretests/src/com/android/internal/widget/LockscreenCredentialTest.java b/core/tests/coretests/src/com/android/internal/widget/LockscreenCredentialTest.java
new file mode 100644
index 0000000..5eec91c
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/widget/LockscreenCredentialTest.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.widget;
+
+
+import android.test.AndroidTestCase;
+
+import java.util.Arrays;
+
+
+public class LockscreenCredentialTest extends AndroidTestCase {
+
+    public void testEmptyCredential() {
+        LockscreenCredential empty = LockscreenCredential.createNone();
+
+        assertTrue(empty.isNone());
+        assertEquals(0, empty.size());
+        assertNotNull(empty.getCredential());
+
+        assertFalse(empty.isPassword());
+        assertFalse(empty.isPattern());
+    }
+
+    public void testPasswordCredential() {
+        LockscreenCredential password = LockscreenCredential.createPassword("password");
+
+        assertTrue(password.isPassword());
+        assertEquals(8, password.size());
+        assertTrue(Arrays.equals("password".getBytes(), password.getCredential()));
+
+        assertFalse(password.isNone());
+        assertFalse(password.isPattern());
+    }
+
+    public void testPatternCredential() {
+        LockscreenCredential pattern = LockscreenCredential.createPattern(Arrays.asList(
+                LockPatternView.Cell.of(0, 0),
+                LockPatternView.Cell.of(0, 1),
+                LockPatternView.Cell.of(0, 2),
+                LockPatternView.Cell.of(1, 2),
+                LockPatternView.Cell.of(2, 2)
+                ));
+
+        assertTrue(pattern.isPattern());
+        assertEquals(5, pattern.size());
+        assertTrue(Arrays.equals("12369".getBytes(), pattern.getCredential()));
+
+        assertFalse(pattern.isNone());
+        assertFalse(pattern.isPassword());
+    }
+
+    public void testPasswordOrNoneCredential() {
+        assertEquals(LockscreenCredential.createNone(),
+                LockscreenCredential.createPasswordOrNone(null));
+        assertEquals(LockscreenCredential.createNone(),
+                LockscreenCredential.createPasswordOrNone(""));
+        assertEquals(LockscreenCredential.createPassword("abcd"),
+                LockscreenCredential.createPasswordOrNone("abcd"));
+    }
+
+    public void testSanitize() {
+        LockscreenCredential password = LockscreenCredential.createPassword("password");
+        password.zeroize();
+
+        try {
+            password.isNone();
+            fail("Sanitized credential still accessible");
+        } catch (IllegalStateException expected) { }
+
+        try {
+            password.isPattern();
+            fail("Sanitized credential still accessible");
+        } catch (IllegalStateException expected) { }
+        try {
+            password.isPassword();
+            fail("Sanitized credential still accessible");
+        } catch (IllegalStateException expected) { }
+        try {
+            password.size();
+            fail("Sanitized credential still accessible");
+        } catch (IllegalStateException expected) { }
+        try {
+            password.getCredential();
+            fail("Sanitized credential still accessible");
+        } catch (IllegalStateException expected) { }
+    }
+
+    public void testEquals() {
+        assertEquals(LockscreenCredential.createNone(), LockscreenCredential.createNone());
+        assertEquals(LockscreenCredential.createPassword("1234"),
+                LockscreenCredential.createPassword("1234"));
+        assertEquals(LockscreenCredential.createPin("4321"),
+                LockscreenCredential.createPin("4321"));
+        assertEquals(createPattern("1234"), createPattern("1234"));
+
+        assertNotSame(LockscreenCredential.createPassword("1234"),
+                LockscreenCredential.createNone());
+        assertNotSame(LockscreenCredential.createPassword("1234"),
+                LockscreenCredential.createPassword("4321"));
+        assertNotSame(LockscreenCredential.createPassword("1234"),
+                createPattern("1234"));
+        assertNotSame(LockscreenCredential.createPassword("1234"),
+                LockscreenCredential.createPin("1234"));
+
+        assertNotSame(LockscreenCredential.createPin("1111"),
+                LockscreenCredential.createNone());
+        assertNotSame(LockscreenCredential.createPin("1111"),
+                LockscreenCredential.createPin("2222"));
+        assertNotSame(LockscreenCredential.createPin("1111"),
+                createPattern("1111"));
+        assertNotSame(LockscreenCredential.createPin("1111"),
+                LockscreenCredential.createPassword("1111"));
+
+        assertNotSame(createPattern("5678"),
+                LockscreenCredential.createNone());
+        assertNotSame(createPattern("5678"),
+                createPattern("1234"));
+        assertNotSame(createPattern("5678"),
+                LockscreenCredential.createPassword("5678"));
+        assertNotSame(createPattern("5678"),
+                LockscreenCredential.createPin("5678"));
+    }
+
+    public void testDuplicate() {
+        LockscreenCredential credential;
+
+        credential = LockscreenCredential.createNone();
+        assertEquals(credential, credential.duplicate());
+        credential = LockscreenCredential.createPassword("abcd");
+        assertEquals(credential, credential.duplicate());
+        credential = LockscreenCredential.createPin("1234");
+        assertEquals(credential, credential.duplicate());
+        credential = createPattern("5678");
+        assertEquals(credential, credential.duplicate());
+    }
+
+    private LockscreenCredential createPattern(String patternString) {
+        return LockscreenCredential.createPattern(LockPatternUtils.byteArrayToPattern(
+                patternString.getBytes()));
+    }
+}
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 10f6b69..4b4e416 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -121,6 +121,7 @@
         <permission name="android.permission.APPROVE_INCIDENT_REPORTS"/>
         <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE" />
         <permission name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME" />
+        <permission name="android.permission.PACKAGE_USAGE_STATS" />
     </privapp-permissions>
 
     <privapp-permissions package="com.android.phone">
@@ -359,6 +360,7 @@
         <permission name="android.permission.CONNECTIVITY_INTERNAL"/>
         <permission name="android.permission.DUMP"/>
         <permission name="android.permission.INTERACT_ACROSS_USERS"/>
+        <permission name="android.permission.INTERNAL_SYSTEM_WINDOW"/>
         <permission name="android.permission.LOCAL_MAC_ADDRESS"/>
         <permission name="android.permission.MANAGE_USERS"/>
         <permission name="android.permission.PACKAGE_USAGE_STATS"/>
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index ae90f11..61b72cf 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -26,8 +26,10 @@
         // a problem
         "-Wno-free-nonheap-object",
 
-        // clang's warning is broken, see: https://llvm.org/bugs/show_bug.cgi?id=21629
-        "-Wno-missing-braces",
+        // Clang is producing non-determistic binary when the new pass manager is
+        // enabled. Disable the new PM as a temporary workaround.
+        // b/142372146
+        "-fno-experimental-new-pass-manager",
     ],
 
     include_dirs: [
diff --git a/libs/hwui/DamageAccumulator.h b/libs/hwui/DamageAccumulator.h
index 7d0b687..030a20f 100644
--- a/libs/hwui/DamageAccumulator.h
+++ b/libs/hwui/DamageAccumulator.h
@@ -27,7 +27,7 @@
 // Smaller than INT_MIN/INT_MAX because we offset these values
 // and thus don't want to be adding offsets to INT_MAX, that's bad
 #define DIRTY_MIN (-0x7ffffff - 1)
-#define DIRTY_MAX (0x7ffffff)
+#define DIRTY_MAX (0x8000000)
 
 namespace android {
 namespace uirenderer {
diff --git a/location/java/android/location/AbstractListenerManager.java b/location/java/android/location/AbstractListenerManager.java
index c41023e..944ebf9 100644
--- a/location/java/android/location/AbstractListenerManager.java
+++ b/location/java/android/location/AbstractListenerManager.java
@@ -42,8 +42,8 @@
         @Nullable private volatile T mListener;
 
         private Registration(Executor executor, T listener) {
-            Preconditions.checkArgument(listener != null);
-            Preconditions.checkArgument(executor != null);
+            Preconditions.checkArgument(listener != null, "invalid null listener/callback");
+            Preconditions.checkArgument(executor != null, "invalid null executor");
             mExecutor = executor;
             mListener = listener;
         }
@@ -83,16 +83,18 @@
         return addInternal(listener, executor);
     }
 
-    protected final boolean addInternal(Object listener, Handler handler) throws RemoteException {
+    protected final boolean addInternal(@NonNull Object listener, @NonNull Handler handler)
+            throws RemoteException {
         return addInternal(listener, new HandlerExecutor(handler));
     }
 
-    protected final boolean addInternal(Object listener, Executor executor) throws RemoteException {
+    protected final boolean addInternal(@NonNull Object listener, @NonNull Executor executor)
+            throws RemoteException {
+        Preconditions.checkArgument(listener != null, "invalid null listener/callback");
         return addInternal(listener, new Registration<>(executor, convertKey(listener)));
     }
 
     private boolean addInternal(Object key, Registration<T> registration) throws RemoteException {
-        Preconditions.checkNotNull(key);
         Preconditions.checkNotNull(registration);
 
         synchronized (mListeners) {
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index 7f650e3..daa2e08 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -42,11 +42,11 @@
 interface ILocationManager
 {
     void requestLocationUpdates(in LocationRequest request, in ILocationListener listener,
-            in PendingIntent intent, String packageName);
+            in PendingIntent intent, String packageName, String listenerIdentifier);
     void removeUpdates(in ILocationListener listener, in PendingIntent intent, String packageName);
 
     void requestGeofence(in LocationRequest request, in Geofence geofence,
-            in PendingIntent intent, String packageName);
+            in PendingIntent intent, String packageName, String listenerIdentifier);
     void removeGeofence(in Geofence fence, in PendingIntent intent, String packageName);
 
     Location getLastLocation(in LocationRequest request, String packageName);
@@ -64,22 +64,23 @@
 
     boolean sendNiResponse(int notifId, int userResponse);
 
-    boolean addGnssMeasurementsListener(in IGnssMeasurementsListener listener, in String packageName);
+    boolean addGnssMeasurementsListener(in IGnssMeasurementsListener listener,
+             String packageName, String listenerIdentifier);
     void injectGnssMeasurementCorrections(in GnssMeasurementCorrections corrections,
             in String packageName);
     long getGnssCapabilities(in String packageName);
     void removeGnssMeasurementsListener(in IGnssMeasurementsListener listener);
 
-    boolean addGnssNavigationMessageListener(
-            in IGnssNavigationMessageListener listener,
-            in String packageName);
+    boolean addGnssNavigationMessageListener(in IGnssNavigationMessageListener listener,
+             String packageName, String listenerIdentifier);
     void removeGnssNavigationMessageListener(in IGnssNavigationMessageListener listener);
 
     int getGnssYearOfHardware();
     String getGnssHardwareModelName();
 
     int getGnssBatchSize(String packageName);
-    boolean addGnssBatchingCallback(in IBatchedLocationCallback callback, String packageName);
+    boolean addGnssBatchingCallback(in IBatchedLocationCallback callback, String packageName,
+             String listenerIdentifier);
     void removeGnssBatchingCallback();
     boolean startGnssBatch(long periodNanos, boolean wakeOnFifoFull, String packageName);
     void flushGnssBatch(String packageName);
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 5a0700d..0b3e1c3 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -543,6 +543,23 @@
     }
 
     /**
+     * Create a string that allows an app to identify a listener
+     *
+     * @param listener The listener
+     *
+     * @return A identifying string
+     */
+    private static String getListenerIdentifier(@NonNull Object listener) {
+        StringBuilder sb = new StringBuilder();
+
+        sb.append(listener.getClass().getName());
+        sb.append('@');
+        sb.append(Integer.toHexString(System.identityHashCode(listener)));
+
+        return sb.toString();
+    }
+
+    /**
      * Register for a single location update using the named provider and
      * a callback.
      *
@@ -982,7 +999,7 @@
             boolean registered = false;
             try {
                 mService.requestLocationUpdates(locationRequest, transport, null,
-                        mContext.getPackageName());
+                        mContext.getPackageName(), getListenerIdentifier(listener));
                 registered = true;
             } catch (RemoteException e) {
                 throw e.rethrowFromSystemServer();
@@ -1027,7 +1044,7 @@
 
         try {
             mService.requestLocationUpdates(locationRequest, null, pendingIntent,
-                    mContext.getPackageName());
+                    mContext.getPackageName(), getListenerIdentifier(pendingIntent));
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1490,7 +1507,8 @@
         Geofence fence = Geofence.createCircle(latitude, longitude, radius);
         LocationRequest request = new LocationRequest().setExpireIn(expiration);
         try {
-            mService.requestGeofence(request, fence, intent, mContext.getPackageName());
+            mService.requestGeofence(request, fence, intent, mContext.getPackageName(),
+                    getListenerIdentifier(intent));
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1567,7 +1585,8 @@
         Preconditions.checkArgument(fence != null, "invalid null geofence");
 
         try {
-            mService.requestGeofence(request, fence, intent, mContext.getPackageName());
+            mService.requestGeofence(request, fence, intent, mContext.getPackageName(),
+                    getListenerIdentifier(intent));
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1781,6 +1800,7 @@
      * @param handler  a handler with a looper that the callback runs on
      * @return true if the listener was successfully added
      *
+     * @throws IllegalArgumentException if callback is null
      * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
      */
     @RequiresPermission(ACCESS_FINE_LOCATION)
@@ -1800,10 +1820,12 @@
     /**
      * Registers a GNSS status callback.
      *
-     * @param callback GNSS status callback object to register
      * @param executor the executor that the callback runs on
+     * @param callback GNSS status callback object to register
      * @return true if the listener was successfully added
      *
+     * @throws IllegalArgumentException if executor is null
+     * @throws IllegalArgumentException if callback is null
      * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
      */
     @RequiresPermission(ACCESS_FINE_LOCATION)
@@ -1872,6 +1894,8 @@
      * @param listener a {@link OnNmeaMessageListener} object to register
      * @param handler  a handler with the looper that the listener runs on.
      * @return true if the listener was successfully added
+     *
+     * @throws IllegalArgumentException if listener is null
      * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
      */
     @RequiresPermission(ACCESS_FINE_LOCATION)
@@ -1893,6 +1917,9 @@
      * @param listener a {@link OnNmeaMessageListener} object to register
      * @param executor the {@link Executor} that the listener runs on.
      * @return true if the listener was successfully added
+     *
+     * @throws IllegalArgumentException if executor is null
+     * @throws IllegalArgumentException if listener is null
      * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
      */
     @RequiresPermission(ACCESS_FINE_LOCATION)
@@ -1965,6 +1992,9 @@
      * @param callback a {@link GnssMeasurementsEvent.Callback} object to register.
      * @param handler  the handler that the callback runs on.
      * @return {@code true} if the callback was added successfully, {@code false} otherwise.
+     *
+     * @throws IllegalArgumentException if callback is null
+     * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
      */
     @RequiresPermission(ACCESS_FINE_LOCATION)
     public boolean registerGnssMeasurementsCallback(
@@ -1985,6 +2015,10 @@
      * @param callback a {@link GnssMeasurementsEvent.Callback} object to register.
      * @param executor the executor that the callback runs on.
      * @return {@code true} if the callback was added successfully, {@code false} otherwise.
+     *
+     * @throws IllegalArgumentException if executor is null
+     * @throws IllegalArgumentException if callback is null
+     * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
      */
     @RequiresPermission(ACCESS_FINE_LOCATION)
     public boolean registerGnssMeasurementsCallback(
@@ -2002,6 +2036,9 @@
      *
      * @param measurementCorrections a {@link GnssMeasurementCorrections} object with the GNSS
      *     measurement corrections to be injected into the GNSS chipset.
+     *
+     * @throws IllegalArgumentException if measurementCorrections is null
+     * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
      * @hide
      */
     @SystemApi
@@ -2076,6 +2113,9 @@
      * @param callback a {@link GnssNavigationMessage.Callback} object to register.
      * @param handler  the handler that the callback runs on.
      * @return {@code true} if the callback was added successfully, {@code false} otherwise.
+     *
+     * @throws IllegalArgumentException if callback is null
+     * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
      */
     @RequiresPermission(ACCESS_FINE_LOCATION)
     public boolean registerGnssNavigationMessageCallback(
@@ -2097,6 +2137,10 @@
      * @param callback a {@link GnssNavigationMessage.Callback} object to register.
      * @param executor the looper that the callback runs on.
      * @return {@code true} if the callback was added successfully, {@code false} otherwise.
+     *
+     * @throws IllegalArgumentException if executor is null
+     * @throws IllegalArgumentException if callback is null
+     * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
      */
     @RequiresPermission(ACCESS_FINE_LOCATION)
     public boolean registerGnssNavigationMessageCallback(
@@ -2557,7 +2601,7 @@
 
             mListenerTransport = new GnssMeasurementsListener();
             return mService.addGnssMeasurementsListener(mListenerTransport,
-                    mContext.getPackageName());
+                    mContext.getPackageName(), "gnss measurement callback");
         }
 
         @Override
@@ -2593,7 +2637,7 @@
 
             mListenerTransport = new GnssNavigationMessageListener();
             return mService.addGnssNavigationMessageListener(mListenerTransport,
-                    mContext.getPackageName());
+                    mContext.getPackageName(), "gnss navigation callback");
         }
 
         @Override
@@ -2628,7 +2672,8 @@
             Preconditions.checkState(mListenerTransport == null);
 
             mListenerTransport = new BatchedLocationCallback();
-            return mService.addGnssBatchingCallback(mListenerTransport, mContext.getPackageName());
+            return mService.addGnssBatchingCallback(mListenerTransport, mContext.getPackageName(),
+                     "batched location callback");
         }
 
         @Override
diff --git a/media/java/android/media/IMediaRoute2Provider.aidl b/media/java/android/media/IMediaRoute2Provider.aidl
index f132cef..66764c7 100644
--- a/media/java/android/media/IMediaRoute2Provider.aidl
+++ b/media/java/android/media/IMediaRoute2Provider.aidl
@@ -27,4 +27,6 @@
     void selectRoute(String packageName, String id);
     void unselectRoute(String packageName, String id);
     void notifyControlRequestSent(String id, in Intent request);
+    void requestSetVolume(String id, int volume);
+    void requestUpdateVolume(String id, int delta);
 }
diff --git a/media/java/android/media/IMediaRouterService.aidl b/media/java/android/media/IMediaRouterService.aidl
index 81213b9..7b7a34e 100644
--- a/media/java/android/media/IMediaRouterService.aidl
+++ b/media/java/android/media/IMediaRouterService.aidl
@@ -46,6 +46,8 @@
     void registerClient2(IMediaRouter2Client client, String packageName);
     void unregisterClient2(IMediaRouter2Client client);
     void sendControlRequest(IMediaRouter2Client client, in MediaRoute2Info route, in Intent request);
+    void requestSetVolume2(IMediaRouter2Client client, in MediaRoute2Info route, int volume);
+    void requestUpdateVolume2(IMediaRouter2Client client, in MediaRoute2Info route, int direction);
     /**
      * Changes the selected route of the client.
      *
@@ -66,4 +68,9 @@
      */
     void selectClientRoute2(IMediaRouter2Manager manager, String packageName,
             in @nullable MediaRoute2Info route);
+
+    void requestSetVolume2Manager(IMediaRouter2Manager manager,
+            in MediaRoute2Info route, int volume);
+    void requestUpdateVolume2Manager(IMediaRouter2Manager manager,
+            in MediaRoute2Info route, int direction);
 }
diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java
index 26e7936..91d644b 100644
--- a/media/java/android/media/MediaCodecInfo.java
+++ b/media/java/android/media/MediaCodecInfo.java
@@ -21,6 +21,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SuppressLint;
 import android.annotation.TestApi;
 import android.annotation.UnsupportedAppUsage;
 import android.os.Build;
@@ -3750,8 +3751,11 @@
         public static final int DolbyVisionProfileDvheStn = 0x20;
         public static final int DolbyVisionProfileDvheDth = 0x40;
         public static final int DolbyVisionProfileDvheDtb = 0x80;
-        public static final int DolbyVisionProfileDvheSt = 0x100;
-        public static final int DolbyVisionProfileDvavSe = 0x200;
+        public static final int DolbyVisionProfileDvheSt  = 0x100;
+        public static final int DolbyVisionProfileDvavSe  = 0x200;
+        /** Dolby Vision AV1 profile */
+        @SuppressLint("AllUpper")
+        public static final int DolbyVisionProfileDvav110 = 0x400;
 
         public static final int DolbyVisionLevelHd24    = 0x1;
         public static final int DolbyVisionLevelHd30    = 0x2;
diff --git a/media/java/android/media/MediaRoute2ProviderService.java b/media/java/android/media/MediaRoute2ProviderService.java
index e8e0f82..58deff2 100644
--- a/media/java/android/media/MediaRoute2ProviderService.java
+++ b/media/java/android/media/MediaRoute2ProviderService.java
@@ -81,6 +81,20 @@
     public abstract void onControlRequest(String routeId, Intent request);
 
     /**
+     * Called when requestSetVolume is called on a route of the provider
+     * @param routeId the id of the route
+     * @param volume the target volume
+     */
+    public abstract void onSetVolume(String routeId, int volume);
+
+    /**
+     * Called when requestUpdateVolume is called on a route of the provider
+     * @param routeId id of the route
+     * @param delta the delta to add to the current volume
+     */
+    public abstract void onUpdateVolume(String routeId, int delta);
+
+    /**
      * Updates provider info and publishes routes
      */
     public final void setProviderInfo(MediaRoute2ProviderInfo info) {
@@ -130,5 +144,17 @@
             mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onControlRequest,
                     MediaRoute2ProviderService.this, id, request));
         }
+
+        @Override
+        public void requestSetVolume(String id, int volume) {
+            mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onSetVolume,
+                    MediaRoute2ProviderService.this, id, volume));
+        }
+
+        @Override
+        public void requestUpdateVolume(String id, int delta) {
+            mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onUpdateVolume,
+                    MediaRoute2ProviderService.this, id, delta));
+        }
     }
 }
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index ed35ef6..aca40d8 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -265,6 +265,54 @@
         }
     }
 
+    /**
+     * Requests a volume change for the route asynchronously.
+     * <p>
+     * It may have no effect if the route is currently not selected.
+     * </p>
+     *
+     * @param volume The new volume value between 0 and {@link MediaRoute2Info#getVolumeMax}.
+     */
+    public void requestSetVolume(@NonNull MediaRoute2Info route, int volume) {
+        Objects.requireNonNull(route, "route must not be null");
+
+        Client client;
+        synchronized (sLock) {
+            client = mClient;
+        }
+        if (client != null) {
+            try {
+                mMediaRouterService.requestSetVolume2(client, route, volume);
+            } catch (RemoteException ex) {
+                Log.e(TAG, "Unable to send control request.", ex);
+            }
+        }
+    }
+
+    /**
+     * Requests an incremental volume update  for the route asynchronously.
+     * <p>
+     * It may have no effect if the route is currently not selected.
+     * </p>
+     *
+     * @param delta The delta to add to the current volume.
+     */
+    public void requestUpdateVolume(@NonNull MediaRoute2Info route, int delta) {
+        Objects.requireNonNull(route, "route must not be null");
+
+        Client client;
+        synchronized (sLock) {
+            client = mClient;
+        }
+        if (client != null) {
+            try {
+                mMediaRouterService.requestUpdateVolume2(client, route, delta);
+            } catch (RemoteException ex) {
+                Log.e(TAG, "Unable to send control request.", ex);
+            }
+        }
+    }
+
     @GuardedBy("mCallbackRecords")
     private int findCallbackRecordIndexLocked(Callback callback) {
         final int count = mCallbackRecords.size();
@@ -310,6 +358,7 @@
             List<MediaRoute2Info> outRoutes) {
         if (provider == null || !provider.isValid()) {
             Log.w(TAG, "Ignoring invalid provider : " + provider);
+            return;
         }
 
         final Collection<MediaRoute2Info> routes = provider.getRoutes();
@@ -321,10 +370,21 @@
             if (!route.supportsControlCategory(controlCategories)) {
                 continue;
             }
+            MediaRoute2Info preRoute = findRouteById(route.getId());
+            if (!route.equals(preRoute)) {
+                notifyRouteChanged(route);
+            }
             outRoutes.add(route);
         }
     }
 
+    MediaRoute2Info findRouteById(String id) {
+        for (MediaRoute2Info route : mRoutes) {
+            if (route.getId().equals(id)) return route;
+        }
+        return null;
+    }
+
     void notifyRouteListChanged(List<MediaRoute2Info> routes) {
         for (CallbackRecord record: mCallbackRecords) {
             record.mExecutor.execute(
@@ -332,10 +392,18 @@
         }
     }
 
+    void notifyRouteChanged(MediaRoute2Info route) {
+        for (CallbackRecord record: mCallbackRecords) {
+            record.mExecutor.execute(
+                    () -> record.mCallback.onRouteChanged(route));
+        }
+    }
+
     /**
      * Interface for receiving events about media routing changes.
      */
     public static class Callback {
+        //TODO: clean up these callbacks
         /**
          * Called when a route is added.
          */
@@ -369,7 +437,7 @@
         void notifyRoutes() {
             final List<MediaRoute2Info> routes = mRoutes;
             // notify only when bound to media router service.
-            //TODO: Correct the condition when control category, default rotue, .. are finalized.
+            //TODO: Correct the condition when control category, default route, .. are finalized.
             if (routes.size() > 0) {
                 mExecutor.execute(() -> mCallback.onRoutesChanged(routes));
             }
diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java
index 0b64569..4f2a295 100644
--- a/media/java/android/media/MediaRouter2Manager.java
+++ b/media/java/android/media/MediaRouter2Manager.java
@@ -234,6 +234,54 @@
         }
     }
 
+    /**
+     * Requests a volume change for the route asynchronously.
+     * <p>
+     * It may have no effect if the route is currently not selected.
+     * </p>
+     *
+     * @param volume The new volume value between 0 and {@link MediaRoute2Info#getVolumeMax}.
+     */
+    public void requestSetVolume(@NonNull MediaRoute2Info route, int volume) {
+        Objects.requireNonNull(route, "route must not be null");
+
+        Client client;
+        synchronized (sLock) {
+            client = mClient;
+        }
+        if (client != null) {
+            try {
+                mMediaRouterService.requestSetVolume2Manager(client, route, volume);
+            } catch (RemoteException ex) {
+                Log.e(TAG, "Unable to send control request.", ex);
+            }
+        }
+    }
+
+    /**
+     * Requests an incremental volume update  for the route asynchronously.
+     * <p>
+     * It may have no effect if the route is currently not selected.
+     * </p>
+     *
+     * @param delta The delta to add to the current volume.
+     */
+    public void requestUpdateVolume(@NonNull MediaRoute2Info route, int delta) {
+        Objects.requireNonNull(route, "route must not be null");
+
+        Client client;
+        synchronized (sLock) {
+            client = mClient;
+        }
+        if (client != null) {
+            try {
+                mMediaRouterService.requestUpdateVolume2Manager(client, route, delta);
+            } catch (RemoteException ex) {
+                Log.e(TAG, "Unable to send control request.", ex);
+            }
+        }
+    }
+
     int findProviderIndex(MediaRoute2ProviderInfo provider) {
         final int count = mProviders.size();
         for (int i = 0; i < count; i++) {
diff --git a/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java b/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java
index 8d39a93..680c879 100644
--- a/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java
+++ b/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java
@@ -130,6 +130,33 @@
         }
     }
 
+    @Override
+    public void onSetVolume(String routeId, int volume) {
+        MediaRoute2Info route = mRoutes.get(routeId);
+        if (route == null) {
+            return;
+        }
+        volume = Math.min(volume, Math.max(0, route.getVolumeMax()));
+        mRoutes.put(routeId, new MediaRoute2Info.Builder(route)
+                .setVolume(volume)
+                .build());
+        publishRoutes();
+    }
+
+    @Override
+    public void onUpdateVolume(String routeId, int delta) {
+        MediaRoute2Info route = mRoutes.get(routeId);
+        if (route == null) {
+            return;
+        }
+        int volume = route.getVolume() + delta;
+        volume = Math.min(volume, Math.max(0, route.getVolumeMax()));
+        mRoutes.put(routeId, new MediaRoute2Info.Builder(route)
+                .setVolume(volume)
+                .build());
+        publishRoutes();
+    }
+
     void publishRoutes() {
         MediaRoute2ProviderInfo info = new MediaRoute2ProviderInfo.Builder()
                 .addRoutes(mRoutes.values())
diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
index da832ac..ca43d04 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
@@ -47,8 +47,10 @@
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.Executor;
 import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
 
 @RunWith(AndroidJUnit4.class)
 @SmallTest
@@ -190,17 +192,8 @@
         MediaRouter2Manager.Callback mockCallback = mock(MediaRouter2Manager.Callback.class);
         mManager.registerCallback(mExecutor, mockCallback);
 
-        MediaRouter2.Callback mockRouterCallback = mock(MediaRouter2.Callback.class);
-
-        mRouter2.setControlCategories(CONTROL_CATEGORIES_SPECIAL);
-        mRouter2.registerCallback(mExecutor, mockRouterCallback);
-        mRouter2.unregisterCallback(mockRouterCallback);
-
-        verify(mockCallback, timeout(TIMEOUT_MS))
-                .onRoutesChanged(argThat(routes -> routes.size() > 0));
-
         Map<String, MediaRoute2Info> routes =
-                createRouteMap(mManager.getAvailableRoutes(mPackageName));
+                waitAndGetRoutesWithManager(CONTROL_CATEGORIES_SPECIAL);
 
         Assert.assertEquals(1, routes.size());
         Assert.assertNotNull(routes.get(ROUTE_ID_SPECIAL_CATEGORY));
@@ -214,12 +207,10 @@
     @Test
     public void testGetRoutes() throws Exception {
         MediaRouter2.Callback mockCallback = mock(MediaRouter2.Callback.class);
-
-        mRouter2.setControlCategories(CONTROL_CATEGORIES_SPECIAL);
         mRouter2.registerCallback(mExecutor, mockCallback);
-        verify(mockCallback, timeout(TIMEOUT_MS).atLeastOnce())
-                .onRoutesChanged(argThat(routes -> routes.size() > 0));
-        Map<String, MediaRoute2Info> routes = createRouteMap(mRouter2.getRoutes());
+
+        Map<String, MediaRoute2Info> routes = waitAndGetRoutes(CONTROL_CATEGORIES_SPECIAL);
+
         Assert.assertEquals(1, routes.size());
         Assert.assertNotNull(routes.get(ROUTE_ID_SPECIAL_CATEGORY));
 
@@ -228,51 +219,40 @@
 
     @Test
     public void testOnRouteSelected() throws Exception {
-        MediaRouter2.Callback mockRouterCallback = mock(MediaRouter2.Callback.class);
+        MediaRouter2.Callback routerCallback = new MediaRouter2.Callback();
         MediaRouter2Manager.Callback managerCallback = mock(MediaRouter2Manager.Callback.class);
 
         mManager.registerCallback(mExecutor, managerCallback);
-        mRouter2.setControlCategories(CONTROL_CATEGORIES_ALL);
-        mRouter2.registerCallback(mExecutor, mockRouterCallback);
+        mRouter2.registerCallback(mExecutor, routerCallback);
 
-        verify(managerCallback, timeout(TIMEOUT_MS))
-                .onRoutesChanged(argThat(routes -> routes.size() > 0));
-
-        Map<String, MediaRoute2Info> routes =
-                createRouteMap(mManager.getAvailableRoutes(mPackageName));
+        Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(CONTROL_CATEGORIES_ALL);
 
         MediaRoute2Info routeToSelect = routes.get(ROUTE_ID1);
-        mManager.selectRoute(mPackageName, routeToSelect);
-
         assertNotNull(routeToSelect);
+
+        mManager.selectRoute(mPackageName, routeToSelect);
         verify(managerCallback, timeout(TIMEOUT_MS))
                 .onRouteAdded(argThat(route -> route.equals(routeToSelect)));
 
+        mRouter2.unregisterCallback(routerCallback);
         mManager.unregisterCallback(managerCallback);
-        mRouter2.unregisterCallback(mockRouterCallback);
     }
 
     /**
      * Tests selecting and unselecting routes of a single provider.
      */
     @Test
-    public void testSingleProviderSelect() {
+    public void testSingleProviderSelect() throws Exception {
         MediaRouter2Manager.Callback managerCallback = mock(MediaRouter2Manager.Callback.class);
         MediaRouter2.Callback routerCallback = mock(MediaRouter2.Callback.class);
 
         mManager.registerCallback(mExecutor, managerCallback);
-        mRouter2.setControlCategories(CONTROL_CATEGORIES_ALL);
         mRouter2.registerCallback(mExecutor, routerCallback);
 
-        verify(managerCallback, timeout(TIMEOUT_MS))
-                .onRoutesChanged(argThat(routes -> routes.size() > 0));
-
-        Map<String, MediaRoute2Info> routes =
-                createRouteMap(mManager.getAvailableRoutes(mPackageName));
+        Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(CONTROL_CATEGORIES_ALL);
 
         mManager.selectRoute(mPackageName, routes.get(ROUTE_ID1));
-        verify(managerCallback, timeout(TIMEOUT_MS)
-        )
+        verify(managerCallback, timeout(TIMEOUT_MS))
                 .onRouteChanged(argThat(routeInfo -> TextUtils.equals(ROUTE_ID1, routeInfo.getId())
                         && TextUtils.equals(routeInfo.getClientPackageName(), mPackageName)));
 
@@ -291,14 +271,67 @@
     }
 
     @Test
-    public void testVolumeHandling() {
+    public void testControlVolumeWithRouter() throws Exception {
         MediaRouter2.Callback mockCallback = mock(MediaRouter2.Callback.class);
 
-        mRouter2.setControlCategories(CONTROL_CATEGORIES_ALL);
+        Map<String, MediaRoute2Info> routes = waitAndGetRoutes(CONTROL_CATEGORIES_ALL);
         mRouter2.registerCallback(mExecutor, mockCallback);
+
+        MediaRoute2Info volRoute = routes.get(ROUTE_ID_VARIABLE_VOLUME);
+        int originalVolume = volRoute.getVolume();
+        int deltaVolume = (originalVolume == volRoute.getVolumeMax() ? -1 : 1);
+        int targetVolume = originalVolume + deltaVolume;
+
+        mRouter2.requestSetVolume(volRoute, targetVolume);
         verify(mockCallback, timeout(TIMEOUT_MS).atLeastOnce())
-                .onRoutesChanged(argThat(routes -> routes.size() > 0));
-        Map<String, MediaRoute2Info> routes = createRouteMap(mRouter2.getRoutes());
+                .onRouteChanged(argThat(route ->
+                        route.getId().equals(volRoute.getId())
+                                && route.getVolume() == targetVolume));
+
+        mRouter2.requestUpdateVolume(volRoute, -deltaVolume);
+        verify(mockCallback, timeout(TIMEOUT_MS).atLeastOnce())
+                .onRouteChanged(argThat(route ->
+                        route.getId().equals(volRoute.getId())
+                                && route.getVolume() == originalVolume));
+
+        mRouter2.unregisterCallback(mockCallback);
+    }
+
+    @Test
+    public void testControlVolumeWithManager() throws Exception {
+        MediaRouter2Manager.Callback managerCallback = mock(MediaRouter2Manager.Callback.class);
+        MediaRouter2.Callback mockCallback = mock(MediaRouter2.Callback.class);
+
+        mManager.registerCallback(mExecutor, managerCallback);
+        mRouter2.registerCallback(mExecutor, mockCallback);
+        Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(CONTROL_CATEGORIES_ALL);
+
+        MediaRoute2Info volRoute = routes.get(ROUTE_ID_VARIABLE_VOLUME);
+        int originalVolume = volRoute.getVolume();
+        int deltaVolume = (originalVolume == volRoute.getVolumeMax() ? -1 : 1);
+        int targetVolume = originalVolume + deltaVolume;
+
+        mManager.requestSetVolume(volRoute, targetVolume);
+        verify(mockCallback, timeout(TIMEOUT_MS).atLeastOnce())
+                .onRouteChanged(argThat(route ->
+                        route.getId().equals(volRoute.getId())
+                                && route.getVolume() == targetVolume));
+
+        mManager.requestUpdateVolume(volRoute, -deltaVolume);
+        verify(mockCallback, timeout(TIMEOUT_MS).atLeastOnce())
+                .onRouteChanged(argThat(route ->
+                        route.getId().equals(volRoute.getId())
+                                && route.getVolume() == originalVolume));
+
+        mRouter2.unregisterCallback(mockCallback);
+        mManager.unregisterCallback(managerCallback);
+    }
+
+    @Test
+    public void testVolumeHandling() throws Exception {
+        MediaRouter2.Callback mockCallback = mock(MediaRouter2.Callback.class);
+        mRouter2.registerCallback(mExecutor, mockCallback);
+        Map<String, MediaRoute2Info> routes = waitAndGetRoutes(CONTROL_CATEGORIES_ALL);
 
         MediaRoute2Info fixedVolumeRoute = routes.get(ROUTE_ID_FIXED_VOLUME);
         MediaRoute2Info variableVolumeRoute = routes.get(ROUTE_ID_VARIABLE_VOLUME);
@@ -310,6 +343,48 @@
         mRouter2.unregisterCallback(mockCallback);
     }
 
+    Map<String, MediaRoute2Info> waitAndGetRoutes(List<String> controlCategories) throws Exception {
+        CountDownLatch latch = new CountDownLatch(1);
+        MediaRouter2.Callback callback = new MediaRouter2.Callback() {
+            @Override
+            public void onRoutesChanged(List<MediaRoute2Info> routes) {
+                if (routes.size() > 0) latch.countDown();
+            }
+        };
+        mRouter2.setControlCategories(controlCategories);
+        mRouter2.registerCallback(mExecutor, callback);
+        try {
+            latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+            return createRouteMap(mRouter2.getRoutes());
+        } finally {
+            mRouter2.unregisterCallback(callback);
+        }
+    }
+
+    Map<String, MediaRoute2Info> waitAndGetRoutesWithManager(List<String> controlCategories)
+            throws Exception {
+        CountDownLatch latch = new CountDownLatch(1);
+
+        // Dummy callback is required to send control category info.
+        MediaRouter2.Callback routerCallback = new MediaRouter2.Callback();
+        MediaRouter2Manager.Callback managerCallback = new MediaRouter2Manager.Callback() {
+            @Override
+            public void onRoutesChanged(List<MediaRoute2Info> routes) {
+                if (routes.size() > 0) latch.countDown();
+            }
+        };
+        mManager.registerCallback(mExecutor, managerCallback);
+        mRouter2.setControlCategories(controlCategories);
+        mRouter2.registerCallback(mExecutor, routerCallback);
+        try {
+            latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS);
+            return createRouteMap(mManager.getAvailableRoutes(mPackageName));
+        } finally {
+            mRouter2.unregisterCallback(routerCallback);
+            mManager.unregisterCallback(managerCallback);
+        }
+    }
+
     // Helper for getting routes easily
     static Map<String, MediaRoute2Info> createRouteMap(List<MediaRoute2Info> routes) {
         Map<String, MediaRoute2Info> routeMap = new HashMap<>();
diff --git a/packages/CarSystemUI/res/layout/car_top_navigation_bar_unprovisioned.xml b/packages/CarSystemUI/res/layout/car_top_navigation_bar_unprovisioned.xml
new file mode 100644
index 0000000..8247211
--- /dev/null
+++ b/packages/CarSystemUI/res/layout/car_top_navigation_bar_unprovisioned.xml
@@ -0,0 +1,147 @@
+<?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
+  -->
+
+<com.android.systemui.statusbar.car.CarNavigationBarView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:systemui="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/car_top_bar"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="@drawable/system_bar_background"
+    android:orientation="vertical">
+
+  <RelativeLayout
+      android:layout_width="match_parent"
+      android:layout_height="wrap_content"
+      android:layout_weight="1">
+
+    <FrameLayout
+        android:id="@+id/left_hvac_container"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:layout_alignParentStart="true"
+        >
+
+      <com.android.systemui.statusbar.car.CarNavigationButton
+          android:id="@+id/hvacleft"
+          android:layout_width="match_parent"
+          android:layout_height="match_parent"
+          android:background="@null"
+          systemui:broadcast="true"
+          systemui:intent="intent:#Intent;action=android.car.intent.action.TOGGLE_HVAC_CONTROLS;end"
+          />
+
+      <com.android.systemui.statusbar.hvac.AnimatedTemperatureView
+          android:id="@+id/lefttext"
+          android:layout_width="wrap_content"
+          android:layout_height="match_parent"
+          android:paddingStart="@*android:dimen/car_padding_4"
+          android:paddingEnd="16dp"
+          android:gravity="center_vertical|start"
+          android:minEms="4"
+          android:textAppearance="@style/TextAppearance.CarStatus"
+          systemui:hvacAreaId="49"
+          systemui:hvacMaxText="@string/hvac_max_text"
+          systemui:hvacMaxValue="@dimen/hvac_max_value"
+          systemui:hvacMinText="@string/hvac_min_text"
+          systemui:hvacMinValue="@dimen/hvac_min_value"
+          systemui:hvacPivotOffset="60dp"
+          systemui:hvacPropertyId="358614275"
+          systemui:hvacTempFormat="%.0f\u00B0"
+          />
+    </FrameLayout>
+
+    <FrameLayout
+        android:id="@+id/clock_container"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:layout_centerInParent="true">
+      <com.android.systemui.statusbar.car.CarNavigationButton
+          android:id="@+id/qs"
+          android:layout_width="match_parent"
+          android:layout_height="match_parent"
+          android:background="@null"/>
+      <com.android.systemui.statusbar.policy.Clock
+          android:id="@+id/clock"
+          android:layout_width="wrap_content"
+          android:layout_height="wrap_content"
+          android:layout_gravity="center"
+          android:elevation="5dp"
+          android:singleLine="true"
+          android:textAppearance="@style/TextAppearance.StatusBar.Clock"/>
+    </FrameLayout>
+
+    <LinearLayout
+        android:id="@+id/system_icon_area"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:layout_centerHorizontal="true"
+        android:layout_centerVertical="true"
+        android:layout_toEndOf="@+id/clock_container"
+        android:paddingStart="@*android:dimen/car_padding_1"
+        android:gravity="center_vertical"
+        android:orientation="horizontal"
+        >
+
+      <include
+          layout="@layout/system_icons"
+          android:layout_width="wrap_content"
+          android:layout_height="wrap_content"
+          android:layout_weight="1"
+          android:paddingStart="4dp"
+          android:gravity="center_vertical"
+          />
+    </LinearLayout>
+
+    <FrameLayout
+        android:id="@+id/right_hvac_container"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:layout_alignParentEnd="true"
+        >
+
+      <com.android.systemui.statusbar.car.CarNavigationButton
+          android:id="@+id/hvacright"
+          android:layout_width="match_parent"
+          android:layout_height="match_parent"
+          android:background="@null"
+          systemui:broadcast="true"
+          systemui:intent="intent:#Intent;action=android.car.intent.action.TOGGLE_HVAC_CONTROLS;end"
+          />
+
+      <com.android.systemui.statusbar.hvac.AnimatedTemperatureView
+          android:id="@+id/righttext"
+          android:layout_width="wrap_content"
+          android:layout_height="match_parent"
+          android:paddingStart="16dp"
+          android:paddingEnd="@*android:dimen/car_padding_4"
+          android:gravity="center_vertical|end"
+          android:minEms="4"
+          android:textAppearance="@style/TextAppearance.CarStatus"
+          systemui:hvacAreaId="68"
+          systemui:hvacMaxText="@string/hvac_max_text"
+          systemui:hvacMaxValue="@dimen/hvac_max_value"
+          systemui:hvacMinText="@string/hvac_min_text"
+          systemui:hvacMinValue="@dimen/hvac_min_value"
+          systemui:hvacPivotOffset="60dp"
+          systemui:hvacPropertyId="358614275"
+          systemui:hvacTempFormat="%.0f\u00B0"
+          />
+    </FrameLayout>
+  </RelativeLayout>
+
+</com.android.systemui.statusbar.car.CarNavigationBarView>
diff --git a/packages/CarSystemUI/res/layout/super_status_bar.xml b/packages/CarSystemUI/res/layout/super_status_bar.xml
index 08d48bf..37cd1d4 100644
--- a/packages/CarSystemUI/res/layout/super_status_bar.xml
+++ b/packages/CarSystemUI/res/layout/super_status_bar.xml
@@ -69,10 +69,10 @@
             android:visibility="gone"
         />
 
-        <include layout="@layout/car_top_navigation_bar"
+        <FrameLayout
+            android:id="@+id/car_top_navigation_bar_container"
             android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-        />
+            android:layout_height="wrap_content"/>
     </LinearLayout>
 
     <include layout="@layout/brightness_mirror"/>
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index a585727..9d47cdc 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -162,9 +162,11 @@
     private BatteryMeterView mBatteryMeterView;
     private Drawable mNotificationPanelBackground;
 
+    private ViewGroup mTopNavigationBarContainer;
     private ViewGroup mNavigationBarWindow;
     private ViewGroup mLeftNavigationBarWindow;
     private ViewGroup mRightNavigationBarWindow;
+    private CarNavigationBarView mTopNavigationBarView;
     private CarNavigationBarView mNavigationBarView;
     private CarNavigationBarView mLeftNavigationBarView;
     private CarNavigationBarView mRightNavigationBarView;
@@ -176,7 +178,7 @@
     private CarFacetButtonController mCarFacetButtonController;
     private ActivityManagerWrapper mActivityManagerWrapper;
     private DeviceProvisionedController mDeviceProvisionedController;
-    private boolean mDeviceIsProvisioned = true;
+    private boolean mDeviceIsSetUpForUser = true;
     private HvacController mHvacController;
     private DrivingStateHelper mDrivingStateHelper;
     private PowerManagerHelper mPowerManagerHelper;
@@ -197,6 +199,9 @@
     private boolean mNotificationListAtBottom;
     // Was the notification list at the bottom when the user first touched the screen
     private boolean mNotificationListAtBottomAtTimeOfTouch;
+    // To be attached to the top navigation bar (i.e. status bar) to pull down the notification
+    // panel.
+    private View.OnTouchListener mTopNavBarNotificationTouchListener;
     // To be attached to the navigation bars such that they can close the notification panel if
     // it's open.
     private View.OnTouchListener mNavBarNotificationTouchListener;
@@ -364,7 +369,7 @@
         // get the provisioned state before calling the parent class since it's that flow that
         // builds the nav bar
         mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class);
-        mDeviceIsProvisioned = mDeviceProvisionedController.isDeviceProvisioned();
+        mDeviceIsSetUpForUser = mDeviceProvisionedController.isCurrentUserSetup();
 
         // Keyboard related setup, before nav bars are created.
         mHideNavBarForKeyboard = mContext.getResources().getBoolean(
@@ -376,6 +381,10 @@
         mScreenLifecycle = Dependency.get(ScreenLifecycle.class);
         mScreenLifecycle.addObserver(mScreenObserver);
 
+        // Need to initialize HVAC controller before calling super.start - before system bars are
+        // created.
+        mHvacController = new HvacController(mContext);
+
         super.start();
         mTaskStackListener = new TaskStackListenerImpl();
         mActivityManagerWrapper = ActivityManagerWrapper.getInstance();
@@ -394,25 +403,18 @@
 
         mHvacController.connectToCarService();
 
-        CarSystemUIFactory factory = SystemUIFactory.getInstance();
-        if (!mDeviceIsProvisioned) {
-            mDeviceProvisionedController.addCallback(
-                    new DeviceProvisionedController.DeviceProvisionedListener() {
-                        @Override
-                        public void onDeviceProvisionedChanged() {
-                            mHandler.post(() -> {
-                                // on initial boot we are getting a call even though the value
-                                // is the same so we are confirming the reset is needed
-                                boolean deviceProvisioned =
-                                        mDeviceProvisionedController.isDeviceProvisioned();
-                                if (mDeviceIsProvisioned != deviceProvisioned) {
-                                    mDeviceIsProvisioned = deviceProvisioned;
-                                    restartNavBars();
-                                }
-                            });
-                        }
-                    });
-        }
+        mDeviceProvisionedController.addCallback(
+                new DeviceProvisionedController.DeviceProvisionedListener() {
+                    @Override
+                    public void onUserSetupChanged() {
+                        mHandler.post(() -> restartNavBarsIfNecessary());
+                    }
+
+                    @Override
+                    public void onUserSwitched() {
+                        mHandler.post(() -> restartNavBarsIfNecessary());
+                    }
+                });
 
         // Register a listener for driving state changes.
         mDrivingStateHelper = new DrivingStateHelper(mContext, this::onDrivingStateChanged);
@@ -424,6 +426,14 @@
         mSwitchToGuestTimer = new SwitchToGuestTimer(mContext);
     }
 
+    private void restartNavBarsIfNecessary() {
+        boolean currentUserSetup = mDeviceProvisionedController.isCurrentUserSetup();
+        if (mDeviceIsSetUpForUser != currentUserSetup) {
+            mDeviceIsSetUpForUser = currentUserSetup;
+            restartNavBars();
+        }
+    }
+
     /**
      * Remove all content from navbars and rebuild them. Used to allow for different nav bars
      * before and after the device is provisioned. . Also for change of density and font size.
@@ -432,8 +442,8 @@
         // remove and reattach all hvac components such that we don't keep a reference to unused
         // ui elements
         mHvacController.removeAllComponents();
-        addTemperatureViewToController(mStatusBarWindow);
         mCarFacetButtonController.removeAll();
+
         if (mNavigationBarWindow != null) {
             mNavigationBarWindow.removeAllViews();
             mNavigationBarView = null;
@@ -527,7 +537,6 @@
     @Override
     protected void makeStatusBarView(@Nullable RegisterStatusBarResult result) {
         super.makeStatusBarView(result);
-        mHvacController = new HvacController(mContext);
 
         CarSystemUIFactory factory = SystemUIFactory.getInstance();
         mCarFacetButtonController = factory.getCarDependencyComponent()
@@ -552,7 +561,8 @@
      * touch listeners needed for opening and closing the notification panel
      */
     private void connectNotificationsUI() {
-        // Attached to the status bar to detect pull down of the notification shade.
+        // Attached to the top navigation bar (i.e. status bar) to detect pull down of the
+        // notification shade.
         GestureDetector openGestureDetector = new GestureDetector(mContext,
                 new OpenNotificationGestureListener() {
                     @Override
@@ -585,6 +595,18 @@
         GestureDetector handleBarCloseNotificationGestureDetector = new GestureDetector(mContext,
                 new HandleBarCloseNotificationGestureListener());
 
+        mTopNavBarNotificationTouchListener = (v, event) -> {
+            if (!mDeviceIsSetUpForUser) {
+                return true;
+            }
+            boolean consumed = openGestureDetector.onTouchEvent(event);
+            if (consumed) {
+                return true;
+            }
+            maybeCompleteAnimation(event);
+            return true;
+        };
+
         mNavBarNotificationTouchListener =
                 (v, event) -> {
                     boolean consumed = navBarCloseNotificationGestureDetector.onTouchEvent(event);
@@ -595,21 +617,6 @@
                     return true;
                 };
 
-        // The following are the ui elements that the user would call the status bar.
-        // This will set the status bar so it they can make call backs.
-        CarNavigationBarView topBar = mStatusBarWindow.findViewById(R.id.car_top_bar);
-        topBar.setStatusBar(this);
-        topBar.setStatusBarWindowTouchListener((v1, event1) -> {
-
-                    boolean consumed = openGestureDetector.onTouchEvent(event1);
-                    if (consumed) {
-                        return true;
-                    }
-                    maybeCompleteAnimation(event1);
-                    return true;
-                }
-        );
-
         mNotificationClickHandlerFactory = new NotificationClickHandlerFactory(mBarService);
         mNotificationClickHandlerFactory.registerClickListener((launchResult, alertEntry) -> {
             if (launchResult == ActivityManager.START_TASK_TO_FRONT
@@ -916,23 +923,30 @@
     }
 
     private void buildNavBarContent() {
+        // Always build top bar.
+        buildTopBar((mDeviceIsSetUpForUser) ? R.layout.car_top_navigation_bar :
+                R.layout.car_top_navigation_bar_unprovisioned);
+
         if (mShowBottom) {
-            buildBottomBar((mDeviceIsProvisioned) ? R.layout.car_navigation_bar :
+            buildBottomBar((mDeviceIsSetUpForUser) ? R.layout.car_navigation_bar :
                     R.layout.car_navigation_bar_unprovisioned);
         }
 
         if (mShowLeft) {
-            buildLeft((mDeviceIsProvisioned) ? R.layout.car_left_navigation_bar :
+            buildLeft((mDeviceIsSetUpForUser) ? R.layout.car_left_navigation_bar :
                     R.layout.car_left_navigation_bar_unprovisioned);
         }
 
         if (mShowRight) {
-            buildRight((mDeviceIsProvisioned) ? R.layout.car_right_navigation_bar :
+            buildRight((mDeviceIsSetUpForUser) ? R.layout.car_right_navigation_bar :
                     R.layout.car_right_navigation_bar_unprovisioned);
         }
     }
 
     private void buildNavBarWindows() {
+        mTopNavigationBarContainer = mStatusBarWindow
+            .findViewById(R.id.car_top_navigation_bar_container);
+
         if (mShowBottom) {
             mNavigationBarWindow = (ViewGroup) View.inflate(mContext,
                     R.layout.navigation_bar_window, null);
@@ -965,15 +979,25 @@
         }
 
         boolean isKeyboardVisible = (vis & InputMethodService.IME_VISIBLE) != 0;
-        if (!isKeyboardVisible) {
-            attachBottomNavBarWindow();
-        } else {
-            detachBottomNavBarWindow();
-        }
+        showBottomNavBarWindow(isKeyboardVisible);
     }
 
     private void attachNavBarWindows() {
-        attachBottomNavBarWindow();
+        if (mShowBottom && !mBottomNavBarVisible) {
+            mBottomNavBarVisible = true;
+
+            WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
+                    LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
+                    WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,
+                    WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+                            | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+                            | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
+                            | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
+                    PixelFormat.TRANSLUCENT);
+            lp.setTitle("CarNavigationBar");
+            lp.windowAnimations = 0;
+            mWindowManager.addView(mNavigationBarWindow, lp);
+        }
 
         if (mShowLeft) {
             int width = mContext.getResources().getDimensionPixelSize(
@@ -1011,47 +1035,32 @@
         }
     }
 
-    /**
-     * Attaches the bottom nav bar window. Can be extended to modify the specific behavior of
-     * attaching the bottom nav bar.
-     */
-    protected void attachBottomNavBarWindow() {
+    private void showBottomNavBarWindow(boolean isKeyboardVisible) {
         if (!mShowBottom) {
             return;
         }
 
-        if (mBottomNavBarVisible) {
+        // If keyboard is visible and bottom nav bar not visible, this is the correct state, so do
+        // nothing. Same with if keyboard is not visible and bottom nav bar is visible.
+        if (isKeyboardVisible ^ mBottomNavBarVisible) {
             return;
         }
-        mBottomNavBarVisible = true;
 
-        WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
-                LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
-                WindowManager.LayoutParams.TYPE_NAVIGATION_BAR,
-                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
-                        | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
-                        | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
-                        | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH,
-                PixelFormat.TRANSLUCENT);
-        lp.setTitle("CarNavigationBar");
-        lp.windowAnimations = 0;
-        mWindowManager.addView(mNavigationBarWindow, lp);
+        mNavigationBarWindow.setVisibility(isKeyboardVisible ? View.GONE : View.VISIBLE);
+        mBottomNavBarVisible = !isKeyboardVisible;
     }
 
-    /**
-     * Detaches the bottom nav bar window. Can be extended to modify the specific behavior of
-     * detaching the bottom nav bar.
-     */
-    protected void detachBottomNavBarWindow() {
-        if (!mShowBottom) {
-            return;
+    private void buildTopBar(int layout) {
+        mTopNavigationBarContainer.removeAllViews();
+        View.inflate(mContext, layout, mTopNavigationBarContainer);
+        mTopNavigationBarView = (CarNavigationBarView) mTopNavigationBarContainer.getChildAt(0);
+        if (mTopNavigationBarView == null) {
+            Log.e(TAG, "CarStatusBar failed inflate for R.layout.car_top_navigation_bar");
+            throw new RuntimeException("Unable to build top nav bar due to missing layout");
         }
-
-        if (!mBottomNavBarVisible) {
-            return;
-        }
-        mBottomNavBarVisible = false;
-        mWindowManager.removeView(mNavigationBarWindow);
+        mTopNavigationBarView.setStatusBar(this);
+        addTemperatureViewToController(mTopNavigationBarView);
+        mTopNavigationBarView.setStatusBarWindowTouchListener(mTopNavBarNotificationTouchListener);
     }
 
     private void buildBottomBar(int layout) {
@@ -1062,7 +1071,7 @@
         mNavigationBarView = (CarNavigationBarView) mNavigationBarWindow.getChildAt(0);
         if (mNavigationBarView == null) {
             Log.e(TAG, "CarStatusBar failed inflate for R.layout.car_navigation_bar");
-            throw new RuntimeException("Unable to build botom nav bar due to missing layout");
+            throw new RuntimeException("Unable to build bottom nav bar due to missing layout");
         }
         mNavigationBarView.setStatusBar(this);
         addTemperatureViewToController(mNavigationBarView);
@@ -1073,7 +1082,7 @@
         View.inflate(mContext, layout, mLeftNavigationBarWindow);
         mLeftNavigationBarView = (CarNavigationBarView) mLeftNavigationBarWindow.getChildAt(0);
         if (mLeftNavigationBarView == null) {
-            Log.e(TAG, "CarStatusBar failed inflate for R.layout.car_navigation_bar");
+            Log.e(TAG, "CarStatusBar failed inflate for R.layout.car_left_navigation_bar");
             throw new RuntimeException("Unable to build left nav bar due to missing layout");
         }
         mLeftNavigationBarView.setStatusBar(this);
@@ -1086,7 +1095,7 @@
         View.inflate(mContext, layout, mRightNavigationBarWindow);
         mRightNavigationBarView = (CarNavigationBarView) mRightNavigationBarWindow.getChildAt(0);
         if (mRightNavigationBarView == null) {
-            Log.e(TAG, "CarStatusBar failed inflate for R.layout.car_navigation_bar");
+            Log.e(TAG, "CarStatusBar failed inflate for R.layout.car_right_navigation_bar");
             throw new RuntimeException("Unable to build right nav bar due to missing layout");
         }
         mRightNavigationBarView.setStatusBar(this);
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/OngoingPrivacyChip.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/OngoingPrivacyChip.java
index ead1de2..88d641e 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/OngoingPrivacyChip.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/OngoingPrivacyChip.java
@@ -47,6 +47,7 @@
 public class OngoingPrivacyChip extends LinearLayout implements View.OnClickListener {
 
     private Context mContext;
+    private Handler mHandler;
 
     private LinearLayout mIconsContainer;
     private List<PrivacyItem> mPrivacyItems;
@@ -88,6 +89,7 @@
 
     private void init(Context context) {
         mContext = context;
+        mHandler = new Handler(Looper.getMainLooper());
         mPrivacyItems = new ArrayList<>();
         sAppOpsController = Dependency.get(AppOpsController.class);
         mUserManager = mContext.getSystemService(UserManager.class);
@@ -131,8 +133,7 @@
     @Override
     public void onClick(View v) {
         updatePrivacyList();
-        Handler mUiHandler = new Handler(Looper.getMainLooper());
-        mUiHandler.post(() -> {
+        mHandler.post(() -> {
             mActivityStarter.postStartActivityDismissingKeyguard(
                     new Intent(Intent.ACTION_REVIEW_ONGOING_PERMISSION_USAGE), 0);
         });
@@ -152,21 +153,17 @@
     }
 
     private void updatePrivacyList() {
-        mPrivacyItems = mCurrentUserIds.stream()
+        List<PrivacyItem> privacyItems = mCurrentUserIds.stream()
                 .flatMap(item -> sAppOpsController.getActiveAppOpsForUser(item).stream())
                 .filter(Objects::nonNull)
                 .map(item -> toPrivacyItem(item))
                 .filter(Objects::nonNull)
                 .collect(Collectors.toList());
-        mPrivacyDialogBuilder = new PrivacyDialogBuilder(mContext, mPrivacyItems);
-
-        Handler refresh = new Handler(Looper.getMainLooper());
-        refresh.post(new Runnable() {
-            @Override
-            public void run() {
-                updateView();
-            }
-        });
+        if (!privacyItems.equals(mPrivacyItems)) {
+            mPrivacyItems = privacyItems;
+            mPrivacyDialogBuilder = new PrivacyDialogBuilder(mContext, mPrivacyItems);
+            mHandler.post(this::updateView);
+        }
     }
 
     private PrivacyItem toPrivacyItem(AppOpItem appOpItem) {
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/PrivacyApplication.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/PrivacyApplication.java
index 5ec7a77..820bea9 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/PrivacyApplication.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/PrivacyApplication.java
@@ -23,16 +23,20 @@
 import android.graphics.drawable.Drawable;
 import android.util.Log;
 
+import java.util.Objects;
+
 /**
  * Class to hold the data for the applications that are using the AppOps permissions.
  */
 public class PrivacyApplication {
     private static final String TAG = "PrivacyApplication";
 
+    private String mPackageName;
     private Drawable mIcon;
     private String mApplicationName;
 
     public PrivacyApplication(String packageName, Context context) {
+        mPackageName = packageName;
         try {
             CarUserManagerHelper carUserManagerHelper = new CarUserManagerHelper(context);
             ApplicationInfo app = context.getPackageManager()
@@ -59,4 +63,17 @@
     public String getApplicationName() {
         return mApplicationName;
     }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        PrivacyApplication that = (PrivacyApplication) o;
+        return mPackageName.equals(that.mPackageName);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mPackageName);
+    }
 }
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/PrivacyItem.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/PrivacyItem.java
index fca1373..d3e123e 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/PrivacyItem.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/PrivacyItem.java
@@ -16,6 +16,8 @@
 
 package com.android.systemui.statusbar.car.privacy;
 
+import java.util.Objects;
+
 /**
  * Class for holding the data of each privacy item displayed in {@link OngoingPrivacyDialog}
  */
@@ -43,4 +45,18 @@
     public PrivacyType getPrivacyType() {
         return mPrivacyType;
     }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+        PrivacyItem that = (PrivacyItem) o;
+        return mPrivacyType == that.mPrivacyType
+                && mPrivacyApplication.equals(that.mPrivacyApplication);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mPrivacyType, mPrivacyApplication);
+    }
 }
diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
index 81c5bcd..642dc82 100644
--- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
+++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java
@@ -35,7 +35,6 @@
 import android.net.http.SslError;
 import android.os.Bundle;
 import android.telephony.CarrierConfigManager;
-import android.telephony.Rlog;
 import android.telephony.SubscriptionManager;
 import android.text.TextUtils;
 import android.util.ArrayMap;
@@ -477,11 +476,11 @@
     }
 
     private static void logd(String s) {
-        Rlog.d(TAG, s);
+        Log.d(TAG, s);
     }
 
     private static void loge(String s) {
-        Rlog.d(TAG, s);
+        Log.d(TAG, s);
     }
 
 }
diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CustomConfigLoader.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CustomConfigLoader.java
index 02c61d7..46b1d5f 100644
--- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CustomConfigLoader.java
+++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CustomConfigLoader.java
@@ -19,7 +19,6 @@
 import android.content.Intent;
 import android.os.PersistableBundle;
 import android.telephony.CarrierConfigManager;
-import android.telephony.Rlog;
 import android.text.TextUtils;
 import android.util.Log;
 
@@ -27,7 +26,6 @@
 import com.android.internal.util.ArrayUtils;
 
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.List;
 
 /**
@@ -44,7 +42,7 @@
     private static final String INTER_GROUP_DELIMITER = "\\s*:\\s*";
 
     private static final String TAG = CustomConfigLoader.class.getSimpleName();
-    private static final boolean VDBG = Rlog.isLoggable(TAG, Log.VERBOSE);
+    private static final boolean VDBG = Log.isLoggable(TAG, Log.VERBOSE);
 
     /**
      * loads and parses the carrier config, return a list of carrier action for the given signal
@@ -70,7 +68,7 @@
         // return an empty list if no match found
         List<Integer> actionList = new ArrayList<>();
         if (carrierConfigManager == null) {
-            Rlog.e(TAG, "load carrier config failure with carrier config manager uninitialized");
+            Log.e(TAG, "load carrier config failure with carrier config manager uninitialized");
             return actionList;
         }
         PersistableBundle b = carrierConfigManager.getConfig();
@@ -101,8 +99,8 @@
                             .EXTRA_DEFAULT_NETWORK_AVAILABLE_KEY, false));
                     break;
                 default:
-                    Rlog.e(TAG, "load carrier config failure with un-configured key: " +
-                            intent.getAction());
+                    Log.e(TAG, "load carrier config failure with un-configured key: "
+                            + intent.getAction());
                     break;
             }
             if (!ArrayUtils.isEmpty(configs)) {
@@ -111,12 +109,12 @@
                     matchConfig(config, arg1, arg2, actionList);
                     if (!actionList.isEmpty()) {
                         // return the first match
-                        if (VDBG) Rlog.d(TAG, "found match action list: " + actionList.toString());
+                        if (VDBG) Log.d(TAG, "found match action list: " + actionList.toString());
                         return actionList;
                     }
                 }
             }
-            Rlog.d(TAG, "no matching entry for signal: " + intent.getAction() + "arg1: " + arg1
+            Log.d(TAG, "no matching entry for signal: " + intent.getAction() + "arg1: " + arg1
                     + "arg2: " + arg2);
         }
         return actionList;
@@ -166,7 +164,7 @@
                 try {
                     actionList.add(Integer.parseInt(idx));
                 } catch (NumberFormatException e) {
-                    Rlog.e(TAG, "NumberFormatException(string: " + idx + " config:" + config + "): "
+                    Log.e(TAG, "NumberFormatException(string: " + idx + " config:" + config + "): "
                             + e);
                 }
             }
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
index 1b27b52..48d34ae 100644
--- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
@@ -16,6 +16,7 @@
 
 package com.android.externalstorage;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.usage.StorageStatsManager;
 import android.content.ContentResolver;
@@ -298,6 +299,53 @@
         return projection != null ? projection : DEFAULT_ROOT_PROJECTION;
     }
 
+    /**
+     * Check that the directory is the root of storage or blocked file from tree.
+     *
+     * @param docId the docId of the directory to be checked
+     * @return true, should be blocked from tree. Otherwise, false.
+     */
+    @Override
+    protected boolean shouldBlockFromTree(@NonNull String docId) {
+        try {
+            final File dir = getFileForDocId(docId, true /* visible */).getCanonicalFile();
+            if (!dir.isDirectory()) {
+                return false;
+            }
+
+            final String path = dir.getAbsolutePath();
+
+            // Block Download folder from tree
+            if (MediaStore.Downloads.isDownloadDir(path)) {
+                return true;
+            }
+
+            final ArrayMap<String, RootInfo> roots = new ArrayMap<>();
+
+            synchronized (mRootsLock) {
+                roots.putAll(mRoots);
+            }
+
+            // block root of storage
+            for (int i = 0; i < roots.size(); i++) {
+                RootInfo rootInfo = roots.valueAt(i);
+                // skip home root
+                if (TextUtils.equals(rootInfo.rootId, ROOT_ID_HOME)) {
+                    continue;
+                }
+
+                // block the root of storage
+                if (TextUtils.equals(path, rootInfo.visiblePath.getAbsolutePath())) {
+                    return true;
+                }
+            }
+            return false;
+        } catch (IOException e) {
+            throw new IllegalArgumentException(
+                    "Failed to determine if " + docId + " should block from tree " + ": " + e);
+        }
+    }
+
     @Override
     protected String getDocIdForFile(File file) throws FileNotFoundException {
         return getDocIdForFileMaybeCreate(file, false);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index 5e2b7c8..17c621e 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -51,6 +51,7 @@
 import com.android.internal.util.XmlUtils;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.internal.widget.LockPatternView;
+import com.android.internal.widget.LockscreenCredential;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -1992,8 +1993,11 @@
                 try {
                     LockPatternUtils lpu = new LockPatternUtils(mContext);
                     List<LockPatternView.Cell> cellPattern =
-                            LockPatternUtils.stringToPattern(lockPattern);
-                    lpu.saveLockPattern(cellPattern, null, UserHandle.USER_SYSTEM);
+                            LockPatternUtils.byteArrayToPattern(lockPattern.getBytes());
+                    lpu.setLockCredential(
+                            LockscreenCredential.createPattern(cellPattern),
+                            LockscreenCredential.createNone(),
+                            UserHandle.USER_SYSTEM);
                 } catch (IllegalArgumentException e) {
                     // Don't want corrupted lock pattern to hang the reboot process
                 }
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
index 2ef0422..748f356 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java
@@ -115,4 +115,15 @@
             Log.e(TAG, "Failed to clean up screenshot of recents animation", e);
         }
     }
+
+    /**
+     * @see {{@link IRecentsAnimationController#setWillFinishToHome(boolean)}}.
+     */
+    public void setWillFinishToHome(boolean willFinishToHome) {
+        try {
+            mAnimationController.setWillFinishToHome(willFinishToHome);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Failed to set overview reached state", e);
+        }
+    }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java
index d45603f..ebac3d9 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java
@@ -33,10 +33,9 @@
 import com.android.internal.util.LatencyTracker;
 import com.android.internal.widget.LockPatternChecker;
 import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.LockscreenCredential;
 import com.android.systemui.R;
 
-import java.util.Arrays;
-
 /**
  * Base class for PIN and password unlock screens.
  */
@@ -132,19 +131,20 @@
     protected void verifyPasswordAndUnlock() {
         if (mDismissing) return; // already verified but haven't been dismissed; don't do it again.
 
-        final byte[] entry = getPasswordText();
+        final LockscreenCredential password =
+                LockscreenCredential.createPassword(getPasswordText());
         setPasswordEntryInputEnabled(false);
         if (mPendingLockCheck != null) {
             mPendingLockCheck.cancel(false);
         }
 
         final int userId = KeyguardUpdateMonitor.getCurrentUser();
-        if (entry.length <= MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT) {
+        if (password.size() <= MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT) {
             // to avoid accidental lockout, only count attempts that are long enough to be a
             // real password. This may require some tweaking.
             setPasswordEntryInputEnabled(true);
             onPasswordChecked(userId, false /* matched */, 0, false /* not valid - too short */);
-            Arrays.fill(entry, (byte) 0);
+            password.zeroize();
             return;
         }
 
@@ -152,9 +152,9 @@
             LatencyTracker.getInstance(mContext).onActionStart(ACTION_CHECK_CREDENTIAL);
             LatencyTracker.getInstance(mContext).onActionStart(ACTION_CHECK_CREDENTIAL_UNLOCKED);
         }
-        mPendingLockCheck = LockPatternChecker.checkPassword(
+        mPendingLockCheck = LockPatternChecker.checkCredential(
                 mLockPatternUtils,
-                entry,
+                password,
                 userId,
                 new LockPatternChecker.OnCheckCallback() {
 
@@ -166,7 +166,7 @@
                         }
                         onPasswordChecked(userId, true /* matched */, 0 /* timeoutMs */,
                                 true /* isValidPassword */);
-                        Arrays.fill(entry, (byte) 0);
+                        password.zeroize();
                     }
 
                     @Override
@@ -181,7 +181,7 @@
                             onPasswordChecked(userId, false /* matched */, timeoutMs,
                                     true /* isValidPassword */);
                         }
-                        Arrays.fill(entry, (byte) 0);
+                        password.zeroize();
                     }
 
                     @Override
@@ -192,7 +192,7 @@
                             LatencyTracker.getInstance(mContext).onActionEnd(
                                     ACTION_CHECK_CREDENTIAL_UNLOCKED);
                         }
-                        Arrays.fill(entry, (byte) 0);
+                        password.zeroize();
                     }
                 });
     }
@@ -223,7 +223,7 @@
     }
 
     protected abstract void resetPasswordText(boolean animate, boolean announce);
-    protected abstract byte[] getPasswordText();
+    protected abstract CharSequence getPasswordText();
     protected abstract void setPasswordEntryEnabled(boolean enabled);
     protected abstract void setPasswordEntryInputEnabled(boolean enabled);
 
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
index e3ac0f6..12c9fc9 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
@@ -243,8 +243,8 @@
     }
 
     @Override
-    protected byte[] getPasswordText() {
-        return charSequenceToByteArray(mPasswordEntry.getText());
+    protected CharSequence getPasswordText() {
+        return mPasswordEntry.getText();
     }
 
     @Override
@@ -379,18 +379,4 @@
         return getContext().getString(
                 com.android.internal.R.string.keyguard_accessibility_password_unlock);
     }
-
-    /*
-     * This method avoids creating a new string when getting a byte array from EditView#getText().
-     */
-    private static byte[] charSequenceToByteArray(CharSequence chars) {
-        if (chars == null) {
-            return null;
-        }
-        byte[] bytes = new byte[chars.length()];
-        for (int i = 0; i < chars.length(); i++) {
-            bytes[i] = (byte) chars.charAt(i);
-        }
-        return bytes;
-    }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
index 297052f..9eb168a 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
@@ -39,6 +39,7 @@
 import com.android.internal.widget.LockPatternChecker;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.internal.widget.LockPatternView;
+import com.android.internal.widget.LockscreenCredential;
 import com.android.settingslib.animation.AppearAnimationCreator;
 import com.android.settingslib.animation.AppearAnimationUtils;
 import com.android.settingslib.animation.DisappearAnimationUtils;
@@ -297,9 +298,9 @@
                 LatencyTracker.getInstance(mContext).onActionStart(ACTION_CHECK_CREDENTIAL);
                 LatencyTracker.getInstance(mContext).onActionStart(ACTION_CHECK_CREDENTIAL_UNLOCKED);
             }
-            mPendingLockCheck = LockPatternChecker.checkPattern(
+            mPendingLockCheck = LockPatternChecker.checkCredential(
                     mLockPatternUtils,
-                    pattern,
+                    LockscreenCredential.createPattern(pattern),
                     userId,
                     new LockPatternChecker.OnCheckCallback() {
 
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
index 274f739..8e9df55 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java
@@ -167,8 +167,8 @@
     }
 
     @Override
-    protected byte[] getPasswordText() {
-        return charSequenceToByteArray(mPasswordEntry.getText());
+    protected CharSequence getPasswordText() {
+        return mPasswordEntry.getText();
     }
 
     @Override
@@ -266,18 +266,4 @@
         return getContext().getString(
                 com.android.internal.R.string.keyguard_accessibility_pin_unlock);
     }
-
-    /*
-     * This method avoids creating a new string when getting a byte array from EditView#getText().
-     */
-    private static byte[] charSequenceToByteArray(CharSequence chars) {
-        if (chars == null) {
-            return null;
-        }
-        byte[] bytes = new byte[chars.length()];
-        for (int i = 0; i < chars.length(); i++) {
-            bytes[i] = (byte) chars.charAt(i);
-        }
-        return bytes;
-    }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
index af4e61b..5d35169 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
@@ -31,6 +31,7 @@
 import android.content.Context;
 import android.graphics.Color;
 import android.graphics.drawable.Drawable;
+import android.graphics.text.LineBreaker;
 import android.net.Uri;
 import android.os.Trace;
 import android.provider.Settings;
@@ -152,6 +153,7 @@
         mRowWithHeaderTextSize = mContext.getResources().getDimensionPixelSize(
                 R.dimen.header_row_font_size);
         mTitle.setOnClickListener(this);
+        mTitle.setBreakStrategy(LineBreaker.BREAK_STRATEGY_BALANCED);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index 3a7a7f7..ad20986 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -460,7 +460,7 @@
                         | WindowManager.LayoutParams.FLAG_SLIPPERY
                         | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
                 PixelFormat.TRANSLUCENT);
-        lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS
+        lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS
                 | WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
 
         if (!DEBUG_SCREENSHOT_ROUNDED_CORNERS) {
diff --git a/packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java b/packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java
index a8591bb..10009f5 100644
--- a/packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java
+++ b/packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java
@@ -203,7 +203,7 @@
             mWinParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                     | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
             mWinParams.format = PixelFormat.TRANSLUCENT;
-            mWinParams.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+            mWinParams.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
             mWinParams.setTitle(SizeCompatModeActivityController.class.getSimpleName()
                     + context.getDisplayId());
         }
diff --git a/packages/SystemUI/src/com/android/systemui/SysUIToast.java b/packages/SystemUI/src/com/android/systemui/SysUIToast.java
index 8bcf057..0f7f1be 100644
--- a/packages/SystemUI/src/com/android/systemui/SysUIToast.java
+++ b/packages/SystemUI/src/com/android/systemui/SysUIToast.java
@@ -31,7 +31,7 @@
     public static Toast makeText(Context context, CharSequence text, @Duration int duration) {
         Toast toast = Toast.makeText(context, text, duration);
         toast.getWindowParams().privateFlags |=
-                WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+                WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
         return toast;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistModule.java b/packages/SystemUI/src/com/android/systemui/assist/AssistModule.java
index 739eade..6f5a17d 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistModule.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistModule.java
@@ -21,6 +21,7 @@
 import android.os.HandlerThread;
 import android.os.SystemClock;
 
+import androidx.annotation.Nullable;
 import androidx.slice.Clock;
 
 import com.android.internal.app.AssistUtils;
@@ -68,6 +69,7 @@
     }
 
     @Provides
+    @Nullable
     static AssistHandleViewController provideAssistHandleViewController(
             NavigationBarController navigationBarController) {
         return navigationBarController.getAssistHandlerViewController();
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index a9359d4..f1abdb3 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -580,7 +580,7 @@
                 WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL,
                 WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
                 PixelFormat.TRANSLUCENT);
-        lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+        lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
         lp.setTitle("BiometricPrompt");
         lp.token = windowToken;
         return lp;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java
index 8df072e..bebaa4b 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java
@@ -26,7 +26,7 @@
 import android.widget.TextView;
 
 import com.android.internal.widget.LockPatternChecker;
-import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.LockscreenCredential;
 import com.android.systemui.R;
 
 /**
@@ -96,13 +96,16 @@
     }
 
     private void checkPasswordAndUnlock() {
-        final byte[] password = LockPatternUtils.charSequenceToByteArray(mPasswordField.getText());
-        if (password == null || password.length == 0) {
-            return;
-        }
+        try (LockscreenCredential password =  mCredentialType == Utils.CREDENTIAL_PIN
+                ? LockscreenCredential.createPinOrNone(mPasswordField.getText())
+                : LockscreenCredential.createPasswordOrNone(mPasswordField.getText())) {
+            if (password.isNone()) {
+                return;
+            }
 
-        mPendingLockCheck = LockPatternChecker.checkPassword(mLockPatternUtils,
-                password, mUserId, this::onCredentialChecked);
+            mPendingLockCheck = LockPatternChecker.checkCredential(mLockPatternUtils,
+                    password, mUserId, this::onCredentialChecked);
+        }
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPatternView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPatternView.java
index 6c36f82..14414a4 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPatternView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPatternView.java
@@ -22,6 +22,7 @@
 import com.android.internal.widget.LockPatternChecker;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.internal.widget.LockPatternView;
+import com.android.internal.widget.LockscreenCredential;
 import com.android.systemui.R;
 
 import java.util.List;
@@ -64,11 +65,13 @@
                 return;
             }
 
-            mPendingLockCheck = LockPatternChecker.checkPattern(
-                    mLockPatternUtils,
-                    pattern,
-                    mUserId,
-                    this::onPatternChecked);
+            try (LockscreenCredential credential = LockscreenCredential.createPattern(pattern)) {
+                mPendingLockCheck = LockPatternChecker.checkCredential(
+                        mLockPatternUtils,
+                        credential,
+                        mUserId,
+                        this::onPatternChecked);
+            }
         }
 
         private void onPatternChecked(boolean matched, int timeoutMs) {
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
index ca4ec6d..b069ba3 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java
@@ -75,7 +75,7 @@
                         handler, wakeLock, machine, dockManager, dozeLog),
                 createDozeUi(context, host, wakeLock, machine, handler, alarmManager, params,
                         dozeLog),
-                new DozeScreenState(wrappedService, handler, params, wakeLock),
+                new DozeScreenState(wrappedService, handler, host, params, wakeLock),
                 createDozeScreenBrightness(context, wrappedService, sensorManager, host, params,
                         handler),
                 new DozeWallpaperState(context, getBiometricUnlockController(dozeService)),
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
index 07dd2cd..1a6bd60 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
@@ -63,9 +63,10 @@
     void setDozeScreenBrightness(int value);
 
     /**
-     * Makes scrims black and changes animation durations.
+     * Fade out screen before switching off the display power mode.
+     * @param onDisplayOffCallback Executed when the display is black.
      */
-    default void prepareForGentleWakeUp() {}
+    void prepareForGentleSleep(Runnable onDisplayOffCallback);
 
     void onIgnoreTouchWhilePulsing(boolean ignore);
 
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
index 38ee2fe..95c42fc 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java
@@ -16,6 +16,12 @@
 
 package com.android.systemui.doze;
 
+import static com.android.systemui.doze.DozeMachine.State.DOZE;
+import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD;
+import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD_PAUSED;
+import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD_PAUSING;
+import static com.android.systemui.doze.DozeMachine.State.DOZE_PULSE_DONE;
+
 import android.os.Handler;
 import android.util.Log;
 import android.view.Display;
@@ -48,21 +54,24 @@
     private final Handler mHandler;
     private final Runnable mApplyPendingScreenState = this::applyPendingScreenState;
     private final DozeParameters mParameters;
+    private final DozeHost mDozeHost;
 
     private int mPendingScreenState = Display.STATE_UNKNOWN;
     private SettableWakeLock mWakeLock;
 
-    public DozeScreenState(DozeMachine.Service service, Handler handler,
+    public DozeScreenState(DozeMachine.Service service, Handler handler, DozeHost host,
             DozeParameters parameters, WakeLock wakeLock) {
         mDozeService = service;
         mHandler = handler;
         mParameters = parameters;
+        mDozeHost = host;
         mWakeLock = new SettableWakeLock(wakeLock, TAG);
     }
 
     @Override
     public void transitionTo(DozeMachine.State oldState, DozeMachine.State newState) {
         int screenState = newState.screenState(mParameters);
+        mDozeHost.prepareForGentleSleep(null);
 
         if (newState == DozeMachine.State.FINISH) {
             // Make sure not to apply the screen state after DozeService was destroyed.
@@ -79,12 +88,13 @@
             return;
         }
 
-        boolean messagePending = mHandler.hasCallbacks(mApplyPendingScreenState);
-        boolean pulseEnding = oldState  == DozeMachine.State.DOZE_PULSE_DONE
-                && newState == DozeMachine.State.DOZE_AOD;
-        boolean turningOn = (oldState == DozeMachine.State.DOZE_AOD_PAUSED
-                || oldState  == DozeMachine.State.DOZE) && newState == DozeMachine.State.DOZE_AOD;
-        boolean justInitialized = oldState == DozeMachine.State.INITIALIZED;
+        final boolean messagePending = mHandler.hasCallbacks(mApplyPendingScreenState);
+        final boolean pulseEnding = oldState  == DOZE_PULSE_DONE && newState == DOZE_AOD;
+        final boolean turningOn = (oldState == DOZE_AOD_PAUSED
+                || oldState  == DOZE) && newState == DOZE_AOD;
+        final boolean turningOff = (oldState == DOZE_AOD && newState == DOZE)
+                || (oldState == DOZE_AOD_PAUSING && newState == DOZE_AOD_PAUSED);
+        final boolean justInitialized = oldState == DozeMachine.State.INITIALIZED;
         if (messagePending || justInitialized || pulseEnding || turningOn) {
             // During initialization, we hide the navigation bar. That is however only applied after
             // a traversal; setting the screen state here is immediate however, so it can happen
@@ -93,7 +103,7 @@
             mPendingScreenState = screenState;
 
             // Delay screen state transitions even longer while animations are running.
-            boolean shouldDelayTransition = newState == DozeMachine.State.DOZE_AOD
+            boolean shouldDelayTransition = newState == DOZE_AOD
                     && mParameters.shouldControlScreenOff() && !turningOn;
 
             if (shouldDelayTransition) {
@@ -114,6 +124,8 @@
             } else if (DEBUG) {
                 Log.d(TAG, "Pending display state change to " + screenState);
             }
+        } else if (turningOff) {
+            mDozeHost.prepareForGentleSleep(() -> applyScreenState(screenState));
         } else {
             applyScreenState(screenState);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
index 2c0ccd21..f155783 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java
@@ -135,7 +135,6 @@
                 break;
             case DOZE:
             case DOZE_AOD_PAUSED:
-                mHost.prepareForGentleWakeUp();
                 unscheduleTimeTick();
                 break;
             case DOZE_REQUEST_PULSE:
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java
index 8224365..3f15966 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java
@@ -107,7 +107,7 @@
                             | LayoutParams.FLAG_NOT_FOCUSABLE,
                     PixelFormat.TRANSLUCENT);
             lp.setTitle("pip-dismiss-overlay");
-            lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+            lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
             lp.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL;
             mWindowManager.addView(mDismissView, lp);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
index ae83567..2e24403 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java
@@ -28,10 +28,12 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
+import android.provider.Settings;
 import android.service.quicksettings.Tile;
 import android.util.AttributeSet;
 import android.view.LayoutInflater;
 import android.view.View;
+import android.widget.FrameLayout;
 import android.widget.LinearLayout;
 
 import com.android.internal.logging.MetricsLogger;
@@ -49,6 +51,8 @@
 import com.android.systemui.qs.external.CustomTile;
 import com.android.systemui.settings.BrightnessController;
 import com.android.systemui.settings.ToggleSliderView;
+import com.android.systemui.shared.plugins.PluginManager;
+import com.android.systemui.statusbar.phone.NPVPluginManager;
 import com.android.systemui.statusbar.policy.BrightnessMirrorController;
 import com.android.systemui.statusbar.policy.BrightnessMirrorController.BrightnessMirrorListener;
 import com.android.systemui.tuner.TunerService;
@@ -98,6 +102,10 @@
     private BrightnessMirrorController mBrightnessMirrorController;
     private View mDivider;
 
+    private FrameLayout mPluginFrame;
+    private final PluginManager mPluginManager;
+    private NPVPluginManager mNPVPluginManager;
+
     public QSPanel(Context context) {
         this(context, null);
     }
@@ -106,9 +114,13 @@
         this(context, attrs, null);
     }
 
+    public QSPanel(Context context, AttributeSet attrs, DumpController dumpController) {
+        this(context, attrs, dumpController, null);
+    }
+
     @Inject
     public QSPanel(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs,
-            DumpController dumpController) {
+            DumpController dumpController, PluginManager pluginManager) {
         super(context, attrs);
         mContext = context;
 
@@ -136,6 +148,15 @@
         mBrightnessController = new BrightnessController(getContext(),
                 findViewById(R.id.brightness_slider));
         mDumpController = dumpController;
+        mPluginManager = pluginManager;
+        if (mPluginManager != null && Settings.System.getInt(
+                mContext.getContentResolver(), "npv_plugin_flag", 0) == 2) {
+            mPluginFrame = (FrameLayout) LayoutInflater.from(mContext).inflate(
+                    R.layout.status_bar_expanded_plugin_frame, this, false);
+            addView(mPluginFrame);
+            mNPVPluginManager = new NPVPluginManager(mPluginFrame, mPluginManager);
+        }
+
     }
 
     protected void addDivider() {
@@ -377,6 +398,7 @@
         if (mListening) {
             refreshAllTiles();
         }
+        if (mNPVPluginManager != null) mNPVPluginManager.setListening(listening);
     }
 
     public void setListening(boolean listening, boolean expanded) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java
index c1ce163..aa64449 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java
@@ -497,7 +497,7 @@
                 WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY,
                 flags,
                 PixelFormat.TRANSLUCENT);
-        lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+        lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
         lp.setTitle("RecentsOnboarding");
         lp.gravity = gravity;
         return lp;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
index 0f277ca..2d1c087 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
@@ -128,7 +128,7 @@
                         | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
                 PixelFormat.TRANSLUCENT);
         lp.token = new Binder();
-        lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+        lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
         lp.setTitle("ScreenPinningConfirmation");
         lp.gravity = Gravity.FILL;
         return lp;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java
index e09e9cd..5144a95 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java
@@ -233,11 +233,13 @@
     }
 
     /** @return {@link NavigationBarFragment} on the default display. */
+    @Nullable
     public NavigationBarFragment getDefaultNavigationBarFragment() {
         return mNavigationBars.get(DEFAULT_DISPLAY);
     }
 
     /** @return {@link AssistHandleViewController} (only on the default display). */
+    @Nullable
     public AssistHandleViewController getAssistHandlerViewController() {
         NavigationBarFragment navBar = getDefaultNavigationBarFragment();
         return navBar == null ? null : navBar.getAssistHandlerViewController();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DoubleTapHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DoubleTapHelper.java
index 0d62703..78ea5c0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DoubleTapHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DoubleTapHelper.java
@@ -100,6 +100,7 @@
                                     event.getY() - mActivationY);
                         }
                         if (withinDoubleTapSlop) {
+                            makeInactive();
                             if (!mDoubleTapListener.onDoubleTap()) {
                                 return false;
                             }
@@ -134,6 +135,7 @@
         if (mActivated) {
             mActivated = false;
             mActivationListener.onActiveChanged(false);
+            mView.removeCallbacks(mTapTimeoutRunnable);
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
index ca7c227..442c089 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java
@@ -276,7 +276,7 @@
                             | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
                     PixelFormat.TRANSLUCENT);
             mEdgePanelLp.privateFlags |=
-                    WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+                    WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
             mEdgePanelLp.setTitle(TAG + mDisplayId);
             mEdgePanelLp.accessibilityTitle = mContext.getString(R.string.nav_bar_edge_panel);
             mEdgePanelLp.windowAnimations = 0;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FloatingRotationButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FloatingRotationButton.java
index deb314b..a784984 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FloatingRotationButton.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FloatingRotationButton.java
@@ -82,7 +82,7 @@
         final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(mDiameter, mDiameter,
                 mMargin, mMargin, WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL, flags,
                 PixelFormat.TRANSLUCENT);
-        lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+        lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
         lp.setTitle("FloatingRotationButton");
         switch (mWindowManager.getDefaultDisplay().getRotation()) {
             case Surface.ROTATION_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 177294b..9ab635c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -43,6 +43,7 @@
 import android.os.PowerManager;
 import android.os.SystemClock;
 import android.provider.DeviceConfig;
+import android.provider.Settings;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.MathUtils;
@@ -541,7 +542,10 @@
         mQsNavbarScrim = findViewById(R.id.qs_navbar_scrim);
         mLastOrientation = getResources().getConfiguration().orientation;
         mPluginFrame = findViewById(R.id.plugin_frame);
-        mNPVPluginManager = new NPVPluginManager(mPluginFrame, mPluginManager);
+        if (Settings.System.getInt(
+                mContext.getContentResolver(), "npv_plugin_flag", 0) == 1) {
+            mNPVPluginManager = new NPVPluginManager(mPluginFrame, mPluginManager);
+        }
 
 
         initBottomArea();
@@ -778,7 +782,7 @@
             mPluginFrame.setLayoutParams(lp);
         }
 
-        mNPVPluginManager.replaceFrameLayout(mPluginFrame);
+        if (mNPVPluginManager != null) mNPVPluginManager.replaceFrameLayout(mPluginFrame);
     }
 
     private void initBottomArea() {
@@ -808,8 +812,10 @@
         int oldMaxHeight = mQsMaxExpansionHeight;
         if (mQs != null) {
             mQsMinExpansionHeight = mKeyguardShowing ? 0 : mQs.getQsMinExpansionHeight();
-            mNPVPluginManager.setYOffset(mQsMinExpansionHeight);
-            mQsMinExpansionHeight += mNPVPluginManager.getHeight();
+            if (mNPVPluginManager != null) {
+                mNPVPluginManager.setYOffset(mQsMinExpansionHeight);
+                mQsMinExpansionHeight += mNPVPluginManager.getHeight();
+            }
             mQsMaxExpansionHeight = mQs.getDesiredHeight();
             mNotificationStackScroller.setMaxTopPadding(
                     mQsMaxExpansionHeight + mQsNotificationTopPadding);
@@ -1915,9 +1921,11 @@
                 mBarState != StatusBarState.KEYGUARD && (!mQsExpanded
                         || mQsExpansionFromOverscroll));
         updateEmptyShadeView();
-        mNPVPluginManager.changeVisibility((mBarState != StatusBarState.KEYGUARD)
-                ? View.VISIBLE
-                : View.INVISIBLE);
+        if (mNPVPluginManager != null) {
+            mNPVPluginManager.changeVisibility((mBarState != StatusBarState.KEYGUARD)
+                    ? View.VISIBLE
+                    : View.INVISIBLE);
+        }
         mQsNavbarScrim.setVisibility(mBarState == StatusBarState.SHADE && mQsExpanded
                 && !mStackScrollerOverscrolling && mQsScrimEnabled
                 ? View.VISIBLE
@@ -1975,7 +1983,9 @@
         float qsExpansionFraction = getQsExpansionFraction();
         mQs.setQsExpansion(qsExpansionFraction, getHeaderTranslation());
         int heightDiff = mQs.getDesiredHeight() - mQs.getQsMinExpansionHeight();
-        mNPVPluginManager.setExpansion(qsExpansionFraction, getHeaderTranslation(), heightDiff);
+        if (mNPVPluginManager != null) {
+            mNPVPluginManager.setExpansion(qsExpansionFraction, getHeaderTranslation(), heightDiff);
+        }
         mNotificationStackScroller.setQsExpansionFraction(qsExpansionFraction);
     }
 
@@ -2396,7 +2406,7 @@
                 appearAmount = mNotificationStackScroller.calculateAppearFractionBypass();
             }
             startHeight = -mQs.getQsMinExpansionHeight();
-            startHeight -= mNPVPluginManager.getHeight();
+            if (mNPVPluginManager != null) startHeight -= mNPVPluginManager.getHeight();
         }
         float translation = MathUtils.lerp(startHeight, 0,
                 Math.min(1.0f, appearAmount))
@@ -2540,7 +2550,7 @@
         mKeyguardStatusBar.setListening(listening);
         if (mQs == null) return;
         mQs.setListening(listening);
-        mNPVPluginManager.setListening(listening);
+        if (mNPVPluginManager != null) mNPVPluginManager.setListening(listening);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 5ba19cc..e7d896c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -258,7 +258,7 @@
 
         final ScrimState oldState = mState;
         mState = state;
-        Trace.traceCounter(Trace.TRACE_TAG_APP, "scrim_state", mState.getIndex());
+        Trace.traceCounter(Trace.TRACE_TAG_APP, "scrim_state", mState.ordinal());
 
         if (mCallback != null) {
             mCallback.onCancelled();
@@ -519,22 +519,6 @@
     }
 
     /**
-     * Set front scrim to black, cancelling animations, in order to prepare to fade them
-     * away once the display turns on.
-     */
-    public void prepareForGentleWakeUp() {
-        if (mState == ScrimState.AOD && mDozeParameters.getAlwaysOn()) {
-            mInFrontAlpha = 1f;
-            mInFrontTint = Color.BLACK;
-            mBehindTint = Color.BLACK;
-            mAnimateChange = false;
-            updateScrims();
-            mAnimateChange = true;
-            mAnimationDuration = ANIMATION_DURATION_LONG;
-        }
-    }
-
-    /**
      * If the lock screen sensor is active.
      */
     public void setWakeLockScreenSensorActive(boolean active) {
@@ -595,6 +579,8 @@
         setScrimAlpha(mScrimInFront, mInFrontAlpha);
         setScrimAlpha(mScrimBehind, mBehindAlpha);
         setScrimAlpha(mScrimForBubble, mBubbleAlpha);
+        // The animation could have all already finished, let's call onFinished just in case
+        onFinished();
         dispatchScrimsVisible();
     }
 
@@ -693,9 +679,9 @@
 
             @Override
             public void onAnimationEnd(Animator animation) {
+                scrim.setTag(TAG_KEY_ANIM, null);
                 onFinished(lastCallback);
 
-                scrim.setTag(TAG_KEY_ANIM, null);
                 dispatchScrimsVisible();
 
                 if (!mDeferFinishedListener && mOnAnimationFinished != null) {
@@ -759,9 +745,9 @@
     }
 
     private void onFinished(Callback callback) {
-        if (!hasReachedFinalState(mScrimBehind)
-            || !hasReachedFinalState(mScrimInFront)
-            || !hasReachedFinalState(mScrimForBubble)) {
+        if (isAnimating(mScrimBehind)
+            || isAnimating(mScrimInFront)
+            || isAnimating(mScrimForBubble)) {
             if (callback != null && callback != mCallback) {
                 // Since we only notify the callback that we're finished once everything has
                 // finished, we need to make sure that any changing callbacks are also invoked
@@ -794,11 +780,6 @@
         }
     }
 
-    private boolean hasReachedFinalState(ScrimView scrim) {
-        return scrim.getViewAlpha() == getCurrentScrimAlpha(scrim)
-                && scrim.getTint() == getCurrentScrimTint(scrim);
-    }
-
     private boolean isAnimating(View scrim) {
         return scrim.getTag(TAG_KEY_ANIM) != null;
     }
@@ -854,10 +835,7 @@
             } else {
                 // update the alpha directly
                 updateScrimColor(scrim, alpha, getCurrentScrimTint(scrim));
-                onFinished();
             }
-        } else {
-            onFinished();
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
index 7463c7c..e0597159 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java
@@ -30,12 +30,30 @@
     /**
      * Initial state.
      */
-    UNINITIALIZED(-1),
+    UNINITIALIZED,
+
+    /**
+     * When turned off by sensors (prox, presence.)
+     */
+    OFF {
+        @Override
+        public void prepare(ScrimState previousState) {
+            mFrontTint = Color.BLACK;
+            mBehindTint = previousState.mBehindTint;
+            mBubbleTint = previousState.mBubbleTint;
+
+            mFrontAlpha = 1f;
+            mBehindAlpha = previousState.mBehindAlpha;
+            mBubbleAlpha = previousState.mBubbleAlpha;
+
+            mAnimationDuration = ScrimController.ANIMATION_DURATION_LONG;
+        }
+    },
 
     /**
      * On the lock screen.
      */
-    KEYGUARD(0) {
+    KEYGUARD {
         @Override
         public void prepare(ScrimState previousState) {
             mBlankScreen = false;
@@ -65,7 +83,7 @@
     /**
      * Showing password challenge on the keyguard.
      */
-    BOUNCER(1) {
+    BOUNCER {
         @Override
         public void prepare(ScrimState previousState) {
             mBehindAlpha = ScrimController.GRADIENT_SCRIM_ALPHA_BUSY;
@@ -77,7 +95,7 @@
     /**
      * Showing password challenge on top of a FLAG_SHOW_WHEN_LOCKED activity.
      */
-    BOUNCER_SCRIMMED(2) {
+    BOUNCER_SCRIMMED {
         @Override
         public void prepare(ScrimState previousState) {
             mBehindAlpha = 0;
@@ -89,7 +107,7 @@
     /**
      * Changing screen brightness from quick settings.
      */
-    BRIGHTNESS_MIRROR(3) {
+    BRIGHTNESS_MIRROR {
         @Override
         public void prepare(ScrimState previousState) {
             mBehindAlpha = 0;
@@ -101,7 +119,7 @@
     /**
      * Always on display or screen off.
      */
-    AOD(4) {
+    AOD {
         @Override
         public void prepare(ScrimState previousState) {
             final boolean alwaysOnEnabled = mDozeParameters.getAlwaysOn();
@@ -136,7 +154,7 @@
     /**
      * When phone wakes up because you received a notification.
      */
-    PULSING(5) {
+    PULSING {
         @Override
         public void prepare(ScrimState previousState) {
             mFrontAlpha = mAodFrontScrimAlpha;
@@ -164,7 +182,7 @@
     /**
      * Unlocked on top of an app (launcher or any other activity.)
      */
-    UNLOCKED(6) {
+    UNLOCKED {
         @Override
         public void prepare(ScrimState previousState) {
             // State that UI will sync to.
@@ -201,7 +219,7 @@
     /**
      * Unlocked with a bubble expanded.
      */
-    BUBBLE_EXPANDED(7) {
+    BUBBLE_EXPANDED {
         @Override
         public void prepare(ScrimState previousState) {
             mFrontTint = Color.TRANSPARENT;
@@ -237,17 +255,12 @@
     DozeParameters mDozeParameters;
     boolean mDisplayRequiresBlanking;
     boolean mWallpaperSupportsAmbientMode;
-    int mIndex;
     boolean mHasBackdrop;
     boolean mLaunchingAffordanceWithPreview;
     boolean mWakeLockScreenSensorActive;
     boolean mKeyguardFadingAway;
     long mKeyguardFadingAwayDuration;
 
-    ScrimState(int index) {
-        mIndex = index;
-    }
-
     public void init(ScrimView scrimInFront, ScrimView scrimBehind, ScrimView scrimForBubble,
             DozeParameters dozeParameters) {
         mScrimInFront = scrimInFront;
@@ -262,10 +275,6 @@
     public void prepare(ScrimState previousState) {
     }
 
-    public int getIndex() {
-        return mIndex;
-    }
-
     public float getFrontAlpha() {
         return mFrontAlpha;
     }
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 9093687..d1fe46e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -4018,6 +4018,13 @@
         } else if (isPulsing()) {
             mScrimController.transitionTo(ScrimState.PULSING,
                     mDozeScrimController.getScrimCallback());
+        } else if (mDozeServiceHost.hasPendingScreenOffCallback()) {
+            mScrimController.transitionTo(ScrimState.OFF, new ScrimController.Callback() {
+                @Override
+                public void onFinished() {
+                    mDozeServiceHost.executePendingScreenOffCallback();
+                }
+            });
         } else if (mDozing && !unlocking) {
             mScrimController.transitionTo(ScrimState.AOD);
         } else if (mIsKeyguard && !unlocking) {
@@ -4044,6 +4051,7 @@
         private boolean mAnimateWakeup;
         private boolean mAnimateScreenOff;
         private boolean mIgnoreTouchWhilePulsing;
+        private Runnable mPendingScreenOffCallback;
         @VisibleForTesting
         boolean mWakeLockScreenPerformsAuth = SystemProperties.getBoolean(
                 "persist.sysui.wake_performs_auth", true);
@@ -4266,8 +4274,33 @@
         }
 
         @Override
-        public void prepareForGentleWakeUp() {
-            mScrimController.prepareForGentleWakeUp();
+        public void prepareForGentleSleep(Runnable onDisplayOffCallback) {
+            if (onDisplayOffCallback != null) {
+                Log.w(TAG, "Overlapping onDisplayOffCallback. Ignoring previous one.");
+            }
+            mPendingScreenOffCallback = onDisplayOffCallback;
+            updateScrimController();
+        }
+
+        /**
+         * When the dozing host is waiting for scrims to fade out to change the display state.
+         */
+        boolean hasPendingScreenOffCallback() {
+            return mPendingScreenOffCallback != null;
+        }
+
+        /**
+         * Executes an nullifies the pending display state callback.
+         *
+         * @see #hasPendingScreenOffCallback()
+         * @see #prepareForGentleSleep(Runnable)
+         */
+        void executePendingScreenOffCallback() {
+            if (mPendingScreenOffCallback == null) {
+                return;
+            }
+            mPendingScreenOffCallback.run();
+            mPendingScreenOffCallback = null;
         }
 
         private void dispatchTap(View view, float x, float y) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
index ce929b7..44be6bc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
@@ -93,10 +93,10 @@
     public static void setShowForAllUsers(Dialog dialog, boolean show) {
         if (show) {
             dialog.getWindow().getAttributes().privateFlags |=
-                    WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+                    WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
         } else {
             dialog.getWindow().getAttributes().privateFlags &=
-                    ~WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+                    ~WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
index 43795dc..cca9479 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -23,12 +23,16 @@
 import android.app.Notification;
 import android.app.PendingIntent;
 import android.app.RemoteInput;
+import android.content.ClipDescription;
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.PackageManager;
 import android.content.pm.ShortcutManager;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
+import android.net.Uri;
 import android.os.Bundle;
+import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.UserHandle;
 import android.text.Editable;
@@ -53,8 +57,13 @@
 import android.widget.ProgressBar;
 import android.widget.TextView;
 
+import androidx.core.view.inputmethod.EditorInfoCompat;
+import androidx.core.view.inputmethod.InputConnectionCompat;
+import androidx.core.view.inputmethod.InputContentInfoCompat;
+
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto;
+import com.android.internal.statusbar.IStatusBarService;
 import com.android.systemui.Dependency;
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
@@ -65,6 +74,7 @@
 import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
 import com.android.systemui.statusbar.phone.LightBarController;
 
+import java.util.HashMap;
 import java.util.function.Consumer;
 
 /**
@@ -88,6 +98,8 @@
     private RemoteInputController mController;
     private RemoteInputQuickSettingsDisabler mRemoteInputQuickSettingsDisabler;
 
+    private IStatusBarService mStatusBarManagerService;
+
     private NotificationEntry mEntry;
 
     private boolean mRemoved;
@@ -103,6 +115,8 @@
     public RemoteInputView(Context context, AttributeSet attrs) {
         super(context, attrs);
         mRemoteInputQuickSettingsDisabler = Dependency.get(RemoteInputQuickSettingsDisabler.class);
+        mStatusBarManagerService = IStatusBarService.Stub.asInterface(
+                ServiceManager.getService(Context.STATUS_BAR_SERVICE));
     }
 
     @Override
@@ -128,7 +142,7 @@
 
                 if (isSoftImeEvent || isKeyboardEnterKey) {
                     if (mEditText.length() > 0) {
-                        sendRemoteInput();
+                        sendRemoteInput(prepareRemoteInputFromText());
                     }
                     // Consume action to prevent IME from closing.
                     return true;
@@ -141,7 +155,7 @@
         mEditText.mRemoteInputView = this;
     }
 
-    private void sendRemoteInput() {
+    protected Intent prepareRemoteInputFromText() {
         Bundle results = new Bundle();
         results.putString(mRemoteInput.getResultKey(), mEditText.getText().toString());
         Intent fillInIntent = new Intent().addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
@@ -153,6 +167,25 @@
             RemoteInput.setResultsSource(fillInIntent, RemoteInput.SOURCE_CHOICE);
         }
 
+        return fillInIntent;
+    }
+
+    protected Intent prepareRemoteInputFromData(String contentType, Uri data) {
+        HashMap<String, Uri> results = new HashMap<>();
+        results.put(contentType, data);
+        try {
+            mStatusBarManagerService.grantInlineReplyUriPermission(
+                    mEntry.notification.getKey(), data);
+        } catch (Exception e) {
+            Log.e(TAG, "Failed to grant URI permissions:" + e.getMessage(), e);
+        }
+        Intent fillInIntent = new Intent().addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+        RemoteInput.addDataResultToIntent(mRemoteInput, fillInIntent, results);
+
+        return fillInIntent;
+    }
+
+    private void sendRemoteInput(Intent intent) {
         mEditText.setEnabled(false);
         mSendButton.setVisibility(INVISIBLE);
         mProgressBar.setVisibility(VISIBLE);
@@ -176,7 +209,7 @@
         MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_REMOTE_INPUT_SEND,
                 mEntry.notification.getPackageName());
         try {
-            mPendingIntent.send(mContext, 0, fillInIntent);
+            mPendingIntent.send(mContext, 0, intent);
         } catch (PendingIntent.CanceledException e) {
             Log.i(TAG, "Unable to send remote input result", e);
             MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_REMOTE_INPUT_FAIL,
@@ -195,7 +228,9 @@
                 LayoutInflater.from(context).inflate(R.layout.remote_input, root, false);
         v.mController = controller;
         v.mEntry = entry;
-        v.mEditText.setTextOperationUser(computeTextOperationUser(entry.notification.getUser()));
+        UserHandle user = computeTextOperationUser(entry.notification.getUser());
+        v.mEditText.mUser = user;
+        v.mEditText.setTextOperationUser(user);
         v.setTag(VIEW_TAG);
 
         return v;
@@ -204,7 +239,7 @@
     @Override
     public void onClick(View v) {
         if (v == mSendButton) {
-            sendRemoteInput();
+            sendRemoteInput(prepareRemoteInputFromText());
         }
     }
 
@@ -518,6 +553,7 @@
         private RemoteInputView mRemoteInputView;
         boolean mShowImeOnInputConnection;
         private LightBarController mLightBarController;
+        UserHandle mUser;
 
         public RemoteEditText(Context context, AttributeSet attrs) {
             super(context, attrs);
@@ -617,11 +653,47 @@
 
         @Override
         public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
+            String[] allowedDataTypes = mRemoteInputView.mRemoteInput.getAllowedDataTypes()
+                    .toArray(new String[0]);
+            EditorInfoCompat.setContentMimeTypes(outAttrs, allowedDataTypes);
             final InputConnection inputConnection = super.onCreateInputConnection(outAttrs);
 
-            if (mShowImeOnInputConnection && inputConnection != null) {
+            final InputConnectionCompat.OnCommitContentListener callback =
+                    new InputConnectionCompat.OnCommitContentListener() {
+                        @Override
+                        public boolean onCommitContent(
+                                InputContentInfoCompat inputContentInfoCompat, int i,
+                                Bundle bundle) {
+                            Uri contentUri = inputContentInfoCompat.getContentUri();
+                            ClipDescription description = inputContentInfoCompat.getDescription();
+                            String mimeType = null;
+                            if (description != null && description.getMimeTypeCount() > 0) {
+                                mimeType = description.getMimeType(0);
+                            }
+                            if (mimeType != null) {
+                                Intent dataIntent = mRemoteInputView.prepareRemoteInputFromData(
+                                        mimeType, contentUri);
+                                mRemoteInputView.sendRemoteInput(dataIntent);
+                            }
+                            return true;
+                        }
+                    };
+
+            InputConnection ic = InputConnectionCompat.createWrapper(
+                    inputConnection, outAttrs, callback);
+
+            Context userContext = null;
+            try {
+                userContext = mContext.createPackageContextAsUser(
+                        mContext.getPackageName(), 0, mUser);
+            } catch (PackageManager.NameNotFoundException e) {
+                Log.e(TAG, "Unable to create user context:" + e.getMessage(), e);
+            }
+
+            if (mShowImeOnInputConnection && ic != null) {
+                Context targetContext = userContext != null ? userContext : getContext();
                 final InputMethodManager imm =
-                        getContext().getSystemService(InputMethodManager.class);
+                        targetContext.getSystemService(InputMethodManager.class);
                 if (imm != null) {
                     // onCreateInputConnection is called by InputMethodManager in the middle of
                     // setting up the connection to the IME; wait with requesting the IME until that
@@ -636,7 +708,7 @@
                 }
             }
 
-            return inputConnection;
+            return ic;
         }
 
         @Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java
index 1ce0172..98ec4594 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java
@@ -20,13 +20,11 @@
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
-import android.app.Instrumentation;
 import android.hardware.display.AmbientDisplayConfiguration;
 import android.os.Handler;
 import android.os.Looper;
@@ -34,7 +32,6 @@
 import android.testing.TestableLooper;
 import android.testing.TestableLooper.RunWithLooper;
 
-import androidx.test.InstrumentationRegistry;
 import androidx.test.filters.SmallTest;
 
 import com.android.systemui.SysuiTestCase;
@@ -45,23 +42,24 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 @RunWithLooper
 public class DozeDockHandlerTest extends SysuiTestCase {
-    private DozeDockHandler mDockHandler;
+    @Mock
     private DozeMachine mMachine;
-    private DozeHostFake mHost;
+    @Mock
+    private DozeHost mHost;
     private AmbientDisplayConfiguration mConfig;
-    private Instrumentation mInstrumentation;
     private DockManagerFake mDockManagerFake;
+    private DozeDockHandler mDockHandler;
 
     @Before
     public void setUp() throws Exception {
-        mInstrumentation = InstrumentationRegistry.getInstrumentation();
-        mMachine = mock(DozeMachine.class);
-        mHost = spy(new DozeHostFake());
+        MockitoAnnotations.initMocks(this);
         mConfig = DozeConfigurationUtil.createMockConfig();
         doReturn(false).when(mConfig).alwaysOnEnabled(anyInt());
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeHostFake.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeHostFake.java
deleted file mode 100644
index abfa755..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeHostFake.java
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.doze;
-
-import android.annotation.NonNull;
-
-/**
- * A rudimentary fake for DozeHost.
- */
-class DozeHostFake implements DozeHost {
-    Callback callback;
-    boolean pulseExtended;
-    boolean animateWakeup;
-    boolean animateScreenOff;
-    boolean dozing;
-    float doubleTapX;
-    float doubleTapY;
-    float aodDimmingScrimOpacity;
-
-    @Override
-    public void addCallback(@NonNull Callback callback) {
-        this.callback = callback;
-    }
-
-    @Override
-    public void removeCallback(@NonNull Callback callback) {
-        this.callback = null;
-    }
-
-    @Override
-    public void startDozing() {
-        dozing = true;
-    }
-
-    @Override
-    public void pulseWhileDozing(@NonNull PulseCallback callback, int reason) {
-        throw new RuntimeException("not implemented");
-    }
-
-    @Override
-    public void stopDozing() {
-        dozing = false;
-    }
-
-    @Override
-    public void dozeTimeTick() {
-        // Nothing to do in here. Real host would just update the UI.
-    }
-
-    @Override
-    public boolean isPowerSaveActive() {
-        return false;
-    }
-
-    @Override
-    public boolean isPulsingBlocked() {
-        return false;
-    }
-
-    @Override
-    public boolean isProvisioned() {
-        return false;
-    }
-
-    @Override
-    public boolean isBlockingDoze() {
-        return false;
-    }
-
-    @Override
-    public void onIgnoreTouchWhilePulsing(boolean ignore) {
-    }
-
-    @Override
-    public void extendPulse(int reason) {
-        pulseExtended = true;
-    }
-
-    @Override
-    public void stopPulsing() {}
-
-    @Override
-    public void setAnimateWakeup(boolean animateWakeup) {
-        this.animateWakeup = animateWakeup;
-    }
-
-    @Override
-    public void setAnimateScreenOff(boolean animateScreenOff) {
-        this.animateScreenOff = animateScreenOff;
-    }
-
-    @Override
-    public void onSlpiTap(float x, float y) {
-        doubleTapX = y;
-        doubleTapY = y;
-    }
-
-    @Override
-    public void setDozeScreenBrightness(int value) {
-    }
-
-    @Override
-    public void setAodDimmingScrim(float scrimOpacity) {
-        aodDimmingScrimOpacity = scrimOpacity;
-    }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
index aa62e9a..316b891 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java
@@ -30,6 +30,11 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
 
 import android.content.Intent;
 import android.os.PowerManager;
@@ -45,6 +50,8 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
 
 @RunWith(AndroidJUnit4.class)
 @SmallTest
@@ -55,22 +62,27 @@
     static final int[] SENSOR_TO_OPACITY = new int[]{-1, 10, 0, 0, 0};
 
     DozeServiceFake mServiceFake;
-    DozeScreenBrightness mScreen;
     FakeSensorManager.FakeGenericSensor mSensor;
     FakeSensorManager mSensorManager;
-    DozeHostFake mHostFake;
+    @Mock
+    DozeHost mDozeHost;
+    DozeScreenBrightness mScreen;
 
     @Before
     public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
         Settings.System.putIntForUser(mContext.getContentResolver(),
                 Settings.System.SCREEN_BRIGHTNESS, DEFAULT_BRIGHTNESS,
                 UserHandle.USER_CURRENT);
+        doAnswer(invocation -> {
+            ((Runnable) invocation.getArgument(0)).run();
+            return null;
+        }).when(mDozeHost).prepareForGentleSleep(any());
         mServiceFake = new DozeServiceFake();
-        mHostFake = new DozeHostFake();
         mSensorManager = new FakeSensorManager(mContext);
         mSensor = mSensorManager.getFakeLightSensor();
         mScreen = new DozeScreenBrightness(mContext, mServiceFake, mSensorManager,
-                mSensor.getSensor(), mHostFake, null /* handler */,
+                mSensor.getSensor(), mDozeHost, null /* handler */,
                 DEFAULT_BRIGHTNESS, SENSOR_TO_BRIGHTNESS, SENSOR_TO_OPACITY,
                 true /* debuggable */);
     }
@@ -173,7 +185,7 @@
     @Test
     public void testNullSensor() throws Exception {
         mScreen = new DozeScreenBrightness(mContext, mServiceFake, mSensorManager,
-                null /* sensor */, mHostFake, null /* handler */,
+                null /* sensor */, mDozeHost, null /* handler */,
                 DEFAULT_BRIGHTNESS, SENSOR_TO_BRIGHTNESS, SENSOR_TO_OPACITY,
                 true /* debuggable */);
 
@@ -203,26 +215,7 @@
         mSensor.sendSensorEvent(0);
 
         assertEquals(1, mServiceFake.screenBrightness);
-        assertEquals(10/255f, mHostFake.aodDimmingScrimOpacity, 0.001f /* delta */);
-    }
-
-    @Test
-    public void pausingAod_softBlanks() throws Exception {
-        mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
-        mScreen.transitionTo(INITIALIZED, DOZE_AOD);
-
-        mSensor.sendSensorEvent(2);
-
-        mScreen.transitionTo(DOZE_AOD, DOZE_AOD_PAUSING);
-        mScreen.transitionTo(DOZE_AOD_PAUSING, DOZE_AOD_PAUSED);
-
-        assertEquals(1f, mHostFake.aodDimmingScrimOpacity, 0.001f /* delta */);
-
-        mSensor.sendSensorEvent(0);
-        assertEquals(1f, mHostFake.aodDimmingScrimOpacity, 0.001f /* delta */);
-
-        mScreen.transitionTo(DOZE_AOD_PAUSED, DOZE_AOD);
-        assertEquals(1f, mHostFake.aodDimmingScrimOpacity, 0.001f /* delta */);
+        verify(mDozeHost).setAodDimmingScrim(eq(10f / 255f));
     }
 
     @Test
@@ -232,8 +225,9 @@
         mScreen.transitionTo(DOZE_AOD, DOZE_AOD_PAUSING);
         mScreen.transitionTo(DOZE_AOD_PAUSING, DOZE_AOD_PAUSED);
 
+        reset(mDozeHost);
         mSensor.sendSensorEvent(1);
-        assertEquals(1f, mHostFake.aodDimmingScrimOpacity, 0.001f /* delta */);
+        verify(mDozeHost).setAodDimmingScrim(eq(1f));
     }
 
     @Test
@@ -241,11 +235,12 @@
         mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
         mScreen.transitionTo(INITIALIZED, DOZE_AOD);
         mScreen.transitionTo(DOZE_AOD, DOZE);
-        assertEquals(1f, mHostFake.aodDimmingScrimOpacity, 0.001f /* delta */);
+        verify(mDozeHost).setAodDimmingScrim(eq(1f));
 
+        reset(mDozeHost);
         mScreen.transitionTo(DOZE, DOZE_AOD);
         mSensor.sendSensorEvent(2);
-        assertEquals(0f, mHostFake.aodDimmingScrimOpacity, 0.001f /* delta */);
+        verify(mDozeHost).setAodDimmingScrim(eq(0f));
     }
 
     @Test
@@ -260,11 +255,10 @@
 
         mSensor.sendSensorEvent(0);
 
+        reset(mDozeHost);
         mScreen.transitionTo(DOZE_AOD_PAUSED, DOZE_AOD);
-
         mSensor.sendSensorEvent(2);
-
-        assertEquals(0f, mHostFake.aodDimmingScrimOpacity, 0.001f /* delta */);
+        verify(mDozeHost).setAodDimmingScrim(eq(0f));
     }
 
     @Test
@@ -273,11 +267,11 @@
         mScreen.transitionTo(INITIALIZED, DOZE_AOD);
 
         mSensor.sendSensorEvent(2);
-
         mScreen.transitionTo(DOZE_AOD, DOZE_AOD_PAUSING);
         mScreen.transitionTo(DOZE_AOD_PAUSING, DOZE_AOD_PAUSED);
-        mScreen.transitionTo(DOZE_AOD_PAUSED, DOZE_AOD);
 
-        assertEquals(0f, mHostFake.aodDimmingScrimOpacity, 0.001f /* delta */);
+        reset(mDozeHost);
+        mScreen.transitionTo(DOZE_AOD_PAUSED, DOZE_AOD);
+        verify(mDozeHost).setAodDimmingScrim(eq(0f));
     }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java
index bfd448a..b92f173 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java
@@ -18,6 +18,8 @@
 
 import static com.android.systemui.doze.DozeMachine.State.DOZE;
 import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD;
+import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD_PAUSED;
+import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD_PAUSING;
 import static com.android.systemui.doze.DozeMachine.State.DOZE_PULSING;
 import static com.android.systemui.doze.DozeMachine.State.DOZE_REQUEST_PULSE;
 import static com.android.systemui.doze.DozeMachine.State.FINISH;
@@ -30,6 +32,9 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.os.Looper;
@@ -46,6 +51,7 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
@@ -53,12 +59,14 @@
 @SmallTest
 public class DozeScreenStateTest extends SysuiTestCase {
 
-    DozeServiceFake mServiceFake;
-    DozeScreenState mScreen;
-    FakeHandler mHandlerFake;
+    private DozeServiceFake mServiceFake;
+    private FakeHandler mHandlerFake;
     @Mock
-    DozeParameters mDozeParameters;
-    WakeLockFake mWakeLock;
+    private DozeHost mDozeHost;
+    @Mock
+    private DozeParameters mDozeParameters;
+    private WakeLockFake mWakeLock;
+    private DozeScreenState mScreen;
 
     @Before
     public void setUp() throws Exception {
@@ -68,7 +76,8 @@
         mServiceFake = new DozeServiceFake();
         mHandlerFake = new FakeHandler(Looper.getMainLooper());
         mWakeLock = new WakeLockFake();
-        mScreen = new DozeScreenState(mServiceFake, mHandlerFake, mDozeParameters, mWakeLock);
+        mScreen = new DozeScreenState(mServiceFake, mHandlerFake, mDozeHost, mDozeParameters,
+                mWakeLock);
     }
 
     @Test
@@ -183,4 +192,20 @@
         assertThat(mWakeLock.isHeld(), is(false));
     }
 
+    @Test
+    public void test_animatesPausing() {
+        ArgumentCaptor<Runnable> captor = ArgumentCaptor.forClass(Runnable.class);
+        doAnswer(invocation -> null).when(mDozeHost).prepareForGentleSleep(captor.capture());
+        mHandlerFake.setMode(QUEUEING);
+
+        mScreen.transitionTo(UNINITIALIZED, INITIALIZED);
+        mScreen.transitionTo(INITIALIZED, DOZE_AOD_PAUSING);
+        mScreen.transitionTo(DOZE_AOD_PAUSING, DOZE_AOD_PAUSED);
+
+        mHandlerFake.dispatchQueuedMessages();
+        verify(mDozeHost).prepareForGentleSleep(eq(captor.getValue()));
+        captor.getValue().run();
+        assertEquals(Display.STATE_OFF, mServiceFake.screenState);
+    }
+
 }
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
index e5ae6d5..777fec7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
@@ -20,6 +20,7 @@
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.spy;
@@ -50,14 +51,22 @@
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 @RunWithLooper(setAsMainLooper = true)
 public class DozeTriggersTest extends SysuiTestCase {
-    private DozeTriggers mTriggers;
+
+    @Mock
     private DozeMachine mMachine;
-    private DozeHostFake mHost;
+    @Mock
+    private DozeHost mHost;
+    @Mock
+    private AlarmManager mAlarmManager;
+    private DozeTriggers mTriggers;
     private FakeSensorManager mSensors;
     private Sensor mTapSensor;
     private DockManager mDockManagerFake;
@@ -65,9 +74,7 @@
 
     @Before
     public void setUp() throws Exception {
-        mMachine = mock(DozeMachine.class);
-        AlarmManager alarmManager = mock(AlarmManager.class);
-        mHost = spy(new DozeHostFake());
+        MockitoAnnotations.initMocks(this);
         AmbientDisplayConfiguration config = DozeConfigurationUtil.createMockConfig();
         DozeParameters parameters = DozeConfigurationUtil.createMockParameters();
         mSensors = spy(new FakeSensorManager(mContext));
@@ -78,7 +85,7 @@
                 new AsyncSensorManager(mSensors, null, new Handler());
         mProximitySensor = new FakeProximitySensor(getContext(), asyncSensorManager);
 
-        mTriggers = new DozeTriggers(mContext, mMachine, mHost, alarmManager, config, parameters,
+        mTriggers = new DozeTriggers(mContext, mMachine, mHost, mAlarmManager, config, parameters,
                 asyncSensorManager, Handler.createAsync(Looper.myLooper()), wakeLock, true,
                 mDockManagerFake, mProximitySensor, mock(DozeLog.class));
         waitForSensorManager();
@@ -87,19 +94,21 @@
     @Test
     public void testOnNotification_stillWorksAfterOneFailedProxCheck() throws Exception {
         when(mMachine.getState()).thenReturn(DozeMachine.State.DOZE);
+        ArgumentCaptor<DozeHost.Callback> captor = ArgumentCaptor.forClass(DozeHost.Callback.class);
+        doAnswer(invocation -> null).when(mHost).addCallback(captor.capture());
 
         mTriggers.transitionTo(DozeMachine.State.UNINITIALIZED, DozeMachine.State.INITIALIZED);
         mTriggers.transitionTo(DozeMachine.State.INITIALIZED, DozeMachine.State.DOZE);
         clearInvocations(mMachine);
 
         mProximitySensor.setLastEvent(new ProximitySensor.ProximityEvent(true, 1));
-        mHost.callback.onNotificationAlerted(null /* pulseSuppressedListener */);
+        captor.getValue().onNotificationAlerted(null /* pulseSuppressedListener */);
         mProximitySensor.alertListeners();
 
         verify(mMachine, never()).requestState(any());
         verify(mMachine, never()).requestPulse(anyInt());
 
-        mHost.callback.onNotificationAlerted(null /* pulseSuppressedListener */);
+        captor.getValue().onNotificationAlerted(null /* pulseSuppressedListener */);
         waitForSensorManager();
         mProximitySensor.setLastEvent(new ProximitySensor.ProximityEvent(false, 2));
         mProximitySensor.alertListeners();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DoubleTapHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DoubleTapHelperTest.java
new file mode 100644
index 0000000..df1233a
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DoubleTapHelperTest.java
@@ -0,0 +1,369 @@
+/*
+ * 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 static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyFloat;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.res.Resources;
+import android.os.SystemClock;
+import android.testing.AndroidTestingRunner;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewConfiguration;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class DoubleTapHelperTest extends SysuiTestCase {
+
+    private DoubleTapHelper mDoubleTapHelper;
+    private int mTouchSlop;
+    private int mDoubleTouchSlop;
+    @Mock private View mView;
+    @Mock private DoubleTapHelper.ActivationListener mActivationListener;
+    @Mock private DoubleTapHelper.DoubleTapListener mDoubleTapListener;
+    @Mock private DoubleTapHelper.SlideBackListener mSlideBackListener;
+    @Mock private DoubleTapHelper.DoubleTapLogListener mDoubleTapLogListener;
+    @Mock private Resources mResources;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        mTouchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop();
+        // The double tap slop has to be less than the regular slop, otherwise it has no effect.
+        mDoubleTouchSlop = mTouchSlop - 1;
+        when(mView.getContext()).thenReturn(mContext);
+        when(mView.getResources()).thenReturn(mResources);
+        when(mResources.getDimension(R.dimen.double_tap_slop))
+                .thenReturn((float) mDoubleTouchSlop);
+
+        mDoubleTapHelper = new DoubleTapHelper(mView,
+                                               mActivationListener,
+                                               mDoubleTapListener,
+                                               mSlideBackListener, mDoubleTapLogListener);
+    }
+
+    @Test
+    public void testDoubleTap_success() {
+        long downtimeA = SystemClock.uptimeMillis();
+        long downtimeB = downtimeA + 100;
+
+        MotionEvent evDownA = MotionEvent.obtain(downtimeA,
+                                                 downtimeA,
+                                                 MotionEvent.ACTION_DOWN,
+                                                 1,
+                                                 1,
+                                                 0);
+        MotionEvent evUpA = MotionEvent.obtain(downtimeA,
+                                               downtimeA + 1,
+                                               MotionEvent.ACTION_UP,
+                                               1,
+                                               1,
+                                               0);
+        MotionEvent evDownB = MotionEvent.obtain(downtimeB,
+                                                 downtimeB,
+                                                 MotionEvent.ACTION_DOWN,
+                                                 1,
+                                                 1,
+                                                 0);
+        MotionEvent evUpB = MotionEvent.obtain(downtimeB,
+                                               downtimeB + 1,
+                                               MotionEvent.ACTION_UP,
+                                               1,
+                                               1,
+                                               0);
+
+        mDoubleTapHelper.onTouchEvent(evDownA);
+        mDoubleTapHelper.onTouchEvent(evUpA);
+        verify(mActivationListener).onActiveChanged(true);
+        verify(mView).postDelayed(any(Runnable.class), anyLong());
+        verify(mDoubleTapLogListener, never()).onDoubleTapLog(anyBoolean(), anyFloat(), anyFloat());
+        verify(mDoubleTapListener, never()).onDoubleTap();
+
+        mDoubleTapHelper.onTouchEvent(evDownB);
+        mDoubleTapHelper.onTouchEvent(evUpB);
+        verify(mDoubleTapLogListener).onDoubleTapLog(true, 0, 0);
+        verify(mDoubleTapListener).onDoubleTap();
+
+        evDownA.recycle();
+        evUpA.recycle();
+        evDownB.recycle();
+        evUpB.recycle();
+    }
+
+    @Test
+    public void testSingleTap_timeout() {
+        long downtimeA = SystemClock.uptimeMillis();
+
+        MotionEvent evDownA = MotionEvent.obtain(downtimeA,
+                                                 downtimeA,
+                                                 MotionEvent.ACTION_DOWN,
+                                                 1,
+                                                 1,
+                                                 0);
+        MotionEvent evUpA = MotionEvent.obtain(downtimeA,
+                                               downtimeA + 1,
+                                               MotionEvent.ACTION_UP,
+                                               1,
+                                               1,
+                                               0);
+
+        ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class);
+        mDoubleTapHelper.onTouchEvent(evDownA);
+        mDoubleTapHelper.onTouchEvent(evUpA);
+        verify(mActivationListener).onActiveChanged(true);
+        verify(mView).postDelayed(runnableCaptor.capture(), anyLong());
+        runnableCaptor.getValue().run();
+        verify(mActivationListener).onActiveChanged(true);
+
+        evDownA.recycle();
+        evUpA.recycle();
+    }
+
+    @Test
+    public void testSingleTap_slop() {
+        long downtimeA = SystemClock.uptimeMillis();
+
+        MotionEvent evDownA = MotionEvent.obtain(downtimeA,
+                                                 downtimeA,
+                                                 MotionEvent.ACTION_DOWN,
+                                                 1,
+                                                 1,
+                                                 0);
+        MotionEvent evUpA = MotionEvent.obtain(downtimeA,
+                                               downtimeA + 1,
+                                               MotionEvent.ACTION_UP,
+                                               1 + mTouchSlop,
+                                               1,
+                                               0);
+
+        ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class);
+        mDoubleTapHelper.onTouchEvent(evDownA);
+        mDoubleTapHelper.onTouchEvent(evUpA);
+        verify(mActivationListener, never()).onActiveChanged(true);
+        verify(mDoubleTapListener, never()).onDoubleTap();
+
+        evDownA.recycle();
+        evUpA.recycle();
+    }
+
+    @Test
+    public void testDoubleTap_slop() {
+        long downtimeA = SystemClock.uptimeMillis();
+        long downtimeB = downtimeA + 100;
+
+        MotionEvent evDownA = MotionEvent.obtain(downtimeA,
+                                                 downtimeA,
+                                                 MotionEvent.ACTION_DOWN,
+                                                 1,
+                                                 1,
+                                                 0);
+        MotionEvent evUpA = MotionEvent.obtain(downtimeA,
+                                               downtimeA + 1,
+                                               MotionEvent.ACTION_UP,
+                                               1,
+                                               1,
+                                               0);
+        MotionEvent evDownB = MotionEvent.obtain(downtimeB,
+                                                 downtimeB,
+                                                 MotionEvent.ACTION_DOWN,
+                                                 1,
+                                                 1,
+                                                 0);
+        MotionEvent evUpB = MotionEvent.obtain(downtimeB,
+                                               downtimeB + 1,
+                                               MotionEvent.ACTION_UP,
+                                               1,
+                                               1 + mDoubleTouchSlop,
+                                               0);
+
+        mDoubleTapHelper.onTouchEvent(evDownA);
+        mDoubleTapHelper.onTouchEvent(evUpA);
+        verify(mActivationListener).onActiveChanged(true);
+        verify(mView).postDelayed(any(Runnable.class), anyLong());
+
+        mDoubleTapHelper.onTouchEvent(evDownB);
+        mDoubleTapHelper.onTouchEvent(evUpB);
+        verify(mDoubleTapLogListener).onDoubleTapLog(false, 0, mDoubleTouchSlop);
+        verify(mActivationListener).onActiveChanged(false);
+        verify(mDoubleTapListener, never()).onDoubleTap();
+
+        evDownA.recycle();
+        evUpA.recycle();
+        evDownB.recycle();
+        evUpB.recycle();
+    }
+
+    @Test
+    public void testSlideBack() {
+        long downtimeA = SystemClock.uptimeMillis();
+        long downtimeB = downtimeA + 100;
+
+        MotionEvent evDownA = MotionEvent.obtain(downtimeA,
+                                                 downtimeA,
+                                                 MotionEvent.ACTION_DOWN,
+                                                 1,
+                                                 1,
+                                                 0);
+        MotionEvent evUpA = MotionEvent.obtain(downtimeA,
+                                               downtimeA + 1,
+                                               MotionEvent.ACTION_UP,
+                                               1,
+                                               1,
+                                               0);
+        MotionEvent evDownB = MotionEvent.obtain(downtimeB,
+                                                 downtimeB,
+                                                 MotionEvent.ACTION_DOWN,
+                                                 1,
+                                                 1,
+                                                 0);
+        MotionEvent evUpB = MotionEvent.obtain(downtimeB,
+                                               downtimeB + 1,
+                                               MotionEvent.ACTION_UP,
+                                               1,
+                                               1,
+                                               0);
+
+        when(mSlideBackListener.onSlideBack()).thenReturn(true);
+
+        mDoubleTapHelper.onTouchEvent(evDownA);
+        mDoubleTapHelper.onTouchEvent(evUpA);
+        verify(mActivationListener, never()).onActiveChanged(true);
+        verify(mActivationListener, never()).onActiveChanged(false);
+        verify(mDoubleTapListener, never()).onDoubleTap();
+        mDoubleTapHelper.onTouchEvent(evDownB);
+        mDoubleTapHelper.onTouchEvent(evUpB);
+        verify(mActivationListener, never()).onActiveChanged(true);
+        verify(mActivationListener, never()).onActiveChanged(false);
+        verify(mDoubleTapListener, never()).onDoubleTap();
+
+        evDownA.recycle();
+        evUpA.recycle();
+        evDownB.recycle();
+        evUpB.recycle();
+    }
+
+
+    @Test
+    public void testMoreThanTwoTaps() {
+        long downtimeA = SystemClock.uptimeMillis();
+        long downtimeB = downtimeA + 100;
+        long downtimeC = downtimeB + 100;
+        long downtimeD = downtimeC + 100;
+
+        MotionEvent evDownA = MotionEvent.obtain(downtimeA,
+                                                 downtimeA,
+                                                 MotionEvent.ACTION_DOWN,
+                                                 1,
+                                                 1,
+                                                 0);
+        MotionEvent evUpA = MotionEvent.obtain(downtimeA,
+                                               downtimeA + 1,
+                                               MotionEvent.ACTION_UP,
+                                               1,
+                                               1,
+                                               0);
+        MotionEvent evDownB = MotionEvent.obtain(downtimeB,
+                                                 downtimeB,
+                                                 MotionEvent.ACTION_DOWN,
+                                                 1,
+                                                 1,
+                                                 0);
+        MotionEvent evUpB = MotionEvent.obtain(downtimeB,
+                                               downtimeB + 1,
+                                               MotionEvent.ACTION_UP,
+                                               1,
+                                               1,
+                                               0);
+        MotionEvent evDownC = MotionEvent.obtain(downtimeC,
+                                                 downtimeC,
+                                                 MotionEvent.ACTION_DOWN,
+                                                 1,
+                                                 1,
+                                                 0);
+        MotionEvent evUpC = MotionEvent.obtain(downtimeC,
+                                               downtimeC + 1,
+                                               MotionEvent.ACTION_UP,
+                                               1,
+                                               1,
+                                               0);
+        MotionEvent evDownD = MotionEvent.obtain(downtimeD,
+                                                 downtimeD,
+                                                 MotionEvent.ACTION_DOWN,
+                                                 1,
+                                                 1,
+                                                 0);
+        MotionEvent evUpD = MotionEvent.obtain(downtimeD,
+                                               downtimeD + 1,
+                                               MotionEvent.ACTION_UP,
+                                               1,
+                                               1,
+                                               0);
+
+        mDoubleTapHelper.onTouchEvent(evDownA);
+        mDoubleTapHelper.onTouchEvent(evUpA);
+        verify(mActivationListener).onActiveChanged(true);
+        verify(mView).postDelayed(any(Runnable.class), anyLong());
+        verify(mDoubleTapLogListener, never()).onDoubleTapLog(anyBoolean(), anyFloat(), anyFloat());
+        verify(mDoubleTapListener, never()).onDoubleTap();
+
+        mDoubleTapHelper.onTouchEvent(evDownB);
+        mDoubleTapHelper.onTouchEvent(evUpB);
+        verify(mDoubleTapLogListener).onDoubleTapLog(true, 0, 0);
+        verify(mDoubleTapListener).onDoubleTap();
+
+        reset(mView);
+        reset(mActivationListener);
+        reset(mDoubleTapLogListener);
+        reset(mDoubleTapListener);
+
+        mDoubleTapHelper.onTouchEvent(evDownC);
+        mDoubleTapHelper.onTouchEvent(evUpC);
+        verify(mActivationListener).onActiveChanged(true);
+        verify(mView).postDelayed(any(Runnable.class), anyLong());
+        verify(mDoubleTapLogListener, never()).onDoubleTapLog(anyBoolean(), anyFloat(), anyFloat());
+        verify(mDoubleTapListener, never()).onDoubleTap();
+
+        mDoubleTapHelper.onTouchEvent(evDownD);
+        mDoubleTapHelper.onTouchEvent(evUpD);
+        verify(mDoubleTapLogListener).onDoubleTapLog(true, 0, 0);
+        verify(mDoubleTapListener).onDoubleTap();
+
+        evDownA.recycle();
+        evUpA.recycle();
+        evDownB.recycle();
+        evUpB.recycle();
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index 214e26a..f3ff7ec 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -133,6 +133,20 @@
     }
 
     @Test
+    public void transitionToOff() {
+        mScrimController.transitionTo(ScrimState.OFF);
+        mScrimController.finishAnimationsImmediately();
+
+        assertScrimAlpha(OPAQUE /* front */,
+                SEMI_TRANSPARENT /* back */,
+                TRANSPARENT /* bubble */);
+
+        assertScrimTint(true /* front */,
+                true /* behind */,
+                false /* bubble */);
+    }
+
+    @Test
     public void transitionToAod_withRegularWallpaper() {
         mScrimController.transitionTo(ScrimState.AOD);
         mScrimController.finishAnimationsImmediately();
diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto
index 5b826b1..b0e401b 100644
--- a/proto/src/metrics_constants/metrics_constants.proto
+++ b/proto/src/metrics_constants/metrics_constants.proto
@@ -2584,15 +2584,15 @@
 
     // ACTION: Logged when trampoline activity finishes.
     // TIME: Indicates total time taken by trampoline activity to finish in MS.
-    PROVISIONING_TRAMPOLINE_ACTIVITY_TIME_MS = 523;
+    PROVISIONING_TRAMPOLINE_ACTIVITY_TIME_MS = 523 [deprecated=true];
 
     // ACTION: Logged when encryption activity finishes.
     // TIME: Indicates total time taken by post encryption activity to finish in MS.
-    PROVISIONING_POST_ENCRYPTION_ACTIVITY_TIME_MS = 524;
+    PROVISIONING_POST_ENCRYPTION_ACTIVITY_TIME_MS = 524 [deprecated=true];
 
     // ACTION: Logged when finalization activity finishes.
     // TIME: Indicates time taken by finalization activity to finish in MS.
-    PROVISIONING_FINALIZATION_ACTIVITY_TIME_MS = 525;
+    PROVISIONING_FINALIZATION_ACTIVITY_TIME_MS = 525 [deprecated=true];
 
     // OPEN: Settings Support > Phone/Chat -> Disclaimer
     DIALOG_SUPPORT_DISCLAIMER = 526;
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 91269c7..e909e7a 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -16,13 +16,6 @@
 
 package com.android.server.accessibility;
 
-import static android.accessibilityservice.AccessibilityService.SHOW_MODE_AUTO;
-import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE;
-import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HARD_KEYBOARD_OVERRIDDEN;
-import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HIDDEN;
-import static android.accessibilityservice.AccessibilityService.SHOW_MODE_IGNORE_HARD_KEYBOARD;
-import static android.accessibilityservice.AccessibilityService.SHOW_MODE_MASK;
-
 import static com.android.internal.util.FunctionalUtils.ignoreRemoteException;
 import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
 
@@ -50,8 +43,6 @@
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
 import android.database.ContentObserver;
-import android.graphics.Point;
-import android.graphics.Rect;
 import android.graphics.Region;
 import android.hardware.display.DisplayManager;
 import android.hardware.fingerprint.IFingerprintService;
@@ -119,7 +110,6 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
-import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
@@ -136,6 +126,7 @@
  */
 public class AccessibilityManagerService extends IAccessibilityManager.Stub
         implements AbstractAccessibilityServiceConnection.SystemSupport,
+        AccessibilityUserState.ServiceInfoChangeListener,
         AccessibilityWindowManager.AccessibilityEventSender,
         AccessibilitySecurityPolicy.AccessibilityUserManager {
 
@@ -178,12 +169,6 @@
     private final SimpleStringSplitter mStringColonSplitter =
             new SimpleStringSplitter(COMPONENT_NAME_SEPARATOR);
 
-    private final Rect mTempRect = new Rect();
-
-    private final Rect mTempRect1 = new Rect();
-
-    private final Point mTempPoint = new Point();
-
     private final PackageManager mPackageManager;
 
     private final PowerManager mPowerManager;
@@ -226,7 +211,7 @@
     private final RemoteCallbackList<IAccessibilityManagerClient> mGlobalClients =
             new RemoteCallbackList<>();
 
-    private final SparseArray<UserState> mUserStates = new SparseArray<>();
+    private final SparseArray<AccessibilityUserState> mUserStates = new SparseArray<>();
 
     private final UiAutomationManager mUiAutomationManager = new UiAutomationManager(mLock);
 
@@ -237,7 +222,7 @@
 
     private boolean mIsAccessibilityButtonShown;
 
-    private UserState getCurrentUserStateLocked() {
+    private AccessibilityUserState getCurrentUserStateLocked() {
         return getUserStateLocked(mCurrentUserId);
     }
 
@@ -293,6 +278,11 @@
         return mIsAccessibilityButtonShown;
     }
 
+    @Override
+    public void onServiceInfoChangedLocked(AccessibilityUserState userState) {
+        scheduleNotifyClientsOfServicesStateChangeLocked(userState);
+    }
+
     @Nullable
     public FingerprintGestureDispatcher getFingerprintGestureDispatcher() {
         return mFingerprintGestureDispatcher;
@@ -307,40 +297,40 @@
         }
     }
 
-    private UserState getUserState(int userId) {
+    private AccessibilityUserState getUserState(int userId) {
         synchronized (mLock) {
             return getUserStateLocked(userId);
         }
     }
 
-    private UserState getUserStateLocked(int userId) {
-        UserState state = mUserStates.get(userId);
+    @NonNull
+    private AccessibilityUserState getUserStateLocked(int userId) {
+        AccessibilityUserState state = mUserStates.get(userId);
         if (state == null) {
-            state = new UserState(userId);
+            state = new AccessibilityUserState(userId, mContext, this);
             mUserStates.put(userId, state);
         }
         return state;
     }
 
     boolean getBindInstantServiceAllowed(int userId) {
-        final UserState userState = getUserState(userId);
-        if (userState == null) return false;
-        return userState.getBindInstantServiceAllowed();
+        synchronized (mLock) {
+            final AccessibilityUserState userState = getUserStateLocked(userId);
+            return userState.getBindInstantServiceAllowedLocked();
+        }
     }
 
     void setBindInstantServiceAllowed(int userId, boolean allowed) {
-        UserState userState;
+        mContext.enforceCallingOrSelfPermission(
+                Manifest.permission.MANAGE_BIND_INSTANT_SERVICE,
+                "setBindInstantServiceAllowed");
         synchronized (mLock) {
-            userState = getUserState(userId);
-            if (userState == null) {
-                if (!allowed) {
-                    return;
-                }
-                userState = new UserState(userId);
-                mUserStates.put(userId, userState);
+            final AccessibilityUserState userState = getUserStateLocked(userId);
+            if (allowed != userState.getBindInstantServiceAllowedLocked()) {
+                userState.setBindInstantServiceAllowedLocked(allowed);
+                onUserStateChangedLocked(userState);
             }
         }
-        userState.setBindInstantServiceAllowed(allowed);
     }
 
     private void registerBroadcastReceivers() {
@@ -354,7 +344,7 @@
                         return;
                     }
                     // We will update when the automation service dies.
-                    UserState userState = getCurrentUserStateLocked();
+                    AccessibilityUserState userState = getCurrentUserStateLocked();
                     // We have to reload the installed services since some services may
                     // have different attributes, resolve info (does not support equals),
                     // etc. Remove them then to force reload.
@@ -376,8 +366,8 @@
                     if (userId != mCurrentUserId) {
                         return;
                     }
-                    UserState userState = getUserStateLocked(userId);
-                    boolean reboundAService = userState.mBindingServices.removeIf(
+                    AccessibilityUserState userState = getUserStateLocked(userId);
+                    boolean reboundAService = userState.getBindingServicesLocked().removeIf(
                             component -> component != null
                                     && component.getPackageName().equals(packageName));
                     if (reboundAService) {
@@ -395,14 +385,14 @@
                     if (userId != mCurrentUserId) {
                         return;
                     }
-                    UserState userState = getUserStateLocked(userId);
+                    AccessibilityUserState userState = getUserStateLocked(userId);
                     Iterator<ComponentName> it = userState.mEnabledServices.iterator();
                     while (it.hasNext()) {
                         ComponentName comp = it.next();
                         String compPkg = comp.getPackageName();
                         if (compPkg.equals(packageName)) {
                             it.remove();
-                            userState.mBindingServices.remove(comp);
+                            userState.getBindingServicesLocked().remove(comp);
                             // Update the enabled services setting.
                             persistComponentNamesToSettingLocked(
                                     Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
@@ -430,7 +420,7 @@
                     if (userId != mCurrentUserId) {
                         return false;
                     }
-                    UserState userState = getUserStateLocked(userId);
+                    AccessibilityUserState userState = getUserStateLocked(userId);
                     Iterator<ComponentName> it = userState.mEnabledServices.iterator();
                     while (it.hasNext()) {
                         ComponentName comp = it.next();
@@ -441,7 +431,7 @@
                                     return true;
                                 }
                                 it.remove();
-                                userState.mBindingServices.remove(comp);
+                                userState.getBindingServicesLocked().remove(comp);
                                 persistComponentNamesToSettingLocked(
                                         Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
                                         userState.mEnabledServices, userId);
@@ -478,7 +468,7 @@
                 } else if (Intent.ACTION_USER_PRESENT.equals(action)) {
                     // We will update when the automation service dies.
                     synchronized (mLock) {
-                        UserState userState = getCurrentUserStateLocked();
+                        AccessibilityUserState userState = getCurrentUserStateLocked();
                         if (readConfigurationForUserStateLocked(userState)) {
                             onUserStateChangedLocked(userState);
                         }
@@ -509,7 +499,7 @@
             // If the client is from a process that runs across users such as
             // the system UI or the system we add it to the global state that
             // is shared across users.
-            UserState userState = getUserStateLocked(resolvedUserId);
+            AccessibilityUserState userState = getUserStateLocked(resolvedUserId);
             Client client = new Client(callback, Binder.getCallingUid(), userState);
             if (mSecurityPolicy.isCallerInteractingAcrossUsers(userId)) {
                 mGlobalClients.register(callback, client);
@@ -517,7 +507,7 @@
                     Slog.i(LOG_TAG, "Added global client for pid:" + Binder.getCallingPid());
                 }
                 return IntPair.of(
-                        userState.getClientState(),
+                        getClientStateLocked(userState),
                         client.mLastSentRelevantEventTypes);
             } else {
                 userState.mUserClients.register(callback, client);
@@ -529,7 +519,7 @@
                             + " and userId:" + mCurrentUserId);
                 }
                 return IntPair.of(
-                        (resolvedUserId == mCurrentUserId) ? userState.getClientState() : 0,
+                        (resolvedUserId == mCurrentUserId) ? getClientStateLocked(userState) : 0,
                         client.mLastSentRelevantEventTypes);
             }
         }
@@ -644,7 +634,7 @@
                     .resolveCallingUserIdEnforcingPermissionsLocked(userId);
 
             // The automation service can suppress other services.
-            final UserState userState = getUserStateLocked(resolvedUserId);
+            final AccessibilityUserState userState = getUserStateLocked(resolvedUserId);
             if (mUiAutomationManager.suppressingAccessibilityServicesLocked()) {
                 return Collections.emptyList();
             }
@@ -754,15 +744,15 @@
         }
         synchronized (mLock) {
             // Set the temporary state.
-            UserState userState = getCurrentUserStateLocked();
+            AccessibilityUserState userState = getCurrentUserStateLocked();
 
-            userState.mIsTouchExplorationEnabled = touchExplorationEnabled;
-            userState.mIsDisplayMagnificationEnabled = false;
-            userState.mIsNavBarMagnificationEnabled = false;
-            userState.mIsAutoclickEnabled = false;
+            userState.setTouchExplorationEnabledLocked(touchExplorationEnabled);
+            userState.setDisplayMagnificationEnabledLocked(false);
+            userState.setNavBarMagnificationEnabledLocked(false);
+            userState.setAutoclickEnabledLocked(false);
             userState.mEnabledServices.clear();
             userState.mEnabledServices.add(service);
-            userState.mBindingServices.clear();
+            userState.getBindingServicesLocked().clear();
             userState.mTouchExplorationGrantedServices.clear();
             userState.mTouchExplorationGrantedServices.add(service);
 
@@ -943,7 +933,7 @@
             }
 
             // Disconnect from services for the old user.
-            UserState oldUserState = getCurrentUserStateLocked();
+            AccessibilityUserState oldUserState = getCurrentUserStateLocked();
             oldUserState.onSwitchToAnotherUserLocked();
 
             // Disable the local managers for the old user.
@@ -960,7 +950,7 @@
             // The user changed.
             mCurrentUserId = userId;
 
-            UserState userState = getCurrentUserStateLocked();
+            AccessibilityUserState userState = getCurrentUserStateLocked();
 
             readConfigurationForUserStateLocked(userState);
             // Even if reading did not yield change, we have to update
@@ -979,8 +969,8 @@
 
     private void announceNewUserIfNeeded() {
         synchronized (mLock) {
-            UserState userState = getCurrentUserStateLocked();
-            if (userState.isHandlingAccessibilityEvents()) {
+            AccessibilityUserState userState = getCurrentUserStateLocked();
+            if (userState.isHandlingAccessibilityEventsLocked()) {
                 UserManager userManager = (UserManager) mContext.getSystemService(
                         Context.USER_SERVICE);
                 String message = mContext.getString(R.string.user_switched,
@@ -997,7 +987,7 @@
         synchronized (mLock) {
             int parentUserId = mSecurityPolicy.resolveProfileParentLocked(userId);
             if (parentUserId == mCurrentUserId) {
-                UserState userState = getUserStateLocked(mCurrentUserId);
+                AccessibilityUserState userState = getUserStateLocked(mCurrentUserId);
                 onUserStateChangedLocked(userState);
             }
         }
@@ -1015,7 +1005,7 @@
         readComponentNamesFromStringLocked(oldSetting, mTempComponentNameSet, false);
         readComponentNamesFromStringLocked(newSetting, mTempComponentNameSet, true);
 
-        UserState userState = getUserStateLocked(UserHandle.USER_SYSTEM);
+        AccessibilityUserState userState = getUserStateLocked(UserHandle.USER_SYSTEM);
         userState.mEnabledServices.clear();
         userState.mEnabledServices.addAll(mTempComponentNameSet);
         persistComponentNamesToSettingLocked(
@@ -1025,6 +1015,10 @@
         onUserStateChangedLocked(userState);
     }
 
+    private int getClientStateLocked(AccessibilityUserState userState) {
+        return userState.getClientStateLocked(mUiAutomationManager.isUiAutomationRunningLocked());
+    }
+
     private InteractionBridge getInteractionBridge() {
         synchronized (mLock) {
             if (mInteractionBridge == null) {
@@ -1044,7 +1038,7 @@
         //       gestures to avoid user frustration when different
         //       behavior is observed from different combinations of
         //       enabled accessibility services.
-        UserState state = getCurrentUserStateLocked();
+        AccessibilityUserState state = getCurrentUserStateLocked();
         for (int i = state.mBoundServices.size() - 1; i >= 0; i--) {
             AccessibilityServiceConnection service = state.mBoundServices.get(i);
             if (service.mRequestTouchExplorationMode && service.mIsDefault == isDefault) {
@@ -1056,7 +1050,7 @@
     }
 
     private void notifyClearAccessibilityCacheLocked() {
-        UserState state = getCurrentUserStateLocked();
+        AccessibilityUserState state = getCurrentUserStateLocked();
         for (int i = state.mBoundServices.size() - 1; i >= 0; i--) {
             AccessibilityServiceConnection service = state.mBoundServices.get(i);
             service.notifyClearAccessibilityNodeInfoCache();
@@ -1065,25 +1059,17 @@
 
     private void notifyMagnificationChangedLocked(int displayId, @NonNull Region region,
             float scale, float centerX, float centerY) {
-        final UserState state = getCurrentUserStateLocked();
+        final AccessibilityUserState state = getCurrentUserStateLocked();
         for (int i = state.mBoundServices.size() - 1; i >= 0; i--) {
             final AccessibilityServiceConnection service = state.mBoundServices.get(i);
             service.notifyMagnificationChangedLocked(displayId, region, scale, centerX, centerY);
         }
     }
 
-    private void notifySoftKeyboardShowModeChangedLocked(int showMode) {
-        final UserState state = getCurrentUserStateLocked();
-        for (int i = state.mBoundServices.size() - 1; i >= 0; i--) {
-            final AccessibilityServiceConnection service = state.mBoundServices.get(i);
-            service.notifySoftKeyboardShowModeChangedLocked(showMode);
-        }
-    }
-
     private void notifyAccessibilityButtonClickedLocked(int displayId) {
-        final UserState state = getCurrentUserStateLocked();
+        final AccessibilityUserState state = getCurrentUserStateLocked();
 
-        int potentialTargets = state.mIsNavBarMagnificationEnabled ? 1 : 0;
+        int potentialTargets = state.isNavBarMagnificationEnabledLocked() ? 1 : 0;
         for (int i = state.mBoundServices.size() - 1; i >= 0; i--) {
             final AccessibilityServiceConnection service = state.mBoundServices.get(i);
             if (service.mRequestAccessibilityButton) {
@@ -1095,7 +1081,7 @@
             return;
         }
         if (potentialTargets == 1) {
-            if (state.mIsNavBarMagnificationEnabled) {
+            if (state.isNavBarMagnificationEnabledLocked()) {
                 mMainHandler.sendMessage(obtainMessage(
                         AccessibilityManagerService::sendAccessibilityButtonToInputFilter, this,
                         displayId));
@@ -1110,13 +1096,13 @@
                 }
             }
         } else {
-            if (state.mServiceAssignedToAccessibilityButton == null
-                    && !state.mIsNavBarMagnificationAssignedToAccessibilityButton) {
+            if (state.getServiceAssignedToAccessibilityButtonLocked() == null
+                    && !state.isNavBarMagnificationAssignedToAccessibilityButtonLocked()) {
                 mMainHandler.sendMessage(obtainMessage(
                         AccessibilityManagerService::showAccessibilityButtonTargetSelection, this,
                         displayId));
-            } else if (state.mIsNavBarMagnificationEnabled
-                    && state.mIsNavBarMagnificationAssignedToAccessibilityButton) {
+            } else if (state.isNavBarMagnificationEnabledLocked()
+                    && state.isNavBarMagnificationAssignedToAccessibilityButtonLocked()) {
                 mMainHandler.sendMessage(obtainMessage(
                         AccessibilityManagerService::sendAccessibilityButtonToInputFilter, this,
                         displayId));
@@ -1125,7 +1111,7 @@
                 for (int i = state.mBoundServices.size() - 1; i >= 0; i--) {
                     final AccessibilityServiceConnection service = state.mBoundServices.get(i);
                     if (service.mRequestAccessibilityButton && (service.mComponentName.equals(
-                            state.mServiceAssignedToAccessibilityButton))) {
+                            state.getServiceAssignedToAccessibilityButtonLocked()))) {
                         service.notifyAccessibilityButtonClickedLocked(displayId);
                         return;
                     }
@@ -1154,7 +1140,7 @@
     }
 
     private void notifyAccessibilityButtonVisibilityChangedLocked(boolean available) {
-        final UserState state = getCurrentUserStateLocked();
+        final AccessibilityUserState state = getCurrentUserStateLocked();
         mIsAccessibilityButtonShown = available;
         for (int i = state.mBoundServices.size() - 1; i >= 0; i--) {
             final AccessibilityServiceConnection clientConnection = state.mBoundServices.get(i);
@@ -1165,7 +1151,7 @@
         }
     }
 
-    private boolean readInstalledAccessibilityServiceLocked(UserState userState) {
+    private boolean readInstalledAccessibilityServiceLocked(AccessibilityUserState userState) {
         mTempAccessibilityServiceInfoList.clear();
 
         int flags = PackageManager.GET_SERVICES
@@ -1174,7 +1160,7 @@
                 | PackageManager.MATCH_DIRECT_BOOT_AWARE
                 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
 
-        if (userState.getBindInstantServiceAllowed()) {
+        if (userState.getBindInstantServiceAllowedLocked()) {
             flags |= PackageManager.MATCH_INSTANT;
         }
 
@@ -1209,7 +1195,7 @@
         return false;
     }
 
-    private boolean readInstalledAccessibilityShortcutLocked(UserState userState) {
+    private boolean readInstalledAccessibilityShortcutLocked(AccessibilityUserState userState) {
         final List<AccessibilityShortcutInfo> shortcutInfos = AccessibilityManager
                 .getInstance(mContext).getInstalledAccessibilityShortcutListAsUser(
                         mContext, mCurrentUserId);
@@ -1221,7 +1207,7 @@
         return false;
     }
 
-    private boolean readEnabledAccessibilityServicesLocked(UserState userState) {
+    private boolean readEnabledAccessibilityServicesLocked(AccessibilityUserState userState) {
         mTempComponentNameSet.clear();
         readComponentNamesFromSettingLocked(Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
                 userState.mUserId, mTempComponentNameSet);
@@ -1236,7 +1222,7 @@
     }
 
     private boolean readTouchExplorationGrantedAccessibilityServicesLocked(
-            UserState userState) {
+            AccessibilityUserState userState) {
         mTempComponentNameSet.clear();
         readComponentNamesFromSettingLocked(
                 Settings.Secure.TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES,
@@ -1261,7 +1247,7 @@
     private void notifyAccessibilityServicesDelayedLocked(AccessibilityEvent event,
             boolean isDefault) {
         try {
-            UserState state = getCurrentUserStateLocked();
+            AccessibilityUserState state = getCurrentUserStateLocked();
             for (int i = 0, count = state.mBoundServices.size(); i < count; i++) {
                 AccessibilityServiceConnection service = state.mBoundServices.get(i);
 
@@ -1276,7 +1262,7 @@
         }
     }
 
-    private void updateRelevantEventsLocked(UserState userState) {
+    private void updateRelevantEventsLocked(AccessibilityUserState userState) {
         mMainHandler.post(() -> {
             broadcastToClients(userState, ignoreRemoteException(client -> {
                 int relevantEventTypes;
@@ -1296,7 +1282,7 @@
         });
     }
 
-    private int computeRelevantEventTypesLocked(UserState userState, Client client) {
+    private int computeRelevantEventTypesLocked(AccessibilityUserState userState, Client client) {
         int relevantEventTypes = 0;
 
         int serviceCount = userState.mBoundServices.size();
@@ -1342,20 +1328,11 @@
     }
 
     private void broadcastToClients(
-            UserState userState, Consumer<Client> clientAction) {
+            AccessibilityUserState userState, Consumer<Client> clientAction) {
         mGlobalClients.broadcastForEachCookie(clientAction);
         userState.mUserClients.broadcastForEachCookie(clientAction);
     }
 
-    private void unbindAllServicesLocked(UserState userState) {
-        List<AccessibilityServiceConnection> services = userState.mBoundServices;
-        for (int count = services.size(); count > 0; count--) {
-            // When the service is unbound, it disappears from the list, so there's no need to
-            // keep track of the index
-            services.get(0).unbindLocked();
-        }
-    }
-
     /**
      * Populates a set with the {@link ComponentName}s stored in a colon
      * separated value setting for a given user.
@@ -1422,7 +1399,7 @@
         }
     }
 
-    private void updateServicesLocked(UserState userState) {
+    private void updateServicesLocked(AccessibilityUserState userState) {
         Map<ComponentName, AccessibilityServiceConnection> componentNameToServiceMap =
                 userState.mComponentNameToServiceMap;
         boolean isUnlockingOrUnlocked = LocalServices.getService(UserManagerInternal.class)
@@ -1442,7 +1419,7 @@
             }
 
             // Wait for the binding if it is in process.
-            if (userState.mBindingServices.contains(componentName)) {
+            if (userState.getBindingServicesLocked().contains(componentName)) {
                 continue;
             }
             if (userState.mEnabledServices.contains(componentName)
@@ -1478,15 +1455,15 @@
         if (audioManager != null) {
             audioManager.setAccessibilityServiceUids(mTempIntArray);
         }
-        updateAccessibilityEnabledSetting(userState);
+        updateAccessibilityEnabledSettingLocked(userState);
     }
 
-    private void scheduleUpdateClientsIfNeededLocked(UserState userState) {
-        final int clientState = userState.getClientState();
-        if (userState.mLastSentClientState != clientState
+    private void scheduleUpdateClientsIfNeededLocked(AccessibilityUserState userState) {
+        final int clientState = getClientStateLocked(userState);
+        if (userState.getLastSentClientStateLocked() != clientState
                 && (mGlobalClients.getRegisteredCallbackCount() > 0
                         || userState.mUserClients.getRegisteredCallbackCount() > 0)) {
-            userState.mLastSentClientState = clientState;
+            userState.setLastSentClientStateLocked(clientState);
             mMainHandler.sendMessage(obtainMessage(
                     AccessibilityManagerService::sendStateToAllClients,
                     this, clientState, userState.mUserId));
@@ -1508,7 +1485,8 @@
                 client -> client.setState(clientState)));
     }
 
-    private void scheduleNotifyClientsOfServicesStateChangeLocked(UserState userState) {
+    private void scheduleNotifyClientsOfServicesStateChangeLocked(
+            AccessibilityUserState userState) {
         updateRecommendedUiTimeoutLocked(userState);
         mMainHandler.sendMessage(obtainMessage(
                 AccessibilityManagerService::sendServicesStateChanged,
@@ -1527,44 +1505,45 @@
                 client -> client.notifyServicesStateChanged(uiTimeout)));
     }
 
-    private void scheduleUpdateInputFilter(UserState userState) {
+    private void scheduleUpdateInputFilter(AccessibilityUserState userState) {
         mMainHandler.sendMessage(obtainMessage(
                 AccessibilityManagerService::updateInputFilter, this, userState));
     }
 
-    private void scheduleUpdateFingerprintGestureHandling(UserState userState) {
+    private void scheduleUpdateFingerprintGestureHandling(AccessibilityUserState userState) {
         mMainHandler.sendMessage(obtainMessage(
                 AccessibilityManagerService::updateFingerprintGestureHandling,
                 this, userState));
     }
 
-    private void updateInputFilter(UserState userState) {
+    private void updateInputFilter(AccessibilityUserState userState) {
         if (mUiAutomationManager.suppressingAccessibilityServicesLocked()) return;
 
         boolean setInputFilter = false;
         AccessibilityInputFilter inputFilter = null;
         synchronized (mLock) {
             int flags = 0;
-            if (userState.mIsDisplayMagnificationEnabled) {
+            if (userState.isDisplayMagnificationEnabledLocked()) {
                 flags |= AccessibilityInputFilter.FLAG_FEATURE_SCREEN_MAGNIFIER;
             }
-            if (userState.mIsNavBarMagnificationEnabled) {
+            if (userState.isNavBarMagnificationEnabledLocked()) {
                 flags |= AccessibilityInputFilter.FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER;
             }
             if (userHasMagnificationServicesLocked(userState)) {
                 flags |= AccessibilityInputFilter.FLAG_FEATURE_CONTROL_SCREEN_MAGNIFIER;
             }
             // Touch exploration without accessibility makes no sense.
-            if (userState.isHandlingAccessibilityEvents() && userState.mIsTouchExplorationEnabled) {
+            if (userState.isHandlingAccessibilityEventsLocked()
+                    && userState.isTouchExplorationEnabledLocked()) {
                 flags |= AccessibilityInputFilter.FLAG_FEATURE_TOUCH_EXPLORATION;
             }
-            if (userState.mIsFilterKeyEventsEnabled) {
+            if (userState.isFilterKeyEventsEnabledLocked()) {
                 flags |= AccessibilityInputFilter.FLAG_FEATURE_FILTER_KEY_EVENTS;
             }
-            if (userState.mIsAutoclickEnabled) {
+            if (userState.isAutoclickEnabledLocked()) {
                 flags |= AccessibilityInputFilter.FLAG_FEATURE_AUTOCLICK;
             }
-            if (userState.mIsPerformGesturesEnabled) {
+            if (userState.isPerformGesturesEnabledLocked()) {
                 flags |= AccessibilityInputFilter.FLAG_FEATURE_INJECT_MOTION_EVENTS;
             }
             if (flags != 0) {
@@ -1597,8 +1576,8 @@
             String label = service.getServiceInfo().getResolveInfo()
                     .loadLabel(mContext.getPackageManager()).toString();
 
-            final UserState userState = getCurrentUserStateLocked();
-            if (userState.mIsTouchExplorationEnabled) {
+            final AccessibilityUserState userState = getCurrentUserStateLocked();
+            if (userState.isTouchExplorationEnabledLocked()) {
                 return;
             }
             if (mEnableTouchExplorationDialog != null
@@ -1608,40 +1587,40 @@
             mEnableTouchExplorationDialog = new AlertDialog.Builder(mContext)
                 .setIconAttribute(android.R.attr.alertDialogIcon)
                 .setPositiveButton(android.R.string.ok, new OnClickListener() {
-                     @Override
-                     public void onClick(DialogInterface dialog, int which) {
-                         // The user allowed the service to toggle touch exploration.
-                         userState.mTouchExplorationGrantedServices.add(service.mComponentName);
-                         persistComponentNamesToSettingLocked(
-                                 Settings.Secure.TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES,
-                                 userState.mTouchExplorationGrantedServices, userState.mUserId);
-                         // Enable touch exploration.
-                         userState.mIsTouchExplorationEnabled = true;
-                         final long identity = Binder.clearCallingIdentity();
-                         try {
-                             Settings.Secure.putIntForUser(mContext.getContentResolver(),
-                                     Settings.Secure.TOUCH_EXPLORATION_ENABLED, 1,
-                                     userState.mUserId);
-                         } finally {
-                             Binder.restoreCallingIdentity(identity);
-                         }
-                         onUserStateChangedLocked(userState);
-                     }
-                 })
-                 .setNegativeButton(android.R.string.cancel, new OnClickListener() {
-                     @Override
-                     public void onClick(DialogInterface dialog, int which) {
-                         dialog.dismiss();
-                     }
-                 })
-                 .setTitle(R.string.enable_explore_by_touch_warning_title)
-                 .setMessage(mContext.getString(
-                         R.string.enable_explore_by_touch_warning_message, label))
-                 .create();
+                    @Override
+                    public void onClick(DialogInterface dialog, int which) {
+                        // The user allowed the service to toggle touch exploration.
+                        userState.mTouchExplorationGrantedServices.add(service.mComponentName);
+                        persistComponentNamesToSettingLocked(
+                                Settings.Secure.TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES,
+                                userState.mTouchExplorationGrantedServices, userState.mUserId);
+                        // Enable touch exploration.
+                        userState.setTouchExplorationEnabledLocked(true);
+                        final long identity = Binder.clearCallingIdentity();
+                        try {
+                            Settings.Secure.putIntForUser(mContext.getContentResolver(),
+                                    Settings.Secure.TOUCH_EXPLORATION_ENABLED, 1,
+                                    userState.mUserId);
+                        } finally {
+                            Binder.restoreCallingIdentity(identity);
+                        }
+                        onUserStateChangedLocked(userState);
+                    }
+                })
+                .setNegativeButton(android.R.string.cancel, new OnClickListener() {
+                    @Override
+                    public void onClick(DialogInterface dialog, int which) {
+                        dialog.dismiss();
+                    }
+                })
+                .setTitle(R.string.enable_explore_by_touch_warning_title)
+                .setMessage(mContext.getString(
+                        R.string.enable_explore_by_touch_warning_message, label))
+                .create();
              mEnableTouchExplorationDialog.getWindow().setType(
                      WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
              mEnableTouchExplorationDialog.getWindow().getAttributes().privateFlags
-                     |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+                     |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
              mEnableTouchExplorationDialog.setCanceledOnTouchOutside(true);
              mEnableTouchExplorationDialog.show();
         }
@@ -1652,7 +1631,7 @@
      *
      * @param userState the new user state
      */
-    private void onUserStateChangedLocked(UserState userState) {
+    private void onUserStateChangedLocked(AccessibilityUserState userState) {
         // TODO: Remove this hack
         mInitialized = true;
         updateLegacyCapabilitiesLocked(userState);
@@ -1670,7 +1649,7 @@
         updateAccessibilityButtonTargetsLocked(userState);
     }
 
-    private void updateWindowsForAccessibilityCallbackLocked(UserState userState) {
+    private void updateWindowsForAccessibilityCallbackLocked(AccessibilityUserState userState) {
         // We observe windows for accessibility only if there is at least
         // one bound service that can retrieve window content that specified
         // it is interested in accessing such windows. For services that are
@@ -1702,7 +1681,7 @@
         }
     }
 
-    private void updateLegacyCapabilitiesLocked(UserState userState) {
+    private void updateLegacyCapabilitiesLocked(AccessibilityUserState userState) {
         // Up to JB-MR1 we had a white list with services that can enable touch
         // exploration. When a service is first started we show a dialog to the
         // use to get a permission to white list the service.
@@ -1724,20 +1703,20 @@
         }
     }
 
-    private void updatePerformGesturesLocked(UserState userState) {
+    private void updatePerformGesturesLocked(AccessibilityUserState userState) {
         final int serviceCount = userState.mBoundServices.size();
         for (int i = 0; i < serviceCount; i++) {
             AccessibilityServiceConnection service = userState.mBoundServices.get(i);
             if ((service.getCapabilities()
                     & AccessibilityServiceInfo.CAPABILITY_CAN_PERFORM_GESTURES) != 0) {
-                userState.mIsPerformGesturesEnabled = true;
+                userState.setPerformGesturesEnabledLocked(true);
                 return;
             }
         }
-        userState.mIsPerformGesturesEnabled = false;
+        userState.setPerformGesturesEnabledLocked(false);
     }
 
-    private void updateFilterKeyEventsLocked(UserState userState) {
+    private void updateFilterKeyEventsLocked(AccessibilityUserState userState) {
         final int serviceCount = userState.mBoundServices.size();
         for (int i = 0; i < serviceCount; i++) {
             AccessibilityServiceConnection service = userState.mBoundServices.get(i);
@@ -1745,14 +1724,14 @@
                     && (service.getCapabilities()
                             & AccessibilityServiceInfo
                             .CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS) != 0) {
-                userState.mIsFilterKeyEventsEnabled = true;
+                userState.setFilterKeyEventsEnabledLocked(true);
                 return;
             }
         }
-        userState.mIsFilterKeyEventsEnabled = false;
+        userState.setFilterKeyEventsEnabledLocked(false);
     }
 
-    private boolean readConfigurationForUserStateLocked(UserState userState) {
+    private boolean readConfigurationForUserStateLocked(AccessibilityUserState userState) {
         boolean somethingChanged = readInstalledAccessibilityServiceLocked(userState);
         somethingChanged |= readInstalledAccessibilityShortcutLocked(userState);
         somethingChanged |= readEnabledAccessibilityServicesLocked(userState);
@@ -1767,10 +1746,10 @@
         return somethingChanged;
     }
 
-    private void updateAccessibilityEnabledSetting(UserState userState) {
+    private void updateAccessibilityEnabledSettingLocked(AccessibilityUserState userState) {
         final long identity = Binder.clearCallingIdentity();
         final boolean isA11yEnabled = mUiAutomationManager.isUiAutomationRunningLocked()
-                || userState.isHandlingAccessibilityEvents();
+                || userState.isHandlingAccessibilityEventsLocked();
         try {
             Settings.Secure.putIntForUser(mContext.getContentResolver(),
                     Settings.Secure.ACCESSIBILITY_ENABLED,
@@ -1781,18 +1760,18 @@
         }
     }
 
-    private boolean readTouchExplorationEnabledSettingLocked(UserState userState) {
+    private boolean readTouchExplorationEnabledSettingLocked(AccessibilityUserState userState) {
         final boolean touchExplorationEnabled = Settings.Secure.getIntForUser(
                 mContext.getContentResolver(),
                 Settings.Secure.TOUCH_EXPLORATION_ENABLED, 0, userState.mUserId) == 1;
-        if (touchExplorationEnabled != userState.mIsTouchExplorationEnabled) {
-            userState.mIsTouchExplorationEnabled = touchExplorationEnabled;
+        if (touchExplorationEnabled != userState.isTouchExplorationEnabledLocked()) {
+            userState.setTouchExplorationEnabledLocked(touchExplorationEnabled);
             return true;
         }
         return false;
     }
 
-    private boolean readMagnificationEnabledSettingsLocked(UserState userState) {
+    private boolean readMagnificationEnabledSettingsLocked(AccessibilityUserState userState) {
         final boolean displayMagnificationEnabled = Settings.Secure.getIntForUser(
                 mContext.getContentResolver(),
                 Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED,
@@ -1801,40 +1780,40 @@
                 mContext.getContentResolver(),
                 Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED,
                 0, userState.mUserId) == 1;
-        if ((displayMagnificationEnabled != userState.mIsDisplayMagnificationEnabled)
-                || (navBarMagnificationEnabled != userState.mIsNavBarMagnificationEnabled)) {
-            userState.mIsDisplayMagnificationEnabled = displayMagnificationEnabled;
-            userState.mIsNavBarMagnificationEnabled = navBarMagnificationEnabled;
+        if ((displayMagnificationEnabled != userState.isDisplayMagnificationEnabledLocked())
+                || (navBarMagnificationEnabled != userState.isNavBarMagnificationEnabledLocked())) {
+            userState.setDisplayMagnificationEnabledLocked(displayMagnificationEnabled);
+            userState.setNavBarMagnificationEnabledLocked(navBarMagnificationEnabled);
             return true;
         }
         return false;
     }
 
-    private boolean readAutoclickEnabledSettingLocked(UserState userState) {
+    private boolean readAutoclickEnabledSettingLocked(AccessibilityUserState userState) {
         final boolean autoclickEnabled = Settings.Secure.getIntForUser(
                 mContext.getContentResolver(),
                 Settings.Secure.ACCESSIBILITY_AUTOCLICK_ENABLED,
                 0, userState.mUserId) == 1;
-        if (autoclickEnabled != userState.mIsAutoclickEnabled) {
-            userState.mIsAutoclickEnabled = autoclickEnabled;
+        if (autoclickEnabled != userState.isAutoclickEnabledLocked()) {
+            userState.setAutoclickEnabledLocked(autoclickEnabled);
             return true;
         }
         return false;
     }
 
-    private boolean readHighTextContrastEnabledSettingLocked(UserState userState) {
+    private boolean readHighTextContrastEnabledSettingLocked(AccessibilityUserState userState) {
         final boolean highTextContrastEnabled = Settings.Secure.getIntForUser(
                 mContext.getContentResolver(),
                 Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED, 0,
                 userState.mUserId) == 1;
-        if (highTextContrastEnabled != userState.mIsTextHighContrastEnabled) {
-            userState.mIsTextHighContrastEnabled = highTextContrastEnabled;
+        if (highTextContrastEnabled != userState.isTextHighContrastEnabledLocked()) {
+            userState.setTextHighContrastEnabledLocked(highTextContrastEnabled);
             return true;
         }
         return false;
     }
 
-    private void updateTouchExplorationLocked(UserState userState) {
+    private void updateTouchExplorationLocked(AccessibilityUserState userState) {
         boolean enabled = mUiAutomationManager.isTouchExplorationEnabledLocked();
         final int serviceCount = userState.mBoundServices.size();
         for (int i = 0; i < serviceCount; i++) {
@@ -1844,8 +1823,8 @@
                 break;
             }
         }
-        if (enabled != userState.mIsTouchExplorationEnabled) {
-            userState.mIsTouchExplorationEnabled = enabled;
+        if (enabled != userState.isTouchExplorationEnabledLocked()) {
+            userState.setTouchExplorationEnabledLocked(enabled);
             final long identity = Binder.clearCallingIdentity();
             try {
                 Settings.Secure.putIntForUser(mContext.getContentResolver(),
@@ -1857,60 +1836,61 @@
         }
     }
 
-    private boolean readAccessibilityShortcutSettingLocked(UserState userState) {
+    private boolean readAccessibilityShortcutSettingLocked(AccessibilityUserState userState) {
         String componentNameToEnableString = AccessibilityShortcutController
                 .getTargetServiceComponentNameString(mContext, userState.mUserId);
         if ((componentNameToEnableString == null) || componentNameToEnableString.isEmpty()) {
-            if (userState.mServiceToEnableWithShortcut == null) {
+            if (userState.getServiceToEnableWithShortcutLocked() == null) {
                 return false;
             }
-            userState.mServiceToEnableWithShortcut = null;
+            userState.setServiceToEnableWithShortcutLocked(null);
             return true;
         }
         ComponentName componentNameToEnable =
             ComponentName.unflattenFromString(componentNameToEnableString);
         if ((componentNameToEnable != null)
-                && componentNameToEnable.equals(userState.mServiceToEnableWithShortcut)) {
+                && componentNameToEnable.equals(userState.getServiceToEnableWithShortcutLocked())) {
             return false;
         }
 
-        userState.mServiceToEnableWithShortcut = componentNameToEnable;
+        userState.setServiceToEnableWithShortcutLocked(componentNameToEnable);
         scheduleNotifyClientsOfServicesStateChangeLocked(userState);
         return true;
     }
 
-    private boolean readAccessibilityButtonSettingsLocked(UserState userState) {
+    private boolean readAccessibilityButtonSettingsLocked(AccessibilityUserState userState) {
         String componentId = Settings.Secure.getStringForUser(mContext.getContentResolver(),
                 Settings.Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT, userState.mUserId);
         if (TextUtils.isEmpty(componentId)) {
-            if ((userState.mServiceAssignedToAccessibilityButton == null)
-                    && !userState.mIsNavBarMagnificationAssignedToAccessibilityButton) {
+            if ((userState.getServiceAssignedToAccessibilityButtonLocked() == null)
+                    && !userState.isNavBarMagnificationAssignedToAccessibilityButtonLocked()) {
                 return false;
             }
-            userState.mServiceAssignedToAccessibilityButton = null;
-            userState.mIsNavBarMagnificationAssignedToAccessibilityButton = false;
+            userState.setServiceAssignedToAccessibilityButtonLocked(null);
+            userState.setNavBarMagnificationAssignedToAccessibilityButtonLocked(false);
             return true;
         }
 
         if (componentId.equals(MagnificationController.class.getName())) {
-            if (userState.mIsNavBarMagnificationAssignedToAccessibilityButton) {
+            if (userState.isNavBarMagnificationAssignedToAccessibilityButtonLocked()) {
                 return false;
             }
-            userState.mServiceAssignedToAccessibilityButton = null;
-            userState.mIsNavBarMagnificationAssignedToAccessibilityButton = true;
+            userState.setServiceAssignedToAccessibilityButtonLocked(null);
+            userState.setNavBarMagnificationAssignedToAccessibilityButtonLocked(true);
             return true;
         }
 
         ComponentName componentName = ComponentName.unflattenFromString(componentId);
-        if (Objects.equals(componentName, userState.mServiceAssignedToAccessibilityButton)) {
+        if (Objects.equals(componentName,
+                userState.getServiceAssignedToAccessibilityButtonLocked())) {
             return false;
         }
-        userState.mServiceAssignedToAccessibilityButton = componentName;
-        userState.mIsNavBarMagnificationAssignedToAccessibilityButton = false;
+        userState.setServiceAssignedToAccessibilityButtonLocked(componentName);
+        userState.setNavBarMagnificationAssignedToAccessibilityButtonLocked(false);
         return true;
     }
 
-    private boolean readUserRecommendedUiTimeoutSettingsLocked(UserState userState) {
+    private boolean readUserRecommendedUiTimeoutSettingsLocked(AccessibilityUserState userState) {
         final int nonInteractiveUiTimeout = Settings.Secure.getIntForUser(
                 mContext.getContentResolver(),
                 Settings.Secure.ACCESSIBILITY_NON_INTERACTIVE_UI_TIMEOUT_MS, 0,
@@ -1919,10 +1899,10 @@
                 mContext.getContentResolver(),
                 Settings.Secure.ACCESSIBILITY_INTERACTIVE_UI_TIMEOUT_MS, 0,
                 userState.mUserId);
-        if (nonInteractiveUiTimeout != userState.mUserNonInteractiveUiTimeout
-                || interactiveUiTimeout != userState.mUserInteractiveUiTimeout) {
-            userState.mUserNonInteractiveUiTimeout = nonInteractiveUiTimeout;
-            userState.mUserInteractiveUiTimeout = interactiveUiTimeout;
+        if (nonInteractiveUiTimeout != userState.getUserNonInteractiveUiTimeoutLocked()
+                || interactiveUiTimeout != userState.getUserInteractiveUiTimeoutLocked()) {
+            userState.setUserNonInteractiveUiTimeoutLocked(nonInteractiveUiTimeout);
+            userState.setUserInteractiveUiTimeoutLocked(interactiveUiTimeout);
             scheduleNotifyClientsOfServicesStateChangeLocked(userState);
             return true;
         }
@@ -1936,22 +1916,22 @@
      *
      * @param userState
      */
-    private void updateAccessibilityShortcutLocked(UserState userState) {
-        if (userState.mServiceToEnableWithShortcut == null) {
+    private void updateAccessibilityShortcutLocked(AccessibilityUserState userState) {
+        if (userState.getServiceToEnableWithShortcutLocked() == null) {
             return;
         }
         boolean shortcutServiceIsInstalled =
                 AccessibilityShortcutController.getFrameworkShortcutFeaturesMap()
-                        .containsKey(userState.mServiceToEnableWithShortcut);
+                        .containsKey(userState.getServiceToEnableWithShortcutLocked());
         for (int i = 0; !shortcutServiceIsInstalled && (i < userState.mInstalledServices.size());
                 i++) {
             if (userState.mInstalledServices.get(i).getComponentName()
-                    .equals(userState.mServiceToEnableWithShortcut)) {
+                    .equals(userState.getServiceToEnableWithShortcutLocked())) {
                 shortcutServiceIsInstalled = true;
             }
         }
         if (!shortcutServiceIsInstalled) {
-            userState.mServiceToEnableWithShortcut = null;
+            userState.setServiceToEnableWithShortcutLocked(null);
             final long identity = Binder.clearCallingIdentity();
             try {
                 Settings.Secure.putStringForUser(mContext.getContentResolver(),
@@ -1967,7 +1947,7 @@
     }
 
     private boolean canRequestAndRequestsTouchExplorationLocked(
-            AccessibilityServiceConnection service, UserState userState) {
+            AccessibilityServiceConnection service, AccessibilityUserState userState) {
         // Service not ready or cannot request the feature - well nothing to do.
         if (!service.canReceiveEventsLocked() || !service.mRequestTouchExplorationMode) {
             return false;
@@ -1997,7 +1977,7 @@
         return false;
     }
 
-    private void updateMagnificationLocked(UserState userState) {
+    private void updateMagnificationLocked(AccessibilityUserState userState) {
         if (userState.mUserId != mCurrentUserId) {
             return;
         }
@@ -2012,8 +1992,8 @@
         // We would skip overlay display because it uses overlay window to simulate secondary
         // displays in one display. It's not a real display and there's no input events for it.
         final ArrayList<Display> displays = getValidDisplayList();
-        if (userState.mIsDisplayMagnificationEnabled
-                || userState.mIsNavBarMagnificationEnabled) {
+        if (userState.isDisplayMagnificationEnabledLocked()
+                || userState.isNavBarMagnificationEnabledLocked()) {
             for (int i = 0; i < displays.size(); i++) {
                 final Display display = displays.get(i);
                 getMagnificationController().register(display.getDisplayId());
@@ -2037,7 +2017,7 @@
      * Returns whether the specified user has any services that are capable of
      * controlling magnification.
      */
-    private boolean userHasMagnificationServicesLocked(UserState userState) {
+    private boolean userHasMagnificationServicesLocked(AccessibilityUserState userState) {
         final List<AccessibilityServiceConnection> services = userState.mBoundServices;
         for (int i = 0, count = services.size(); i < count; i++) {
             final AccessibilityServiceConnection service = services.get(i);
@@ -2052,7 +2032,7 @@
      * Returns whether the specified user has any services that are capable of
      * controlling magnification and are actively listening for magnification updates.
      */
-    private boolean userHasListeningMagnificationServicesLocked(UserState userState,
+    private boolean userHasListeningMagnificationServicesLocked(AccessibilityUserState userState,
             int displayId) {
         final List<AccessibilityServiceConnection> services = userState.mBoundServices;
         for (int i = 0, count = services.size(); i < count; i++) {
@@ -2065,7 +2045,7 @@
         return false;
     }
 
-    private void updateFingerprintGestureHandling(UserState userState) {
+    private void updateFingerprintGestureHandling(AccessibilityUserState userState) {
         final List<AccessibilityServiceConnection> services;
         synchronized (mLock) {
             services = userState.mBoundServices;
@@ -2097,7 +2077,7 @@
         }
     }
 
-    private void updateAccessibilityButtonTargetsLocked(UserState userState) {
+    private void updateAccessibilityButtonTargetsLocked(AccessibilityUserState userState) {
         for (int i = userState.mBoundServices.size() - 1; i >= 0; i--) {
             final AccessibilityServiceConnection service = userState.mBoundServices.get(i);
             if (service.mRequestAccessibilityButton) {
@@ -2107,9 +2087,9 @@
         }
     }
 
-    private void updateRecommendedUiTimeoutLocked(UserState userState) {
-        int newNonInteractiveUiTimeout = userState.mUserNonInteractiveUiTimeout;
-        int newInteractiveUiTimeout = userState.mUserInteractiveUiTimeout;
+    private void updateRecommendedUiTimeoutLocked(AccessibilityUserState userState) {
+        int newNonInteractiveUiTimeout = userState.getUserNonInteractiveUiTimeoutLocked();
+        int newInteractiveUiTimeout = userState.getUserInteractiveUiTimeoutLocked();
         // read from a11y services if user does not specify value
         if (newNonInteractiveUiTimeout == 0 || newInteractiveUiTimeout == 0) {
             int serviceNonInteractiveUiTimeout = 0;
@@ -2132,8 +2112,8 @@
                 newInteractiveUiTimeout = serviceInteractiveUiTimeout;
             }
         }
-        userState.mNonInteractiveUiTimeout = newNonInteractiveUiTimeout;
-        userState.mInteractiveUiTimeout = newInteractiveUiTimeout;
+        userState.setNonInteractiveUiTimeoutLocked(newNonInteractiveUiTimeout);
+        userState.setInteractiveUiTimeoutLocked(newInteractiveUiTimeout);
     }
 
     @GuardedBy("mLock")
@@ -2180,8 +2160,8 @@
         final Map<ComponentName, ToggleableFrameworkFeatureInfo> frameworkFeatureMap =
                 AccessibilityShortcutController.getFrameworkShortcutFeaturesMap();
         synchronized(mLock) {
-            final UserState userState = getUserStateLocked(mCurrentUserId);
-            final ComponentName serviceName = userState.mServiceToEnableWithShortcut;
+            final AccessibilityUserState userState = getUserStateLocked(mCurrentUserId);
+            final ComponentName serviceName = userState.getServiceToEnableWithShortcutLocked();
             if (serviceName == null) {
                 return;
             }
@@ -2218,11 +2198,11 @@
                     "getAccessibilityShortcutService requires the MANAGE_ACCESSIBILITY permission");
         }
         synchronized(mLock) {
-            final UserState userState = getUserStateLocked(mCurrentUserId);
-            if (userState.mServiceToEnableWithShortcut == null) {
+            final AccessibilityUserState userState = getUserStateLocked(mCurrentUserId);
+            if (userState.getServiceToEnableWithShortcutLocked() == null) {
                 return null;
             }
-            return userState.mServiceToEnableWithShortcut.flattenToString();
+            return userState.getServiceToEnableWithShortcutLocked().flattenToString();
         }
     }
 
@@ -2237,7 +2217,7 @@
                         userId);
         setting.write(ComponentNameSet.add(setting.read(), componentName));
 
-        UserState userState = getUserStateLocked(userId);
+        AccessibilityUserState userState = getUserStateLocked(userId);
         if (userState.mEnabledServices.add(componentName)) {
             onUserStateChangedLocked(userState);
         }
@@ -2254,7 +2234,7 @@
                         userId);
         setting.write(ComponentNameSet.remove(setting.read(), componentName));
 
-        UserState userState = getUserStateLocked(userId);
+        AccessibilityUserState userState = getUserStateLocked(userId);
         if (userState.mEnabledServices.remove(componentName)) {
             onUserStateChangedLocked(userState);
         }
@@ -2322,14 +2302,14 @@
     @Override
     public long getRecommendedTimeoutMillis() {
         synchronized(mLock) {
-            final UserState userState = getCurrentUserStateLocked();
+            final AccessibilityUserState userState = getCurrentUserStateLocked();
             return getRecommendedTimeoutMillisLocked(userState);
         }
     }
 
-    private long getRecommendedTimeoutMillisLocked(UserState userState) {
-        return IntPair.of(userState.mInteractiveUiTimeout,
-                userState.mNonInteractiveUiTimeout);
+    private long getRecommendedTimeoutMillisLocked(AccessibilityUserState userState) {
+        return IntPair.of(userState.getInteractiveUiTimeoutLocked(),
+                userState.getNonInteractiveUiTimeoutLocked());
     }
 
     @Override
@@ -2338,78 +2318,20 @@
         synchronized (mLock) {
             pw.println("ACCESSIBILITY MANAGER (dumpsys accessibility)");
             pw.println();
+            pw.append("currentUserId=").append(String.valueOf(mCurrentUserId));
+            pw.println();
             final int userCount = mUserStates.size();
             for (int i = 0; i < userCount; i++) {
-                UserState userState = mUserStates.valueAt(i);
-                pw.append("User state[attributes:{id=" + userState.mUserId);
-                pw.append(", currentUser=" + (userState.mUserId == mCurrentUserId));
-                pw.append(", touchExplorationEnabled=" + userState.mIsTouchExplorationEnabled);
-                pw.append(", displayMagnificationEnabled="
-                        + userState.mIsDisplayMagnificationEnabled);
-                pw.append(", navBarMagnificationEnabled="
-                        + userState.mIsNavBarMagnificationEnabled);
-                pw.append(", autoclickEnabled=" + userState.mIsAutoclickEnabled);
-                pw.append(", nonInteractiveUiTimeout=" + userState.mNonInteractiveUiTimeout);
-                pw.append(", interactiveUiTimeout=" + userState.mInteractiveUiTimeout);
-                pw.append(", installedServiceCount=" + userState.mInstalledServices.size());
-                if (mUiAutomationManager.isUiAutomationRunningLocked()) {
-                    pw.append(", ");
-                    mUiAutomationManager.dumpUiAutomationService(fd, pw, args);
-                    pw.println();
-                }
-                pw.append("}");
-                pw.println();
-                pw.append("     Bound services:{");
-                final int serviceCount = userState.mBoundServices.size();
-                for (int j = 0; j < serviceCount; j++) {
-                    if (j > 0) {
-                        pw.append(", ");
-                        pw.println();
-                        pw.append("                     ");
-                    }
-                    AccessibilityServiceConnection service = userState.mBoundServices.get(j);
-                    service.dump(fd, pw, args);
-                }
-                pw.println("}");
-                pw.append("     Enabled services:{");
-                Iterator<ComponentName> it = userState.mEnabledServices.iterator();
-                if (it.hasNext()) {
-                    ComponentName componentName = it.next();
-                    pw.append(componentName.toShortString());
-                    while (it.hasNext()) {
-                        componentName = it.next();
-                        pw.append(", ");
-                        pw.append(componentName.toShortString());
-                    }
-                }
-                pw.println("}");
-                pw.append("     Binding services:{");
-                it = userState.mBindingServices.iterator();
-                if (it.hasNext()) {
-                    ComponentName componentName = it.next();
-                    pw.append(componentName.toShortString());
-                    while (it.hasNext()) {
-                        componentName = it.next();
-                        pw.append(", ");
-                        pw.append(componentName.toShortString());
-                    }
-                }
-                pw.println("}]");
+                mUserStates.valueAt(i).dump(fd, pw, args);
+            }
+            if (mUiAutomationManager.isUiAutomationRunningLocked()) {
+                mUiAutomationManager.dumpUiAutomationService(fd, pw, args);
                 pw.println();
             }
             mA11yWindowManager.dump(fd, pw, args);
         }
     }
 
-    private void putSecureIntForUser(String key, int value, int userid) {
-        final long identity = Binder.clearCallingIdentity();
-        try {
-            Settings.Secure.putIntForUser(mContext.getContentResolver(), key, value, userid);
-        } finally {
-            Binder.restoreCallingIdentity(identity);
-        }
-    }
-
     //TODO remove after refactoring KeyEventDispatcherTest
     final class MainHandler extends Handler {
         public static final int MSG_SEND_KEY_EVENT_TO_INPUT_FILTER = 8;
@@ -2446,7 +2368,7 @@
 
     @Override
     public void onClientChangeLocked(boolean serviceInfoChanged) {
-        AccessibilityManagerService.UserState userState = getUserStateLocked(mCurrentUserId);
+        AccessibilityUserState userState = getUserStateLocked(mCurrentUserId);
         onUserStateChangedLocked(userState);
         if (serviceInfoChanged) {
             scheduleNotifyClientsOfServicesStateChangeLocked(userState);
@@ -2474,7 +2396,7 @@
             info.setCapabilities(AccessibilityServiceInfo.CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT);
             info.flags |= AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS;
             info.flags |= AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS;
-            final UserState userState;
+            final AccessibilityUserState userState;
             synchronized (mLock) {
                 userState = getCurrentUserStateLocked();
             }
@@ -2593,7 +2515,7 @@
                 if (mInputFilter != null) {
                     mInputFilter.onDisplayChanged();
                 }
-                UserState userState = getCurrentUserStateLocked();
+                AccessibilityUserState userState = getCurrentUserStateLocked();
                 if (displayId != Display.DEFAULT_DISPLAY) {
                     final List<AccessibilityServiceConnection> services = userState.mBoundServices;
                     for (int i = 0; i < services.size(); i++) {
@@ -2618,7 +2540,7 @@
                 if (mInputFilter != null) {
                     mInputFilter.onDisplayChanged();
                 }
-                UserState userState = getCurrentUserStateLocked();
+                AccessibilityUserState userState = getCurrentUserStateLocked();
                 if (displayId != Display.DEFAULT_DISPLAY) {
                     final List<AccessibilityServiceConnection> services = userState.mBoundServices;
                     for (int i = 0; i < services.size(); i++) {
@@ -2658,7 +2580,8 @@
         final String[] mPackageNames;
         int mLastSentRelevantEventTypes;
 
-        private Client(IAccessibilityManagerClient callback, int clientUid, UserState userState) {
+        private Client(IAccessibilityManagerClient callback, int clientUid,
+                AccessibilityUserState userState) {
             mCallback = callback;
             mPackageNames = mPackageManager.getPackagesForUid(clientUid);
             synchronized (mLock) {
@@ -2667,316 +2590,6 @@
         }
     }
 
-    public class UserState {
-        public final int mUserId;
-
-        // Non-transient state.
-
-        public final RemoteCallbackList<IAccessibilityManagerClient> mUserClients =
-                new RemoteCallbackList<>();
-
-        // Transient state.
-
-        public final ArrayList<AccessibilityServiceConnection> mBoundServices = new ArrayList<>();
-
-        public final Map<ComponentName, AccessibilityServiceConnection> mComponentNameToServiceMap =
-                new HashMap<>();
-
-        public final List<AccessibilityServiceInfo> mInstalledServices =
-                new ArrayList<>();
-
-        public final List<AccessibilityShortcutInfo> mInstalledShortcuts = new ArrayList<>();
-
-        private final Set<ComponentName> mBindingServices = new HashSet<>();
-
-        public final Set<ComponentName> mEnabledServices = new HashSet<>();
-
-        public final Set<ComponentName> mTouchExplorationGrantedServices =
-                new HashSet<>();
-
-        public ComponentName mServiceChangingSoftKeyboardMode;
-
-        public ComponentName mServiceToEnableWithShortcut;
-
-        public int mLastSentClientState = -1;
-        public int mNonInteractiveUiTimeout = 0;
-        public int mInteractiveUiTimeout = 0;
-
-        private int mSoftKeyboardShowMode = 0;
-
-        public boolean mIsNavBarMagnificationAssignedToAccessibilityButton;
-        public ComponentName mServiceAssignedToAccessibilityButton;
-
-        public boolean mIsTouchExplorationEnabled;
-        public boolean mIsTextHighContrastEnabled;
-        public boolean mIsDisplayMagnificationEnabled;
-        public boolean mIsNavBarMagnificationEnabled;
-        public boolean mIsAutoclickEnabled;
-        public boolean mIsPerformGesturesEnabled;
-        public boolean mIsFilterKeyEventsEnabled;
-        public int mUserNonInteractiveUiTimeout;
-        public int mUserInteractiveUiTimeout;
-
-        private boolean mBindInstantServiceAllowed;
-
-        public UserState(int userId) {
-            mUserId = userId;
-        }
-
-        public int getClientState() {
-            int clientState = 0;
-            final boolean a11yEnabled = (mUiAutomationManager.isUiAutomationRunningLocked()
-                    || isHandlingAccessibilityEvents());
-            if (a11yEnabled) {
-                clientState |= AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED;
-            }
-            // Touch exploration relies on enabled accessibility.
-            if (a11yEnabled && mIsTouchExplorationEnabled) {
-                clientState |= AccessibilityManager.STATE_FLAG_TOUCH_EXPLORATION_ENABLED;
-            }
-            if (mIsTextHighContrastEnabled) {
-                clientState |= AccessibilityManager.STATE_FLAG_HIGH_TEXT_CONTRAST_ENABLED;
-            }
-            return clientState;
-        }
-
-        public boolean isHandlingAccessibilityEvents() {
-            return !mBoundServices.isEmpty() || !mBindingServices.isEmpty();
-        }
-
-        public void onSwitchToAnotherUserLocked() {
-            // Unbind all services.
-            unbindAllServicesLocked(this);
-
-            // Clear service management state.
-            mBoundServices.clear();
-            mBindingServices.clear();
-
-            // Clear event management state.
-            mLastSentClientState = -1;
-
-            // clear UI timeout
-            mNonInteractiveUiTimeout = 0;
-            mInteractiveUiTimeout = 0;
-
-            // Clear state persisted in settings.
-            mEnabledServices.clear();
-            mTouchExplorationGrantedServices.clear();
-            mIsTouchExplorationEnabled = false;
-            mIsDisplayMagnificationEnabled = false;
-            mIsNavBarMagnificationEnabled = false;
-            mServiceAssignedToAccessibilityButton = null;
-            mIsNavBarMagnificationAssignedToAccessibilityButton = false;
-            mIsAutoclickEnabled = false;
-            mUserNonInteractiveUiTimeout = 0;
-            mUserInteractiveUiTimeout = 0;
-        }
-
-        public void addServiceLocked(AccessibilityServiceConnection serviceConnection) {
-            if (!mBoundServices.contains(serviceConnection)) {
-                serviceConnection.onAdded();
-                mBoundServices.add(serviceConnection);
-                mComponentNameToServiceMap.put(serviceConnection.mComponentName, serviceConnection);
-                scheduleNotifyClientsOfServicesStateChangeLocked(this);
-            }
-        }
-
-        /**
-         * Removes a service.
-         * There are three states to a service here: off, bound, and binding.
-         * This stops tracking the service as bound.
-         *
-         * @param serviceConnection The service.
-         */
-        public void removeServiceLocked(AccessibilityServiceConnection serviceConnection) {
-            mBoundServices.remove(serviceConnection);
-            serviceConnection.onRemoved();
-            if ((mServiceChangingSoftKeyboardMode != null)
-                    && (mServiceChangingSoftKeyboardMode.equals(
-                            serviceConnection.getServiceInfo().getComponentName()))) {
-                setSoftKeyboardModeLocked(SHOW_MODE_AUTO, null);
-            }
-            // It may be possible to bind a service twice, which confuses the map. Rebuild the map
-            // to make sure we can still reach a service
-            mComponentNameToServiceMap.clear();
-            for (int i = 0; i < mBoundServices.size(); i++) {
-                AccessibilityServiceConnection boundClient = mBoundServices.get(i);
-                mComponentNameToServiceMap.put(boundClient.mComponentName, boundClient);
-            }
-            scheduleNotifyClientsOfServicesStateChangeLocked(this);
-        }
-
-        /**
-         * Make sure a services disconnected but still 'on' state is reflected in UserState
-         * There are three states to a service here: off, bound, and binding.
-         * This drops a service from a bound state, to the binding state.
-         * The binding state describes the situation where a service is on, but not bound.
-         *
-         * @param serviceConnection The service.
-         */
-        public void serviceDisconnectedLocked(AccessibilityServiceConnection serviceConnection) {
-            removeServiceLocked(serviceConnection);
-            mBindingServices.add(serviceConnection.getComponentName());
-        }
-
-        public Set<ComponentName> getBindingServicesLocked() {
-            return mBindingServices;
-        }
-
-        /**
-         * Returns enabled service list.
-         */
-        public Set<ComponentName> getEnabledServicesLocked() {
-            return mEnabledServices;
-        }
-
-        public int getSoftKeyboardShowMode() {
-            return mSoftKeyboardShowMode;
-        }
-
-        /**
-         * Set the soft keyboard mode. This mode is a bit odd, as it spans multiple settings.
-         * The ACCESSIBILITY_SOFT_KEYBOARD_MODE setting can be checked by the rest of the system
-         * to see if it should suppress showing the IME. The SHOW_IME_WITH_HARD_KEYBOARD setting
-         * setting can be changed by the user, and prevents the system from suppressing the soft
-         * keyboard when the hard keyboard is connected. The hard keyboard setting needs to defer
-         * to the user's preference, if they have supplied one.
-         *
-         * @param newMode The new mode
-         * @param requester The service requesting the change, so we can undo it when the
-         *                  service stops. Set to null if something other than a service is forcing
-         *                  the change.
-         *
-         * @return Whether or not the soft keyboard mode equals the new mode after the call
-         */
-        public boolean setSoftKeyboardModeLocked(int newMode, @Nullable ComponentName requester) {
-            if ((newMode != SHOW_MODE_AUTO) && (newMode != SHOW_MODE_HIDDEN)
-                    && (newMode != SHOW_MODE_IGNORE_HARD_KEYBOARD))
-            {
-                Slog.w(LOG_TAG, "Invalid soft keyboard mode");
-                return false;
-            }
-            if (mSoftKeyboardShowMode == newMode) return true;
-
-            if (newMode == SHOW_MODE_IGNORE_HARD_KEYBOARD) {
-                if (hasUserOverriddenHardKeyboardSettingLocked()) {
-                    // The user has specified a default for this setting
-                    return false;
-                }
-                // Save the original value. But don't do this if the value in settings is already
-                // the new mode. That happens when we start up after a reboot, and we don't want
-                // to overwrite the value we had from when we first started controlling the setting.
-                if (getSoftKeyboardValueFromSettings() != SHOW_MODE_IGNORE_HARD_KEYBOARD) {
-                    setOriginalHardKeyboardValue(
-                            Settings.Secure.getInt(mContext.getContentResolver(),
-                                    Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 0) != 0);
-                }
-                putSecureIntForUser(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 1, mUserId);
-            } else if (mSoftKeyboardShowMode == SHOW_MODE_IGNORE_HARD_KEYBOARD) {
-                putSecureIntForUser(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD,
-                        getOriginalHardKeyboardValue() ? 1 : 0, mUserId);
-            }
-
-            saveSoftKeyboardValueToSettings(newMode);
-            mSoftKeyboardShowMode = newMode;
-            mServiceChangingSoftKeyboardMode = requester;
-            notifySoftKeyboardShowModeChangedLocked(mSoftKeyboardShowMode);
-            return true;
-        }
-
-        /**
-         * If the settings are inconsistent with the internal state, make the internal state
-         * match the settings.
-         */
-        public void reconcileSoftKeyboardModeWithSettingsLocked() {
-            final ContentResolver cr = mContext.getContentResolver();
-            final boolean showWithHardKeyboardSettings =
-                    Settings.Secure.getInt(cr, Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 0) != 0;
-            if (mSoftKeyboardShowMode == SHOW_MODE_IGNORE_HARD_KEYBOARD) {
-                if (!showWithHardKeyboardSettings) {
-                    // The user has overridden the setting. Respect that and prevent further changes
-                    // to this behavior.
-                    setSoftKeyboardModeLocked(SHOW_MODE_AUTO, null);
-                    setUserOverridesHardKeyboardSettingLocked();
-                }
-            }
-
-            // If the setting and the internal state are out of sync, set both to default
-            if (getSoftKeyboardValueFromSettings() != mSoftKeyboardShowMode)
-            {
-                Slog.e(LOG_TAG,
-                        "Show IME setting inconsistent with internal state. Overwriting");
-                setSoftKeyboardModeLocked(SHOW_MODE_AUTO, null);
-                putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
-                        SHOW_MODE_AUTO, mUserId);
-            }
-        }
-
-        private void setUserOverridesHardKeyboardSettingLocked() {
-            final int softKeyboardSetting = Settings.Secure.getInt(mContext.getContentResolver(),
-                    Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, 0);
-            putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
-                    softKeyboardSetting | SHOW_MODE_HARD_KEYBOARD_OVERRIDDEN,
-                    mUserId);
-        }
-
-        private boolean hasUserOverriddenHardKeyboardSettingLocked() {
-            final int softKeyboardSetting = Settings.Secure.getInt(mContext.getContentResolver(),
-                    Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, 0);
-            return (softKeyboardSetting & SHOW_MODE_HARD_KEYBOARD_OVERRIDDEN)
-                    != 0;
-        }
-
-        private void setOriginalHardKeyboardValue(boolean originalHardKeyboardValue) {
-            final int oldSoftKeyboardSetting = Settings.Secure.getInt(mContext.getContentResolver(),
-                    Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, 0);
-            final int newSoftKeyboardSetting = oldSoftKeyboardSetting
-                    & (~SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE)
-                    | ((originalHardKeyboardValue) ? SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE : 0);
-            putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
-                    newSoftKeyboardSetting, mUserId);
-        }
-
-        private void saveSoftKeyboardValueToSettings(int softKeyboardShowMode) {
-            final int oldSoftKeyboardSetting = Settings.Secure.getInt(mContext.getContentResolver(),
-                    Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, 0);
-            final int newSoftKeyboardSetting = oldSoftKeyboardSetting & (~SHOW_MODE_MASK)
-                    | softKeyboardShowMode;
-            putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
-                    newSoftKeyboardSetting, mUserId);
-        }
-
-        private int getSoftKeyboardValueFromSettings() {
-            return Settings.Secure.getInt(mContext.getContentResolver(),
-                    Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
-                    SHOW_MODE_AUTO) & SHOW_MODE_MASK;
-        }
-
-        private boolean getOriginalHardKeyboardValue() {
-            return (Settings.Secure.getInt(mContext.getContentResolver(),
-                    Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, 0)
-                    & SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE) != 0;
-        }
-
-        public boolean getBindInstantServiceAllowed() {
-            synchronized (mLock) {
-                return mBindInstantServiceAllowed;
-            }
-        }
-
-        public void setBindInstantServiceAllowed(boolean allowed) {
-            synchronized (mLock) {
-                mContext.enforceCallingOrSelfPermission(
-                        Manifest.permission.MANAGE_BIND_INSTANT_SERVICE,
-                        "setBindInstantServiceAllowed");
-                if (allowed) {
-                    mBindInstantServiceAllowed = allowed;
-                    onUserStateChangedLocked(this);
-                }
-            }
-        }
-    }
-
     private final class AccessibilityContentObserver extends ContentObserver {
 
         private final Uri mTouchExplorationEnabledUri = Settings.Secure.getUriFor(
@@ -3057,7 +2670,7 @@
             synchronized (mLock) {
                 // Profiles share the accessibility state of the parent. Therefore,
                 // we are checking for changes only the parent settings.
-                UserState userState = getCurrentUserStateLocked();
+                AccessibilityUserState userState = getCurrentUserStateLocked();
 
                 if (mTouchExplorationEnabledUri.equals(uri)) {
                     if (readTouchExplorationEnabledSettingLocked(userState)) {
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
index e7f3ccc..d154060 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java
@@ -35,7 +35,6 @@
 import android.util.Slog;
 import android.view.Display;
 
-import com.android.server.accessibility.AccessibilityManagerService.UserState;
 import com.android.server.wm.WindowManagerInternal;
 
 import java.lang.ref.WeakReference;
@@ -52,13 +51,13 @@
 class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnection {
     private static final String LOG_TAG = "AccessibilityServiceConnection";
     /*
-     Holding a weak reference so there isn't a loop of references. UserState keeps lists of bound
-     and binding services. These are freed on user changes, but just in case it somehow gets lost
-     the weak reference will let the memory get GCed.
+     Holding a weak reference so there isn't a loop of references. AccessibilityUserState keeps
+     lists of bound and binding services. These are freed on user changes, but just in case it
+     somehow gets lost the weak reference will let the memory get GCed.
 
      Having the reference be null when being called is a very bad sign, but we check the condition.
     */
-    final WeakReference<UserState> mUserStateWeakReference;
+    final WeakReference<AccessibilityUserState> mUserStateWeakReference;
     final Intent mIntent;
 
     private final Handler mMainHandler;
@@ -66,7 +65,7 @@
     private boolean mWasConnectedAndDied;
 
 
-    public AccessibilityServiceConnection(UserState userState, Context context,
+    AccessibilityServiceConnection(AccessibilityUserState userState, Context context,
             ComponentName componentName,
             AccessibilityServiceInfo accessibilityServiceInfo, int id, Handler mainHandler,
             Object lock, AccessibilitySecurityPolicy securityPolicy, SystemSupport systemSupport,
@@ -74,7 +73,7 @@
             SystemActionPerformer systemActionPerfomer, AccessibilityWindowManager awm) {
         super(context, componentName, accessibilityServiceInfo, id, mainHandler, lock,
                 securityPolicy, systemSupport, windowManagerInternal, systemActionPerfomer, awm);
-        mUserStateWeakReference = new WeakReference<UserState>(userState);
+        mUserStateWeakReference = new WeakReference<AccessibilityUserState>(userState);
         mIntent = new Intent().setComponent(mComponentName);
         mMainHandler = mainHandler;
         mIntent.putExtra(Intent.EXTRA_CLIENT_LABEL,
@@ -89,13 +88,13 @@
     }
 
     public void bindLocked() {
-        UserState userState = mUserStateWeakReference.get();
+        AccessibilityUserState userState = mUserStateWeakReference.get();
         if (userState == null) return;
         final long identity = Binder.clearCallingIdentity();
         try {
             int flags = Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE
                     | Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS;
-            if (userState.getBindInstantServiceAllowed()) {
+            if (userState.getBindInstantServiceAllowedLocked()) {
                 flags |= Context.BIND_ALLOW_INSTANT;
             }
             if (mService == null && mContext.bindServiceAsUser(
@@ -109,7 +108,7 @@
 
     public void unbindLocked() {
         mContext.unbindService(this);
-        UserState userState = mUserStateWeakReference.get();
+        AccessibilityUserState userState = mUserStateWeakReference.get();
         if (userState == null) return;
         userState.removeServiceLocked(this);
         mSystemSupport.getMagnificationController().resetAllIfNeeded(mId);
@@ -123,7 +122,7 @@
     @Override
     public void disableSelf() {
         synchronized (mLock) {
-            UserState userState = mUserStateWeakReference.get();
+            AccessibilityUserState userState = mUserStateWeakReference.get();
             if (userState == null) return;
             if (userState.getEnabledServicesLocked().remove(mComponentName)) {
                 final long identity = Binder.clearCallingIdentity();
@@ -156,7 +155,7 @@
                 }
             }
             mServiceInterface = IAccessibilityServiceClient.Stub.asInterface(service);
-            UserState userState = mUserStateWeakReference.get();
+            AccessibilityUserState userState = mUserStateWeakReference.get();
             if (userState == null) return;
             userState.addServiceLocked(this);
             mSystemSupport.onClientChangeLocked(false);
@@ -177,7 +176,7 @@
     private void initializeService() {
         IAccessibilityServiceClient serviceInterface = null;
         synchronized (mLock) {
-            UserState userState = mUserStateWeakReference.get();
+            AccessibilityUserState userState = mUserStateWeakReference.get();
             if (userState == null) return;
             Set<ComponentName> bindingServices = userState.getBindingServicesLocked();
             if (bindingServices.contains(mComponentName) || mWasConnectedAndDied) {
@@ -240,7 +239,7 @@
             if (!hasRightsToCurrentUserLocked()) {
                 return false;
             }
-            final UserState userState = mUserStateWeakReference.get();
+            final AccessibilityUserState userState = mUserStateWeakReference.get();
             if (userState == null) return false;
             return userState.setSoftKeyboardModeLocked(showMode, mComponentName);
         }
@@ -248,8 +247,8 @@
 
     @Override
     public int getSoftKeyboardShowMode() {
-        final UserState userState = mUserStateWeakReference.get();
-        return (userState != null) ? userState.getSoftKeyboardShowMode() : 0;
+        final AccessibilityUserState userState = mUserStateWeakReference.get();
+        return (userState != null) ? userState.getSoftKeyboardShowModeLocked() : 0;
     }
 
     @Override
@@ -258,7 +257,7 @@
             if (!hasRightsToCurrentUserLocked()) {
                 return false;
             }
-            UserState userState = mUserStateWeakReference.get();
+            AccessibilityUserState userState = mUserStateWeakReference.get();
             return (userState != null) && isAccessibilityButtonAvailableLocked(userState);
         }
     }
@@ -273,7 +272,7 @@
                 return;
             }
             mWasConnectedAndDied = true;
-            UserState userState = mUserStateWeakReference.get();
+            AccessibilityUserState userState = mUserStateWeakReference.get();
             if (userState != null) {
                 userState.serviceDisconnectedLocked(this);
             }
@@ -283,7 +282,7 @@
         }
     }
 
-    public boolean isAccessibilityButtonAvailableLocked(UserState userState) {
+    public boolean isAccessibilityButtonAvailableLocked(AccessibilityUserState userState) {
         // If the service does not request the accessibility button, it isn't available
         if (!mRequestAccessibilityButton) {
             return false;
@@ -295,8 +294,8 @@
         }
 
         // If magnification is on and assigned to the accessibility button, services cannot be
-        if (userState.mIsNavBarMagnificationEnabled
-                && userState.mIsNavBarMagnificationAssignedToAccessibilityButton) {
+        if (userState.isNavBarMagnificationEnabledLocked()
+                && userState.isNavBarMagnificationAssignedToAccessibilityButtonLocked()) {
             return false;
         }
 
@@ -314,13 +313,14 @@
             return true;
         } else {
             // With more than one active service, we derive the target from the user's settings
-            if (userState.mServiceAssignedToAccessibilityButton == null) {
+            if (userState.getServiceAssignedToAccessibilityButtonLocked() == null) {
                 // If the user has not made an assignment, we treat the button as available to
                 // all services until the user interacts with the button to make an assignment
                 return true;
             } else {
                 // If an assignment was made, it defines availability
-                return mComponentName.equals(userState.mServiceAssignedToAccessibilityButton);
+                return mComponentName.equals(
+                        userState.getServiceAssignedToAccessibilityButtonLocked());
             }
         }
     }
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
new file mode 100644
index 0000000..69f1e0e
--- /dev/null
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java
@@ -0,0 +1,573 @@
+/*
+ * 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.accessibility;
+
+import static android.accessibilityservice.AccessibilityService.SHOW_MODE_AUTO;
+import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE;
+import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HARD_KEYBOARD_OVERRIDDEN;
+import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HIDDEN;
+import static android.accessibilityservice.AccessibilityService.SHOW_MODE_IGNORE_HARD_KEYBOARD;
+import static android.accessibilityservice.AccessibilityService.SHOW_MODE_MASK;
+
+import android.accessibilityservice.AccessibilityService.SoftKeyboardShowMode;
+import android.accessibilityservice.AccessibilityServiceInfo;
+import android.accessibilityservice.AccessibilityShortcutInfo;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.Binder;
+import android.os.RemoteCallbackList;
+import android.provider.Settings;
+import android.util.Slog;
+import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.IAccessibilityManagerClient;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Class that hold states and settings per user and share between
+ * {@link AccessibilityManagerService} and {@link AccessibilityServiceConnection}.
+ */
+class AccessibilityUserState {
+    private static final String LOG_TAG = AccessibilityUserState.class.getSimpleName();
+
+    final int mUserId;
+
+    // Non-transient state.
+
+    final RemoteCallbackList<IAccessibilityManagerClient> mUserClients = new RemoteCallbackList<>();
+
+    // Transient state.
+
+    final ArrayList<AccessibilityServiceConnection> mBoundServices = new ArrayList<>();
+
+    final Map<ComponentName, AccessibilityServiceConnection> mComponentNameToServiceMap =
+            new HashMap<>();
+
+    final List<AccessibilityServiceInfo> mInstalledServices = new ArrayList<>();
+
+    final List<AccessibilityShortcutInfo> mInstalledShortcuts = new ArrayList<>();
+
+    final Set<ComponentName> mBindingServices = new HashSet<>();
+
+    final Set<ComponentName> mEnabledServices = new HashSet<>();
+
+    final Set<ComponentName> mTouchExplorationGrantedServices = new HashSet<>();
+
+    private final ServiceInfoChangeListener mServiceInfoChangeListener;
+
+    private ComponentName mServiceAssignedToAccessibilityButton;
+
+    private ComponentName mServiceChangingSoftKeyboardMode;
+
+    private ComponentName mServiceToEnableWithShortcut;
+
+    private boolean mBindInstantServiceAllowed;
+    private boolean mIsAutoclickEnabled;
+    private boolean mIsDisplayMagnificationEnabled;
+    private boolean mIsFilterKeyEventsEnabled;
+    private boolean mIsNavBarMagnificationAssignedToAccessibilityButton;
+    private boolean mIsNavBarMagnificationEnabled;
+    private boolean mIsPerformGesturesEnabled;
+    private boolean mIsTextHighContrastEnabled;
+    private boolean mIsTouchExplorationEnabled;
+    private int mUserInteractiveUiTimeout;
+    private int mUserNonInteractiveUiTimeout;
+    private int mNonInteractiveUiTimeout = 0;
+    private int mInteractiveUiTimeout = 0;
+    private int mLastSentClientState = -1;
+
+    private Context mContext;
+
+    @SoftKeyboardShowMode
+    private int mSoftKeyboardShowMode = SHOW_MODE_AUTO;
+
+    interface ServiceInfoChangeListener {
+        void onServiceInfoChangedLocked(AccessibilityUserState userState);
+    }
+
+    AccessibilityUserState(int userId, @NonNull Context context,
+            @NonNull ServiceInfoChangeListener serviceInfoChangeListener) {
+        mUserId = userId;
+        mContext = context;
+        mServiceInfoChangeListener = serviceInfoChangeListener;
+    }
+
+    boolean isHandlingAccessibilityEventsLocked() {
+        return !mBoundServices.isEmpty() || !mBindingServices.isEmpty();
+    }
+
+    void onSwitchToAnotherUserLocked() {
+        // Unbind all services.
+        unbindAllServicesLocked();
+
+        // Clear service management state.
+        mBoundServices.clear();
+        mBindingServices.clear();
+
+        // Clear event management state.
+        mLastSentClientState = -1;
+
+        // clear UI timeout
+        mNonInteractiveUiTimeout = 0;
+        mInteractiveUiTimeout = 0;
+
+        // Clear state persisted in settings.
+        mEnabledServices.clear();
+        mTouchExplorationGrantedServices.clear();
+        mIsTouchExplorationEnabled = false;
+        mIsDisplayMagnificationEnabled = false;
+        mIsNavBarMagnificationEnabled = false;
+        mServiceAssignedToAccessibilityButton = null;
+        mIsNavBarMagnificationAssignedToAccessibilityButton = false;
+        mIsAutoclickEnabled = false;
+        mUserNonInteractiveUiTimeout = 0;
+        mUserInteractiveUiTimeout = 0;
+    }
+
+    void addServiceLocked(AccessibilityServiceConnection serviceConnection) {
+        if (!mBoundServices.contains(serviceConnection)) {
+            serviceConnection.onAdded();
+            mBoundServices.add(serviceConnection);
+            mComponentNameToServiceMap.put(serviceConnection.getComponentName(), serviceConnection);
+            mServiceInfoChangeListener.onServiceInfoChangedLocked(this);
+        }
+    }
+
+    /**
+     * Removes a service.
+     * There are three states to a service here: off, bound, and binding.
+     * This stops tracking the service as bound.
+     *
+     * @param serviceConnection The service.
+     */
+    void removeServiceLocked(AccessibilityServiceConnection serviceConnection) {
+        mBoundServices.remove(serviceConnection);
+        serviceConnection.onRemoved();
+        if ((mServiceChangingSoftKeyboardMode != null)
+                && (mServiceChangingSoftKeyboardMode.equals(
+                serviceConnection.getServiceInfo().getComponentName()))) {
+            setSoftKeyboardModeLocked(SHOW_MODE_AUTO, null);
+        }
+        // It may be possible to bind a service twice, which confuses the map. Rebuild the map
+        // to make sure we can still reach a service
+        mComponentNameToServiceMap.clear();
+        for (int i = 0; i < mBoundServices.size(); i++) {
+            AccessibilityServiceConnection boundClient = mBoundServices.get(i);
+            mComponentNameToServiceMap.put(boundClient.getComponentName(), boundClient);
+        }
+        mServiceInfoChangeListener.onServiceInfoChangedLocked(this);
+    }
+
+    /**
+     * Make sure a services disconnected but still 'on' state is reflected in AccessibilityUserState
+     * There are three states to a service here: off, bound, and binding.
+     * This drops a service from a bound state, to the binding state.
+     * The binding state describes the situation where a service is on, but not bound.
+     *
+     * @param serviceConnection The service.
+     */
+    void serviceDisconnectedLocked(AccessibilityServiceConnection serviceConnection) {
+        removeServiceLocked(serviceConnection);
+        mBindingServices.add(serviceConnection.getComponentName());
+    }
+
+    /**
+     * Set the soft keyboard mode. This mode is a bit odd, as it spans multiple settings.
+     * The ACCESSIBILITY_SOFT_KEYBOARD_MODE setting can be checked by the rest of the system
+     * to see if it should suppress showing the IME. The SHOW_IME_WITH_HARD_KEYBOARD setting
+     * setting can be changed by the user, and prevents the system from suppressing the soft
+     * keyboard when the hard keyboard is connected. The hard keyboard setting needs to defer
+     * to the user's preference, if they have supplied one.
+     *
+     * @param newMode The new mode
+     * @param requester The service requesting the change, so we can undo it when the
+     *                  service stops. Set to null if something other than a service is forcing
+     *                  the change.
+     *
+     * @return Whether or not the soft keyboard mode equals the new mode after the call
+     */
+    boolean setSoftKeyboardModeLocked(@SoftKeyboardShowMode int newMode,
+            @Nullable ComponentName requester) {
+        if ((newMode != SHOW_MODE_AUTO)
+                && (newMode != SHOW_MODE_HIDDEN)
+                && (newMode != SHOW_MODE_IGNORE_HARD_KEYBOARD)) {
+            Slog.w(LOG_TAG, "Invalid soft keyboard mode");
+            return false;
+        }
+        if (mSoftKeyboardShowMode == newMode) {
+            return true;
+        }
+
+        if (newMode == SHOW_MODE_IGNORE_HARD_KEYBOARD) {
+            if (hasUserOverriddenHardKeyboardSetting()) {
+                // The user has specified a default for this setting
+                return false;
+            }
+            // Save the original value. But don't do this if the value in settings is already
+            // the new mode. That happens when we start up after a reboot, and we don't want
+            // to overwrite the value we had from when we first started controlling the setting.
+            if (getSoftKeyboardValueFromSettings() != SHOW_MODE_IGNORE_HARD_KEYBOARD) {
+                setOriginalHardKeyboardValue(getSecureIntForUser(
+                        Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 0, mUserId) != 0);
+            }
+            putSecureIntForUser(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 1, mUserId);
+        } else if (mSoftKeyboardShowMode == SHOW_MODE_IGNORE_HARD_KEYBOARD) {
+            putSecureIntForUser(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD,
+                    getOriginalHardKeyboardValue() ? 1 : 0, mUserId);
+        }
+
+        saveSoftKeyboardValueToSettings(newMode);
+        mSoftKeyboardShowMode = newMode;
+        mServiceChangingSoftKeyboardMode = requester;
+        for (int i = mBoundServices.size() - 1; i >= 0; i--) {
+            final AccessibilityServiceConnection service = mBoundServices.get(i);
+            service.notifySoftKeyboardShowModeChangedLocked(mSoftKeyboardShowMode);
+        }
+        return true;
+    }
+
+    @SoftKeyboardShowMode
+    int getSoftKeyboardShowModeLocked() {
+        return mSoftKeyboardShowMode;
+    }
+
+    /**
+     * If the settings are inconsistent with the internal state, make the internal state
+     * match the settings.
+     */
+    void reconcileSoftKeyboardModeWithSettingsLocked() {
+        final boolean showWithHardKeyboardSettings =
+                getSecureIntForUser(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 0, mUserId) != 0;
+        if (mSoftKeyboardShowMode == SHOW_MODE_IGNORE_HARD_KEYBOARD) {
+            if (!showWithHardKeyboardSettings) {
+                // The user has overridden the setting. Respect that and prevent further changes
+                // to this behavior.
+                setSoftKeyboardModeLocked(SHOW_MODE_AUTO, null);
+                setUserOverridesHardKeyboardSetting();
+            }
+        }
+
+        // If the setting and the internal state are out of sync, set both to default
+        if (getSoftKeyboardValueFromSettings() != mSoftKeyboardShowMode) {
+            Slog.e(LOG_TAG, "Show IME setting inconsistent with internal state. Overwriting");
+            setSoftKeyboardModeLocked(SHOW_MODE_AUTO, null);
+            putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
+                    SHOW_MODE_AUTO, mUserId);
+        }
+    }
+
+    boolean getBindInstantServiceAllowedLocked() {
+        return mBindInstantServiceAllowed;
+    }
+
+    /* Need to have a permission check on callee */
+    void setBindInstantServiceAllowedLocked(boolean allowed) {
+        mBindInstantServiceAllowed = allowed;
+    }
+
+    Set<ComponentName> getBindingServicesLocked() {
+        return mBindingServices;
+    }
+
+    /**
+     * Returns enabled service list.
+     */
+    Set<ComponentName> getEnabledServicesLocked() {
+        return mEnabledServices;
+    }
+
+    List<AccessibilityServiceConnection> getBoundServicesLocked() {
+        return mBoundServices;
+    }
+
+    int getClientStateLocked(boolean isUiAutomationRunning) {
+        int clientState = 0;
+        final boolean a11yEnabled = isUiAutomationRunning
+                || isHandlingAccessibilityEventsLocked();
+        if (a11yEnabled) {
+            clientState |= AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED;
+        }
+        // Touch exploration relies on enabled accessibility.
+        if (a11yEnabled && mIsTouchExplorationEnabled) {
+            clientState |= AccessibilityManager.STATE_FLAG_TOUCH_EXPLORATION_ENABLED;
+        }
+        if (mIsTextHighContrastEnabled) {
+            clientState |= AccessibilityManager.STATE_FLAG_HIGH_TEXT_CONTRAST_ENABLED;
+        }
+        return clientState;
+    }
+
+    private void setUserOverridesHardKeyboardSetting() {
+        final int softKeyboardSetting = getSecureIntForUser(
+                Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, SHOW_MODE_AUTO, mUserId);
+        putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
+                softKeyboardSetting | SHOW_MODE_HARD_KEYBOARD_OVERRIDDEN,
+                mUserId);
+    }
+
+    private boolean hasUserOverriddenHardKeyboardSetting() {
+        final int softKeyboardSetting = getSecureIntForUser(
+                Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, SHOW_MODE_AUTO, mUserId);
+        return (softKeyboardSetting & SHOW_MODE_HARD_KEYBOARD_OVERRIDDEN)
+                != 0;
+    }
+
+    private void setOriginalHardKeyboardValue(boolean originalHardKeyboardValue) {
+        final int oldSoftKeyboardSetting = getSecureIntForUser(
+                Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, SHOW_MODE_AUTO, mUserId);
+        final int newSoftKeyboardSetting = oldSoftKeyboardSetting
+                & (~SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE)
+                | ((originalHardKeyboardValue) ? SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE : 0);
+        putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
+                newSoftKeyboardSetting, mUserId);
+    }
+
+    private void saveSoftKeyboardValueToSettings(int softKeyboardShowMode) {
+        final int oldSoftKeyboardSetting = getSecureIntForUser(
+                Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, SHOW_MODE_AUTO, mUserId);
+        final int newSoftKeyboardSetting = oldSoftKeyboardSetting & (~SHOW_MODE_MASK)
+                | softKeyboardShowMode;
+        putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
+                newSoftKeyboardSetting, mUserId);
+    }
+
+    private int getSoftKeyboardValueFromSettings() {
+        return getSecureIntForUser(
+                Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, SHOW_MODE_AUTO, mUserId)
+                & SHOW_MODE_MASK;
+    }
+
+    private boolean getOriginalHardKeyboardValue() {
+        return (getSecureIntForUser(
+                Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, SHOW_MODE_AUTO, mUserId)
+                & SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE) != 0;
+    }
+
+    private void unbindAllServicesLocked() {
+        final List<AccessibilityServiceConnection> services = mBoundServices;
+        for (int count = services.size(); count > 0; count--) {
+            // When the service is unbound, it disappears from the list, so there's no need to
+            // keep track of the index
+            services.get(0).unbindLocked();
+        }
+    }
+
+    private int getSecureIntForUser(String key, int def, int userId) {
+        return Settings.Secure.getIntForUser(mContext.getContentResolver(), key, def, userId);
+    }
+
+    private void putSecureIntForUser(String key, int value, int userId) {
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            Settings.Secure.putIntForUser(mContext.getContentResolver(), key, value, userId);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        pw.append("User state[");
+        pw.println();
+        pw.append("     attributes:{id=").append(String.valueOf(mUserId));
+        pw.append(", touchExplorationEnabled=").append(String.valueOf(mIsTouchExplorationEnabled));
+        pw.append(", displayMagnificationEnabled=").append(String.valueOf(
+                mIsDisplayMagnificationEnabled));
+        pw.append(", navBarMagnificationEnabled=").append(String.valueOf(
+                mIsNavBarMagnificationEnabled));
+        pw.append(", autoclickEnabled=").append(String.valueOf(mIsAutoclickEnabled));
+        pw.append(", nonInteractiveUiTimeout=").append(String.valueOf(mNonInteractiveUiTimeout));
+        pw.append(", interactiveUiTimeout=").append(String.valueOf(mInteractiveUiTimeout));
+        pw.append(", installedServiceCount=").append(String.valueOf(mInstalledServices.size()));
+        pw.append("}");
+        pw.println();
+        pw.append("     Bound services:{");
+        final int serviceCount = mBoundServices.size();
+        for (int j = 0; j < serviceCount; j++) {
+            if (j > 0) {
+                pw.append(", ");
+                pw.println();
+                pw.append("                     ");
+            }
+            AccessibilityServiceConnection service = mBoundServices.get(j);
+            service.dump(fd, pw, args);
+        }
+        pw.println("}");
+        pw.append("     Enabled services:{");
+        Iterator<ComponentName> it = mEnabledServices.iterator();
+        if (it.hasNext()) {
+            ComponentName componentName = it.next();
+            pw.append(componentName.toShortString());
+            while (it.hasNext()) {
+                componentName = it.next();
+                pw.append(", ");
+                pw.append(componentName.toShortString());
+            }
+        }
+        pw.println("}");
+        pw.append("     Binding services:{");
+        it = mBindingServices.iterator();
+        if (it.hasNext()) {
+            ComponentName componentName = it.next();
+            pw.append(componentName.toShortString());
+            while (it.hasNext()) {
+                componentName = it.next();
+                pw.append(", ");
+                pw.append(componentName.toShortString());
+            }
+        }
+        pw.println("}]");
+    }
+
+    public boolean isAutoclickEnabledLocked() {
+        return mIsAutoclickEnabled;
+    }
+
+    public void setAutoclickEnabledLocked(boolean enabled) {
+        mIsAutoclickEnabled = enabled;
+    }
+
+    public boolean isDisplayMagnificationEnabledLocked() {
+        return mIsDisplayMagnificationEnabled;
+    }
+
+    public void setDisplayMagnificationEnabledLocked(boolean enabled) {
+        mIsDisplayMagnificationEnabled = enabled;
+    }
+
+    public boolean isFilterKeyEventsEnabledLocked() {
+        return mIsFilterKeyEventsEnabled;
+    }
+
+    public void setFilterKeyEventsEnabledLocked(boolean enabled) {
+        mIsFilterKeyEventsEnabled = enabled;
+    }
+
+    public int getInteractiveUiTimeoutLocked() {
+        return mInteractiveUiTimeout;
+    }
+
+    public void setInteractiveUiTimeoutLocked(int timeout) {
+        mInteractiveUiTimeout = timeout;
+    }
+
+    public int getLastSentClientStateLocked() {
+        return mLastSentClientState;
+    }
+
+    public void setLastSentClientStateLocked(int state) {
+        mLastSentClientState = state;
+    }
+
+    public boolean isNavBarMagnificationAssignedToAccessibilityButtonLocked() {
+        return mIsNavBarMagnificationAssignedToAccessibilityButton;
+    }
+
+    public void setNavBarMagnificationAssignedToAccessibilityButtonLocked(boolean assigned) {
+        mIsNavBarMagnificationAssignedToAccessibilityButton = assigned;
+    }
+
+    public boolean isNavBarMagnificationEnabledLocked() {
+        return mIsNavBarMagnificationEnabled;
+    }
+
+    public void setNavBarMagnificationEnabledLocked(boolean enabled) {
+        mIsNavBarMagnificationEnabled = enabled;
+    }
+
+    public int getNonInteractiveUiTimeoutLocked() {
+        return mNonInteractiveUiTimeout;
+    }
+
+    public void setNonInteractiveUiTimeoutLocked(int timeout) {
+        mNonInteractiveUiTimeout = timeout;
+    }
+
+    public boolean isPerformGesturesEnabledLocked() {
+        return mIsPerformGesturesEnabled;
+    }
+
+    public void setPerformGesturesEnabledLocked(boolean enabled) {
+        mIsPerformGesturesEnabled = enabled;
+    }
+
+    public ComponentName getServiceAssignedToAccessibilityButtonLocked() {
+        return mServiceAssignedToAccessibilityButton;
+    }
+
+    public void setServiceAssignedToAccessibilityButtonLocked(ComponentName componentName) {
+        mServiceAssignedToAccessibilityButton = componentName;
+    }
+
+    public ComponentName getServiceChangingSoftKeyboardModeLocked() {
+        return mServiceChangingSoftKeyboardMode;
+    }
+
+    public void setServiceChangingSoftKeyboardModeLocked(
+            ComponentName serviceChangingSoftKeyboardMode) {
+        mServiceChangingSoftKeyboardMode = serviceChangingSoftKeyboardMode;
+    }
+
+    public ComponentName getServiceToEnableWithShortcutLocked() {
+        return mServiceToEnableWithShortcut;
+    }
+
+    public void setServiceToEnableWithShortcutLocked(ComponentName componentName) {
+        mServiceToEnableWithShortcut = componentName;
+    }
+
+    public boolean isTextHighContrastEnabledLocked() {
+        return mIsTextHighContrastEnabled;
+    }
+
+    public void setTextHighContrastEnabledLocked(boolean enabled) {
+        mIsTextHighContrastEnabled = enabled;
+    }
+
+    public boolean isTouchExplorationEnabledLocked() {
+        return mIsTouchExplorationEnabled;
+    }
+
+    public void setTouchExplorationEnabledLocked(boolean enabled) {
+        mIsTouchExplorationEnabled = enabled;
+    }
+
+    public int getUserInteractiveUiTimeoutLocked() {
+        return mUserInteractiveUiTimeout;
+    }
+
+    public void setUserInteractiveUiTimeoutLocked(int timeout) {
+        mUserInteractiveUiTimeout = timeout;
+    }
+
+    public int getUserNonInteractiveUiTimeoutLocked() {
+        return mUserNonInteractiveUiTimeout;
+    }
+
+    public void setUserNonInteractiveUiTimeoutLocked(int timeout) {
+        mUserNonInteractiveUiTimeout = timeout;
+    }
+}
diff --git a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
index b35300c..7d129ea 100644
--- a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
@@ -286,7 +286,7 @@
         window.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
                 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
                 | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH);
-        window.addPrivateFlags(WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS);
+        window.addPrivateFlags(WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS);
         window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
         window.setGravity(Gravity.BOTTOM | Gravity.CENTER);
         window.setCloseOnTouchOutside(true);
diff --git a/services/backup/backuplib/java/com/android/server/backup/TransportManager.java b/services/backup/backuplib/java/com/android/server/backup/TransportManager.java
index de48f4b..30ce4cf 100644
--- a/services/backup/backuplib/java/com/android/server/backup/TransportManager.java
+++ b/services/backup/backuplib/java/com/android/server/backup/TransportManager.java
@@ -93,8 +93,7 @@
         mTransportWhitelist = Preconditions.checkNotNull(whitelist);
         mCurrentTransportName = selectedTransport;
         mTransportStats = new TransportStats();
-        mTransportClientManager = TransportClientManager.createEncryptingClientManager(mUserId,
-                context, mTransportStats);
+        mTransportClientManager = new TransportClientManager(mUserId, context, mTransportStats);
     }
 
     @VisibleForTesting
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index d6515f3..dc24cff 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -60,6 +60,7 @@
     public static final int PACKAGE_INCIDENT_REPORT_APPROVER = 10;
     public static final int PACKAGE_APP_PREDICTOR = 11;
     public static final int PACKAGE_TELEPHONY = 12;
+    public static final int PACKAGE_WIFI = 13;
     @IntDef(value = {
         PACKAGE_SYSTEM,
         PACKAGE_SETUP_WIZARD,
@@ -74,6 +75,7 @@
         PACKAGE_INCIDENT_REPORT_APPROVER,
         PACKAGE_APP_PREDICTOR,
         PACKAGE_TELEPHONY,
+        PACKAGE_WIFI,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface KnownPackage {}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index e0f60b4..0bb72cb 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -1149,7 +1149,6 @@
     private static NetworkCapabilities createDefaultNetworkCapabilitiesForUid(int uid) {
         final NetworkCapabilities netCap = new NetworkCapabilities();
         netCap.addCapability(NET_CAPABILITY_INTERNET);
-        netCap.addCapability(NET_CAPABILITY_NOT_RESTRICTED);
         netCap.removeCapability(NET_CAPABILITY_NOT_VPN);
         netCap.setSingleUid(uid);
         return netCap;
@@ -1159,7 +1158,6 @@
             int transportType, NetworkRequest.Type type) {
         final NetworkCapabilities netCap = new NetworkCapabilities();
         netCap.addCapability(NET_CAPABILITY_INTERNET);
-        netCap.addCapability(NET_CAPABILITY_NOT_RESTRICTED);
         if (transportType > -1) {
             netCap.addTransportType(transportType);
         }
diff --git a/services/core/java/com/android/server/GnssManagerService.java b/services/core/java/com/android/server/GnssManagerService.java
index b6e619e..44a8234 100644
--- a/services/core/java/com/android/server/GnssManagerService.java
+++ b/services/core/java/com/android/server/GnssManagerService.java
@@ -17,6 +17,7 @@
 package com.android.server;
 
 import android.Manifest;
+import android.annotation.NonNull;
 import android.app.ActivityManager;
 import android.app.AppOpsManager;
 import android.content.Context;
@@ -298,7 +299,8 @@
      * @param packageName name of requesting package
      * @return true if callback is successfully added, false otherwise
      */
-    public boolean addGnssBatchingCallback(IBatchedLocationCallback callback, String packageName) {
+    public boolean addGnssBatchingCallback(IBatchedLocationCallback callback, String packageName,
+            @NonNull String listenerIdentity) {
         mContext.enforceCallingPermission(
                 android.Manifest.permission.LOCATION_HARDWARE,
                 "Location Hardware permission not granted to access hardware batching");
@@ -316,7 +318,8 @@
         }
 
         CallerIdentity callerIdentity =
-                new CallerIdentity(Binder.getCallingUid(), Binder.getCallingPid(), packageName);
+                new CallerIdentity(Binder.getCallingUid(), Binder.getCallingPid(), packageName,
+                        listenerIdentity);
         synchronized (mGnssBatchingLock) {
             mGnssBatchingCallback = callback;
             mGnssBatchingDeathCallback =
@@ -494,7 +497,7 @@
     private <TListener extends IInterface> boolean addGnssDataListenerLocked(
             TListener listener,
             String packageName,
-            String listenerName,
+            @NonNull String listenerIdentifier,
             RemoteListenerHelper<TListener> gnssDataProvider,
             ArrayMap<IBinder,
                     LinkedListener<TListener>> gnssDataListeners,
@@ -513,10 +516,11 @@
         }
 
         CallerIdentity callerIdentity =
-                new CallerIdentity(Binder.getCallingUid(), Binder.getCallingPid(), packageName);
+                new CallerIdentity(Binder.getCallingUid(), Binder.getCallingPid(), packageName,
+                        listenerIdentifier);
         LinkedListener<TListener> linkedListener =
                 new LocationManagerServiceUtils.LinkedListener<>(
-                        listener, listenerName, callerIdentity, binderDeathCallback);
+                        listener, listenerIdentifier, callerIdentity, binderDeathCallback);
         IBinder binder = listener.asBinder();
         if (!linkedListener.linkToListenerDeathNotificationLocked(binder)) {
             return false;
@@ -606,7 +610,7 @@
             return addGnssDataListenerLocked(
                     listener,
                     packageName,
-                    "GnssStatusListener",
+                    "Gnss status",
                     mGnssStatusProvider,
                     mGnssStatusListeners,
                     this::unregisterGnssStatusCallback);
@@ -632,12 +636,13 @@
      * @return true if listener is successfully added, false otherwise
      */
     public boolean addGnssMeasurementsListener(
-            IGnssMeasurementsListener listener, String packageName) {
+            IGnssMeasurementsListener listener, String packageName,
+            @NonNull String listenerIdentifier) {
         synchronized (mGnssMeasurementsListeners) {
             return addGnssDataListenerLocked(
                     listener,
                     packageName,
-                    "GnssMeasurementsListener",
+                    listenerIdentifier,
                     mGnssMeasurementsProvider,
                     mGnssMeasurementsListeners,
                     this::removeGnssMeasurementsListener);
@@ -689,12 +694,13 @@
      * @return true if listener is successfully added, false otherwise
      */
     public boolean addGnssNavigationMessageListener(
-            IGnssNavigationMessageListener listener, String packageName) {
+            IGnssNavigationMessageListener listener, String packageName,
+            @NonNull String listenerIdentifier) {
         synchronized (mGnssNavigationMessageListeners) {
             return addGnssDataListenerLocked(
                     listener,
                     packageName,
-                    "GnssNavigationMessageListener",
+                    listenerIdentifier,
                     mGnssNavigationMessageProvider,
                     mGnssNavigationMessageListeners,
                     this::removeGnssNavigationMessageListener);
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index a6f4f6a..aa22feb 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -1223,8 +1223,10 @@
         PowerManager.WakeLock mWakeLock;
 
         private Receiver(ILocationListener listener, PendingIntent intent, int pid, int uid,
-                String packageName, WorkSource workSource, boolean hideFromAppOps) {
-            super(new CallerIdentity(uid, pid, packageName), "LocationListener");
+                String packageName, WorkSource workSource, boolean hideFromAppOps,
+                @NonNull String listenerIdentifier) {
+            super(new CallerIdentity(uid, pid, packageName, listenerIdentifier),
+                    "LocationListener");
             mListener = listener;
             mPendingIntent = intent;
             if (listener != null) {
@@ -1532,9 +1534,12 @@
     }
 
     @Override
-    public boolean addGnssBatchingCallback(IBatchedLocationCallback callback, String packageName) {
+    public boolean addGnssBatchingCallback(IBatchedLocationCallback callback, String packageName,
+            String listenerIdentifier) {
+        Preconditions.checkNotNull(listenerIdentifier);
+
         return mGnssManagerService == null ? false : mGnssManagerService.addGnssBatchingCallback(
-                callback, packageName);
+                callback, packageName, listenerIdentifier);
     }
 
     @Override
@@ -1696,11 +1701,12 @@
         }
     }
 
-    private boolean reportLocationAccessNoThrow(
-            int pid, int uid, String packageName, int allowedResolutionLevel) {
+    private boolean reportLocationAccessNoThrow(int pid, int uid, String packageName,
+            int allowedResolutionLevel, @Nullable String message) {
         int op = resolutionLevelToOp(allowedResolutionLevel);
         if (op >= 0) {
-            if (mAppOps.noteOpNoThrow(op, uid, packageName) != AppOpsManager.MODE_ALLOWED) {
+            if (mAppOps.noteOpNoThrow(op, uid, packageName, message)
+                    != AppOpsManager.MODE_ALLOWED) {
                 return false;
             }
         }
@@ -2133,12 +2139,13 @@
 
     @GuardedBy("mLock")
     private Receiver getReceiverLocked(ILocationListener listener, int pid, int uid,
-            String packageName, WorkSource workSource, boolean hideFromAppOps) {
+            String packageName, WorkSource workSource, boolean hideFromAppOps,
+            @NonNull String listenerIdentifier) {
         IBinder binder = listener.asBinder();
         Receiver receiver = mReceivers.get(binder);
         if (receiver == null) {
             receiver = new Receiver(listener, null, pid, uid, packageName, workSource,
-                    hideFromAppOps);
+                    hideFromAppOps, listenerIdentifier);
             if (!receiver.linkToListenerDeathNotificationLocked(
                     receiver.getListener().asBinder())) {
                 return null;
@@ -2150,11 +2157,11 @@
 
     @GuardedBy("mLock")
     private Receiver getReceiverLocked(PendingIntent intent, int pid, int uid, String packageName,
-            WorkSource workSource, boolean hideFromAppOps) {
+            WorkSource workSource, boolean hideFromAppOps, @NonNull String listenerIdentifier) {
         Receiver receiver = mReceivers.get(intent);
         if (receiver == null) {
             receiver = new Receiver(null, intent, pid, uid, packageName, workSource,
-                    hideFromAppOps);
+                    hideFromAppOps, listenerIdentifier);
             mReceivers.put(intent, receiver);
         }
         return receiver;
@@ -2216,7 +2223,9 @@
 
     @Override
     public void requestLocationUpdates(LocationRequest request, ILocationListener listener,
-            PendingIntent intent, String packageName) {
+            PendingIntent intent, String packageName, String listenerIdentifier) {
+        Preconditions.checkNotNull(listenerIdentifier);
+
         synchronized (mLock) {
             if (request == null) request = DEFAULT_LOCATION_REQUEST;
             checkPackageName(packageName);
@@ -2271,10 +2280,10 @@
                 Receiver receiver;
                 if (intent != null) {
                     receiver = getReceiverLocked(intent, pid, uid, packageName, workSource,
-                            hideFromAppOps);
+                            hideFromAppOps, listenerIdentifier);
                 } else {
                     receiver = getReceiverLocked(listener, pid, uid, packageName, workSource,
-                            hideFromAppOps);
+                            hideFromAppOps, listenerIdentifier);
                 }
                 requestLocationUpdatesLocked(sanitizedRequest, receiver, uid, packageName);
             } finally {
@@ -2343,9 +2352,9 @@
         synchronized (mLock) {
             Receiver receiver;
             if (intent != null) {
-                receiver = getReceiverLocked(intent, pid, uid, packageName, null, false);
+                receiver = getReceiverLocked(intent, pid, uid, packageName, null, false, "");
             } else {
-                receiver = getReceiverLocked(listener, pid, uid, packageName, null, false);
+                receiver = getReceiverLocked(listener, pid, uid, packageName, null, false, "");
             }
 
             long identity = Binder.clearCallingIdentity();
@@ -2464,8 +2473,8 @@
                 }
                 // Don't report location access if there is no last location to deliver.
                 if (lastLocation != null) {
-                    if (!reportLocationAccessNoThrow(
-                            pid, uid, packageName, allowedResolutionLevel)) {
+                    if (!reportLocationAccessNoThrow(pid, uid, packageName, allowedResolutionLevel,
+                            null)) {
                         if (D) {
                             Log.d(TAG, "not returning last loc for no op app: " + packageName);
                         }
@@ -2519,7 +2528,9 @@
 
     @Override
     public void requestGeofence(LocationRequest request, Geofence geofence, PendingIntent intent,
-            String packageName) {
+            String packageName, String listenerIdentifier) {
+        Preconditions.checkNotNull(listenerIdentifier);
+
         if (request == null) request = DEFAULT_LOCATION_REQUEST;
         int allowedResolutionLevel = getCallerAllowedResolutionLevel();
         checkResolutionLevelIsSufficientForGeofenceUse(allowedResolutionLevel);
@@ -2564,9 +2575,8 @@
                         mActivityManager.getPackageImportance(packageName));
             }
 
-            mGeofenceManager.addFence(sanitizedRequest, geofence, intent,
-                    allowedResolutionLevel,
-                    uid, packageName);
+            mGeofenceManager.addFence(sanitizedRequest, geofence, intent, allowedResolutionLevel,
+                    uid, packageName, listenerIdentifier);
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
@@ -2613,10 +2623,13 @@
     }
 
     @Override
-    public boolean addGnssMeasurementsListener(
-            IGnssMeasurementsListener listener, String packageName) {
+    public boolean addGnssMeasurementsListener(IGnssMeasurementsListener listener,
+            String packageName, String listenerIdentifier) {
+        Preconditions.checkNotNull(listenerIdentifier);
+
         return mGnssManagerService == null ? false
-                : mGnssManagerService.addGnssMeasurementsListener(listener, packageName);
+                : mGnssManagerService.addGnssMeasurementsListener(listener, packageName,
+                        listenerIdentifier);
     }
 
     @Override
@@ -2643,10 +2656,13 @@
     }
 
     @Override
-    public boolean addGnssNavigationMessageListener(
-            IGnssNavigationMessageListener listener, String packageName) {
+    public boolean addGnssNavigationMessageListener(IGnssNavigationMessageListener listener,
+            String packageName, String listenerIdentifier) {
+        Preconditions.checkNotNull(listenerIdentifier);
+
         return mGnssManagerService == null ? false
-                : mGnssManagerService.addGnssNavigationMessageListener(listener, packageName);
+                : mGnssManagerService.addGnssNavigationMessageListener(listener, packageName,
+                        listenerIdentifier);
     }
 
     @Override
@@ -2952,7 +2968,8 @@
                             receiver.mCallerIdentity.mPid,
                             receiver.mCallerIdentity.mUid,
                             receiver.mCallerIdentity.mPackageName,
-                            receiver.mAllowedResolutionLevel)) {
+                            receiver.mAllowedResolutionLevel,
+                            "Location sent to " + receiver.mCallerIdentity.mListenerIdentifier)) {
                         if (D) {
                             Log.d(TAG, "skipping loc update for no op app: "
                                     + receiver.mCallerIdentity.mPackageName);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 8c672a1..55cd933 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -900,7 +900,7 @@
 
         // The other observer methods are unused
         @Override
-        public void onIntentStarted(Intent intent) {
+        public void onIntentStarted(Intent intent, long timestampNs) {
         }
 
         @Override
@@ -912,7 +912,11 @@
         }
 
         @Override
-        public void onActivityLaunchFinished(byte[] finalActivity) {
+        public void onActivityLaunchFinished(byte[] finalActivity, long timestampNs) {
+        }
+
+        @Override
+        public void onReportFullyDrawn(byte[] finalActivity, long timestampNs) {
         }
     };
 
@@ -8658,7 +8662,7 @@
         lp.format = v.getBackground().getOpacity();
         lp.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                 | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
-        lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+        lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
         ((WindowManager)mContext.getSystemService(
                 Context.WINDOW_SERVICE)).addView(v, lp);
     }
diff --git a/services/core/java/com/android/server/am/AppErrorDialog.java b/services/core/java/com/android/server/am/AppErrorDialog.java
index a80a5b5..852c9b65 100644
--- a/services/core/java/com/android/server/am/AppErrorDialog.java
+++ b/services/core/java/com/android/server/am/AppErrorDialog.java
@@ -92,7 +92,7 @@
         WindowManager.LayoutParams attrs = getWindow().getAttributes();
         attrs.setTitle("Application Error: " + mProc.info.processName);
         attrs.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SYSTEM_ERROR
-                | WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+                | WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
         getWindow().setAttributes(attrs);
         if (mProc.isPersistent()) {
             getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ERROR);
diff --git a/services/core/java/com/android/server/am/AppNotRespondingDialog.java b/services/core/java/com/android/server/am/AppNotRespondingDialog.java
index cb76e2f..65d7e01 100644
--- a/services/core/java/com/android/server/am/AppNotRespondingDialog.java
+++ b/services/core/java/com/android/server/am/AppNotRespondingDialog.java
@@ -94,7 +94,7 @@
         WindowManager.LayoutParams attrs = getWindow().getAttributes();
         attrs.setTitle("Application Not Responding: " + mProc.info.processName);
         attrs.privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_SYSTEM_ERROR |
-                WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+                WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
         getWindow().setAttributes(attrs);
     }
 
diff --git a/services/core/java/com/android/server/am/UserSwitchingDialog.java b/services/core/java/com/android/server/am/UserSwitchingDialog.java
index 98f5557..bbf5772 100644
--- a/services/core/java/com/android/server/am/UserSwitchingDialog.java
+++ b/services/core/java/com/android/server/am/UserSwitchingDialog.java
@@ -78,7 +78,7 @@
 
         WindowManager.LayoutParams attrs = getWindow().getAttributes();
         attrs.privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_SYSTEM_ERROR |
-            WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+            WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
         getWindow().setAttributes(attrs);
     }
 
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 6c4cc2d..2ac6eb0 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -68,6 +68,7 @@
 import android.net.Uri;
 import android.os.AsyncTask;
 import android.os.Binder;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
@@ -1398,6 +1399,12 @@
     }
 
     private void updatePermissionRevokedCompat(int uid, int switchCode, int mode) {
+        PackageManagerInternal packageManagerInternal = LocalServices.getService(
+                PackageManagerInternal.class);
+        if (packageManagerInternal.getUidTargetSdkVersion(uid) >= Build.VERSION_CODES.M) {
+            return;
+        }
+
         PackageManager packageManager = mContext.getPackageManager();
         String[] packageNames = packageManager.getPackagesForUid(uid);
         if (ArrayUtils.isEmpty(packageNames)) {
diff --git a/services/core/java/com/android/server/hdmi/Constants.java b/services/core/java/com/android/server/hdmi/Constants.java
index 96ba8ef..648e07a 100644
--- a/services/core/java/com/android/server/hdmi/Constants.java
+++ b/services/core/java/com/android/server/hdmi/Constants.java
@@ -394,14 +394,10 @@
     static final String PROPERTY_PREFERRED_ADDRESS_PLAYBACK = "persist.sys.hdmi.addr.playback";
     static final String PROPERTY_PREFERRED_ADDRESS_TV = "persist.sys.hdmi.addr.tv";
 
-
     // TODO(OEM): Set this to false to keep the playback device in sleep upon hotplug event.
     //            True by default.
     static final String PROPERTY_WAKE_ON_HOTPLUG = "ro.hdmi.wake_on_hotplug";
 
-    // TODO(OEM): Set this to true to enable 'Set Menu Language' feature. False by default.
-    static final String PROPERTY_SET_MENU_LANGUAGE = "ro.hdmi.set_menu_language";
-
     /**
      * Property to save the ARC port id on system audio device.
      * <p>When ARC is initiated, this port will be used to turn on ARC.
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
index 61d4d4b..211d028 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java
@@ -1238,8 +1238,8 @@
         }
         // Wake up if the current device if ready to route.
         mService.wakeUp();
-        if (getLocalActivePort() == portId) {
-            HdmiLogger.debug("Not switching to the same port " + portId);
+        if ((getLocalActivePort() == portId) && (portId != Constants.CEC_SWITCH_ARC)) {
+            HdmiLogger.debug("Not switching to the same port " + portId + " except for arc");
             return;
         }
         // Switch to HOME if the current active port is not HOME yet
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index 413e7a0..0944324 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -24,6 +24,7 @@
 import android.os.PowerManager.WakeLock;
 import android.os.SystemProperties;
 import android.provider.Settings.Global;
+import android.sysprop.HdmiProperties;
 import android.util.Slog;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -47,7 +48,7 @@
             SystemProperties.getBoolean(Constants.PROPERTY_WAKE_ON_HOTPLUG, true);
 
     private static final boolean SET_MENU_LANGUAGE =
-            SystemProperties.getBoolean(Constants.PROPERTY_SET_MENU_LANGUAGE, false);
+            HdmiProperties.set_menu_language().orElse(false);
 
     // Used to keep the device awake while it is the active source. For devices that
     // cannot wake up via CEC commands, this address the inconvenience of having to
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index f1af73a..362955d 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -199,8 +199,7 @@
             int deviceId, int sourceMask, int sw);
     private static native boolean nativeHasKeys(long ptr,
             int deviceId, int sourceMask, int[] keyCodes, boolean[] keyExists);
-    private static native void nativeRegisterInputChannel(long ptr, InputChannel inputChannel,
-            int displayId);
+    private static native void nativeRegisterInputChannel(long ptr, InputChannel inputChannel);
     private static native void nativeRegisterInputMonitor(long ptr, InputChannel inputChannel,
             int displayId, boolean isGestureMonitor);
     private static native void nativeUnregisterInputChannel(long ptr, InputChannel inputChannel);
@@ -525,7 +524,6 @@
             throw new IllegalArgumentException("displayId must >= 0.");
         }
 
-
         final long ident = Binder.clearCallingIdentity();
         try {
             InputChannel[] inputChannels = InputChannel.openInputChannelPair(inputChannelName);
@@ -533,7 +531,7 @@
             inputChannels[0].setToken(host.asBinder());
             nativeRegisterInputMonitor(mPtr, inputChannels[0], displayId,
                     true /*isGestureMonitor*/);
-            return new InputMonitor(inputChannelName, inputChannels[1], host);
+            return new InputMonitor(inputChannels[1], host);
         } finally {
             Binder.restoreCallingIdentity(ident);
         }
@@ -551,7 +549,7 @@
         }
         inputChannel.setToken(new Binder());
 
-        nativeRegisterInputChannel(mPtr, inputChannel, Display.INVALID_DISPLAY);
+        nativeRegisterInputChannel(mPtr, inputChannel);
     }
 
     /**
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index b7fcd3f..471fa72 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -3570,10 +3570,14 @@
                 return;
             }
             if (!setVisible) {
-                // Client hides the IME directly.
-                if (mCurClient != null && mCurClient.client != null) {
-                    executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIO(
-                            MSG_APPLY_IME_VISIBILITY, setVisible ? 1 : 0, mCurClient));
+                if (mCurClient != null) {
+                    // IMMS only knows of focused window, not the actual IME target.
+                    // e.g. it isn't aware of any window that has both
+                    // NOT_FOCUSABLE, ALT_FOCUSABLE_IM flags set and can the IME target.
+                    // Send it to window manager to hide IME from IME target window.
+                    // TODO(b/139861270): send to mCurClient.client once IMMS is aware of
+                    // actual IME target.
+                    mWindowManagerInternal.hideIme(mCurClient.selfReportedDisplayId);
                 }
             } else {
                 // Send to window manager to show IME after IME layout finishes.
@@ -4208,7 +4212,7 @@
             // with other IME windows based on type vs. grouping based on whichever token happens
             // to get selected by the system later on.
             attrs.token = mSwitchingDialogToken;
-            attrs.privateFlags |= LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+            attrs.privateFlags |= LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
             attrs.setTitle("Select input method");
             w.setAttributes(attrs);
             updateSystemUiLocked(mImeWindowVis, mBackDisposition);
diff --git a/services/core/java/com/android/server/location/CallerIdentity.java b/services/core/java/com/android/server/location/CallerIdentity.java
index da31d0b..61e5d1f 100644
--- a/services/core/java/com/android/server/location/CallerIdentity.java
+++ b/services/core/java/com/android/server/location/CallerIdentity.java
@@ -16,6 +16,8 @@
 
 package com.android.server.location;
 
+import android.annotation.NonNull;
+
 /**
  * Represents the calling process's uid, pid, and package name.
  */
@@ -23,10 +25,13 @@
     public final int mUid;
     public final int mPid;
     public final String mPackageName;
+    public final @NonNull String mListenerIdentifier;
 
-    public CallerIdentity(int uid, int pid, String packageName) {
+    public CallerIdentity(int uid, int pid, String packageName,
+            @NonNull String listenerIdentifier) {
         mUid = uid;
         mPid = pid;
         mPackageName = packageName;
+        mListenerIdentifier = listenerIdentifier;
     }
 }
diff --git a/services/core/java/com/android/server/location/GeofenceManager.java b/services/core/java/com/android/server/location/GeofenceManager.java
index a192206..895c4b3 100644
--- a/services/core/java/com/android/server/location/GeofenceManager.java
+++ b/services/core/java/com/android/server/location/GeofenceManager.java
@@ -16,6 +16,7 @@
 
 package com.android.server.location;
 
+import android.annotation.NonNull;
 import android.app.AppOpsManager;
 import android.app.PendingIntent;
 import android.content.ContentResolver;
@@ -151,14 +152,16 @@
     }
 
     public void addFence(LocationRequest request, Geofence geofence, PendingIntent intent,
-            int allowedResolutionLevel, int uid, String packageName) {
+            int allowedResolutionLevel, int uid, String packageName,
+            @NonNull String listenerIdentifier) {
         if (D) {
             Slog.d(TAG, "addFence: request=" + request + ", geofence=" + geofence
                     + ", intent=" + intent + ", uid=" + uid + ", packageName=" + packageName);
         }
 
         GeofenceState state = new GeofenceState(geofence,
-                request.getExpireAt(), allowedResolutionLevel, uid, packageName, intent);
+                request.getExpireAt(), allowedResolutionLevel, uid, packageName, listenerIdentifier,
+                intent);
         synchronized (mLock) {
             // first make sure it doesn't already exist
             for (int i = mFences.size() - 1; i >= 0; i--) {
@@ -301,7 +304,8 @@
                 int op = LocationManagerService.resolutionLevelToOp(state.mAllowedResolutionLevel);
                 if (op >= 0) {
                     if (mAppOps.noteOpNoThrow(AppOpsManager.OP_FINE_LOCATION, state.mUid,
-                            state.mPackageName) != AppOpsManager.MODE_ALLOWED) {
+                            state.mPackageName, state.mListenerIdentifier)
+                            != AppOpsManager.MODE_ALLOWED) {
                         if (D) {
                             Slog.d(TAG, "skipping geofence processing for no op app: "
                                     + state.mPackageName);
diff --git a/services/core/java/com/android/server/location/GeofenceState.java b/services/core/java/com/android/server/location/GeofenceState.java
index 3ebe20a..fe0719d 100644
--- a/services/core/java/com/android/server/location/GeofenceState.java
+++ b/services/core/java/com/android/server/location/GeofenceState.java
@@ -17,6 +17,7 @@
 
 package com.android.server.location;
 
+import android.annotation.NonNull;
 import android.app.PendingIntent;
 import android.location.Geofence;
 import android.location.Location;
@@ -38,13 +39,14 @@
     public final int mAllowedResolutionLevel;
     public final int mUid;
     public final String mPackageName;
+    public final @NonNull String mListenerIdentifier;
     public final PendingIntent mIntent;
 
     int mState;  // current state
     double mDistanceToCenter;  // current distance to center of fence
 
-    public GeofenceState(Geofence fence, long expireAt,
-            int allowedResolutionLevel, int uid, String packageName, PendingIntent intent) {
+    public GeofenceState(Geofence fence, long expireAt, int allowedResolutionLevel, int uid,
+            String packageName, @NonNull String listenerIdentifier, PendingIntent intent) {
         mState = STATE_UNKNOWN;
         mDistanceToCenter = Double.MAX_VALUE;
 
@@ -53,6 +55,7 @@
         mAllowedResolutionLevel = allowedResolutionLevel;
         mUid = uid;
         mPackageName = packageName;
+        mListenerIdentifier = listenerIdentifier;
         mIntent = intent;
 
         mLocation = new Location("");
diff --git a/services/core/java/com/android/server/location/RemoteListenerHelper.java b/services/core/java/com/android/server/location/RemoteListenerHelper.java
index aa8a25a..0929d93 100644
--- a/services/core/java/com/android/server/location/RemoteListenerHelper.java
+++ b/services/core/java/com/android/server/location/RemoteListenerHelper.java
@@ -182,7 +182,9 @@
         }
 
         return mAppOps.noteOpNoThrow(AppOpsManager.OP_FINE_LOCATION, callerIdentity.mUid,
-                callerIdentity.mPackageName) == AppOpsManager.MODE_ALLOWED;
+                callerIdentity.mPackageName,
+                "Location sent to " + callerIdentity.mListenerIdentifier)
+                == AppOpsManager.MODE_ALLOWED;
     }
 
     protected void logPermissionDisabledEventNotReported(String tag, String packageName,
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index bad484f..63ba138 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -118,6 +118,7 @@
 import com.android.internal.widget.LockPatternUtils;
 import com.android.internal.widget.LockPatternUtils.CredentialType;
 import com.android.internal.widget.LockSettingsInternal;
+import com.android.internal.widget.LockscreenCredential;
 import com.android.internal.widget.VerifyCredentialResponse;
 import com.android.server.LocalServices;
 import com.android.server.ServiceThread;
@@ -2058,7 +2059,8 @@
             @UserIdInt int userHandle) {
         synchronized (this) {
             mUserPasswordMetrics.put(userHandle,
-                    PasswordMetrics.computeForCredential(credentialType, password));
+                    PasswordMetrics.computeForCredential(
+                            LockscreenCredential.createRaw(credentialType, password)));
         }
     }
 
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java b/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java
index a5d59e3..0a8e5bd 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java
@@ -16,16 +16,12 @@
 
 package com.android.server.locksettings;
 
-import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC;
-import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
-
-import static com.android.internal.widget.LockPatternUtils.stringToPattern;
-
 import android.app.ActivityManager;
 import android.os.ShellCommand;
 
 import com.android.internal.widget.LockPatternUtils;
 import com.android.internal.widget.LockPatternUtils.RequestThrottledException;
+import com.android.internal.widget.LockscreenCredential;
 
 import java.io.PrintWriter;
 
@@ -189,31 +185,49 @@
                 mLockPatternUtils.isSyntheticPasswordEnabled()));
     }
 
+    private LockscreenCredential getOldCredential() {
+        if (mLockPatternUtils.isLockPasswordEnabled(mCurrentUserId)) {
+            final int quality = mLockPatternUtils.getKeyguardStoredPasswordQuality(mCurrentUserId);
+            if (LockPatternUtils.isQualityAlphabeticPassword(quality)) {
+                return LockscreenCredential.createPassword(mOld);
+            } else {
+                return LockscreenCredential.createPin(mOld);
+            }
+        } else if (mLockPatternUtils.isLockPatternEnabled(mCurrentUserId)) {
+            return LockscreenCredential.createPattern(LockPatternUtils.byteArrayToPattern(
+                    mOld.getBytes()));
+        } else {
+            return LockscreenCredential.createNone();
+        }
+    }
+
     private void runSetPattern() {
-        byte[] oldBytes = mOld != null ? mOld.getBytes() : null;
-        mLockPatternUtils.saveLockPattern(stringToPattern(mNew), oldBytes, mCurrentUserId);
+        mLockPatternUtils.setLockCredential(
+                LockscreenCredential.createPattern(LockPatternUtils.byteArrayToPattern(
+                        mNew.getBytes())),
+                getOldCredential(),
+                mCurrentUserId);
         getOutPrintWriter().println("Pattern set to '" + mNew + "'");
     }
 
     private void runSetPassword() {
-        byte[] newBytes = mNew != null ? mNew.getBytes() : null;
-        byte[] oldBytes = mOld != null ? mOld.getBytes() : null;
-        mLockPatternUtils.saveLockPassword(newBytes, oldBytes, PASSWORD_QUALITY_ALPHABETIC,
+        mLockPatternUtils.setLockCredential(LockscreenCredential.createPassword(mNew),
+                getOldCredential(),
                 mCurrentUserId);
         getOutPrintWriter().println("Password set to '" + mNew + "'");
     }
 
     private void runSetPin() {
-        byte[] newBytes = mNew != null ? mNew.getBytes() : null;
-        byte[] oldBytes = mOld != null ? mOld.getBytes() : null;
-        mLockPatternUtils.saveLockPassword(newBytes, oldBytes, PASSWORD_QUALITY_NUMERIC,
+        mLockPatternUtils.setLockCredential(LockscreenCredential.createPin(mNew),
+                getOldCredential(),
                 mCurrentUserId);
         getOutPrintWriter().println("Pin set to '" + mNew + "'");
     }
 
     private void runClear() {
-        byte[] oldBytes = mOld != null ? mOld.getBytes() : null;
-        mLockPatternUtils.clearLock(oldBytes, mCurrentUserId);
+        mLockPatternUtils.setLockCredential(LockscreenCredential.createNone(),
+                getOldCredential(),
+                mCurrentUserId);
         getOutPrintWriter().println("Lock credential cleared");
     }
 
@@ -238,13 +252,8 @@
             }
 
             try {
-                final boolean result;
-                if (havePassword) {
-                    byte[] passwordBytes = mOld != null ? mOld.getBytes() : null;
-                    result = mLockPatternUtils.checkPassword(passwordBytes, mCurrentUserId);
-                } else {
-                    result = mLockPatternUtils.checkPattern(stringToPattern(mOld), mCurrentUserId);
-                }
+                final boolean result = mLockPatternUtils.checkCredential(getOldCredential(),
+                        mCurrentUserId, null);
                 if (!result) {
                     if (!mLockPatternUtils.isManagedProfileWithUnifiedChallenge(mCurrentUserId)) {
                         mLockPatternUtils.reportFailedPasswordAttempt(mCurrentUserId);
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
index 5594614..f4cad63 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java
@@ -95,8 +95,6 @@
 
     @VisibleForTesting
     public static class CredentialHash {
-        /** Deprecated private static final int VERSION_LEGACY = 0; */
-        private static final int VERSION_GATEKEEPER = 1;
 
         private CredentialHash(byte[] hash, @CredentialType int type) {
             if (type != LockPatternUtils.CREDENTIAL_TYPE_NONE) {
@@ -126,42 +124,6 @@
 
         byte[] hash;
         @CredentialType int type;
-
-        public byte[] toBytes() {
-            try {
-                ByteArrayOutputStream os = new ByteArrayOutputStream();
-                DataOutputStream dos = new DataOutputStream(os);
-                dos.write(VERSION_GATEKEEPER);
-                dos.write(type);
-                if (hash != null && hash.length > 0) {
-                    dos.writeInt(hash.length);
-                    dos.write(hash);
-                } else {
-                    dos.writeInt(0);
-                }
-                dos.close();
-                return os.toByteArray();
-            } catch (IOException e) {
-                throw new IllegalStateException("Fail to serialze credential hash", e);
-            }
-        }
-
-        public static CredentialHash fromBytes(byte[] bytes) {
-            try {
-                DataInputStream is = new DataInputStream(new ByteArrayInputStream(bytes));
-                /* int version = */ is.read();
-                int type = is.read();
-                int hashSize = is.readInt();
-                byte[] hash = null;
-                if (hashSize > 0) {
-                    hash = new byte[hashSize];
-                    is.readFully(hash);
-                }
-                return new CredentialHash(hash, type);
-            } catch (IOException e) {
-                throw new IllegalStateException("Fail to deserialze credential hash", e);
-            }
-        }
     }
 
     public LockSettingsStorage(Context context) {
diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java b/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java
index e753a7b..9eac252 100644
--- a/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java
+++ b/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java
@@ -93,7 +93,7 @@
 
     public void unselectRoute(String packageName, String routeId) {
         if (mConnectionReady) {
-            mActiveConnection.unselectRotue(packageName, routeId);
+            mActiveConnection.unselectRoute(packageName, routeId);
             updateBinding();
         }
     }
@@ -105,6 +105,20 @@
         }
     }
 
+    public void requestSetVolume(MediaRoute2Info route, int volume) {
+        if (mConnectionReady) {
+            mActiveConnection.requestSetVolume(route.getId(), volume);
+            updateBinding();
+        }
+    }
+
+    public void requestUpdateVolume(MediaRoute2Info route, int delta) {
+        if (mConnectionReady) {
+            mActiveConnection.requestUpdateVolume(route.getId(), delta);
+            updateBinding();
+        }
+    }
+
     @Nullable
     public MediaRoute2ProviderInfo getProviderInfo() {
         return mProviderInfo;
@@ -324,7 +338,7 @@
             }
         }
 
-        public void unselectRotue(String packageName, String routeId) {
+        public void unselectRoute(String packageName, String routeId) {
             try {
                 mProvider.unselectRoute(packageName, routeId);
             } catch (RemoteException ex) {
@@ -340,6 +354,22 @@
             }
         }
 
+        public void requestSetVolume(String routeId, int volume) {
+            try {
+                mProvider.requestSetVolume(routeId, volume);
+            } catch (RemoteException ex) {
+                Slog.e(TAG, "Failed to deliver request to request set volume.", ex);
+            }
+        }
+
+        public void requestUpdateVolume(String routeId, int delta) {
+            try {
+                mProvider.requestUpdateVolume(routeId, delta);
+            } catch (RemoteException ex) {
+                Slog.e(TAG, "Failed to deliver request to request update volume.", ex);
+            }
+        }
+
         @Override
         public void binderDied() {
             mHandler.post(() -> onConnectionDied(Connection.this));
diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
index 668f2be..74d59ac 100644
--- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
+++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java
@@ -198,6 +198,34 @@
         }
     }
 
+    public void requestSetVolume2(IMediaRouter2Client client, MediaRoute2Info route, int volume) {
+        Objects.requireNonNull(client, "client must not be null");
+        Objects.requireNonNull(route, "route must not be null");
+
+        final long token = Binder.clearCallingIdentity();
+        try {
+            synchronized (mLock) {
+                requestSetVolumeLocked(client, route, volume);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    public void requestUpdateVolume2(IMediaRouter2Client client, MediaRoute2Info route, int delta) {
+        Objects.requireNonNull(client, "client must not be null");
+        Objects.requireNonNull(route, "route must not be null");
+
+        final long token = Binder.clearCallingIdentity();
+        try {
+            synchronized (mLock) {
+                requestUpdateVolumeLocked(client, route, delta);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
     public void selectClientRoute2(@NonNull IMediaRouter2Manager manager,
             String packageName, @Nullable MediaRoute2Info route) {
         final long token = Binder.clearCallingIdentity();
@@ -210,6 +238,37 @@
         }
     }
 
+    public void requestSetVolume2Manager(IMediaRouter2Manager manager,
+            MediaRoute2Info route, int volume) {
+        Objects.requireNonNull(manager, "manager must not be null");
+        Objects.requireNonNull(route, "route must not be null");
+
+        final long token = Binder.clearCallingIdentity();
+        try {
+            synchronized (mLock) {
+                requestSetVolumeLocked(manager, route, volume);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+    public void requestUpdateVolume2Manager(IMediaRouter2Manager manager,
+            MediaRoute2Info route, int delta) {
+        Objects.requireNonNull(manager, "manager must not be null");
+        Objects.requireNonNull(route, "route must not be null");
+
+        final long token = Binder.clearCallingIdentity();
+        try {
+            synchronized (mLock) {
+                requestUpdateVolumeLocked(manager, route, delta);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
+
+
     public void registerClient(@NonNull IMediaRouterClient client, @NonNull String packageName) {
         Objects.requireNonNull(client, "client must not be null");
 
@@ -362,6 +421,30 @@
         }
     }
 
+    private void requestSetVolumeLocked(IMediaRouter2Client client, MediaRoute2Info route,
+            int volume) {
+        final IBinder binder = client.asBinder();
+        ClientRecord clientRecord = mAllClientRecords.get(binder);
+
+        if (clientRecord != null) {
+            clientRecord.mUserRecord.mHandler.sendMessage(
+                    obtainMessage(UserHandler::requestSetVolume,
+                            clientRecord.mUserRecord.mHandler, route, volume));
+        }
+    }
+
+    private void requestUpdateVolumeLocked(IMediaRouter2Client client, MediaRoute2Info route,
+            int delta) {
+        final IBinder binder = client.asBinder();
+        ClientRecord clientRecord = mAllClientRecords.get(binder);
+
+        if (clientRecord != null) {
+            clientRecord.mUserRecord.mHandler.sendMessage(
+                    obtainMessage(UserHandler::requestUpdateVolume,
+                            clientRecord.mUserRecord.mHandler, route, delta));
+        }
+    }
+
     private void registerManagerLocked(IMediaRouter2Manager manager,
             int uid, int pid, String packageName, int userId, boolean trusted) {
         final IBinder binder = manager.asBinder();
@@ -424,6 +507,31 @@
         }
     }
 
+    private void requestSetVolumeLocked(IMediaRouter2Manager manager, MediaRoute2Info route,
+            int volume) {
+        final IBinder binder = manager.asBinder();
+        ManagerRecord managerRecord = mAllManagerRecords.get(binder);
+
+        if (managerRecord != null) {
+            managerRecord.mUserRecord.mHandler.sendMessage(
+                    obtainMessage(UserHandler::requestSetVolume,
+                            managerRecord.mUserRecord.mHandler, route, volume));
+        }
+    }
+
+    private void requestUpdateVolumeLocked(IMediaRouter2Manager manager, MediaRoute2Info route,
+            int delta) {
+        final IBinder binder = manager.asBinder();
+        ManagerRecord managerRecord = mAllManagerRecords.get(binder);
+
+        if (managerRecord != null) {
+            managerRecord.mUserRecord.mHandler.sendMessage(
+                    obtainMessage(UserHandler::requestUpdateVolume,
+                            managerRecord.mUserRecord.mHandler, route, delta));
+        }
+    }
+
+
     private void initializeUserLocked(UserRecord userRecord) {
         if (DEBUG) {
             Slog.d(TAG, userRecord + ": Initialized");
@@ -679,6 +787,20 @@
             }
         }
 
+        private void requestSetVolume(MediaRoute2Info route, int volume) {
+            final MediaRoute2ProviderProxy provider = findProvider(route.getProviderId());
+            if (provider != null) {
+                provider.requestSetVolume(route, volume);
+            }
+        }
+
+        private void requestUpdateVolume(MediaRoute2Info route, int delta) {
+            final MediaRoute2ProviderProxy provider = findProvider(route.getProviderId());
+            if (provider != null) {
+                provider.requestUpdateVolume(route, delta);
+            }
+        }
+
         private void scheduleUpdateProviderInfos() {
             if (!mProviderInfosUpdateScheduled) {
                 mProviderInfosUpdateScheduled = true;
diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java
index 796a25d..afd92f6 100644
--- a/services/core/java/com/android/server/media/MediaRouterService.java
+++ b/services/core/java/com/android/server/media/MediaRouterService.java
@@ -499,6 +499,32 @@
         mService2.setControlCategories2(client, categories);
     }
 
+    // Binder call
+    @Override
+    public void requestSetVolume2(IMediaRouter2Client client, MediaRoute2Info route, int volume) {
+        mService2.requestSetVolume2(client, route, volume);
+    }
+
+    // Binder call
+    @Override
+    public void requestUpdateVolume2(IMediaRouter2Client client, MediaRoute2Info route, int delta) {
+        mService2.requestUpdateVolume2(client, route, delta);
+    }
+
+    // Binder call
+    @Override
+    public void requestSetVolume2Manager(IMediaRouter2Manager manager,
+            MediaRoute2Info route, int volume) {
+        mService2.requestSetVolume2Manager(manager, route, volume);
+    }
+
+    // Binder call
+    @Override
+    public void requestUpdateVolume2Manager(IMediaRouter2Manager manager,
+            MediaRoute2Info route, int delta) {
+        mService2.requestUpdateVolume2Manager(manager, route, delta);
+    }
+
     void restoreBluetoothA2dp() {
         try {
             boolean a2dpOn;
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 976a0c6..812ce32 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -217,7 +217,6 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
 import com.android.internal.notification.SystemNotificationChannels;
-import com.android.internal.os.SomeArgs;
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.ConcurrentUtils;
@@ -385,6 +384,7 @@
     private static final int MSG_SUBSCRIPTION_OVERRIDE = 16;
     private static final int MSG_METERED_RESTRICTED_PACKAGES_CHANGED = 17;
     private static final int MSG_SET_NETWORK_TEMPLATE_ENABLED = 18;
+    private static final int MSG_SUBSCRIPTION_PLANS_CHANGED = 19;
 
     private static final int UID_MSG_STATE_CHANGED = 100;
     private static final int UID_MSG_GONE = 101;
@@ -1509,6 +1509,11 @@
         latch.await(5, TimeUnit.SECONDS);
     }
 
+    @VisibleForTesting
+    Handler getHandlerForTesting() {
+        return mHandler;
+    }
+
     /**
      * Update mobile policies with data cycle information from {@link CarrierConfigManager}
      * if necessary.
@@ -3064,6 +3069,34 @@
         mContext.enforceCallingOrSelfPermission(MANAGE_SUBSCRIPTION_PLANS, TAG);
     }
 
+    private void enforceSubscriptionPlanValidity(SubscriptionPlan[] plans) {
+        // nothing to check if no plans
+        if (plans.length == 0) {
+            return;
+        }
+
+        long applicableNetworkTypes = 0;
+        boolean allNetworks = false;
+        for (SubscriptionPlan plan : plans) {
+            if (plan.getNetworkTypes() == null) {
+                allNetworks = true;
+            } else {
+                if ((applicableNetworkTypes & plan.getNetworkTypesBitMask()) != 0) {
+                    throw new IllegalArgumentException(
+                            "Multiple subscription plans defined for a single network type.");
+                } else {
+                    applicableNetworkTypes |= plan.getNetworkTypesBitMask();
+                }
+            }
+        }
+
+        // ensure at least one plan applies for every network type
+        if (!allNetworks) {
+            throw new IllegalArgumentException(
+                    "No generic subscription plan that applies to all network types.");
+        }
+    }
+
     @Override
     public SubscriptionPlan[] getSubscriptionPlans(int subId, String callingPackage) {
         enforceSubscriptionPlanAccess(subId, Binder.getCallingUid(), callingPackage);
@@ -3228,6 +3261,7 @@
     @Override
     public void setSubscriptionPlans(int subId, SubscriptionPlan[] plans, String callingPackage) {
         enforceSubscriptionPlanAccess(subId, Binder.getCallingUid(), callingPackage);
+        enforceSubscriptionPlanValidity(plans);
 
         for (SubscriptionPlan plan : plans) {
             Preconditions.checkNotNull(plan);
@@ -3256,6 +3290,8 @@
             intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
             intent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, subId);
             mContext.sendBroadcast(intent, android.Manifest.permission.MANAGE_SUBSCRIPTION_PLANS);
+            mHandler.sendMessage(
+                    mHandler.obtainMessage(MSG_SUBSCRIPTION_PLANS_CHANGED, subId, 0, plans));
         } finally {
             Binder.restoreCallingIdentity(token);
         }
@@ -3282,7 +3318,7 @@
 
     @Override
     public void setSubscriptionOverride(int subId, int overrideMask, int overrideValue,
-            long networkTypeMask, long timeoutMillis, String callingPackage) {
+            long timeoutMillis, String callingPackage) {
         enforceSubscriptionPlanAccess(subId, Binder.getCallingUid(), callingPackage);
 
         // We can only override when carrier told us about plans
@@ -3300,16 +3336,11 @@
         final boolean overrideEnabled = Settings.Global.getInt(mContext.getContentResolver(),
                 NETPOLICY_OVERRIDE_ENABLED, 1) != 0;
         if (overrideEnabled || overrideValue == 0) {
-            SomeArgs args = SomeArgs.obtain();
-            args.arg1 = subId;
-            args.arg2 = overrideMask;
-            args.arg3 = overrideValue;
-            args.arg4 = networkTypeMask;
-            mHandler.sendMessage(mHandler.obtainMessage(MSG_SUBSCRIPTION_OVERRIDE, args));
+            mHandler.sendMessage(mHandler.obtainMessage(MSG_SUBSCRIPTION_OVERRIDE,
+                    overrideMask, overrideValue, subId));
             if (timeoutMillis > 0) {
-                args.arg3 = 0;
-                mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_SUBSCRIPTION_OVERRIDE, args),
-                        timeoutMillis);
+                mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_SUBSCRIPTION_OVERRIDE,
+                        overrideMask, 0, subId), timeoutMillis);
             }
         }
     }
@@ -4445,11 +4476,20 @@
     }
 
     private void dispatchSubscriptionOverride(INetworkPolicyListener listener, int subId,
-            int overrideMask, int overrideValue, long networkTypeMask) {
+            int overrideMask, int overrideValue) {
         if (listener != null) {
             try {
-                listener.onSubscriptionOverride(subId, overrideMask, overrideValue,
-                        networkTypeMask);
+                listener.onSubscriptionOverride(subId, overrideMask, overrideValue);
+            } catch (RemoteException ignored) {
+            }
+        }
+    }
+
+    private void dispatchSubscriptionPlansChanged(INetworkPolicyListener listener, int subId,
+            SubscriptionPlan[] plans) {
+        if (listener != null) {
+            try {
+                listener.onSubscriptionPlansChanged(subId, plans);
             } catch (RemoteException ignored) {
             }
         }
@@ -4550,16 +4590,13 @@
                     return true;
                 }
                 case MSG_SUBSCRIPTION_OVERRIDE: {
-                    final SomeArgs args = (SomeArgs) msg.obj;
-                    final int subId = (int) args.arg1;
-                    final int overrideMask = (int) args.arg2;
-                    final int overrideValue = (int) args.arg3;
-                    final long networkTypeMask = (long) args.arg4;
+                    final int overrideMask = msg.arg1;
+                    final int overrideValue = msg.arg2;
+                    final int subId = (int) msg.obj;
                     final int length = mListeners.beginBroadcast();
                     for (int i = 0; i < length; i++) {
                         final INetworkPolicyListener listener = mListeners.getBroadcastItem(i);
-                        dispatchSubscriptionOverride(listener, subId, overrideMask, overrideValue,
-                                networkTypeMask);
+                        dispatchSubscriptionOverride(listener, subId, overrideMask, overrideValue);
                     }
                     mListeners.finishBroadcast();
                     return true;
@@ -4576,6 +4613,17 @@
                     setNetworkTemplateEnabledInner(template, enabled);
                     return true;
                 }
+                case MSG_SUBSCRIPTION_PLANS_CHANGED: {
+                    final SubscriptionPlan[] plans = (SubscriptionPlan[]) msg.obj;
+                    final int subId = msg.arg1;
+                    final int length = mListeners.beginBroadcast();
+                    for (int i = 0; i < length; i++) {
+                        final INetworkPolicyListener listener = mListeners.getBroadcastItem(i);
+                        dispatchSubscriptionPlansChanged(listener, subId, plans);
+                    }
+                    mListeners.finishBroadcast();
+                    return true;
+                }
                 default: {
                     return false;
                 }
diff --git a/services/core/java/com/android/server/notification/NotificationDelegate.java b/services/core/java/com/android/server/notification/NotificationDelegate.java
index 61be1f5..6f0ad33 100644
--- a/services/core/java/com/android/server/notification/NotificationDelegate.java
+++ b/services/core/java/com/android/server/notification/NotificationDelegate.java
@@ -17,6 +17,7 @@
 package com.android.server.notification;
 
 import android.app.Notification;
+import android.net.Uri;
 import android.service.notification.NotificationStats;
 
 import com.android.internal.statusbar.NotificationVisibility;
@@ -49,6 +50,12 @@
     void onNotificationBubbleChanged(String key, boolean isBubble);
 
     /**
+     * Grant permission to read the specified URI to the package associated with the
+     * NotificationRecord associated with the given key.
+     */
+    void grantInlineReplyUriPermission(String key, Uri uri, int callingUid);
+
+    /**
      * Notifies that smart replies and actions have been added to the UI.
      */
     void onNotificationSmartSuggestionsAdded(String key, int smartReplyCount, int smartActionCount,
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 4457e9c..ca979f8 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -21,6 +21,7 @@
 import static android.app.Notification.FLAG_AUTOGROUP_SUMMARY;
 import static android.app.Notification.FLAG_BUBBLE;
 import static android.app.Notification.FLAG_FOREGROUND_SERVICE;
+import static android.app.Notification.FLAG_INSISTENT;
 import static android.app.Notification.FLAG_NO_CLEAR;
 import static android.app.Notification.FLAG_ONGOING_EVENT;
 import static android.app.Notification.FLAG_ONLY_ALERT_ONCE;
@@ -55,6 +56,7 @@
 import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
 import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.media.AudioAttributes.USAGE_NOTIFICATION_RINGTONE;
 import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_CRITICAL;
 import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL;
 import static android.os.UserHandle.USER_NULL;
@@ -1154,6 +1156,56 @@
                 }
             }
         }
+
+        @Override
+        /**
+         * Grant permission to read the specified URI to the package specified in the
+         * NotificationRecord associated with the given key. The callingUid represents the UID of
+         * SystemUI from which this method is being called.
+         *
+         * For this to work, SystemUI must have permission to read the URI when running under the
+         * user associated with the NotificationRecord, and this grant will fail when trying
+         * to grant URI permissions across users.
+         */
+        public void grantInlineReplyUriPermission(String key, Uri uri, int callingUid) {
+            synchronized (mNotificationLock) {
+                NotificationRecord r = mNotificationsByKey.get(key);
+                if (r != null) {
+                    IBinder owner = r.permissionOwner;
+                    if (owner == null) {
+                        r.permissionOwner = mUgmInternal.newUriPermissionOwner("NOTIF:" + key);
+                        owner = r.permissionOwner;
+                    }
+                    int uid = callingUid;
+                    int userId = r.sbn.getUserId();
+                    if (userId == UserHandle.USER_ALL) {
+                        userId = USER_SYSTEM;
+                    }
+                    if (UserHandle.getUserId(uid) != userId) {
+                        try {
+                            final String[] pkgs = mPackageManager.getPackagesForUid(callingUid);
+                            if (pkgs == null) {
+                                Log.e(TAG, "Cannot grant uri permission to unknown UID: "
+                                        + callingUid);
+                            }
+                            final String pkg = pkgs[0]; // Get the SystemUI package
+                            // Find the UID for SystemUI for the correct user
+                            uid =  mPackageManager.getPackageUid(pkg, 0, userId);
+                        } catch (RemoteException re) {
+                            Log.e(TAG, "Cannot talk to package manager", re);
+                        }
+                    }
+                    grantUriPermission(owner, uri, uid, r.sbn.getPackageName(), userId);
+                } else {
+                    Log.w(TAG, "No record found for notification key:" + key);
+
+                    // TODO: figure out cancel story. I think it's: sysui needs to tell us
+                    // whenever noitifications held by a lifetimextender go away
+                    // IBinder owner = mUgmInternal.newUriPermissionOwner("InlineReply:" + key);
+                    // pass in userId and package as well as key (key for logging purposes)
+                }
+            }
+        }
     };
 
     @VisibleForTesting
@@ -1183,7 +1235,7 @@
     }
 
     @GuardedBy("mNotificationLock")
-    private void clearSoundLocked() {
+    void clearSoundLocked() {
         mSoundNotificationKey = null;
         long identity = Binder.clearCallingIdentity();
         try {
@@ -1198,7 +1250,7 @@
     }
 
     @GuardedBy("mNotificationLock")
-    private void clearVibrateLocked() {
+    void clearVibrateLocked() {
         mVibrateNotificationKey = null;
         long identity = Binder.clearCallingIdentity();
         try {
@@ -4495,13 +4547,13 @@
         if (record != null && record.getAudioAttributes() != null) {
             if ((mListenerHints & HINT_HOST_DISABLE_NOTIFICATION_EFFECTS) != 0) {
                 if (record.getAudioAttributes().getUsage()
-                        != AudioAttributes.USAGE_VOICE_COMMUNICATION) {
+                        != AudioAttributes.USAGE_NOTIFICATION_RINGTONE) {
                     return "listenerNoti";
                 }
             }
             if ((mListenerHints & HINT_HOST_DISABLE_CALL_EFFECTS) != 0) {
                 if (record.getAudioAttributes().getUsage()
-                        == AudioAttributes.USAGE_VOICE_COMMUNICATION) {
+                        == AudioAttributes.USAGE_NOTIFICATION_RINGTONE) {
                     return "listenerCall";
                 }
             }
@@ -6060,7 +6112,6 @@
                 mIsAutomotive
                         ? record.getImportance() > NotificationManager.IMPORTANCE_DEFAULT
                         : record.getImportance() >= NotificationManager.IMPORTANCE_DEFAULT;
-
         // Remember if this notification already owns the notification channels.
         boolean wasBeep = key != null && key.equals(mSoundNotificationKey);
         boolean wasBuzz = key != null && key.equals(mVibrateNotificationKey);
@@ -6076,7 +6127,6 @@
         }
 
         if (aboveThreshold && isNotificationForCurrentUser(record)) {
-
             if (mSystemReady && mAudioManager != null) {
                 Uri soundUri = record.getSound();
                 hasValidSound = soundUri != null && !Uri.EMPTY.equals(soundUri);
@@ -6091,7 +6141,6 @@
                     vibration = mFallbackVibrationPattern;
                 }
                 hasValidVibrate = vibration != null;
-
                 boolean hasAudibleAlert = hasValidSound || hasValidVibrate;
                 if (hasAudibleAlert && !shouldMuteNotificationLocked(record)) {
                     if (!sentAccessibilityEvent) {
@@ -6248,11 +6297,29 @@
             return true;
         }
 
+        // A looping ringtone, such as an incoming call is playing
+        if (isLoopingRingtoneNotification(mNotificationsByKey.get(mSoundNotificationKey))
+                || isLoopingRingtoneNotification(
+                        mNotificationsByKey.get(mVibrateNotificationKey))) {
+            return true;
+        }
+
+        return false;
+    }
+
+    @GuardedBy("mNotificationLock")
+    private boolean isLoopingRingtoneNotification(final NotificationRecord playingRecord) {
+        if (playingRecord != null) {
+            if (playingRecord.getAudioAttributes().getUsage() == USAGE_NOTIFICATION_RINGTONE
+                    && (playingRecord.getNotification().flags & FLAG_INSISTENT) != 0) {
+                return true;
+            }
+        }
         return false;
     }
 
     private boolean playSound(final NotificationRecord record, Uri soundUri) {
-        boolean looping = (record.getNotification().flags & Notification.FLAG_INSISTENT) != 0;
+        boolean looping = (record.getNotification().flags & FLAG_INSISTENT) != 0;
         // play notifications if there is no user of exclusive audio focus
         // and the stream volume is not 0 (non-zero volume implies not silenced by SILENT or
         //   VIBRATE ringer mode)
@@ -6304,7 +6371,6 @@
                     try {
                         Thread.sleep(waitMs);
                     } catch (InterruptedException e) { }
-
                     // Notifications might be canceled before it actually vibrates due to waitMs,
                     // so need to check the notification still valide for vibrate.
                     synchronized (mNotificationLock) {
@@ -7012,7 +7078,6 @@
     private void grantUriPermission(IBinder owner, Uri uri, int sourceUid, String targetPkg,
             int targetUserId) {
         if (uri == null || !ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) return;
-
         final long ident = Binder.clearCallingIdentity();
         try {
             mUgm.grantUriPermissionFromOwner(owner, sourceUid, targetPkg,
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index ca577ee..982ecbb 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -974,124 +974,9 @@
         @Override public final boolean hasFeature(String feature) {
             return PackageManagerService.this.hasSystemFeature(feature, 0);
         }
-
-        final List<PackageParser.Package> getStaticOverlayPackages(
-                Collection<PackageParser.Package> allPackages, String targetPackageName) {
-            if ("android".equals(targetPackageName)) {
-                // Static RROs targeting to "android", ie framework-res.apk, are already applied by
-                // native AssetManager.
-                return null;
-            }
-
-            List<PackageParser.Package> overlayPackages = null;
-            for (PackageParser.Package p : allPackages) {
-                if (targetPackageName.equals(p.mOverlayTarget) && p.mOverlayIsStatic) {
-                    if (overlayPackages == null) {
-                        overlayPackages = new ArrayList<>();
-                    }
-                    overlayPackages.add(p);
-                }
-            }
-            if (overlayPackages != null) {
-                Comparator<PackageParser.Package> cmp =
-                        Comparator.comparingInt(p -> p.mOverlayPriority);
-                overlayPackages.sort(cmp);
-            }
-            return overlayPackages;
-        }
-
-        final String[] getStaticOverlayPaths(List<PackageParser.Package> overlayPackages,
-                String targetPath) {
-            if (overlayPackages == null || overlayPackages.isEmpty()) {
-                return null;
-            }
-            List<String> overlayPathList = null;
-            for (PackageParser.Package overlayPackage : overlayPackages) {
-                if (targetPath == null) {
-                    if (overlayPathList == null) {
-                        overlayPathList = new ArrayList<>();
-                    }
-                    overlayPathList.add(overlayPackage.baseCodePath);
-                    continue;
-                }
-
-                try {
-                    // Creates idmaps for system to parse correctly the Android manifest of the
-                    // target package.
-                    //
-                    // OverlayManagerService will update each of them with a correct gid from its
-                    // target package app id.
-                    mInstaller.idmap(targetPath, overlayPackage.baseCodePath,
-                            UserHandle.getSharedAppGid(
-                                    UserHandle.getUserGid(UserHandle.USER_SYSTEM)));
-                    if (overlayPathList == null) {
-                        overlayPathList = new ArrayList<>();
-                    }
-                    overlayPathList.add(overlayPackage.baseCodePath);
-                } catch (InstallerException e) {
-                    Slog.e(TAG, "Failed to generate idmap for " + targetPath + " and " +
-                            overlayPackage.baseCodePath);
-                }
-            }
-            return overlayPathList == null ? null : overlayPathList.toArray(new String[0]);
-        }
-
-        String[] getStaticOverlayPaths(String targetPackageName, String targetPath) {
-            List<PackageParser.Package> overlayPackages;
-            synchronized (mInstallLock) {
-                synchronized (mLock) {
-                    overlayPackages = getStaticOverlayPackages(
-                            mPackages.values(), targetPackageName);
-                }
-                // It is safe to keep overlayPackages without holding mPackages because static overlay
-                // packages can't be uninstalled or disabled.
-                return getStaticOverlayPaths(overlayPackages, targetPath);
-            }
-        }
-
-        @Override public final String[] getOverlayApks(String targetPackageName) {
-            return getStaticOverlayPaths(targetPackageName, null);
-        }
-
-        @Override public final String[] getOverlayPaths(String targetPackageName,
-                String targetPath) {
-            return getStaticOverlayPaths(targetPackageName, targetPath);
-        }
-    }
-
-    class ParallelPackageParserCallback extends PackageParserCallback {
-        List<PackageParser.Package> mOverlayPackages = null;
-
-        void findStaticOverlayPackages() {
-            synchronized (mLock) {
-                for (PackageParser.Package p : mPackages.values()) {
-                    if (p.mOverlayIsStatic) {
-                        if (mOverlayPackages == null) {
-                            mOverlayPackages = new ArrayList<>();
-                        }
-                        mOverlayPackages.add(p);
-                    }
-                }
-            }
-        }
-
-        @Override
-        synchronized String[] getStaticOverlayPaths(String targetPackageName, String targetPath) {
-            // We can trust mOverlayPackages without holding mPackages because package uninstall
-            // can't happen while running parallel parsing.
-            // And we can call mInstaller inside getStaticOverlayPaths without holding mInstallLock
-            // because mInstallLock is held before running parallel parsing.
-            // Moreover holding mPackages or mInstallLock on each parsing thread causes dead-lock.
-            return mOverlayPackages == null ? null :
-                    getStaticOverlayPaths(
-                            getStaticOverlayPackages(mOverlayPackages, targetPackageName),
-                            targetPath);
-        }
     }
 
     final PackageParser.Callback mPackageParserCallback = new PackageParserCallback();
-    final ParallelPackageParserCallback mParallelPackageParserCallback =
-            new ParallelPackageParserCallback();
 
     // Currently known shared libraries.
     final ArrayMap<String, LongSparseArray<SharedLibraryInfo>> mSharedLibraries = new ArrayMap<>();
@@ -1604,6 +1489,7 @@
     final @Nullable String mAppPredictionServicePackage;
     final @Nullable String mIncidentReportApproverPackage;
     final @Nullable String[] mTelephonyPackages;
+    final @Nullable String mWifiPackage;
     final @NonNull String mServicesSystemSharedLibraryPackageName;
     final @NonNull String mSharedSystemSharedLibraryPackageName;
 
@@ -2807,8 +2693,6 @@
                         systemScanFlags | partition.scanFlag, 0);
             }
 
-            mParallelPackageParserCallback.findStaticOverlayPackages();
-
             scanDirTracedLI(frameworkDir, systemParseFlags,
                     systemScanFlags | SCAN_NO_DEX | SCAN_AS_PRIVILEGED, 0);
             if (!mPackages.containsKey("android")) {
@@ -3061,6 +2945,7 @@
             mAppPredictionServicePackage = getAppPredictionServicePackageName();
             mIncidentReportApproverPackage = getIncidentReportApproverPackageName();
             mTelephonyPackages = getTelephonyPackageNames();
+            mWifiPackage = mContext.getString(R.string.config_wifiPackage);
 
             // Now that we know all of the shared libraries, update all clients to have
             // the correct library paths.
@@ -8469,7 +8354,7 @@
         }
         try (ParallelPackageParser parallelPackageParser = new ParallelPackageParser(
                 mSeparateProcesses, mOnlyCore, mMetrics, mCacheDir,
-                mParallelPackageParserCallback)) {
+                mPackageParserCallback)) {
             // Submit files for parsing in parallel
             int fileCount = 0;
             for (File file : files) {
@@ -10685,6 +10570,50 @@
         return changedAbiCodePath;
     }
 
+    /**
+     * Sets the enabled state of components configured through {@link SystemConfig}.
+     * This modifies the {@link PackageSetting} object.
+     **/
+    static void configurePackageComponents(PackageParser.Package pkg) {
+        final ArrayMap<String, Boolean> componentsEnabledStates = SystemConfig.getInstance()
+                .getComponentsEnabledStates(pkg.packageName);
+        if (componentsEnabledStates == null) {
+            return;
+        }
+
+        for (int i = pkg.activities.size() - 1; i >= 0; i--) {
+            final PackageParser.Activity component = pkg.activities.get(i);
+            final Boolean enabled = componentsEnabledStates.get(component.className);
+            if (enabled != null) {
+                component.info.enabled = enabled;
+            }
+        }
+
+        for (int i = pkg.receivers.size() - 1; i >= 0; i--) {
+            final PackageParser.Activity component = pkg.receivers.get(i);
+            final Boolean enabled = componentsEnabledStates.get(component.className);
+            if (enabled != null) {
+                component.info.enabled = enabled;
+            }
+        }
+
+        for (int i = pkg.providers.size() - 1; i >= 0; i--) {
+            final PackageParser.Provider component = pkg.providers.get(i);
+            final Boolean enabled = componentsEnabledStates.get(component.className);
+            if (enabled != null) {
+                component.info.enabled = enabled;
+            }
+        }
+
+        for (int i = pkg.services.size() - 1; i >= 0; i--) {
+            final PackageParser.Service component = pkg.services.get(i);
+            final Boolean enabled = componentsEnabledStates.get(component.className);
+            if (enabled != null) {
+                component.info.enabled = enabled;
+            }
+        }
+    }
+
 
     /**
      * Just scans the package without any side effects.
@@ -10852,6 +10781,10 @@
             pkg.applicationInfo.initForUser(UserHandle.USER_SYSTEM);
         }
 
+        if (pkg.isSystem()) {
+            configurePackageComponents(pkg);
+        }
+
         final String cpuAbiOverride = deriveAbiOverride(pkg.cpuAbiOverride, pkgSetting);
 
         if ((scanFlags & SCAN_NEW_INSTALL) == 0) {
@@ -16067,10 +16000,6 @@
      * will be used to scan and reconcile the package.
      */
     private static class PrepareResult {
-        public final int installReason;
-        public final String volumeUuid;
-        public final String installerPackageName;
-        public final UserHandle user;
         public final boolean replace;
         public final int scanFlags;
         public final int parseFlags;
@@ -16079,24 +16008,16 @@
         public final PackageParser.Package packageToScan;
         public final boolean clearCodeCache;
         public final boolean system;
-        /* The original package name if it was changed during an update, otherwise {@code null}. */
-        @Nullable
-        public final String renamedPackage;
         public final PackageFreezer freezer;
         public final PackageSetting originalPs;
         public final PackageSetting disabledPs;
         public final PackageSetting[] childPackageSettings;
 
-        private PrepareResult(int installReason, String volumeUuid,
-                String installerPackageName, UserHandle user, boolean replace, int scanFlags,
+        private PrepareResult(boolean replace, int scanFlags,
                 int parseFlags, PackageParser.Package existingPackage,
                 PackageParser.Package packageToScan, boolean clearCodeCache, boolean system,
-                String renamedPackage, PackageFreezer freezer, PackageSetting originalPs,
+                PackageFreezer freezer, PackageSetting originalPs,
                 PackageSetting disabledPs, PackageSetting[] childPackageSettings) {
-            this.installReason = installReason;
-            this.volumeUuid = volumeUuid;
-            this.installerPackageName = installerPackageName;
-            this.user = user;
             this.replace = replace;
             this.scanFlags = scanFlags;
             this.parseFlags = parseFlags;
@@ -16104,7 +16025,6 @@
             this.packageToScan = packageToScan;
             this.clearCodeCache = clearCodeCache;
             this.system = system;
-            this.renamedPackage = renamedPackage;
             this.freezer = freezer;
             this.originalPs = originalPs;
             this.disabledPs = disabledPs;
@@ -16144,8 +16064,6 @@
     private PrepareResult preparePackageLI(InstallArgs args, PackageInstalledInfo res)
             throws PrepareFailure {
         final int installFlags = args.installFlags;
-        final String installerPackageName = args.installerPackageName;
-        final String volumeUuid = args.volumeUuid;
         final File tmpPackageFile = new File(args.getCodePath());
         final boolean onExternal = args.volumeUuid != null;
         final boolean instantApp = ((installFlags & PackageManager.INSTALL_INSTANT_APP) != 0);
@@ -16570,14 +16488,12 @@
             final PackageParser.Package existingPackage;
             String renamedPackage = null;
             boolean sysPkg = false;
-            String targetVolumeUuid = volumeUuid;
             int targetScanFlags = scanFlags;
             int targetParseFlags = parseFlags;
             final PackageSetting ps;
             final PackageSetting disabledPs;
             final PackageSetting[] childPackages;
             if (replace) {
-                targetVolumeUuid = null;
                 if (pkg.applicationInfo.isStaticSharedLibrary()) {
                     // Static libs have a synthetic package name containing the version
                     // and cannot be updated as an update would get a new package name,
@@ -16829,9 +16745,8 @@
             // we're passing the freezer back to be closed in a later phase of install
             shouldCloseFreezerBeforeReturn = false;
 
-            return new PrepareResult(args.installReason, targetVolumeUuid, installerPackageName,
-                    args.user, replace, targetScanFlags, targetParseFlags, existingPackage, pkg,
-                    replace /* clearCodeCache */, sysPkg, renamedPackage, freezer,
+            return new PrepareResult(replace, targetScanFlags, targetParseFlags,
+                    existingPackage, pkg, replace /* clearCodeCache */, sysPkg, freezer,
                     ps, disabledPs, childPackages);
         } finally {
             if (shouldCloseFreezerBeforeReturn) {
@@ -22942,6 +22857,8 @@
                     return new String[]{mAppPredictionServicePackage};
                 case PackageManagerInternal.PACKAGE_TELEPHONY:
                     return mTelephonyPackages;
+                case PackageManagerInternal.PACKAGE_WIFI:
+                    return new String[]{mWifiPackage};
             }
             return ArrayUtils.emptyArray(String.class);
         }
diff --git a/services/core/java/com/android/server/pm/permission/BasePermission.java b/services/core/java/com/android/server/pm/permission/BasePermission.java
index c39dcfe..037912a 100644
--- a/services/core/java/com/android/server/pm/permission/BasePermission.java
+++ b/services/core/java/com/android/server/pm/permission/BasePermission.java
@@ -279,6 +279,9 @@
     public boolean isTelephony() {
         return (protectionLevel & PermissionInfo.PROTECTION_FLAG_TELEPHONY) != 0;
     }
+    public boolean isWifi() {
+        return (protectionLevel & PermissionInfo.PROTECTION_FLAG_WIFI) != 0;
+    }
 
     public void transfer(@NonNull String origPackageName, @NonNull String newPackageName) {
         if (!origPackageName.equals(sourcePackageName)) {
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 53cd530..3f45b0b 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -3282,6 +3282,13 @@
                 // Special permissions for the system telephony apps.
                 allowed = true;
             }
+            if (!allowed && bp.isWifi()
+                    && ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames(
+                        PackageManagerInternal.PACKAGE_WIFI, UserHandle.USER_SYSTEM),
+                    pkg.packageName)) {
+                // Special permissions for the system wifi.
+                allowed = true;
+            }
         }
         return allowed;
     }
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index c851cc6..2593c38 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -47,8 +47,8 @@
 import static android.view.WindowManager.LayoutParams.LAST_SYSTEM_WINDOW;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SYSTEM_ERROR;
+import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
 import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
@@ -2189,10 +2189,10 @@
             default:
                 // These are the windows that by default are shown only to the user that created
                 // them. If this needs to be overridden, set
-                // {@link WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS} in
+                // {@link WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS} in
                 // {@link WindowManager.LayoutParams}. Note that permission
                 // {@link android.Manifest.permission.INTERNAL_SYSTEM_WINDOW} is required as well.
-                if ((attrs.privateFlags & PRIVATE_FLAG_SHOW_FOR_ALL_USERS) == 0) {
+                if ((attrs.privateFlags & SYSTEM_FLAG_SHOW_FOR_ALL_USERS) == 0) {
                     return true;
                 }
                 break;
@@ -2446,7 +2446,7 @@
                     com.android.internal.R.styleable.Window_windowAnimationStyle, 0);
             params.privateFlags |=
                     WindowManager.LayoutParams.PRIVATE_FLAG_FAKE_HARDWARE_ACCELERATED;
-            params.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+            params.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
 
             if (!compatInfo.supportsScreen()) {
                 params.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 3439d38..65bb2342 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -29,6 +29,7 @@
 import android.hardware.biometrics.IBiometricServiceReceiverInternal;
 import android.hardware.display.DisplayManager;
 import android.hardware.display.DisplayManager.DisplayListener;
+import android.net.Uri;
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Handler;
@@ -1334,6 +1335,18 @@
     }
 
     @Override
+    public void grantInlineReplyUriPermission(String key, Uri uri) {
+        enforceStatusBarService();
+        int callingUid = Binder.getCallingUid();
+        long identity = Binder.clearCallingIdentity();
+        try {
+            mNotificationDelegate.grantInlineReplyUriPermission(key, uri, callingUid);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    @Override
     public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
             String[] args, ShellCallback callback, ResultReceiver resultReceiver) {
         (new StatusBarShellCommand(this, mContext)).exec(
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLaunchObserver.java b/services/core/java/com/android/server/wm/ActivityMetricsLaunchObserver.java
index eff0f75..c6b17e2 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLaunchObserver.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLaunchObserver.java
@@ -39,8 +39,13 @@
  * If an activity is successfully started, the launch sequence's state will transition into
  * {@code STARTED} via {@link #onActivityLaunched}. This is a transient state.
  *
- * It must then transition to either {@code CANCELLED} with {@link #onActivityLaunchCancelled}
- * or into {@code FINISHED} with {@link #onActivityLaunchFinished}. These are terminal states.
+ * It must then transition to either {@code CANCELLED} with {@link #onActivityLaunchCancelled},
+ * which is a terminal state or into {@code FINISHED} with {@link #onActivityLaunchFinished}.
+ *
+ * The {@code FINISHED} with {@link #onActivityLaunchFinished} then may transition to
+ * {@code FULLY_DRAWN} with {@link #onReportFullyDrawn}, which is a terminal state.
+ * Note this transition may not happen if the reportFullyDrawn event is not receivied,
+ * in which case {@code FINISHED} is terminal.
  *
  * Note that the {@code ActivityRecordProto} provided as a parameter to some state transitions isn't
  * necessarily the same within a single launch sequence: it is only the top-most activity at the
@@ -51,15 +56,15 @@
  * until a subsequent transition into {@code INTENT_STARTED} initiates a new launch sequence.
  *
  * <pre>
- *        ┌⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯┐     ┌⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯┐     ╔══════════════════════════╗
- *    ╴╴▶ ⋮ INTENT_STARTED ⋮ ──▶ ⋮     ACTIVITY_LAUNCHED     ⋮ ──▶ ║ ACTIVITY_LAUNCH_FINISHED ║
- *        └⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯┘     └⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯┘     ╚══════════════════════════╝
- *          :                      :
- *          :                      :
- *          ▼                      ▼
- *        ╔════════════════╗     ╔═══════════════════════════╗
- *        ║ INTENT_FAILED  ║     ║ ACTIVITY_LAUNCH_CANCELLED ║
- *        ╚════════════════╝     ╚═══════════════════════════╝
+ *        ┌⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯┐     ┌⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯┐     ┌--------------------------┐
+ *    ╴╴▶  INTENT_STARTED    ──▶      ACTIVITY_LAUNCHED        ──▶   ACTIVITY_LAUNCH_FINISHED
+ *        └⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯┘     └⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯┘     └--------------------------┘
+ *          :                      :                                 :
+ *          :                      :                                 :
+ *          ▼                      ▼                                 ▼
+ *        ╔════════════════╗     ╔═══════════════════════════╗     ╔═══════════════════════════╗
+ *        ║ INTENT_FAILED  ║     ║ ACTIVITY_LAUNCH_CANCELLED ║     ║    REPORT_FULLY_DRAWN     ║
+ *        ╚════════════════╝     ╚═══════════════════════════╝     ╚═══════════════════════════╝
  * </pre>
  */
 public interface ActivityMetricsLaunchObserver {
@@ -111,7 +116,7 @@
      * Multiple calls to this method cannot occur without first terminating the current
      * launch sequence.
      */
-    public void onIntentStarted(@NonNull Intent intent);
+    public void onIntentStarted(@NonNull Intent intent, long timestampNanos);
 
     /**
      * Notifies the observer that the current launch sequence has failed to launch an activity.
@@ -177,6 +182,9 @@
      * drawn for the first time: the top-most activity at the time is what's reported here.
      *
      * @param finalActivity the top-most activity whose windows were first to fully draw
+     * @param timestampNanos the timestamp of ActivityLaunchFinished event in nanoseconds.
+     *        To compute the TotalTime duration, deduct the timestamp {@link #onIntentStarted}
+     *        from {@code timestampNanos}.
      *
      * Multiple calls to this method cannot occur without first terminating the current
      * launch sequence.
@@ -186,5 +194,22 @@
      *          and only the latest activity that was top-most during first-frame drawn
      *          is reported here.
      */
-    public void onActivityLaunchFinished(@NonNull @ActivityRecordProto byte[] finalActivity);
+    public void onActivityLaunchFinished(@NonNull @ActivityRecordProto byte[] finalActivity,
+                                         long timestampNanos);
+
+    /**
+     * Notifies the observer that the application self-reported itself as being fully drawn.
+     *
+     * @param activity the activity that triggers the ReportFullyDrawn event.
+     * @param timestampNanos the timestamp of ReportFullyDrawn event in nanoseconds.
+     *        To compute the duration, deduct the deduct the timestamp {@link #onIntentStarted}
+     *        from {@code timestampNanos}.
+     *
+     * @apiNote The behavior of ReportFullyDrawn mostly depends on the app.
+     *          It is used as an accurate estimate of meanfully app startup time.
+     *          This event may be missing for many apps.
+     */
+    public void onReportFullyDrawn(@NonNull @ActivityRecordProto byte[] activity,
+        long timestampNanos);
+
 }
diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
index ef8c020..e6c6b12e 100644
--- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
+++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java
@@ -92,6 +92,7 @@
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.os.SomeArgs;
 import com.android.server.LocalServices;
+import java.util.concurrent.TimeUnit;
 
 /**
  * Listens to activity launches, transitions, visibility changes and window drawn callbacks to
@@ -132,8 +133,8 @@
 
     // set to INVALID_START_TIME in reset.
     // set to valid value in notifyActivityLaunching
-    private long mCurrentTransitionStartTime = INVALID_START_TIME;
-    private long mLastTransitionStartTime = INVALID_START_TIME;
+    private long mCurrentTransitionStartTimeNs = INVALID_START_TIME;
+    private long mLastTransitionStartTimeNs = INVALID_START_TIME;
 
     private int mCurrentTransitionDeviceUptime;
     private int mCurrentTransitionDelayMs;
@@ -326,12 +327,12 @@
                                       intent));
         }
 
-        if (mCurrentTransitionStartTime == INVALID_START_TIME) {
+        if (mCurrentTransitionStartTimeNs == INVALID_START_TIME) {
 
-            mCurrentTransitionStartTime = SystemClock.uptimeMillis();
-            mLastTransitionStartTime = mCurrentTransitionStartTime;
+            mCurrentTransitionStartTimeNs = SystemClock.elapsedRealtimeNanos();
+            mLastTransitionStartTimeNs = mCurrentTransitionStartTimeNs;
 
-            launchObserverNotifyIntentStarted(intent);
+            launchObserverNotifyIntentStarted(intent, mCurrentTransitionStartTimeNs);
         }
     }
 
@@ -382,14 +383,15 @@
                 ? launchedActivity.getWindowingMode()
                 : WINDOWING_MODE_UNDEFINED;
         final WindowingModeTransitionInfo info = mWindowingModeTransitionInfo.get(windowingMode);
-        if (mCurrentTransitionStartTime == INVALID_START_TIME) {
+        if (mCurrentTransitionStartTimeNs == INVALID_START_TIME) {
             // No transition is active ignore this launch.
             return;
         }
 
         if (launchedActivity != null && launchedActivity.mDrawn) {
             // Launched activity is already visible. We cannot measure windows drawn delay.
-            reset(true /* abort */, info, "launched activity already visible");
+            reset(true /* abort */, info, "launched activity already visible",
+                0L /* timestampNs */);
             return;
         }
 
@@ -407,7 +409,8 @@
         if ((!isLoggableResultCode(resultCode) || launchedActivity == null || !processSwitch
                 || windowingMode == WINDOWING_MODE_UNDEFINED) && !otherWindowModesLaunching) {
             // Failed to launch or it was not a process switch, so we don't care about the timing.
-            reset(true /* abort */, info, "failed to launch or not a process switch");
+            reset(true /* abort */, info, "failed to launch or not a process switch",
+                0L /* timestampNs */);
             return;
         } else if (otherWindowModesLaunching) {
             // Don't log this windowing mode but continue with the other windowing modes.
@@ -441,19 +444,20 @@
      * Notifies the tracker that all windows of the app have been drawn.
      */
     WindowingModeTransitionInfoSnapshot notifyWindowsDrawn(@WindowingMode int windowingMode,
-                                                           long timestamp) {
+                                                           long timestampNs) {
         if (DEBUG_METRICS) Slog.i(TAG, "notifyWindowsDrawn windowingMode=" + windowingMode);
 
         final WindowingModeTransitionInfo info = mWindowingModeTransitionInfo.get(windowingMode);
         if (info == null || info.loggedWindowsDrawn) {
             return null;
         }
-        info.windowsDrawnDelayMs = calculateDelay(timestamp);
+        info.windowsDrawnDelayMs = calculateDelay(timestampNs);
         info.loggedWindowsDrawn = true;
         final WindowingModeTransitionInfoSnapshot infoSnapshot =
                 new WindowingModeTransitionInfoSnapshot(info);
         if (allWindowsDrawn() && mLoggedTransitionStarting) {
-            reset(false /* abort */, info, "notifyWindowsDrawn - all windows drawn");
+            reset(false /* abort */, info, "notifyWindowsDrawn - all windows drawn",
+                timestampNs /* timestampNs */);
         }
         return infoSnapshot;
     }
@@ -476,7 +480,7 @@
      * @param windowingModeToReason A map from windowing mode to a reason integer, which must be on
      *                              of ActivityTaskManagerInternal.APP_TRANSITION_* reasons.
      */
-    void notifyTransitionStarting(SparseIntArray windowingModeToReason, long timestamp) {
+    void notifyTransitionStarting(SparseIntArray windowingModeToReason, long timestampNs) {
         if (!isAnyTransitionActive() || mLoggedTransitionStarting) {
             // Ignore calls to this made after a reset and prior to notifyActivityLaunching.
 
@@ -484,7 +488,7 @@
             return;
         }
         if (DEBUG_METRICS) Slog.i(TAG, "notifyTransitionStarting");
-        mCurrentTransitionDelayMs = calculateDelay(timestamp);
+        mCurrentTransitionDelayMs = calculateDelay(timestampNs);
         mLoggedTransitionStarting = true;
 
         WindowingModeTransitionInfo foundInfo = null;
@@ -501,7 +505,8 @@
         if (allWindowsDrawn()) {
             // abort metrics collection if we cannot find a matching transition.
             final boolean abortMetrics = foundInfo == null;
-            reset(abortMetrics, foundInfo, "notifyTransitionStarting - all windows drawn");
+            reset(abortMetrics, foundInfo, "notifyTransitionStarting - all windows drawn",
+                timestampNs /* timestampNs */);
         }
     }
 
@@ -567,7 +572,8 @@
             logAppTransitionCancel(info);
             mWindowingModeTransitionInfo.remove(r.getWindowingMode());
             if (mWindowingModeTransitionInfo.size() == 0) {
-                reset(true /* abort */, info, "notifyVisibilityChanged to invisible");
+                reset(true /* abort */, info, "notifyVisibilityChanged to invisible",
+                    0L /* timestampNs */);
             }
         }
     }
@@ -598,12 +604,16 @@
     }
 
     private boolean isAnyTransitionActive() {
-        return mCurrentTransitionStartTime != INVALID_START_TIME
+        return mCurrentTransitionStartTimeNs != INVALID_START_TIME
                 && mWindowingModeTransitionInfo.size() > 0;
     }
 
-    private void reset(boolean abort, WindowingModeTransitionInfo info, String cause) {
-        if (DEBUG_METRICS) Slog.i(TAG, "reset abort=" + abort + ",cause=" + cause);
+    private void reset(boolean abort, WindowingModeTransitionInfo info, String cause,
+        long timestampNs) {
+        if (DEBUG_METRICS) {
+            Slog.i(TAG,
+                "reset abort=" + abort + ",cause=" + cause + ",timestamp=" + timestampNs);
+        }
         if (!abort && isAnyTransitionActive()) {
             logAppTransitionMultiEvents();
         }
@@ -615,13 +625,13 @@
             if (abort) {
                 launchObserverNotifyActivityLaunchCancelled(info);
             } else {
-                launchObserverNotifyActivityLaunchFinished(info);
+                launchObserverNotifyActivityLaunchFinished(info, timestampNs);
             }
         } else {
             launchObserverNotifyIntentFailed();
         }
 
-        mCurrentTransitionStartTime = INVALID_START_TIME;
+        mCurrentTransitionStartTimeNs = INVALID_START_TIME;
         mCurrentTransitionDelayMs = INVALID_DELAY;
         mLoggedTransitionStarting = false;
         mWindowingModeTransitionInfo.clear();
@@ -629,12 +639,14 @@
 
     private int calculateCurrentDelay() {
         // Shouldn't take more than 25 days to launch an app, so int is fine here.
-        return (int) (SystemClock.uptimeMillis() - mCurrentTransitionStartTime);
+        return (int) TimeUnit.NANOSECONDS
+            .toMillis(SystemClock.elapsedRealtimeNanos() - mCurrentTransitionStartTimeNs);
     }
 
-    private int calculateDelay(long timestamp) {
+    private int calculateDelay(long timestampNs) {
         // Shouldn't take more than 25 days to launch an app, so int is fine here.
-        return (int) (timestamp - mCurrentTransitionStartTime);
+        return (int) TimeUnit.NANOSECONDS.toMillis(timestampNs -
+            mCurrentTransitionStartTimeNs);
     }
 
     private void logAppTransitionCancel(WindowingModeTransitionInfo info) {
@@ -679,7 +691,7 @@
             // Take a snapshot of the transition info before sending it to the handler for logging.
             // This will avoid any races with other operations that modify the ActivityRecord.
             final WindowingModeTransitionInfoSnapshot infoSnapshot =
-                    new WindowingModeTransitionInfoSnapshot(info);
+                     new WindowingModeTransitionInfoSnapshot(info);
             final int currentTransitionDeviceUptime = mCurrentTransitionDeviceUptime;
             final int currentTransitionDelayMs = mCurrentTransitionDelayMs;
             BackgroundThread.getHandler().post(() -> logAppTransition(
@@ -811,7 +823,9 @@
         final LogMaker builder = new LogMaker(APP_TRANSITION_REPORTED_DRAWN);
         builder.setPackageName(r.packageName);
         builder.addTaggedData(FIELD_CLASS_NAME, r.info.name);
-        long startupTimeMs = SystemClock.uptimeMillis() - mLastTransitionStartTime;
+        long currentTimestampNs = SystemClock.elapsedRealtimeNanos();
+        long startupTimeMs =
+            TimeUnit.NANOSECONDS.toMillis(currentTimestampNs - mLastTransitionStartTimeNs);
         builder.addTaggedData(APP_TRANSITION_REPORTED_DRAWN_MS, startupTimeMs);
         builder.setType(restoredFromBundle
                 ? TYPE_TRANSITION_REPORTED_DRAWN_WITH_BUNDLE
@@ -837,6 +851,10 @@
         final WindowingModeTransitionInfoSnapshot infoSnapshot =
                 new WindowingModeTransitionInfoSnapshot(info, r, (int) startupTimeMs);
         BackgroundThread.getHandler().post(() -> logAppFullyDrawn(infoSnapshot));
+
+        // Notify reportFullyDrawn event.
+        launchObserverNotifyReportFullyDrawn(r, currentTimestampNs);
+
         return infoSnapshot;
     }
 
@@ -1006,12 +1024,12 @@
     }
 
     /** Notify the {@link ActivityMetricsLaunchObserver} that a new launch sequence has begun. */
-    private void launchObserverNotifyIntentStarted(Intent intent) {
+    private void launchObserverNotifyIntentStarted(Intent intent, long timestampNs) {
         Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
                 "MetricsLogger:launchObserverNotifyIntentStarted");
 
         // Beginning a launch is timing sensitive and so should be observed as soon as possible.
-        mLaunchObserver.onIntentStarted(intent);
+        mLaunchObserver.onIntentStarted(intent, timestampNs);
 
         Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
     }
@@ -1049,6 +1067,16 @@
     }
 
     /**
+     * Notifies the {@link ActivityMetricsLaunchObserver} the reportFullDrawn event.
+     */
+    private void launchObserverNotifyReportFullyDrawn(ActivityRecord r, long timestampNs) {
+        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+            "MetricsLogger:launchObserverNotifyReportFullyDrawn");
+        mLaunchObserver.onReportFullyDrawn(convertActivityRecordToProto(r), timestampNs);
+        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+    }
+
+    /**
      * Notify the {@link ActivityMetricsLaunchObserver} that the current launch sequence is
      * cancelled.
      */
@@ -1068,12 +1096,14 @@
      * Notify the {@link ActivityMetricsLaunchObserver} that the current launch sequence's activity
      * has fully finished (successfully).
      */
-    private void launchObserverNotifyActivityLaunchFinished(WindowingModeTransitionInfo info) {
+    private void launchObserverNotifyActivityLaunchFinished(WindowingModeTransitionInfo info,
+        long timestampNs) {
         Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
                 "MetricsLogger:launchObserverNotifyActivityLaunchFinished");
 
-        mLaunchObserver.onActivityLaunchFinished(
-                convertActivityRecordToProto(info.launchedActivity));
+        mLaunchObserver
+            .onActivityLaunchFinished(convertActivityRecordToProto(info.launchedActivity),
+                timestampNs);
 
         Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
     }
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 16e617c..76a551f 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -144,10 +144,10 @@
 import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE;
 import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
 import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE;
+import static com.android.server.wm.ActivityTaskManagerService.getInputDispatchingTimeoutLocked;
 import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
 import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ORIENTATION;
 import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_STARTING_WINDOW;
-import static com.android.server.wm.ActivityTaskManagerService.getInputDispatchingTimeoutLocked;
 import static com.android.server.wm.TaskPersister.DEBUG;
 import static com.android.server.wm.TaskPersister.IMAGE_EXTENSION;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
@@ -318,6 +318,7 @@
     ArrayList<ResultInfo> results; // pending ActivityResult objs we have received
     HashSet<WeakReference<PendingIntentRecord>> pendingResults; // all pending intents for this act
     ArrayList<ReferrerIntent> newIntents; // any pending new intents for single-top mode
+    Intent mLastNewIntent;  // the last new intent we delivered to client
     ActivityOptions pendingOptions; // most recently given options
     ActivityOptions returningOptions; // options that are coming back via convertToTranslucent
     AppTimeTracker appTimeTracker; // set if we are tracking the time in this app/task/activity
@@ -2844,6 +2845,9 @@
         }
         idle = false;
         results = null;
+        if (newIntents != null && newIntents.size() > 0) {
+            mLastNewIntent = newIntents.get(newIntents.size() - 1);
+        }
         newIntents = null;
         stopped = false;
 
@@ -3102,7 +3106,7 @@
 
     void reportFullyDrawnLocked(boolean restoredFromBundle) {
         final WindowingModeTransitionInfoSnapshot info = mStackSupervisor
-                .getActivityMetricsLogger().logAppTransitionReportedDrawn(this, restoredFromBundle);
+            .getActivityMetricsLogger().logAppTransitionReportedDrawn(this, restoredFromBundle);
         if (info != null) {
             mStackSupervisor.reportActivityLaunchedLocked(false /* timeout */, this,
                     info.windowsFullyDrawnDelayMs, info.getLaunchState());
@@ -3110,13 +3114,13 @@
     }
 
     /** Called when the windows associated app window container are drawn. */
-    void onWindowsDrawn(boolean drawn, long timestamp) {
+    void onWindowsDrawn(boolean drawn, long timestampNs) {
         mDrawn = drawn;
         if (!drawn) {
             return;
         }
         final WindowingModeTransitionInfoSnapshot info = mStackSupervisor
-                .getActivityMetricsLogger().notifyWindowsDrawn(getWindowingMode(), timestamp);
+            .getActivityMetricsLogger().notifyWindowsDrawn(getWindowingMode(), timestampNs);
         final int windowsDrawnDelayMs = info != null ? info.windowsDrawnDelayMs : INVALID_DELAY;
         final @LaunchState int launchState = info != null ? info.getLaunchState() : -1;
         mStackSupervisor.reportActivityLaunchedLocked(false /* timeout */, this,
@@ -4198,12 +4202,19 @@
             return true;
         }
 
-        // Restrict task snapshot starting window to launcher start, or there is no intent at all
-        // (eg. task being brought to front). If the intent is something else, likely the app is
-        // going to show some specific page or view, instead of what's left last time.
+        // Restrict task snapshot starting window to launcher start, or is same as the last
+        // delivered intent, or there is no intent at all (eg. task being brought to front). If
+        // the intent is something else, likely the app is going to show some specific page or
+        // view, instead of what's left last time.
         for (int i = newIntents.size() - 1; i >= 0; i--) {
             final Intent intent = newIntents.get(i);
-            if (intent != null && !ActivityRecord.isMainIntent(intent)) {
+            if (intent == null || ActivityRecord.isMainIntent(intent)) {
+                continue;
+            }
+
+            final boolean sameIntent = mLastNewIntent != null ? mLastNewIntent.filterEquals(intent)
+                    : this.intent.filterEquals(intent);
+            if (!sameIntent || intent.getExtras() != null) {
                 return false;
             }
         }
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index 11c1e36..41c1e4e 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -33,7 +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.os.Trace.TRACE_TAG_WINDOW_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;
@@ -4342,7 +4342,7 @@
             return;
         }
 
-        Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "stack.resize_" + mStackId);
+        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "stack.resize_" + mStackId);
         mService.deferWindowLayout();
         try {
             // Update override configurations of all tasks in the stack.
@@ -4368,7 +4368,7 @@
             }
         } finally {
             mService.continueWindowLayout();
-            Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index a262f16..7753f57 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -44,7 +44,7 @@
 import static android.os.PowerManager.PARTIAL_WAKE_LOCK;
 import static android.os.Process.INVALID_UID;
 import static android.os.Process.SYSTEM_UID;
-import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
+import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.Display.INVALID_DISPLAY;
 import static android.view.Display.TYPE_VIRTUAL;
@@ -690,7 +690,7 @@
     ResolveInfo resolveIntent(Intent intent, String resolvedType, int userId, int flags,
             int filterCallingUid) {
         try {
-            Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "resolveIntent");
+            Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "resolveIntent");
             int modifiedFlags = flags
                     | PackageManager.MATCH_DEFAULT_ONLY | ActivityManagerService.STOCK_PM_FLAGS;
             if (intent.isWebIntent()
@@ -711,7 +711,7 @@
                 Binder.restoreCallingIdentity(token);
             }
         } finally {
-            Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
         }
     }
 
@@ -1629,7 +1629,7 @@
             mPendingTempOtherTaskInsetBounds = copyOrNull(tempOtherTaskInsetBounds);
         }
 
-        Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizeDockedStack");
+        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "resizeDockedStack");
         mService.deferWindowLayout();
         try {
             // Don't allow re-entry while resizing. E.g. due to docked stack detaching.
@@ -1695,7 +1695,7 @@
         } finally {
             mAllowDockedStackResize = true;
             mService.continueWindowLayout();
-            Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
         }
     }
 
@@ -1717,7 +1717,7 @@
             return;
         }
 
-        Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizePinnedStack");
+        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "resizePinnedStack");
         mService.deferWindowLayout();
         try {
             Rect insetBounds = null;
@@ -1739,7 +1739,7 @@
                     !DEFER_RESUME);
         } finally {
             mService.continueWindowLayout();
-            Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index b68ba7c..1a80006 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -770,13 +770,13 @@
         boolean restrictedBgActivity = false;
         if (!abort) {
             try {
-                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
+                Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER,
                         "shouldAbortBackgroundActivityStart");
                 restrictedBgActivity = shouldAbortBackgroundActivityStart(callingUid,
                         callingPid, callingPackage, realCallingUid, realCallingPid, callerApp,
                         originatingPendingIntent, allowBackgroundActivityStart, intent);
             } finally {
-                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+                Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
             }
         }
 
@@ -1401,41 +1401,12 @@
         final ActivityStack startedActivityStack;
         try {
             mService.deferWindowLayout();
-            result = startActivityUnchecked(r, sourceRecord, voiceSession, voiceInteractor,
+            Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "startActivityInner");
+            result = startActivityInner(r, sourceRecord, voiceSession, voiceInteractor,
                     startFlags, doResume, options, inTask, outActivity, restrictedBgActivity);
         } finally {
-            final ActivityStack currentStack = r.getActivityStack();
-            startedActivityStack = currentStack != null ? currentStack : mTargetStack;
-
-            if (ActivityManager.isStartResultSuccessful(result)) {
-                if (startedActivityStack != null) {
-                    // If there is no state change (e.g. a resumed activity is reparented to
-                    // top of another display) to trigger a visibility/configuration checking,
-                    // we have to update the configuration for changing to different display.
-                    final ActivityRecord currentTop =
-                            startedActivityStack.topRunningActivityLocked();
-                    if (currentTop != null && currentTop.shouldUpdateConfigForDisplayChanged()) {
-                        mRootActivityContainer.ensureVisibilityAndConfig(
-                                currentTop, currentTop.getDisplayId(),
-                                true /* markFrozenIfConfigChanged */, false /* deferResume */);
-                    }
-                }
-            } else {
-                // If we are not able to proceed, disassociate the activity from the task.
-                // Leaving an activity in an incomplete state can lead to issues, such as
-                // performing operations without a window container.
-                final ActivityStack stack = mStartActivity.getActivityStack();
-                if (stack != null) {
-                    mStartActivity.finishIfPossible("startActivity", true /* oomAdj */);
-                }
-
-                // Stack should also be detached from display and be removed if it's empty.
-                if (startedActivityStack != null && startedActivityStack.isAttached()
-                        && startedActivityStack.numActivities() == 0
-                        && !startedActivityStack.isActivityTypeHome()) {
-                    startedActivityStack.remove();
-                }
-            }
+            Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
+            startedActivityStack = handleStartResult(r, result);
             mService.continueWindowLayout();
         }
 
@@ -1445,6 +1416,49 @@
     }
 
     /**
+     * If the start result is success, ensure that the configuration of the started activity matches
+     * the current display. Otherwise clean up unassociated containers to avoid leakage.
+     *
+     * @return the stack where the successful started activity resides.
+     */
+    private @Nullable ActivityStack handleStartResult(@NonNull ActivityRecord started, int result) {
+        final ActivityStack currentStack = started.getActivityStack();
+        ActivityStack startedActivityStack = currentStack != null ? currentStack : mTargetStack;
+
+        if (ActivityManager.isStartResultSuccessful(result)) {
+            if (startedActivityStack != null) {
+                // If there is no state change (e.g. a resumed activity is reparented to top of
+                // another display) to trigger a visibility/configuration checking, we have to
+                // update the configuration for changing to different display.
+                final ActivityRecord currentTop = startedActivityStack.topRunningActivityLocked();
+                if (currentTop != null && currentTop.shouldUpdateConfigForDisplayChanged()) {
+                    mRootActivityContainer.ensureVisibilityAndConfig(
+                            currentTop, currentTop.getDisplayId(),
+                            true /* markFrozenIfConfigChanged */, false /* deferResume */);
+                }
+            }
+            return startedActivityStack;
+        }
+
+        // If we are not able to proceed, disassociate the activity from the task. Leaving an
+        // activity in an incomplete state can lead to issues, such as performing operations
+        // without a window container.
+        final ActivityStack stack = mStartActivity.getActivityStack();
+        if (stack != null) {
+            mStartActivity.finishIfPossible("startActivity", true /* oomAdj */);
+        }
+
+        // Stack should also be detached from display and be removed if it's empty.
+        if (startedActivityStack != null && startedActivityStack.isAttached()
+                && startedActivityStack.numActivities() == 0
+                && !startedActivityStack.isActivityTypeHome()) {
+            startedActivityStack.remove();
+            startedActivityStack = null;
+        }
+        return startedActivityStack;
+    }
+
+    /**
      * Return true if background activity is really aborted.
      *
      * TODO(b/131748165): Refactor the logic so we don't need to call this method everywhere.
@@ -1469,7 +1483,7 @@
     }
 
     // Note: This method should only be called from {@link startActivity}.
-    private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord,
+    private int startActivityInner(final ActivityRecord r, ActivityRecord sourceRecord,
             IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
             int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask,
             ActivityRecord[] outActivity, boolean restrictedBgActivity) {
@@ -1592,7 +1606,7 @@
                 // accordingly.
                 if (mTargetStack.isFocusable()
                         && !mRootActivityContainer.isTopDisplayFocusedStack(mTargetStack)) {
-                    mTargetStack.moveToFront("startActivityUnchecked");
+                    mTargetStack.moveToFront("startActivityInner");
                 }
                 mRootActivityContainer.resumeFocusedStacksTopActivities(
                         mTargetStack, mStartActivity, mOptions);
@@ -1873,7 +1887,7 @@
                     mTargetStack = computeStackFocus(mSourceRecord, false /* newTask */,
                             mLaunchFlags, mOptions);
                     mTargetStack.addTask(targetTask,
-                            !mLaunchTaskBehind /* toTop */, "startActivityUnchecked");
+                            !mLaunchTaskBehind /* toTop */, "complyActivityFlags");
                 }
             }
         } else if ((mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) == 0 && !mAddingToTask
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index ab35652..0488a3b 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -159,10 +159,10 @@
      *
      * @param reasons A map from windowing mode to a reason integer why the transition was started,
      *                which must be one of the APP_TRANSITION_* values.
-     * @param timestamp The time at which the app transition started in
-     *                  {@link SystemClock#uptimeMillis()} timebase.
+     * @param timestampNs The time at which the app transition started in
+     *                  {@link SystemClock#elapsedRealtimeNs()} ()} timebase.
      */
-    public abstract void notifyAppTransitionStarting(SparseIntArray reasons, long timestamp);
+    public abstract void notifyAppTransitionStarting(SparseIntArray reasons, long timestampNs);
 
     /**
      * Callback for window manager to let activity manager know that the app transition was
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 7ce3e79..20113a6 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -52,7 +52,7 @@
 import static android.os.FactoryTest.FACTORY_TEST_OFF;
 import static android.os.Process.FIRST_APPLICATION_UID;
 import static android.os.Process.SYSTEM_UID;
-import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
+import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
 import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT;
 import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES;
 import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RTL;
@@ -1606,6 +1606,7 @@
             }
 
             final long origId = Binder.clearCallingIdentity();
+            Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "finishActivity");
             try {
                 boolean res;
                 final boolean finishWithRootActivity =
@@ -1633,6 +1634,7 @@
                 }
                 return res;
             } finally {
+                Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
                 Binder.restoreCallingIdentity(origId);
             }
         }
@@ -1667,6 +1669,7 @@
         try {
             WindowProcessController proc = null;
             synchronized (mGlobalLock) {
+                Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "activityIdle");
                 ActivityStack stack = ActivityRecord.getStackLocked(token);
                 if (stack == null) {
                     return;
@@ -1681,6 +1684,7 @@
                 }
             }
         } finally {
+            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
             Binder.restoreCallingIdentity(origId);
         }
     }
@@ -1707,10 +1711,12 @@
     public final void activityPaused(IBinder token) {
         final long origId = Binder.clearCallingIdentity();
         synchronized (mGlobalLock) {
+            Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "activityPaused");
             ActivityStack stack = ActivityRecord.getStackLocked(token);
             if (stack != null) {
                 stack.activityPausedLocked(token, false);
             }
+            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
         }
         Binder.restoreCallingIdentity(origId);
     }
@@ -1731,6 +1737,7 @@
         int restartingUid = 0;
         final ActivityRecord r;
         synchronized (mGlobalLock) {
+            Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "activityStopped");
             r = ActivityRecord.isInStackLocked(token);
             if (r != null) {
                 if (r.attachedToProcess()
@@ -1742,6 +1749,7 @@
                 }
                 r.activityStoppedLocked(icicle, persistentState, description);
             }
+            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
         }
 
         if (restartingName != null) {
@@ -1763,12 +1771,14 @@
         if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "ACTIVITY DESTROYED: " + token);
         synchronized (mGlobalLock) {
             final long origId = Binder.clearCallingIdentity();
+            Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "activityDestroyed");
             try {
                 final ActivityRecord activity = ActivityRecord.forTokenLocked(token);
                 if (activity != null) {
                     activity.destroyed("activityDestroyed");
                 }
             } finally {
+                Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
                 Binder.restoreCallingIdentity(origId);
             }
         }
@@ -5506,8 +5516,8 @@
     void startProcessAsync(ActivityRecord activity, boolean knownToBeDead, boolean isTop,
             String hostingType) {
         try {
-            if (Trace.isTagEnabled(TRACE_TAG_ACTIVITY_MANAGER)) {
-                Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "dispatchingStartProcess:"
+            if (Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER)) {
+                Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "dispatchingStartProcess:"
                         + activity.processName);
             }
             // Post message to start process to avoid possible deadlock of calling into AMS with the
@@ -5517,7 +5527,7 @@
                     isTop, hostingType, activity.intent.getComponent());
             mH.sendMessage(m);
         } finally {
-            Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
         }
     }
 
@@ -5670,11 +5680,11 @@
 
     private void updateResumedAppTrace(@Nullable ActivityRecord resumed) {
         if (mTracedResumedActivity != null) {
-            Trace.asyncTraceEnd(TRACE_TAG_ACTIVITY_MANAGER,
+            Trace.asyncTraceEnd(TRACE_TAG_WINDOW_MANAGER,
                     constructResumedTraceName(mTracedResumedActivity.packageName), 0);
         }
         if (resumed != null) {
-            Trace.asyncTraceBegin(TRACE_TAG_ACTIVITY_MANAGER,
+            Trace.asyncTraceBegin(TRACE_TAG_WINDOW_MANAGER,
                     constructResumedTraceName(resumed.packageName), 0);
         }
         mTracedResumedActivity = resumed;
@@ -6002,10 +6012,10 @@
 
         @Override
         public void notifyAppTransitionStarting(SparseIntArray reasons,
-                long timestamp) {
+                long timestampNs) {
             synchronized (mGlobalLock) {
                 mStackSupervisor.getActivityMetricsLogger().notifyTransitionStarting(
-                        reasons, timestamp);
+                        reasons, timestampNs);
             }
         }
 
@@ -6773,7 +6783,14 @@
         @Override
         public boolean attachApplication(WindowProcessController wpc) throws RemoteException {
             synchronized (mGlobalLockWithoutBoost) {
-                return mRootActivityContainer.attachApplication(wpc);
+                if (Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER)) {
+                    Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "attachApplication:" + wpc.mName);
+                }
+                try {
+                    return mRootActivityContainer.attachApplication(wpc);
+                } finally {
+                    Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+                }
             }
         }
 
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index 894dfd4..0c07e15 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -209,7 +209,7 @@
         mDisplayContent.computeImeTarget(true /* updateImeTarget */);
 
         mService.mAtmInternal.notifyAppTransitionStarting(mTempTransitionReasons.clone(),
-                SystemClock.uptimeMillis());
+            SystemClock.elapsedRealtimeNanos());
 
         if (transit == TRANSIT_SHOW_SINGLE_TASK_DISPLAY) {
             mService.mAnimator.addAfterPrepareSurfacesRunnable(() -> {
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 80a295d..e56fdd2 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -512,7 +512,7 @@
         if (DEBUG_VISIBILITY) Slog.v(TAG, "VIS " + this + ": interesting="
                 + numInteresting + " visible=" + numVisible);
         if (nowDrawn != reportedDrawn) {
-            onWindowsDrawn(nowDrawn, SystemClock.uptimeMillis());
+            onWindowsDrawn(nowDrawn, SystemClock.elapsedRealtimeNanos());
             reportedDrawn = nowDrawn;
         }
         if (nowVisible != reportedVisible) {
@@ -2283,7 +2283,7 @@
         }
     }
 
-    private final Runnable mAddStartingWindow = new Runnable() {
+    private class AddStartingWindow implements Runnable {
 
         @Override
         public void run() {
@@ -2343,7 +2343,9 @@
                         AppWindowToken.this);
             }
         }
-    };
+    }
+
+    private final AddStartingWindow mAddStartingWindow = new AddStartingWindow();
 
     private int getStartingWindowType(boolean newTask, boolean taskSwitch, boolean processRunning,
             boolean allowTaskSnapshot, boolean activityCreated, boolean fromRecents,
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 150ae79..60e9819 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -196,6 +196,11 @@
     // Nav bar is never forced opaque.
     private static final int NAV_BAR_FORCE_TRANSPARENT = 2;
 
+    /** Don't apply window animation (see {@link #selectAnimation}). */
+    static final int ANIMATION_NONE = -1;
+    /** Use the transit animation in style resource (see {@link #selectAnimation}). */
+    static final int ANIMATION_STYLEABLE = 0;
+
     /**
      * These are the system UI flags that, when changing, can cause the layout
      * of the screen to change.
@@ -1036,9 +1041,9 @@
     }
 
     /**
-     * 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
-     * to use the default animation.
+     * Control the animation to run when a window's state changes.  Return a positive number to
+     * force the animation to a specific resource ID, {@link #ANIMATION_STYLEABLE} to use the
+     * style resource defining the animation, or {@link #ANIMATION_NONE} for no animation.
      *
      * @param win The window that is changing.
      * @param transit What is happening to the window:
@@ -1047,9 +1052,9 @@
      *                {@link com.android.server.policy.WindowManagerPolicy#TRANSIT_SHOW}, or
      *                {@link com.android.server.policy.WindowManagerPolicy#TRANSIT_HIDE}.
      *
-     * @return Resource ID of the actual animation to use, or 0 for none.
+     * @return Resource ID of the actual animation to use, or {@link #ANIMATION_NONE} for none.
      */
-    public int selectAnimationLw(WindowState win, int transit) {
+    int selectAnimation(WindowState win, int transit) {
         if (DEBUG_ANIM) Slog.i(TAG, "selectAnimation in " + win
                 + ": transit=" + transit);
         if (win == mStatusBar) {
@@ -1057,7 +1062,7 @@
             final boolean expanded = win.getAttrs().height == MATCH_PARENT
                     && win.getAttrs().width == MATCH_PARENT;
             if (isKeyguard || expanded) {
-                return -1;
+                return ANIMATION_NONE;
             }
             if (transit == TRANSIT_EXIT
                     || transit == TRANSIT_HIDE) {
@@ -1068,7 +1073,7 @@
             }
         } else if (win == mNavigationBar) {
             if (win.getAttrs().windowAnimations != 0) {
-                return 0;
+                return ANIMATION_STYLEABLE;
             }
             // This can be on either the bottom or the right or the left.
             if (mNavigationBarPosition == NAV_BAR_BOTTOM) {
@@ -1101,7 +1106,7 @@
                 }
             }
         } else if (win.getAttrs().type == TYPE_DOCK_DIVIDER) {
-            return selectDockedDividerAnimationLw(win, transit);
+            return selectDockedDividerAnimation(win, transit);
         }
 
         if (transit == TRANSIT_PREVIEW_DONE) {
@@ -1115,13 +1120,13 @@
             // is shown.  We don't want an animation on the dream, because
             // we need it shown immediately with the keyguard animating away
             // to reveal it.
-            return -1;
+            return ANIMATION_NONE;
         }
 
-        return 0;
+        return ANIMATION_STYLEABLE;
     }
 
-    private int selectDockedDividerAnimationLw(WindowState win, int transit) {
+    private int selectDockedDividerAnimation(WindowState win, int transit) {
         int insets = mDisplayContent.getDockedDividerController().getContentInsets();
 
         // If the divider is behind the navigation bar, don't animate.
@@ -1140,14 +1145,14 @@
                 || frame.bottom + insets >= win.getDisplayFrameLw().bottom);
         final boolean offscreen = offscreenLandscape || offscreenPortrait;
         if (behindNavBar || offscreen) {
-            return 0;
+            return ANIMATION_STYLEABLE;
         }
         if (transit == TRANSIT_ENTER || transit == TRANSIT_SHOW) {
             return R.anim.fade_in;
         } else if (transit == TRANSIT_EXIT) {
             return R.anim.fade_out;
         } else {
-            return 0;
+            return ANIMATION_STYLEABLE;
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
index 7e085f6..bc95481 100644
--- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java
@@ -25,7 +25,7 @@
  */
 class ImeInsetsSourceProvider extends InsetsSourceProvider {
 
-    private WindowState mCurImeTarget;
+    private WindowState mImeTargetFromIme;
     private Runnable mShowImeRunner;
     private boolean mIsImeLayoutDrawn;
 
@@ -40,8 +40,8 @@
     void onPostLayout() {
         super.onPostLayout();
 
-        if (mCurImeTarget != null
-                && mCurImeTarget == mDisplayContent.mInputMethodTarget
+        if (mImeTargetFromIme != null
+                && isImeTargetFromDisplayContentAndImeSame()
                 && mWin != null
                 && mWin.isDrawnLw()
                 && !mWin.mGivenInsetsPending) {
@@ -64,18 +64,33 @@
     /**
      * Called from {@link WindowManagerInternal#showImePostLayout} when {@link InputMethodService}
      * requests to show IME on {@param imeTarget}.
-     * @param imeTarget imeTarget on which IME is displayed.
+     * @param imeTarget imeTarget on which IME request is coming from.
      */
     void scheduleShowImePostLayout(WindowState imeTarget) {
-        mCurImeTarget = imeTarget;
+        mImeTargetFromIme = imeTarget;
         mShowImeRunner = () -> {
             // Target should still be the same.
-            if (mCurImeTarget == mDisplayContent.mInputMethodTarget) {
+            if (isImeTargetFromDisplayContentAndImeSame()) {
                 mDisplayContent.mInputMethodTarget.showInsets(
                         WindowInsets.Type.ime(), true /* fromIme */);
             }
-            mCurImeTarget = null;
+            mImeTargetFromIme = null;
         };
     }
 
+    private boolean isImeTargetFromDisplayContentAndImeSame() {
+        // IMMS#mLastImeTargetWindow always considers focused window as
+        // IME target, however DisplayContent#computeImeTarget() can compute
+        // a different IME target.
+        // Refer to WindowManagerService#applyImeVisibility(token, false).
+        // If IMMS's imeTarget is child of DisplayContent's imeTarget and child window
+        // is above the parent, we will consider it as the same target for now.
+        // TODO(b/139861270): Remove the child & sublayer check once IMMS is aware of
+        //  actual IME target.
+        return mImeTargetFromIme == mDisplayContent.mInputMethodTarget
+                || (mDisplayContent.mInputMethodTarget.getParentWindow() == mImeTargetFromIme
+                        && mDisplayContent.mInputMethodTarget.mSubLayer
+                                > mImeTargetFromIme.mSubLayer);
+    }
+
 }
diff --git a/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java b/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java
index d774dc3..82ac6b8 100644
--- a/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java
+++ b/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java
@@ -189,7 +189,7 @@
                         | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED
                         | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
                 PixelFormat.TRANSLUCENT);
-        lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+        lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
         lp.setTitle("ImmersiveModeConfirmation");
         lp.windowAnimations = com.android.internal.R.style.Animation_ImmersiveModeConfirmation;
         lp.token = getWindowToken();
diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java
index eb9b3e9..9973e11 100644
--- a/services/core/java/com/android/server/wm/InputManagerCallback.java
+++ b/services/core/java/com/android/server/wm/InputManagerCallback.java
@@ -266,7 +266,8 @@
 
         final IWindow focusedWindow = mFocusedWindow.get();
         if (focusedWindow != null) {
-            if (focusedWindow.asBinder() == newFocusedWindow.asBinder()) {
+            if (newFocusedWindow != null
+                    && newFocusedWindow.asBinder() == focusedWindow.asBinder()) {
                 Slog.w(TAG, "notifyFocusChanged called with unchanged mFocusedWindow="
                         + focusedWindow);
                 return false;
diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java
index dc9a598..584c6e1 100644
--- a/services/core/java/com/android/server/wm/InputMonitor.java
+++ b/services/core/java/com/android/server/wm/InputMonitor.java
@@ -112,7 +112,7 @@
         }
     }
 
-    private final Runnable mUpdateInputWindows = new Runnable() {
+    private class UpdateInputWindows implements Runnable {
         @Override
         public void run() {
             synchronized (mService.mGlobalLock) {
@@ -148,7 +148,9 @@
                 mUpdateInputForAllWindowsConsumer.updateInputWindows(inDrag);
             }
         }
-    };
+    }
+
+    private final UpdateInputWindows mUpdateInputWindows = new UpdateInputWindows();
 
     public InputMonitor(WindowManagerService service, int displayId) {
         mService = service;
diff --git a/services/core/java/com/android/server/wm/InsetsControlTarget.java b/services/core/java/com/android/server/wm/InsetsControlTarget.java
index b7184a5..22ba82a 100644
--- a/services/core/java/com/android/server/wm/InsetsControlTarget.java
+++ b/services/core/java/com/android/server/wm/InsetsControlTarget.java
@@ -33,4 +33,13 @@
      */
     default void showInsets(@InsetType int types, boolean fromIme) {
     }
+
+    /**
+     * Instructs the control target to hide inset sources.
+     *
+     * @param types to specify which types of insets source window should be hidden.
+     * @param fromIme {@code true} if IME hide request originated from {@link InputMethodService}.
+     */
+    default void hideInsets(@InsetType int types, boolean fromIme) {
+    }
 }
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index 2b5eb3a..52cc422 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -16,7 +16,7 @@
 
 package com.android.server.wm;
 
-import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
+import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
 import static android.view.Display.DEFAULT_DISPLAY;
 import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION;
 import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION;
@@ -180,7 +180,7 @@
         if (!mKeyguardShowing) {
             return;
         }
-        Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "keyguardGoingAway");
+        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "keyguardGoingAway");
         mService.deferWindowLayout();
         try {
             setKeyguardGoingAway(true);
@@ -202,11 +202,8 @@
                     true /* taskSwitch */);
             mWindowManager.executeAppTransition();
         } finally {
-            Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "keyguardGoingAway: surfaceLayout");
             mService.continueWindowLayout();
-            Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
-
-            Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/LaunchObserverRegistryImpl.java b/services/core/java/com/android/server/wm/LaunchObserverRegistryImpl.java
index 93e2d8d..362ed3c 100644
--- a/services/core/java/com/android/server/wm/LaunchObserverRegistryImpl.java
+++ b/services/core/java/com/android/server/wm/LaunchObserverRegistryImpl.java
@@ -70,9 +70,12 @@
     }
 
     @Override
-    public void onIntentStarted(Intent intent) {
+    public void onIntentStarted(Intent intent, long timestampNs) {
         mHandler.sendMessage(PooledLambda.obtainMessage(
-                LaunchObserverRegistryImpl::handleOnIntentStarted, this, intent));
+                LaunchObserverRegistryImpl::handleOnIntentStarted,
+                this,
+                intent,
+                timestampNs));
     }
 
     @Override
@@ -99,9 +102,22 @@
 
     @Override
     public void onActivityLaunchFinished(
-        @ActivityRecordProto byte[] activity) {
+        @ActivityRecordProto byte[] activity,
+        long timestampNs) {
         mHandler.sendMessage(PooledLambda.obtainMessage(
-                LaunchObserverRegistryImpl::handleOnActivityLaunchFinished, this, activity));
+            LaunchObserverRegistryImpl::handleOnActivityLaunchFinished,
+            this,
+            activity,
+            timestampNs));
+    }
+
+    @Override
+    public void onReportFullyDrawn(@ActivityRecordProto byte[] activity, long timestampNs) {
+        mHandler.sendMessage(PooledLambda.obtainMessage(
+            LaunchObserverRegistryImpl::handleOnReportFullyDrawn,
+            this,
+            activity,
+            timestampNs));
     }
 
     // Use PooledLambda.obtainMessage to invoke below methods. Every method reference must be
@@ -116,11 +132,11 @@
         mList.remove(observer);
     }
 
-    private void handleOnIntentStarted(Intent intent) {
+    private void handleOnIntentStarted(Intent intent, long timestampNs) {
         // Traverse start-to-end to meet the registerLaunchObserver multi-cast order guarantee.
         for (int i = 0; i < mList.size(); i++) {
              ActivityMetricsLaunchObserver o = mList.get(i);
-             o.onIntentStarted(intent);
+             o.onIntentStarted(intent, timestampNs);
         }
     }
 
@@ -152,11 +168,20 @@
     }
 
     private void handleOnActivityLaunchFinished(
-            @ActivityRecordProto byte[] activity) {
+            @ActivityRecordProto byte[] activity, long timestampNs) {
         // Traverse start-to-end to meet the registerLaunchObserver multi-cast order guarantee.
         for (int i = 0; i < mList.size(); i++) {
             ActivityMetricsLaunchObserver o = mList.get(i);
-            o.onActivityLaunchFinished(activity);
+            o.onActivityLaunchFinished(activity, timestampNs);
+        }
+    }
+
+    private void handleOnReportFullyDrawn(
+            @ActivityRecordProto byte[] activity, long timestampNs) {
+        // Traverse start-to-end to meet the registerLaunchObserver multi-cast order guarantee.
+        for (int i = 0; i < mList.size(); i++) {
+            ActivityMetricsLaunchObserver o = mList.get(i);
+            o.onReportFullyDrawn(activity, timestampNs);
         }
     }
 }
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index 062cdc5..12579e6 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -22,7 +22,7 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
 import static android.content.Intent.FLAG_ACTIVITY_NO_ANIMATION;
-import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
+import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
 import static android.view.WindowManager.TRANSIT_NONE;
 
 import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
@@ -158,7 +158,7 @@
 
     void startRecentsActivity(IRecentsAnimationRunner recentsAnimationRunner) {
         ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "startRecentsActivity(): intent=%s", mTargetIntent);
-        Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "RecentsAnimation#startRecentsActivity");
+        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "RecentsAnimation#startRecentsActivity");
 
         // TODO(multi-display) currently only support recents animation in default display.
         final DisplayContent dc =
@@ -263,7 +263,7 @@
             throw e;
         } finally {
             mService.continueWindowLayout();
-            Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
         }
     }
 
@@ -297,7 +297,7 @@
             }
 
             mWindowManager.inSurfaceTransaction(() -> {
-                Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER,
+                Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER,
                         "RecentsAnimation#onAnimationFinished_inSurfaceTransaction");
                 mService.deferWindowLayout();
                 try {
@@ -394,7 +394,7 @@
                     throw e;
                 } finally {
                     mService.continueWindowLayout();
-                    Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+                    Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
                 }
             });
         }
diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java
index bd27905..d606e5d 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimationController.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java
@@ -98,8 +98,10 @@
     private final ArrayList<WallpaperAnimationAdapter> mPendingWallpaperAnimations =
             new ArrayList<>();
     private final int mDisplayId;
-    private final Runnable mFailsafeRunnable = () ->
-            cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "failSafeRunnable");
+    private boolean mWillFinishToHome = false;
+    private final Runnable mFailsafeRunnable = () -> cancelAnimation(
+            mWillFinishToHome ? REORDER_MOVE_TO_TOP : REORDER_MOVE_TO_ORIGINAL_POSITION,
+            "failSafeRunnable");
 
     // The recents component app token that is shown behind the visibile tasks
     private AppWindowToken mTargetAppToken;
@@ -326,6 +328,13 @@
                 }
             }
         }
+
+        @Override
+        public void setWillFinishToHome(boolean willFinishToHome) {
+            synchronized (mService.getWindowManagerLock()) {
+                mWillFinishToHome = willFinishToHome;
+            }
+        }
     };
 
     /**
@@ -494,7 +503,8 @@
         }
         final SparseIntArray reasons = new SparseIntArray();
         reasons.put(WINDOWING_MODE_FULLSCREEN, APP_TRANSITION_RECENTS_ANIM);
-        mService.mAtmInternal.notifyAppTransitionStarting(reasons, SystemClock.uptimeMillis());
+        mService.mAtmInternal
+            .notifyAppTransitionStarting(reasons, SystemClock.elapsedRealtimeNanos());
     }
 
     private RemoteAnimationTarget[] createAppAnimations() {
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index c5e9edb..f0717ca 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -121,8 +121,11 @@
     // TODO: Remove after unification.
     @Override
     public void onConfigurationChanged(Configuration newParentConfig) {
-        // Only forward configuration changes in cases where children won't get it from TaskRecord.
-        onConfigurationChanged(newParentConfig, mTaskRecord == null /*forwardToChildren*/);
+        // Forward configuration changes in cases
+        // - children won't get it from TaskRecord
+        // - it's a pinned task
+        onConfigurationChanged(newParentConfig,
+                (mTaskRecord == null) || inPinnedWindowingMode() /*forwardToChildren*/);
     }
 
     Task(int taskId, TaskStack stack, int userId, WindowManagerService service, int resizeMode,
diff --git a/services/core/java/com/android/server/wm/TaskRecord.java b/services/core/java/com/android/server/wm/TaskRecord.java
index ab661ca..299b32c 100644
--- a/services/core/java/com/android/server/wm/TaskRecord.java
+++ b/services/core/java/com/android/server/wm/TaskRecord.java
@@ -48,7 +48,7 @@
 import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
 import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
 import static android.content.res.Configuration.ORIENTATION_UNDEFINED;
-import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
+import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
 import static android.provider.Settings.Secure.USER_SETUP_COMPLETE;
 import static android.view.Display.DEFAULT_DISPLAY;
 
@@ -553,7 +553,7 @@
             // This method assumes that the task is already placed in the right stack.
             // we do not mess with that decision and we only do the resize!
 
-            Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizeTask_" + mTaskId);
+            Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "resizeTask_" + mTaskId);
 
             boolean updatedConfig = false;
             mTmpConfig.setTo(getResolvedOverrideConfiguration());
@@ -587,7 +587,7 @@
 
             saveLaunchingStateIfNeeded();
 
-            Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
+            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
             return kept;
         } finally {
             mAtmService.continueWindowLayout();
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index f4b7672..0cb4826 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -514,6 +514,13 @@
     public abstract void showImePostLayout(IBinder imeTargetWindowToken);
 
     /**
+     * Hide IME using imeTargetWindow when requested.
+     *
+     * @param displayId on which IME is shown
+     */
+    public abstract void hideIme(int displayId);
+
+    /**
      * Tell window manager about a package that should not be running with high refresh rate
      * setting until removeNonHighRefreshRatePackage is called for the same package.
      *
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 0287270..c485280 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -236,6 +236,7 @@
 import android.view.SurfaceSession;
 import android.view.View;
 import android.view.WindowContentFrameStats;
+import android.view.WindowInsets;
 import android.view.WindowManager;
 import android.view.WindowManager.LayoutParams;
 import android.view.WindowManager.RemoveContentMode;
@@ -7312,6 +7313,16 @@
         }
 
         @Override
+        public void hideIme(int displayId) {
+            synchronized (mGlobalLock) {
+                final DisplayContent dc = mRoot.getDisplayContent(displayId);
+                if (dc != null && dc.mInputMethodTarget != null) {
+                    dc.mInputMethodTarget.hideInsets(WindowInsets.Type.ime(), true /* fromIme */);
+                }
+            }
+        }
+
+        @Override
         public boolean isUidAllowedOnDisplay(int displayId, int uid) {
             if (displayId == Display.DEFAULT_DISPLAY) {
                 return true;
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 40bb059..7ff9b70 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -3377,6 +3377,15 @@
         }
     }
 
+    @Override
+    public void hideInsets(@InsetType int types, boolean fromIme) {
+        try {
+            mClient.hideInsets(types, fromIme);
+        } catch (RemoteException e) {
+            Slog.w(TAG, "Failed to deliver showInsets", e);
+        }
+    }
+
     Rect getBackdropFrame(Rect frame) {
         // When the task is docked, we send fullscreen sized backDropFrame as soon as resizing
         // start even if we haven't received the relayout window, so that the client requests
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 1328273..eac372f 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -1348,13 +1348,16 @@
         // frozen, there is no reason to animate and it can cause strange
         // artifacts when we unfreeze the display if some different animation
         // is running.
-        Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "WSA#applyAnimationLocked");
         if (mWin.mToken.okToAnimate()) {
-            int anim = mWin.getDisplayContent().getDisplayPolicy().selectAnimationLw(mWin, transit);
+            int anim = mWin.getDisplayContent().getDisplayPolicy().selectAnimation(mWin, transit);
             int attr = -1;
             Animation a = null;
-            if (anim != 0) {
-                a = anim != -1 ? AnimationUtils.loadAnimation(mContext, anim) : null;
+            if (anim != DisplayPolicy.ANIMATION_STYLEABLE) {
+                if (anim != DisplayPolicy.ANIMATION_NONE) {
+                    Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "WSA#loadAnimation");
+                    a = AnimationUtils.loadAnimation(mContext, anim);
+                    Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
+                }
             } else {
                 switch (transit) {
                     case WindowManagerPolicy.TRANSIT_ENTER:
@@ -1384,7 +1387,9 @@
                     + " isEntrance=" + isEntrance + " Callers " + Debug.getCallers(3));
             if (a != null) {
                 if (DEBUG_ANIM) logWithStack(TAG, "Loaded animation " + a + " for " + this);
+                Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "WSA#startAnimation");
                 mWin.startAnimation(a);
+                Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
                 mAnimationIsEntrance = isEntrance;
             }
         } else {
@@ -1395,7 +1400,6 @@
             mWin.getDisplayContent().adjustForImeIfNeeded();
         }
 
-        Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
         return mWin.isAnimating();
     }
 
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 56f6d4b..0cfdebc 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -23,7 +23,6 @@
 import static com.android.server.wm.WindowManagerService.LAYOUT_REPEAT_THRESHOLD;
 
 import android.os.Debug;
-import android.os.Trace;
 import android.util.Slog;
 
 import java.io.PrintWriter;
@@ -52,15 +51,19 @@
     /** The number of layout requests when deferring. */
     private int mDeferredRequests;
 
-    private final Runnable mPerformSurfacePlacement;
-
-    public WindowSurfacePlacer(WindowManagerService service) {
-        mService = service;
-        mPerformSurfacePlacement = () -> {
+    private class Traverser implements Runnable {
+        @Override
+        public void run() {
             synchronized (mService.mGlobalLock) {
                 performSurfacePlacement();
             }
-        };
+        }
+    }
+
+    private final Traverser mPerformSurfacePlacement = new Traverser();
+
+    WindowSurfacePlacer(WindowManagerService service) {
+        mService = service;
     }
 
     /**
@@ -152,7 +155,6 @@
             return;
         }
 
-        Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "wmLayout");
         mInLayout = true;
 
         boolean recoveringMemory = false;
@@ -198,8 +200,6 @@
             mInLayout = false;
             Slog.wtf(TAG, "Unhandled exception while laying out windows", e);
         }
-
-        Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
     }
 
     void debugLayoutRepeats(final String msg, int pendingLayoutChanges) {
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index fb2fdab..dd2629d 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -201,8 +201,7 @@
 
     void setDisplayViewports(JNIEnv* env, jobjectArray viewportObjArray);
 
-    status_t registerInputChannel(JNIEnv* env, const sp<InputChannel>& inputChannel,
-            int32_t displayId);
+    status_t registerInputChannel(JNIEnv* env, const sp<InputChannel>& inputChannel);
     status_t registerInputMonitor(JNIEnv* env, const sp<InputChannel>& inputChannel,
             int32_t displayId, bool isGestureMonitor);
     status_t unregisterInputChannel(JNIEnv* env, const sp<InputChannel>& inputChannel);
@@ -435,10 +434,9 @@
 }
 
 status_t NativeInputManager::registerInputChannel(JNIEnv* /* env */,
-        const sp<InputChannel>& inputChannel, int32_t displayId) {
+        const sp<InputChannel>& inputChannel) {
     ATRACE_CALL();
-    return mInputManager->getDispatcher()->registerInputChannel(
-            inputChannel, displayId);
+    return mInputManager->getDispatcher()->registerInputChannel(inputChannel);
 }
 
 status_t NativeInputManager::registerInputMonitor(JNIEnv* /* env */,
@@ -1405,7 +1403,7 @@
 }
 
 static void nativeRegisterInputChannel(JNIEnv* env, jclass /* clazz */,
-        jlong ptr, jobject inputChannelObj, jint displayId) {
+        jlong ptr, jobject inputChannelObj) {
     NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
 
     sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
@@ -1415,7 +1413,7 @@
         return;
     }
 
-    status_t status = im->registerInputChannel(env, inputChannel, displayId);
+    status_t status = im->registerInputChannel(env, inputChannel);
 
     if (status) {
         std::string message;
@@ -1757,7 +1755,7 @@
     { "nativeHasKeys", "(JII[I[Z)Z",
             (void*) nativeHasKeys },
     { "nativeRegisterInputChannel",
-            "(JLandroid/view/InputChannel;I)V",
+            "(JLandroid/view/InputChannel;)V",
             (void*) nativeRegisterInputChannel },
     { "nativeRegisterInputMonitor",
             "(JLandroid/view/InputChannel;IZ)V",
diff --git a/services/devicepolicy/Android.bp b/services/devicepolicy/Android.bp
index 47790ce..91c05a8 100644
--- a/services/devicepolicy/Android.bp
+++ b/services/devicepolicy/Android.bp
@@ -5,4 +5,13 @@
     libs: [
         "services.core",
     ],
+
+    plugins: [
+        "compat-changeid-annotation-processor",
+    ],
 }
+
+platform_compat_config {
+    name: "services-devicepolicy-platform-compat-config",
+    src: ":services.devicepolicy",
+}
\ No newline at end of file
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 9569ac8..0ae205a 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -137,6 +137,8 @@
 import android.app.backup.IBackupManager;
 import android.app.trust.TrustManager;
 import android.app.usage.UsageStatsManagerInternal;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
 import android.content.ActivityNotFoundException;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -235,6 +237,7 @@
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.compat.IPlatformCompat;
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
 import com.android.internal.notification.SystemNotificationChannels;
@@ -251,6 +254,7 @@
 import com.android.internal.util.XmlUtils;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.internal.widget.LockSettingsInternal;
+import com.android.internal.widget.LockscreenCredential;
 import com.android.server.LocalServices;
 import com.android.server.LockGuard;
 import com.android.server.SystemServerInitThreadPool;
@@ -494,6 +498,22 @@
     private static final String LOG_TAG_PROFILE_OWNER = "profile-owner";
     private static final String LOG_TAG_DEVICE_OWNER = "device-owner";
 
+    /**
+     * For admin apps targeting R+, throw when the app sets password requirement
+     * that is not taken into account at given quality. For example when quality is set
+     * to {@link DevicePolicyManager#PASSWORD_QUALITY_UNSPECIFIED}, it doesn't make sense to
+     * require certain password length. If the intent is to require a password of certain length
+     * having at least NUMERIC quality, the admin should first call
+     * {@link #setPasswordQuality(ComponentName, int, boolean)} and only then call
+     * {@link #setPasswordMinimumLength(ComponentName, int, boolean)}.
+     *
+     * <p>Conversely when an admin app targeting R+ lowers password quality, those
+     * requirements that stop making sense are reset to default values.
+     */
+    @ChangeId
+    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q)
+    private static final long ADMIN_APP_PASSWORD_COMPLEXITY = 123562444L;
+
     final Context mContext;
     final Injector mInjector;
     final IPackageManager mIPackageManager;
@@ -506,6 +526,7 @@
     private final LockSettingsInternal mLockSettingsInternal;
     private final DeviceAdminServiceController mDeviceAdminServiceController;
     private final OverlayPackagesProvider mOverlayPackagesProvider;
+    private final IPlatformCompat mIPlatformCompat;
 
     private final DevicePolicyCacheImpl mPolicyCache = new DevicePolicyCacheImpl();
     private final DeviceStateCacheImpl mStateCache = new DeviceStateCacheImpl();
@@ -1984,6 +2005,11 @@
             return LocalServices.getService(LockSettingsInternal.class);
         }
 
+        IPlatformCompat getIPlatformCompat() {
+            return IPlatformCompat.Stub.asInterface(
+                    ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
+        }
+
         boolean hasUserSetupCompleted(DevicePolicyData userData) {
             return userData.mUserSetupComplete;
         }
@@ -2222,6 +2248,7 @@
         mUsageStatsManagerInternal = Preconditions.checkNotNull(
                 injector.getUsageStatsManagerInternal());
         mIPackageManager = Preconditions.checkNotNull(injector.getIPackageManager());
+        mIPlatformCompat = Preconditions.checkNotNull(injector.getIPlatformCompat());
         mIPermissionManager = Preconditions.checkNotNull(injector.getIPermissionManager());
         mTelephonyManager = Preconditions.checkNotNull(injector.getTelephonyManager());
 
@@ -4156,12 +4183,22 @@
                 .write();
     }
 
+    private boolean passwordQualityInvocationOrderCheckEnabled(String packageName, int userId) {
+        try {
+            return mIPlatformCompat.isChangeEnabledByPackageName(ADMIN_APP_PASSWORD_COMPLEXITY,
+                    packageName);
+        } catch (RemoteException e) {
+            Log.e(LOG_TAG, "Failed to get a response from PLATFORM_COMPAT_SERVICE", e);
+        }
+        return getTargetSdk(packageName, userId) > Build.VERSION_CODES.Q;
+    }
+
     /**
      * For admins targeting R+ reset various password constraints to default values when quality is
      * set to a value that makes those constraints that have no effect.
      */
     private void resetInactivePasswordRequirementsIfRPlus(int userId, ActiveAdmin admin) {
-        if (getTargetSdk(admin.info.getPackageName(), userId) > Build.VERSION_CODES.Q) {
+        if (passwordQualityInvocationOrderCheckEnabled(admin.info.getPackageName(), userId)) {
             final PasswordMetrics metrics = admin.minimumPasswordMetrics;
             if (metrics.quality < PASSWORD_QUALITY_NUMERIC) {
                 metrics.length = ActiveAdmin.DEF_MINIMUM_PASSWORD_LENGTH;
@@ -4326,7 +4363,8 @@
     private void ensureMinimumQuality(
             int userId, ActiveAdmin admin, int minimumQuality, String operation) {
         if (admin.minimumPasswordMetrics.quality < minimumQuality
-                && getTargetSdk(admin.info.getPackageName(), userId) > Build.VERSION_CODES.Q) {
+                && passwordQualityInvocationOrderCheckEnabled(admin.info.getPackageName(),
+                userId)) {
             throw new IllegalStateException(String.format(
                     "password quality should be at least %d for %s", minimumQuality, operation));
         }
@@ -5222,28 +5260,20 @@
         // back in to the service.
         final long ident = mInjector.binderClearCallingIdentity();
         final boolean result;
+        final LockscreenCredential newCredential =
+                LockscreenCredential.createPasswordOrNone(password);
         try {
             if (token == null) {
                 // This is the legacy reset password for DPM. Here we want to be able to override
                 // the old device password without necessarily knowing it.
-                if (!TextUtils.isEmpty(password)) {
-                    mLockPatternUtils.saveLockPassword(password.getBytes(), null, quality,
-                            userHandle, /*allowUntrustedChange */true);
-                } else {
-                    mLockPatternUtils.clearLock(null, userHandle,
-                            /*allowUntrustedChange */ true);
-                }
+                mLockPatternUtils.setLockCredential(
+                        newCredential,
+                        LockscreenCredential.createNone(),
+                        userHandle, /*allowUntrustedChange */true);
                 result = true;
             } else {
-                if (!TextUtils.isEmpty(password)) {
-                    result = mLockPatternUtils.setLockCredentialWithToken(password.getBytes(),
-                            LockPatternUtils.CREDENTIAL_TYPE_PASSWORD,
-                            quality, tokenHandle, token, userHandle);
-                } else {
-                    result = mLockPatternUtils.setLockCredentialWithToken(null,
-                            LockPatternUtils.CREDENTIAL_TYPE_NONE,
-                            quality, tokenHandle, token, userHandle);
-                }
+                result = mLockPatternUtils.setLockCredentialWithToken(newCredential, tokenHandle,
+                        token, userHandle);
             }
             boolean requireEntry = (flags & DevicePolicyManager.RESET_PASSWORD_REQUIRE_ENTRY) != 0;
             if (requireEntry) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportUtils.java b/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportUtils.java
index 0838fbc..7cfbcc8 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportUtils.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportUtils.java
@@ -51,7 +51,7 @@
     static final long REMOTE_BUGREPORT_TIMEOUT_MILLIS = 10 * DateUtils.MINUTE_IN_MILLIS;
 
     static final String CTL_STOP = "ctl.stop";
-    static final String REMOTE_BUGREPORT_SERVICE = "bugreportremote";
+    static final String REMOTE_BUGREPORT_SERVICE = "bugreportd";
 
     static final String BUGREPORT_MIMETYPE = "application/vnd.android.bugreport";
 
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 4cf98d3..88859a7 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -921,7 +921,6 @@
                 false);
         boolean disableCameraService = SystemProperties.getBoolean("config.disable_cameraservice",
                 false);
-        boolean disableSlices = SystemProperties.getBoolean("config.disable_slices", false);
         boolean enableLeftyService = SystemProperties.getBoolean("config.enable_lefty", false);
 
         boolean isEmulator = SystemProperties.get("ro.kernel.qemu").equals("1");
@@ -1856,7 +1855,7 @@
             t.traceEnd();
         }
 
-        if (!disableSlices) {
+        if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_SLICES_DISABLED)) {
             t.traceBegin("StartSliceManagerService");
             mSystemServiceManager.startService(SLICE_MANAGER_SERVICE_CLASS);
             t.traceEnd();
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
index edf82ee..597d337 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java
@@ -41,12 +41,14 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.UserHandle;
+import android.testing.DexmakerShareClassLoaderRule;
 import android.view.Display;
 
 import com.android.server.wm.WindowManagerInternal;
 
 import org.junit.After;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
@@ -65,9 +67,14 @@
             "com.android.server.accessibility", "AccessibilityServiceConnectionTest");
     static final int SERVICE_ID = 42;
 
+    // Mock package-private AccessibilityUserState class
+    @Rule
+    public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule =
+            new DexmakerShareClassLoaderRule();
+
     AccessibilityServiceConnection mConnection;
 
-    @Mock AccessibilityManagerService.UserState mMockUserState;
+    @Mock AccessibilityUserState mMockUserState;
     @Mock Context mMockContext;
     @Mock AccessibilityServiceInfo mMockServiceInfo;
     @Mock ResolveInfo mMockResolveInfo;
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
new file mode 100644
index 0000000..9180054
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java
@@ -0,0 +1,297 @@
+/*
+ * 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.accessibility;
+
+import static android.accessibilityservice.AccessibilityService.SHOW_MODE_AUTO;
+import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE;
+import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HARD_KEYBOARD_OVERRIDDEN;
+import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HIDDEN;
+import static android.accessibilityservice.AccessibilityService.SHOW_MODE_IGNORE_HARD_KEYBOARD;
+import static android.view.accessibility.AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED;
+import static android.view.accessibility.AccessibilityManager.STATE_FLAG_HIGH_TEXT_CONTRAST_ENABLED;
+import static android.view.accessibility.AccessibilityManager.STATE_FLAG_TOUCH_EXPLORATION_ENABLED;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.accessibilityservice.AccessibilityServiceInfo;
+import android.content.ComponentName;
+import android.content.Context;
+import android.provider.Settings;
+import android.test.mock.MockContentResolver;
+import android.testing.DexmakerShareClassLoaderRule;
+
+import com.android.internal.util.test.FakeSettingsProvider;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/** Tests for AccessibilityUserState */
+public class AccessibilityUserStateTest {
+
+    private static final ComponentName COMPONENT_NAME =
+            new ComponentName("com.android.server.accessibility", "AccessibilityUserStateTest");
+
+    // Values of setting key SHOW_IME_WITH_HARD_KEYBOARD
+    private static final int STATE_HIDE_IME = 0;
+    private static final int STATE_SHOW_IME = 1;
+
+    private static final int USER_ID = 42;
+
+    // Mock package-private class AccessibilityServiceConnection
+    @Rule public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule =
+            new DexmakerShareClassLoaderRule();
+
+    @Mock private AccessibilityServiceInfo mMockServiceInfo;
+
+    @Mock private AccessibilityServiceConnection mMockConnection;
+
+    @Mock private AccessibilityUserState.ServiceInfoChangeListener mMockListener;
+
+    @Mock private Context mContext;
+
+    private MockContentResolver mMockResolver;
+
+    private AccessibilityUserState mUserState;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        FakeSettingsProvider.clearSettingsProvider();
+        mMockResolver = new MockContentResolver();
+        mMockResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
+        when(mContext.getContentResolver()).thenReturn(mMockResolver);
+        when(mMockServiceInfo.getComponentName()).thenReturn(COMPONENT_NAME);
+        when(mMockConnection.getServiceInfo()).thenReturn(mMockServiceInfo);
+
+        mUserState = new AccessibilityUserState(USER_ID, mContext, mMockListener);
+    }
+
+    @After
+    public void tearDown() {
+        FakeSettingsProvider.clearSettingsProvider();
+    }
+
+    @Test
+    public void onSwitchToAnotherUser_userStateClearedNonDefaultValues() {
+        mUserState.getBoundServicesLocked().add(mMockConnection);
+        mUserState.getBindingServicesLocked().add(COMPONENT_NAME);
+        mUserState.setLastSentClientStateLocked(
+                STATE_FLAG_ACCESSIBILITY_ENABLED
+                        | STATE_FLAG_TOUCH_EXPLORATION_ENABLED
+                        | STATE_FLAG_HIGH_TEXT_CONTRAST_ENABLED);
+        mUserState.setNonInteractiveUiTimeoutLocked(30);
+        mUserState.setInteractiveUiTimeoutLocked(30);
+        mUserState.mEnabledServices.add(COMPONENT_NAME);
+        mUserState.mTouchExplorationGrantedServices.add(COMPONENT_NAME);
+        mUserState.setTouchExplorationEnabledLocked(true);
+        mUserState.setDisplayMagnificationEnabledLocked(true);
+        mUserState.setNavBarMagnificationEnabledLocked(true);
+        mUserState.setServiceAssignedToAccessibilityButtonLocked(COMPONENT_NAME);
+        mUserState.setNavBarMagnificationAssignedToAccessibilityButtonLocked(true);
+        mUserState.setAutoclickEnabledLocked(true);
+        mUserState.setUserNonInteractiveUiTimeoutLocked(30);
+        mUserState.setUserInteractiveUiTimeoutLocked(30);
+
+        mUserState.onSwitchToAnotherUserLocked();
+
+        verify(mMockConnection).unbindLocked();
+        assertTrue(mUserState.getBoundServicesLocked().isEmpty());
+        assertTrue(mUserState.getBindingServicesLocked().isEmpty());
+        assertEquals(-1, mUserState.getLastSentClientStateLocked());
+        assertEquals(0, mUserState.getNonInteractiveUiTimeoutLocked());
+        assertEquals(0, mUserState.getInteractiveUiTimeoutLocked());
+        assertTrue(mUserState.mEnabledServices.isEmpty());
+        assertTrue(mUserState.mTouchExplorationGrantedServices.isEmpty());
+        assertFalse(mUserState.isTouchExplorationEnabledLocked());
+        assertFalse(mUserState.isDisplayMagnificationEnabledLocked());
+        assertFalse(mUserState.isNavBarMagnificationEnabledLocked());
+        assertNull(mUserState.getServiceAssignedToAccessibilityButtonLocked());
+        assertFalse(mUserState.isNavBarMagnificationAssignedToAccessibilityButtonLocked());
+        assertFalse(mUserState.isAutoclickEnabledLocked());
+        assertEquals(0, mUserState.getUserNonInteractiveUiTimeoutLocked());
+        assertEquals(0, mUserState.getUserInteractiveUiTimeoutLocked());
+    }
+
+    @Test
+    public void addService_connectionAlreadyAdded_notAddAgain() {
+        mUserState.getBoundServicesLocked().add(mMockConnection);
+
+        mUserState.addServiceLocked(mMockConnection);
+
+        verify(mMockConnection, never()).onAdded();
+    }
+
+    @Test
+    public void addService_connectionNotYetAddedToBoundService_addAndNotifyServices() {
+        when(mMockConnection.getComponentName()).thenReturn(COMPONENT_NAME);
+
+        mUserState.addServiceLocked(mMockConnection);
+
+        verify(mMockConnection).onAdded();
+        assertTrue(mUserState.getBoundServicesLocked().contains(mMockConnection));
+        assertEquals(mMockConnection, mUserState.mComponentNameToServiceMap.get(COMPONENT_NAME));
+        verify(mMockListener).onServiceInfoChangedLocked(eq(mUserState));
+    }
+
+    @Test
+    public void reconcileSoftKeyboardMode_whenStateNotMatchSettings_setBothDefault() {
+        // When soft kb show mode is hidden in settings and is auto in state.
+        putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
+                SHOW_MODE_HIDDEN, USER_ID);
+
+        mUserState.reconcileSoftKeyboardModeWithSettingsLocked();
+
+        assertEquals(SHOW_MODE_AUTO, mUserState.getSoftKeyboardShowModeLocked());
+        assertEquals(SHOW_MODE_AUTO, getSecureIntForUser(
+                Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, USER_ID));
+        assertNull(mUserState.getServiceChangingSoftKeyboardModeLocked());
+    }
+
+    @Test
+    public void
+            reconcileSoftKeyboardMode_stateIgnoreHardKb_settingsShowImeHardKb_setAutoOverride() {
+        // When show mode is ignore hard kb without original hard kb value
+        // and show ime with hard kb is hide
+        putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
+                SHOW_MODE_IGNORE_HARD_KEYBOARD, USER_ID);
+        mUserState.setSoftKeyboardModeLocked(SHOW_MODE_IGNORE_HARD_KEYBOARD, COMPONENT_NAME);
+        putSecureIntForUser(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD,
+                STATE_HIDE_IME, USER_ID);
+
+        mUserState.reconcileSoftKeyboardModeWithSettingsLocked();
+
+        assertEquals(SHOW_MODE_AUTO | SHOW_MODE_HARD_KEYBOARD_OVERRIDDEN,
+                getSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, USER_ID));
+        assertNull(mUserState.getServiceChangingSoftKeyboardModeLocked());
+    }
+
+    @Test
+    public void removeService_serviceChangingSoftKeyboardMode_removeAndSetSoftKbModeAuto() {
+        mUserState.setServiceChangingSoftKeyboardModeLocked(COMPONENT_NAME);
+        mUserState.mComponentNameToServiceMap.put(COMPONENT_NAME, mMockConnection);
+        mUserState.setSoftKeyboardModeLocked(SHOW_MODE_HIDDEN, COMPONENT_NAME);
+
+        mUserState.removeServiceLocked(mMockConnection);
+
+        assertFalse(mUserState.getBoundServicesLocked().contains(mMockConnection));
+        verify(mMockConnection).onRemoved();
+        assertEquals(SHOW_MODE_AUTO, mUserState.getSoftKeyboardShowModeLocked());
+        assertNull(mUserState.mComponentNameToServiceMap.get(COMPONENT_NAME));
+        verify(mMockListener).onServiceInfoChangedLocked(eq(mUserState));
+    }
+
+    @Test
+    public void serviceDisconnected_removeServiceAndAddToBinding() {
+        when(mMockConnection.getComponentName()).thenReturn(COMPONENT_NAME);
+        mUserState.addServiceLocked(mMockConnection);
+
+        mUserState.serviceDisconnectedLocked(mMockConnection);
+
+        assertFalse(mUserState.getBoundServicesLocked().contains(mMockConnection));
+        assertTrue(mUserState.getBindingServicesLocked().contains(COMPONENT_NAME));
+    }
+
+    @Test
+    public void setSoftKeyboardMode_withInvalidShowMode_shouldKeepDefaultAuto() {
+        final int invalidShowMode = SHOW_MODE_HIDDEN | SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE;
+
+        assertFalse(mUserState.setSoftKeyboardModeLocked(invalidShowMode, null));
+
+        assertEquals(SHOW_MODE_AUTO, mUserState.getSoftKeyboardShowModeLocked());
+    }
+
+    @Test
+    public void setSoftKeyboardMode_newModeSameWithCurrentState_returnTrue() {
+        when(mMockConnection.getComponentName()).thenReturn(COMPONENT_NAME);
+        mUserState.addServiceLocked(mMockConnection);
+
+        assertTrue(mUserState.setSoftKeyboardModeLocked(SHOW_MODE_AUTO, null));
+    }
+
+    @Test
+    public void setSoftKeyboardMode_withIgnoreHardKb_whenHardKbOverridden_returnFalseAdNoChange() {
+        putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
+                SHOW_MODE_AUTO | SHOW_MODE_HARD_KEYBOARD_OVERRIDDEN, USER_ID);
+
+        assertFalse(mUserState.setSoftKeyboardModeLocked(SHOW_MODE_IGNORE_HARD_KEYBOARD, null));
+
+        assertEquals(SHOW_MODE_AUTO, mUserState.getSoftKeyboardShowModeLocked());
+    }
+
+    @Test
+    public void
+            setSoftKeyboardMode_withIgnoreHardKb_whenShowImeWithHardKb_setOriginalHardKbValue() {
+        putSecureIntForUser(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, STATE_SHOW_IME, USER_ID);
+
+        assertTrue(mUserState.setSoftKeyboardModeLocked(SHOW_MODE_IGNORE_HARD_KEYBOARD, null));
+
+        assertEquals(SHOW_MODE_IGNORE_HARD_KEYBOARD | SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE,
+                getSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, USER_ID));
+    }
+
+    @Test
+    public void setSoftKeyboardMode_whenCurrentIgnoreHardKb_shouldSetShowImeWithHardKbValue() {
+        mUserState.setSoftKeyboardModeLocked(SHOW_MODE_IGNORE_HARD_KEYBOARD, COMPONENT_NAME);
+        putSecureIntForUser(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, STATE_HIDE_IME, USER_ID);
+        putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE,
+                SHOW_MODE_IGNORE_HARD_KEYBOARD | SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE, USER_ID);
+
+        assertTrue(mUserState.setSoftKeyboardModeLocked(SHOW_MODE_AUTO, null));
+
+        assertEquals(STATE_SHOW_IME, getSecureIntForUser(
+                Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, USER_ID));
+    }
+
+    @Test
+    public void setSoftKeyboardMode_withRequester_shouldUpdateInternalStateAndSettingsAsIs() {
+        assertTrue(mUserState.setSoftKeyboardModeLocked(SHOW_MODE_HIDDEN, COMPONENT_NAME));
+
+        assertEquals(SHOW_MODE_HIDDEN, mUserState.getSoftKeyboardShowModeLocked());
+        assertEquals(SHOW_MODE_HIDDEN, getSecureIntForUser(
+                Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, USER_ID));
+        assertEquals(COMPONENT_NAME, mUserState.getServiceChangingSoftKeyboardModeLocked());
+    }
+
+    @Test
+    public void setSoftKeyboardMode_shouldNotifyBoundService() {
+        mUserState.addServiceLocked(mMockConnection);
+
+        assertTrue(mUserState.setSoftKeyboardModeLocked(SHOW_MODE_HIDDEN, COMPONENT_NAME));
+
+        verify(mMockConnection).notifySoftKeyboardShowModeChangedLocked(eq(SHOW_MODE_HIDDEN));
+    }
+
+    private int getSecureIntForUser(String key, int userId) {
+        return Settings.Secure.getIntForUser(mMockResolver, key, -1, userId);
+    }
+
+    private void putSecureIntForUser(String key, int value, int userId) {
+        Settings.Secure.putIntForUser(mMockResolver, key, value, userId);
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java
index deb6f71..8da927d 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java
@@ -56,7 +56,6 @@
 
     MessageCapturingHandler mMessageCapturingHandler;
 
-    @Mock AccessibilityManagerService.UserState mMockUserState;
     @Mock Context mMockContext;
     @Mock AccessibilityServiceInfo mMockServiceInfo;
     @Mock ResolveInfo mMockResolveInfo;
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 9a1fd9c..c3ef832 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -93,7 +93,7 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.R;
-import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.LockscreenCredential;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
 import com.android.server.devicepolicy.DevicePolicyManagerService.RestrictionsListener;
@@ -4266,9 +4266,9 @@
         assertTrue(dpm.isResetPasswordTokenActive(admin1));
 
         // test reset password with token
-        when(getServices().lockPatternUtils.setLockCredentialWithToken(eq(password.getBytes()),
-                eq(LockPatternUtils.CREDENTIAL_TYPE_PASSWORD),
-                eq(DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC), eq(handle), eq(token),
+        when(getServices().lockPatternUtils.setLockCredentialWithToken(
+                eq(LockscreenCredential.createPassword(password)),
+                eq(handle), eq(token),
                 eq(UserHandle.USER_SYSTEM)))
                 .thenReturn(true);
         assertTrue(dpm.resetPasswordWithToken(admin1, password, token, 0));
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
index f9ac022..537287d 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java
@@ -35,7 +35,9 @@
 import android.content.pm.PackageManager;
 import android.content.pm.UserInfo;
 import android.hardware.authsecret.V1_0.IAuthSecret;
+import android.hardware.face.Face;
 import android.hardware.face.FaceManager;
+import android.hardware.fingerprint.Fingerprint;
 import android.hardware.fingerprint.FingerprintManager;
 import android.os.FileUtils;
 import android.os.IProgressListener;
@@ -249,11 +251,32 @@
         when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)).thenReturn(true);
         when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
         when(mFingerprintManager.hasEnrolledFingerprints(userId)).thenReturn(true);
+        doAnswer(new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                Fingerprint fp = (Fingerprint) invocation.getArguments()[0];
+                FingerprintManager.RemovalCallback callback =
+                        (FingerprintManager.RemovalCallback) invocation.getArguments()[2];
+                callback.onRemovalSucceeded(fp, 0);
+                return null;
+            }
+        }).when(mFingerprintManager).remove(any(), eq(userId), any());
+
 
         // Hardware must be detected and templates must be enrolled
         when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE)).thenReturn(true);
         when(mFaceManager.isHardwareDetected()).thenReturn(true);
         when(mFaceManager.hasEnrolledTemplates(userId)).thenReturn(true);
+        doAnswer(new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                Face face = (Face) invocation.getArguments()[0];
+                FaceManager.RemovalCallback callback =
+                        (FaceManager.RemovalCallback) invocation.getArguments()[2];
+                callback.onRemovalSucceeded(face, 0);
+                return null;
+            }
+        }).when(mFaceManager).remove(any(), eq(userId), any());
     }
 
     @Override
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java
index c00d33b..b60111e 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java
@@ -19,10 +19,9 @@
 import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC;
 import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
 
-import static com.android.internal.widget.LockPatternUtils.stringToPattern;
-
 import static junit.framework.Assert.assertEquals;
 
+import static org.mockito.Matchers.anyBoolean;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.never;
@@ -48,6 +47,8 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.LockPatternView;
+import com.android.internal.widget.LockscreenCredential;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -55,6 +56,8 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.util.List;
+
 /**
  * Test class for {@link LockSettingsShellCommand}.
  *
@@ -87,24 +90,30 @@
     public void testWrongPassword() throws Exception {
         when(mLockPatternUtils.isLockPatternEnabled(mUserId)).thenReturn(false);
         when(mLockPatternUtils.isLockPasswordEnabled(mUserId)).thenReturn(true);
-        when(mLockPatternUtils.checkPassword("1234".getBytes(), mUserId)).thenReturn(false);
+        when(mLockPatternUtils.checkCredential(
+                LockscreenCredential.createPassword("1234"), mUserId, null)).thenReturn(false);
         assertEquals(-1, mCommand.exec(mBinder, in, out, err,
                 new String[] { "set-pin", "--old", "1234" },
                 mShellCallback, mResultReceiver));
-        verify(mLockPatternUtils, never()).saveLockPassword(any(byte[].class), any(byte[].class),
-                anyInt(), anyInt());
+        verify(mLockPatternUtils, never()).setLockCredential(any(), any(),
+                anyInt(), anyBoolean());
     }
 
     @Test
     public void testChangePin() throws Exception {
         when(mLockPatternUtils.isLockPatternEnabled(mUserId)).thenReturn(false);
         when(mLockPatternUtils.isLockPasswordEnabled(mUserId)).thenReturn(true);
-        when(mLockPatternUtils.checkPassword("1234".getBytes(), mUserId)).thenReturn(true);
+        when(mLockPatternUtils.getKeyguardStoredPasswordQuality(mUserId)).thenReturn(
+                PASSWORD_QUALITY_NUMERIC);
+        when(mLockPatternUtils.checkCredential(
+                LockscreenCredential.createPin("1234"), mUserId, null)).thenReturn(true);
         assertEquals(0, mCommand.exec(new Binder(), in, out, err,
                 new String[] { "set-pin", "--old", "1234", "4321" },
                 mShellCallback, mResultReceiver));
-        verify(mLockPatternUtils).saveLockPassword("4321".getBytes(), "1234".getBytes(),
-                PASSWORD_QUALITY_NUMERIC, mUserId);
+        verify(mLockPatternUtils).setLockCredential(
+                LockscreenCredential.createPin("4321"),
+                LockscreenCredential.createPin("1234"),
+                mUserId);
     }
 
     @Test
@@ -121,12 +130,17 @@
     public void testChangePassword() throws Exception {
         when(mLockPatternUtils.isLockPatternEnabled(mUserId)).thenReturn(false);
         when(mLockPatternUtils.isLockPasswordEnabled(mUserId)).thenReturn(true);
-        when(mLockPatternUtils.checkPassword("1234".getBytes(), mUserId)).thenReturn(true);
+        when(mLockPatternUtils.getKeyguardStoredPasswordQuality(mUserId)).thenReturn(
+                PASSWORD_QUALITY_ALPHABETIC);
+        when(mLockPatternUtils.checkCredential(
+                LockscreenCredential.createPassword("1234"), mUserId, null)).thenReturn(true);
         assertEquals(0,  mCommand.exec(new Binder(), in, out, err,
                 new String[] { "set-password", "--old", "1234", "4321" },
                 mShellCallback, mResultReceiver));
-        verify(mLockPatternUtils).saveLockPassword("4321".getBytes(), "1234".getBytes(),
-                PASSWORD_QUALITY_ALPHABETIC, mUserId);
+        verify(mLockPatternUtils).setLockCredential(
+                LockscreenCredential.createPassword("4321"),
+                LockscreenCredential.createPassword("1234"),
+                mUserId);
     }
 
     @Test
@@ -143,11 +157,15 @@
     public void testChangePattern() throws Exception {
         when(mLockPatternUtils.isLockPatternEnabled(mUserId)).thenReturn(true);
         when(mLockPatternUtils.isLockPasswordEnabled(mUserId)).thenReturn(false);
-        when(mLockPatternUtils.checkPattern(stringToPattern("1234"), mUserId)).thenReturn(true);
+        when(mLockPatternUtils.checkCredential(
+                LockscreenCredential.createPattern(stringToPattern("1234")),
+                mUserId, null)).thenReturn(true);
         assertEquals(0, mCommand.exec(new Binder(), in, out, err,
                 new String[] { "set-pattern", "--old", "1234", "4321" },
                 mShellCallback, mResultReceiver));
-        verify(mLockPatternUtils).saveLockPattern(stringToPattern("4321"), "1234".getBytes(),
+        verify(mLockPatternUtils).setLockCredential(
+                LockscreenCredential.createPattern(stringToPattern("4321")),
+                LockscreenCredential.createPattern(stringToPattern("1234")),
                 mUserId);
     }
 
@@ -165,10 +183,19 @@
     public void testClear() throws Exception {
         when(mLockPatternUtils.isLockPatternEnabled(mUserId)).thenReturn(true);
         when(mLockPatternUtils.isLockPasswordEnabled(mUserId)).thenReturn(false);
-        when(mLockPatternUtils.checkPattern(stringToPattern("1234"), mUserId)).thenReturn(true);
+        when(mLockPatternUtils.checkCredential(
+                LockscreenCredential.createPattern(stringToPattern("1234")),
+                mUserId, null)).thenReturn(true);
         assertEquals(0, mCommand.exec(new Binder(), in, out, err,
                 new String[] { "clear", "--old", "1234" },
                 mShellCallback, mResultReceiver));
-        verify(mLockPatternUtils).clearLock("1234".getBytes(), mUserId);
+        verify(mLockPatternUtils).setLockCredential(
+                LockscreenCredential.createNone(),
+                LockscreenCredential.createPattern(stringToPattern("1234")),
+                mUserId);
+    }
+
+    private List<LockPatternView.Cell> stringToPattern(String str) {
+        return LockPatternUtils.byteArrayToPattern(str.getBytes());
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java
index 2a169b7..cb51897 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java
@@ -434,35 +434,6 @@
         assertEquals(2, PersistentData.TYPE_SP_WEAVER);
     }
 
-    public void testCredentialHash_serializeUnserialize() {
-        byte[] serialized = CredentialHash.create(
-                PAYLOAD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD).toBytes();
-        CredentialHash deserialized = CredentialHash.fromBytes(serialized);
-
-        assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, deserialized.type);
-        assertArrayEquals(PAYLOAD, deserialized.hash);
-    }
-
-    public void testCredentialHash_unserialize_versionGatekeeper() {
-        // This test ensures that we can read serialized VERSION_GATEKEEPER CredentialHashes
-        // even if we change the wire format in the future.
-        byte[] serialized = new byte[] {
-                1, /* VERSION_GATEKEEPER */
-                2, /* CREDENTIAL_TYPE_PASSWORD */
-                0, 0, 0, 5, /* hash length */
-                1, 2, -1, -2, 33, /* hash */
-        };
-        CredentialHash deserialized = CredentialHash.fromBytes(serialized);
-
-        assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, deserialized.type);
-        assertArrayEquals(PAYLOAD, deserialized.hash);
-
-        // Make sure the constants we use on the wire do not change.
-        assertEquals(-1, LockPatternUtils.CREDENTIAL_TYPE_NONE);
-        assertEquals(1, LockPatternUtils.CREDENTIAL_TYPE_PATTERN);
-        assertEquals(2, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD);
-    }
-
     private static void assertArrayEquals(byte[] expected, byte[] actual) {
         if (!Arrays.equals(expected, actual)) {
             fail("expected:<" + Arrays.toString(expected) +
diff --git a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
index 0776589..42ca42a 100644
--- a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
+++ b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java
@@ -39,6 +39,7 @@
 import androidx.test.filters.SmallTest;
 
 import com.android.internal.widget.LockPatternUtils;
+import com.android.internal.widget.LockscreenCredential;
 import com.android.internal.widget.VerifyCredentialResponse;
 import com.android.server.locksettings.SyntheticPasswordManager.AuthenticationResult;
 import com.android.server.locksettings.SyntheticPasswordManager.AuthenticationToken;
@@ -364,7 +365,7 @@
         // Verify DPM gets notified about new device lock
         flushHandlerTasks();
         final PasswordMetrics metric = PasswordMetrics.computeForCredential(
-                LockPatternUtils.CREDENTIAL_TYPE_PATTERN, pattern);
+                LockscreenCredential.createPattern(LockPatternUtils.byteArrayToPattern(pattern)));
         assertEquals(metric, mService.getUserPasswordMetrics(PRIMARY_USER_ID));
         verify(mDevicePolicyManager).reportPasswordChanged(PRIMARY_USER_ID);
 
@@ -512,7 +513,7 @@
         assertFalse(mService.havePattern(PRIMARY_USER_ID));
     }
 
-    public void testgetHashFactorPrimaryUser() throws RemoteException {
+    public void testGetHashFactorPrimaryUser() throws RemoteException {
         final byte[] password = "password".getBytes();
         mService.setLockCredential(password, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
                 PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID, false);
@@ -527,7 +528,7 @@
         assertArrayEquals(hashFactor, newHashFactor);
     }
 
-    public void testgetHashFactorManagedProfileUnifiedChallenge() throws RemoteException {
+    public void testGetHashFactorManagedProfileUnifiedChallenge() throws RemoteException {
         final byte[] pattern = "1236".getBytes();
         mService.setLockCredential(pattern, LockPatternUtils.CREDENTIAL_TYPE_PATTERN,
                 null, PASSWORD_QUALITY_SOMETHING, PRIMARY_USER_ID, false);
@@ -535,7 +536,7 @@
         assertNotNull(mService.getHashFactor(null, MANAGED_PROFILE_USER_ID));
     }
 
-    public void testgetHashFactorManagedProfileSeparateChallenge() throws RemoteException {
+    public void testGetHashFactorManagedProfileSeparateChallenge() throws RemoteException {
         final byte[] primaryPassword = "primary".getBytes();
         final byte[] profilePassword = "profile".getBytes();
         mService.setLockCredential(primaryPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null,
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
index ba12b73..8a48904 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -113,6 +113,7 @@
 import android.net.NetworkTemplate;
 import android.net.StringNetworkSpecifier;
 import android.os.Binder;
+import android.os.Handler;
 import android.os.INetworkManagementService;
 import android.os.PersistableBundle;
 import android.os.PowerManagerInternal;
@@ -1117,7 +1118,7 @@
         // Define simple data plan
         final SubscriptionPlan plan = buildMonthlyDataPlan(
                 ZonedDateTime.parse("2015-11-01T00:00:00.00Z"), DataUnit.MEGABYTES.toBytes(1800));
-        mService.setSubscriptionPlans(TEST_SUB_ID, new SubscriptionPlan[] { plan },
+        setSubscriptionPlans(TEST_SUB_ID, new SubscriptionPlan[] { plan },
                 mServiceContext.getOpPackageName());
 
         // We're 20% through the month (6 days)
@@ -1241,7 +1242,7 @@
         // Define simple data plan which gives us effectively 60MB/day
         final SubscriptionPlan plan = buildMonthlyDataPlan(
                 ZonedDateTime.parse("2015-11-01T00:00:00.00Z"), DataUnit.MEGABYTES.toBytes(1800));
-        mService.setSubscriptionPlans(TEST_SUB_ID, new SubscriptionPlan[] { plan },
+        setSubscriptionPlans(TEST_SUB_ID, new SubscriptionPlan[] { plan },
                 mServiceContext.getOpPackageName());
 
         // We're 20% through the month (6 days)
@@ -1457,6 +1458,8 @@
         when(mConnManager.getAllNetworkState()).thenReturn(new NetworkState[0]);
         when(mSubscriptionManager.getActiveSubscriptionIdList()).thenReturn(new int[]{FAKE_SUB_ID});
         when(mTelephonyManager.getSubscriberId(FAKE_SUB_ID)).thenReturn(FAKE_SUBSCRIBER_ID);
+        when(mTelephonyManager.createForSubscriptionId(FAKE_SUB_ID))
+                .thenReturn(mock(TelephonyManager.class));
         PersistableBundle bundle = CarrierConfigManager.getDefaultConfig();
         when(mCarrierConfigManager.getConfigForSubId(FAKE_SUB_ID)).thenReturn(bundle);
         setNetworkPolicies(buildDefaultFakeMobilePolicy());
@@ -1468,6 +1471,8 @@
         when(mConnManager.getAllNetworkState()).thenReturn(new NetworkState[0]);
         when(mSubscriptionManager.getActiveSubscriptionIdList()).thenReturn(new int[]{FAKE_SUB_ID});
         when(mTelephonyManager.getSubscriberId(FAKE_SUB_ID)).thenReturn(FAKE_SUBSCRIBER_ID);
+        when(mTelephonyManager.createForSubscriptionId(FAKE_SUB_ID))
+                .thenReturn(mock(TelephonyManager.class));
         when(mCarrierConfigManager.getConfigForSubId(FAKE_SUB_ID)).thenReturn(null);
         setNetworkPolicies(buildDefaultFakeMobilePolicy());
         // smoke test to make sure no errors are raised
@@ -1653,7 +1658,7 @@
             final SubscriptionPlan plan = buildMonthlyDataPlan(
                     ZonedDateTime.parse("2015-11-01T00:00:00.00Z"),
                     DataUnit.MEGABYTES.toBytes(1800));
-            mService.setSubscriptionPlans(TEST_SUB_ID, new SubscriptionPlan[]{plan},
+            setSubscriptionPlans(TEST_SUB_ID, new SubscriptionPlan[]{plan},
                     mServiceContext.getOpPackageName());
 
             reset(mTelephonyManager, mNetworkManager, mNotifManager);
@@ -1674,7 +1679,7 @@
             final SubscriptionPlan plan = buildMonthlyDataPlan(
                     ZonedDateTime.parse("2015-11-01T00:00:00.00Z"),
                     DataUnit.MEGABYTES.toBytes(100));
-            mService.setSubscriptionPlans(TEST_SUB_ID, new SubscriptionPlan[]{plan},
+            setSubscriptionPlans(TEST_SUB_ID, new SubscriptionPlan[]{plan},
                     mServiceContext.getOpPackageName());
 
             reset(mTelephonyManager, mNetworkManager, mNotifManager);
@@ -1690,7 +1695,7 @@
         {
             final SubscriptionPlan plan = buildMonthlyDataPlan(
                     ZonedDateTime.parse("2015-11-01T00:00:00.00Z"), BYTES_UNLIMITED);
-            mService.setSubscriptionPlans(TEST_SUB_ID, new SubscriptionPlan[]{plan},
+            setSubscriptionPlans(TEST_SUB_ID, new SubscriptionPlan[]{plan},
                     mServiceContext.getOpPackageName());
 
             reset(mTelephonyManager, mNetworkManager, mNotifManager);
@@ -1707,7 +1712,7 @@
         {
             final SubscriptionPlan plan = buildMonthlyDataPlan(
                     ZonedDateTime.parse("2015-11-01T00:00:00.00Z"), BYTES_UNLIMITED);
-            mService.setSubscriptionPlans(TEST_SUB_ID, new SubscriptionPlan[]{plan},
+            setSubscriptionPlans(TEST_SUB_ID, new SubscriptionPlan[]{plan},
                     mServiceContext.getOpPackageName());
 
             reset(mTelephonyManager, mNetworkManager, mNotifManager);
@@ -1923,6 +1928,8 @@
         when(mSubscriptionManager.getActiveSubscriptionIdList()).thenReturn(
                 new int[] { TEST_SUB_ID });
         when(mTelephonyManager.getSubscriberId(TEST_SUB_ID)).thenReturn(TEST_IMSI);
+        when(mTelephonyManager.createForSubscriptionId(TEST_SUB_ID))
+                .thenReturn(mock(TelephonyManager.class));
         doNothing().when(mTelephonyManager).setPolicyDataEnabled(anyBoolean(), anyInt());
         expectNetworkState(false /* roaming */);
     }
@@ -2049,6 +2056,23 @@
 
     private FutureIntent mRestrictBackgroundChanged;
 
+    private void postMsgAndWaitForCompletion() throws InterruptedException {
+        final Handler handler = mService.getHandlerForTesting();
+        final CountDownLatch latch = new CountDownLatch(1);
+        mService.getHandlerForTesting().post(latch::countDown);
+        if (!latch.await(5, TimeUnit.SECONDS)) {
+            fail("Timed out waiting for the test msg to be handled");
+        }
+    }
+
+    private void setSubscriptionPlans(int subId, SubscriptionPlan[] plans, String callingPackage)
+            throws InterruptedException {
+        mService.setSubscriptionPlans(subId, plans, callingPackage);
+        // setSubscriptionPlans() triggers async events, wait for those to be completed before
+        // moving forward as they could interfere with the tests later.
+        postMsgAndWaitForCompletion();
+    }
+
     private void setRestrictBackground(boolean flag) throws Exception {
         mService.setRestrictBackground(flag);
         // Sanity check.
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
index 8c3373f..3ae5674 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java
@@ -22,6 +22,7 @@
 import static android.app.NotificationManager.IMPORTANCE_LOW;
 import static android.app.NotificationManager.IMPORTANCE_MIN;
 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS;
+import static android.media.AudioAttributes.USAGE_NOTIFICATION_RINGTONE;
 
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertNull;
@@ -105,6 +106,7 @@
     private int mUid = 1000;
     private int mPid = 2000;
     private android.os.UserHandle mUser = UserHandle.of(ActivityManager.getCurrentUser());
+    private NotificationChannel mChannel;
 
     private VibrateRepeatMatcher mVibrateOnceMatcher = new VibrateRepeatMatcher(-1);
     private VibrateRepeatMatcher mVibrateLoopMatcher = new VibrateRepeatMatcher(0);
@@ -158,6 +160,8 @@
         mService.mScreenOn = false;
         mService.mInCallStateOffHook = false;
         mService.mNotificationPulseEnabled = true;
+
+        mChannel = new NotificationChannel("test", "test", IMPORTANCE_HIGH);
     }
 
     //
@@ -174,13 +178,18 @@
                 true /* noisy */, false /* buzzy*/, false /* lights */);
     }
 
+    private NotificationRecord getBeepyOtherNotification() {
+        return getNotificationRecord(mOtherId, false /* insistent */, false /* once */,
+                true /* noisy */, false /* buzzy*/, false /* lights */);
+    }
+
     private NotificationRecord getBeepyOnceNotification() {
         return getNotificationRecord(mId, false /* insistent */, true /* once */,
                 true /* noisy */, false /* buzzy*/, false /* lights */);
     }
 
     private NotificationRecord getQuietNotification() {
-        return getNotificationRecord(mId, false /* insistent */, false /* once */,
+        return getNotificationRecord(mId, false /* insistent */, true /* once */,
                 false /* noisy */, false /* buzzy*/, false /* lights */);
     }
 
@@ -214,6 +223,11 @@
                 false /* noisy */, true /* buzzy*/, false /* lights */);
     }
 
+    private NotificationRecord getBuzzyOtherNotification() {
+        return getNotificationRecord(mOtherId, false /* insistent */, false /* once */,
+                false /* noisy */, true /* buzzy*/, false /* lights */);
+    }
+
     private NotificationRecord getBuzzyOnceNotification() {
         return getNotificationRecord(mId, false /* insistent */, true /* once */,
                 false /* noisy */, true /* buzzy*/, false /* lights */);
@@ -239,22 +253,34 @@
                 false /* noisy */, false /* buzzy*/, true /* lights */);
     }
 
-    private NotificationRecord getCallRecord(int id, boolean insistent) {
-        return getNotificationRecord(id, false, false /* once */, true /* noisy */,
-                false /* buzzy */, false /* lights */, false /* default vib */,
-                false /* default sound */, false /* default lights */, "",
-                Notification.GROUP_ALERT_ALL, false);
+    private NotificationRecord getCallRecord(int id, NotificationChannel channel, boolean looping) {
+        final Builder builder = new Builder(getContext())
+                .setContentTitle("foo")
+                .setSmallIcon(android.R.drawable.sym_def_app_icon)
+                .setPriority(Notification.PRIORITY_HIGH);
+        Notification n = builder.build();
+        if (looping) {
+            n.flags |= Notification.FLAG_INSISTENT;
+        }
+        StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, id, mTag, mUid,
+                mPid, n, mUser, null, System.currentTimeMillis());
+        NotificationRecord r = new NotificationRecord(getContext(), sbn, channel);
+        mService.addNotification(r);
+
+        return r;
     }
 
     private NotificationRecord getNotificationRecord(int id, boolean insistent, boolean once,
             boolean noisy, boolean buzzy, boolean lights) {
-        return getNotificationRecord(id, insistent, once, noisy, buzzy, lights, true, true, true,
-                null, Notification.GROUP_ALERT_ALL, false);
+        return getNotificationRecord(id, insistent, once, noisy, buzzy, lights, buzzy, noisy,
+                lights, null, Notification.GROUP_ALERT_ALL, false);
     }
 
-    private NotificationRecord getLeanbackNotificationRecord(int id, boolean insistent, boolean once,
+    private NotificationRecord getLeanbackNotificationRecord(int id, boolean insistent,
+            boolean once,
             boolean noisy, boolean buzzy, boolean lights) {
-        return getNotificationRecord(id, insistent, once, noisy, buzzy, lights, true, true, true,
+        return getNotificationRecord(id, insistent, once, noisy, buzzy, lights, true, true,
+                true,
                 null, Notification.GROUP_ALERT_ALL, true);
     }
 
@@ -265,16 +291,16 @@
 
     private NotificationRecord getLightsNotificationRecord(String groupKey,
             int groupAlertBehavior) {
-        return getNotificationRecord(mId, false, false, false, false, true /*lights*/, true, true,
-                true, groupKey, groupAlertBehavior, false);
+        return getNotificationRecord(mId, false, false, false, false, true /*lights*/, true,
+                true, true, groupKey, groupAlertBehavior, false);
     }
 
-    private NotificationRecord getNotificationRecord(int id, boolean insistent, boolean once,
+    private NotificationRecord getNotificationRecord(int id,
+            boolean insistent, boolean once,
             boolean noisy, boolean buzzy, boolean lights, boolean defaultVibration,
             boolean defaultSound, boolean defaultLights, String groupKey, int groupAlertBehavior,
             boolean isLeanback) {
-        NotificationChannel channel =
-                new NotificationChannel("test", "test", IMPORTANCE_HIGH);
+
         final Builder builder = new Builder(getContext())
                 .setContentTitle("foo")
                 .setSmallIcon(android.R.drawable.sym_def_app_icon)
@@ -285,31 +311,37 @@
         if (noisy) {
             if (defaultSound) {
                 defaults |= Notification.DEFAULT_SOUND;
-                channel.setSound(Settings.System.DEFAULT_NOTIFICATION_URI,
+                mChannel.setSound(Settings.System.DEFAULT_NOTIFICATION_URI,
                         Notification.AUDIO_ATTRIBUTES_DEFAULT);
             } else {
                 builder.setSound(CUSTOM_SOUND);
-                channel.setSound(CUSTOM_SOUND, CUSTOM_ATTRIBUTES);
+                mChannel.setSound(CUSTOM_SOUND, CUSTOM_ATTRIBUTES);
             }
         } else {
-            channel.setSound(null, null);
+            mChannel.setSound(null, null);
         }
         if (buzzy) {
             if (defaultVibration) {
                 defaults |= Notification.DEFAULT_VIBRATE;
             } else {
                 builder.setVibrate(CUSTOM_VIBRATION);
-                channel.setVibrationPattern(CUSTOM_VIBRATION);
+                mChannel.setVibrationPattern(CUSTOM_VIBRATION);
             }
-            channel.enableVibration(true);
+            mChannel.enableVibration(true);
+        } else {
+            mChannel.setVibrationPattern(null);
+            mChannel.enableVibration(false);
         }
+
         if (lights) {
             if (defaultLights) {
                 defaults |= Notification.DEFAULT_LIGHTS;
             } else {
                 builder.setLights(CUSTOM_LIGHT_COLOR, CUSTOM_LIGHT_ON, CUSTOM_LIGHT_OFF);
             }
-            channel.enableLights(true);
+            mChannel.enableLights(true);
+        } else {
+            mChannel.enableLights(false);
         }
         builder.setDefaults(defaults);
 
@@ -329,7 +361,7 @@
 
         StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, id, mTag, mUid,
                 mPid, n, mUser, null, System.currentTimeMillis());
-        NotificationRecord r = new NotificationRecord(context, sbn, channel);
+        NotificationRecord r = new NotificationRecord(context, sbn, mChannel);
         mService.addNotification(r);
         return r;
     }
@@ -339,18 +371,19 @@
     //
 
     private void verifyNeverBeep() throws RemoteException {
-        verify(mRingtonePlayer, never()).playAsync((Uri) anyObject(), (UserHandle) anyObject(),
-                anyBoolean(), (AudioAttributes) anyObject());
+        verify(mRingtonePlayer, never()).playAsync(any(), any(), anyBoolean(), any());
     }
 
-    private void verifyBeep() throws RemoteException {
-        verify(mRingtonePlayer, times(1)).playAsync((Uri) anyObject(), (UserHandle) anyObject(),
-                eq(true), (AudioAttributes) anyObject());
+    private void verifyBeepUnlooped() throws RemoteException  {
+        verify(mRingtonePlayer, times(1)).playAsync(any(), any(), eq(false), any());
     }
 
-    private void verifyBeepLooped() throws RemoteException {
-        verify(mRingtonePlayer, times(1)).playAsync((Uri) anyObject(), (UserHandle) anyObject(),
-                eq(false), (AudioAttributes) anyObject());
+    private void verifyBeepLooped() throws RemoteException  {
+        verify(mRingtonePlayer, times(1)).playAsync(any(), any(), eq(true), any());
+    }
+
+    private void verifyBeep(int times)  throws RemoteException  {
+        verify(mRingtonePlayer, times(times)).playAsync(any(), any(), anyBoolean(), any());
     }
 
     private void verifyNeverStopAudio() throws RemoteException {
@@ -362,24 +395,31 @@
     }
 
     private void verifyNeverVibrate() {
-        verify(mVibrator, never()).vibrate(anyInt(), anyString(), (VibrationEffect) anyObject(),
-                anyString(), (AudioAttributes) anyObject());
+        verify(mVibrator, never()).vibrate(anyInt(), anyString(), any(), anyString(), any());
     }
 
     private void verifyVibrate() {
         verify(mVibrator, times(1)).vibrate(anyInt(), anyString(), argThat(mVibrateOnceMatcher),
-                anyString(), (AudioAttributes) anyObject());
+                anyString(), any());
+    }
+
+    private void verifyVibrate(int times) {
+        verify(mVibrator, times(times)).vibrate(anyInt(), anyString(), any(), anyString(), any());
     }
 
     private void verifyVibrateLooped() {
         verify(mVibrator, times(1)).vibrate(anyInt(), anyString(), argThat(mVibrateLoopMatcher),
-                anyString(), (AudioAttributes) anyObject());
+                anyString(), any());
     }
 
     private void verifyDelayedVibrateLooped() {
         verify(mVibrator, timeout(MAX_VIBRATION_DELAY).times(1)).vibrate(anyInt(), anyString(),
-                argThat(mVibrateLoopMatcher), anyString(),
-                (AudioAttributes) anyObject());
+                argThat(mVibrateLoopMatcher), anyString(), any());
+    }
+
+    private void verifyDelayedVibrate() {
+        verify(mVibrator, timeout(MAX_VIBRATION_DELAY).times(1)).vibrate(anyInt(), anyString(),
+                argThat(mVibrateOnceMatcher), anyString(), any());
     }
 
     private void verifyStopVibrate() {
@@ -398,11 +438,6 @@
         verify(mLight, times(1)).setFlashing(anyInt(), anyInt(), anyInt(), anyInt());
     }
 
-    private void verifyCustomLights() {
-        verify(mLight, times(1)).setFlashing(
-                eq(CUSTOM_LIGHT_COLOR), anyInt(), eq(CUSTOM_LIGHT_ON), eq(CUSTOM_LIGHT_OFF));
-    }
-
     //
     // Tests
     //
@@ -425,7 +460,7 @@
 
         mService.buzzBeepBlinkLocked(r);
 
-        verifyBeepLooped();
+        verifyBeepUnlooped();
         verifyNeverVibrate();
         verify(mAccessibilityService, times(1)).sendAccessibilityEvent(any(), anyInt());
         assertTrue(r.isInterruptive());
@@ -438,7 +473,7 @@
 
         mService.buzzBeepBlinkLocked(r);
 
-        verifyBeep();
+        verifyBeepLooped();
         assertTrue(r.isInterruptive());
         assertNotEquals(-1, r.getLastAudiblyAlertedMs());
     }
@@ -492,7 +527,7 @@
 
         mService.buzzBeepBlinkLocked(r);
 
-        verifyBeepLooped();
+        verifyBeepUnlooped();
         assertTrue(r.isInterruptive());
     }
 
@@ -533,7 +568,7 @@
         // update should beep
         r.isUpdate = true;
         mService.buzzBeepBlinkLocked(r);
-        verifyBeepLooped();
+        verifyBeepUnlooped();
         verify(mAccessibilityService, times(2)).sendAccessibilityEvent(any(), anyInt());
         assertTrue(r.isInterruptive());
         assertNotEquals(-1, r.getLastAudiblyAlertedMs());
@@ -723,7 +758,7 @@
         mService.buzzBeepBlinkLocked(r);
 
         verifyNeverVibrate();
-        verifyBeepLooped();
+        verifyBeepUnlooped();
         assertTrue(r.isInterruptive());
         assertNotEquals(-1, r.getLastAudiblyAlertedMs());
     }
@@ -821,7 +856,7 @@
 
         mService.buzzBeepBlinkLocked(summary);
 
-        verifyBeepLooped();
+        verifyBeepUnlooped();
         // summaries are never interruptive for notification counts
         assertFalse(summary.isInterruptive());
         assertNotEquals(-1, summary.getLastAudiblyAlertedMs());
@@ -833,7 +868,7 @@
 
         mService.buzzBeepBlinkLocked(nonGroup);
 
-        verifyBeepLooped();
+        verifyBeepUnlooped();
         assertTrue(nonGroup.isInterruptive());
         assertNotEquals(-1, nonGroup.getLastAudiblyAlertedMs());
     }
@@ -856,7 +891,7 @@
 
         mService.buzzBeepBlinkLocked(child);
 
-        verifyBeepLooped();
+        verifyBeepUnlooped();
         assertTrue(child.isInterruptive());
         assertNotEquals(-1, child.getLastAudiblyAlertedMs());
     }
@@ -867,7 +902,7 @@
 
         mService.buzzBeepBlinkLocked(nonGroup);
 
-        verifyBeepLooped();
+        verifyBeepUnlooped();
         assertTrue(nonGroup.isInterruptive());
         assertNotEquals(-1, nonGroup.getLastAudiblyAlertedMs());
     }
@@ -878,7 +913,7 @@
 
         mService.buzzBeepBlinkLocked(group);
 
-        verifyBeepLooped();
+        verifyBeepUnlooped();
         assertTrue(group.isInterruptive());
         assertNotEquals(-1, group.getLastAudiblyAlertedMs());
     }
@@ -1293,7 +1328,11 @@
 
     @Test
     public void testListenerHintCall() throws Exception {
-        NotificationRecord r = getCallRecord(1, true);
+        NotificationChannel ringtoneChannel =
+                new NotificationChannel("ringtone", "", IMPORTANCE_HIGH);
+        ringtoneChannel.setSound(Settings.System.DEFAULT_RINGTONE_URI,
+                new AudioAttributes.Builder().setUsage(USAGE_NOTIFICATION_RINGTONE).build());
+        NotificationRecord r = getCallRecord(1, ringtoneChannel, true);
 
         mService.setHints(NotificationListenerService.HINT_HOST_DISABLE_CALL_EFFECTS);
 
@@ -1310,7 +1349,7 @@
 
         mService.buzzBeepBlinkLocked(r);
 
-        verifyBeepLooped();
+        verifyBeepUnlooped();
     }
 
     @Test
@@ -1326,7 +1365,11 @@
 
     @Test
     public void testListenerHintBoth() throws Exception {
-        NotificationRecord r = getCallRecord(1, true);
+        NotificationChannel ringtoneChannel =
+                new NotificationChannel("ringtone", "", IMPORTANCE_HIGH);
+        ringtoneChannel.setSound(Settings.System.DEFAULT_RINGTONE_URI,
+                new AudioAttributes.Builder().setUsage(USAGE_NOTIFICATION_RINGTONE).build());
+        NotificationRecord r = getCallRecord(1, ringtoneChannel, true);
         NotificationRecord s = getBeepyNotification();
 
         mService.setHints(NotificationListenerService.HINT_HOST_DISABLE_NOTIFICATION_EFFECTS
@@ -1340,7 +1383,11 @@
 
     @Test
     public void testListenerHintNotification_callSound() throws Exception {
-        NotificationRecord r = getCallRecord(1, true);
+        NotificationChannel ringtoneChannel =
+                new NotificationChannel("ringtone", "", IMPORTANCE_HIGH);
+        ringtoneChannel.setSound(Settings.System.DEFAULT_RINGTONE_URI,
+                new AudioAttributes.Builder().setUsage(USAGE_NOTIFICATION_RINGTONE).build());
+        NotificationRecord r = getCallRecord(1, ringtoneChannel, true);
 
         mService.setHints(NotificationListenerService.HINT_HOST_DISABLE_NOTIFICATION_EFFECTS);
 
@@ -1349,6 +1396,167 @@
         verifyBeepLooped();
     }
 
+    @Test
+    public void testCannotInterruptRingtoneInsistentBeep() throws Exception {
+        NotificationChannel ringtoneChannel =
+                new NotificationChannel("ringtone", "", IMPORTANCE_HIGH);
+        ringtoneChannel.setSound(Settings.System.DEFAULT_RINGTONE_URI,
+                new AudioAttributes.Builder().setUsage(USAGE_NOTIFICATION_RINGTONE).build());
+        NotificationRecord ringtoneNotification = getCallRecord(1, ringtoneChannel, true);
+        mService.addNotification(ringtoneNotification);
+
+        mService.buzzBeepBlinkLocked(ringtoneNotification);
+        verifyBeepLooped();
+
+        NotificationRecord interrupter = getBeepyOtherNotification();
+        assertTrue(mService.shouldMuteNotificationLocked(interrupter));
+        mService.buzzBeepBlinkLocked(interrupter);
+
+        verifyBeep(1);
+
+        assertFalse(interrupter.isInterruptive());
+        assertEquals(-1, interrupter.getLastAudiblyAlertedMs());
+    }
+
+    @Test
+    public void testCannotInterruptRingtoneInsistentBuzz() {
+        NotificationChannel ringtoneChannel =
+                new NotificationChannel("ringtone", "", IMPORTANCE_HIGH);
+        ringtoneChannel.setSound(Uri.EMPTY,
+                new AudioAttributes.Builder().setUsage(USAGE_NOTIFICATION_RINGTONE).build());
+        ringtoneChannel.enableVibration(true);
+        NotificationRecord ringtoneNotification = getCallRecord(1, ringtoneChannel, true);
+        assertFalse(mService.shouldMuteNotificationLocked(ringtoneNotification));
+
+        mService.buzzBeepBlinkLocked(ringtoneNotification);
+        verifyVibrateLooped();
+
+        NotificationRecord interrupter = getBuzzyOtherNotification();
+        assertTrue(mService.shouldMuteNotificationLocked(interrupter));
+        mService.buzzBeepBlinkLocked(interrupter);
+
+        verifyVibrate(1);
+
+        assertFalse(interrupter.isInterruptive());
+        assertEquals(-1, interrupter.getLastAudiblyAlertedMs());
+    }
+
+    @Test
+    public void testCanInterruptRingtoneNonInsistentBeep() throws Exception {
+        NotificationChannel ringtoneChannel =
+                new NotificationChannel("ringtone", "", IMPORTANCE_HIGH);
+        ringtoneChannel.setSound(Settings.System.DEFAULT_RINGTONE_URI,
+                new AudioAttributes.Builder().setUsage(USAGE_NOTIFICATION_RINGTONE).build());
+        NotificationRecord ringtoneNotification = getCallRecord(1, ringtoneChannel, false);
+
+        mService.buzzBeepBlinkLocked(ringtoneNotification);
+        verifyBeepUnlooped();
+
+        NotificationRecord interrupter = getBeepyOtherNotification();
+        mService.buzzBeepBlinkLocked(interrupter);
+
+        verifyBeep(2);
+
+        assertTrue(interrupter.isInterruptive());
+    }
+
+    @Test
+    public void testCanInterruptRingtoneNonInsistentBuzz() {
+        NotificationChannel ringtoneChannel =
+                new NotificationChannel("ringtone", "", IMPORTANCE_HIGH);
+        ringtoneChannel.setSound(null,
+                new AudioAttributes.Builder().setUsage(USAGE_NOTIFICATION_RINGTONE).build());
+        ringtoneChannel.enableVibration(true);
+        NotificationRecord ringtoneNotification = getCallRecord(1, ringtoneChannel, false);
+
+        mService.buzzBeepBlinkLocked(ringtoneNotification);
+        verifyVibrate();
+
+        NotificationRecord interrupter = getBuzzyOtherNotification();
+        mService.buzzBeepBlinkLocked(interrupter);
+
+        verifyVibrate(2);
+
+        assertTrue(interrupter.isInterruptive());
+    }
+
+    @Test
+    public void testRingtoneInsistentBeep_doesNotBlockFutureSoundsOnceStopped() throws Exception {
+        NotificationChannel ringtoneChannel =
+                new NotificationChannel("ringtone", "", IMPORTANCE_HIGH);
+        ringtoneChannel.setSound(Settings.System.DEFAULT_RINGTONE_URI,
+                new AudioAttributes.Builder().setUsage(USAGE_NOTIFICATION_RINGTONE).build());
+        NotificationRecord ringtoneNotification = getCallRecord(1, ringtoneChannel, true);
+
+        mService.buzzBeepBlinkLocked(ringtoneNotification);
+        verifyBeepLooped();
+
+        mService.clearSoundLocked();
+
+        NotificationRecord interrupter = getBeepyOtherNotification();
+        mService.buzzBeepBlinkLocked(interrupter);
+
+        verifyBeep(2);
+
+        assertTrue(interrupter.isInterruptive());
+    }
+
+    @Test
+    public void testRingtoneInsistentBuzz_doesNotBlockFutureSoundsOnceStopped() {
+        NotificationChannel ringtoneChannel =
+                new NotificationChannel("ringtone", "", IMPORTANCE_HIGH);
+        ringtoneChannel.setSound(null,
+                new AudioAttributes.Builder().setUsage(USAGE_NOTIFICATION_RINGTONE).build());
+        ringtoneChannel.enableVibration(true);
+        NotificationRecord ringtoneNotification = getCallRecord(1, ringtoneChannel, true);
+
+        mService.buzzBeepBlinkLocked(ringtoneNotification);
+        verifyVibrateLooped();
+
+        mService.clearVibrateLocked();
+
+        NotificationRecord interrupter = getBuzzyOtherNotification();
+        mService.buzzBeepBlinkLocked(interrupter);
+
+        verifyVibrate(2);
+
+        assertTrue(interrupter.isInterruptive());
+    }
+
+    @Test
+    public void testCanInterruptNonRingtoneInsistentBeep() throws Exception {
+        NotificationChannel fakeRingtoneChannel =
+                new NotificationChannel("ringtone", "", IMPORTANCE_HIGH);
+        NotificationRecord ringtoneNotification = getCallRecord(1, fakeRingtoneChannel, true);
+
+        mService.buzzBeepBlinkLocked(ringtoneNotification);
+        verifyBeepLooped();
+
+        NotificationRecord interrupter = getBeepyOtherNotification();
+        mService.buzzBeepBlinkLocked(interrupter);
+
+        verifyBeep(2);
+
+        assertTrue(interrupter.isInterruptive());
+    }
+
+    @Test
+    public void testCanInterruptNonRingtoneInsistentBuzz() throws Exception {
+        NotificationChannel fakeRingtoneChannel =
+                new NotificationChannel("ringtone", "", IMPORTANCE_HIGH);
+        fakeRingtoneChannel.enableVibration(true);
+        NotificationRecord ringtoneNotification = getCallRecord(1, fakeRingtoneChannel, true);
+
+        mService.buzzBeepBlinkLocked(ringtoneNotification);
+
+        NotificationRecord interrupter = getBuzzyOtherNotification();
+        mService.buzzBeepBlinkLocked(interrupter);
+
+        verifyVibrate(2);
+
+        assertTrue(interrupter.isInterruptive());
+    }
+
     static class VibrateRepeatMatcher implements ArgumentMatcher<VibrationEffect> {
         private final int mRepeatIndex;
 
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 4ea2fc0..cd0f4f1 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -537,6 +537,18 @@
         return new NotificationRecord(mContext, sbn, channel);
     }
 
+    private NotificationRecord generateNotificationRecord(NotificationChannel channel, int userId) {
+        if (channel == null) {
+            channel = mTestNotificationChannel;
+        }
+        Notification.Builder nb = new Notification.Builder(mContext, channel.getId())
+                .setContentTitle("foo")
+                .setSmallIcon(android.R.drawable.sym_def_app_icon);
+        StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, "tag", mUid, 0,
+                nb.build(), new UserHandle(userId), null, 0);
+        return new NotificationRecord(mContext, sbn, channel);
+    }
+
     private Map<String, Answer> getSignalExtractorSideEffects() {
         Map<String, Answer> answers = new ArrayMap<>();
 
@@ -5451,6 +5463,112 @@
     }
 
     @Test
+    public void testGrantInlineReplyUriPermission_recordExists() throws Exception {
+        NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel, 0);
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
+                nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
+        waitForIdle();
+
+        // A notification exists for the given record
+        StatusBarNotification[] notifsBefore = mBinderService.getActiveNotifications(PKG);
+        assertEquals(1, notifsBefore.length);
+
+        reset(mPackageManager);
+
+        Uri uri = ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, 1);
+
+        mService.mNotificationDelegate.grantInlineReplyUriPermission(
+                nr.getKey(), uri, nr.sbn.getUid());
+
+        // Grant permission called for the UID of SystemUI under the target user ID
+        verify(mUgm, times(1)).grantUriPermissionFromOwner(any(),
+                eq(nr.sbn.getUid()), eq(nr.sbn.getPackageName()), eq(uri), anyInt(), anyInt(),
+                eq(nr.sbn.getUserId()));
+    }
+
+    @Test
+    public void testGrantInlineReplyUriPermission_userAll() throws Exception {
+        // generate a NotificationRecord for USER_ALL to make sure it's converted into USER_SYSTEM
+        NotificationRecord nr =
+                generateNotificationRecord(mTestNotificationChannel, UserHandle.USER_ALL);
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
+                nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
+        waitForIdle();
+
+        // A notification exists for the given record
+        StatusBarNotification[] notifsBefore = mBinderService.getActiveNotifications(PKG);
+        assertEquals(1, notifsBefore.length);
+
+        reset(mPackageManager);
+
+        Uri uri = ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, 1);
+
+        mService.mNotificationDelegate.grantInlineReplyUriPermission(
+                nr.getKey(), uri, nr.sbn.getUid());
+
+        // Target user for the grant is USER_ALL instead of USER_SYSTEM
+        verify(mUgm, times(1)).grantUriPermissionFromOwner(any(),
+                eq(nr.sbn.getUid()), eq(nr.sbn.getPackageName()), eq(uri), anyInt(), anyInt(),
+                eq(UserHandle.USER_SYSTEM));
+    }
+
+    @Test
+    public void testGrantInlineReplyUriPermission_acrossUsers() throws Exception {
+        // generate a NotificationRecord for USER_ALL to make sure it's converted into USER_SYSTEM
+        int otherUserId = 11;
+        NotificationRecord nr =
+                generateNotificationRecord(mTestNotificationChannel, otherUserId);
+        mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag",
+                nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId());
+        waitForIdle();
+
+        // A notification exists for the given record
+        StatusBarNotification[] notifsBefore = mBinderService.getActiveNotifications(PKG);
+        assertEquals(1, notifsBefore.length);
+
+        reset(mPackageManager);
+
+        Uri uri = ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, 1);
+
+        int uid = 0; // sysui on primary user
+        int otherUserUid = (otherUserId * 100000) + 1; // SystemUI as a different user
+        String sysuiPackage = "sysui";
+        final String[] sysuiPackages = new String[] { sysuiPackage };
+        when(mPackageManager.getPackagesForUid(uid)).thenReturn(sysuiPackages);
+
+        // Make sure to mock call for USER_SYSTEM and not USER_ALL, since it's been replaced by the
+        // time this is called
+        when(mPackageManager.getPackageUid(sysuiPackage, 0, otherUserId))
+                .thenReturn(otherUserUid);
+
+        mService.mNotificationDelegate.grantInlineReplyUriPermission(nr.getKey(), uri, uid);
+
+        // Target user for the grant is USER_ALL instead of USER_SYSTEM
+        verify(mUgm, times(1)).grantUriPermissionFromOwner(any(),
+                eq(otherUserUid), eq(nr.sbn.getPackageName()), eq(uri), anyInt(), anyInt(),
+                eq(otherUserId));
+    }
+
+    @Test
+    public void testGrantInlineReplyUriPermission_noRecordExists() throws Exception {
+        NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel);
+        waitForIdle();
+
+        // No notifications exist for the given record
+        StatusBarNotification[] notifsBefore = mBinderService.getActiveNotifications(PKG);
+        assertEquals(0, notifsBefore.length);
+
+        Uri uri = ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, 1);
+        int uid = 0; // sysui on primary user
+
+        mService.mNotificationDelegate.grantInlineReplyUriPermission(nr.getKey(), uri, uid);
+
+        // Grant permission not called if no record exists for the given key
+        verify(mUgm, times(0)).grantUriPermissionFromOwner(any(), anyInt(), any(),
+                eq(uri), anyInt(), anyInt(), anyInt());
+    }
+
+    @Test
     public void testNotificationBubbles_disabled_lowRamDevice() throws Exception {
         // Bubbles are allowed!
         setUpPrefsForBubbles(true /* global */, true /* app */, true /* channel */);
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
index aaaa7a5..2836e69 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java
@@ -28,6 +28,7 @@
 import static com.google.common.truth.Truth.assertWithMessage;
 
 import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.argThat;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.timeout;
@@ -128,7 +129,7 @@
 
         mActivityMetricsLogger.notifyActivityLaunching(intent);
 
-        verifyAsync(mLaunchObserver).onIntentStarted(eq(intent));
+        verifyAsync(mLaunchObserver).onIntentStarted(eq(intent), anyLong());
         verifyNoMoreInteractions(mLaunchObserver);
     }
 
@@ -163,12 +164,12 @@
        testOnActivityLaunched();
 
        mActivityMetricsLogger.notifyTransitionStarting(new SparseIntArray(),
-               SystemClock.uptimeMillis());
+               SystemClock.elapsedRealtimeNanos());
 
        mActivityMetricsLogger.notifyWindowsDrawn(mActivityRecord.getWindowingMode(),
-               SystemClock.uptimeMillis());
+               SystemClock.elapsedRealtimeNanos());
 
-       verifyAsync(mLaunchObserver).onActivityLaunchFinished(eqProto(mActivityRecord));
+       verifyAsync(mLaunchObserver).onActivityLaunchFinished(eqProto(mActivityRecord), anyLong());
        verifyNoMoreInteractions(mLaunchObserver);
     }
 
@@ -186,6 +187,16 @@
     }
 
     @Test
+    public void testOnReportFullyDrawn() throws Exception {
+        testOnActivityLaunched();
+
+        mActivityMetricsLogger.logAppTransitionReportedDrawn(mActivityRecord, false);
+
+        verifyAsync(mLaunchObserver).onReportFullyDrawn(eqProto(mActivityRecord), anyLong());
+        verifyNoMoreInteractions(mLaunchObserver);
+    }
+
+    @Test
     public void testOnActivityLaunchedTrampoline() throws Exception {
         testOnIntentStarted();
 
@@ -206,12 +217,13 @@
        testOnActivityLaunchedTrampoline();
 
        mActivityMetricsLogger.notifyTransitionStarting(new SparseIntArray(),
-               SystemClock.uptimeMillis());
+               SystemClock.elapsedRealtimeNanos());
 
        mActivityMetricsLogger.notifyWindowsDrawn(mActivityRecordTrampoline.getWindowingMode(),
-               SystemClock.uptimeMillis());
+               SystemClock.elapsedRealtimeNanos());
 
-       verifyAsync(mLaunchObserver).onActivityLaunchFinished(eqProto(mActivityRecordTrampoline));
+       verifyAsync(mLaunchObserver).onActivityLaunchFinished(eqProto(mActivityRecordTrampoline),
+                                                             anyLong());
        verifyNoMoreInteractions(mLaunchObserver);
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java b/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java
index 09e5027..6a94137 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java
@@ -115,4 +115,8 @@
     @Override
     public void showInsets(int types, boolean fromIme) throws RemoteException {
     }
+
+    @Override
+    public void hideInsets(int types, boolean fromIme) throws RemoteException {
+    }
 }
diff --git a/startop/apps/test/Android.bp b/startop/apps/test/Android.bp
index d949f74..2ff26b8 100644
--- a/startop/apps/test/Android.bp
+++ b/startop/apps/test/Android.bp
@@ -17,11 +17,12 @@
 android_app {
     name: "startop_test_app",
     srcs: [
+        "src/ComplexLayoutInflationActivity.java",
         "src/CPUIntensive.java",
         "src/EmptyActivity.java",
-        "src/LayoutInflationActivity.java",
-        "src/ComplexLayoutInflationActivity.java",
         "src/FrameLayoutInflationActivity.java",
+        "src/LayoutInflationActivity.java",
+        "src/NonInteractiveSystemServerBenchmarkActivity.java",
         "src/SystemServerBenchmarkActivity.java",
         "src/SystemServerBenchmarks.java",
         "src/TextViewInflationActivity.java",
diff --git a/startop/apps/test/AndroidManifest.xml b/startop/apps/test/AndroidManifest.xml
index 15785d4..a5ac348 100644
--- a/startop/apps/test/AndroidManifest.xml
+++ b/startop/apps/test/AndroidManifest.xml
@@ -84,6 +84,13 @@
             </intent-filter>
         </activity>
 
+        <activity
+            android:label="Non-interactive SystemServer Benchmark"
+            android:name=".NonInteractiveSystemServerBenchmarkActivity"
+            android:exported="true" />
+
     </application>
 
+    <uses-permission android:name="android.permission.WAKE_LOCK" />
+
 </manifest>
diff --git a/startop/apps/test/README.md b/startop/apps/test/README.md
index dadc66a..949dff7 100644
--- a/startop/apps/test/README.md
+++ b/startop/apps/test/README.md
@@ -24,3 +24,14 @@
 inflation.
 
     adb shell am start -n com.android.startop.test/.ComplexLayoutInflationActivity
+
+## NonInteractiveSystemServerBenchmark
+
+This activity is for running microbenchmarks from the command line. Run as follows:
+
+   adb shell am start -W -n com.android.startop.test .NonInteractiveSystemServerBenchmarkActivity
+
+It takes awhile (and there's currently no automated way to make sure it's done),
+but when it finishes, you can get the results like this:
+
+    adb shell cat /sdcard/Android/data/com.android.startop.test/files/benchmark.csv
diff --git a/startop/apps/test/src/NonInteractiveSystemServerBenchmarkActivity.java b/startop/apps/test/src/NonInteractiveSystemServerBenchmarkActivity.java
new file mode 100644
index 0000000..a2dc2cf
--- /dev/null
+++ b/startop/apps/test/src/NonInteractiveSystemServerBenchmarkActivity.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.startop.test;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.PrintStream;
+import java.util.ArrayList;
+
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.GridLayout;
+import android.widget.TextView;
+
+public class NonInteractiveSystemServerBenchmarkActivity extends Activity {
+    ArrayList<CharSequence> benchmarkNames = new ArrayList();
+    ArrayList<Runnable> benchmarkThunks = new ArrayList();
+
+    PrintStream out;
+
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        SystemServerBenchmarks.initializeBenchmarks(this, (name, thunk) -> {
+            benchmarkNames.add(name);
+            benchmarkThunks.add(thunk);
+        });
+
+        try {
+            out = new PrintStream(new File(getExternalFilesDir(null), "benchmark.csv"));
+        } catch (FileNotFoundException e) {
+            throw new RuntimeException(e);
+        }
+        out.println("Name,Mean,Stdev");
+        runBenchmarks(0);
+    }
+
+    void runBenchmarks(int i) {
+        if (i < benchmarkNames.size()) {
+            SystemServerBenchmarks.runBenchmarkInBackground(benchmarkThunks.get(i),
+                    (mean, stdev) -> {
+                        out.printf("%s,%.0f,%.0f\n", benchmarkNames.get(i), mean, stdev);
+                        runBenchmarks(i + 1);
+                    });
+        }
+    }
+}
diff --git a/startop/apps/test/src/SystemServerBenchmarks.java b/startop/apps/test/src/SystemServerBenchmarks.java
index 818f178..126c2c8 100644
--- a/startop/apps/test/src/SystemServerBenchmarks.java
+++ b/startop/apps/test/src/SystemServerBenchmarks.java
@@ -18,6 +18,7 @@
 
 import android.app.Activity;
 import android.app.ActivityManager;
+import android.app.PendingIntent;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -25,6 +26,7 @@
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.os.AsyncTask;
+import android.os.PowerManager;
 
 /**
  * An interface for running benchmarks and collecting results. Used so we can have both an
@@ -48,7 +50,7 @@
     // Time limit to run benchmarks in seconds
     public static final int TIME_LIMIT = 5;
 
-    static void initializeBenchmarks(BenchmarkRunner benchmarks, Activity parent) {
+    static void initializeBenchmarks(Activity parent, BenchmarkRunner benchmarks) {
         benchmarks.addBenchmark("Empty", () -> {
         });
 
@@ -158,6 +160,21 @@
         benchmarks.addBenchmark("getRunningAppProcesses", () -> {
             am.getRunningAppProcesses();
         });
+
+        // We use PendingIntent.getCreatorPackage, since
+        // getPackageIntentForSender is not public to us, but getCreatorPackage
+        // is just a thin wrapper around it.
+        PendingIntent pi = PendingIntent.getActivity(parent, 0, new Intent(), 0);
+        benchmarks.addBenchmark("getPackageIntentForSender", () -> {
+            pi.getCreatorPackage();
+        });
+
+        PowerManager pwr = (PowerManager) parent.getSystemService(Context.POWER_SERVICE);
+        PowerManager.WakeLock wl = pwr.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "benchmark tag");
+        benchmarks.addBenchmark("WakeLock Acquire/Release", () -> {
+            wl.acquire();
+            wl.release();
+        });
     }
 
     /**
diff --git a/startop/iorap/src/com/google/android/startop/iorap/AppLaunchEvent.java b/startop/iorap/src/com/google/android/startop/iorap/AppLaunchEvent.java
index acf9946..cf120cf 100644
--- a/startop/iorap/src/com/google/android/startop/iorap/AppLaunchEvent.java
+++ b/startop/iorap/src/com/google/android/startop/iorap/AppLaunchEvent.java
@@ -86,10 +86,14 @@
     public static final class IntentStarted extends AppLaunchEvent {
         @NonNull
         public final Intent intent;
+        public final long timestampNs;
 
-        public IntentStarted(@SequenceId long sequenceId, Intent intent) {
+        public IntentStarted(@SequenceId long sequenceId,
+                             Intent intent,
+                             long timestampNs) {
             super(sequenceId);
             this.intent = intent;
+            this.timestampNs = timestampNs;
 
             Objects.requireNonNull(intent, "intent");
         }
@@ -98,14 +102,16 @@
         public boolean equals(Object other) {
             if (other instanceof IntentStarted) {
                 return intent.equals(((IntentStarted)other).intent) &&
-                        super.equals(other);
+                       timestampNs == ((IntentStarted)other).timestampNs &&
+                       super.equals(other);
             }
             return false;
         }
 
         @Override
         protected String toStringBody() {
-            return ", intent=" + intent.toString();
+            return ", intent=" + intent.toString() +
+                   " , timestampNs=" + Long.toString(timestampNs);
         }
 
 
@@ -113,11 +119,13 @@
         protected void writeToParcelImpl(Parcel p, int flags) {
             super.writeToParcelImpl(p, flags);
             IntentProtoParcelable.write(p, intent, flags);
+            p.writeLong(timestampNs);
         }
 
         IntentStarted(Parcel p) {
             super(p);
             intent = IntentProtoParcelable.create(p);
+            timestampNs = p.readLong();
         }
     }
 
@@ -216,18 +224,39 @@
     }
 
     public static final class ActivityLaunchFinished extends BaseWithActivityRecordData {
+        public final long timestampNs;
+
         public ActivityLaunchFinished(@SequenceId long sequenceId,
-                @NonNull @ActivityRecordProto byte[] snapshot) {
+                @NonNull @ActivityRecordProto byte[] snapshot,
+                long timestampNs) {
             super(sequenceId, snapshot);
+            this.timestampNs = timestampNs;
         }
 
         @Override
         public boolean equals(Object other) {
             if (other instanceof ActivityLaunched) {
-                return super.equals(other);
+                return timestampNs == ((ActivityLaunchFinished)other).timestampNs &&
+                       super.equals(other);
             }
             return false;
         }
+
+        @Override
+        protected String toStringBody() {
+            return ", timestampNs=" + Long.toString(timestampNs);
+        }
+
+        @Override
+        protected void writeToParcelImpl(Parcel p, int flags) {
+           super.writeToParcelImpl(p, flags);
+           p.writeLong(timestampNs);
+        }
+
+        ActivityLaunchFinished(Parcel p) {
+            super(p);
+            timestampNs = p.readLong();
+        }
     }
 
      public static class ActivityLaunchCancelled extends AppLaunchEvent {
@@ -275,6 +304,42 @@
         }
     }
 
+    public static final class ReportFullyDrawn extends BaseWithActivityRecordData {
+        public final long timestampNs;
+
+        public ReportFullyDrawn(@SequenceId long sequenceId,
+                @NonNull @ActivityRecordProto byte[] snapshot,
+                long timestampNs) {
+            super(sequenceId, snapshot);
+            this.timestampNs = timestampNs;
+        }
+
+        @Override
+        public boolean equals(Object other) {
+            if (other instanceof ReportFullyDrawn) {
+                return timestampNs == ((ReportFullyDrawn)other).timestampNs &&
+                        super.equals(other);
+            }
+            return false;
+        }
+
+        @Override
+        protected String toStringBody() {
+            return ", timestampNs=" + Long.toString(timestampNs);
+        }
+
+        @Override
+        protected void writeToParcelImpl(Parcel p, int flags) {
+           super.writeToParcelImpl(p, flags);
+           p.writeLong(timestampNs);
+        }
+
+        ReportFullyDrawn(Parcel p) {
+            super(p);
+            timestampNs = p.readLong();
+        }
+    }
+
     @Override
     public @ContentsFlags int describeContents() { return 0; }
 
@@ -348,6 +413,7 @@
             ActivityLaunched.class,
             ActivityLaunchFinished.class,
             ActivityLaunchCancelled.class,
+            ReportFullyDrawn.class,
     };
 
     public static class ActivityRecordProtoParcelable {
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 902da4c..f753548 100644
--- a/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java
+++ b/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java
@@ -315,19 +315,19 @@
         // All callbacks occur on the same background thread. Don't synchronize explicitly.
 
         @Override
-        public void onIntentStarted(@NonNull Intent intent) {
+        public void onIntentStarted(@NonNull Intent intent, long timestampNs) {
             // #onIntentStarted [is the only transition that] initiates a new launch sequence.
             ++mSequenceId;
 
             if (DEBUG) {
-                Log.v(TAG, String.format("AppLaunchObserver#onIntentStarted(%d, %s)",
-                        mSequenceId, intent));
+                Log.v(TAG, String.format("AppLaunchObserver#onIntentStarted(%d, %s, %d)",
+                        mSequenceId, intent, timestampNs));
             }
 
             invokeRemote(mIorapRemote,
                 (IIorap remote) ->
                     remote.onAppLaunchEvent(RequestId.nextValueForSequence(),
-                        new AppLaunchEvent.IntentStarted(mSequenceId, intent))
+                        new AppLaunchEvent.IntentStarted(mSequenceId, intent, timestampNs))
             );
         }
 
@@ -374,16 +374,34 @@
         }
 
         @Override
-        public void onActivityLaunchFinished(@NonNull @ActivityRecordProto byte[] activity) {
+        public void onActivityLaunchFinished(@NonNull @ActivityRecordProto byte[] activity,
+            long timestampNs) {
             if (DEBUG) {
-                Log.v(TAG, String.format("AppLaunchObserver#onActivityLaunchFinished(%d, %s)",
-                        mSequenceId, activity));
+                Log.v(TAG, String.format("AppLaunchObserver#onActivityLaunchFinished(%d, %s, %d)",
+                        mSequenceId, activity, timestampNs));
             }
 
             invokeRemote(mIorapRemote,
                 (IIorap remote) ->
                     remote.onAppLaunchEvent(RequestId.nextValueForSequence(),
-                        new AppLaunchEvent.ActivityLaunchFinished(mSequenceId, activity))
+                        new AppLaunchEvent.ActivityLaunchFinished(mSequenceId,
+                            activity,
+                            timestampNs))
+            );
+        }
+
+        @Override
+        public void onReportFullyDrawn(@NonNull @ActivityRecordProto byte[] activity,
+            long timestampNs) {
+            if (DEBUG) {
+                Log.v(TAG, String.format("AppLaunchObserver#onReportFullyDrawn(%d, %s, %d)",
+                        mSequenceId, activity, timestampNs));
+            }
+
+            invokeRemote(mIorapRemote,
+                (IIorap remote) ->
+                    remote.onAppLaunchEvent(RequestId.nextValueForSequence(),
+                        new AppLaunchEvent.ReportFullyDrawn(mSequenceId, activity, timestampNs))
             );
         }
     }
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 1d5c18d..9e60afc 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -2817,7 +2817,7 @@
             "ping_test_before_data_switch_bool";
 
     /**
-     * Controls time in milli seconds until DcTracker reevaluates 5G connection state.
+     * Controls time in milliseconds until DcTracker reevaluates 5G connection state.
      * @hide
      */
     public static final String KEY_5G_WATCHDOG_TIME_MS_LONG =
@@ -3199,6 +3199,13 @@
     public static final String KEY_CARRIER_CERTIFICATE_STRING_ARRAY =
             "carrier_certificate_string_array";
 
+    /**
+     * DisconnectCause array to play busy tone. Value should be array of
+     * {@link android.telephony.DisconnectCause}.
+     */
+    public static final String KEY_DISCONNECT_CAUSE_PLAY_BUSYTONE_INT_ARRAY =
+            "disconnect_cause_play_busytone_int_array";
+
     /** The default value for every variable. */
     private final static PersistableBundle sDefaults;
 
@@ -3628,6 +3635,8 @@
         sDefaults.putBoolean(KEY_SUPPORT_WPS_OVER_IMS_BOOL, true);
         sDefaults.putAll(Ims.getDefaults());
         sDefaults.putStringArray(KEY_CARRIER_CERTIFICATE_STRING_ARRAY, null);
+        sDefaults.putIntArray(KEY_DISCONNECT_CAUSE_PLAY_BUSYTONE_INT_ARRAY,
+                new int[] {4 /* BUSY */});
     }
 
     /**
diff --git a/telephony/java/android/telephony/CellBroadcastService.java b/telephony/java/android/telephony/CellBroadcastService.java
index d5e447e..46eb9df 100644
--- a/telephony/java/android/telephony/CellBroadcastService.java
+++ b/telephony/java/android/telephony/CellBroadcastService.java
@@ -21,6 +21,7 @@
 import android.app.Service;
 import android.content.Intent;
 import android.os.IBinder;
+import android.telephony.cdma.CdmaSmsCbProgramData;
 
 /**
  * A service which exposes the cell broadcast handling module to the system.
@@ -69,9 +70,11 @@
     /**
      * Handle a CDMA cell broadcast SMS message forwarded from the system.
      * @param slotIndex the index of the slot which received the message
-     * @param message the SMS PDU
+     * @param bearerData the CDMA SMS bearer data
+     * @param serviceCategory the CDMA SCPT service category
      */
-    public abstract void onCdmaCellBroadcastSms(int slotIndex, byte[] message);
+    public abstract void onCdmaCellBroadcastSms(int slotIndex, byte[] bearerData,
+            @CdmaSmsCbProgramData.Category int serviceCategory);
 
     /**
      * If overriding this method, call through to the super method for any unknown actions.
@@ -102,11 +105,14 @@
         /**
          * Handle a CDMA cell broadcast SMS.
          * @param slotIndex the index of the slot which received the broadcast
-         * @param message the SMS message PDU
+         * @param bearerData the CDMA SMS bearer data
+         * @param serviceCategory the CDMA SCPT service category
          */
         @Override
-        public void handleCdmaCellBroadcastSms(int slotIndex, byte[] message) {
-            CellBroadcastService.this.onCdmaCellBroadcastSms(slotIndex, message);
+        public void handleCdmaCellBroadcastSms(int slotIndex, byte[] bearerData,
+                int serviceCategory) {
+            CellBroadcastService.this.onCdmaCellBroadcastSms(slotIndex, bearerData,
+                    serviceCategory);
         }
     }
 }
diff --git a/telephony/java/android/telephony/CellSignalStrengthWcdma.java b/telephony/java/android/telephony/CellSignalStrengthWcdma.java
index cdf01da..4dc54f0 100644
--- a/telephony/java/android/telephony/CellSignalStrengthWcdma.java
+++ b/telephony/java/android/telephony/CellSignalStrengthWcdma.java
@@ -245,9 +245,12 @@
     }
 
     /**
-     * Get the Ec/No as dB
+     * Get the Ec/No (Energy per chip over the noise spectral density) as dB.
      *
-     * @hide
+     * Reference: TS 25.133 Section 9.1.2.3
+     *
+     * @return the Ec/No of the measured cell in the range [-24, 1] or
+     * {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable
      */
     public int getEcNo() {
         return mEcNo;
diff --git a/telephony/java/android/telephony/ICellBroadcastService.aidl b/telephony/java/android/telephony/ICellBroadcastService.aidl
index eff64a2..bcd6cc5 100644
--- a/telephony/java/android/telephony/ICellBroadcastService.aidl
+++ b/telephony/java/android/telephony/ICellBroadcastService.aidl
@@ -28,5 +28,5 @@
     oneway void handleGsmCellBroadcastSms(int slotId, in byte[] message);
 
     /** @see android.telephony.CellBroadcastService#onCdmaCellBroadcastSms */
-    oneway void handleCdmaCellBroadcastSms(int slotId, in byte[] message);
+    oneway void handleCdmaCellBroadcastSms(int slotId, in byte[] bearerData, int serviceCategory);
 }
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index e288f25..40d057f 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -53,7 +53,6 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager;
-import android.telephony.Annotation.NetworkType;
 import android.telephony.euicc.EuiccManager;
 import android.telephony.ims.ImsMmTelManager;
 import android.util.DisplayMetrics;
@@ -2420,8 +2419,12 @@
      * @param plans the list of plans. The first plan is always the primary and
      *            most important plan. Any additional plans are secondary and
      *            may not be displayed or used by decision making logic.
+     *            The list of all plans must meet the requirements defined in
+     *            {@link SubscriptionPlan.Builder#setNetworkTypes(int[])}.
      * @throws SecurityException if the caller doesn't meet the requirements
      *             outlined above.
+     * @throws IllegalArgumentException if plans don't meet the requirements
+     *             mentioned above.
      */
     public void setSubscriptionPlans(int subId, @NonNull List<SubscriptionPlan> plans) {
         try {
@@ -2466,51 +2469,10 @@
      */
     public void setSubscriptionOverrideUnmetered(int subId, boolean overrideUnmetered,
             @DurationMillisLong long timeoutMillis) {
-        setSubscriptionOverrideUnmetered(subId, null, overrideUnmetered, timeoutMillis);
-    }
-
-    /**
-     * Temporarily override the billing relationship between a carrier and
-     * a specific subscriber to be considered unmetered for the given network
-     * types. This will be reflected to apps via
-     * {@link NetworkCapabilities#NET_CAPABILITY_NOT_METERED}.
-     * This method is only accessible to the following narrow set of apps:
-     * <ul>
-     * <li>The carrier app for this subscriberId, as determined by
-     * {@link TelephonyManager#hasCarrierPrivileges()}.
-     * <li>The carrier app explicitly delegated access through
-     * {@link CarrierConfigManager#KEY_CONFIG_PLANS_PACKAGE_OVERRIDE_STRING}.
-     * </ul>
-     *
-     * @param subId the subscriber this override applies to.
-     * @param networkTypes all network types to set an override for. A null
-     *            network type means to apply the override to all network types.
-     *            Any unspecified network types will default to metered.
-     * @param overrideUnmetered set if the billing relationship should be
-     *            considered unmetered.
-     * @param timeoutMillis the timeout after which the requested override will
-     *            be automatically cleared, or {@code 0} to leave in the
-     *            requested state until explicitly cleared, or the next reboot,
-     *            whichever happens first.
-     * @throws SecurityException if the caller doesn't meet the requirements
-     *            outlined above.
-     * {@hide}
-     */
-    public void setSubscriptionOverrideUnmetered(int subId,
-            @Nullable @NetworkType int[] networkTypes, boolean overrideUnmetered,
-            @DurationMillisLong long timeoutMillis) {
         try {
-            long networkTypeMask = 0;
-            if (networkTypes != null) {
-                for (int networkType : networkTypes) {
-                    networkTypeMask |= TelephonyManager.getBitMaskForNetworkType(networkType);
-                }
-            } else {
-                networkTypeMask = ~0;
-            }
             final int overrideValue = overrideUnmetered ? OVERRIDE_UNMETERED : 0;
             getNetworkPolicy().setSubscriptionOverride(subId, OVERRIDE_UNMETERED, overrideValue,
-                    networkTypeMask, timeoutMillis, mContext.getOpPackageName());
+                    timeoutMillis, mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -2542,52 +2504,10 @@
      */
     public void setSubscriptionOverrideCongested(int subId, boolean overrideCongested,
             @DurationMillisLong long timeoutMillis) {
-        setSubscriptionOverrideCongested(subId, null, overrideCongested, timeoutMillis);
-    }
-
-    /**
-     * Temporarily override the billing relationship plan between a carrier and
-     * a specific subscriber to be considered congested. This will cause the
-     * device to delay certain network requests when possible, such as developer
-     * jobs that are willing to run in a flexible time window.
-     * <p>
-     * This method is only accessible to the following narrow set of apps:
-     * <ul>
-     * <li>The carrier app for this subscriberId, as determined by
-     * {@link TelephonyManager#hasCarrierPrivileges()}.
-     * <li>The carrier app explicitly delegated access through
-     * {@link CarrierConfigManager#KEY_CONFIG_PLANS_PACKAGE_OVERRIDE_STRING}.
-     * </ul>
-     *
-     * @param subId the subscriber this override applies to.
-     * @param networkTypes all network types to set an override for. A null
-     *            network type means to apply the override to all network types.
-     *            Any unspecified network types will default to not congested.
-     * @param overrideCongested set if the subscription should be considered
-     *            congested.
-     * @param timeoutMillis the timeout after which the requested override will
-     *            be automatically cleared, or {@code 0} to leave in the
-     *            requested state until explicitly cleared, or the next reboot,
-     *            whichever happens first.
-     * @throws SecurityException if the caller doesn't meet the requirements
-     *             outlined above.
-     * @hide
-     */
-    public void setSubscriptionOverrideCongested(int subId,
-            @Nullable @NetworkType int[] networkTypes, boolean overrideCongested,
-            @DurationMillisLong long timeoutMillis) {
         try {
-            long networkTypeMask = 0;
-            if (networkTypes != null) {
-                for (int networkType : networkTypes) {
-                    networkTypeMask |= TelephonyManager.getBitMaskForNetworkType(networkType);
-                }
-            } else {
-                networkTypeMask = ~0;
-            }
             final int overrideValue = overrideCongested ? OVERRIDE_CONGESTED : 0;
             getNetworkPolicy().setSubscriptionOverride(subId, OVERRIDE_CONGESTED, overrideValue,
-                    networkTypeMask, timeoutMillis, mContext.getOpPackageName());
+                    timeoutMillis, mContext.getOpPackageName());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/telephony/java/android/telephony/SubscriptionPlan.java b/telephony/java/android/telephony/SubscriptionPlan.java
index ec2050f..e24eb26 100644
--- a/telephony/java/android/telephony/SubscriptionPlan.java
+++ b/telephony/java/android/telephony/SubscriptionPlan.java
@@ -24,6 +24,7 @@
 import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.telephony.Annotation.NetworkType;
 import android.util.Range;
 import android.util.RecurrenceRule;
 
@@ -33,6 +34,7 @@
 import java.lang.annotation.RetentionPolicy;
 import java.time.Period;
 import java.time.ZonedDateTime;
+import java.util.Arrays;
 import java.util.Iterator;
 import java.util.Objects;
 
@@ -80,6 +82,8 @@
     private int dataLimitBehavior = LIMIT_BEHAVIOR_UNKNOWN;
     private long dataUsageBytes = BYTES_UNKNOWN;
     private long dataUsageTime = TIME_UNKNOWN;
+    private @NetworkType int[] networkTypes;
+    private long networkTypesBitMask;
 
     private SubscriptionPlan(RecurrenceRule cycleRule) {
         this.cycleRule = Preconditions.checkNotNull(cycleRule);
@@ -93,6 +97,7 @@
         dataLimitBehavior = source.readInt();
         dataUsageBytes = source.readLong();
         dataUsageTime = source.readLong();
+        networkTypes = source.createIntArray();
     }
 
     @Override
@@ -109,6 +114,7 @@
         dest.writeInt(dataLimitBehavior);
         dest.writeLong(dataUsageBytes);
         dest.writeLong(dataUsageTime);
+        dest.writeIntArray(networkTypes);
     }
 
     @Override
@@ -121,13 +127,14 @@
                 .append(" dataLimitBehavior=").append(dataLimitBehavior)
                 .append(" dataUsageBytes=").append(dataUsageBytes)
                 .append(" dataUsageTime=").append(dataUsageTime)
+                .append(" networkTypes=").append(Arrays.toString(networkTypes))
                 .append("}").toString();
     }
 
     @Override
     public int hashCode() {
         return Objects.hash(cycleRule, title, summary, dataLimitBytes, dataLimitBehavior,
-                dataUsageBytes, dataUsageTime);
+                dataUsageBytes, dataUsageTime, Arrays.hashCode(networkTypes));
     }
 
     @Override
@@ -140,7 +147,8 @@
                     && dataLimitBytes == other.dataLimitBytes
                     && dataLimitBehavior == other.dataLimitBehavior
                     && dataUsageBytes == other.dataUsageBytes
-                    && dataUsageTime == other.dataUsageTime;
+                    && dataUsageTime == other.dataUsageTime
+                    && Arrays.equals(networkTypes, other.networkTypes);
         }
         return false;
     }
@@ -204,6 +212,32 @@
     }
 
     /**
+     * Return an array containing all {@link NetworkType}s this SubscriptionPlan applies to.
+     * A null array means this SubscriptionPlan applies to all network types.
+     */
+    public @Nullable @NetworkType int[] getNetworkTypes() {
+        return networkTypes;
+    }
+
+    /**
+     * Return the networkTypes array converted to a {@link TelephonyManager.NetworkTypeBitMask}
+     * @hide
+     */
+    public long getNetworkTypesBitMask() {
+        // calculate bitmask the first time and save for future calls
+        if (networkTypesBitMask == 0) {
+            if (networkTypes == null) {
+                networkTypesBitMask = ~0;
+            } else {
+                for (int networkType : networkTypes) {
+                    networkTypesBitMask |= TelephonyManager.getBitMaskForNetworkType(networkType);
+                }
+            }
+        }
+        return networkTypesBitMask;
+    }
+
+    /**
      * Return an iterator that will return all valid data usage cycles based on
      * any recurrence rules. The iterator starts from the currently active cycle
      * and walks backwards through time.
@@ -335,5 +369,24 @@
             plan.dataUsageTime = dataUsageTime;
             return this;
         }
+
+        /**
+         * Set the network types this SubscriptionPlan applies to.
+         * The developer must supply at least one plan that applies to all network types (default),
+         * and all additional plans may not include a particular network type more than once.
+         * Plan selection will prefer plans that have specific network types defined
+         * over plans that apply to all network types.
+         *
+         * @param networkTypes a set of all {@link NetworkType}s that apply to this plan.
+         *            A null value or empty array means the plan applies to all network types.
+         */
+        public @NonNull Builder setNetworkTypes(@Nullable @NetworkType int[] networkTypes) {
+            if (networkTypes == null || networkTypes.length == 0) {
+                plan.networkTypes = null;
+            } else {
+                plan.networkTypes = networkTypes;
+            }
+            return this;
+        }
     }
 }
diff --git a/telephony/java/android/telephony/ims/ImsReasonInfo.java b/telephony/java/android/telephony/ims/ImsReasonInfo.java
index 1e0d9a78..10251d7 100644
--- a/telephony/java/android/telephony/ims/ImsReasonInfo.java
+++ b/telephony/java/android/telephony/ims/ImsReasonInfo.java
@@ -885,6 +885,12 @@
      */
     public static final int CODE_WFC_SERVICE_NOT_AVAILABLE_IN_THIS_LOCATION = 1623;
 
+    /**
+     * The dialed RTT call should be retried without RTT
+     * @hide
+     */
+    public static final int CODE_RETRY_ON_IMS_WITHOUT_RTT = 3001;
+
     /*
      * OEM specific error codes. To be used by OEMs when they don't want to reveal error code which
      * would be replaced by ERROR_UNSPECIFIED.
@@ -1065,6 +1071,7 @@
             CODE_REJECT_VT_AVPF_NOT_ALLOWED,
             CODE_REJECT_ONGOING_ENCRYPTED_CALL,
             CODE_REJECT_ONGOING_CS_CALL,
+            CODE_RETRY_ON_IMS_WITHOUT_RTT,
             CODE_OEM_CAUSE_1,
             CODE_OEM_CAUSE_2,
             CODE_OEM_CAUSE_3,
diff --git a/telephony/java/com/android/internal/telephony/DctConstants.java b/telephony/java/com/android/internal/telephony/DctConstants.java
index 668a6af..9e786ce 100644
--- a/telephony/java/com/android/internal/telephony/DctConstants.java
+++ b/telephony/java/com/android/internal/telephony/DctConstants.java
@@ -111,7 +111,7 @@
     public static final int EVENT_DATA_SERVICE_BINDING_CHANGED = BASE + 49;
     public static final int EVENT_DEVICE_PROVISIONED_CHANGE = BASE + 50;
     public static final int EVENT_DATA_ENABLED_OVERRIDE_RULES_CHANGED = BASE + 51;
-    public static final int EVENT_5G_NETWORK_CHANGED = BASE + 52;
+    public static final int EVENT_SERVICE_STATE_CHANGED = BASE + 52;
     public static final int EVENT_5G_TIMER_HYSTERESIS = BASE + 53;
     public static final int EVENT_5G_TIMER_WATCHDOG = BASE + 54;
 
diff --git a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
index 4654437..b357fa4 100644
--- a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
+++ b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java
@@ -900,6 +900,20 @@
     }
 
     /**
+     * @return the bearer data byte array
+     */
+    public byte[] getEnvelopeBearerData() {
+        return mEnvelope.bearerData;
+    }
+
+    /**
+     * @return the 16-bit CDMA SCPT service category
+     */
+    public @CdmaSmsCbProgramData.Category int getEnvelopeServiceCategory() {
+        return mEnvelope.serviceCategory;
+    }
+
+    /**
      * {@inheritDoc}
      */
     @Override
diff --git a/telephony/java/com/android/internal/telephony/cdma/sms/SmsEnvelope.java b/telephony/java/com/android/internal/telephony/cdma/sms/SmsEnvelope.java
index 2787b24..c924ab3 100644
--- a/telephony/java/com/android/internal/telephony/cdma/sms/SmsEnvelope.java
+++ b/telephony/java/com/android/internal/telephony/cdma/sms/SmsEnvelope.java
@@ -57,17 +57,17 @@
 
     // CMAS alert service category assignments, see 3GPP2 C.R1001 table 9.3.3-1
     public static final int SERVICE_CATEGORY_CMAS_PRESIDENTIAL_LEVEL_ALERT  =
-            CdmaSmsCbProgramData.CATEGORY_CMAS_PRESIDENTIAL_LEVEL_ALERT;
+            CdmaSmsCbProgramData.CATEGORY_CMAS_PRESIDENTIAL_LEVEL_ALERT;  // = 4096
     public static final int SERVICE_CATEGORY_CMAS_EXTREME_THREAT            =
-            CdmaSmsCbProgramData.CATEGORY_CMAS_EXTREME_THREAT;
+            CdmaSmsCbProgramData.CATEGORY_CMAS_EXTREME_THREAT;            // = 4097
     public static final int SERVICE_CATEGORY_CMAS_SEVERE_THREAT             =
-            CdmaSmsCbProgramData.CATEGORY_CMAS_SEVERE_THREAT;
+            CdmaSmsCbProgramData.CATEGORY_CMAS_SEVERE_THREAT;             // = 4098
     public static final int SERVICE_CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY =
-            CdmaSmsCbProgramData.CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY;
+            CdmaSmsCbProgramData.CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY; // = 4099
     public static final int SERVICE_CATEGORY_CMAS_TEST_MESSAGE              =
-            CdmaSmsCbProgramData.CATEGORY_CMAS_TEST_MESSAGE;
+            CdmaSmsCbProgramData.CATEGORY_CMAS_TEST_MESSAGE;              // = 4100
     public static final int SERVICE_CATEGORY_CMAS_LAST_RESERVED_VALUE       =
-            CdmaSmsCbProgramData.CATEGORY_CMAS_LAST_RESERVED_VALUE;
+            CdmaSmsCbProgramData.CATEGORY_CMAS_LAST_RESERVED_VALUE;       // = 4351
 
     /**
      * Provides the type of a SMS message like point to point, broadcast or acknowledge
diff --git a/test-mock/src/android/test/mock/MockContext.java b/test-mock/src/android/test/mock/MockContext.java
index fcd4701..5053cee 100644
--- a/test-mock/src/android/test/mock/MockContext.java
+++ b/test-mock/src/android/test/mock/MockContext.java
@@ -758,7 +758,7 @@
 
     /** {@hide} */
     @Override
-    public Context createContextAsUser(UserHandle user) {
+    public Context createContextAsUser(UserHandle user, @CreatePackageOptions int flags) {
         throw new UnsupportedOperationException();
     }
 
diff --git a/tests/Codegen/runTest.sh b/tests/Codegen/runTest.sh
index 0e90dea..929f122 100755
--- a/tests/Codegen/runTest.sh
+++ b/tests/Codegen/runTest.sh
@@ -17,11 +17,13 @@
         header_and_eval codegen $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java && \
         header_and_eval codegen $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java && \
         header_and_eval codegen $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java && \
-        cd $ANDROID_BUILD_TOP &&
-        header_and_eval mmma -j16 frameworks/base/tests/Codegen && \
-        header_and_eval adb install -r -t "$(find $ANDROID_TARGET_OUT_TESTCASES -name 'CodegenTests.apk')" && \
-        # header_and_eval adb shell am set-debug-app -w com.android.codegentest && \
-        header_and_eval adb shell am instrument -w -e package com.android.codegentest com.android.codegentest/androidx.test.runner.AndroidJUnitRunner
+        (
+            cd $ANDROID_BUILD_TOP &&
+            header_and_eval mmma -j16 frameworks/base/tests/Codegen && \
+            header_and_eval adb install -r -t "$(find $ANDROID_TARGET_OUT_TESTCASES -name 'CodegenTests.apk')" && \
+            # header_and_eval adb shell am set-debug-app -w com.android.codegentest && \
+            header_and_eval adb shell am instrument -w -e package com.android.codegentest com.android.codegentest/androidx.test.runner.AndroidJUnitRunner
+        )
 
         exitCode=$?
 
diff --git a/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java b/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java
index 10eba6a..3515053 100644
--- a/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java
+++ b/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java
@@ -32,13 +32,17 @@
 
 
 
-    // Code below generated by codegen v1.0.7.
+    // Code below generated by codegen v1.0.8.
     //
     // DO NOT MODIFY!
     // CHECKSTYLE:OFF Generated code
     //
     // To regenerate run:
     // $ codegen $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
 
 
     @DataClass.Generated.Member
@@ -94,8 +98,8 @@
     };
 
     @DataClass.Generated(
-            time = 1570576455287L,
-            codegenVersion = "1.0.7",
+            time = 1570828332402L,
+            codegenVersion = "1.0.8",
             sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java",
             inputSignatures = "private  int mBaseData\nclass HierrarchicalDataClassBase extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genSetters=true)")
     @Deprecated
diff --git a/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java b/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java
index 1085a6a..c867409 100644
--- a/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java
+++ b/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java
@@ -46,13 +46,17 @@
 
 
 
-    // Code below generated by codegen v1.0.7.
+    // Code below generated by codegen v1.0.8.
     //
     // DO NOT MODIFY!
     // CHECKSTYLE:OFF Generated code
     //
     // To regenerate run:
     // $ codegen $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
 
 
     @DataClass.Generated.Member
@@ -116,8 +120,8 @@
     };
 
     @DataClass.Generated(
-            time = 1570576456245L,
-            codegenVersion = "1.0.7",
+            time = 1570828333399L,
+            codegenVersion = "1.0.8",
             sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java",
             inputSignatures = "private @android.annotation.NonNull java.lang.String mChildData\nclass HierrarchicalDataClassChild extends com.android.codegentest.HierrarchicalDataClassBase implements []\n@com.android.internal.util.DataClass(genParcelable=true, genConstructor=false, genSetters=true)")
     @Deprecated
diff --git a/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java b/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java
index 75ef963..8d097a0 100644
--- a/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java
+++ b/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java
@@ -16,6 +16,7 @@
 package com.android.codegentest;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.SparseArray;
@@ -46,15 +47,22 @@
     @NonNull SparseArray<SampleWithCustomBuilder> mSparseArray = null;
     @NonNull SparseIntArray mSparseIntArray = null;
 
+    @SuppressWarnings({"WeakerAccess"})
+    @Nullable Boolean mNullableBoolean = null;
 
 
-    // Code below generated by codegen v1.0.7.
+
+    // Code below generated by codegen v1.0.8.
     //
     // DO NOT MODIFY!
     // CHECKSTYLE:OFF Generated code
     //
     // To regenerate run:
     // $ codegen $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
 
 
     @DataClass.Generated.Member
@@ -65,7 +73,8 @@
             @NonNull Map<String,SampleWithCustomBuilder> map,
             @NonNull Map<String,String> stringMap,
             @NonNull SparseArray<SampleWithCustomBuilder> sparseArray,
-            @NonNull SparseIntArray sparseIntArray) {
+            @NonNull SparseIntArray sparseIntArray,
+            @SuppressWarnings({ "WeakerAccess" }) @Nullable Boolean nullableBoolean) {
         this.mStringArray = stringArray;
         AnnotationValidations.validate(
                 NonNull.class, null, mStringArray);
@@ -87,6 +96,7 @@
         this.mSparseIntArray = sparseIntArray;
         AnnotationValidations.validate(
                 NonNull.class, null, mSparseIntArray);
+        this.mNullableBoolean = nullableBoolean;
 
         // onConstructed(); // You can define this method to get a callback
     }
@@ -126,6 +136,11 @@
         return mSparseIntArray;
     }
 
+    @DataClass.Generated.Member
+    public @SuppressWarnings({ "WeakerAccess" }) @Nullable Boolean getNullableBoolean() {
+        return mNullableBoolean;
+    }
+
     @Override
     @DataClass.Generated.Member
     public String toString() {
@@ -139,7 +154,8 @@
                 "map = " + mMap + ", " +
                 "stringMap = " + mStringMap + ", " +
                 "sparseArray = " + mSparseArray + ", " +
-                "sparseIntArray = " + mSparseIntArray +
+                "sparseIntArray = " + mSparseIntArray + ", " +
+                "nullableBoolean = " + mNullableBoolean +
         " }";
     }
 
@@ -149,6 +165,9 @@
         // You can override field parcelling by defining methods like:
         // void parcelFieldName(Parcel dest, int flags) { ... }
 
+        int flg = 0;
+        if (mNullableBoolean != null) flg |= 0x80;
+        dest.writeInt(flg);
         dest.writeStringArray(mStringArray);
         dest.writeIntArray(mIntArray);
         dest.writeStringList(mStringList);
@@ -156,6 +175,7 @@
         dest.writeMap(mStringMap);
         dest.writeSparseArray(mSparseArray);
         dest.writeSparseIntArray(mSparseIntArray);
+        if (mNullableBoolean != null) dest.writeBoolean(mNullableBoolean);
     }
 
     @Override
@@ -169,6 +189,7 @@
         // You can override field unparcelling by defining methods like:
         // static FieldType unparcelFieldName(Parcel in) { ... }
 
+        int flg = in.readInt();
         String[] stringArray = in.createStringArray();
         int[] intArray = in.createIntArray();
         List<String> stringList = new java.util.ArrayList<>();
@@ -179,6 +200,7 @@
         in.readMap(stringMap, String.class.getClassLoader());
         SparseArray<SampleWithCustomBuilder> sparseArray = (SparseArray) in.readSparseArray(SampleWithCustomBuilder.class.getClassLoader());
         SparseIntArray sparseIntArray = (SparseIntArray) in.readSparseIntArray();
+        Boolean nullableBoolean = (flg & 0x80) == 0 ? null : (Boolean) in.readBoolean();
 
         this.mStringArray = stringArray;
         AnnotationValidations.validate(
@@ -201,6 +223,7 @@
         this.mSparseIntArray = sparseIntArray;
         AnnotationValidations.validate(
                 NonNull.class, null, mSparseIntArray);
+        this.mNullableBoolean = nullableBoolean;
 
         // onConstructed(); // You can define this method to get a callback
     }
@@ -233,6 +256,7 @@
         private @NonNull Map<String,String> mStringMap;
         private @NonNull SparseArray<SampleWithCustomBuilder> mSparseArray;
         private @NonNull SparseIntArray mSparseIntArray;
+        private @SuppressWarnings({ "WeakerAccess" }) @Nullable Boolean mNullableBoolean;
 
         private long mBuilderFieldsSet = 0L;
 
@@ -328,10 +352,18 @@
             return this;
         }
 
+        @DataClass.Generated.Member
+        public @NonNull Builder setNullableBoolean(@SuppressWarnings({ "WeakerAccess" }) @Nullable Boolean value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x80;
+            mNullableBoolean = value;
+            return this;
+        }
+
         /** Builds the instance. This builder should not be touched after calling this! */
         public ParcelAllTheThingsDataClass build() {
             checkNotUsed();
-            mBuilderFieldsSet |= 0x80; // Mark builder used
+            mBuilderFieldsSet |= 0x100; // Mark builder used
 
             if ((mBuilderFieldsSet & 0x1) == 0) {
                 mStringArray = null;
@@ -354,6 +386,9 @@
             if ((mBuilderFieldsSet & 0x40) == 0) {
                 mSparseIntArray = null;
             }
+            if ((mBuilderFieldsSet & 0x80) == 0) {
+                mNullableBoolean = null;
+            }
             ParcelAllTheThingsDataClass o = new ParcelAllTheThingsDataClass(
                     mStringArray,
                     mIntArray,
@@ -361,12 +396,13 @@
                     mMap,
                     mStringMap,
                     mSparseArray,
-                    mSparseIntArray);
+                    mSparseIntArray,
+                    mNullableBoolean);
             return o;
         }
 
         private void checkNotUsed() {
-            if ((mBuilderFieldsSet & 0x80) != 0) {
+            if ((mBuilderFieldsSet & 0x100) != 0) {
                 throw new IllegalStateException(
                         "This Builder should not be reused. Use a new Builder instance instead");
             }
@@ -374,10 +410,10 @@
     }
 
     @DataClass.Generated(
-            time = 1570576454326L,
-            codegenVersion = "1.0.7",
+            time = 1570828331396L,
+            codegenVersion = "1.0.8",
             sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java",
-            inputSignatures = " @android.annotation.NonNull java.lang.String[] mStringArray\n @android.annotation.NonNull int[] mIntArray\n @android.annotation.NonNull java.util.List<java.lang.String> mStringList\n @android.annotation.NonNull java.util.Map<java.lang.String,com.android.codegentest.SampleWithCustomBuilder> mMap\n @android.annotation.NonNull java.util.Map<java.lang.String,java.lang.String> mStringMap\n @android.annotation.NonNull android.util.SparseArray<com.android.codegentest.SampleWithCustomBuilder> mSparseArray\n @android.annotation.NonNull android.util.SparseIntArray mSparseIntArray\nclass ParcelAllTheThingsDataClass extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genAidl=false, genToString=true)")
+            inputSignatures = " @android.annotation.NonNull java.lang.String[] mStringArray\n @android.annotation.NonNull int[] mIntArray\n @android.annotation.NonNull java.util.List<java.lang.String> mStringList\n @android.annotation.NonNull java.util.Map<java.lang.String,com.android.codegentest.SampleWithCustomBuilder> mMap\n @android.annotation.NonNull java.util.Map<java.lang.String,java.lang.String> mStringMap\n @android.annotation.NonNull android.util.SparseArray<com.android.codegentest.SampleWithCustomBuilder> mSparseArray\n @android.annotation.NonNull android.util.SparseIntArray mSparseIntArray\n @java.lang.SuppressWarnings({\"WeakerAccess\"}) @android.annotation.Nullable java.lang.Boolean mNullableBoolean\nclass ParcelAllTheThingsDataClass extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genAidl=false, genToString=true)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/tests/Codegen/src/com/android/codegentest/SampleDataClass.java b/tests/Codegen/src/com/android/codegentest/SampleDataClass.java
index 14010a9..d014d6d 100644
--- a/tests/Codegen/src/com/android/codegentest/SampleDataClass.java
+++ b/tests/Codegen/src/com/android/codegentest/SampleDataClass.java
@@ -342,13 +342,17 @@
 
 
 
-    // Code below generated by codegen v1.0.7.
+    // Code below generated by codegen v1.0.8.
     //
     // DO NOT MODIFY!
     // CHECKSTYLE:OFF Generated code
     //
     // To regenerate run:
     // $ codegen $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/src/com/android/codegentest/SampleDataClass.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
 
 
     @IntDef(prefix = "STATE_", value = {
@@ -1868,8 +1872,8 @@
     }
 
     @DataClass.Generated(
-            time = 1570576452225L,
-            codegenVersion = "1.0.7",
+            time = 1570828329319L,
+            codegenVersion = "1.0.8",
             sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/SampleDataClass.java",
             inputSignatures = "public static final  java.lang.String STATE_NAME_UNDEFINED\npublic static final  java.lang.String STATE_NAME_ON\npublic static final  java.lang.String STATE_NAME_OFF\npublic static final  int STATE_UNDEFINED\npublic static final  int STATE_ON\npublic static final  int STATE_OFF\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_MANUAL_REQUEST\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_COMPATIBILITY_MODE_REQUEST\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_AUGMENTED_REQUEST\nprivate  int mNum\nprivate  int mNum2\nprivate  int mNum4\nprivate @android.annotation.Nullable java.lang.String mName\nprivate @android.annotation.NonNull java.lang.String mName2\nprivate @android.annotation.NonNull java.lang.String mName4\nprivate @android.annotation.Nullable android.view.accessibility.AccessibilityNodeInfo mOtherParcelable\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.codegentest.MyDateParcelling.class) @android.annotation.NonNull java.util.Date mDate\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForPattern.class) @android.annotation.NonNull java.util.regex.Pattern mPattern\nprivate @android.annotation.NonNull java.util.List<android.net.LinkAddress> mLinkAddresses2\nprivate @com.android.internal.util.DataClass.PluralOf(\"linkAddress\") @android.annotation.NonNull java.util.ArrayList<android.net.LinkAddress> mLinkAddresses\nprivate @android.annotation.Nullable android.net.LinkAddress[] mLinkAddresses4\nprivate @com.android.codegentest.SampleDataClass.StateName @android.annotation.NonNull java.lang.String mStateName\nprivate @com.android.codegentest.SampleDataClass.RequestFlags int mFlags\nprivate @com.android.codegentest.SampleDataClass.State int mState\npublic @android.annotation.NonNull java.lang.CharSequence charSeq\nprivate final @android.annotation.Nullable android.net.LinkAddress[] mLinkAddresses5\nprivate transient  android.net.LinkAddress[] mLinkAddresses6\ntransient  int[] mTmpStorage\nprivate @android.annotation.StringRes int mStringRes\nprivate @android.annotation.IntRange(from=0L, to=6L) int mDayOfWeek\nprivate @android.annotation.Size(2L) @android.annotation.NonNull @com.android.internal.util.DataClass.Each @android.annotation.FloatRange(from=0.0) float[] mCoords\nprivate static  java.lang.String defaultName4()\nprivate  int[] lazyInitTmpStorage()\npublic  android.net.LinkAddress[] getLinkAddresses4()\nprivate  boolean patternEquals(java.util.regex.Pattern)\nprivate  int patternHashCode()\nprivate  void onConstructed()\npublic  void dump(java.io.PrintWriter)\nclass SampleDataClass extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genConstructor=true, genEqualsHashCode=true, genToString=true, genForEachField=true, genSetters=true)")
     @Deprecated
diff --git a/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java b/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java
index b5f6c73..1c87e8f 100644
--- a/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java
+++ b/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java
@@ -85,13 +85,17 @@
 
 
 
-    // Code below generated by codegen v1.0.7.
+    // Code below generated by codegen v1.0.8.
     //
     // DO NOT MODIFY!
     // CHECKSTYLE:OFF Generated code
     //
     // To regenerate run:
     // $ codegen $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
 
 
     @DataClass.Generated.Member
@@ -249,8 +253,8 @@
     }
 
     @DataClass.Generated(
-            time = 1570576453295L,
-            codegenVersion = "1.0.7",
+            time = 1570828330331L,
+            codegenVersion = "1.0.8",
             sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java",
             inputSignatures = "  long delayAmount\n @android.annotation.NonNull java.util.concurrent.TimeUnit delayUnit\n  long creationTimestamp\nprivate static  java.util.concurrent.TimeUnit unparcelDelayUnit(android.os.Parcel)\nprivate  void parcelDelayUnit(android.os.Parcel,int)\nclass SampleWithCustomBuilder extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genAidl=false, genToString=true)\nabstract  com.android.codegentest.SampleWithCustomBuilder.Builder setDelayAmount(long)\npublic abstract  com.android.codegentest.SampleWithCustomBuilder.Builder setDelayUnit(java.util.concurrent.TimeUnit)\npublic  com.android.codegentest.SampleWithCustomBuilder.Builder setDelay(long,java.util.concurrent.TimeUnit)\nclass BaseBuilder extends java.lang.Object implements []")
     @Deprecated
diff --git a/tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java b/tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java
index 0ce8aba..27af37f 100644
--- a/tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java
+++ b/tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java
@@ -51,18 +51,22 @@
 
 
 
-    // Code below generated by codegen v1.0.7.
+    // Code below generated by codegen v1.0.8.
     //
     // DO NOT MODIFY!
     // CHECKSTYLE:OFF Generated code
     //
     // To regenerate run:
     // $ codegen $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
 
 
     @DataClass.Generated(
-            time = 1570576457249L,
-            codegenVersion = "1.0.7",
+            time = 1570828334384L,
+            codegenVersion = "1.0.8",
             sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java",
             inputSignatures = "public @android.annotation.NonNull java.lang.String someMethod(int)\nclass StaleDataclassDetectorFalsePositivesTest extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=false)")
     @Deprecated
diff --git a/tests/Gating/Android.bp b/tests/Gating/Android.bp
new file mode 100644
index 0000000..b6c0094
--- /dev/null
+++ b/tests/Gating/Android.bp
@@ -0,0 +1,17 @@
+android_test {
+    name: "Gating",
+    // Only compile source java files in this apk.
+    srcs: ["src/**/*.java"],
+    certificate: "platform",
+    libs: [
+        "android.test.runner",
+        "android.test.base",
+    ],
+    static_libs: [
+        "junit",
+        "android-support-test",
+        "mockito-target-minus-junit4",
+        "truth-prebuilt",
+        "platform_compat-test-rules"
+    ],
+}
diff --git a/tests/Gating/AndroidManifest.xml b/tests/Gating/AndroidManifest.xml
new file mode 100644
index 0000000..7f14b83
--- /dev/null
+++ b/tests/Gating/AndroidManifest.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.tests.gating">
+    <application android:label="GatingTest">
+        <uses-library android:name="android.test.runner" />
+    </application>
+
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+                     android:targetPackage="com.android.tests.gating"/>
+</manifest>
diff --git a/tests/Gating/AndroidTest.xml b/tests/Gating/AndroidTest.xml
new file mode 100644
index 0000000..730e74a
--- /dev/null
+++ b/tests/Gating/AndroidTest.xml
@@ -0,0 +1,30 @@
+<!-- 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.
+-->
+<configuration description="Test compatibility change gating.">
+    <target_preparer class="com.android.tradefed.targetprep.TestFilePushSetup"/>
+    <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+        <option name="test-file-name" value="Gating.apk"/>
+    </target_preparer>
+    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"/>
+    <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"/>
+    <option name="test-suite-tag" value="apct"/>
+    <option name="test-tag" value="Gating"/>
+
+    <test class="com.android.tradefed.testtype.AndroidJUnitTest">
+        <option name="package" value="com.android.tests.gating"/>
+        <option name="runner" value="android.support.test.runner.AndroidJUnitRunner"/>
+        <option name="hidden-api-checks" value="false"/>
+    </test>
+</configuration>
diff --git a/tests/Gating/src/com/android/compat/DummyApi.java b/tests/Gating/src/com/android/compat/DummyApi.java
new file mode 100644
index 0000000..93d60d0
--- /dev/null
+++ b/tests/Gating/src/com/android/compat/DummyApi.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.compat;
+
+import android.compat.Compatibility;
+import android.content.Context;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+
+import com.android.internal.compat.IPlatformCompat;
+
+/**
+ * This is a dummy API to test gating
+ *
+ * @hide
+ */
+public class DummyApi {
+
+    public static final long CHANGE_ID = 666013;
+    public static final long CHANGE_ID_1 = 666014;
+    public static final long CHANGE_ID_2 = 666015;
+    public static final long CHANGE_SYSTEM_SERVER = 666016;
+
+    /**
+     * Dummy method
+     * @return "A" if change is enabled, "B" otherwise.
+     */
+    public static String dummyFunc() {
+        if (Compatibility.isChangeEnabled(CHANGE_ID)) {
+            return "A";
+        }
+        return "B";
+    }
+
+    /**
+     * Dummy combined method
+     * @return "0" if {@link CHANGE_ID_1} is disabled and {@link CHANGE_ID_2} is disabled,
+               "1" if {@link CHANGE_ID_1} is disabled and {@link CHANGE_ID_2} is enabled,
+               "2" if {@link CHANGE_ID_1} is enabled and {@link CHANGE_ID_2} is disabled,
+               "3" if {@link CHANGE_ID_1} is enabled and {@link CHANGE_ID_2} is enabled.
+     */
+    public static String dummyCombinedFunc() {
+        if (!Compatibility.isChangeEnabled(CHANGE_ID_1)
+                && !Compatibility.isChangeEnabled(CHANGE_ID_2)) {
+            return "0";
+        } else if (!Compatibility.isChangeEnabled(CHANGE_ID_1)
+                && Compatibility.isChangeEnabled(CHANGE_ID_2)) {
+            return "1";
+        } else if (Compatibility.isChangeEnabled(CHANGE_ID_1)
+                && !Compatibility.isChangeEnabled(CHANGE_ID_2)) {
+            return "2";
+        }
+        return "3";
+    }
+
+    /**
+     * Dummy api using system server API.
+     */
+    public static String dummySystemServer(Context context) {
+        IPlatformCompat platformCompat = IPlatformCompat.Stub
+                .asInterface(ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
+        if (platformCompat == null) {
+            throw new RuntimeException("Could not obtain IPlatformCompat instance!");
+        }
+        String packageName = context.getPackageName();
+        try {
+            if (platformCompat.isChangeEnabledByPackageName(CHANGE_SYSTEM_SERVER, packageName)) {
+                return "True";
+            } else {
+                return "False";
+            }
+        } catch (RemoteException e) {
+            throw new RuntimeException("Could not get change value!", e);
+        }
+    }
+}
diff --git a/tests/Gating/src/com/android/tests/gating/GatingTest.java b/tests/Gating/src/com/android/tests/gating/GatingTest.java
new file mode 100644
index 0000000..0867624
--- /dev/null
+++ b/tests/Gating/src/com/android/tests/gating/GatingTest.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.tests.gating;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.compat.CompatChangeRule;
+import android.compat.CompatChangeRule.DisableCompatChanges;
+import android.compat.CompatChangeRule.EnableCompatChanges;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+
+import com.android.compat.DummyApi;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestRule;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests for platform compatibility change gating.
+ */
+@RunWith(AndroidJUnit4.class)
+public class GatingTest {
+
+    @Rule
+    public TestRule compatChangeRule = new CompatChangeRule();
+
+    @Test
+    @EnableCompatChanges({DummyApi.CHANGE_ID})
+    public void testDummyGatingPositive() {
+        assertThat(DummyApi.dummyFunc()).isEqualTo("A");
+    }
+
+    @Test
+    @DisableCompatChanges({DummyApi.CHANGE_ID})
+    public void testDummyGatingNegative() {
+        assertThat(DummyApi.dummyFunc()).isEqualTo("B");
+    }
+
+    @Test
+    @DisableCompatChanges({DummyApi.CHANGE_ID_1, DummyApi.CHANGE_ID_2})
+    public void testDummyGatingCombined0() {
+        assertThat(DummyApi.dummyCombinedFunc()).isEqualTo("0");
+    }
+
+    @Test
+    @DisableCompatChanges({DummyApi.CHANGE_ID_1})
+    @EnableCompatChanges({DummyApi.CHANGE_ID_2})
+    public void testDummyGatingCombined1() {
+        assertThat(DummyApi.dummyCombinedFunc()).isEqualTo("1");
+    }
+
+    @Test
+    @EnableCompatChanges({DummyApi.CHANGE_ID_1})
+    @DisableCompatChanges({DummyApi.CHANGE_ID_2})
+    public void testDummyGatingCombined2() {
+        assertThat(DummyApi.dummyCombinedFunc()).isEqualTo("2");
+    }
+
+    @Test
+    @EnableCompatChanges({DummyApi.CHANGE_ID_1, DummyApi.CHANGE_ID_2})
+    public void testDummyGatingCombined3() {
+        assertThat(DummyApi.dummyCombinedFunc()).isEqualTo("3");
+    }
+
+    @Test
+    @EnableCompatChanges({DummyApi.CHANGE_SYSTEM_SERVER})
+    public void testDummyGatingPositiveSystemServer() {
+        assertThat(
+                DummyApi.dummySystemServer(InstrumentationRegistry.getTargetContext())).isEqualTo(
+                "True");
+    }
+
+    @Test
+    @DisableCompatChanges({DummyApi.CHANGE_SYSTEM_SERVER})
+    public void testDummyGatingNegativeSystemServer() {
+        assertThat(
+                DummyApi.dummySystemServer(InstrumentationRegistry.getTargetContext())).isEqualTo(
+                "False");
+    }
+}
diff --git a/tests/RollbackTest/Android.bp b/tests/RollbackTest/Android.bp
index 231d045b..085c53c 100644
--- a/tests/RollbackTest/Android.bp
+++ b/tests/RollbackTest/Android.bp
@@ -19,7 +19,6 @@
     static_libs: ["androidx.test.rules", "cts-rollback-lib", "cts-install-lib"],
     test_suites: ["general-tests"],
     test_config: "RollbackTest.xml",
-    // TODO: sdk_version: "test_current" when Intent#resolveSystemservice is TestApi
 }
 
 java_test_host {
diff --git a/tools/codegen/src/com/android/codegen/FieldInfo.kt b/tools/codegen/src/com/android/codegen/FieldInfo.kt
index ba00264..1a7fd6e 100644
--- a/tools/codegen/src/com/android/codegen/FieldInfo.kt
+++ b/tools/codegen/src/com/android/codegen/FieldInfo.kt
@@ -191,7 +191,7 @@
      * Parcel.write* and Parcel.read* method name wildcard values
      */
     val ParcelMethodsSuffix = when {
-        FieldClass in PRIMITIVE_TYPES - "char" - "boolean" +
+        FieldClass in PRIMITIVE_TYPES - "char" - "boolean" + BOXED_PRIMITIVE_TYPES +
                 listOf("String", "CharSequence", "Exception", "Size", "SizeF", "Bundle",
                         "FileDescriptor", "SparseBooleanArray", "SparseIntArray", "SparseArray") ->
             FieldClass
diff --git a/tools/codegen/src/com/android/codegen/Generators.kt b/tools/codegen/src/com/android/codegen/Generators.kt
index 5a95676..0ebb3cf 100644
--- a/tools/codegen/src/com/android/codegen/Generators.kt
+++ b/tools/codegen/src/com/android/codegen/Generators.kt
@@ -854,6 +854,7 @@
         it.nameAsString == intOrStringDef?.AnnotationName
                 || it.nameAsString in knownNonValidationAnnotations
                 || it in perElementValidations
+                || it.args.any { (_, value) -> value is ArrayInitializerExpr }
     }.forEach { annotation ->
         appendValidateCall(annotation,
                 valueToValidate = if (annotation.nameAsString == Size) sizeExpr else name)
@@ -874,14 +875,7 @@
     val validate = memberRef("com.android.internal.util.AnnotationValidations.validate")
     "$validate(" {
         !"${annotation.nameAsString}.class, null, $valueToValidate"
-        val params = when (annotation) {
-            is MarkerAnnotationExpr -> emptyMap()
-            is SingleMemberAnnotationExpr -> mapOf("value" to annotation.memberValue)
-            is NormalAnnotationExpr ->
-                annotation.pairs.map { it.name.asString() to it.value }.toMap()
-            else -> throw IllegalStateException()
-        }
-        params.forEach { name, value ->
+        annotation.args.forEach { name, value ->
             !",\n\"$name\", $value"
         }
     }
diff --git a/tools/codegen/src/com/android/codegen/InputSignaturesComputation.kt b/tools/codegen/src/com/android/codegen/InputSignaturesComputation.kt
index 24cf469..d6953c0 100644
--- a/tools/codegen/src/com/android/codegen/InputSignaturesComputation.kt
+++ b/tools/codegen/src/com/android/codegen/InputSignaturesComputation.kt
@@ -87,6 +87,14 @@
         is IntegerLiteralExpr -> sb.append(ex.asInt()).append("L")
         is LongLiteralExpr -> sb.append(ex.asLong()).append("L")
         is DoubleLiteralExpr -> sb.append(ex.asDouble())
+        is ArrayInitializerExpr -> {
+            sb.append("{")
+            ex.values.forEachLastAware { arrayElem, isLast ->
+                appendExpr(sb, arrayElem)
+                if (!isLast) sb.append(", ")
+            }
+            sb.append("}")
+        }
         else -> sb.append(ex)
     }
 }
diff --git a/tools/codegen/src/com/android/codegen/Main.kt b/tools/codegen/src/com/android/codegen/Main.kt
index 039f7b2..ce83d3d 100755
--- a/tools/codegen/src/com/android/codegen/Main.kt
+++ b/tools/codegen/src/com/android/codegen/Main.kt
@@ -9,6 +9,7 @@
 const val INDENT_SINGLE = "    "
 
 val PRIMITIVE_TYPES = listOf("byte", "short", "int", "long", "char", "float", "double", "boolean")
+val BOXED_PRIMITIVE_TYPES = PRIMITIVE_TYPES.map { it.capitalize() } - "Int" + "Integer" - "Char" + "Character"
 
 val BUILTIN_SPECIAL_PARCELLINGS = listOf("Pattern")
 
@@ -142,6 +143,10 @@
         //
         // To regenerate run:
         // $ $cliExecutable ${cliArgs.dropLast(1).joinToString("") { "$it " }}$fileEscaped
+        //
+        // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+        //   Settings > Editor > Code Style > Formatter Control
+        //@formatter:off
 
         """
 
diff --git a/tools/codegen/src/com/android/codegen/SharedConstants.kt b/tools/codegen/src/com/android/codegen/SharedConstants.kt
index 47f774f..8c4583f 100644
--- a/tools/codegen/src/com/android/codegen/SharedConstants.kt
+++ b/tools/codegen/src/com/android/codegen/SharedConstants.kt
@@ -1,7 +1,7 @@
 package com.android.codegen
 
 const val CODEGEN_NAME = "codegen"
-const val CODEGEN_VERSION = "1.0.7"
+const val CODEGEN_VERSION = "1.0.8"
 
 const val CANONICAL_BUILDER_CLASS = "Builder"
 const val BASE_BUILDER_CLASS = "BaseBuilder"
diff --git a/tools/codegen/src/com/android/codegen/Utils.kt b/tools/codegen/src/com/android/codegen/Utils.kt
index a1f068a..e703397 100644
--- a/tools/codegen/src/com/android/codegen/Utils.kt
+++ b/tools/codegen/src/com/android/codegen/Utils.kt
@@ -1,9 +1,6 @@
 package com.android.codegen
 
-import com.github.javaparser.ast.Modifier
-import com.github.javaparser.ast.expr.AnnotationExpr
-import com.github.javaparser.ast.expr.Expression
-import com.github.javaparser.ast.expr.SingleMemberAnnotationExpr
+import com.github.javaparser.ast.expr.*
 import com.github.javaparser.ast.nodeTypes.NodeWithModifiers
 import java.time.Instant
 import java.time.ZoneId
@@ -88,3 +85,10 @@
 }
 
 fun bitAtExpr(bitIndex: Int) = "0x${java.lang.Long.toHexString(1L shl bitIndex)}"
+
+val AnnotationExpr.args: Map<String, Expression> get() = when (this) {
+    is MarkerAnnotationExpr -> emptyMap()
+    is SingleMemberAnnotationExpr -> mapOf("value" to memberValue)
+    is NormalAnnotationExpr -> pairs.map { it.name.asString() to it.value }.toMap()
+    else -> throw IllegalArgumentException("Unknown annotation expression: $this")
+}
diff --git a/tools/streaming_proto/Android.bp b/tools/streaming_proto/Android.bp
index 14eead8..1390f63 100644
--- a/tools/streaming_proto/Android.bp
+++ b/tools/streaming_proto/Android.bp
@@ -29,7 +29,7 @@
         "-Werror",
     ],
 
-    shared_libs: ["libprotoc"],
+    static_libs: ["libprotoc"],
 }
 
 cc_binary_host {