Merge "Change Stack to have generic WindowContainer children (76/n)"
diff --git a/Android.bp b/Android.bp
index 0544330..04b4e6e 100644
--- a/Android.bp
+++ b/Android.bp
@@ -205,10 +205,23 @@
         "wifi/java/**/*.java",
         "wifi/java/**/*.aidl",
     ],
+    exclude_srcs: [
+        ":framework-wifi-non-updatable-sources"
+    ],
     path: "wifi/java",
 }
 
 filegroup {
+    name: "framework-wifi-non-updatable-sources",
+    srcs: [
+        // TODO(b/146011398) package android.net.wifi is now split amongst 2 jars: framework.jar and
+        // framework-wifi.jar. This is not a good idea, should move WifiNetworkScoreCache
+        // to a separate package.
+        "wifi/java/android/net/wifi/WifiNetworkScoreCache.java"
+    ],
+}
+
+filegroup {
     name: "framework-non-updatable-sources",
     srcs: [
         // Java/AIDL sources under frameworks/base
@@ -233,6 +246,7 @@
         ":framework-telephony-common-sources",
         ":framework-telephony-sources",
         ":framework-wifi-sources",
+        ":framework-wifi-non-updatable-sources",
         ":PacProcessor-aidl-sources",
         ":ProxyHandler-aidl-sources",
 
@@ -379,7 +393,9 @@
 
     sdk_version: "core_platform",
     libs: [
+        "app-compat-annotations",
         "ext",
+        "unsupportedappusage",
         "updatable_media_stubs",
     ],
 
@@ -427,7 +443,6 @@
     name: "framework-minus-apex",
     defaults: ["framework-defaults"],
     srcs: [":framework-non-updatable-sources"],
-    libs: ["app-compat-annotations"],
     installable: true,
     javac_shard_size: 150,
     required: [
@@ -445,6 +460,7 @@
     ],
     // For backwards compatibility.
     stem: "framework",
+    apex_available: ["//apex_available:platform"],
 }
 
 // This "framework" module is NOT installed to the device. It's
@@ -465,6 +481,7 @@
         // TODO(jiyong): add stubs for APEXes here
     ],
     sdk_version: "core_platform",
+    apex_available: ["//apex_available:platform"],
 }
 
 java_library {
@@ -472,15 +489,18 @@
     defaults: ["framework-defaults"],
     srcs: [":framework-all-sources"],
     installable: false,
-    libs: ["app-compat-annotations"],
-    static_libs: ["exoplayer2-core"]
+    static_libs: ["exoplayer2-core"],
+    apex_available: ["//apex_available:platform"],
 }
 
 java_library {
     name: "framework-annotation-proc",
     defaults: ["framework-aidl-export-defaults"],
     srcs: [":framework-all-sources"],
-    libs: ["app-compat-annotations"],
+    libs: [
+        "app-compat-annotations",
+        "unsupportedappusage",
+    ],
     installable: false,
     plugins: [
         "unsupportedappusage-annotation-processor",
diff --git a/apex/sdkext/derive_sdk/derive_sdk.cpp b/apex/sdkext/derive_sdk/derive_sdk.cpp
index 0aacebe..7536def 100644
--- a/apex/sdkext/derive_sdk/derive_sdk.cpp
+++ b/apex/sdkext/derive_sdk/derive_sdk.cpp
@@ -68,7 +68,7 @@
     auto itr = std::min_element(versions.begin(), versions.end());
     std::string prop_value = itr == versions.end() ? "0" : std::to_string(*itr);
 
-    if (!android::base::SetProperty("persist.com.android.sdkext.sdk_info", prop_value)) {
+    if (!android::base::SetProperty("ro.build.version.extensions.r", prop_value)) {
         LOG(ERROR) << "failed to set sdk_info prop";
         return EXIT_FAILURE;
     }
diff --git a/apex/sdkext/framework/java/android/os/ext/SdkExtensions.java b/apex/sdkext/framework/java/android/os/ext/SdkExtensions.java
index 331ef21..d3b9397 100644
--- a/apex/sdkext/framework/java/android/os/ext/SdkExtensions.java
+++ b/apex/sdkext/framework/java/android/os/ext/SdkExtensions.java
@@ -38,7 +38,7 @@
 
     private static final int R_EXTENSION_INT;
     static {
-        R_EXTENSION_INT = SystemProperties.getInt("persist.com.android.sdkext.sdk_info", 0);
+        R_EXTENSION_INT = SystemProperties.getInt("ro.build.version.extensions.r", 0);
     }
 
     /**
diff --git a/api/current.txt b/api/current.txt
index be45132..6b2f45a 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -25646,7 +25646,7 @@
     method public void onTracksFound(int);
   }
 
-  public static interface MediaParser.SeekMap {
+  public static final class MediaParser.SeekMap {
     method public long getDurationUs();
     method @NonNull public android.util.Pair<android.media.MediaParser.SeekPoint,android.media.MediaParser.SeekPoint> getSeekPoints(long);
     method public boolean isSeekable();
diff --git a/api/system-current.txt b/api/system-current.txt
index dd94efd..6b51eab 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -8568,14 +8568,6 @@
     field public static final String KEY_CARRIER_SETUP_APP_STRING = "carrier_setup_app_string";
   }
 
-  public static final class CarrierConfigManager.Wifi {
-    field public static final String KEY_CARRIER_CONNECTION_MANAGER_PACKAGE_STRING = "wifi.carrier_connection_manager_package_string";
-    field public static final String KEY_CARRIER_PROFILES_VERSION_INT = "wifi.carrier_profiles_version_int";
-    field public static final String KEY_NETWORK_PROFILES_STRING_ARRAY = "wifi.network_profiles_string_array";
-    field public static final String KEY_PASSPOINT_PROFILES_STRING_ARRAY = "wifi.passpoint_profiles_string_array";
-    field public static final String KEY_PREFIX = "wifi.";
-  }
-
   public final class CarrierRestrictionRules implements android.os.Parcelable {
     method @NonNull public java.util.List<java.lang.Boolean> areCarrierIdentifiersAllowed(@NonNull java.util.List<android.service.carrier.CarrierIdentifier>);
     method public int describeContents();
diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp
index 17427a2..484f823 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -250,6 +250,7 @@
         "tests/external/GpuStatsPuller_test.cpp",
         "tests/external/IncidentReportArgs_test.cpp",
         "tests/external/puller_util_test.cpp",
+        "tests/external/StatsCallbackPuller_test.cpp",
         "tests/external/StatsPuller_test.cpp",
         "tests/external/SurfaceflingerStatsPuller_test.cpp",
         "tests/FieldValue_test.cpp",
diff --git a/cmds/statsd/src/external/StatsCallbackPuller.cpp b/cmds/statsd/src/external/StatsCallbackPuller.cpp
index f5b1e7f..0e6b677 100644
--- a/cmds/statsd/src/external/StatsCallbackPuller.cpp
+++ b/cmds/statsd/src/external/StatsCallbackPuller.cpp
@@ -35,8 +35,9 @@
 namespace os {
 namespace statsd {
 
-StatsCallbackPuller::StatsCallbackPuller(int tagId, const sp<IPullAtomCallback>& callback)
-    : StatsPuller(tagId), mCallback(callback) {
+StatsCallbackPuller::StatsCallbackPuller(int tagId, const sp<IPullAtomCallback>& callback,
+                                         int64_t timeoutNs)
+    : StatsPuller(tagId), mCallback(callback), mTimeoutNs(timeoutNs) {
     VLOG("StatsCallbackPuller created for tag %d", tagId);
 }
 
@@ -64,10 +65,9 @@
                 {
                     lock_guard<mutex> lk(*cv_mutex);
                     for (const StatsEventParcel& parcel: output) {
-                        shared_ptr<LogEvent> event =
-                              make_shared<LogEvent>(const_cast<uint8_t*>(parcel.buffer.data()),
-                                                    parcel.buffer.size(),
-                                                    /*uid=*/ -1);
+                        shared_ptr<LogEvent> event = make_shared<LogEvent>(
+                                const_cast<uint8_t*>(parcel.buffer.data()), parcel.buffer.size(),
+                                /*uid=*/-1, /*useNewSchema=*/true);
                         sharedData->push_back(event);
                     }
                     *pullSuccess = success;
@@ -76,7 +76,8 @@
                 cv->notify_one();
             });
 
-    // Initiate the pull.
+    // Initiate the pull. This is a oneway call to a different process, except
+    // in unit tests. In process calls are not oneway.
     Status status = mCallback->onPullAtom(mTagId, resultReceiver);
     if (!status.isOk()) {
         return false;
@@ -84,10 +85,8 @@
 
     {
         unique_lock<mutex> unique_lk(*cv_mutex);
-        int64_t pullTimeoutNs =
-                StatsPullerManager::kAllPullAtomInfo.at({.atomTag = mTagId}).pullTimeoutNs;
         // Wait until the pull finishes, or until the pull timeout.
-        cv->wait_for(unique_lk, chrono::nanoseconds(pullTimeoutNs),
+        cv->wait_for(unique_lk, chrono::nanoseconds(mTimeoutNs),
                      [pullFinish] { return *pullFinish; });
         if (!*pullFinish) {
             // Note: The parent stats puller will also note that there was a timeout and that the
@@ -96,7 +95,7 @@
             return true;
         } else {
             // Only copy the data if we did not timeout and the pull was successful.
-            if (pullSuccess) {
+            if (*pullSuccess) {
                 *data = std::move(*sharedData);
             }
             VLOG("StatsCallbackPuller::pull succeeded for %d", mTagId);
diff --git a/cmds/statsd/src/external/StatsCallbackPuller.h b/cmds/statsd/src/external/StatsCallbackPuller.h
index ce506c7..d943f9d 100644
--- a/cmds/statsd/src/external/StatsCallbackPuller.h
+++ b/cmds/statsd/src/external/StatsCallbackPuller.h
@@ -27,11 +27,17 @@
 
 class StatsCallbackPuller : public StatsPuller {
 public:
-    explicit StatsCallbackPuller(int tagId, const sp<IPullAtomCallback>& callback);
+    explicit StatsCallbackPuller(int tagId, const sp<IPullAtomCallback>& callback,
+                                 int64_t timeoutNs);
 
 private:
     bool PullInternal(vector<std::shared_ptr<LogEvent> >* data) override;
     const sp<IPullAtomCallback> mCallback;
+    const int64_t mTimeoutNs;
+
+    FRIEND_TEST(StatsCallbackPullerTest, PullFail);
+    FRIEND_TEST(StatsCallbackPullerTest, PullSuccess);
+    FRIEND_TEST(StatsCallbackPullerTest, PullTimeout);
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index 615af89..9ee627e 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -500,10 +500,11 @@
     VLOG("RegisterPullerCallback: adding puller for tag %d", atomTag);
     // TODO: linkToDeath with the callback so that we can remove it and delete the puller.
     StatsdStats::getInstance().notePullerCallbackRegistrationChanged(atomTag, /*registered=*/true);
-    kAllPullAtomInfo[{.atomTag = atomTag}] = {.additiveFields = additiveFields,
-                                              .coolDownNs = coolDownNs,
-                                              .puller = new StatsCallbackPuller(atomTag, callback),
-                                              .pullTimeoutNs = timeoutNs,
+    kAllPullAtomInfo[{.atomTag = atomTag}] = {
+            .additiveFields = additiveFields,
+            .coolDownNs = coolDownNs,
+            .puller = new StatsCallbackPuller(atomTag, callback, timeoutNs),
+            .pullTimeoutNs = timeoutNs,
     };
 }
 
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index 67022a0..36f4623 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -52,6 +52,17 @@
 #endif
 }
 
+LogEvent::LogEvent(uint8_t* msg, uint32_t len, uint32_t uid, bool useNewSchema)
+    : mBuf(msg), mRemainingLen(len), mLogdTimestampNs(time(nullptr)), mLogUid(uid) {
+    if (useNewSchema) {
+        initNew();
+    } else {
+        mContext = create_android_log_parser((char*)msg, len);
+        init(mContext);
+        if (mContext) android_log_destroy(&mContext);  // set mContext to NULL
+    }
+}
+
 LogEvent::LogEvent(const LogEvent& event) {
     mTagId = event.mTagId;
     mLogUid = event.mLogUid;
diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h
index 1ff95f7..596d623 100644
--- a/cmds/statsd/src/logd/LogEvent.h
+++ b/cmds/statsd/src/logd/LogEvent.h
@@ -75,6 +75,11 @@
     explicit LogEvent(uint8_t* msg, uint32_t len, uint32_t uid);
 
     /**
+     * Temp constructor to use for pulled atoms until we flip the socket schema.
+     */
+    explicit LogEvent(uint8_t* msg, uint32_t len, uint32_t uid, bool useNewSchema);
+
+    /**
      * Creates LogEvent from StatsLogEventWrapper.
      */
     static void createLogEvents(const StatsLogEventWrapper& statsLogEventWrapper,
diff --git a/cmds/statsd/src/state/StateManager.cpp b/cmds/statsd/src/state/StateManager.cpp
index 2fa28c9..80d3983 100644
--- a/cmds/statsd/src/state/StateManager.cpp
+++ b/cmds/statsd/src/state/StateManager.cpp
@@ -29,18 +29,15 @@
 }
 
 void StateManager::onLogEvent(const LogEvent& event) {
-    std::lock_guard<std::mutex> lock(mMutex);
     if (mStateTrackers.find(event.GetTagId()) != mStateTrackers.end()) {
         mStateTrackers[event.GetTagId()]->onLogEvent(event);
     }
 }
 
 bool StateManager::registerListener(int32_t atomId, wp<StateListener> listener) {
-    std::lock_guard<std::mutex> lock(mMutex);
-
-    // Check if state tracker already exists
+    // Check if state tracker already exists.
     if (mStateTrackers.find(atomId) == mStateTrackers.end()) {
-        // Create a new state tracker iff atom is a state atom
+        // Create a new state tracker iff atom is a state atom.
         auto it = android::util::AtomsInfo::kStateAtomsFieldOptions.find(atomId);
         if (it != android::util::AtomsInfo::kStateAtomsFieldOptions.end()) {
             mStateTrackers[atomId] = new StateTracker(atomId, it->second);
@@ -79,8 +76,6 @@
 
 bool StateManager::getStateValue(int32_t atomId, const HashableDimensionKey& key,
                                  FieldValue* output) const {
-    std::lock_guard<std::mutex> lock(mMutex);
-
     auto it = mStateTrackers.find(atomId);
     if (it != mStateTrackers.end()) {
         return it->second->getStateValue(key, output);
diff --git a/cmds/statsd/src/state/StateManager.h b/cmds/statsd/src/state/StateManager.h
index 272724c..a6053e6 100644
--- a/cmds/statsd/src/state/StateManager.h
+++ b/cmds/statsd/src/state/StateManager.h
@@ -27,6 +27,10 @@
 namespace os {
 namespace statsd {
 
+/**
+ * This class is NOT thread safe.
+ * It should only be used while StatsLogProcessor's lock is held.
+ */
 class StateManager : public virtual RefBase {
 public:
     StateManager(){};
@@ -56,13 +60,10 @@
                        FieldValue* output) const;
 
     inline int getStateTrackersCount() const {
-        std::lock_guard<std::mutex> lock(mMutex);
         return mStateTrackers.size();
     }
 
     inline int getListenersCount(int32_t atomId) const {
-        std::lock_guard<std::mutex> lock(mMutex);
-
         auto it = mStateTrackers.find(atomId);
         if (it != mStateTrackers.end()) {
             return it->second->getListenersCount();
@@ -71,10 +72,10 @@
     }
 
 private:
-  mutable std::mutex mMutex;
+    mutable std::mutex mMutex;
 
-  // Maps state atom ids to StateTrackers
-  std::unordered_map<int32_t, sp<StateTracker>> mStateTrackers;
+    // Maps state atom ids to StateTrackers
+    std::unordered_map<int32_t, sp<StateTracker>> mStateTrackers;
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/tests/external/StatsCallbackPuller_test.cpp b/cmds/statsd/tests/external/StatsCallbackPuller_test.cpp
new file mode 100644
index 0000000..2b0590d
--- /dev/null
+++ b/cmds/statsd/tests/external/StatsCallbackPuller_test.cpp
@@ -0,0 +1,210 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "src/external/StatsCallbackPuller.h"
+
+#include <android/os/BnPullAtomCallback.h>
+#include <android/os/IPullAtomResultReceiver.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <stdio.h>
+
+#include <chrono>
+#include <thread>
+#include <vector>
+
+#include "../metrics/metrics_test_helper.h"
+#include "src/stats_log_util.h"
+#include "tests/statsd_test_util.h"
+
+#ifdef __ANDROID__
+
+namespace android {
+namespace os {
+namespace statsd {
+
+using namespace testing;
+using std::make_shared;
+using std::shared_ptr;
+using std::vector;
+using std::this_thread::sleep_for;
+using testing::Contains;
+
+namespace {
+int pullTagId = -12;
+bool pullSuccess;
+vector<int64_t> values;
+int64_t pullDelayNs;
+int64_t pullTimeoutNs;
+int64_t pullCoolDownNs;
+std::thread pullThread;
+
+stats_event* createSimpleEvent(int64_t value) {
+    stats_event* event = stats_event_obtain();
+    stats_event_set_atom_id(event, pullTagId);
+    stats_event_write_int64(event, value);
+    stats_event_build(event);
+    return event;
+}
+
+void executePull(const sp<IPullAtomResultReceiver>& resultReceiver) {
+    // Convert stats_events into StatsEventParcels.
+    std::vector<android::util::StatsEventParcel> parcels;
+    for (int i = 0; i < values.size(); i++) {
+        stats_event* event = createSimpleEvent(values[i]);
+        size_t size;
+        uint8_t* buffer = stats_event_get_buffer(event, &size);
+
+        android::util::StatsEventParcel p;
+        // vector.assign() creates a copy, but this is inevitable unless
+        // stats_event.h/c uses a vector as opposed to a buffer.
+        p.buffer.assign(buffer, buffer + size);
+        parcels.push_back(std::move(p));
+        stats_event_release(event);
+    }
+
+    sleep_for(std::chrono::nanoseconds(pullDelayNs));
+    resultReceiver->pullFinished(pullTagId, pullSuccess, parcels);
+}
+
+class FakePullAtomCallback : public BnPullAtomCallback {
+public:
+    binder::Status onPullAtom(int atomTag,
+                              const sp<IPullAtomResultReceiver>& resultReceiver) override {
+        // Force pull to happen in separate thread to simulate binder.
+        pullThread = std::thread(executePull, resultReceiver);
+        return binder::Status::ok();
+    }
+};
+
+class StatsCallbackPullerTest : public ::testing::Test {
+public:
+    StatsCallbackPullerTest() {
+    }
+
+    void SetUp() override {
+        pullSuccess = false;
+        pullDelayNs = 0;
+        values.clear();
+        pullTimeoutNs = 10000000000LL;  // 10 seconds.
+        pullCoolDownNs = 1000000000;    // 1 second.
+    }
+
+    void TearDown() override {
+        if (pullThread.joinable()) {
+            pullThread.join();
+        }
+        values.clear();
+    }
+};
+}  // Anonymous namespace.
+
+TEST_F(StatsCallbackPullerTest, PullSuccess) {
+    sp<FakePullAtomCallback> cb = new FakePullAtomCallback();
+    int64_t value = 43;
+    pullSuccess = true;
+    values.push_back(value);
+
+    StatsCallbackPuller puller(pullTagId, cb, pullTimeoutNs);
+
+    vector<std::shared_ptr<LogEvent>> dataHolder;
+    int64_t startTimeNs = getElapsedRealtimeNs();
+    EXPECT_TRUE(puller.PullInternal(&dataHolder));
+    int64_t endTimeNs = getElapsedRealtimeNs();
+
+    EXPECT_EQ(1, dataHolder.size());
+    EXPECT_EQ(pullTagId, dataHolder[0]->GetTagId());
+    EXPECT_LT(startTimeNs, dataHolder[0]->GetElapsedTimestampNs());
+    EXPECT_GT(endTimeNs, dataHolder[0]->GetElapsedTimestampNs());
+    EXPECT_EQ(1, dataHolder[0]->size());
+    EXPECT_EQ(value, dataHolder[0]->getValues()[0].mValue.int_value);
+}
+
+TEST_F(StatsCallbackPullerTest, PullFail) {
+    sp<FakePullAtomCallback> cb = new FakePullAtomCallback();
+    pullSuccess = false;
+    int64_t value = 1234;
+    values.push_back(value);
+
+    StatsCallbackPuller puller(pullTagId, cb, pullTimeoutNs);
+
+    vector<std::shared_ptr<LogEvent>> dataHolder;
+    EXPECT_FALSE(puller.PullInternal(&dataHolder));
+    EXPECT_EQ(0, dataHolder.size());
+}
+
+TEST_F(StatsCallbackPullerTest, PullTimeout) {
+    sp<FakePullAtomCallback> cb = new FakePullAtomCallback();
+    pullSuccess = true;
+    pullDelayNs = 500000000;  // 500ms.
+    pullTimeoutNs = 10000;    // 10 microseconds.
+    int64_t value = 4321;
+    values.push_back(value);
+
+    StatsCallbackPuller puller(pullTagId, cb, pullTimeoutNs);
+
+    vector<std::shared_ptr<LogEvent>> dataHolder;
+    int64_t startTimeNs = getElapsedRealtimeNs();
+    // Returns true to let StatsPuller code evaluate the timeout.
+    EXPECT_TRUE(puller.PullInternal(&dataHolder));
+    int64_t endTimeNs = getElapsedRealtimeNs();
+    int64_t actualPullDurationNs = endTimeNs - startTimeNs;
+
+    // Pull should take at least the timeout amount of time, but should stop early because the delay
+    // is bigger.
+    EXPECT_LT(pullTimeoutNs, actualPullDurationNs);
+    EXPECT_GT(pullDelayNs, actualPullDurationNs);
+    EXPECT_EQ(0, dataHolder.size());
+
+    // Let the pull return and make sure that the dataHolder is not modified.
+    pullThread.join();
+    EXPECT_EQ(0, dataHolder.size());
+}
+
+// Register a puller and ensure that the timeout logic works.
+TEST_F(StatsCallbackPullerTest, RegisterAndTimeout) {
+    sp<FakePullAtomCallback> cb = new FakePullAtomCallback();
+    pullSuccess = true;
+    pullDelayNs = 500000000;  // 500 ms.
+    pullTimeoutNs = 10000;    // 10 microsseconds.
+    int64_t value = 4321;
+    values.push_back(value);
+
+    StatsPullerManager pullerManager;
+    pullerManager.RegisterPullAtomCallback(/*uid=*/-1, pullTagId, pullCoolDownNs, pullTimeoutNs,
+                                           vector<int32_t>(), cb);
+    vector<std::shared_ptr<LogEvent>> dataHolder;
+    int64_t startTimeNs = getElapsedRealtimeNs();
+    // Returns false, since StatsPuller code will evaluate the timeout.
+    EXPECT_FALSE(pullerManager.Pull(pullTagId, &dataHolder));
+    int64_t endTimeNs = getElapsedRealtimeNs();
+    int64_t actualPullDurationNs = endTimeNs - startTimeNs;
+
+    // Pull should take at least the timeout amount of time, but should stop early because the delay
+    // is bigger.
+    EXPECT_LT(pullTimeoutNs, actualPullDurationNs);
+    EXPECT_GT(pullDelayNs, actualPullDurationNs);
+    EXPECT_EQ(0, dataHolder.size());
+
+    // Let the pull return and make sure that the dataHolder is not modified.
+    pullThread.join();
+    EXPECT_EQ(0, dataHolder.size());
+}
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
diff --git a/cmds/statsd/tests/external/StatsPuller_test.cpp b/cmds/statsd/tests/external/StatsPuller_test.cpp
index 76e2097..c40719a 100644
--- a/cmds/statsd/tests/external/StatsPuller_test.cpp
+++ b/cmds/statsd/tests/external/StatsPuller_test.cpp
@@ -35,6 +35,7 @@
 using std::this_thread::sleep_for;
 using testing::Contains;
 
+namespace {
 // cooldown time 1sec.
 int pullTagId = 10014;
 
@@ -76,7 +77,9 @@
     }
 };
 
-TEST_F(StatsPullerTest, PullSucces) {
+}  // Anonymous namespace.
+
+TEST_F(StatsPullerTest, PullSuccess) {
     pullData.push_back(createSimpleEvent(1111L, 33));
 
     pullSuccess = true;
diff --git a/cmds/uiautomator/library/Android.bp b/cmds/uiautomator/library/Android.bp
index 1173d57..3a26063 100644
--- a/cmds/uiautomator/library/Android.bp
+++ b/cmds/uiautomator/library/Android.bp
@@ -22,6 +22,7 @@
         "android.test.runner",
         "junit",
         "android.test.base",
+        "unsupportedappusage",
     ],
     custom_template: "droiddoc-templates-sdk",
     installable: false,
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 5032f77..f2fa4fd 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -2008,7 +2008,7 @@
             false, // WRITE_MEDIA_VIDEO
             false, // READ_MEDIA_IMAGES
             false, // WRITE_MEDIA_IMAGES
-            false, // LEGACY_STORAGE
+            true,  // LEGACY_STORAGE
             false, // ACCESS_ACCESSIBILITY
             false, // READ_DEVICE_IDENTIFIERS
             false, // ACCESS_MEDIA_LOCATION
diff --git a/core/java/android/app/IBackupAgent.aidl b/core/java/android/app/IBackupAgent.aidl
index d3d7c68..254657e 100644
--- a/core/java/android/app/IBackupAgent.aidl
+++ b/core/java/android/app/IBackupAgent.aidl
@@ -84,6 +84,19 @@
             long appVersionCode, in ParcelFileDescriptor newState,
             int token, IBackupManager callbackBinder);
 
+     /**
+     * Restore an entire data snapshot to the application and pass the list of excluded keys to the
+     * backup agent.
+     *
+     * @param excludedKeys List of keys to be excluded from the restore. It will be passed to the
+     *        backup agent to make it aware of what data has been removed (in case it has any
+     *        application-level implications) as well as the data that should be removed by the
+     *        agent itself.
+     */
+    void doRestoreWithExcludedKeys(in ParcelFileDescriptor data,
+            long appVersionCode, in ParcelFileDescriptor newState,
+            int token, IBackupManager callbackBinder, in List<String> excludedKeys);
+
     /**
      * Perform a "full" backup to the given file descriptor.  The output file is presumed
      * to be a socket or other non-seekable, write-only data sink.  When this method is
diff --git a/core/java/android/app/backup/BackupAgent.java b/core/java/android/app/backup/BackupAgent.java
index 24580b4..20aa064 100644
--- a/core/java/android/app/backup/BackupAgent.java
+++ b/core/java/android/app/backup/BackupAgent.java
@@ -45,7 +45,10 @@
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
+import java.util.Collections;
+import java.util.HashSet;
 import java.util.LinkedList;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.CountDownLatch;
@@ -327,6 +330,28 @@
     }
 
     /**
+     * New version of {@link #onRestore(BackupDataInput, long, android.os.ParcelFileDescriptor)}
+     * that has a list of keys to be excluded from the restore. Key/value pairs for which the key
+     * is present in {@code excludedKeys} have already been excluded from the restore data by the
+     * system. The list is passed to the agent to make it aware of what data has been removed (in
+     * case it has any application-level consequences) as well as the data that should be removed
+     * by the agent itself.
+     *
+     * The default implementation calls {@link #onRestore(BackupDataInput, long,
+     * android.os.ParcelFileDescriptor)}.
+     *
+     * @param excludedKeys A list of keys to be excluded from restore.
+     *
+     * @hide
+     */
+    public void onRestore(BackupDataInput data, long appVersionCode,
+            ParcelFileDescriptor newState,
+            Set<String> excludedKeys)
+            throws IOException {
+        onRestore(data, appVersionCode, newState);
+    }
+
+    /**
      * The application is having its entire file system contents backed up.  {@code data}
      * points to the backup destination, and the app has the opportunity to choose which
      * files are to be stored.  To commit a file as part of the backup, call the
@@ -1016,8 +1041,22 @@
 
         @Override
         public void doRestore(ParcelFileDescriptor data, long appVersionCode,
-                ParcelFileDescriptor newState,
-                int token, IBackupManager callbackBinder) throws RemoteException {
+                ParcelFileDescriptor newState, int token, IBackupManager callbackBinder)
+                throws RemoteException {
+            doRestoreInternal(data, appVersionCode, newState, token, callbackBinder,
+                    /* excludedKeys */ null);
+        }
+
+        @Override
+        public void doRestoreWithExcludedKeys(ParcelFileDescriptor data, long appVersionCode,
+                ParcelFileDescriptor newState, int token, IBackupManager callbackBinder,
+                List<String> excludedKeys) throws RemoteException {
+            doRestoreInternal(data, appVersionCode, newState, token, callbackBinder, excludedKeys);
+        }
+
+        private void doRestoreInternal(ParcelFileDescriptor data, long appVersionCode,
+                ParcelFileDescriptor newState, int token, IBackupManager callbackBinder,
+                List<String> excludedKeys) throws RemoteException {
             // Ensure that we're running with the app's normal permission level
             long ident = Binder.clearCallingIdentity();
 
@@ -1029,7 +1068,9 @@
 
             BackupDataInput input = new BackupDataInput(data.getFileDescriptor());
             try {
-                BackupAgent.this.onRestore(input, appVersionCode, newState);
+                BackupAgent.this.onRestore(input, appVersionCode, newState,
+                        excludedKeys != null ? new HashSet<>(excludedKeys)
+                                : Collections.emptySet());
             } catch (IOException ex) {
                 Log.d(TAG, "onRestore (" + BackupAgent.this.getClass().getName() + ") threw", ex);
                 throw new RuntimeException(ex);
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index d370a38..7b580c3 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -4973,7 +4973,7 @@
      * {@link android.content.pm.DataLoaderManager}.
      * @hide
      */
-    public static final String DATA_LOADER_MANAGER_SERVICE = "dataloadermanager";
+    public static final String DATA_LOADER_MANAGER_SERVICE = "dataloader_manager";
 
     /**
      * Use with {@link #getSystemService(String)} to retrieve an
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index 090629f..fa5ad39 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -452,6 +452,9 @@
         if ((diff & ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE) != 0) {
             list.add("CONFIG_SMALLEST_SCREEN_SIZE");
         }
+        if ((diff & ActivityInfo.CONFIG_DENSITY) != 0) {
+            list.add("CONFIG_DENSITY");
+        }
         if ((diff & ActivityInfo.CONFIG_LAYOUT_DIRECTION) != 0) {
             list.add("CONFIG_LAYOUT_DIRECTION");
         }
@@ -461,6 +464,9 @@
         if ((diff & ActivityInfo.CONFIG_ASSETS_PATHS) != 0) {
             list.add("CONFIG_ASSETS_PATHS");
         }
+        if ((diff & ActivityInfo.CONFIG_WINDOW_CONFIGURATION) != 0) {
+            list.add("CONFIG_WINDOW_CONFIGURATION");
+        }
         StringBuilder builder = new StringBuilder("{");
         for (int i = 0, n = list.size(); i < n; i++) {
             builder.append(list.get(i));
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 2698c2d..c698267 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -1859,6 +1859,7 @@
             mResId = other.mResId == null ? null : other.mResId.clone();
             mForce = other.mForce == null ? null : other.mForce.clone();
             mCount = other.mCount;
+            mHashCode = other.mHashCode;
         }
 
         @Override
diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java
index 84489cf..e4a4a1a 100644
--- a/core/java/android/content/res/ResourcesImpl.java
+++ b/core/java/android/content/res/ResourcesImpl.java
@@ -344,7 +344,7 @@
         try {
             return mAssets.openNonAssetFd(tempValue.assetCookie, tempValue.string.toString());
         } catch (Exception e) {
-            throw new NotFoundException("File " + tempValue.string.toString() + " from drawable "
+            throw new NotFoundException("File " + tempValue.string.toString() + " from "
                     + "resource ID #0x" + Integer.toHexString(id), e);
         }
     }
@@ -359,7 +359,7 @@
             // Note: value.string might be null
             NotFoundException rnf = new NotFoundException("File "
                     + (value.string == null ? "(null)" : value.string.toString())
-                    + " from drawable resource ID #0x" + Integer.toHexString(id));
+                    + " from resource ID #0x" + Integer.toHexString(id));
             rnf.initCause(e);
             throw rnf;
         }
diff --git a/core/java/android/os/storage/StorageManagerInternal.java b/core/java/android/os/storage/StorageManagerInternal.java
index 14c299d..5cac5f5 100644
--- a/core/java/android/os/storage/StorageManagerInternal.java
+++ b/core/java/android/os/storage/StorageManagerInternal.java
@@ -109,4 +109,13 @@
      */
     public abstract void onAppOpsChanged(int code, int uid,
             @Nullable String packageName, int mode);
+
+    /**
+     * Asks the StorageManager to reset all state for the provided user; this will result
+     * in the unmounting for all volumes of the user, and, if the user is still running, the
+     * volumes will be re-mounted as well.
+     *
+     * @param userId the userId for which to reset storage
+     */
+    public abstract void resetUser(int userId);
 }
diff --git a/core/java/android/provider/DeviceConfig.java b/core/java/android/provider/DeviceConfig.java
index abf34ca..9dc6806 100644
--- a/core/java/android/provider/DeviceConfig.java
+++ b/core/java/android/provider/DeviceConfig.java
@@ -357,6 +357,13 @@
     @TestApi
     public static final String NAMESPACE_PERMISSIONS = "permissions";
 
+    /**
+     * Namespace for all widget related features.
+     *
+     * @hide
+     */
+    public static final String NAMESPACE_WIDGET = "widget";
+
     private static final Object sLock = new Object();
     @GuardedBy("sLock")
     private static ArrayMap<OnPropertiesChangedListener, Pair<String, Executor>> sListeners =
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index ad8d553..165c284 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -648,6 +648,22 @@
             "android.settings.NIGHT_DISPLAY_SETTINGS";
 
     /**
+     * Activity Action: Show settings to allow configuration of Dark theme.
+     * <p>
+     * In some cases, a matching Activity may not exist, so ensure you
+     * safeguard against this.
+     * <p>
+     * Input: Nothing.
+     * <p>
+     * Output: Nothing.
+     *
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_DARK_THEME_SETTINGS =
+            "android.settings.DARK_THEME_SETTINGS";
+
+    /**
      * Activity Action: Show settings to allow configuration of locale.
      * <p>
      * In some cases, a matching Activity may not exist, so ensure you
diff --git a/core/java/android/service/incremental/IncrementalDataLoaderService.java b/core/java/android/service/incremental/IncrementalDataLoaderService.java
new file mode 100644
index 0000000..c4a06c8
--- /dev/null
+++ b/core/java/android/service/incremental/IncrementalDataLoaderService.java
@@ -0,0 +1,563 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.incremental;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.Service;
+import android.content.Intent;
+import android.content.pm.IDataLoader;
+import android.content.pm.IDataLoaderStatusListener;
+import android.content.pm.InstallationFile;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.incremental.IncrementalDataLoaderParams;
+import android.os.incremental.IncrementalDataLoaderParamsParcel;
+import android.os.incremental.IncrementalFileSystemControlParcel;
+import android.os.incremental.NamedParcelFileDescriptor;
+import android.util.Slog;
+
+import java.io.IOException;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Collection;
+import java.util.List;
+
+
+/**
+ * The base class for implementing data loader service to control data loaders. Expecting
+ * Incremental Service to bind to a children class of this.
+ *
+ * @hide
+ *
+ * Hide for now, should be @SystemApi
+ * TODO(b/136132412): update with latest API design
+ */
+public abstract class IncrementalDataLoaderService extends Service {
+    private static final String TAG = "IncrementalDataLoaderService";
+    private final DataLoaderBinderService mBinder = new DataLoaderBinderService();
+
+    public static final int DATA_LOADER_READY =
+            IDataLoaderStatusListener.DATA_LOADER_READY;
+    public static final int DATA_LOADER_NOT_READY =
+            IDataLoaderStatusListener.DATA_LOADER_NOT_READY;
+    public static final int DATA_LOADER_RUNNING =
+            IDataLoaderStatusListener.DATA_LOADER_RUNNING;
+    public static final int DATA_LOADER_STOPPED =
+            IDataLoaderStatusListener.DATA_LOADER_STOPPED;
+    public static final int DATA_LOADER_SLOW_CONNECTION =
+            IDataLoaderStatusListener.DATA_LOADER_SLOW_CONNECTION;
+    public static final int DATA_LOADER_NO_CONNECTION =
+            IDataLoaderStatusListener.DATA_LOADER_NO_CONNECTION;
+    public static final int DATA_LOADER_CONNECTION_OK =
+            IDataLoaderStatusListener.DATA_LOADER_CONNECTION_OK;
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"DATA_LOADER_"}, value = {
+            DATA_LOADER_READY,
+            DATA_LOADER_NOT_READY,
+            DATA_LOADER_RUNNING,
+            DATA_LOADER_STOPPED,
+            DATA_LOADER_SLOW_CONNECTION,
+            DATA_LOADER_NO_CONNECTION,
+            DATA_LOADER_CONNECTION_OK
+    })
+    public @interface DataLoaderStatus {
+    }
+
+    /**
+     * Incremental FileSystem block size.
+     **/
+    public static final int BLOCK_SIZE = 4096;
+
+    /**
+     * Data compression types
+     */
+    public static final int COMPRESSION_NONE = 0;
+    public static final int COMPRESSION_LZ4 = 1;
+
+    /**
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({COMPRESSION_NONE, COMPRESSION_LZ4})
+    public @interface CompressionType {
+    }
+
+    /**
+     * Managed DataLoader interface. Each instance corresponds to a single Incremental File System
+     * instance.
+     */
+    public abstract static class DataLoader {
+        /**
+         * A virtual constructor used to do simple initialization. Not ready to serve any data yet.
+         * All heavy-lifting has to be done in onStart.
+         *
+         * @param params    Data loader configuration parameters.
+         * @param connector IncFS API wrapper.
+         * @param listener  Used for reporting internal state to IncrementalService.
+         * @return True if initialization of a Data Loader was successful. False will be reported to
+         * IncrementalService and can cause an unmount of an IFS instance.
+         */
+        public abstract boolean onCreate(@NonNull IncrementalDataLoaderParams params,
+                @NonNull FileSystemConnector connector,
+                @NonNull StatusListener listener);
+
+        /**
+         * Start the data loader. After this method returns data loader is considered to be ready to
+         * receive callbacks from IFS, supply data via connector and send status updates via
+         * callbacks.
+         *
+         * @return True if Data Loader was able to start. False will be reported to
+         * IncrementalService and can cause an unmount of an IFS instance.
+         */
+        public abstract boolean onStart();
+
+        /**
+         * Stop the data loader. Use to stop any additional threads and free up resources. Data
+         * loader is not longer responsible for supplying data. Start/Stop pair can be called
+         * multiple times e.g. if IFS detects corruption and data needs to be re-loaded.
+         */
+        public abstract void onStop();
+
+        /**
+         * Virtual destructor. Use to cleanup all internal state. After this method returns, the
+         * data loader can no longer use connector or callbacks. For any additional operations with
+         * this instance of IFS a new DataLoader will be created using createDataLoader method.
+         */
+        public abstract void onDestroy();
+
+        /**
+         * IFS reports a pending read each time the page needs to be loaded, e.g. missing.
+         *
+         * @param pendingReads array of blocks to load.
+         *
+         * TODO(b/136132412): avoid using collections
+         */
+        public abstract void onPendingReads(
+                @NonNull Collection<FileSystemConnector.PendingReadInfo> pendingReads);
+
+        /**
+         * IFS tracks all reads and reports them using onPageReads.
+         *
+         * @param reads array of blocks.
+         *
+         * TODO(b/136132412): avoid using collections
+         */
+        public abstract void onPageReads(@NonNull Collection<FileSystemConnector.ReadInfo> reads);
+
+        /**
+         * IFS informs data loader that a new file has been created.
+         * <p>
+         * This can be used to prepare the data loader before it starts loading data. For example,
+         * the data loader can keep a list of newly created files, so that it knows what files to
+         * download from the server.
+         *
+         * @param inode    The inode value of the new file.
+         * @param metadata The metadata of the new file.
+         */
+        public abstract void onFileCreated(long inode, byte[] metadata);
+    }
+
+    /**
+     * DataLoader factory method.
+     *
+     * @return An instance of a DataLoader.
+     */
+    public abstract @Nullable DataLoader onCreateDataLoader();
+
+    /**
+     * @hide
+     */
+    public final @NonNull IBinder onBind(@NonNull Intent intent) {
+        return (IBinder) mBinder;
+    }
+
+    private class DataLoaderBinderService extends IDataLoader.Stub {
+        private int mId;
+
+        @Override
+        public void create(int id, @NonNull Bundle options,
+                @NonNull IDataLoaderStatusListener listener)
+                    throws IllegalArgumentException, RuntimeException {
+            mId = id;
+            final IncrementalDataLoaderParamsParcel params =  options.getParcelable("params");
+            if (params == null) {
+                throw new IllegalArgumentException("Must specify Incremental data loader params");
+            }
+            final IncrementalFileSystemControlParcel control =
+                    options.getParcelable("control");
+            if (control == null) {
+                throw new IllegalArgumentException("Must specify Incremental control parcel");
+            }
+            mStatusListener = listener;
+            try {
+                if (!nativeCreateDataLoader(id, control, params, listener)) {
+                    Slog.e(TAG, "Failed to create native loader for " + mId);
+                }
+            } catch (Exception ex) {
+                destroy();
+                throw new RuntimeException(ex);
+            } finally {
+                // Closing FDs.
+                if (control.cmd != null) {
+                    try {
+                        control.cmd.close();
+                    } catch (IOException e) {
+                        Slog.e(TAG, "Failed to close IncFs CMD file descriptor " + e);
+                    }
+                }
+                if (control.log != null) {
+                    try {
+                        control.log.close();
+                    } catch (IOException e) {
+                        Slog.e(TAG, "Failed to close IncFs LOG file descriptor " + e);
+                    }
+                }
+                NamedParcelFileDescriptor[] fds = params.dynamicArgs;
+                for (NamedParcelFileDescriptor nfd : fds) {
+                    try {
+                        nfd.fd.close();
+                    } catch (IOException e) {
+                        Slog.e(TAG,
+                                "Failed to close DynamicArgs parcel file descriptor " + e);
+                    }
+                }
+            }
+        }
+
+        @Override
+        public void start(List<InstallationFile> fileInfos) {
+            if (!nativeStartDataLoader(mId)) {
+                Slog.e(TAG, "Failed to start loader: loader not found for " + mId);
+            }
+        }
+
+        @Override
+        public void stop() {
+            if (!nativeStopDataLoader(mId)) {
+                Slog.w(TAG, "Failed to stop loader: loader not found for " + mId);
+            }
+        }
+
+        @Override
+        public void destroy() {
+            if (!nativeDestroyDataLoader(mId)) {
+                Slog.w(TAG, "Failed to destroy loader: loader not found for " + mId);
+            }
+        }
+
+        @Override
+        // TODO(b/136132412): remove this
+        public void onFileCreated(long inode, byte[] metadata) {
+            if (!nativeOnFileCreated(mId, inode, metadata)) {
+                Slog.w(TAG, "Failed to handle onFileCreated for storage:" + mId
+                        + " inode:" + inode);
+            }
+        }
+    }
+
+    /**
+     * IncFs API wrapper for writing pages and getting page missing info. Non-hidden methods are
+     * expected to be called by the IncrementalDataLoaderService implemented by developers.
+     *
+     * @hide
+     *
+     * TODO(b/136132412) Should be @SystemApi
+     */
+    public static final class FileSystemConnector {
+        /**
+         * Defines a block address. A block is the unit of data chunk that IncFs operates with.
+         *
+         * @hide
+         */
+        public static class BlockAddress {
+            /**
+             * Linux inode uniquely identifies file within a single IFS instance.
+             */
+            private final long mFileIno;
+            /**
+             * Index of a 4K block within a file.
+             */
+            private final int mBlockIndex;
+
+            public BlockAddress(long fileIno, int blockIndex) {
+                this.mFileIno = fileIno;
+                this.mBlockIndex = blockIndex;
+            }
+
+            public long getFileIno() {
+                return mFileIno;
+            }
+
+            public int getBlockIndex() {
+                return mBlockIndex;
+            }
+        }
+
+        /**
+         * A block is the unit of data chunk that IncFs operates with.
+         *
+         * @hide
+         */
+        public static class Block extends BlockAddress {
+            /**
+             * Data content of the block.
+             */
+            private final @NonNull byte[] mDataBytes;
+
+            public Block(long fileIno, int blockIndex, @NonNull byte[] dataBytes) {
+                super(fileIno, blockIndex);
+                this.mDataBytes = dataBytes;
+            }
+        }
+
+        /**
+         * Defines a page/block inside a file.
+         */
+        public static class DataBlock extends Block {
+            /**
+             * Compression type of the data block.
+             */
+            private final @CompressionType int mCompressionType;
+
+            public DataBlock(long fileIno, int blockIndex, @NonNull byte[] dataBytes,
+                    @CompressionType int compressionType) {
+                super(fileIno, blockIndex, dataBytes);
+                this.mCompressionType = compressionType;
+            }
+        }
+
+        /**
+         * Defines a hash block for a certain file. A hash block index is the index in an array of
+         * hashes which is the 1-d representation of the hash tree. One DataBlock might be
+         * associated with multiple HashBlocks.
+         */
+        public static class HashBlock extends Block {
+            public HashBlock(long fileIno, int blockIndex, @NonNull byte[] dataBytes) {
+                super(fileIno, blockIndex, dataBytes);
+            }
+        }
+
+        /**
+         * Information about a page that is pending to be read.
+         */
+        public static class PendingReadInfo extends BlockAddress {
+            PendingReadInfo(long fileIno, int blockIndex) {
+                super(fileIno, blockIndex);
+            }
+        }
+
+        /**
+         * Information about a page that is read.
+         */
+        public static class ReadInfo extends BlockAddress {
+            /**
+             * A monotonically increasing read timestamp.
+             */
+            private final long mTimePoint;
+            /**
+             * Number of blocks read starting from blockIndex.
+             */
+            private final int mBlockCount;
+
+            ReadInfo(long timePoint, long fileIno, int firstBlockIndex, int blockCount) {
+                super(fileIno, firstBlockIndex);
+                this.mTimePoint = timePoint;
+                this.mBlockCount = blockCount;
+            }
+
+            public long getTimePoint() {
+                return mTimePoint;
+            }
+
+            public int getBlockCount() {
+                return mBlockCount;
+            }
+        }
+
+        /**
+         * Defines the dynamic information about an IncFs file.
+         */
+        public static class FileInfo {
+            /**
+             * BitSet to show if any block is available at each block index.
+             */
+            private final @NonNull
+            byte[] mBlockBitmap;
+
+            /**
+             * @hide
+             */
+            public FileInfo(@NonNull byte[] blockBitmap) {
+                this.mBlockBitmap = blockBitmap;
+            }
+        }
+
+        /**
+         * Creates a wrapper for a native instance.
+         */
+        FileSystemConnector(long nativeInstance) {
+            mNativeInstance = nativeInstance;
+        }
+
+        /**
+         * Checks whether a range in a file if loaded.
+         *
+         * @param node  inode of the file.
+         * @param start The starting offset of the range.
+         * @param end   The ending offset of the range.
+         * @return True if the file is fully loaded.
+         */
+        public boolean isFileRangeLoaded(long node, long start, long end) {
+            return nativeIsFileRangeLoadedNode(mNativeInstance, node, start, end);
+        }
+
+        /**
+         * Gets the metadata of a file.
+         *
+         * @param node inode of the file.
+         * @return The metadata object.
+         */
+        @NonNull
+        public byte[] getFileMetadata(long node) throws IOException {
+            final byte[] metadata = nativeGetFileMetadataNode(mNativeInstance, node);
+            if (metadata == null || metadata.length == 0) {
+                throw new IOException(
+                        "IncrementalFileSystem failed to obtain metadata for node: " + node);
+            }
+            return metadata;
+        }
+
+        /**
+         * Gets the dynamic information of a file, such as page bitmaps. Can be used to get missing
+         * page indices by the FileSystemConnector.
+         *
+         * @param node inode of the file.
+         * @return Dynamic file info.
+         */
+        @NonNull
+        public FileInfo getDynamicFileInfo(long node) throws IOException {
+            final byte[] blockBitmap = nativeGetFileInfoNode(mNativeInstance, node);
+            if (blockBitmap == null || blockBitmap.length == 0) {
+                throw new IOException(
+                        "IncrementalFileSystem failed to obtain dynamic file info for node: "
+                                + node);
+            }
+            return new FileInfo(blockBitmap);
+        }
+
+        /**
+         * Writes a page's data and/or hashes.
+         *
+         * @param dataBlocks the DataBlock objects that contain data block index and data bytes.
+         * @param hashBlocks the HashBlock objects that contain hash indices and hash bytes.
+         *
+         * TODO(b/136132412): change API to avoid dynamic allocation of data block objects
+         */
+        public void writeMissingData(@NonNull DataBlock[] dataBlocks,
+                @Nullable HashBlock[] hashBlocks) throws IOException {
+            if (!nativeWriteMissingData(mNativeInstance, dataBlocks, hashBlocks)) {
+                throw new IOException("IncrementalFileSystem failed to write missing data.");
+            }
+        }
+
+        /**
+         * Writes the signer block of a file. Expecting the connector to call this when it got
+         * signing data from data loader.
+         *
+         * @param node       the file to be written to.
+         * @param signerData the raw signer data byte array.
+         */
+        public void writeSignerData(long node, @NonNull byte[] signerData)
+                throws IOException {
+            if (!nativeWriteSignerDataNode(mNativeInstance, node, signerData)) {
+                throw new IOException(
+                        "IncrementalFileSystem failed to write signer data of node " + node);
+            }
+        }
+
+        private final long mNativeInstance;
+    }
+
+    /**
+     * Wrapper for native reporting DataLoader statuses.
+     *
+     * @hide
+     *
+     * TODO(b/136132412) Should be @SystemApi
+     */
+    public static final class StatusListener {
+        /**
+         * Creates a wrapper for a native instance.
+         *
+         * @hide
+         */
+        StatusListener(long nativeInstance) {
+            mNativeInstance = nativeInstance;
+        }
+
+        /**
+         * Report the status of DataLoader. Used for system-wide notifications e.g., disabling
+         * applications which rely on this data loader to function properly.
+         *
+         * @param status status to report.
+         * @return True if status was reported successfully.
+         */
+        public boolean onStatusChanged(@DataLoaderStatus int status) {
+            return nativeReportStatus(mNativeInstance, status);
+        }
+
+        private final long mNativeInstance;
+    }
+
+    private IDataLoaderStatusListener mStatusListener = null;
+
+    /* Native methods */
+    private native boolean nativeCreateDataLoader(int storageId,
+            @NonNull IncrementalFileSystemControlParcel control,
+            @NonNull IncrementalDataLoaderParamsParcel params,
+            IDataLoaderStatusListener listener);
+
+    private native boolean nativeStartDataLoader(int storageId);
+
+    private native boolean nativeStopDataLoader(int storageId);
+
+    private native boolean nativeDestroyDataLoader(int storageId);
+
+    private static native boolean nativeOnFileCreated(int storageId,
+            long inode, byte[] metadata);
+
+    private static native boolean nativeIsFileRangeLoadedNode(
+            long nativeInstance, long node, long start, long end);
+
+    private static native boolean nativeWriteMissingData(
+            long nativeInstance, FileSystemConnector.DataBlock[] dataBlocks,
+            FileSystemConnector.HashBlock[] hashBlocks);
+
+    private static native boolean nativeWriteSignerDataNode(
+            long nativeInstance, long node, byte[] signerData);
+
+    private static native byte[] nativeGetFileMetadataNode(
+            long nativeInstance, long node);
+
+    private static native byte[] nativeGetFileInfoNode(
+            long nativeInstance, long node);
+
+    private static native boolean nativeReportStatus(long nativeInstance, int status);
+}
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index d40f832..62a824d 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -1106,6 +1106,13 @@
         public static final int TYPE_APPLICATION_OVERLAY = FIRST_SYSTEM_WINDOW + 38;
 
         /**
+         * Window type: Window for adding accessibility window magnification above other windows.
+         * This will place the window in the overlay windows.
+         * @hide
+         */
+        public static final int TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY = FIRST_SYSTEM_WINDOW + 39;
+
+        /**
          * End of types of system windows.
          */
         public static final int LAST_SYSTEM_WINDOW      = 2999;
diff --git a/core/java/android/view/textclassifier/SelectionEvent.java b/core/java/android/view/textclassifier/SelectionEvent.java
index 12ed4b9..7d1077e 100644
--- a/core/java/android/view/textclassifier/SelectionEvent.java
+++ b/core/java/android/view/textclassifier/SelectionEvent.java
@@ -161,7 +161,6 @@
         mEntityType = in.readString();
         mWidgetVersion = in.readInt() > 0 ? in.readString() : null;
         mPackageName = in.readString();
-        mUserId = in.readInt();
         mWidgetType = in.readString();
         mInvocationMethod = in.readInt();
         mResultId = in.readString();
@@ -175,6 +174,7 @@
         mEnd = in.readInt();
         mSmartStart = in.readInt();
         mSmartEnd = in.readInt();
+        mUserId = in.readInt();
     }
 
     @Override
@@ -188,7 +188,6 @@
             dest.writeString(mWidgetVersion);
         }
         dest.writeString(mPackageName);
-        dest.writeInt(mUserId);
         dest.writeString(mWidgetType);
         dest.writeInt(mInvocationMethod);
         dest.writeString(mResultId);
@@ -204,6 +203,7 @@
         dest.writeInt(mEnd);
         dest.writeInt(mSmartStart);
         dest.writeInt(mSmartEnd);
+        dest.writeInt(mUserId);
     }
 
     @Override
diff --git a/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
new file mode 100644
index 0000000..2fd5bfd
--- /dev/null
+++ b/core/java/com/android/internal/app/AbstractMultiProfilePagerAdapter.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.internal.app;
+import android.annotation.IntDef;
+import android.content.Context;
+import android.os.UserHandle;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.widget.PagerAdapter;
+
+import com.android.internal.util.Preconditions;
+import com.android.internal.widget.ViewPager;
+
+/**
+ * Skeletal {@link PagerAdapter} implementation of a work or personal profile page for
+ * intent resolution (including share sheet).
+ */
+public abstract class AbstractMultiProfilePagerAdapter extends PagerAdapter {
+
+    static final int PROFILE_PERSONAL = 0;
+    static final int PROFILE_WORK = 1;
+    @IntDef({PROFILE_PERSONAL, PROFILE_WORK})
+    @interface Profile {}
+
+    private final Context mContext;
+    private int mCurrentPage;
+
+    AbstractMultiProfilePagerAdapter(Context context, int currentPage) {
+        mContext = Preconditions.checkNotNull(context);
+        mCurrentPage = currentPage;
+    }
+
+    Context getContext() {
+        return mContext;
+    }
+
+    /**
+     * Sets this instance of this class as {@link ViewPager}'s {@link PagerAdapter} and sets
+     * an {@link ViewPager.OnPageChangeListener} where it keeps track of the currently displayed
+     * page and rebuilds the list.
+     */
+    void setupViewPager(ViewPager viewPager) {
+        viewPager.setCurrentItem(mCurrentPage);
+        viewPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
+            @Override
+            public void onPageSelected(int position) {
+                mCurrentPage = position;
+                getCurrentListAdapter().rebuildList();
+            }
+        });
+        viewPager.setAdapter(this);
+    }
+
+    @Override
+    public ViewGroup instantiateItem(ViewGroup container, int position) {
+        final ProfileDescriptor profileDescriptor = getItem(position);
+        setupListAdapter(position);
+        container.addView(profileDescriptor.rootView);
+        return profileDescriptor.rootView;
+    }
+
+    @Override
+    public void destroyItem(ViewGroup container, int position, Object view) {
+        container.removeView((View) view);
+    }
+
+    @Override
+    public int getCount() {
+        return getItemCount();
+    }
+
+    protected int getCurrentPage() {
+        return mCurrentPage;
+    }
+
+    UserHandle getCurrentUserHandle() {
+        return getCurrentListAdapter().mResolverListController.getUserHandle();
+    }
+
+    @Override
+    public boolean isViewFromObject(View view, Object object) {
+        return view == object;
+    }
+
+    @Override
+    public CharSequence getPageTitle(int position) {
+        return null;
+    }
+
+    /**
+     * Returns the {@link ProfileDescriptor} relevant to the given <code>pageIndex</code>.
+     * <ul>
+     * <li>For a device with only one user, <code>pageIndex</code> value of
+     * <code>0</code> would return the personal profile {@link ProfileDescriptor}.</li>
+     * <li>For a device with a work profile, <code>pageIndex</code> value of <code>0</code> would
+     * return the personal profile {@link ProfileDescriptor}, and <code>pageIndex</code> value of
+     * <code>1</code> would return the work profile {@link ProfileDescriptor}.</li>
+     * </ul>
+     */
+    abstract ProfileDescriptor getItem(int pageIndex);
+
+    /**
+     * Returns the number of {@link ProfileDescriptor} objects.
+     * <p>For a normal consumer device with only one user returns <code>1</code>.
+     * <p>For a device with a work profile returns <code>2</code>.
+     */
+    abstract int getItemCount();
+
+    /**
+     * Responsible for assigning an adapter to the list view for the relevant page, specified by
+     * <code>pageIndex</code>, and other list view-related initialization procedures.
+     */
+    abstract void setupListAdapter(int pageIndex);
+
+    /**
+     * Returns the adapter of the list view for the relevant page specified by
+     * <code>pageIndex</code>.
+     * <p>This method is meant to be implemented with an implementation-specific return type
+     * depending on the adapter type.
+     */
+    abstract Object getAdapterForIndex(int pageIndex);
+
+    @VisibleForTesting
+    public abstract ResolverListAdapter getCurrentListAdapter();
+
+    abstract Object getCurrentRootAdapter();
+
+    abstract ViewGroup getCurrentAdapterView();
+
+    protected class ProfileDescriptor {
+        final ViewGroup rootView;
+        ProfileDescriptor(ViewGroup rootView) {
+            this.rootView = rootView;
+        }
+    }
+}
\ No newline at end of file
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index 183e9a6..1af3926 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -98,6 +98,7 @@
 import android.view.View.OnLongClickListener;
 import android.view.ViewGroup;
 import android.view.ViewGroup.LayoutParams;
+import android.view.WindowInsets;
 import android.view.animation.AccelerateInterpolator;
 import android.view.animation.DecelerateInterpolator;
 import android.widget.ImageView;
@@ -173,7 +174,6 @@
 
     @VisibleForTesting
     public static final int LIST_VIEW_UPDATE_INTERVAL_IN_MILLIS = 250;
-    private static final int SINGLE_CELL_SPAN_SIZE = 1;
 
     private boolean mIsAppPredictorComponentAvailable;
     private AppPredictor mAppPredictor;
@@ -229,9 +229,6 @@
     private long mQueriedTargetServicesTimeMs;
     private long mQueriedSharingShortcutsTimeMs;
 
-    private RecyclerView mRecyclerView;
-    private ChooserListAdapter mChooserListAdapter;
-    private ChooserGridAdapter mChooserGridAdapter;
     private int mChooserRowServiceSpacing;
 
     private int mCurrAvailableWidth = 0;
@@ -265,6 +262,9 @@
 
     private ContentPreviewCoordinator mPreviewCoord;
 
+    @VisibleForTesting
+    protected ChooserMultiProfilePagerAdapter mChooserMultiProfilePagerAdapter;
+
     private class ContentPreviewCoordinator {
         private static final int IMAGE_FADE_IN_MILLIS = 150;
         private static final int IMAGE_LOAD_TIMEOUT = 1;
@@ -362,8 +362,8 @@
                 Log.i(TAG, "Hiding image preview area. Timed out waiting for preview to load"
                         + " within " + mImageLoadTimeoutMillis + "ms.");
                 collapseParentView();
-                if (mChooserGridAdapter != null) {
-                    mChooserGridAdapter.hideContentPreview();
+                if (mChooserMultiProfilePagerAdapter.getCurrentRootAdapter() != null) {
+                    mChooserMultiProfilePagerAdapter.getCurrentRootAdapter().hideContentPreview();
                 }
                 mHideParentOnFail = false;
             }
@@ -431,13 +431,14 @@
                 logDirectShareTargetReceived(
                         MetricsEvent.ACTION_DIRECT_SHARE_TARGETS_LOADED_CHOOSER_SERVICE);
                 sendVoiceChoicesIfNeeded();
-                mChooserListAdapter.completeServiceTargetLoading();
+                mChooserMultiProfilePagerAdapter.getCurrentListAdapter()
+                        .completeServiceTargetLoading();
             }
         }
 
         @Override
         public void handleMessage(Message msg) {
-            if (mChooserListAdapter == null || isDestroyed()) {
+            if (mChooserMultiProfilePagerAdapter.getCurrentListAdapter() == null || isDestroyed()) {
                 return;
             }
 
@@ -452,8 +453,10 @@
                         break;
                     }
                     if (sri.resultTargets != null) {
-                        mChooserListAdapter.addServiceResults(sri.originalTarget,
-                                sri.resultTargets, TARGET_TYPE_CHOOSER_TARGET);
+                        // TODO(arangelov): Instead of using getCurrentListAdapter(), pass the
+                        // profileId as part of the message.
+                        mChooserMultiProfilePagerAdapter.getCurrentListAdapter().addServiceResults(
+                                sri.originalTarget, sri.resultTargets, TARGET_TYPE_CHOOSER_TARGET);
                     }
                     unbindService(sri.connection);
                     sri.connection.destroy();
@@ -476,15 +479,15 @@
                         Log.d(TAG, "LIST_VIEW_UPDATE_MESSAGE; ");
                     }
 
-                    mChooserListAdapter.refreshListView();
+                    mChooserMultiProfilePagerAdapter.getCurrentListAdapter().refreshListView();
                     break;
 
                 case SHORTCUT_MANAGER_SHARE_TARGET_RESULT:
                     if (DEBUG) Log.d(TAG, "SHORTCUT_MANAGER_SHARE_TARGET_RESULT");
                     final ServiceResultInfo resultInfo = (ServiceResultInfo) msg.obj;
                     if (resultInfo.resultTargets != null) {
-                        mChooserListAdapter.addServiceResults(resultInfo.originalTarget,
-                                resultInfo.resultTargets, msg.arg1);
+                        mChooserMultiProfilePagerAdapter.getCurrentListAdapter().addServiceResults(
+                                resultInfo.originalTarget, resultInfo.resultTargets, msg.arg1);
                     }
                     break;
 
@@ -504,6 +507,7 @@
     protected void onCreate(Bundle savedInstanceState) {
         final long intentReceivedTime = System.currentTimeMillis();
         // This is the only place this value is being set. Effectively final.
+        //TODO(arangelov) - should there be a mIsAppPredictorComponentAvailable flag for work tab?
         mIsAppPredictorComponentAvailable = isAppPredictionServiceAvailable();
 
         mIsSuccessfullySelected = false;
@@ -638,19 +642,24 @@
         if (appPredictor != null) {
             mDirectShareAppTargetCache = new HashMap<>();
             mAppPredictorCallback = resultList -> {
+                //TODO(arangelov) Take care of edge case when callback called after swiping tabs
                 if (isFinishing() || isDestroyed()) {
                     return;
                 }
-                if (mChooserListAdapter.getCount() == 0) {
+                if (mChooserMultiProfilePagerAdapter.getCurrentListAdapter().getCount() == 0) {
                     return;
                 }
                 if (resultList.isEmpty()) {
                     // APS may be disabled, so try querying targets ourselves.
-                    queryDirectShareTargets(mChooserListAdapter, true);
+                    //TODO(arangelov) queryDirectShareTargets indirectly uses mIntents.
+                    // Investigate implications for work tab.
+                    queryDirectShareTargets(
+                            mChooserMultiProfilePagerAdapter.getCurrentListAdapter(), true);
                     return;
                 }
                 final List<DisplayResolveInfo> driList =
-                        getDisplayResolveInfos(mChooserListAdapter);
+                        getDisplayResolveInfos(
+                                mChooserMultiProfilePagerAdapter.getCurrentListAdapter());
                 final List<ShortcutManager.ShareShortcutInfo> shareShortcutInfos =
                         new ArrayList<>();
                 for (AppTarget appTarget : resultList) {
@@ -684,21 +693,22 @@
             final float chooserHeaderScrollElevation =
                     getResources().getDimensionPixelSize(R.dimen.chooser_header_scroll_elevation);
 
-            mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
-                public void onScrollStateChanged(RecyclerView view, int scrollState) {
-                }
-
-                public void onScrolled(RecyclerView view, int dx, int dy) {
-                    if (view.getChildCount() > 0) {
-                        View child = view.getLayoutManager().findViewByPosition(0);
-                        if (child == null || child.getTop() < 0) {
-                            chooserHeader.setElevation(chooserHeaderScrollElevation);
-                            return;
+            mChooserMultiProfilePagerAdapter.getCurrentAdapterView().addOnScrollListener(
+                    new RecyclerView.OnScrollListener() {
+                        public void onScrollStateChanged(RecyclerView view, int scrollState) {
                         }
-                    }
 
-                    chooserHeader.setElevation(defaultElevation);
-                }
+                        public void onScrolled(RecyclerView view, int dx, int dy) {
+                            if (view.getChildCount() > 0) {
+                                View child = view.getLayoutManager().findViewByPosition(0);
+                                if (child == null || child.getTop() < 0) {
+                                    chooserHeader.setElevation(chooserHeaderScrollElevation);
+                                    return;
+                                }
+                            }
+
+                            chooserHeader.setElevation(defaultElevation);
+                        }
             });
 
             mResolverDrawerLayout.setOnCollapsedChangedListener(
@@ -738,6 +748,71 @@
         return context.getSharedPreferences(prefsFile, MODE_PRIVATE);
     }
 
+    @Override
+    protected AbstractMultiProfilePagerAdapter createMultiProfilePagerAdapter(
+            Intent[] initialIntents,
+            List<ResolveInfo> rList,
+            boolean filterLastUsed) {
+        if (hasWorkProfile() && ENABLE_TABBED_VIEW) {
+            mChooserMultiProfilePagerAdapter = createChooserMultiProfilePagerAdapterForTwoProfiles(
+                    initialIntents, rList, filterLastUsed);
+        } else {
+            mChooserMultiProfilePagerAdapter = createChooserMultiProfilePagerAdapterForOneProfile(
+                    initialIntents, rList, filterLastUsed);
+        }
+        return mChooserMultiProfilePagerAdapter;
+    }
+
+    private ChooserMultiProfilePagerAdapter createChooserMultiProfilePagerAdapterForOneProfile(
+            Intent[] initialIntents,
+            List<ResolveInfo> rList,
+            boolean filterLastUsed) {
+        ChooserGridAdapter adapter = createChooserGridAdapter(
+                /* context */ this,
+                /* payloadIntents */ mIntents,
+                initialIntents,
+                rList,
+                filterLastUsed,
+                mUseLayoutForBrowsables,
+                /* userHandle */ UserHandle.of(UserHandle.myUserId()));
+        return new ChooserMultiProfilePagerAdapter(
+                /* context */ this,
+                adapter);
+    }
+
+    private ChooserMultiProfilePagerAdapter createChooserMultiProfilePagerAdapterForTwoProfiles(
+            Intent[] initialIntents,
+            List<ResolveInfo> rList,
+            boolean filterLastUsed) {
+        ChooserGridAdapter personalAdapter = createChooserGridAdapter(
+                /* context */ this,
+                /* payloadIntents */ mIntents,
+                initialIntents,
+                rList,
+                filterLastUsed,
+                mUseLayoutForBrowsables,
+                /* userHandle */ getPersonalProfileUserHandle());
+        ChooserGridAdapter workAdapter = createChooserGridAdapter(
+                /* context */ this,
+                /* payloadIntents */ mIntents,
+                initialIntents,
+                rList,
+                filterLastUsed,
+                mUseLayoutForBrowsables,
+                /* userHandle */ getWorkProfileUserHandle());
+        return new ChooserMultiProfilePagerAdapter(
+                /* context */ this,
+                personalAdapter,
+                workAdapter,
+                /* defaultProfile */ getCurrentProfile());
+    }
+
+    @Override
+    protected boolean postRebuildList(boolean rebuildCompleted) {
+        mChooserMultiProfilePagerAdapter.getCurrentRootAdapter().maybeLogActionShareWithPreview();
+        return postRebuildListInternal(rebuildCompleted);
+    }
+
     /**
      * Returns true if app prediction service is defined and the component exists on device.
      */
@@ -776,7 +851,7 @@
      * set up)
      */
     protected boolean isWorkProfile() {
-        return ((UserManager) getSystemService(Context.USER_SERVICE))
+        return getSystemService(UserManager.class)
                 .getUserInfo(UserHandle.myUserId()).isManagedProfile();
     }
 
@@ -785,7 +860,9 @@
         return new PackageMonitor() {
             @Override
             public void onSomePackagesChanged() {
-                mAdapter.handlePackagesChanged();
+                // TODO(arangelov): Dispatch this to all adapters when we have the helper methods
+                // in a follow-up CL
+                mChooserMultiProfilePagerAdapter.getCurrentListAdapter().handlePackagesChanged();
                 updateProfileViewButton();
             }
         };
@@ -1239,36 +1316,14 @@
     }
 
     @Override
-    public void onPrepareAdapterView(ResolverListAdapter adapter, boolean isVisible) {
-        mRecyclerView = findViewById(R.id.resolver_list);
-        if (!isVisible) {
-            mRecyclerView.setVisibility(View.GONE);
-            return;
-        }
-        mRecyclerView.setVisibility(View.VISIBLE);
+    public void onPrepareAdapterView(ResolverListAdapter adapter) {
+        mChooserMultiProfilePagerAdapter.getCurrentAdapterView().setVisibility(View.VISIBLE);
         if (mCallerChooserTargets != null && mCallerChooserTargets.length > 0) {
-            mChooserListAdapter.addServiceResults(null, Lists.newArrayList(mCallerChooserTargets),
+            mChooserMultiProfilePagerAdapter.getCurrentListAdapter().addServiceResults(
+                    /* origTarget */ null,
+                    Lists.newArrayList(mCallerChooserTargets),
                     TARGET_TYPE_DEFAULT);
         }
-        mChooserGridAdapter = new ChooserGridAdapter(mChooserListAdapter);
-        GridLayoutManager glm = (GridLayoutManager) mRecyclerView.getLayoutManager();
-        glm.setSpanCount(mChooserGridAdapter.getMaxTargetsPerRow());
-        glm.setSpanSizeLookup(
-                new GridLayoutManager.SpanSizeLookup() {
-                    @Override
-                    public int getSpanSize(int position) {
-                        return mChooserGridAdapter.getItemViewType(position)
-                                == ChooserGridAdapter.VIEW_TYPE_NORMAL
-                                ? SINGLE_CELL_SPAN_SIZE
-                                : glm.getSpanCount();
-                    }
-                });
-    }
-
-    @Override
-    protected boolean postRebuildList(boolean rebuildCompleted) {
-        mChooserListAdapter = (ChooserListAdapter) mAdapter;
-        return postRebuildListInternal(rebuildCompleted);
     }
 
     @Override
@@ -1348,7 +1403,10 @@
 
     @Override
     public void startSelected(int which, boolean always, boolean filtered) {
-        TargetInfo targetInfo = mChooserListAdapter.targetInfoForPosition(which, filtered);
+        ChooserListAdapter currentListAdapter =
+                mChooserMultiProfilePagerAdapter.getCurrentListAdapter();
+        TargetInfo targetInfo = currentListAdapter
+                .targetInfoForPosition(which, filtered);
         if (targetInfo != null && targetInfo instanceof NotSelectableTargetInfo) {
             return;
         }
@@ -1356,7 +1414,7 @@
         final long selectionCost = System.currentTimeMillis() - mChooserShownTime;
         super.startSelected(which, always, filtered);
 
-        if (mChooserListAdapter.getCount() > 0) {
+        if (currentListAdapter.getCount() > 0) {
             // Log the index of which type of target the user picked.
             // Lower values mean the ranking was better.
             int cat = 0;
@@ -1364,13 +1422,12 @@
             int directTargetAlsoRanked = -1;
             int numCallerProvided = 0;
             HashedStringCache.HashResult directTargetHashed = null;
-            switch (mChooserListAdapter.getPositionTargetType(which)) {
+            switch (currentListAdapter.getPositionTargetType(which)) {
                 case ChooserListAdapter.TARGET_SERVICE:
                     cat = MetricsEvent.ACTION_ACTIVITY_CHOOSER_PICKED_SERVICE_TARGET;
                     // Log the package name + target name to answer the question if most users
                     // share to mostly the same person or to a bunch of different people.
-                    ChooserTarget target =
-                            mChooserListAdapter.getChooserTargetForValue(value);
+                    ChooserTarget target = currentListAdapter.getChooserTargetForValue(value);
                     directTargetHashed = HashedStringCache.getInstance().hashString(
                             this,
                             TAG,
@@ -1386,8 +1443,8 @@
                 case ChooserListAdapter.TARGET_CALLER:
                 case ChooserListAdapter.TARGET_STANDARD:
                     cat = MetricsEvent.ACTION_ACTIVITY_CHOOSER_PICKED_APP_TARGET;
-                    value -= mChooserListAdapter.getSelectableServiceTargetCount();
-                    numCallerProvided = mChooserListAdapter.getCallerTargetCount();
+                    value -= currentListAdapter.getSelectableServiceTargetCount();
+                    numCallerProvided = currentListAdapter.getCallerTargetCount();
                     break;
                 case ChooserListAdapter.TARGET_STANDARD_AZ:
                     // A-Z targets are unranked standard targets; we use -1 to mark that they
@@ -1429,11 +1486,13 @@
     private int getRankedPosition(SelectableTargetInfo targetInfo) {
         String targetPackageName =
                 targetInfo.getChooserTarget().getComponentName().getPackageName();
-        int maxRankedResults = Math.min(mChooserListAdapter.mDisplayList.size(),
-                        MAX_LOG_RANK_POSITION);
+        ChooserListAdapter currentListAdapter =
+                mChooserMultiProfilePagerAdapter.getCurrentListAdapter();
+        int maxRankedResults = Math.min(currentListAdapter.mDisplayList.size(),
+                MAX_LOG_RANK_POSITION);
 
         for (int i = 0; i < maxRankedResults; i++) {
-            if (mChooserListAdapter.mDisplayList.get(i)
+            if (currentListAdapter.mDisplayList.get(i)
                     .getResolveInfo().activityInfo.packageName.equals(targetPackageName)) {
                 return i;
             }
@@ -1577,6 +1636,7 @@
             }
         }
         // Default to just querying ShortcutManager if AppPredictor not present.
+        //TODO(arangelov) we're using mIntents here, investicate possible implications on work tab
         final IntentFilter filter = getTargetIntentFilter();
         if (filter == null) {
             return;
@@ -1584,6 +1644,7 @@
         final List<DisplayResolveInfo> driList = getDisplayResolveInfos(adapter);
 
         AsyncTask.execute(() -> {
+            //TODO(arangelov) use the selected probile tab's ShortcutManager
             ShortcutManager sm = (ShortcutManager) getSystemService(Context.SHORTCUT_SERVICE);
             List<ShortcutManager.ShareShortcutInfo> resultList = sm.getShareTargets(filter);
             sendShareShortcutInfoList(resultList, driList, null);
@@ -1779,9 +1840,11 @@
             final ResolveInfo ri = info.getResolveInfo();
             Intent targetIntent = getTargetIntent();
             if (ri != null && ri.activityInfo != null && targetIntent != null) {
-                if (mAdapter != null) {
-                    mAdapter.updateModel(info.getResolvedComponentName());
-                    mAdapter.updateChooserCounts(ri.activityInfo.packageName, getUserId(),
+                ChooserListAdapter currentListAdapter =
+                        mChooserMultiProfilePagerAdapter.getCurrentListAdapter();
+                if (currentListAdapter != null) {
+                    currentListAdapter.updateModel(info.getResolvedComponentName());
+                    currentListAdapter.updateChooserCounts(ri.activityInfo.packageName, getUserId(),
                             targetIntent.getAction());
                 }
                 if (DEBUG) {
@@ -1956,8 +2019,9 @@
                 Intent targetIntent,
                 String referrerPackageName,
                 int launchedFromUid,
+                UserHandle userId,
                 AbstractResolverComparator resolverComparator) {
-            super(context, pm, targetIntent, referrerPackageName, launchedFromUid,
+            super(context, pm, targetIntent, referrerPackageName, launchedFromUid, userId,
                     resolverComparator);
         }
 
@@ -1980,17 +2044,18 @@
         }
     }
 
-    @Override
-    public ResolverListAdapter createAdapter(Context context, List<Intent> payloadIntents,
-            Intent[] initialIntents, List<ResolveInfo> rList,
-            boolean filterLastUsed, boolean useLayoutForBrowsables) {
-        return new ChooserListAdapter(context, payloadIntents,
-                initialIntents, rList, filterLastUsed, createListController(),
-                useLayoutForBrowsables, this, this);
+    @VisibleForTesting
+    public ChooserGridAdapter createChooserGridAdapter(Context context,
+            List<Intent> payloadIntents, Intent[] initialIntents, List<ResolveInfo> rList,
+            boolean filterLastUsed, boolean useLayoutForBrowsables, UserHandle userHandle) {
+        return new ChooserGridAdapter(
+                new ChooserListAdapter(context, payloadIntents, initialIntents, rList,
+                        filterLastUsed, createListController(userHandle), useLayoutForBrowsables,
+                        this, this));
     }
 
     @VisibleForTesting
-    protected ResolverListController createListController() {
+    protected ResolverListController createListController(UserHandle userHandle) {
         AppPredictor appPredictor = getAppPredictorForShareActivitesIfEnabled();
         AbstractResolverComparator resolverComparator;
         if (appPredictor != null) {
@@ -2008,6 +2073,7 @@
                 getTargetIntent(),
                 getReferrerPackageName(),
                 mLaunchedFromUid,
+                userHandle,
                 resolverComparator);
     }
 
@@ -2041,8 +2107,8 @@
     }
 
     private void handleScroll(View view, int x, int y, int oldx, int oldy) {
-        if (mChooserGridAdapter != null) {
-            mChooserGridAdapter.handleScroll(view, y, oldy);
+        if (mChooserMultiProfilePagerAdapter.getCurrentRootAdapter() != null) {
+            mChooserMultiProfilePagerAdapter.getCurrentRootAdapter().handleScroll(view, y, oldy);
         }
     }
 
@@ -2053,37 +2119,42 @@
      */
     private void handleLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft,
             int oldTop, int oldRight, int oldBottom) {
-        if (mChooserGridAdapter == null || mRecyclerView == null) {
+        if (mChooserMultiProfilePagerAdapter == null) {
+            return;
+        }
+        RecyclerView recyclerView = mChooserMultiProfilePagerAdapter.getCurrentAdapterView();
+        ChooserGridAdapter gridAdapter = mChooserMultiProfilePagerAdapter.getCurrentRootAdapter();
+        if (gridAdapter == null || recyclerView == null) {
             return;
         }
 
         final int availableWidth = right - left - v.getPaddingLeft() - v.getPaddingRight();
-        if (mChooserGridAdapter.consumeLayoutRequest()
-                || mChooserGridAdapter.calculateChooserTargetWidth(availableWidth)
-                || mRecyclerView.getAdapter() == null
+        if (gridAdapter.consumeLayoutRequest()
+                || gridAdapter.calculateChooserTargetWidth(availableWidth)
+                || recyclerView.getAdapter() == null
                 || availableWidth != mCurrAvailableWidth) {
             mCurrAvailableWidth = availableWidth;
-            mRecyclerView.setAdapter(mChooserGridAdapter);
-            ((GridLayoutManager) mRecyclerView.getLayoutManager())
-                    .setSpanCount(mChooserGridAdapter.getMaxTargetsPerRow());
+            recyclerView.setAdapter(gridAdapter);
+            ((GridLayoutManager) recyclerView.getLayoutManager())
+                    .setSpanCount(gridAdapter.getMaxTargetsPerRow());
 
             getMainThreadHandler().post(() -> {
-                if (mResolverDrawerLayout == null || mChooserGridAdapter == null) {
+                if (mResolverDrawerLayout == null || gridAdapter == null) {
                     return;
                 }
 
                 final int bottomInset = mSystemWindowInsets != null
                                             ? mSystemWindowInsets.bottom : 0;
                 int offset = bottomInset;
-                int rowsToShow = mChooserGridAdapter.getContentPreviewRowCount()
-                        + mChooserGridAdapter.getProfileRowCount()
-                        + mChooserGridAdapter.getServiceTargetRowCount()
-                        + mChooserGridAdapter.getCallerAndRankedTargetRowCount();
+                int rowsToShow = gridAdapter.getContentPreviewRowCount()
+                        + gridAdapter.getProfileRowCount()
+                        + gridAdapter.getServiceTargetRowCount()
+                        + gridAdapter.getCallerAndRankedTargetRowCount();
 
                 // then this is most likely not a SEND_* action, so check
                 // the app target count
                 if (rowsToShow == 0) {
-                    rowsToShow = mChooserGridAdapter.getRowCount();
+                    rowsToShow = gridAdapter.getRowCount();
                 }
 
                 // still zero? then use a default height and leave, which
@@ -2097,9 +2168,9 @@
 
                 int directShareHeight = 0;
                 rowsToShow = Math.min(4, rowsToShow);
-                for (int i = 0, childCount = mRecyclerView.getChildCount();
+                for (int i = 0, childCount = recyclerView.getChildCount();
                         i < childCount && rowsToShow > 0; i++) {
-                    View child = mRecyclerView.getChildAt(i);
+                    View child = recyclerView.getChildAt(i);
                     if (((GridLayoutManager.LayoutParams)
                             child.getLayoutParams()).getSpanIndex() != 0) {
                         continue;
@@ -2107,9 +2178,9 @@
                     int height = child.getHeight();
                     offset += height;
 
-                    if (mChooserGridAdapter.getTargetType(
-                            mRecyclerView.getChildAdapterPosition(child))
-                            == mChooserListAdapter.TARGET_SERVICE) {
+                    if (gridAdapter.getTargetType(
+                            recyclerView.getChildAdapterPosition(child))
+                            == ChooserListAdapter.TARGET_SERVICE) {
                         directShareHeight = height;
                     }
                     rowsToShow--;
@@ -2145,13 +2216,13 @@
     @Override // ResolverListCommunicator
     public void onHandlePackagesChanged() {
         mServicesRequested.clear();
-        mAdapter.notifyDataSetChanged();
+        mChooserMultiProfilePagerAdapter.getCurrentListAdapter().notifyDataSetChanged();
         super.onHandlePackagesChanged();
     }
 
     @Override // SelectableTargetInfoCommunicator
     public ActivityInfoPresentationGetter makePresentationGetter(ActivityInfo info) {
-        return mChooserListAdapter.makePresentationGetter(info);
+        return mChooserMultiProfilePagerAdapter.getCurrentListAdapter().makePresentationGetter(info);
     }
 
     @Override // SelectableTargetInfoCommunicator
@@ -2161,9 +2232,9 @@
 
     @Override // ChooserListCommunicator
     public int getMaxRankedTargets() {
-        return mChooserGridAdapter == null
+        return mChooserMultiProfilePagerAdapter.getCurrentRootAdapter() == null
                 ? ChooserGridAdapter.MAX_TARGETS_PER_ROW_PORTRAIT
-                : mChooserGridAdapter.getMaxTargetsPerRow();
+                : mChooserMultiProfilePagerAdapter.getCurrentRootAdapter().getMaxTargetsPerRow();
     }
 
     @Override // ChooserListCommunicator
@@ -2174,19 +2245,21 @@
 
     @Override
     public void onListRebuilt() {
-        if (mChooserListAdapter.mDisplayList == null
-                || mChooserListAdapter.mDisplayList.isEmpty()) {
-            mChooserListAdapter.notifyDataSetChanged();
+        final ChooserListAdapter currentListAdapter =
+                mChooserMultiProfilePagerAdapter.getCurrentListAdapter();
+        if (currentListAdapter.mDisplayList == null
+                || currentListAdapter.mDisplayList.isEmpty()) {
+            currentListAdapter.notifyDataSetChanged();
         } else {
             new AsyncTask<Void, Void, Void>() {
                 @Override
                 protected Void doInBackground(Void... voids) {
-                    mChooserListAdapter.updateAlphabeticalList();
+                    currentListAdapter.updateAlphabeticalList();
                     return null;
                 }
                 @Override
                 protected void onPostExecute(Void aVoid) {
-                    mChooserListAdapter.notifyDataSetChanged();
+                    currentListAdapter.notifyDataSetChanged();
                 }
             }.execute();
         }
@@ -2202,14 +2275,14 @@
                 Log.d(TAG, "querying direct share targets from ShortcutManager");
             }
 
-            queryDirectShareTargets(mChooserListAdapter, false);
+            queryDirectShareTargets(currentListAdapter, false);
         }
         if (USE_CHOOSER_TARGET_SERVICE_FOR_DIRECT_TARGETS) {
             if (DEBUG) {
                 Log.d(TAG, "List built querying services");
             }
 
-            queryTargetServices(mChooserListAdapter);
+            queryTargetServices(currentListAdapter);
         }
     }
 
@@ -2250,8 +2323,8 @@
                         false/* always */, true/* filterd */));
                 itemView.setOnLongClickListener(v -> {
                     showTargetDetails(
-                            mChooserListAdapter.resolveInfoForPosition(
-                                    mListPosition, true/* filtered */));
+                            mChooserMultiProfilePagerAdapter.getCurrentListAdapter()
+                                    .resolveInfoForPosition(mListPosition, /* filtered */ true));
                     return true;
                 });
             }
@@ -2259,6 +2332,29 @@
     }
 
     /**
+     * Intentionally override the {@link ResolverActivity} implementation as we only need that
+     * implementation for the intent resolver case.
+     */
+    @Override
+    protected WindowInsets onApplyWindowInsets(View v, WindowInsets insets) {
+        return insets.consumeSystemWindowInsets();
+    }
+
+    /**
+     * Intentionally override the {@link ResolverActivity} implementation as we only need that
+     * implementation for the intent resolver case.
+     */
+    @Override
+    public void onButtonClick(View v) {}
+
+    /**
+     * Intentionally override the {@link ResolverActivity} implementation as we only need that
+     * implementation for the intent resolver case.
+     */
+    @Override
+    protected void resetButtonBar() {}
+
+    /**
      * Adapter for all types of items and targets in ShareSheet.
      * Note that ranked sections like Direct Share - while appearing grid-like - are handled on the
      * row level by this adapter but not on the item level. Individual targets within the row are
@@ -2329,12 +2425,11 @@
             return false;
         }
 
-        private int getMaxTargetsPerRow() {
+        int getMaxTargetsPerRow() {
             int maxTargets = MAX_TARGETS_PER_ROW_PORTRAIT;
             if (shouldDisplayLandscape(getResources().getConfiguration().orientation)) {
                 maxTargets = MAX_TARGETS_PER_ROW_LANDSCAPE;
             }
-
             return maxTargets;
         }
 
@@ -2477,10 +2572,6 @@
         private ViewGroup createContentPreviewView(ViewGroup parent) {
             Intent targetIntent = getTargetIntent();
             int previewType = findPreferredContentPreview(targetIntent, getContentResolver());
-
-            getMetricsLogger().write(new LogMaker(MetricsEvent.ACTION_SHARE_WITH_PREVIEW)
-                        .setSubtype(previewType));
-
             return displayContentPreview(previewType, targetIntent, mLayoutInflater, parent);
         }
 
@@ -2667,6 +2758,7 @@
 
             for (int i = 0; i < columnCount; i++) {
                 final View v = holder.getView(i);
+
                 if (start + i <= end) {
                     holder.setViewVisibility(i, View.VISIBLE);
                     holder.setItemIndex(i, start + i);
@@ -2712,9 +2804,29 @@
                     && !isInMultiWindowMode();
 
             if (mDirectShareViewHolder != null && canExpandDirectShare) {
-                mDirectShareViewHolder.handleScroll(mRecyclerView, y, oldy, getMaxTargetsPerRow());
+                mDirectShareViewHolder.handleScroll(
+                        mChooserMultiProfilePagerAdapter.getCurrentAdapterView(), y, oldy,
+                        getMaxTargetsPerRow());
             }
         }
+
+        public ChooserListAdapter getListAdapter() {
+            return mChooserListAdapter;
+        }
+
+        void maybeLogActionShareWithPreview() {
+            if (getContentPreviewRowCount() == 0) {
+                return;
+            }
+            Intent targetIntent = getTargetIntent();
+            int previewType = findPreferredContentPreview(targetIntent, getContentResolver());
+            getMetricsLogger().write(new LogMaker(MetricsEvent.ACTION_SHARE_WITH_PREVIEW)
+                    .setSubtype(previewType));
+        }
+
+        boolean shouldCellSpan(int position) {
+            return getItemViewType(position) == VIEW_TYPE_NORMAL;
+        }
     }
 
     /**
@@ -2898,7 +3010,8 @@
 
                 // only expand if we have more than maxTargetsPerRow, and delay that decision
                 // until they start to scroll
-                if (mChooserListAdapter.getSelectableServiceTargetCount() <= maxTargetsPerRow) {
+                if (mChooserMultiProfilePagerAdapter.getCurrentListAdapter()
+                        .getSelectableServiceTargetCount() <= maxTargetsPerRow) {
                     mHideDirectShareExpansion = true;
                     return;
                 }
diff --git a/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java
new file mode 100644
index 0000000..aa8ab28
--- /dev/null
+++ b/core/java/com/android/internal/app/ChooserMultiProfilePagerAdapter.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.app;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
+
+import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.widget.GridLayoutManager;
+import com.android.internal.widget.PagerAdapter;
+import com.android.internal.widget.RecyclerView;
+
+/**
+ * A {@link PagerAdapter} which describes the work and personal profile share sheet screens.
+ */
+@VisibleForTesting
+public class ChooserMultiProfilePagerAdapter extends AbstractMultiProfilePagerAdapter {
+    private static final int SINGLE_CELL_SPAN_SIZE = 1;
+
+    private final ChooserProfileDescriptor[] mItems;
+
+    ChooserMultiProfilePagerAdapter(Context context,
+            ChooserActivity.ChooserGridAdapter adapter) {
+        super(context, /* currentPage */ 0);
+        mItems = new ChooserProfileDescriptor[] {
+                createProfileDescriptor(adapter)
+        };
+    }
+
+    ChooserMultiProfilePagerAdapter(Context context,
+            ChooserActivity.ChooserGridAdapter personalAdapter,
+            ChooserActivity.ChooserGridAdapter workAdapter,
+            @Profile int defaultProfile) {
+        super(context, /* currentPage */ defaultProfile);
+        mItems = new ChooserProfileDescriptor[] {
+                createProfileDescriptor(personalAdapter),
+                createProfileDescriptor(workAdapter)
+        };
+    }
+
+    private ChooserProfileDescriptor createProfileDescriptor(
+            ChooserActivity.ChooserGridAdapter adapter) {
+        final LayoutInflater inflater = LayoutInflater.from(getContext());
+        final ViewGroup rootView =
+                (ViewGroup) inflater.inflate(R.layout.chooser_list_per_profile, null, false);
+        return new ChooserProfileDescriptor(rootView, adapter);
+    }
+
+    RecyclerView getListViewForIndex(int index) {
+        return getItem(index).recyclerView;
+    }
+
+    @Override
+    ChooserProfileDescriptor getItem(int pageIndex) {
+        return mItems[pageIndex];
+    }
+
+    @Override
+    int getItemCount() {
+        return mItems.length;
+    }
+
+    @Override
+    ChooserActivity.ChooserGridAdapter getAdapterForIndex(int pageIndex) {
+        return mItems[pageIndex].chooserGridAdapter;
+    }
+
+    @Override
+    void setupListAdapter(int pageIndex) {
+        final RecyclerView recyclerView = getItem(pageIndex).recyclerView;
+        ChooserActivity.ChooserGridAdapter chooserGridAdapter =
+                getItem(pageIndex).chooserGridAdapter;
+        recyclerView.setAdapter(chooserGridAdapter);
+        GridLayoutManager glm = (GridLayoutManager) recyclerView.getLayoutManager();
+        glm.setSpanCount(chooserGridAdapter.getMaxTargetsPerRow());
+        glm.setSpanSizeLookup(
+                new GridLayoutManager.SpanSizeLookup() {
+                    @Override
+                    public int getSpanSize(int position) {
+                        return chooserGridAdapter.shouldCellSpan(position)
+                                ? SINGLE_CELL_SPAN_SIZE
+                                : glm.getSpanCount();
+                    }
+                });
+    }
+
+    @Override
+    @VisibleForTesting
+    public ChooserListAdapter getCurrentListAdapter() {
+        return getAdapterForIndex(getCurrentPage()).getListAdapter();
+    }
+
+    @Override
+    ChooserActivity.ChooserGridAdapter getCurrentRootAdapter() {
+        return getAdapterForIndex(getCurrentPage());
+    }
+
+    @Override
+    RecyclerView getCurrentAdapterView() {
+        return getListViewForIndex(getCurrentPage());
+    }
+
+    class ChooserProfileDescriptor extends ProfileDescriptor {
+        private ChooserActivity.ChooserGridAdapter chooserGridAdapter;
+        private RecyclerView recyclerView;
+        ChooserProfileDescriptor(ViewGroup rootView, ChooserActivity.ChooserGridAdapter adapter) {
+            super(rootView);
+            chooserGridAdapter = adapter;
+            recyclerView = rootView.findViewById(R.id.resolver_list);
+        }
+    }
+}
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 3c028d7..9cf5e9f 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -18,11 +18,15 @@
 
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
 
+import static com.android.internal.app.AbstractMultiProfilePagerAdapter.PROFILE_PERSONAL;
+import static com.android.internal.app.AbstractMultiProfilePagerAdapter.PROFILE_WORK;
+
 import android.annotation.Nullable;
 import android.annotation.StringRes;
 import android.annotation.UiThread;
 import android.annotation.UnsupportedAppUsage;
 import android.app.Activity;
+import android.app.ActivityManager;
 import android.app.ActivityTaskManager;
 import android.app.ActivityThread;
 import android.app.VoiceInteractor.PickOptionRequest;
@@ -72,6 +76,7 @@
 
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.app.AbstractMultiProfilePagerAdapter.Profile;
 import com.android.internal.app.chooser.DisplayResolveInfo;
 import com.android.internal.app.chooser.TargetInfo;
 import com.android.internal.content.PackageMonitor;
@@ -99,10 +104,7 @@
     public ResolverActivity() {
     }
 
-    @UnsupportedAppUsage
-    protected ResolverListAdapter mAdapter;
     private boolean mSafeForwardingMode;
-    private AbsListView mAdapterView;
     private Button mAlwaysButton;
     private Button mOnceButton;
     protected View mProfileView;
@@ -143,8 +145,16 @@
     private static final String EXTRA_FRAGMENT_ARG_KEY = ":settings:fragment_args_key";
     private static final String OPEN_LINKS_COMPONENT_KEY = "app_link_state";
 
+    /**
+     * TODO(arangelov): Remove a couple of weeks after work/personal tabs are finalized.
+     */
+    static final boolean ENABLE_TABBED_VIEW = false;
+
     private final PackageMonitor mPackageMonitor = createPackageMonitor();
 
+    @VisibleForTesting
+    protected AbstractMultiProfilePagerAdapter mMultiProfilePagerAdapter;
+
     // Intent extra for connected audio devices
     public static final String EXTRA_IS_AUDIO_CAPTURE_DEVICE = "is_audio_capture_device";
 
@@ -230,7 +240,7 @@
         return new PackageMonitor() {
             @Override
             public void onSomePackagesChanged() {
-                mAdapter.handlePackagesChanged();
+                mMultiProfilePagerAdapter.getCurrentListAdapter().handlePackagesChanged();
                 updateProfileViewButton();
             }
 
@@ -325,15 +335,13 @@
 
         mSupportsAlwaysUseOption = supportsAlwaysUseOption;
 
-        // The last argument of createAdapter is whether to do special handling
+        // The last argument of createResolverListAdapter is whether to do special handling
         // of the last used choice to highlight it in the list.  We need to always
         // turn this off when running under voice interaction, since it results in
         // a more complicated UI that the current voice interaction flow is not able
         // to handle.
         boolean filterLastUsed = mSupportsAlwaysUseOption && !isVoiceInteraction();
-        mAdapter = createAdapter(this, mIntents, initialIntents, rList,
-                filterLastUsed, mUseLayoutForBrowsables);
-
+        mMultiProfilePagerAdapter = createMultiProfilePagerAdapter(initialIntents, rList, filterLastUsed);
         if (configureContentView()) {
             return;
         }
@@ -364,15 +372,96 @@
         }
 
         final Set<String> categories = intent.getCategories();
-        MetricsLogger.action(this, mAdapter.hasFilteredItem()
+        MetricsLogger.action(this, mMultiProfilePagerAdapter.getCurrentListAdapter().hasFilteredItem()
                 ? MetricsProto.MetricsEvent.ACTION_SHOW_APP_DISAMBIG_APP_FEATURED
                 : MetricsProto.MetricsEvent.ACTION_SHOW_APP_DISAMBIG_NONE_FEATURED,
                 intent.getAction() + ":" + intent.getType() + ":"
                         + (categories != null ? Arrays.toString(categories.toArray()) : ""));
     }
 
+    protected AbstractMultiProfilePagerAdapter createMultiProfilePagerAdapter(
+            Intent[] initialIntents,
+            List<ResolveInfo> rList,
+            boolean filterLastUsed) {
+        AbstractMultiProfilePagerAdapter resolverMultiProfilePagerAdapter = null;
+        if (hasWorkProfile() && ENABLE_TABBED_VIEW) {
+            resolverMultiProfilePagerAdapter =
+                    createResolverMultiProfilePagerAdapterForTwoProfiles(
+                            initialIntents, rList, filterLastUsed);
+        } else {
+            resolverMultiProfilePagerAdapter = createResolverMultiProfilePagerAdapterForOneProfile(
+                    initialIntents, rList, filterLastUsed);
+        }
+        return resolverMultiProfilePagerAdapter;
+    }
+
+    private ResolverMultiProfilePagerAdapter createResolverMultiProfilePagerAdapterForOneProfile(
+            Intent[] initialIntents,
+            List<ResolveInfo> rList, boolean filterLastUsed) {
+        ResolverListAdapter adapter = createResolverListAdapter(
+                /* context */ this,
+                /* payloadIntents */ mIntents,
+                initialIntents,
+                rList,
+                filterLastUsed,
+                mUseLayoutForBrowsables,
+                /* userHandle */ UserHandle.of(UserHandle.myUserId()));
+        return new ResolverMultiProfilePagerAdapter(
+                /* context */ this,
+                adapter);
+    }
+
+    private ResolverMultiProfilePagerAdapter createResolverMultiProfilePagerAdapterForTwoProfiles(
+            Intent[] initialIntents,
+            List<ResolveInfo> rList,
+            boolean filterLastUsed) {
+        ResolverListAdapter personalAdapter = createResolverListAdapter(
+                /* context */ this,
+                /* payloadIntents */ mIntents,
+                initialIntents,
+                rList,
+                filterLastUsed,
+                mUseLayoutForBrowsables,
+                /* userHandle */ getPersonalProfileUserHandle());
+        ResolverListAdapter workAdapter = createResolverListAdapter(
+                /* context */ this,
+                /* payloadIntents */ mIntents,
+                initialIntents,
+                rList,
+                filterLastUsed,
+                mUseLayoutForBrowsables,
+                /* userHandle */ getWorkProfileUserHandle());
+        return new ResolverMultiProfilePagerAdapter(
+                /* context */ this,
+                personalAdapter,
+                workAdapter,
+                /* defaultProfile */ getCurrentProfile());
+    }
+
+    protected @Profile int getCurrentProfile() {
+        return (UserHandle.myUserId() == UserHandle.USER_SYSTEM ? PROFILE_PERSONAL : PROFILE_WORK);
+    }
+
+    protected UserHandle getPersonalProfileUserHandle() {
+        return UserHandle.of(ActivityManager.getCurrentUser());
+    }
+    protected @Nullable UserHandle getWorkProfileUserHandle() {
+        UserManager userManager = getSystemService(UserManager.class);
+        for (final UserInfo userInfo : userManager.getProfiles(ActivityManager.getCurrentUser())) {
+            if (userInfo.isManagedProfile()) {
+                return userInfo.getUserHandle();
+            }
+        }
+        return null;
+    }
+
+    protected boolean hasWorkProfile() {
+        return getWorkProfileUserHandle() != null;
+    }
+
     protected void onProfileClick(View v) {
-        final DisplayResolveInfo dri = mAdapter.getOtherProfile();
+        final DisplayResolveInfo dri =
+                mMultiProfilePagerAdapter.getCurrentListAdapter().getOtherProfile();
         if (dri == null) {
             return;
         }
@@ -395,11 +484,13 @@
             if (mFooterSpacer == null) {
                 mFooterSpacer = new Space(getApplicationContext());
             } else {
-                ((ListView) mAdapterView).removeFooterView(mFooterSpacer);
+                ((ResolverMultiProfilePagerAdapter) mMultiProfilePagerAdapter)
+                        .getCurrentAdapterView().removeFooterView(mFooterSpacer);
             }
             mFooterSpacer.setLayoutParams(new AbsListView.LayoutParams(LayoutParams.MATCH_PARENT,
                                                                        mSystemWindowInsets.bottom));
-            ((ListView) mAdapterView).addFooterView(mFooterSpacer);
+            ((ResolverMultiProfilePagerAdapter) mMultiProfilePagerAdapter)
+                    .getCurrentAdapterView().addFooterView(mFooterSpacer);
         } else {
             View emptyView = findViewById(R.id.empty);
             if (emptyView != null) {
@@ -417,7 +508,7 @@
     @Override
     public void onConfigurationChanged(Configuration newConfig) {
         super.onConfigurationChanged(newConfig);
-        mAdapter.handlePackagesChanged();
+        mMultiProfilePagerAdapter.getCurrentListAdapter().handlePackagesChanged();
 
         if (mSystemWindowInsets != null) {
             mResolverDrawerLayout.setPadding(mSystemWindowInsets.left, mSystemWindowInsets.top,
@@ -432,9 +523,10 @@
             return;
         }
 
-        final Option[] options = new Option[mAdapter.getCount()];
+        int count = mMultiProfilePagerAdapter.getCurrentListAdapter().getCount();
+        final Option[] options = new Option[count];
         for (int i = 0, N = options.length; i < N; i++) {
-            TargetInfo target = mAdapter.getItem(i);
+            TargetInfo target = mMultiProfilePagerAdapter.getCurrentListAdapter().getItem(i);
             if (target == null) {
                 // If this occurs, a new set of targets is being loaded. Let that complete,
                 // and have the next call to send voice choices proceed instead.
@@ -483,8 +575,9 @@
             return;
         }
 
-        final DisplayResolveInfo dri = mAdapter.getOtherProfile();
-        if (dri != null) {
+        final DisplayResolveInfo dri =
+                mMultiProfilePagerAdapter.getCurrentListAdapter().getOtherProfile();
+        if (dri != null && !ENABLE_TABBED_VIEW) {
             mProfileView.setVisibility(View.VISIBLE);
             View text = mProfileView.findViewById(R.id.profile_button);
             if (!(text instanceof TextView)) {
@@ -535,7 +628,8 @@
 
         // While there may already be a filtered item, we can only use it in the title if the list
         // is already sorted and all information relevant to it is already in the list.
-        final boolean named = mAdapter.getFilteredPosition() >= 0;
+        final boolean named =
+                mMultiProfilePagerAdapter.getCurrentListAdapter().getFilteredPosition() >= 0;
         if (title == ActionTitle.DEFAULT && defaultTitleRes != 0) {
             return getString(defaultTitleRes);
         } else if (isHttpSchemeAndViewAction(intent)) {
@@ -544,12 +638,14 @@
             String dialogTitle = null;
             if (named && !mUseLayoutForBrowsables) {
                 dialogTitle = getString(ActionTitle.BROWSABLE_APP_TITLE_RES,
-                        mAdapter.getFilteredItem().getDisplayLabel());
+                        mMultiProfilePagerAdapter.getCurrentListAdapter().getFilteredItem()
+                                .getDisplayLabel());
             } else if (named && mUseLayoutForBrowsables) {
                 dialogTitle = getString(ActionTitle.BROWSABLE_HOST_APP_TITLE_RES,
                         intent.getData().getHost(),
-                        mAdapter.getFilteredItem().getDisplayLabel());
-            } else if (mAdapter.areAllTargetsBrowsers()) {
+                        mMultiProfilePagerAdapter.getCurrentListAdapter().getFilteredItem()
+                                .getDisplayLabel());
+            } else if (mMultiProfilePagerAdapter.getCurrentListAdapter().areAllTargetsBrowsers()) {
                 dialogTitle = getString(ActionTitle.BROWSABLE_TITLE_RES);
             } else {
                 dialogTitle = getString(ActionTitle.BROWSABLE_HOST_TITLE_RES,
@@ -558,7 +654,8 @@
             return dialogTitle;
         } else {
             return named
-                    ? getString(title.namedTitleRes, mAdapter.getFilteredItem().getDisplayLabel())
+                    ? getString(title.namedTitleRes, mMultiProfilePagerAdapter
+                            .getCurrentListAdapter().getFilteredItem().getDisplayLabel())
                     : getString(title.titleRes);
         }
     }
@@ -576,7 +673,7 @@
             mPackageMonitor.register(this, getMainLooper(), false);
             mRegistered = true;
         }
-        mAdapter.handlePackagesChanged();
+        mMultiProfilePagerAdapter.getCurrentListAdapter().handlePackagesChanged();
         updateProfileViewButton();
     }
 
@@ -609,8 +706,8 @@
         if (!isChangingConfigurations() && mPickOptionRequest != null) {
             mPickOptionRequest.cancel();
         }
-        if (mAdapter != null) {
-            mAdapter.onDestroy();
+        if (mMultiProfilePagerAdapter.getCurrentListAdapter() != null) {
+            mMultiProfilePagerAdapter.getCurrentListAdapter().onDestroy();
         }
     }
 
@@ -660,7 +757,8 @@
         boolean enabled = false;
         ResolveInfo ri = null;
         if (hasValidSelection) {
-            ri = mAdapter.resolveInfoForPosition(checkedPos, filtered);
+            ri = mMultiProfilePagerAdapter.getCurrentListAdapter()
+                    .resolveInfoForPosition(checkedPos, filtered);
             if (ri == null) {
                 Log.e(TAG, "Invalid position supplied to setAlwaysButtonEnabled");
                 return;
@@ -701,11 +799,13 @@
 
     public void onButtonClick(View v) {
         final int id = v.getId();
-        int which = mAdapter.hasFilteredItem()
-                ? mAdapter.getFilteredPosition()
-                : mAdapterView.getCheckedItemPosition();
-        boolean hasIndexBeenFiltered = !mAdapter.hasFilteredItem();
-        ResolveInfo ri = mAdapter.resolveInfoForPosition(which, hasIndexBeenFiltered);
+        ListView listView = (ListView) mMultiProfilePagerAdapter.getCurrentAdapterView();
+        ResolverListAdapter currentListAdapter = mMultiProfilePagerAdapter.getCurrentListAdapter();
+        int which = currentListAdapter.hasFilteredItem()
+                ? currentListAdapter.getFilteredPosition()
+                : listView.getCheckedItemPosition();
+        boolean hasIndexBeenFiltered = !currentListAdapter.hasFilteredItem();
+        ResolveInfo ri = currentListAdapter.resolveInfoForPosition(which, hasIndexBeenFiltered);
         if (mUseLayoutForBrowsables
                 && !ri.handleAllWebDataURI && id == R.id.button_always) {
             showSettingsForSelected(ri);
@@ -736,7 +836,8 @@
         if (isFinishing()) {
             return;
         }
-        ResolveInfo ri = mAdapter.resolveInfoForPosition(which, hasIndexBeenFiltered);
+        ResolveInfo ri = mMultiProfilePagerAdapter.getCurrentListAdapter()
+                .resolveInfoForPosition(which, hasIndexBeenFiltered);
         if (mResolvingHome && hasManagedProfile() && !supportsManagedProfiles(ri)) {
             Toast.makeText(this, String.format(getResources().getString(
                     com.android.internal.R.string.activity_resolver_work_profiles_support),
@@ -745,7 +846,8 @@
             return;
         }
 
-        TargetInfo target = mAdapter.targetInfoForPosition(which, hasIndexBeenFiltered);
+        TargetInfo target = mMultiProfilePagerAdapter.getCurrentListAdapter()
+                .targetInfoForPosition(which, hasIndexBeenFiltered);
         if (target == null) {
             return;
         }
@@ -760,7 +862,8 @@
                 MetricsLogger.action(
                         this, MetricsProto.MetricsEvent.ACTION_APP_DISAMBIG_TAP);
             }
-            MetricsLogger.action(this, mAdapter.hasFilteredItem()
+            MetricsLogger.action(this,
+                    mMultiProfilePagerAdapter.getCurrentListAdapter().hasFilteredItem()
                             ? MetricsProto.MetricsEvent.ACTION_HIDE_APP_DISAMBIG_APP_FEATURED
                             : MetricsProto.MetricsEvent.ACTION_HIDE_APP_DISAMBIG_NONE_FEATURED);
             finish();
@@ -783,10 +886,11 @@
     }
 
     protected void onListRebuilt() {
-        int count = mAdapter.getUnfilteredCount();
-        if (count == 1 && mAdapter.getOtherProfile() == null) {
+        int count = mMultiProfilePagerAdapter.getCurrentListAdapter().getUnfilteredCount();
+        if (count == 1 && mMultiProfilePagerAdapter.getCurrentListAdapter().getOtherProfile() == null) {
             // Only one target, so we're a candidate to auto-launch!
-            final TargetInfo target = mAdapter.targetInfoForPosition(0, false);
+            final TargetInfo target =
+                    mMultiProfilePagerAdapter.getCurrentListAdapter().targetInfoForPosition(0, false);
             if (shouldAutoLaunchSingleChoice(target)) {
                 safelyStartActivity(target);
                 finish();
@@ -798,8 +902,9 @@
         final ResolveInfo ri = target.getResolveInfo();
         final Intent intent = target != null ? target.getResolvedIntent() : null;
 
-        if (intent != null && (mSupportsAlwaysUseOption || mAdapter.hasFilteredItem())
-                && mAdapter.mUnfilteredResolveList != null) {
+        if (intent != null && (mSupportsAlwaysUseOption
+                || mMultiProfilePagerAdapter.getCurrentListAdapter().hasFilteredItem())
+                && mMultiProfilePagerAdapter.getCurrentListAdapter().getUnfilteredResolveList() != null) {
             // Build a reasonable intent filter, based on what matched.
             IntentFilter filter = new IntentFilter();
             Intent filterIntent;
@@ -884,13 +989,14 @@
             }
 
             if (filter != null) {
-                final int N = mAdapter.mUnfilteredResolveList.size();
+                final int N = mMultiProfilePagerAdapter.getCurrentListAdapter()
+                        .getUnfilteredResolveList().size();
                 ComponentName[] set;
                 // If we don't add back in the component for forwarding the intent to a managed
                 // profile, the preferred activity may not be updated correctly (as the set of
                 // components we tell it we knew about will have changed).
                 final boolean needToAddBackProfileForwardingComponent =
-                        mAdapter.getOtherProfile() != null;
+                        mMultiProfilePagerAdapter.getCurrentListAdapter().getOtherProfile() != null;
                 if (!needToAddBackProfileForwardingComponent) {
                     set = new ComponentName[N];
                 } else {
@@ -899,15 +1005,18 @@
 
                 int bestMatch = 0;
                 for (int i=0; i<N; i++) {
-                    ResolveInfo r = mAdapter.mUnfilteredResolveList.get(i).getResolveInfoAt(0);
+                    ResolveInfo r = mMultiProfilePagerAdapter.getCurrentListAdapter()
+                            .getUnfilteredResolveList().get(i).getResolveInfoAt(0);
                     set[i] = new ComponentName(r.activityInfo.packageName,
                             r.activityInfo.name);
                     if (r.match > bestMatch) bestMatch = r.match;
                 }
 
                 if (needToAddBackProfileForwardingComponent) {
-                    set[N] = mAdapter.getOtherProfile().getResolvedComponentName();
-                    final int otherProfileMatch = mAdapter.getOtherProfile().getResolveInfo().match;
+                    set[N] = mMultiProfilePagerAdapter.getCurrentListAdapter()
+                            .getOtherProfile().getResolvedComponentName();
+                    final int otherProfileMatch = mMultiProfilePagerAdapter.getCurrentListAdapter()
+                            .getOtherProfile().getResolveInfo().match;
                     if (otherProfileMatch > bestMatch) bestMatch = otherProfileMatch;
                 }
 
@@ -946,7 +1055,8 @@
                     }
                 } else {
                     try {
-                        mAdapter.mResolverListController.setLastChosen(intent, filter, bestMatch);
+                        mMultiProfilePagerAdapter.getCurrentListAdapter()
+                                .mResolverListController.setLastChosen(intent, filter, bestMatch);
                     } catch (RemoteException re) {
                         Log.d(TAG, "Error calling setLastChosenActivity\n" + re);
                     }
@@ -984,14 +1094,15 @@
         if (mProfileSwitchMessageId != -1) {
             Toast.makeText(this, getString(mProfileSwitchMessageId), Toast.LENGTH_LONG).show();
         }
+        UserHandle currentUserHandle = mMultiProfilePagerAdapter.getCurrentUserHandle();
         if (!mSafeForwardingMode) {
-            if (cti.start(this, null)) {
+            if (cti.startAsUser(this, null, currentUserHandle)) {
                 onActivityStarted(cti);
             }
             return;
         }
         try {
-            if (cti.startAsCaller(this, null, UserHandle.USER_NULL)) {
+            if (cti.startAsCaller(this, null, currentUserHandle.getIdentifier())) {
                 onActivityStarted(cti);
             }
         } catch (RuntimeException e) {
@@ -1061,26 +1172,27 @@
         startActivity(in);
     }
 
-    public ResolverListAdapter createAdapter(Context context, List<Intent> payloadIntents,
-            Intent[] initialIntents, List<ResolveInfo> rList,
-            boolean filterLastUsed, boolean useLayoutForBrowsables) {
-
+    @VisibleForTesting
+    protected ResolverListAdapter createResolverListAdapter(Context context,
+            List<Intent> payloadIntents, Intent[] initialIntents, List<ResolveInfo> rList,
+            boolean filterLastUsed, boolean useLayoutForBrowsables, UserHandle userHandle) {
         Intent startIntent = getIntent();
         boolean isAudioCaptureDevice =
                 startIntent.getBooleanExtra(EXTRA_IS_AUDIO_CAPTURE_DEVICE, false);
         return new ResolverListAdapter(context, payloadIntents, initialIntents, rList,
-                filterLastUsed, createListController(), useLayoutForBrowsables, this,
+                filterLastUsed, createListController(userHandle), useLayoutForBrowsables, this,
                 isAudioCaptureDevice);
     }
 
     @VisibleForTesting
-    protected ResolverListController createListController() {
+    protected ResolverListController createListController(UserHandle userHandle) {
         return new ResolverListController(
                 this,
                 mPm,
                 getTargetIntent(),
                 getReferrerPackageName(),
-                mLaunchedFromUid);
+                mLaunchedFromUid,
+                userHandle);
     }
 
     /**
@@ -1088,16 +1200,17 @@
      * @return <code>true</code> if the activity is finishing and creation should halt.
      */
     private boolean configureContentView() {
-        if (mAdapter == null) {
+        if (mMultiProfilePagerAdapter.getCurrentListAdapter() == null) {
             throw new IllegalStateException("mAdapter cannot be null.");
         }
-        boolean rebuildCompleted = mAdapter.rebuildList();
+        boolean rebuildCompleted = mMultiProfilePagerAdapter.getCurrentListAdapter().rebuildList();
         if (useLayoutWithDefault()) {
             mLayoutId = R.layout.resolver_list_with_default;
         } else {
             mLayoutId = getLayoutResource();
         }
         setContentView(mLayoutId);
+        mMultiProfilePagerAdapter.setupViewPager(findViewById(R.id.profile_pager));
         return postRebuildList(rebuildCompleted);
     }
 
@@ -1118,14 +1231,16 @@
      */
     final boolean postRebuildListInternal(boolean rebuildCompleted) {
 
-        int count = mAdapter.getUnfilteredCount();
+        int count = mMultiProfilePagerAdapter.getCurrentListAdapter().getUnfilteredCount();
 
         // We only rebuild asynchronously when we have multiple elements to sort. In the case where
         // we're already done, we can check if we should auto-launch immediately.
         if (rebuildCompleted) {
-            if (count == 1 && mAdapter.getOtherProfile() == null) {
+            if (count == 1
+                    && mMultiProfilePagerAdapter.getCurrentListAdapter().getOtherProfile() == null) {
                 // Only one target, so we're a candidate to auto-launch!
-                final TargetInfo target = mAdapter.targetInfoForPosition(0, false);
+                final TargetInfo target = mMultiProfilePagerAdapter.getCurrentListAdapter()
+                        .targetInfoForPosition(0, false);
                 if (shouldAutoLaunchSingleChoice(target)) {
                     safelyStartActivity(target);
                     mPackageMonitor.unregister();
@@ -1136,37 +1251,32 @@
             }
         }
 
-        boolean isAdapterViewVisible = true;
-        if (count == 0 && mAdapter.getPlaceholderCount() == 0) {
+        setupViewVisibilities(count);
+        return false;
+    }
+
+    private void setupViewVisibilities(int count) {
+        if (count == 0
+                && mMultiProfilePagerAdapter.getCurrentListAdapter().getPlaceholderCount() == 0) {
             final TextView emptyView = findViewById(R.id.empty);
             emptyView.setVisibility(View.VISIBLE);
-            isAdapterViewVisible = false;
+            findViewById(R.id.profile_pager).setVisibility(View.GONE);
+        } else {
+            onPrepareAdapterView(mMultiProfilePagerAdapter.getCurrentListAdapter());
         }
-
-        onPrepareAdapterView(mAdapter, isAdapterViewVisible);
-        return false;
     }
 
     /**
      * Prepare the scrollable view which consumes data in the list adapter.
      * @param adapter The adapter used to provide data to item views.
-     * @param isVisible True if the scrollable view should be visible; false, otherwise.
      */
-    public void onPrepareAdapterView(ResolverListAdapter adapter, boolean isVisible) {
-        mAdapterView = findViewById(R.id.resolver_list);
-        if (!isVisible) {
-            mAdapterView.setVisibility(View.GONE);
-            return;
-        }
-        mAdapterView.setVisibility(View.VISIBLE);
+    public void onPrepareAdapterView(ResolverListAdapter adapter) {
+        mMultiProfilePagerAdapter.getCurrentAdapterView().setVisibility(View.VISIBLE);
         final boolean useHeader = adapter.hasFilteredItem();
-        final ListView listView = mAdapterView instanceof ListView ? (ListView) mAdapterView : null;
-
-        mAdapterView.setAdapter(mAdapter);
-
+        final ListView listView = (ListView) mMultiProfilePagerAdapter.getCurrentAdapterView();
         final ItemClickListener listener = new ItemClickListener();
-        mAdapterView.setOnItemClickListener(listener);
-        mAdapterView.setOnItemLongClickListener(listener);
+        listView.setOnItemClickListener(listener);
+        listView.setOnItemLongClickListener(listener);
 
         if (mSupportsAlwaysUseOption || mUseLayoutForBrowsables) {
             listView.setChoiceMode(AbsListView.CHOICE_MODE_SINGLE);
@@ -1185,7 +1295,8 @@
      * Configure the area above the app selection list (title, content preview, etc).
      */
     public void setHeader() {
-        if (mAdapter.getCount() == 0 && mAdapter.getPlaceholderCount() == 0) {
+        if (mMultiProfilePagerAdapter.getCurrentListAdapter().getCount() == 0
+                && mMultiProfilePagerAdapter.getCurrentListAdapter().getPlaceholderCount() == 0) {
             final TextView titleView = findViewById(R.id.title);
             if (titleView != null) {
                 titleView.setVisibility(View.GONE);
@@ -1206,11 +1317,11 @@
 
         final ImageView iconView = findViewById(R.id.icon);
         if (iconView != null) {
-            mAdapter.loadFilteredItemIconTaskAsync(iconView);
+            mMultiProfilePagerAdapter.getCurrentListAdapter().loadFilteredItemIconTaskAsync(iconView);
         }
     }
 
-    private void resetButtonBar() {
+    protected void resetButtonBar() {
         if (!mSupportsAlwaysUseOption && !mUseLayoutForBrowsables) {
             return;
         }
@@ -1234,24 +1345,27 @@
     }
 
     private void resetAlwaysOrOnceButtonBar() {
-        if (useLayoutWithDefault()
-                && mAdapter.getFilteredPosition() != ListView.INVALID_POSITION) {
-            setAlwaysButtonEnabled(true, mAdapter.getFilteredPosition(), false);
+        int filteredPosition = mMultiProfilePagerAdapter.getCurrentListAdapter()
+                .getFilteredPosition();
+        if (useLayoutWithDefault() && filteredPosition != ListView.INVALID_POSITION) {
+            setAlwaysButtonEnabled(true, filteredPosition, false);
             mOnceButton.setEnabled(true);
             return;
         }
 
         // When the items load in, if an item was already selected, enable the buttons
-        if (mAdapterView != null
-                && mAdapterView.getCheckedItemPosition() != ListView.INVALID_POSITION) {
-            setAlwaysButtonEnabled(true, mAdapterView.getCheckedItemPosition(), true);
+        ListView currentAdapterView = (ListView) mMultiProfilePagerAdapter.getCurrentAdapterView();
+        if (currentAdapterView != null
+                && currentAdapterView.getCheckedItemPosition() != ListView.INVALID_POSITION) {
+            setAlwaysButtonEnabled(true, currentAdapterView.getCheckedItemPosition(), true);
             mOnceButton.setEnabled(true);
         }
     }
 
     @Override // ResolverListCommunicator
     public boolean useLayoutWithDefault() {
-        return mSupportsAlwaysUseOption && mAdapter.hasFilteredItem();
+        return mSupportsAlwaysUseOption
+                && mMultiProfilePagerAdapter.getCurrentListAdapter().hasFilteredItem();
     }
 
     /**
@@ -1275,7 +1389,7 @@
 
     @Override // ResolverListCommunicator
     public void onHandlePackagesChanged() {
-        if (mAdapter.getCount() == 0) {
+        if (mMultiProfilePagerAdapter.getCurrentListAdapter().getCount() == 0) {
             // We no longer have any items...  just finish the activity.
             finish();
         }
@@ -1350,11 +1464,14 @@
                 return;
             }
             // If we're still loading, we can't yet enable the buttons.
-            if (mAdapter.resolveInfoForPosition(position, true) == null) {
+            if (mMultiProfilePagerAdapter.getCurrentListAdapter()
+                    .resolveInfoForPosition(position, true) == null) {
                 return;
             }
 
-            final int checkedPos = mAdapterView.getCheckedItemPosition();
+            ListView currentAdapterView =
+                    (ListView) mMultiProfilePagerAdapter.getCurrentAdapterView();
+            final int checkedPos = currentAdapterView.getCheckedItemPosition();
             final boolean hasValidSelection = checkedPos != ListView.INVALID_POSITION;
             if (!useLayoutWithDefault()
                     && (!hasValidSelection || mLastSelected != checkedPos)
@@ -1362,7 +1479,7 @@
                 setAlwaysButtonEnabled(hasValidSelection, checkedPos, true);
                 mOnceButton.setEnabled(hasValidSelection);
                 if (hasValidSelection) {
-                    mAdapterView.smoothScrollToPosition(checkedPos);
+                    currentAdapterView.smoothScrollToPosition(checkedPos);
                 }
                 mLastSelected = checkedPos;
             } else {
@@ -1380,7 +1497,8 @@
                 // Header views don't count.
                 return false;
             }
-            ResolveInfo ri = mAdapter.resolveInfoForPosition(position, true);
+            ResolveInfo ri = mMultiProfilePagerAdapter.getCurrentListAdapter()
+                    .resolveInfoForPosition(position, true);
             showTargetDetails(ri);
             return true;
         }
@@ -1420,7 +1538,8 @@
 
             final ResolverActivity ra = (ResolverActivity) getActivity();
             if (ra != null) {
-                final TargetInfo ti = ra.mAdapter.getItem(selections[0].getIndex());
+                final TargetInfo ti = ra.mMultiProfilePagerAdapter.getCurrentListAdapter()
+                        .getItem(selections[0].getIndex());
                 if (ra.onTargetSelected(ti, false)) {
                     ra.mPickOptionRequest = null;
                     ra.finish();
diff --git a/core/java/com/android/internal/app/ResolverListAdapter.java b/core/java/com/android/internal/app/ResolverListAdapter.java
index bb7ca35..48064da 100644
--- a/core/java/com/android/internal/app/ResolverListAdapter.java
+++ b/core/java/com/android/internal/app/ResolverListAdapter.java
@@ -37,7 +37,6 @@
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.os.AsyncTask;
-import android.os.Process;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
@@ -81,7 +80,7 @@
 
     // This one is the list that the Adapter will actually present.
     List<DisplayResolveInfo> mDisplayList;
-    List<ResolvedComponentInfo> mUnfilteredResolveList;
+    private List<ResolvedComponentInfo> mUnfilteredResolveList;
 
     private int mLastChosenPosition = -1;
     private boolean mFilterLastUsed;
@@ -162,6 +161,10 @@
         mResolverListController.updateChooserCounts(packageName, userId, action);
     }
 
+    List<ResolvedComponentInfo> getUnfilteredResolveList() {
+        return mUnfilteredResolveList;
+    }
+
     /**
      * @return true if all items in the display list are defined as browsers by
      *         ResolveInfo.handleAllWebDataURI
@@ -576,7 +579,7 @@
 
     Drawable loadIconForResolveInfo(ResolveInfo ri) {
         // Load icons based on the current process. If in work profile icons should be badged.
-        return makePresentationGetter(ri).getIcon(Process.myUserHandle());
+        return makePresentationGetter(ri).getIcon(mResolverListController.getUserHandle());
     }
 
     void loadFilteredItemIconTaskAsync(@NonNull ImageView iconView) {
diff --git a/core/java/com/android/internal/app/ResolverListController.java b/core/java/com/android/internal/app/ResolverListController.java
index b456ca0..abd3eb2 100644
--- a/core/java/com/android/internal/app/ResolverListController.java
+++ b/core/java/com/android/internal/app/ResolverListController.java
@@ -28,6 +28,7 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.os.RemoteException;
+import android.os.UserHandle;
 import android.util.Log;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -55,6 +56,7 @@
 
     private static final String TAG = "ResolverListController";
     private static final boolean DEBUG = false;
+    private final UserHandle mUserHandle;
 
     private AbstractResolverComparator mResolverComparator;
     private boolean isComputed = false;
@@ -64,8 +66,9 @@
             PackageManager pm,
             Intent targetIntent,
             String referrerPackage,
-            int launchedFromUid) {
-        this(context, pm, targetIntent, referrerPackage, launchedFromUid,
+            int launchedFromUid,
+            UserHandle userHandle) {
+        this(context, pm, targetIntent, referrerPackage, launchedFromUid, userHandle,
                     new ResolverRankerServiceResolverComparator(
                         context, targetIntent, referrerPackage, null));
     }
@@ -76,12 +79,14 @@
             Intent targetIntent,
             String referrerPackage,
             int launchedFromUid,
+            UserHandle userHandle,
             AbstractResolverComparator resolverComparator) {
         mContext = context;
         mpm = pm;
         mLaunchedFromUid = launchedFromUid;
         mTargetIntent = targetIntent;
         mReferrerPackage = referrerPackage;
+        mUserHandle = userHandle;
         mResolverComparator = resolverComparator;
     }
 
@@ -116,7 +121,8 @@
                         || (intent.getFlags() & Intent.FLAG_ACTIVITY_MATCH_EXTERNAL) != 0) {
                 flags |= PackageManager.MATCH_INSTANT;
             }
-            final List<ResolveInfo> infos = mpm.queryIntentActivities(intent, flags);
+            final List<ResolveInfo> infos = mpm.queryIntentActivitiesAsUser(intent, flags,
+                    mUserHandle);
             if (infos != null) {
                 if (resolvedComponents == null) {
                     resolvedComponents = new ArrayList<>();
@@ -127,6 +133,10 @@
         return resolvedComponents;
     }
 
+    UserHandle getUserHandle() {
+        return mUserHandle;
+    }
+
     @VisibleForTesting
     public void addResolveListDedupe(List<ResolverActivity.ResolvedComponentInfo> into,
             Intent intent,
diff --git a/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java b/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java
new file mode 100644
index 0000000..9e814ab
--- /dev/null
+++ b/core/java/com/android/internal/app/ResolverMultiProfilePagerAdapter.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.app;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
+import android.widget.ListView;
+
+import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.widget.PagerAdapter;
+
+/**
+ * A {@link PagerAdapter} which describes the work and personal profile intent resolver screens.
+ */
+@VisibleForTesting
+public class ResolverMultiProfilePagerAdapter extends AbstractMultiProfilePagerAdapter {
+
+    private final ResolverProfileDescriptor[] mItems;
+
+    ResolverMultiProfilePagerAdapter(Context context,
+            ResolverListAdapter adapter) {
+        super(context, /* currentPage */ 0);
+        mItems = new ResolverProfileDescriptor[] {
+                createProfileDescriptor(adapter)
+        };
+    }
+
+    ResolverMultiProfilePagerAdapter(Context context,
+            ResolverListAdapter personalAdapter,
+            ResolverListAdapter workAdapter,
+            @Profile int defaultProfile) {
+        super(context, /* currentPage */ defaultProfile);
+        mItems = new ResolverProfileDescriptor[] {
+                createProfileDescriptor(personalAdapter),
+                createProfileDescriptor(workAdapter)
+        };
+    }
+
+    private ResolverProfileDescriptor createProfileDescriptor(
+            ResolverListAdapter adapter) {
+        final LayoutInflater inflater = LayoutInflater.from(getContext());
+        final ViewGroup rootView =
+                (ViewGroup) inflater.inflate(R.layout.resolver_list_per_profile, null, false);
+        return new ResolverProfileDescriptor(rootView, adapter);
+    }
+
+    ListView getListViewForIndex(int index) {
+        return getItem(index).listView;
+    }
+
+    @Override
+    ResolverProfileDescriptor getItem(int pageIndex) {
+        return mItems[pageIndex];
+    }
+
+    @Override
+    int getItemCount() {
+        return mItems.length;
+    }
+
+    @Override
+    void setupListAdapter(int pageIndex) {
+        final ListView listView = getItem(pageIndex).listView;
+        listView.setAdapter(getItem(pageIndex).resolverListAdapter);
+    }
+
+    @Override
+    ResolverListAdapter getAdapterForIndex(int pageIndex) {
+        return mItems[pageIndex].resolverListAdapter;
+    }
+
+    @Override
+    @VisibleForTesting
+    public ResolverListAdapter getCurrentListAdapter() {
+        return getAdapterForIndex(getCurrentPage());
+    }
+
+    @Override
+    ResolverListAdapter getCurrentRootAdapter() {
+        return getCurrentListAdapter();
+    }
+
+    @Override
+    ListView getCurrentAdapterView() {
+        return getListViewForIndex(getCurrentPage());
+    }
+
+    class ResolverProfileDescriptor extends ProfileDescriptor {
+        private ResolverListAdapter resolverListAdapter;
+        final ListView listView;
+        ResolverProfileDescriptor(ViewGroup rootView, ResolverListAdapter adapter) {
+            super(rootView);
+            resolverListAdapter = adapter;
+            listView = rootView.findViewById(R.id.resolver_list);
+        }
+    }
+}
diff --git a/core/java/com/android/internal/app/WrapHeightViewPager.java b/core/java/com/android/internal/app/WrapHeightViewPager.java
new file mode 100644
index 0000000..b017bb4
--- /dev/null
+++ b/core/java/com/android/internal/app/WrapHeightViewPager.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.app;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+
+import com.android.internal.widget.ViewPager;
+
+/**
+ * A {@link ViewPager} which wraps around its first child's height.
+ * <p>Normally {@link ViewPager} instances expand their height to cover all remaining space in
+ * the layout.
+ * <p>This class is used for the intent resolver picker's tabbed view to maintain
+ * consistency with the previous behavior.
+ */
+public class WrapHeightViewPager extends ViewPager {
+
+    public WrapHeightViewPager(Context context) {
+        super(context);
+    }
+
+    public WrapHeightViewPager(Context context, AttributeSet attrs) {
+        super(context, attrs);
+    }
+
+    public WrapHeightViewPager(Context context, AttributeSet attrs, int defStyleAttr) {
+        super(context, attrs, defStyleAttr);
+    }
+
+    public WrapHeightViewPager(Context context, AttributeSet attrs,
+            int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+    }
+
+    // TODO(arangelov): When we have multiple pages, the height should wrap to the currently
+    // displayed page. Investigate whether onMeasure is called when changing a page, and instead
+    // of getChildAt(0), use the currently displayed one.
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+        if (MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.AT_MOST) {
+            return;
+        }
+        widthMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY);
+        int height = getMeasuredHeight();
+        if (getChildCount() > 0) {
+            View firstChild = getChildAt(0);
+            firstChild.measure(widthMeasureSpec,
+                    MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST));
+            height = firstChild.getMeasuredHeight();
+        }
+        heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+    }
+}
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 025de5e..b91d359 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -144,6 +144,7 @@
                 "android_os_VintfRuntimeInfo.cpp",
                 "android_net_LocalSocketImpl.cpp",
                 "android_net_NetUtils.cpp",
+                "android_service_DataLoaderService.cpp",
                 "android_util_AssetManager.cpp",
                 "android_util_Binder.cpp",
                 "android_util_StatsLog.cpp",
@@ -240,6 +241,8 @@
                 "libGLESv1_CM",
                 "libGLESv2",
                 "libGLESv3",
+                "libincfs",
+                "libdataloader",
                 "libvulkan",
                 "libETC1",
                 "libhardware",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index d92ab49..b8fd3ad 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -151,6 +151,7 @@
 extern int register_android_os_HidlMemory(JNIEnv* env);
 extern int register_android_os_MemoryFile(JNIEnv* env);
 extern int register_android_os_SharedMemory(JNIEnv* env);
+extern int register_android_service_DataLoaderService(JNIEnv* env);
 extern int register_android_net_LocalSocketImpl(JNIEnv* env);
 extern int register_android_net_NetworkUtils(JNIEnv* env);
 extern int register_android_text_AndroidCharacter(JNIEnv *env);
@@ -1452,6 +1453,7 @@
     REG_JNI(register_android_os_NativeHandle),
     REG_JNI(register_android_os_VintfObject),
     REG_JNI(register_android_os_VintfRuntimeInfo),
+    REG_JNI(register_android_service_DataLoaderService),
     REG_JNI(register_android_view_DisplayEventReceiver),
     REG_JNI(register_android_view_RenderNodeAnimator),
     REG_JNI(register_android_view_InputApplicationHandle),
diff --git a/core/jni/android_service_DataLoaderService.cpp b/core/jni/android_service_DataLoaderService.cpp
new file mode 100644
index 0000000..4c0f55f
--- /dev/null
+++ b/core/jni/android_service_DataLoaderService.cpp
@@ -0,0 +1,269 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "dataloader-jni"
+
+#include <vector>
+
+#include "core_jni_helpers.h"
+#include "dataloader_ndk.h"
+#include "jni.h"
+
+namespace android {
+namespace {
+
+struct JniIds {
+    jfieldID dataBlockFileIno;
+    jfieldID dataBlockBlockIndex;
+    jfieldID dataBlockDataBytes;
+    jfieldID dataBlockCompressionType;
+
+    JniIds(JNIEnv* env) {
+        const auto dataBlock =
+                FindClassOrDie(env,
+                               "android/service/incremental/"
+                               "IncrementalDataLoaderService$FileSystemConnector$DataBlock");
+        dataBlockFileIno = GetFieldIDOrDie(env, dataBlock, "mFileIno", "J");
+        dataBlockBlockIndex =
+                GetFieldIDOrDie(env, dataBlock, "mBlockIndex", "I");
+        dataBlockDataBytes = GetFieldIDOrDie(env, dataBlock, "mDataBytes", "[B");
+        dataBlockCompressionType =
+                GetFieldIDOrDie(env, dataBlock, "mCompressionType", "I");
+    }
+};
+
+const JniIds& jniIds(JNIEnv* env) {
+    static const JniIds ids(env);
+    return ids;
+}
+
+class ScopedJniArrayCritical {
+public:
+    ScopedJniArrayCritical(JNIEnv* env, jarray array) : mEnv(env), mArr(array) {
+        mPtr = array ? env->GetPrimitiveArrayCritical(array, nullptr) : nullptr;
+    }
+    ~ScopedJniArrayCritical() {
+        if (mPtr) {
+            mEnv->ReleasePrimitiveArrayCritical(mArr, mPtr, 0);
+            mPtr = nullptr;
+        }
+    }
+
+    ScopedJniArrayCritical(const ScopedJniArrayCritical&) = delete;
+    void operator=(const ScopedJniArrayCritical&) = delete;
+
+    ScopedJniArrayCritical(ScopedJniArrayCritical&& other)
+        : mEnv(other.mEnv),
+          mArr(std::exchange(mArr, nullptr)),
+          mPtr(std::exchange(mPtr, nullptr)) {}
+    ScopedJniArrayCritical& operator=(ScopedJniArrayCritical&& other) {
+        mEnv = other.mEnv;
+        mArr = std::exchange(other.mArr, nullptr);
+        mPtr = std::exchange(other.mPtr, nullptr);
+        return *this;
+    }
+
+    void* ptr() const { return mPtr; }
+    jsize size() const { return mArr ? mEnv->GetArrayLength(mArr) : 0; }
+
+private:
+    JNIEnv* mEnv;
+    jarray mArr;
+    void* mPtr;
+};
+
+static jboolean nativeCreateDataLoader(JNIEnv* env,
+                                       jobject thiz,
+                                       jint storageId,
+                                       jobject control,
+                                       jobject params,
+                                       jobject callback) {
+    ALOGE("nativeCreateDataLoader: %p/%d, %d, %p, %p, %p", thiz,
+          env->GetObjectRefType(thiz), storageId, params, control, callback);
+    return DataLoaderService_OnCreate(env, thiz,
+                     storageId, control, params, callback);
+}
+
+static jboolean nativeStartDataLoader(JNIEnv* env,
+                                      jobject thiz,
+                                      jint storageId) {
+    ALOGE("nativeStartDataLoader: %p/%d, %d", thiz, env->GetObjectRefType(thiz),
+          storageId);
+    return DataLoaderService_OnStart(storageId);
+}
+
+static jboolean nativeStopDataLoader(JNIEnv* env,
+                                     jobject thiz,
+                                     jint storageId) {
+    ALOGE("nativeStopDataLoader: %p/%d, %d", thiz, env->GetObjectRefType(thiz),
+          storageId);
+    return DataLoaderService_OnStop(storageId);
+}
+
+static jboolean nativeDestroyDataLoader(JNIEnv* env,
+                                        jobject thiz,
+                                        jint storageId) {
+    ALOGE("nativeDestroyDataLoader: %p/%d, %d", thiz,
+          env->GetObjectRefType(thiz), storageId);
+    return DataLoaderService_OnDestroy(storageId);
+}
+
+
+static jboolean nativeOnFileCreated(JNIEnv* env,
+                                   jobject thiz,
+                                   jint storageId,
+                                   jlong inode,
+                                   jbyteArray metadata) {
+    ALOGE("nativeOnFileCreated: %p/%d, %d", thiz,
+          env->GetObjectRefType(thiz), storageId);
+    return DataLoaderService_OnFileCreated(storageId, inode, metadata);
+}
+
+static jboolean nativeIsFileRangeLoadedNode(JNIEnv* env,
+                                            jobject clazz,
+                                            jlong self,
+                                            jlong node,
+                                            jlong start,
+                                            jlong end) {
+    // TODO(b/136132412): implement this
+    return JNI_FALSE;
+}
+
+static jboolean nativeWriteMissingData(JNIEnv* env,
+                                       jobject clazz,
+                                       jlong self,
+                                       jobjectArray data_block,
+                                       jobjectArray hash_blocks) {
+    const auto& jni = jniIds(env);
+    auto length = env->GetArrayLength(data_block);
+    std::vector<incfs_new_data_block> instructions(length);
+
+    // May not call back into Java after even a single jniArrayCritical, so
+    // let's collect the Java pointers to byte buffers first and lock them in
+    // memory later.
+
+    std::vector<jbyteArray> blockBuffers(length);
+    for (int i = 0; i != length; ++i) {
+        auto& inst = instructions[i];
+        auto jniBlock = env->GetObjectArrayElement(data_block, i);
+        inst.file_ino = env->GetLongField(jniBlock, jni.dataBlockFileIno);
+        inst.block_index = env->GetIntField(jniBlock, jni.dataBlockBlockIndex);
+        blockBuffers[i] = (jbyteArray)env->GetObjectField(
+                jniBlock, jni.dataBlockDataBytes);
+        inst.compression = (incfs_compression_alg)env->GetIntField(
+                jniBlock, jni.dataBlockCompressionType);
+    }
+
+    std::vector<ScopedJniArrayCritical> jniScopedArrays;
+    jniScopedArrays.reserve(length);
+    for (int i = 0; i != length; ++i) {
+        auto buffer = blockBuffers[i];
+        jniScopedArrays.emplace_back(env, buffer);
+        auto& inst = instructions[i];
+        inst.data = (uint64_t)jniScopedArrays.back().ptr();
+        inst.data_len = jniScopedArrays.back().size();
+    }
+
+    auto connector = (DataLoaderFilesystemConnectorPtr)self;
+    if (auto err = DataLoader_FilesystemConnector_writeBlocks(
+                             connector, instructions.data(), length);
+        err < 0) {
+        jniScopedArrays.clear();
+        return JNI_FALSE;
+    }
+
+    return JNI_TRUE;
+}
+
+static jboolean nativeWriteSignerDataNode(JNIEnv* env,
+                                          jobject clazz,
+                                          jlong self,
+                                          jstring relative_path,
+                                          jbyteArray signer_data) {
+    // TODO(b/136132412): implement this
+    return JNI_TRUE;
+}
+
+static jbyteArray nativeGetFileMetadataNode(JNIEnv* env,
+                                            jobject clazz,
+                                            jlong self,
+                                            jlong inode) {
+    auto connector = (DataLoaderFilesystemConnectorPtr)self;
+    std::vector<char> metadata(INCFS_MAX_FILE_ATTR_SIZE);
+    size_t size = metadata.size();
+    if (DataLoader_FilesystemConnector_getRawMetadata(connector, inode,
+                  metadata.data(), &size) < 0) {
+        size = 0;
+    }
+    metadata.resize(size);
+
+    auto buffer = env->NewByteArray(metadata.size());
+    env->SetByteArrayRegion(buffer, 0, metadata.size(),
+                            (jbyte*)metadata.data());
+    return buffer;
+}
+
+static jbyteArray nativeGetFileInfoNode(JNIEnv* env,
+                                        jobject clazz,
+                                        jlong self,
+                                        jlong inode) {
+    // TODO(b/136132412): implement this
+    return nullptr;
+}
+
+static jboolean nativeReportStatus(JNIEnv* env,
+                                   jobject clazz,
+                                   jlong self,
+                                   jint status) {
+    auto listener = (DataLoaderStatusListenerPtr)self;
+    return DataLoader_StatusListener_reportStatus(listener,
+                     (DataLoaderStatus)status);
+}
+
+static const JNINativeMethod dlc_method_table[] = {
+        {"nativeCreateDataLoader",
+         "(ILandroid/os/incremental/IncrementalFileSystemControlParcel;"
+         "Landroid/os/incremental/IncrementalDataLoaderParamsParcel;"
+         "Landroid/content/pm/IDataLoaderStatusListener;)Z",
+         (void*)nativeCreateDataLoader},
+        {"nativeStartDataLoader", "(I)Z", (void*)nativeStartDataLoader},
+        {"nativeStopDataLoader", "(I)Z", (void*)nativeStopDataLoader},
+        {"nativeDestroyDataLoader", "(I)Z", (void*)nativeDestroyDataLoader},
+        {"nativeIsFileRangeLoadedNode", "(JJJJ)Z",
+         (void*)nativeIsFileRangeLoadedNode},
+        {"nativeWriteMissingData",
+         "(J[Landroid/service/incremental/"
+         "IncrementalDataLoaderService$FileSystemConnector$DataBlock;[Landroid/service/incremental/"
+         "IncrementalDataLoaderService$FileSystemConnector$HashBlock;)Z",
+         (void*)nativeWriteMissingData},
+        {"nativeWriteSignerDataNode", "(JJ[B)Z",
+         (void*)nativeWriteSignerDataNode},
+        {"nativeGetFileMetadataNode", "(JJ)[B",
+         (void*)nativeGetFileMetadataNode},
+        {"nativeGetFileInfoNode", "(JJ)[B", (void*)nativeGetFileInfoNode},
+        {"nativeReportStatus", "(JI)Z", (void*)nativeReportStatus},
+        {"nativeOnFileCreated", "(IJ[B)Z", (void*)nativeOnFileCreated},
+};
+
+}  // namespace
+
+int register_android_service_DataLoaderService(JNIEnv* env) {
+    return jniRegisterNativeMethods(env,
+                                    "android/service/incremental/IncrementalDataLoaderService",
+                                    dlc_method_table, NELEM(dlc_method_table));
+}
+
+}  // namespace android
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index 24456d8..0c74842 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -163,6 +163,7 @@
     repeated IdentifierProto opening_apps = 17;
     repeated IdentifierProto closing_apps = 18;
     repeated IdentifierProto changing_apps = 19;
+    repeated WindowTokenProto overlay_windows = 20;
 }
 
 /* represents DisplayFrames */
diff --git a/core/res/res/layout/chooser_grid.xml b/core/res/res/layout/chooser_grid.xml
index 6807f9a..9f296f8f 100644
--- a/core/res/res/layout/chooser_grid.xml
+++ b/core/res/res/layout/chooser_grid.xml
@@ -29,7 +29,7 @@
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:layout_alwaysShow="true"
-        android:elevation="1dp"
+        android:elevation="0dp"
         android:background="@drawable/bottomsheet_background">
 
         <ImageView
@@ -55,16 +55,10 @@
                   android:layout_centerHorizontal="true"/>
     </RelativeLayout>
 
-    <com.android.internal.widget.RecyclerView
+    <com.android.internal.widget.ViewPager
+        android:id="@+id/profile_pager"
         android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:layoutManager="com.android.internal.widget.GridLayoutManager"
-        android:id="@+id/resolver_list"
-        android:clipToPadding="false"
-        android:background="?attr/colorBackgroundFloating"
-        android:scrollbars="none"
-        android:elevation="1dp"
-        android:nestedScrollingEnabled="true"/>
+        android:layout_height="wrap_content"/>
 
     <TextView android:id="@+id/empty"
               android:layout_width="match_parent"
diff --git a/core/res/res/layout/chooser_list_per_profile.xml b/core/res/res/layout/chooser_list_per_profile.xml
new file mode 100644
index 0000000..212813f
--- /dev/null
+++ b/core/res/res/layout/chooser_list_per_profile.xml
@@ -0,0 +1,27 @@
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+
+<com.android.internal.widget.RecyclerView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:layoutManager="com.android.internal.widget.GridLayoutManager"
+    android:id="@+id/resolver_list"
+    android:clipToPadding="false"
+    android:background="?attr/colorBackgroundFloating"
+    android:scrollbars="none"
+    android:elevation="1dp"
+    android:nestedScrollingEnabled="true"/>
\ No newline at end of file
diff --git a/core/res/res/layout/resolver_list.xml b/core/res/res/layout/resolver_list.xml
index 6e45e7a..c5d8912 100644
--- a/core/res/res/layout/resolver_list.xml
+++ b/core/res/res/layout/resolver_list.xml
@@ -63,32 +63,28 @@
     </RelativeLayout>
 
     <View
-        android:layout_alwaysShow="true"
-        android:layout_width="match_parent"
-        android:layout_height="1dp"
-        android:background="?attr/colorBackgroundFloating"
-        android:foreground="?attr/dividerVertical" />
-    <ListView
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:id="@+id/resolver_list"
-        android:clipToPadding="false"
-        android:background="?attr/colorBackgroundFloating"
-        android:elevation="@dimen/resolver_elevation"
-        android:nestedScrollingEnabled="true"
-        android:scrollbarStyle="outsideOverlay"
-        android:scrollIndicators="top|bottom"
-        android:divider="?attr/dividerVertical"
-        android:footerDividersEnabled="false"
-        android:headerDividersEnabled="false"
-        android:dividerHeight="1dp" />
-    <View
+        android:id="@+id/divider"
         android:layout_alwaysShow="true"
         android:layout_width="match_parent"
         android:layout_height="1dp"
         android:background="?attr/colorBackgroundFloating"
         android:foreground="?attr/dividerVertical" />
 
+    <com.android.internal.app.WrapHeightViewPager
+        android:id="@+id/profile_pager"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:divider="?attr/dividerVertical"
+        android:footerDividersEnabled="false"
+        android:headerDividersEnabled="false"
+        android:dividerHeight="1dp"/>
+
+    <View
+        android:layout_alwaysShow="true"
+        android:layout_width="match_parent"
+        android:layout_height="1dp"
+        android:background="?attr/colorBackgroundFloating"
+        android:foreground="?attr/dividerVertical" />
 
     <TextView android:id="@+id/empty"
               android:layout_width="match_parent"
diff --git a/core/res/res/layout/resolver_list_per_profile.xml b/core/res/res/layout/resolver_list_per_profile.xml
new file mode 100644
index 0000000..68b9917
--- /dev/null
+++ b/core/res/res/layout/resolver_list_per_profile.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  -->
+<ListView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:id="@+id/resolver_list"
+    android:clipToPadding="false"
+    android:background="?attr/colorBackgroundFloating"
+    android:elevation="@dimen/resolver_elevation"
+    android:nestedScrollingEnabled="true"
+    android:scrollbarStyle="outsideOverlay"
+    android:scrollIndicators="top|bottom"
+    android:divider="?attr/dividerVertical"
+    android:footerDividersEnabled="false"
+    android:headerDividersEnabled="false"
+    android:dividerHeight="1dp" />
\ No newline at end of file
diff --git a/core/res/res/layout/resolver_list_with_default.xml b/core/res/res/layout/resolver_list_with_default.xml
index dbba0b7..5b3d929 100644
--- a/core/res/res/layout/resolver_list_with_default.xml
+++ b/core/res/res/layout/resolver_list_with_default.xml
@@ -144,25 +144,21 @@
     </LinearLayout>
 
     <View
+        android:id="@+id/divider"
         android:layout_alwaysShow="true"
         android:layout_width="match_parent"
         android:layout_height="1dp"
         android:background="?attr/colorBackgroundFloating"
         android:foreground="?attr/dividerVertical" />
-    <ListView
+
+    <com.android.internal.app.WrapHeightViewPager
+        android:id="@+id/profile_pager"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:id="@+id/resolver_list"
-        android:clipToPadding="false"
-        android:background="?attr/colorBackgroundFloating"
-        android:elevation="@dimen/resolver_elevation"
-        android:nestedScrollingEnabled="true"
-        android:scrollbarStyle="outsideOverlay"
-        android:scrollIndicators="top|bottom"
+        android:dividerHeight="1dp"
         android:divider="?attr/dividerVertical"
         android:footerDividersEnabled="false"
-        android:headerDividersEnabled="false"
-        android:dividerHeight="1dp" />
+        android:headerDividersEnabled="false"/>
     <View
         android:layout_alwaysShow="true"
         android:layout_width="match_parent"
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index fc1c358..90343e0 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -247,6 +247,7 @@
   <java-symbol type="id" name="mic" />
   <java-symbol type="id" name="overlay" />
   <java-symbol type="id" name="app_ops" />
+  <java-symbol type="id" name="profile_pager" />
 
   <java-symbol type="attr" name="actionModeShareDrawable" />
   <java-symbol type="attr" name="alertDialogCenterButtons" />
@@ -1533,6 +1534,8 @@
   <java-symbol type="layout" name="user_switching_dialog" />
   <java-symbol type="layout" name="common_tab_settings" />
   <java-symbol type="layout" name="notification_material_media_seekbar" />
+  <java-symbol type="layout" name="resolver_list_per_profile" />
+  <java-symbol type="layout" name="chooser_list_per_profile" />
 
   <java-symbol type="anim" name="slide_in_child_bottom" />
   <java-symbol type="anim" name="slide_in_right" />
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
index d427cbd..8622b7e 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserActivityTest.java
@@ -313,7 +313,7 @@
         assertThat(activity.isFinishing(), is(false));
 
         onView(withId(R.id.empty)).check(matches(isDisplayed()));
-        onView(withId(R.id.resolver_list)).check(matches(not(isDisplayed())));
+        onView(withId(R.id.profile_pager)).check(matches(not(isDisplayed())));
         InstrumentationRegistry.getInstrumentation().runOnMainSync(
                 () -> activity.getAdapter().handlePackagesChanged()
         );
@@ -674,12 +674,12 @@
 
         mActivityRule.launchActivity(Intent.createChooser(sendIntent, null));
         waitForIdle();
+        // Second invocation is from onCreate
         verify(mockLogger, Mockito.times(2)).write(logMakerCaptor.capture());
-        // First invocation is from onCreate
-        assertThat(logMakerCaptor.getAllValues().get(1).getCategory(),
-                is(MetricsEvent.ACTION_SHARE_WITH_PREVIEW));
-        assertThat(logMakerCaptor.getAllValues().get(1).getSubtype(),
+        assertThat(logMakerCaptor.getAllValues().get(0).getSubtype(),
                 is(CONTENT_PREVIEW_TEXT));
+        assertThat(logMakerCaptor.getAllValues().get(0).getCategory(),
+                is(MetricsEvent.ACTION_SHARE_WITH_PREVIEW));
     }
 
     @Test
@@ -706,10 +706,10 @@
         waitForIdle();
         verify(mockLogger, Mockito.times(2)).write(logMakerCaptor.capture());
         // First invocation is from onCreate
-        assertThat(logMakerCaptor.getAllValues().get(1).getCategory(),
-                is(MetricsEvent.ACTION_SHARE_WITH_PREVIEW));
-        assertThat(logMakerCaptor.getAllValues().get(1).getSubtype(),
+        assertThat(logMakerCaptor.getAllValues().get(0).getSubtype(),
                 is(CONTENT_PREVIEW_IMAGE));
+        assertThat(logMakerCaptor.getAllValues().get(0).getCategory(),
+                is(MetricsEvent.ACTION_SHARE_WITH_PREVIEW));
     }
 
     @Test
diff --git a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
index 03705d0..a2e0095 100644
--- a/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
+++ b/core/tests/coretests/src/com/android/internal/app/ChooserWrapperActivity.java
@@ -29,6 +29,7 @@
 import android.database.Cursor;
 import android.graphics.Bitmap;
 import android.net.Uri;
+import android.os.UserHandle;
 import android.util.Size;
 
 import com.android.internal.app.ResolverListAdapter.ResolveInfoPresentationGetter;
@@ -47,7 +48,7 @@
     private UsageStatsManager mUsm;
 
     ChooserListAdapter getAdapter() {
-        return (ChooserListAdapter) mAdapter;
+        return mChooserMultiProfilePagerAdapter.getCurrentListAdapter();
     }
 
     boolean getIsSelected() { return mIsSuccessfullySelected; }
@@ -77,7 +78,7 @@
     }
 
     @Override
-    protected ResolverListController createListController() {
+    protected ResolverListController createListController(UserHandle userHandle) {
         return sOverrides.resolverListController;
     }
 
diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
index 344c286..923ce3e 100644
--- a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
@@ -116,14 +116,14 @@
         waitForIdle();
 
         final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
-        final View resolverList = activity.findViewById(R.id.resolver_list);
-        final int initialResolverHeight = resolverList.getHeight();
+        final View viewPager = activity.findViewById(R.id.profile_pager);
+        final int initialResolverHeight = viewPager.getHeight();
 
         activity.runOnUiThread(() -> {
             ResolverDrawerLayout layout = (ResolverDrawerLayout)
                     activity.findViewById(
                             R.id.contentPanel);
-            ((ResolverDrawerLayout.LayoutParams) resolverList.getLayoutParams()).maxHeight
+            ((ResolverDrawerLayout.LayoutParams) viewPager.getLayoutParams()).maxHeight
                 = initialResolverHeight - 1;
             // Force a relayout
             layout.invalidate();
@@ -131,13 +131,13 @@
         });
         waitForIdle();
         assertThat("Drawer should be capped at maxHeight",
-            resolverList.getHeight() == (initialResolverHeight - 1));
+                viewPager.getHeight() == (initialResolverHeight - 1));
 
         activity.runOnUiThread(() -> {
             ResolverDrawerLayout layout = (ResolverDrawerLayout)
                     activity.findViewById(
                             R.id.contentPanel);
-            ((ResolverDrawerLayout.LayoutParams) resolverList.getLayoutParams()).maxHeight
+            ((ResolverDrawerLayout.LayoutParams) viewPager.getLayoutParams()).maxHeight
                 = initialResolverHeight + 1;
             // Force a relayout
             layout.invalidate();
@@ -145,7 +145,7 @@
         });
         waitForIdle();
         assertThat("Drawer should not change height if its height is less than maxHeight",
-            resolverList.getHeight() == initialResolverHeight);
+                viewPager.getHeight() == initialResolverHeight);
     }
 
     @Ignore // Failing - b/144929805
@@ -160,11 +160,13 @@
         waitForIdle();
 
         final ResolverWrapperActivity activity = mActivityRule.launchActivity(sendIntent);
-        final View resolverList = activity.findViewById(R.id.resolver_list);
+        final View viewPager = activity.findViewById(R.id.profile_pager);
+        final View divider = activity.findViewById(R.id.divider);
         final RelativeLayout profileView =
             (RelativeLayout) activity.findViewById(R.id.profile_button).getParent();
         assertThat("Drawer should show at bottom by default",
-                profileView.getBottom() == resolverList.getTop() && profileView.getTop() > 0);
+                profileView.getBottom() + divider.getHeight() == viewPager.getTop()
+                        && profileView.getTop() > 0);
 
         activity.runOnUiThread(() -> {
             ResolverDrawerLayout layout = (ResolverDrawerLayout)
@@ -174,7 +176,8 @@
         });
         waitForIdle();
         assertThat("Drawer should show at top with new attribute",
-            profileView.getBottom() == resolverList.getTop() && profileView.getTop() == 0);
+            profileView.getBottom() + divider.getHeight() == viewPager.getTop()
+                    && profileView.getTop() == 0);
     }
 
     @Test
diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverListControllerTest.java b/core/tests/coretests/src/com/android/internal/app/ResolverListControllerTest.java
index 5ac1489..64906bb 100644
--- a/core/tests/coretests/src/com/android/internal/app/ResolverListControllerTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ResolverListControllerTest.java
@@ -115,7 +115,7 @@
         mUsm = new UsageStatsManager(mMockContext, mMockService);
         when(mMockContext.getSystemService(Context.USAGE_STATS_SERVICE)).thenReturn(mUsm);
         mController = new ResolverListController(mMockContext, mMockPackageManager, sendIntent,
-                refererPackage, UserHandle.USER_CURRENT);
+                refererPackage, UserHandle.USER_CURRENT, /* userHandle */ UserHandle.SYSTEM);
         mController.sort(new ArrayList<ResolvedComponentInfo>());
         long beforeReport = getCount(mUsm, packageName, action, annotation);
         mController.updateChooserCounts(packageName, UserHandle.USER_CURRENT, action);
@@ -132,7 +132,7 @@
         mUsm = new UsageStatsManager(mMockContext, mMockService);
         when(mMockContext.getSystemService(Context.USAGE_STATS_SERVICE)).thenReturn(mUsm);
         mController = new ResolverListController(mMockContext, mMockPackageManager, sendIntent,
-                refererPackage, UserHandle.USER_CURRENT);
+                refererPackage, UserHandle.USER_CURRENT, /* userHandle */ UserHandle.SYSTEM);
         List<ResolvedComponentInfo> topKList = new ArrayList<>(resolvedComponents);
         mController.topK(topKList, 5);
         List<ResolvedComponentInfo> sortList = new ArrayList<>(topKList);
diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverWrapperActivity.java b/core/tests/coretests/src/com/android/internal/app/ResolverWrapperActivity.java
index 39cc83c..93357af 100644
--- a/core/tests/coretests/src/com/android/internal/app/ResolverWrapperActivity.java
+++ b/core/tests/coretests/src/com/android/internal/app/ResolverWrapperActivity.java
@@ -23,6 +23,7 @@
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
+import android.os.UserHandle;
 
 import com.android.internal.app.chooser.TargetInfo;
 
@@ -37,15 +38,15 @@
     private UsageStatsManager mUsm;
 
     @Override
-    public ResolverListAdapter createAdapter(Context context, List<Intent> payloadIntents,
-            Intent[] initialIntents, List<ResolveInfo> rList, boolean filterLastUsed,
-            boolean useLayoutForBrowsables) {
+    public ResolverListAdapter createResolverListAdapter(Context context,
+            List<Intent> payloadIntents, Intent[] initialIntents, List<ResolveInfo> rList,
+            boolean filterLastUsed, boolean useLayoutForBrowsables, UserHandle userHandle) {
         return new ResolverWrapperAdapter(context, payloadIntents, initialIntents, rList,
-                filterLastUsed, createListController(), useLayoutForBrowsables, this);
+                filterLastUsed, createListController(userHandle), useLayoutForBrowsables, this);
     }
 
     ResolverWrapperAdapter getAdapter() {
-        return (ResolverWrapperAdapter) mAdapter;
+        return (ResolverWrapperAdapter) mMultiProfilePagerAdapter.getCurrentListAdapter();
     }
 
     @Override
@@ -66,7 +67,7 @@
     }
 
     @Override
-    protected ResolverListController createListController() {
+    protected ResolverListController createListController(UserHandle userHandle) {
         return sOverrides.resolverListController;
     }
 
diff --git a/graphics/java/android/graphics/ImageDecoder.java b/graphics/java/android/graphics/ImageDecoder.java
index 6619dba..8ebac66 100644
--- a/graphics/java/android/graphics/ImageDecoder.java
+++ b/graphics/java/android/graphics/ImageDecoder.java
@@ -1675,6 +1675,9 @@
         if (r == null) {
             return;
         }
+        if (r.width() <= 0 || r.height() <= 0) {
+            throw new IllegalStateException("Subset " + r + " is empty/unsorted");
+        }
         if (r.left < 0 || r.top < 0 || r.right > width || r.bottom > height) {
             throw new IllegalStateException("Subset " + r + " not contained by "
                     + "scaled image bounds: (" + width + " x " + height + ")");
diff --git a/media/apex/java/android/media/MediaParser.java b/media/apex/java/android/media/MediaParser.java
index 8824269..b83f445 100644
--- a/media/apex/java/android/media/MediaParser.java
+++ b/media/apex/java/android/media/MediaParser.java
@@ -156,19 +156,29 @@
      * <p>A {@link SeekPoint} is a position in the stream from which a player may successfully start
      * playing media samples.
      */
-    public interface SeekMap {
+    public static final class SeekMap {
 
         /** Returned by {@link #getDurationUs()} when the duration is unknown. */
-        int UNKNOWN_DURATION = Integer.MIN_VALUE;
+        public static final int UNKNOWN_DURATION = Integer.MIN_VALUE;
+
+        private final com.google.android.exoplayer2.extractor.SeekMap mExoPlayerSeekMap;
+
+        private SeekMap(com.google.android.exoplayer2.extractor.SeekMap exoplayerSeekMap) {
+            mExoPlayerSeekMap = exoplayerSeekMap;
+        }
 
         /** Returns whether seeking is supported. */
-        boolean isSeekable();
+        public boolean isSeekable() {
+            return mExoPlayerSeekMap.isSeekable();
+        }
 
         /**
          * Returns the duration of the stream in microseconds or {@link #UNKNOWN_DURATION} if the
          * duration is unknown.
          */
-        long getDurationUs();
+        public long getDurationUs() {
+            return mExoPlayerSeekMap.getDurationUs();
+        }
 
         /**
          * Obtains {@link SeekPoint SeekPoints} for the specified seek time in microseconds.
@@ -184,7 +194,10 @@
          * @return The corresponding {@link SeekPoint SeekPoints}.
          */
         @NonNull
-        Pair<SeekPoint, SeekPoint> getSeekPoints(long timeUs);
+        public Pair<SeekPoint, SeekPoint> getSeekPoints(long timeUs) {
+            SeekPoints seekPoints = mExoPlayerSeekMap.getSeekPoints(timeUs);
+            return new Pair<>(toSeekPoint(seekPoints.first), toSeekPoint(seekPoints.second));
+        }
     }
 
     /** Defines a seek point in a media stream. */
@@ -647,7 +660,7 @@
 
         @Override
         public void seekMap(com.google.android.exoplayer2.extractor.SeekMap exoplayerSeekMap) {
-            mOutputConsumer.onSeekMap(new ExoToMediaParserSeekMapAdapter(exoplayerSeekMap));
+            mOutputConsumer.onSeekMap(new SeekMap(exoplayerSeekMap));
         }
     }
 
@@ -764,32 +777,6 @@
         Extractor createInstance();
     }
 
-    private static class ExoToMediaParserSeekMapAdapter implements SeekMap {
-
-        private final com.google.android.exoplayer2.extractor.SeekMap mExoPlayerSeekMap;
-
-        private ExoToMediaParserSeekMapAdapter(
-                com.google.android.exoplayer2.extractor.SeekMap exoplayerSeekMap) {
-            mExoPlayerSeekMap = exoplayerSeekMap;
-        }
-
-        @Override
-        public boolean isSeekable() {
-            return mExoPlayerSeekMap.isSeekable();
-        }
-
-        @Override
-        public long getDurationUs() {
-            return mExoPlayerSeekMap.getDurationUs();
-        }
-
-        @Override
-        public Pair<SeekPoint, SeekPoint> getSeekPoints(long timeUs) {
-            SeekPoints seekPoints = mExoPlayerSeekMap.getSeekPoints(timeUs);
-            return new Pair<>(toSeekPoint(seekPoints.first), toSeekPoint(seekPoints.second));
-        }
-    }
-
     // Private static methods.
 
     private static MediaFormat toMediaFormat(Format format) {
diff --git a/media/java/android/media/tv/tuner/DvrSettings.java b/media/java/android/media/tv/tuner/DvrSettings.java
new file mode 100644
index 0000000..76160dc
--- /dev/null
+++ b/media/java/android/media/tv/tuner/DvrSettings.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.tuner;
+
+import android.media.tv.tuner.TunerConstants.DataFormat;
+import android.media.tv.tuner.TunerConstants.DvrSettingsType;
+
+/**
+ * DVR settings.
+ *
+ * @hide
+ */
+public class DvrSettings {
+    private int mStatusMask;
+    private int mLowThreshold;
+    private int mHighThreshold;
+    private int mPacketSize;
+
+    @DataFormat
+    private int mDataFormat;
+    @DvrSettingsType
+    private int mType;
+
+    private DvrSettings(int statusMask, int lowThreshold, int highThreshold, int packetSize,
+            @DataFormat int dataFormat, @DvrSettingsType int type) {
+        mStatusMask = statusMask;
+        mLowThreshold = lowThreshold;
+        mHighThreshold = highThreshold;
+        mPacketSize = packetSize;
+        mDataFormat = dataFormat;
+        mType = type;
+    }
+
+    /**
+     * Creates a new builder.
+     */
+    public static Builder newBuilder() {
+        return new Builder();
+    }
+
+    /**
+     * Builder for DvrSettings.
+     */
+    public static final class Builder {
+        private int mStatusMask;
+        private int mLowThreshold;
+        private int mHighThreshold;
+        private int mPacketSize;
+        @DataFormat
+        private int mDataFormat;
+        @DvrSettingsType
+        private int mType;
+
+        /**
+         * Sets status mask.
+         */
+        public Builder setStatusMask(int statusMask) {
+            this.mStatusMask = statusMask;
+            return this;
+        }
+
+        /**
+         * Sets low threshold.
+         */
+        public Builder setLowThreshold(int lowThreshold) {
+            this.mLowThreshold = lowThreshold;
+            return this;
+        }
+
+        /**
+         * Sets high threshold.
+         */
+        public Builder setHighThreshold(int highThreshold) {
+            this.mHighThreshold = highThreshold;
+            return this;
+        }
+
+        /**
+         * Sets packet size.
+         */
+        public Builder setPacketSize(int packetSize) {
+            this.mPacketSize = packetSize;
+            return this;
+        }
+
+        /**
+         * Sets data format.
+         */
+        public Builder setDataFormat(@DataFormat int dataFormat) {
+            this.mDataFormat = dataFormat;
+            return this;
+        }
+
+        /**
+         * Sets settings type.
+         */
+        public Builder setType(@DvrSettingsType int type) {
+            this.mType = type;
+            return this;
+        }
+
+        /**
+         * Builds a DvrSettings instance.
+         */
+        public DvrSettings build() {
+            return new DvrSettings(
+                    mStatusMask, mLowThreshold, mHighThreshold, mPacketSize, mDataFormat, mType);
+        }
+    }
+}
diff --git a/media/java/android/media/tv/tuner/Tuner.java b/media/java/android/media/tv/tuner/Tuner.java
index 6537f6f..4c93101 100644
--- a/media/java/android/media/tv/tuner/Tuner.java
+++ b/media/java/android/media/tv/tuner/Tuner.java
@@ -368,6 +368,7 @@
 
         private native boolean nativeAttachFilter(Filter filter);
         private native boolean nativeDetachFilter(Filter filter);
+        private native int nativeConfigureDvr(DvrSettings settings);
         private native boolean nativeStartDvr();
         private native boolean nativeStopDvr();
         private native boolean nativeFlushDvr();
@@ -380,6 +381,9 @@
         public boolean detachFilter(Filter filter) {
             return nativeDetachFilter(filter);
         }
+        public int configure(DvrSettings settings) {
+            return nativeConfigureDvr(settings);
+        }
         public boolean start() {
             return nativeStartDvr();
         }
diff --git a/media/java/android/media/tv/tuner/TunerConstants.java b/media/java/android/media/tv/tuner/TunerConstants.java
index f2d5e93..261b2de 100644
--- a/media/java/android/media/tv/tuner/TunerConstants.java
+++ b/media/java/android/media/tv/tuner/TunerConstants.java
@@ -101,6 +101,13 @@
     public static final int FILTER_SETTINGS_TLV = Constants.DemuxFilterMainType.TLV;
     public static final int FILTER_SETTINGS_ALP = Constants.DemuxFilterMainType.ALP;
 
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({DVR_SETTINGS_RECORD, DVR_SETTINGS_PLAYBACK})
+    public @interface DvrSettingsType {}
+
+    public static final int DVR_SETTINGS_RECORD = Constants.DvrType.RECORD;
+    public static final int DVR_SETTINGS_PLAYBACK = Constants.DvrType.PLAYBACK;
+
     private TunerConstants() {
     }
 }
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index a0be12e..5fa6a1f 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -27,6 +27,7 @@
 
 using ::android::hardware::Void;
 using ::android::hardware::hidl_vec;
+using ::android::hardware::tv::tuner::V1_0::DataFormat;
 using ::android::hardware::tv::tuner::V1_0::DemuxFilterMainType;
 using ::android::hardware::tv::tuner::V1_0::DemuxFilterPesDataSettings;
 using ::android::hardware::tv::tuner::V1_0::DemuxFilterSettings;
@@ -35,10 +36,13 @@
 using ::android::hardware::tv::tuner::V1_0::DemuxTpid;
 using ::android::hardware::tv::tuner::V1_0::DemuxTsFilterSettings;
 using ::android::hardware::tv::tuner::V1_0::DemuxTsFilterType;
+using ::android::hardware::tv::tuner::V1_0::DvrSettings;
 using ::android::hardware::tv::tuner::V1_0::FrontendAnalogSettings;
 using ::android::hardware::tv::tuner::V1_0::FrontendAnalogSifStandard;
 using ::android::hardware::tv::tuner::V1_0::FrontendAnalogType;
 using ::android::hardware::tv::tuner::V1_0::ITuner;
+using ::android::hardware::tv::tuner::V1_0::PlaybackSettings;
+using ::android::hardware::tv::tuner::V1_0::RecordSettings;
 using ::android::hardware::tv::tuner::V1_0::Result;
 
 struct fields_t {
@@ -485,6 +489,49 @@
     return (Filter *)env->GetLongField(filter, gFields.filterContext);
 }
 
+static DvrSettings getDvrSettings(JNIEnv *env, jobject settings) {
+    DvrSettings dvrSettings;
+    jclass clazz = env->FindClass("android/media/tv/tuner/DvrSettings");
+    uint32_t statusMask =
+            static_cast<uint32_t>(env->GetIntField(
+                    settings, env->GetFieldID(clazz, "mStatusMask", "I")));
+    uint32_t lowThreshold =
+            static_cast<uint32_t>(env->GetIntField(
+                    settings, env->GetFieldID(clazz, "mLowThreshold", "I")));
+    uint32_t highThreshold =
+            static_cast<uint32_t>(env->GetIntField(
+                    settings, env->GetFieldID(clazz, "mHighThreshold", "I")));
+    uint8_t packetSize =
+            static_cast<uint8_t>(env->GetIntField(
+                    settings, env->GetFieldID(clazz, "mPacketSize", "I")));
+    DataFormat dataFormat =
+            static_cast<DataFormat>(env->GetIntField(
+                    settings, env->GetFieldID(clazz, "mDataFormat", "I")));
+    DvrType type =
+            static_cast<DvrType>(env->GetIntField(
+                    settings, env->GetFieldID(clazz, "mType", "I")));
+    if (type == DvrType::RECORD) {
+        RecordSettings recordSettings {
+                .statusMask = static_cast<unsigned char>(statusMask),
+                .lowThreshold = lowThreshold,
+                .highThreshold = highThreshold,
+                .dataFormat = dataFormat,
+                .packetSize = packetSize,
+        };
+        dvrSettings.record(recordSettings);
+    } else if (type == DvrType::PLAYBACK) {
+        PlaybackSettings PlaybackSettings {
+                .statusMask = statusMask,
+                .lowThreshold = lowThreshold,
+                .highThreshold = highThreshold,
+                .dataFormat = dataFormat,
+                .packetSize = packetSize,
+        };
+        dvrSettings.playback(PlaybackSettings);
+    }
+    return dvrSettings;
+}
+
 static sp<IDvr> getDvr(JNIEnv *env, jobject dvr) {
     return (IDvr *)env->GetLongField(dvr, gFields.dvrContext);
 }
@@ -749,6 +796,16 @@
     return result == Result::SUCCESS;
 }
 
+static int android_media_tv_Tuner_configure_dvr(JNIEnv *env, jobject dvr, jobject settings) {
+    sp<IDvr> dvrSp = getDvr(env, dvr);
+    if (dvrSp == NULL) {
+        ALOGD("Failed to configure dvr: dvr not found");
+        return (int)Result::INVALID_STATE;
+    }
+    Result result = dvrSp->configure(getDvrSettings(env, settings));
+    return (int)result;
+}
+
 static bool android_media_tv_Tuner_start_dvr(JNIEnv *env, jobject dvr) {
     sp<IDvr> dvrSp = getDvr(env, dvr);
     if (dvrSp == NULL) {
@@ -818,6 +875,8 @@
             (void *)android_media_tv_Tuner_attach_filter },
     { "nativeDetachFilter", "(Landroid/media/tv/tuner/Tuner$Filter;)Z",
             (void *)android_media_tv_Tuner_detach_filter },
+    { "nativeConfigureDvr", "(Landroid/media/tv/tuner/DvrSettings;)I",
+            (void *)android_media_tv_Tuner_configure_dvr },
     { "nativeStartDvr", "()Z", (void *)android_media_tv_Tuner_start_dvr },
     { "nativeStopDvr", "()Z", (void *)android_media_tv_Tuner_stop_dvr },
     { "nativeFlushDvr", "()Z", (void *)android_media_tv_Tuner_flush_dvr },
diff --git a/media/jni/audioeffect/Android.bp b/media/jni/audioeffect/Android.bp
index 41ab670..5ba5c01 100644
--- a/media/jni/audioeffect/Android.bp
+++ b/media/jni/audioeffect/Android.bp
@@ -17,6 +17,7 @@
         "libnativehelper",
         "libaudioclient",
         "libaudioutils",
+        "libaudiofoundation",
     ],
 
     version_script: "exports.lds",
diff --git a/packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java b/packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java
index 72a4030..4749add 100644
--- a/packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java
+++ b/packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java
@@ -280,6 +280,7 @@
                     R.layout.size_compat_mode_hint, null /* root */);
             PopupWindow popupWindow = new PopupWindow(popupView,
                     LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
+            popupWindow.setWindowLayoutType(mWinParams.type);
             popupWindow.setElevation(getResources().getDimension(R.dimen.bubble_elevation));
             popupWindow.setAnimationStyle(android.R.style.Animation_InputMethod);
             popupWindow.setClippingEnabled(false);
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java
index 8299f22..4187985 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java
@@ -224,7 +224,11 @@
         // Use the icon of the person if available
         List<Person> personList = getPeopleFromNotification(entry);
         if (personList.size() > 0) {
-            icon = personList.get(0).getIcon();
+            final Person person = personList.get(0);
+
+            if (person != null) {
+                icon = person.getIcon();
+            }
         }
         if (icon == null) {
             icon = notification.getLargeIcon() != null
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
index 9fe9703..d79e383 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/UiModeNightTile.java
@@ -92,7 +92,7 @@
         boolean nightMode = (mContext.getResources().getConfiguration().uiMode
                         & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES;
 
-        if (isAuto) {
+        if (isAuto && !powerSave) {
             state.secondaryLabel = mContext.getResources().getString(nightMode
                     ? R.string.quick_settings_dark_mode_secondary_label_until_sunrise
                     : R.string.quick_settings_dark_mode_secondary_label_on_at_sunset);
@@ -123,7 +123,7 @@
 
     @Override
     public Intent getLongClickIntent() {
-        return new Intent(Settings.ACTION_DISPLAY_SETTINGS);
+        return new Intent(Settings.ACTION_DARK_THEME_SETTINGS);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/volume/Events.java b/packages/SystemUI/src/com/android/systemui/volume/Events.java
index 5ed8b8f..e121001 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/Events.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/Events.java
@@ -21,7 +21,11 @@
 import android.provider.Settings.Global;
 import android.util.Log;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.UiEvent;
+import com.android.internal.logging.UiEventLogger;
+import com.android.internal.logging.UiEventLoggerImpl;
 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
 import com.android.systemui.plugins.VolumeDialogController.State;
 
@@ -37,7 +41,7 @@
     public static final int EVENT_DISMISS_DIALOG = 1; // (reason|int)
     public static final int EVENT_ACTIVE_STREAM_CHANGED = 2; // (stream|int)
     public static final int EVENT_EXPAND = 3; // (expand|bool)
-    public static final int EVENT_KEY = 4;
+    public static final int EVENT_KEY = 4; // (stream|int) (lastAudibleStreamVolume)
     public static final int EVENT_COLLECTION_STARTED = 5;
     public static final int EVENT_COLLECTION_STOPPED = 6;
     public static final int EVENT_ICON_CLICK = 7; // (stream|int) (icon_state|int)
@@ -49,7 +53,7 @@
     public static final int EVENT_ZEN_MODE_CHANGED = 13; // (mode|int)
     public static final int EVENT_SUPPRESSOR_CHANGED = 14;  // (component|string) (name|string)
     public static final int EVENT_MUTE_CHANGED = 15;  // (stream|int) (muted|bool)
-    public static final int EVENT_TOUCH_LEVEL_DONE = 16;  // (stream|int) (level|bool)
+    public static final int EVENT_TOUCH_LEVEL_DONE = 16;  // (stream|int) (level|int)
     public static final int EVENT_ZEN_CONFIG_CHANGED = 17; // (allow/disallow|string)
     public static final int EVENT_RINGER_TOGGLE = 18; // (ringer_mode)
     public static final int EVENT_SHOW_USB_OVERHEAT_ALARM = 19; // (reason|int) (keyguard|bool)
@@ -122,105 +126,365 @@
     public static final int ICON_STATE_MUTE = 2;
     public static final int ICON_STATE_VIBRATE = 3;
 
+    @VisibleForTesting
+    public enum VolumeDialogOpenEvent implements UiEventLogger.UiEventEnum {
+        //TODO zap the lock/unlock distinction
+        INVALID(0),
+        @UiEvent(doc = "The volume dialog was shown because the volume changed")
+        VOLUME_DIALOG_SHOW_VOLUME_CHANGED(128),
+        @UiEvent(doc = "The volume dialog was shown because the volume changed remotely")
+        VOLUME_DIALOG_SHOW_REMOTE_VOLUME_CHANGED(129),
+        @UiEvent(doc = "The volume dialog was shown because the usb high temperature alarm changed")
+        VOLUME_DIALOG_SHOW_USB_TEMP_ALARM_CHANGED(130);
+
+        private final int mId;
+        VolumeDialogOpenEvent(int id) {
+            mId = id;
+        }
+        public int getId() {
+            return mId;
+        }
+        static VolumeDialogOpenEvent fromReasons(int reason) {
+            switch (reason) {
+                case SHOW_REASON_VOLUME_CHANGED:
+                    return VOLUME_DIALOG_SHOW_VOLUME_CHANGED;
+                case SHOW_REASON_REMOTE_VOLUME_CHANGED:
+                    return VOLUME_DIALOG_SHOW_REMOTE_VOLUME_CHANGED;
+                case SHOW_REASON_USB_OVERHEAD_ALARM_CHANGED:
+                    return VOLUME_DIALOG_SHOW_USB_TEMP_ALARM_CHANGED;
+            }
+            return INVALID;
+        }
+    }
+
+    @VisibleForTesting
+    public enum VolumeDialogCloseEvent implements UiEventLogger.UiEventEnum {
+        INVALID(0),
+        @UiEvent(doc = "The volume dialog was dismissed because of a touch outside the dialog")
+        VOLUME_DIALOG_DISMISS_TOUCH_OUTSIDE(134),
+        @UiEvent(doc = "The system asked the volume dialog to close, e.g. for a navigation bar "
+                 + "touch, or ActivityManager ACTION_CLOSE_SYSTEM_DIALOGS broadcast.")
+        VOLUME_DIALOG_DISMISS_SYSTEM(135),
+        @UiEvent(doc = "The volume dialog was dismissed because it timed out")
+        VOLUME_DIALOG_DISMISS_TIMEOUT(136),
+        @UiEvent(doc = "The volume dialog was dismissed because the screen turned off")
+        VOLUME_DIALOG_DISMISS_SCREEN_OFF(137),
+        @UiEvent(doc = "The volume dialog was dismissed because the settings icon was clicked")
+        VOLUME_DIALOG_DISMISS_SETTINGS(138),
+        // reserving 139 for DISMISS_REASON_DONE_CLICKED which is currently unused
+        @UiEvent(doc = "The volume dialog was dismissed because the stream no longer exists")
+        VOLUME_DIALOG_DISMISS_STREAM_GONE(140),
+        // reserving 141 for DISMISS_REASON_OUTPUT_CHOOSER which is currently unused
+        @UiEvent(doc = "The volume dialog was dismissed because the usb high temperature alarm "
+                 + "changed")
+        VOLUME_DIALOG_DISMISS_USB_TEMP_ALARM_CHANGED(142);
+
+        private final int mId;
+        VolumeDialogCloseEvent(int id) {
+            mId = id;
+        }
+        public int getId() {
+            return mId;
+        }
+
+        static VolumeDialogCloseEvent fromReason(int reason) {
+            switch (reason) {
+                case DISMISS_REASON_TOUCH_OUTSIDE:
+                    return VOLUME_DIALOG_DISMISS_TOUCH_OUTSIDE;
+                case DISMISS_REASON_VOLUME_CONTROLLER:
+                    return VOLUME_DIALOG_DISMISS_SYSTEM;
+                case DISMISS_REASON_TIMEOUT:
+                    return VOLUME_DIALOG_DISMISS_TIMEOUT;
+                case DISMISS_REASON_SCREEN_OFF:
+                    return VOLUME_DIALOG_DISMISS_SCREEN_OFF;
+                case DISMISS_REASON_SETTINGS_CLICKED:
+                    return VOLUME_DIALOG_DISMISS_SETTINGS;
+                case DISMISS_STREAM_GONE:
+                    return VOLUME_DIALOG_DISMISS_STREAM_GONE;
+                case DISMISS_REASON_USB_OVERHEAD_ALARM_CHANGED:
+                    return VOLUME_DIALOG_DISMISS_USB_TEMP_ALARM_CHANGED;
+            }
+            return INVALID;
+        }
+    }
+
+    @VisibleForTesting
+    public enum VolumeDialogEvent implements UiEventLogger.UiEventEnum {
+        INVALID(0),
+        @UiEvent(doc = "The volume dialog settings icon was clicked")
+        VOLUME_DIALOG_SETTINGS_CLICK(143),
+        @UiEvent(doc = "The volume dialog details were expanded")
+        VOLUME_DIALOG_EXPAND_DETAILS(144),
+        @UiEvent(doc = "The volume dialog details were collapsed")
+        VOLUME_DIALOG_COLLAPSE_DETAILS(145),
+        @UiEvent(doc = "The active audio stream changed")
+        VOLUME_DIALOG_ACTIVE_STREAM_CHANGED(146),
+        @UiEvent(doc = "The audio stream was muted via icon")
+        VOLUME_DIALOG_MUTE_STREAM(147),
+        @UiEvent(doc = "The audio stream was unmuted via icon")
+        VOLUME_DIALOG_UNMUTE_STREAM(148),
+        @UiEvent(doc = "The audio stream was set to vibrate via icon")
+        VOLUME_DIALOG_TO_VIBRATE_STREAM(149),
+        @UiEvent(doc = "The audio stream was set to non-silent via slider")
+        VOLUME_DIALOG_SLIDER(150),
+        @UiEvent(doc = "The audio stream was set to silent via slider")
+        VOLUME_DIALOG_SLIDER_TO_ZERO(151),
+        @UiEvent(doc = "The audio volume was adjusted to silent via key")
+        VOLUME_KEY_TO_ZERO(152),
+        @UiEvent(doc = "The audio volume was adjusted to non-silent via key")
+        VOLUME_KEY(153),
+        @UiEvent(doc = "The ringer mode was toggled to silent")
+        RINGER_MODE_SILENT(154),
+        @UiEvent(doc = "The ringer mode was toggled to vibrate")
+        RINGER_MODE_VIBRATE(155),
+        @UiEvent(doc = "The ringer mode was toggled to normal")
+        RINGER_MODE_NORMAL(156),
+        @UiEvent(doc = "USB Overheat alarm was raised")
+        USB_OVERHEAT_ALARM(160),
+        @UiEvent(doc = "USB Overheat alarm was dismissed")
+        USB_OVERHEAT_ALARM_DISMISSED(161);
+
+        private final int mId;
+
+        VolumeDialogEvent(int id) {
+            mId = id;
+        }
+
+        public int getId() {
+            return mId;
+        }
+
+        static VolumeDialogEvent fromIconState(int iconState) {
+            switch (iconState) {
+                case ICON_STATE_UNMUTE:
+                    return VOLUME_DIALOG_UNMUTE_STREAM;
+                case ICON_STATE_MUTE:
+                    return VOLUME_DIALOG_MUTE_STREAM;
+                case ICON_STATE_VIBRATE:
+                    return VOLUME_DIALOG_TO_VIBRATE_STREAM;
+                default:
+                    return INVALID;
+            }
+        }
+
+        static VolumeDialogEvent fromSliderLevel(int level) {
+            return level == 0 ? VOLUME_DIALOG_SLIDER_TO_ZERO : VOLUME_DIALOG_SLIDER;
+        }
+
+        static VolumeDialogEvent fromKeyLevel(int level) {
+            return level == 0 ? VOLUME_KEY_TO_ZERO : VOLUME_KEY;
+        }
+
+        static VolumeDialogEvent fromRingerMode(int ringerMode) {
+            switch (ringerMode) {
+                case AudioManager.RINGER_MODE_SILENT:
+                    return RINGER_MODE_SILENT;
+                case AudioManager.RINGER_MODE_VIBRATE:
+                    return RINGER_MODE_VIBRATE;
+                case AudioManager.RINGER_MODE_NORMAL:
+                    return RINGER_MODE_NORMAL;
+                default:
+                    return INVALID;
+            }
+        }
+    }
+
+    @VisibleForTesting
+    public enum ZenModeEvent implements UiEventLogger.UiEventEnum {
+        INVALID(0),
+        @UiEvent(doc = "Zen (do not disturb) mode was toggled to off")
+        ZEN_MODE_OFF(156),
+        @UiEvent(doc = "Zen (do not disturb) mode was toggled to important interruptions only")
+        ZEN_MODE_IMPORTANT_ONLY(157),
+        @UiEvent(doc = "Zen (do not disturb) mode was toggled to alarms only")
+        ZEN_MODE_ALARMS_ONLY(158),
+        @UiEvent(doc = "Zen (do not disturb) mode was toggled to block all interruptions")
+        ZEN_MODE_NO_INTERRUPTIONS(159);
+
+        private final int mId;
+        ZenModeEvent(int id) {
+            mId = id;
+        }
+        public int getId() {
+            return mId;
+        }
+
+        static ZenModeEvent fromZenMode(int zenMode) {
+            switch (zenMode) {
+                case Global.ZEN_MODE_OFF: return ZEN_MODE_OFF;
+                case Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS: return ZEN_MODE_IMPORTANT_ONLY;
+                case Global.ZEN_MODE_ALARMS: return ZEN_MODE_ALARMS_ONLY;
+                case Global.ZEN_MODE_NO_INTERRUPTIONS: return ZEN_MODE_NO_INTERRUPTIONS;
+                default: return INVALID;
+            }
+        }
+    }
+
     public static Callback sCallback;
+    @VisibleForTesting
+    static MetricsLogger sLegacyLogger = new MetricsLogger();
+    @VisibleForTesting
+    static UiEventLogger sUiEventLogger = new UiEventLoggerImpl();
 
     /**
-     * Logs an event to the system log and the event log.
+     * Logs an event to the system log, to sCallback if present, and to the logEvent destinations.
      * @param tag One of the EVENT_* codes above.
      * @param list Any additional event-specific arguments, documented above.
      */
     public static void writeEvent(int tag, Object... list) {
-        MetricsLogger logger = new MetricsLogger();
         final long time = System.currentTimeMillis();
-        final StringBuilder sb = new StringBuilder("writeEvent ").append(EVENT_TAGS[tag]);
-        if (list != null && list.length > 0) {
-            sb.append(" ");
-            switch (tag) {
-                case EVENT_SHOW_DIALOG:
-                    logger.visible(MetricsEvent.VOLUME_DIALOG);
-                    logger.histogram("volume_from_keyguard",
-                            (Boolean) list[1] ? 1 : 0);
-                    sb.append(SHOW_REASONS[(Integer) list[0]]).append(" keyguard=").append(list[1]);
-                    break;
-                case EVENT_EXPAND:
-                    logger.visibility(MetricsEvent.VOLUME_DIALOG_DETAILS,
-                            (Boolean) list[0]);
-                    sb.append(list[0]);
-                    break;
-                case EVENT_DISMISS_DIALOG:
-                    logger.hidden(MetricsEvent.VOLUME_DIALOG);
-                    sb.append(DISMISS_REASONS[(Integer) list[0]]);
-                    break;
-                case EVENT_ACTIVE_STREAM_CHANGED:
-                    logger.action(MetricsEvent.ACTION_VOLUME_STREAM,
-                            (Integer) list[0]);
-                    sb.append(AudioSystem.streamToString((Integer) list[0]));
-                    break;
-                case EVENT_ICON_CLICK:
-                    logger.action(MetricsEvent.ACTION_VOLUME_ICON,
-                            (Integer) list[0]);
-                    sb.append(AudioSystem.streamToString((Integer) list[0])).append(' ')
-                            .append(iconStateToString((Integer) list[1]));
-                    break;
-                case EVENT_TOUCH_LEVEL_DONE:
-                    logger.action(MetricsEvent.ACTION_VOLUME_SLIDER,
-                            (Integer) list[1]);
-                    // fall through
-                case EVENT_TOUCH_LEVEL_CHANGED:
-                case EVENT_LEVEL_CHANGED:
-                case EVENT_MUTE_CHANGED:
-                    sb.append(AudioSystem.streamToString((Integer) list[0])).append(' ')
-                            .append(list[1]);
-                    break;
-                case EVENT_KEY:
-                    logger.action(MetricsEvent.ACTION_VOLUME_KEY,
-                            (Integer) list[0]);
-                    sb.append(AudioSystem.streamToString((Integer) list[0])).append(' ')
-                            .append(list[1]);
-                    break;
-                case EVENT_RINGER_TOGGLE:
-                    logger.action(MetricsEvent.ACTION_VOLUME_RINGER_TOGGLE, (Integer) list[0]);
-                    break;
-                case EVENT_SETTINGS_CLICK:
-                    logger.action(MetricsEvent.ACTION_VOLUME_SETTINGS);
-                    break;
-                case EVENT_EXTERNAL_RINGER_MODE_CHANGED:
-                    logger.action(MetricsEvent.ACTION_RINGER_MODE,
-                            (Integer) list[0]);
-                    // fall through
-                case EVENT_INTERNAL_RINGER_MODE_CHANGED:
-                    sb.append(ringerModeToString((Integer) list[0]));
-                    break;
-                case EVENT_ZEN_MODE_CHANGED:
-                    sb.append(zenModeToString((Integer) list[0]));
-                    break;
-                case EVENT_SUPPRESSOR_CHANGED:
-                    sb.append(list[0]).append(' ').append(list[1]);
-                    break;
-                case EVENT_SHOW_USB_OVERHEAT_ALARM:
-                    logger.visible(MetricsEvent.POWER_OVERHEAT_ALARM);
-                    logger.histogram("show_usb_overheat_alarm",
-                            (Boolean) list[1] ? 1 : 0);
-                    sb.append(SHOW_REASONS[(Integer) list[0]]).append(" keyguard=").append(list[1]);
-                    break;
-                case EVENT_DISMISS_USB_OVERHEAT_ALARM:
-                    logger.hidden(MetricsEvent.POWER_OVERHEAT_ALARM);
-                    logger.histogram("dismiss_usb_overheat_alarm",
-                            (Boolean) list[1] ? 1 : 0);
-                    sb.append(DISMISS_REASONS[(Integer) list[0]])
-                        .append(" keyguard=").append(list[1]);
-                    break;
-                default:
-                    sb.append(Arrays.asList(list));
-                    break;
-            }
-        }
-        Log.i(TAG, sb.toString());
+        Log.i(TAG, logEvent(tag, list));
         if (sCallback != null) {
             sCallback.writeEvent(time, tag, list);
         }
     }
 
+    /**
+     * Logs an event to the event log and UiEvent (Westworld) logging. Compare writeEvent, which
+     * adds more log destinations.
+     * @param tag One of the EVENT_* codes above.
+     * @param list Any additional event-specific arguments, documented above.
+     * @return String a readable description of the event.  Begins "writeEvent <tag_description>"
+     * if the tag is valid.
+     */
+    public static String logEvent(int tag, Object... list) {
+        if (tag >= EVENT_TAGS.length) {
+            return "";
+        }
+        final StringBuilder sb = new StringBuilder("writeEvent ").append(EVENT_TAGS[tag]);
+        // Handle events without extra data
+        if (list == null || list.length == 0) {
+            if (tag == EVENT_SETTINGS_CLICK) {
+                sLegacyLogger.action(MetricsEvent.ACTION_VOLUME_SETTINGS);
+                sUiEventLogger.log(VolumeDialogEvent.VOLUME_DIALOG_SETTINGS_CLICK);
+            }
+            return sb.toString();
+        }
+        // Handle events with extra data. We've established list[0] exists.
+        sb.append(" ");
+        switch (tag) {
+            case EVENT_SHOW_DIALOG:
+                sLegacyLogger.visible(MetricsEvent.VOLUME_DIALOG);
+                if (list.length > 1) {
+                    final Integer reason = (Integer) list[0];
+                    final Boolean keyguard = (Boolean) list[1];
+                    sLegacyLogger.histogram("volume_from_keyguard", keyguard ? 1 : 0);
+                    sUiEventLogger.log(VolumeDialogOpenEvent.fromReasons(reason));
+                    sb.append(SHOW_REASONS[reason]).append(" keyguard=").append(keyguard);
+                }
+                break;
+            case EVENT_EXPAND: {
+                final Boolean expand = (Boolean) list[0];
+                sLegacyLogger.visibility(MetricsEvent.VOLUME_DIALOG_DETAILS, expand);
+                sUiEventLogger.log(expand ? VolumeDialogEvent.VOLUME_DIALOG_EXPAND_DETAILS
+                        : VolumeDialogEvent.VOLUME_DIALOG_COLLAPSE_DETAILS);
+                sb.append(expand);
+                break;
+            }
+            case EVENT_DISMISS_DIALOG: {
+                sLegacyLogger.hidden(MetricsEvent.VOLUME_DIALOG);
+                final Integer reason = (Integer) list[0];
+                sUiEventLogger.log(VolumeDialogCloseEvent.fromReason(reason));
+                sb.append(DISMISS_REASONS[reason]);
+                break;
+            }
+            case EVENT_ACTIVE_STREAM_CHANGED: {
+                final Integer stream = (Integer) list[0];
+                sLegacyLogger.action(MetricsEvent.ACTION_VOLUME_STREAM, stream);
+                sUiEventLogger.log(VolumeDialogEvent.VOLUME_DIALOG_ACTIVE_STREAM_CHANGED);
+                sb.append(AudioSystem.streamToString(stream));
+                break;
+            }
+            case EVENT_ICON_CLICK:
+                if (list.length > 1) {
+                    final Integer stream = (Integer) list[0];
+                    sLegacyLogger.action(MetricsEvent.ACTION_VOLUME_ICON, stream);
+                    final Integer iconState = (Integer) list[1];
+                    sUiEventLogger.log(VolumeDialogEvent.fromIconState(iconState));
+                    sb.append(AudioSystem.streamToString(stream)).append(' ')
+                            .append(iconStateToString(iconState));
+                }
+                break;
+            case EVENT_TOUCH_LEVEL_DONE: // (stream|int) (level|int)
+                if (list.length > 1) {
+                    final Integer level = (Integer) list[1];
+                    sLegacyLogger.action(MetricsEvent.ACTION_VOLUME_SLIDER, level);
+                    sUiEventLogger.log(VolumeDialogEvent.fromSliderLevel(level));
+                }
+                // fall through
+            case EVENT_TOUCH_LEVEL_CHANGED:
+            case EVENT_LEVEL_CHANGED:
+            case EVENT_MUTE_CHANGED:  // (stream|int) (level|int)
+                if (list.length > 1) {
+                    sb.append(AudioSystem.streamToString((Integer) list[0])).append(' ')
+                            .append(list[1]);
+                }
+                break;
+            case EVENT_KEY: // (stream|int) (lastAudibleStreamVolume)
+                if (list.length > 1) {
+                    final Integer stream = (Integer) list[0];
+                    sLegacyLogger.action(MetricsEvent.ACTION_VOLUME_KEY, stream);
+                    final Integer level = (Integer) list[1];
+                    sUiEventLogger.log(VolumeDialogEvent.fromKeyLevel(level));
+                    sb.append(AudioSystem.streamToString(stream)).append(' ').append(level);
+                }
+                break;
+            case EVENT_RINGER_TOGGLE: {
+                final Integer ringerMode = (Integer) list[0];
+                sLegacyLogger.action(MetricsEvent.ACTION_VOLUME_RINGER_TOGGLE, ringerMode);
+                sUiEventLogger.log(VolumeDialogEvent.fromRingerMode(ringerMode));
+                sb.append(ringerModeToString(ringerMode));
+                break;
+            }
+            case EVENT_EXTERNAL_RINGER_MODE_CHANGED: {
+                final Integer ringerMode = (Integer) list[0];
+                sLegacyLogger.action(MetricsEvent.ACTION_RINGER_MODE, ringerMode);
+            }
+                // fall through
+            case EVENT_INTERNAL_RINGER_MODE_CHANGED: {
+                final Integer ringerMode = (Integer) list[0];
+                sb.append(ringerModeToString(ringerMode));
+                break;
+            }
+            case EVENT_ZEN_MODE_CHANGED: {
+                final Integer zenMode = (Integer) list[0];
+                sb.append(zenModeToString(zenMode));
+                sUiEventLogger.log(ZenModeEvent.fromZenMode(zenMode));
+                break;
+            }
+            case EVENT_SUPPRESSOR_CHANGED:  // (component|string) (name|string)
+                if (list.length > 1) {
+                    sb.append(list[0]).append(' ').append(list[1]);
+                }
+                break;
+            case EVENT_SHOW_USB_OVERHEAT_ALARM:
+                sLegacyLogger.visible(MetricsEvent.POWER_OVERHEAT_ALARM);
+                sUiEventLogger.log(VolumeDialogEvent.USB_OVERHEAT_ALARM);
+                if (list.length > 1) {
+                    final Boolean keyguard = (Boolean) list[1];
+                    sLegacyLogger.histogram("show_usb_overheat_alarm", keyguard ? 1 : 0);
+                    final Integer reason = (Integer) list[0];
+                    sb.append(SHOW_REASONS[reason]).append(" keyguard=").append(keyguard);
+                }
+                break;
+            case EVENT_DISMISS_USB_OVERHEAT_ALARM:
+                sLegacyLogger.hidden(MetricsEvent.POWER_OVERHEAT_ALARM);
+                sUiEventLogger.log(VolumeDialogEvent.USB_OVERHEAT_ALARM_DISMISSED);
+                if (list.length > 1) {
+                    final Boolean keyguard = (Boolean) list[1];
+                    sLegacyLogger.histogram("dismiss_usb_overheat_alarm", keyguard ? 1 : 0);
+                    final Integer reason = (Integer) list[0];
+                    sb.append(DISMISS_REASONS[reason])
+                            .append(" keyguard=").append(keyguard);
+                }
+                break;
+            default:
+                sb.append(Arrays.asList(list));
+                break;
+        }
+        return sb.toString();
+    }
+
     public static void writeState(long time, State state) {
         if (sCallback != null) {
             sCallback.writeState(time, state);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/glwallpaper/ImageRevealHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/glwallpaper/ImageRevealHelperTest.java
new file mode 100644
index 0000000..c827ac7
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/glwallpaper/ImageRevealHelperTest.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.glwallpaper;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class ImageRevealHelperTest extends SysuiTestCase {
+
+    static final int ANIMATION_DURATION = 500;
+    ImageRevealHelper mImageRevealHelper;
+    ImageRevealHelper.RevealStateListener mRevealStateListener;
+
+    @Before
+    public void setUp() throws Exception {
+        mRevealStateListener = new ImageRevealHelper.RevealStateListener() {
+            @Override
+            public void onRevealStateChanged() {
+                // no-op
+            }
+
+            @Override
+            public void onRevealStart(boolean animate) {
+                // no-op
+            }
+
+            @Override
+            public void onRevealEnd() {
+                // no-op
+            }
+        };
+        mImageRevealHelper = new ImageRevealHelper(mRevealStateListener);
+    }
+
+    @Test
+    public void testBiometricAuthUnlockAnimateImageRevealState_shouldNotBlackoutScreen() {
+        assertThat(mImageRevealHelper.getReveal()).isEqualTo(0f);
+
+        mImageRevealHelper.updateAwake(true /* awake */, ANIMATION_DURATION);
+        assertThat(mImageRevealHelper.getReveal()).isEqualTo(0f);
+
+        // When device unlock through Biometric, should not show reveal transition
+        mImageRevealHelper.updateAwake(false /* awake */, 0);
+        assertThat(mImageRevealHelper.getReveal()).isEqualTo(1f);
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/volume/EventsTest.java b/packages/SystemUI/tests/src/com/android/systemui/volume/EventsTest.java
new file mode 100644
index 0000000..701b2fa
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/volume/EventsTest.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.volume;
+
+import static org.junit.Assert.assertEquals;
+
+import android.media.AudioManager;
+import android.media.AudioSystem;
+import android.metrics.LogMaker;
+import android.provider.Settings;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.logging.UiEventLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.internal.logging.testing.FakeMetricsLogger;
+import com.android.internal.logging.testing.UiEventLoggerFake;
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Queue;
+
+/**
+ * Parameterized unit test for Events.logEvent.
+ *
+ * This test captures a translation table between the Event class tags, the debugging logs,
+ * the event-buffer logs, and the statsd logs.
+ *
+ * This test works as a straight JUnit4 test, but is declared as a SysuiTestCase because
+ * AAAPlusPlusVerifySysuiRequiredTestPropertiesTest requires all tests in SystemUiTest extend
+ * either SysuiTestCase or SysUiBaseFragmentTest.
+ *
+ */
+@RunWith(Parameterized.class)
+@SmallTest
+public class EventsTest extends SysuiTestCase {
+    private FakeMetricsLogger mLegacyLogger;
+    private UiEventLoggerFake mUiEventLogger;
+
+    @Before
+    public void setFakeLoggers() {
+        mLegacyLogger = new FakeMetricsLogger();
+        Events.sLegacyLogger = mLegacyLogger;
+        mUiEventLogger = new UiEventLoggerFake();
+        Events.sUiEventLogger = mUiEventLogger;
+    }
+
+    // Parameters for calling writeEvent with arbitrary args.
+    @Parameterized.Parameter
+    public int mTag;
+
+    @Parameterized.Parameter(1)
+    public Object[] mArgs;
+
+    // Expect returned string exactly matches.
+    @Parameterized.Parameter(2)
+    public String mExpectedMessage;
+
+    // Expect these MetricsLogger calls.
+
+    @Parameterized.Parameter(3)
+    public int[] mExpectedMetrics;
+
+    // Expect this UiEvent (use null if there isn't one).
+    @Parameterized.Parameter(4)
+    public UiEventLogger.UiEventEnum mUiEvent;
+
+    @Test
+    public void testLogEvent() {
+        String result = Events.logEvent(mTag, mArgs);
+        assertEquals("Show Dialog", mExpectedMessage, result);
+
+        Queue<LogMaker> logs = mLegacyLogger.getLogs();
+        if (mExpectedMetrics == null) {
+            assertEquals(0, logs.size());
+        } else {
+            assertEquals(mExpectedMetrics.length, logs.size());
+            if (mExpectedMetrics.length > 0) {
+                assertEquals(mExpectedMetrics[0], logs.remove().getCategory());
+            }
+            if (mExpectedMetrics.length > 1) {
+                assertEquals(mExpectedMetrics[1], logs.remove().getCategory());
+            }
+        }
+        Queue<UiEventLoggerFake.FakeUiEvent> events = mUiEventLogger.getLogs();
+        if (mUiEvent != null) {
+            assertEquals(mUiEvent.getId(), events.remove().eventId);
+        }
+    }
+
+    @Parameterized.Parameters(name = "{index}: {2}")
+    public static Collection<Object[]> data() {
+        return Arrays.asList(new Object[][]{
+                {Events.EVENT_SETTINGS_CLICK, null,
+                        "writeEvent settings_click",
+                        new int[]{MetricsEvent.ACTION_VOLUME_SETTINGS},
+                        Events.VolumeDialogEvent.VOLUME_DIALOG_SETTINGS_CLICK},
+                {Events.EVENT_SHOW_DIALOG, new Object[]{Events.SHOW_REASON_VOLUME_CHANGED, false},
+                        "writeEvent show_dialog volume_changed keyguard=false",
+                        new int[]{MetricsEvent.VOLUME_DIALOG,
+                                MetricsEvent.RESERVED_FOR_LOGBUILDER_HISTOGRAM},
+                        Events.VolumeDialogOpenEvent.VOLUME_DIALOG_SHOW_VOLUME_CHANGED},
+                {Events.EVENT_EXPAND, new Object[]{true},
+                        "writeEvent expand true",
+                        new int[]{MetricsEvent.VOLUME_DIALOG_DETAILS},
+                        Events.VolumeDialogEvent.VOLUME_DIALOG_EXPAND_DETAILS},
+                {Events.EVENT_DISMISS_DIALOG,
+                        new Object[]{Events.DISMISS_REASON_TOUCH_OUTSIDE, true},
+                        "writeEvent dismiss_dialog touch_outside",
+                        new int[]{MetricsEvent.VOLUME_DIALOG},
+                        Events.VolumeDialogCloseEvent.VOLUME_DIALOG_DISMISS_TOUCH_OUTSIDE},
+                {Events.EVENT_ACTIVE_STREAM_CHANGED, new Object[]{AudioSystem.STREAM_ACCESSIBILITY},
+                        "writeEvent active_stream_changed STREAM_ACCESSIBILITY",
+                        new int[]{MetricsEvent.ACTION_VOLUME_STREAM},
+                        Events.VolumeDialogEvent.VOLUME_DIALOG_ACTIVE_STREAM_CHANGED},
+                {Events.EVENT_ICON_CLICK,
+                        new Object[]{AudioSystem.STREAM_MUSIC, Events.ICON_STATE_MUTE},
+                        "writeEvent icon_click STREAM_MUSIC mute",
+                        new int[]{MetricsEvent.ACTION_VOLUME_ICON},
+                        Events.VolumeDialogEvent.VOLUME_DIALOG_MUTE_STREAM},
+                {Events.EVENT_TOUCH_LEVEL_DONE,
+                        new Object[]{AudioSystem.STREAM_MUSIC, /* volume */ 0},
+                        "writeEvent touch_level_done STREAM_MUSIC 0",
+                        new int[]{MetricsEvent.ACTION_VOLUME_SLIDER},
+                        Events.VolumeDialogEvent.VOLUME_DIALOG_SLIDER_TO_ZERO},
+                {Events.EVENT_TOUCH_LEVEL_DONE,
+                        new Object[]{AudioSystem.STREAM_MUSIC, /* volume */ 1},
+                        "writeEvent touch_level_done STREAM_MUSIC 1",
+                        new int[]{MetricsEvent.ACTION_VOLUME_SLIDER},
+                        Events.VolumeDialogEvent.VOLUME_DIALOG_SLIDER},
+                {Events.EVENT_TOUCH_LEVEL_CHANGED,
+                        new Object[]{AudioSystem.STREAM_MUSIC, /* volume */ 0},
+                        "writeEvent touch_level_changed STREAM_MUSIC 0",
+                        null, null},
+                {Events.EVENT_LEVEL_CHANGED,
+                        new Object[]{AudioSystem.STREAM_MUSIC, /* volume */ 0},
+                        "writeEvent level_changed STREAM_MUSIC 0",
+                        null, null},
+                {Events.EVENT_MUTE_CHANGED,
+                        new Object[]{AudioSystem.STREAM_MUSIC, /* volume */ 0},
+                        "writeEvent mute_changed STREAM_MUSIC 0",
+                        null, null},
+                {Events.EVENT_KEY,
+                        new Object[]{AudioSystem.STREAM_MUSIC, /* volume */ 0},
+                        "writeEvent key STREAM_MUSIC 0",
+                        new int[]{MetricsEvent.ACTION_VOLUME_KEY},
+                        Events.VolumeDialogEvent.VOLUME_KEY_TO_ZERO},
+                {Events.EVENT_KEY,
+                        new Object[]{AudioSystem.STREAM_MUSIC, /* volume */ 1},
+                        "writeEvent key STREAM_MUSIC 1",
+                        new int[]{MetricsEvent.ACTION_VOLUME_KEY},
+                        Events.VolumeDialogEvent.VOLUME_KEY},
+                {Events.EVENT_RINGER_TOGGLE, new Object[]{AudioManager.RINGER_MODE_NORMAL},
+                        "writeEvent ringer_toggle normal",
+                        new int[]{MetricsEvent.ACTION_VOLUME_RINGER_TOGGLE},
+                        Events.VolumeDialogEvent.RINGER_MODE_NORMAL},
+                {Events.EVENT_EXTERNAL_RINGER_MODE_CHANGED,
+                        new Object[]{AudioManager.RINGER_MODE_NORMAL},
+                        "writeEvent external_ringer_mode_changed normal",
+                        new int[]{MetricsEvent.ACTION_RINGER_MODE},
+                        null},
+                {Events.EVENT_INTERNAL_RINGER_MODE_CHANGED,
+                        new Object[]{AudioManager.RINGER_MODE_NORMAL},
+                        "writeEvent internal_ringer_mode_changed normal",
+                        null, null},
+                {Events.EVENT_ZEN_MODE_CHANGED,
+                        new Object[]{Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS},
+                        "writeEvent zen_mode_changed important_interruptions",
+                        null, Events.ZenModeEvent.ZEN_MODE_IMPORTANT_ONLY},
+                {Events.EVENT_ZEN_MODE_CHANGED,
+                        new Object[]{Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS},
+                        "writeEvent zen_mode_changed important_interruptions",
+                        null, Events.ZenModeEvent.ZEN_MODE_IMPORTANT_ONLY},
+                {Events.EVENT_SUPPRESSOR_CHANGED,
+                        new Object[]{"component", "name"},
+                        "writeEvent suppressor_changed component name",
+                        null, null},
+                {Events.EVENT_SHOW_USB_OVERHEAT_ALARM,
+                        new Object[]{Events.SHOW_REASON_USB_OVERHEAD_ALARM_CHANGED, true},
+                        "writeEvent show_usb_overheat_alarm usb_temperature_above_threshold "
+                                + "keyguard=true",
+                        new int[]{MetricsEvent.POWER_OVERHEAT_ALARM,
+                                MetricsEvent.RESERVED_FOR_LOGBUILDER_HISTOGRAM},
+                        Events.VolumeDialogEvent.USB_OVERHEAT_ALARM},
+                {Events.EVENT_DISMISS_USB_OVERHEAT_ALARM,
+                        new Object[]{Events.DISMISS_REASON_USB_OVERHEAD_ALARM_CHANGED, true},
+                        "writeEvent dismiss_usb_overheat_alarm usb_temperature_below_threshold "
+                                + "keyguard=true",
+                        new int[]{MetricsEvent.POWER_OVERHEAT_ALARM,
+                                MetricsEvent.RESERVED_FOR_LOGBUILDER_HISTOGRAM},
+                        Events.VolumeDialogEvent.USB_OVERHEAT_ALARM_DISMISSED},
+        });
+    }
+}
+
diff --git a/packages/Tethering/Android.bp b/packages/Tethering/Android.bp
index 7e8721d..3c953b3 100644
--- a/packages/Tethering/Android.bp
+++ b/packages/Tethering/Android.bp
@@ -31,6 +31,7 @@
         "android.hardware.tetheroffload.control-V1.0-java",
         "tethering-client",
     ],
+    libs: ["unsupportedappusage"],
     manifest: "AndroidManifestBase.xml",
 }
 
diff --git a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
index be597d7..de6a080 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
@@ -766,8 +766,10 @@
             backupManagerService.prepareOperationTimeout(
                     mEphemeralOpToken, restoreAgentTimeoutMillis, this, OP_TYPE_RESTORE_WAIT);
             startedAgentRestore = true;
-            mAgent.doRestore(mBackupData, appVersionCode, mNewState,
-                    mEphemeralOpToken, backupManagerService.getBackupManagerBinder());
+            mAgent.doRestoreWithExcludedKeys(mBackupData, appVersionCode, mNewState,
+                    mEphemeralOpToken, backupManagerService.getBackupManagerBinder(),
+                    mExcludedKeys.containsKey(packageName)
+                            ? new ArrayList<>(mExcludedKeys.get(packageName)) : null);
         } catch (Exception e) {
             Slog.e(TAG, "Unable to call app for restore: " + packageName, e);
             EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE,
diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java
index 21f5f89..53f306b 100644
--- a/services/core/java/android/content/pm/PackageManagerInternal.java
+++ b/services/core/java/android/content/pm/PackageManagerInternal.java
@@ -822,4 +822,19 @@
 
     /** Sets the enforcement of reading external storage */
     public abstract void setReadExternalStorageEnforced(boolean enforced);
+
+    /**
+     * Allows the integrity component to respond to the
+     * {@link Intent#ACTION_PACKAGE_NEEDS_INTEGRITY_VERIFICATION package verification
+     * broadcast} to respond to the package manager. The response must include
+     * the {@code verificationCode} which is one of
+     * {@link PackageManager#VERIFICATION_ALLOW} or
+     * {@link PackageManager#VERIFICATION_REJECT}.
+     *
+     * @param verificationId pending package identifier as passed via the
+     *            {@link PackageManager#EXTRA_VERIFICATION_ID} Intent extra.
+     * @param verificationResult either {@link PackageManager#VERIFICATION_ALLOW}
+     *            or {@link PackageManager#VERIFICATION_REJECT}.
+     */
+    public abstract void setIntegrityVerificationResult(int verificationId, int verificationResult);
 }
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 54dfc98..6300ab8 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -343,11 +343,6 @@
                     onLocationModeChangedLocked(userId);
                 }
             });
-            mSettingsStore.addOnLocationProvidersAllowedChangedListener((userId) -> {
-                synchronized (mLock) {
-                    onProviderAllowedChangedLocked(userId);
-                }
-            });
             mSettingsStore.addOnBackgroundThrottleIntervalChangedListener(() -> {
                 synchronized (mLock) {
                     onBackgroundThrottleIntervalChangedLocked();
@@ -474,18 +469,11 @@
         }
 
         Intent intent = new Intent(LocationManager.MODE_CHANGED_ACTION);
-        intent.putExtra(LocationManager.EXTRA_LOCATION_ENABLED, isLocationEnabled());
+        intent.putExtra(LocationManager.EXTRA_LOCATION_ENABLED, isLocationEnabledForUser(userId));
         mContext.sendBroadcastAsUser(intent, UserHandle.of(userId));
 
         for (LocationProvider p : mProviders) {
-            p.onLocationModeChangedLocked(userId);
-        }
-    }
-
-    @GuardedBy("mLock")
-    private void onProviderAllowedChangedLocked(int userId) {
-        for (LocationProvider p : mProviders) {
-            p.onAllowedChangedLocked(userId);
+            p.onUseableChangedLocked(userId);
         }
     }
 
@@ -649,7 +637,7 @@
 
         if (GnssManagerService.isGnssSupported()) {
             // Create a gps location provider manager
-            LocationProvider gnssProviderManager = new LocationProvider(GPS_PROVIDER, true);
+            LocationProvider gnssProviderManager = new LocationProvider(GPS_PROVIDER);
             mRealProviders.add(gnssProviderManager);
             addProviderLocked(gnssProviderManager);
 
@@ -680,7 +668,7 @@
         ensureFallbackFusedProviderPresentLocked(pkgs);
 
         // bind to network provider
-        LocationProvider networkProviderManager = new LocationProvider(NETWORK_PROVIDER, true);
+        LocationProvider networkProviderManager = new LocationProvider(NETWORK_PROVIDER);
         LocationProviderProxy networkProvider = LocationProviderProxy.createAndBind(
                 mContext,
                 networkProviderManager,
@@ -793,8 +781,8 @@
 
         // let providers know the current user has changed
         for (LocationProvider p : mProviders) {
-            p.onCurrentUserChangedLocked(oldUserId);
-            p.onCurrentUserChangedLocked(mCurrentUserId);
+            p.onUseableChangedLocked(oldUserId);
+            p.onUseableChangedLocked(mCurrentUserId);
         }
     }
 
@@ -805,9 +793,6 @@
 
         private final String mName;
 
-        // whether this provider should respect LOCATION_PROVIDERS_ALLOWED (ie gps and network)
-        private final boolean mIsManagedBySettings;
-
         // remember to clear binder identity before invoking any provider operation
         @GuardedBy("mLock")
         @Nullable
@@ -816,8 +801,6 @@
         @GuardedBy("mLock")
         private SparseArray<Boolean> mUseable;  // combined state for each user id
         @GuardedBy("mLock")
-        private boolean mAllowed;  // state of LOCATION_PROVIDERS_ALLOWED
-        @GuardedBy("mLock")
         private boolean mEnabled;  // state of provider
 
         @GuardedBy("mLock")
@@ -825,27 +808,19 @@
         private ProviderProperties mProperties;
 
         private LocationProvider(String name) {
-            this(name, false);
-        }
-
-        private LocationProvider(String name, boolean isManagedBySettings) {
             mName = name;
-            mIsManagedBySettings = isManagedBySettings;
 
             mProvider = null;
             mUseable = new SparseArray<>(1);
-            mAllowed = !mIsManagedBySettings;
             mEnabled = false;
             mProperties = null;
 
-            if (mIsManagedBySettings) {
-                // since we assume providers are disabled by default
-                Settings.Secure.putStringForUser(
-                        mContext.getContentResolver(),
-                        Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
-                        "-" + mName,
-                        mCurrentUserId);
-            }
+            // update LOCATION_PROVIDERS_ALLOWED for best effort backwards compatibility
+            Settings.Secure.putStringForUser(
+                    mContext.getContentResolver(),
+                    Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
+                    "-" + mName,
+                    mCurrentUserId);
         }
 
         @GuardedBy("mLock")
@@ -861,7 +836,7 @@
 
             // it would be more correct to call this for all users, but we know this can only
             // affect the current user since providers are disabled for non-current users
-            onUseableChangedLocked(false, mCurrentUserId);
+            onUseableChangedLocked(mCurrentUserId);
         }
 
         public String getName() {
@@ -931,9 +906,6 @@
             pw.println("useable=" + isUseableLocked(mCurrentUserId));
             if (!isUseableLocked(mCurrentUserId)) {
                 pw.println("attached=" + (mProvider != null));
-                if (mIsManagedBySettings) {
-                    pw.println("allowed=" + mAllowed);
-                }
                 pw.println("enabled=" + mEnabled);
             }
 
@@ -997,7 +969,7 @@
 
                 // it would be more correct to call this for all users, but we know this can only
                 // affect the current user since providers are disabled for non-current users
-                onUseableChangedLocked(false, mCurrentUserId);
+                onUseableChangedLocked(mCurrentUserId);
             }
         }
 
@@ -1009,43 +981,6 @@
         }
 
         @GuardedBy("mLock")
-        public void onLocationModeChangedLocked(int userId) {
-            if (!isCurrentProfileLocked(userId)) {
-                return;
-            }
-
-            onUseableChangedLocked(false, userId);
-        }
-
-        @GuardedBy("mLock")
-        public void onAllowedChangedLocked(int userId) {
-            if (!isCurrentProfileLocked(userId)) {
-                return;
-            }
-
-            if (mIsManagedBySettings) {
-                boolean allowed = mSettingsStore.getLocationProvidersAllowed(
-                        mCurrentUserId).contains(mName);
-
-                if (allowed == mAllowed) {
-                    return;
-                }
-
-                if (D) {
-                    Log.d(TAG, mName + " provider allowed is now " + mAllowed);
-                }
-
-                mAllowed = allowed;
-                onUseableChangedLocked(true, userId);
-            }
-        }
-
-        @GuardedBy("mLock")
-        public void onCurrentUserChangedLocked(int userId) {
-            onUseableChangedLocked(false, userId);
-        }
-
-        @GuardedBy("mLock")
         public boolean isUseableLocked() {
             return isUseableLocked(mCurrentUserId);
         }
@@ -1056,38 +991,13 @@
         }
 
         @GuardedBy("mLock")
-        public void onUseableChangedLocked(boolean isAllowedChanged, int userId) {
+        public void onUseableChangedLocked(int userId) {
             // if any property that contributes to "useability" here changes state, it MUST result
             // in a direct or indrect call to onUseableChangedLocked. this allows the provider to
             // guarantee that it will always eventually reach the correct state.
-            boolean useableIgnoringAllowed = mProvider != null && mProviders.contains(this)
+            boolean useable = mProvider != null && mProviders.contains(this)
                     && isCurrentProfileLocked(userId) && isLocationEnabledForUser(userId)
                     && mEnabled;
-            boolean useable = useableIgnoringAllowed && mAllowed;
-
-            // update deprecated provider allowed settings for backwards compatibility
-            if (mIsManagedBySettings) {
-                // a "-" change derived from the allowed setting should not be overwritten, but a
-                // "+" change should be corrected if necessary
-                if (useableIgnoringAllowed && !isAllowedChanged) {
-                    Settings.Secure.putStringForUser(
-                            mContext.getContentResolver(),
-                            Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
-                            "+" + mName,
-                            userId);
-                } else if (!useableIgnoringAllowed) {
-                    Settings.Secure.putStringForUser(
-                            mContext.getContentResolver(),
-                            Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
-                            "-" + mName,
-                            userId);
-                }
-
-                Intent intent = new Intent(LocationManager.PROVIDERS_CHANGED_ACTION);
-                intent.putExtra(LocationManager.EXTRA_PROVIDER_NAME, mName);
-                intent.putExtra(LocationManager.EXTRA_PROVIDER_ENABLED, useable);
-                mContext.sendBroadcastAsUser(intent, UserHandle.of(userId));
-            }
 
             if (useable == isUseableLocked(userId)) {
                 return;
@@ -1098,6 +1008,21 @@
                 Log.d(TAG, "[u" + userId + "] " + mName + " provider useable = " + useable);
             }
 
+            // fused and passive provider never get public updates for legacy reasons
+            if (!FUSED_PROVIDER.equals(mName) && !PASSIVE_PROVIDER.equals(mName)) {
+                // update LOCATION_PROVIDERS_ALLOWED for best effort backwards compatibility
+                Settings.Secure.putStringForUser(
+                        mContext.getContentResolver(),
+                        Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
+                        (useable ? "+" : "-") + mName,
+                        userId);
+
+                Intent intent = new Intent(LocationManager.PROVIDERS_CHANGED_ACTION);
+                intent.putExtra(LocationManager.EXTRA_PROVIDER_NAME, mName);
+                intent.putExtra(LocationManager.EXTRA_PROVIDER_ENABLED, useable);
+                mContext.sendBroadcastAsUser(intent, UserHandle.of(userId));
+            }
+
             if (!useable) {
                 // If any provider has been disabled, clear all last locations for all
                 // providers. This is to be on the safe side in case a provider has location
@@ -1541,12 +1466,9 @@
 
         mProviders.add(provider);
 
-        // allowed state may change while provider was inactive
-        provider.onAllowedChangedLocked(mCurrentUserId);
-
         // it would be more correct to call this for all users, but we know this can only
         // affect the current user since providers are disabled for non-current users
-        provider.onUseableChangedLocked(false, mCurrentUserId);
+        provider.onUseableChangedLocked(mCurrentUserId);
     }
 
     @GuardedBy("mLock")
@@ -1554,7 +1476,7 @@
         if (mProviders.remove(provider)) {
             // it would be more correct to call this for all users, but we know this can only
             // affect the current user since providers are disabled for non-current users
-            provider.onUseableChangedLocked(false, mCurrentUserId);
+            provider.onUseableChangedLocked(mCurrentUserId);
         }
     }
 
@@ -2805,10 +2727,6 @@
         }
     }
 
-    private boolean isLocationEnabled() {
-        return isLocationEnabledForUser(mCurrentUserId);
-    }
-
     @Override
     public boolean isLocationEnabledForUser(int userId) {
         // Check INTERACT_ACROSS_USERS permission if userId is not current user id.
@@ -3247,7 +3165,7 @@
                     + TimeUtils.formatDuration(SystemClock.elapsedRealtime()));
             ipw.println("Current user: " + mCurrentUserId + " " + Arrays.toString(
                     mCurrentUserProfiles));
-            ipw.println("Location Mode: " + isLocationEnabled());
+            ipw.println("Location Mode: " + isLocationEnabledForUser(mCurrentUserId));
             ipw.println("Battery Saver Location Mode: "
                     + locationPowerSaveModeToString(mBatterySaverMode));
 
diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java
index 822fc90..0a6473a 100644
--- a/services/core/java/com/android/server/StorageManagerService.java
+++ b/services/core/java/com/android/server/StorageManagerService.java
@@ -20,6 +20,7 @@
 import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
 import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
 import static android.Manifest.permission.WRITE_MEDIA_STORAGE;
+import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT;
 import static android.app.AppOpsManager.MODE_ALLOWED;
 import static android.app.AppOpsManager.OP_LEGACY_STORAGE;
 import static android.app.AppOpsManager.OP_READ_EXTERNAL_STORAGE;
@@ -49,6 +50,7 @@
 import android.Manifest;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
+import android.app.ActivityManagerInternal;
 import android.app.AppOpsManager;
 import android.app.IActivityManager;
 import android.app.KeyguardManager;
@@ -253,6 +255,11 @@
         public void onCleanupUser(int userHandle) {
             mStorageManagerService.onCleanupUser(userHandle);
         }
+
+        @Override
+        public void onStopUser(int userHandle) {
+            mStorageManagerService.onStopUser(userHandle);
+        }
     }
 
     private static final boolean DEBUG_EVENTS = false;
@@ -1075,6 +1082,15 @@
         }
     }
 
+    private void onStopUser(int userId) {
+        Slog.i(TAG, "onStopUser " + userId);
+        try {
+            mStorageSessionController.onUserStopping(userId);
+        } catch (Exception e) {
+            Slog.wtf(TAG, e);
+        }
+    }
+
     private boolean supportsBlockCheckpoint() throws RemoteException {
         enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
         return mVold.supportsBlockCheckpoint();
@@ -1309,6 +1325,15 @@
             Slog.d(TAG, "System booted in core-only mode; ignoring volume " + vol.getId());
             return;
         }
+        final ActivityManagerInternal amInternal =
+                LocalServices.getService(ActivityManagerInternal.class);
+
+        if (mIsFuseEnabled && vol.mountUserId >= 0
+                && !amInternal.isUserRunning(vol.mountUserId, 0)) {
+            Slog.d(TAG, "Ignoring volume " + vol.getId() + " because user "
+                    + Integer.toString(vol.mountUserId) + " is no longer running.");
+            return;
+        }
 
         if (vol.type == VolumeInfo.TYPE_EMULATED) {
             final StorageManager storage = mContext.getSystemService(StorageManager.class);
@@ -2229,6 +2254,11 @@
     }
 
     private void remountUidExternalStorage(int uid, int mode) {
+        if (uid == Process.SYSTEM_UID) {
+            // No need to remount uid for system because it has all access anyways
+            return;
+        }
+
         try {
             mVold.remountUid(uid, mode);
         } catch (Exception e) {
@@ -3399,7 +3429,13 @@
         public void opChanged(int op, int uid, String packageName) throws RemoteException {
             if (!ENABLE_ISOLATED_STORAGE) return;
 
-            remountUidExternalStorage(uid, getMountMode(uid, packageName));
+            int mountMode = getMountMode(uid, packageName);
+            boolean isUidActive = LocalServices.getService(ActivityManagerInternal.class)
+                    .getUidProcessState(uid) != PROCESS_STATE_NONEXISTENT;
+
+            if (isUidActive) {
+                remountUidExternalStorage(uid, mountMode);
+            }
         }
     };
 
@@ -4092,6 +4128,13 @@
             }
         }
 
+        @Override
+        public void resetUser(int userId) {
+            // TODO(b/145931219): ideally, we only reset storage for the user in question,
+            // but for now, reset everything.
+            mHandler.obtainMessage(H_RESET).sendToTarget();
+        }
+
         public boolean hasExternalStorage(int uid, String packageName) {
             // No need to check for system uid. This avoids a deadlock between
             // PackageManagerService and AppOpsService.
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 305d0fb..b560761 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -446,9 +446,9 @@
             mOtaspMode[i] = TelephonyManager.OTASP_UNKNOWN;
             mCallDisconnectCause[i] = DisconnectCause.NOT_VALID;
             mCallPreciseDisconnectCause[i] = PreciseDisconnectCause.NOT_VALID;
-            mCallQuality[i] = new CallQuality();
+            mCallQuality[i] = createCallQuality();
             mCallAttributes[i] = new CallAttributes(new PreciseCallState(),
-                    TelephonyManager.NETWORK_TYPE_UNKNOWN, new CallQuality());
+                    TelephonyManager.NETWORK_TYPE_UNKNOWN, createCallQuality());
             mCallNetworkType[i] = TelephonyManager.NETWORK_TYPE_UNKNOWN;
             mPreciseCallState[i] = new PreciseCallState();
             mRingingCallState[i] = PreciseCallState.PRECISE_CALL_STATE_IDLE;
@@ -541,9 +541,9 @@
             mOtaspMode[i] = TelephonyManager.OTASP_UNKNOWN;
             mCallDisconnectCause[i] = DisconnectCause.NOT_VALID;
             mCallPreciseDisconnectCause[i] = PreciseDisconnectCause.NOT_VALID;
-            mCallQuality[i] = new CallQuality();
+            mCallQuality[i] = createCallQuality();
             mCallAttributes[i] = new CallAttributes(new PreciseCallState(),
-                    TelephonyManager.NETWORK_TYPE_UNKNOWN, new CallQuality());
+                    TelephonyManager.NETWORK_TYPE_UNKNOWN, createCallQuality());
             mCallNetworkType[i] = TelephonyManager.NETWORK_TYPE_UNKNOWN;
             mPreciseCallState[i] = new PreciseCallState();
             mRingingCallState[i] = PreciseCallState.PRECISE_CALL_STATE_IDLE;
@@ -1571,8 +1571,6 @@
         }
         broadcastDataConnectionStateChanged(state, isDataAllowed, apn, apnType, linkProperties,
                 networkCapabilities, roaming, subId);
-        broadcastPreciseDataConnectionStateChanged(state, networkType, apnType, apn,
-                linkProperties, DataFailCause.NONE);
     }
 
     public void notifyDataConnectionFailed(String apnType) {
@@ -1612,9 +1610,6 @@
             handleRemoveListLocked();
         }
         broadcastDataConnectionFailed(apnType, subId);
-        broadcastPreciseDataConnectionStateChanged(TelephonyManager.DATA_UNKNOWN,
-                TelephonyManager.NETWORK_TYPE_UNKNOWN, apnType, null, null,
-                DataFailCause.NONE);
     }
 
     public void notifyCellLocation(Bundle cellLocation) {
@@ -1704,7 +1699,7 @@
                     if (mPreciseCallState[phoneId].getForegroundCallState()
                             != PreciseCallState.PRECISE_CALL_STATE_ACTIVE) {
                         mCallNetworkType[phoneId] = TelephonyManager.NETWORK_TYPE_UNKNOWN;
-                        mCallQuality[phoneId] = new CallQuality();
+                        mCallQuality[phoneId] = createCallQuality();
                     }
                     mCallAttributes[phoneId] = new CallAttributes(mPreciseCallState[phoneId],
                             mCallNetworkType[phoneId], mCallQuality[phoneId]);
@@ -1732,8 +1727,6 @@
             }
             handleRemoveListLocked();
         }
-        broadcastPreciseCallStateChanged(ringingCallState, foregroundCallState,
-                backgroundCallState);
     }
 
     public void notifyDisconnectCause(int phoneId, int subId, int disconnectCause,
@@ -1815,8 +1808,6 @@
 
             handleRemoveListLocked();
         }
-        broadcastPreciseDataConnectionStateChanged(TelephonyManager.DATA_UNKNOWN,
-                TelephonyManager.NETWORK_TYPE_UNKNOWN, apnType, apn, null, failCause);
     }
 
     @Override
@@ -2308,33 +2299,6 @@
         mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
     }
 
-    private void broadcastPreciseCallStateChanged(int ringingCallState, int foregroundCallState,
-                                                  int backgroundCallState) {
-        Intent intent = new Intent(TelephonyManager.ACTION_PRECISE_CALL_STATE_CHANGED);
-        intent.putExtra(TelephonyManager.EXTRA_RINGING_CALL_STATE, ringingCallState);
-        intent.putExtra(TelephonyManager.EXTRA_FOREGROUND_CALL_STATE, foregroundCallState);
-        intent.putExtra(TelephonyManager.EXTRA_BACKGROUND_CALL_STATE, backgroundCallState);
-        mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
-                android.Manifest.permission.READ_PRECISE_PHONE_STATE);
-    }
-
-    private void broadcastPreciseDataConnectionStateChanged(int state, int networkType,
-            String apnType, String apn, LinkProperties linkProperties,
-            @DataFailureCause int failCause) {
-        Intent intent = new Intent(TelephonyManager.ACTION_PRECISE_DATA_CONNECTION_STATE_CHANGED);
-        intent.putExtra(TelephonyManager.EXTRA_STATE, state);
-        intent.putExtra(PhoneConstants.DATA_NETWORK_TYPE_KEY, networkType);
-        if (apnType != null) intent.putExtra(PhoneConstants.DATA_APN_TYPE_KEY, apnType);
-        if (apn != null) intent.putExtra(PhoneConstants.DATA_APN_KEY, apn);
-        if (linkProperties != null) {
-            intent.putExtra(PhoneConstants.DATA_LINK_PROPERTIES_KEY, linkProperties);
-        }
-        intent.putExtra(PhoneConstants.DATA_FAILURE_CAUSE_KEY, failCause);
-
-        mContext.sendBroadcastAsUser(intent, UserHandle.ALL,
-                android.Manifest.permission.READ_PRECISE_PHONE_STATE);
-    }
-
     private void enforceNotifyPermissionOrCarrierPrivilege(String method) {
         if (checkNotifyPermission()) {
             return;
@@ -2730,4 +2694,9 @@
                 return "UNKNOWN";
         }
     }
+
+    /** Returns a new CallQuality object with default values. */
+    private static CallQuality createCallQuality() {
+        return new CallQuality(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
+    }
 }
diff --git a/services/core/java/com/android/server/UiModeManagerService.java b/services/core/java/com/android/server/UiModeManagerService.java
index b1e2c0f..5bec7a3 100644
--- a/services/core/java/com/android/server/UiModeManagerService.java
+++ b/services/core/java/com/android/server/UiModeManagerService.java
@@ -56,6 +56,7 @@
 import android.service.vr.IVrStateCallbacks;
 import android.util.ArraySet;
 import android.util.Slog;
+
 import com.android.internal.R;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.DisableCarModeActivity;
@@ -73,8 +74,6 @@
 import java.util.Map;
 import java.util.Set;
 
-import static android.content.Intent.ACTION_SCREEN_OFF;
-
 final class UiModeManagerService extends SystemService {
     private static final String TAG = UiModeManager.class.getSimpleName();
     private static final boolean LOG = false;
@@ -97,10 +96,15 @@
     private boolean mCarModeEnabled = false;
     private boolean mCharging = false;
     private boolean mPowerSave = false;
+    // Do not change configuration now. wait until screen turns off.
+    // This prevents jank and activity restart when the user
+    // is actively using the device
+    private boolean mWaitForScreenOff = false;
     private int mDefaultUiModeType;
     private boolean mCarModeKeepsScreenOn;
     private boolean mDeskModeKeepsScreenOn;
     private boolean mTelevision;
+    private boolean mCar;
     private boolean mWatch;
     private boolean mVrHeadset;
     private boolean mComputedNightMode;
@@ -208,24 +212,27 @@
         public void onTwilightStateChanged(@Nullable TwilightState state) {
             synchronized (mLock) {
                 if (mNightMode == UiModeManager.MODE_NIGHT_AUTO) {
-                    final IntentFilter intentFilter =
-                            new IntentFilter(ACTION_SCREEN_OFF);
-                    getContext().registerReceiver(mOnScreenOffHandler, intentFilter);
+                    if (mCar) {
+                        updateLocked(0, 0);
+                    } else {
+                        registerScreenOffEvent();
+                    }
                 }
             }
         }
     };
 
+    /**
+     *  DO NOT USE DIRECTLY
+     *  see register registerScreenOffEvent and unregisterScreenOffEvent
+     */
     private final BroadcastReceiver mOnScreenOffHandler = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
             synchronized (mLock) {
+                // must unregister first before updating
+                unregisterScreenOffEvent();
                 updateLocked(0, 0);
-                try {
-                    getContext().unregisterReceiver(mOnScreenOffHandler);
-                } catch (IllegalArgumentException e) {
-                    // we ignore this exception if the receiver is unregistered already.
-                }
             }
         }
     };
@@ -327,6 +334,7 @@
         final PackageManager pm = context.getPackageManager();
         mTelevision = pm.hasSystemFeature(PackageManager.FEATURE_TELEVISION)
                 || pm.hasSystemFeature(PackageManager.FEATURE_LEANBACK);
+        mCar = pm.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
         mWatch = pm.hasSystemFeature(PackageManager.FEATURE_WATCH);
 
         updateNightModeFromSettings(context, res, UserHandle.getCallingUserId());
@@ -335,7 +343,7 @@
         SystemServerInitThreadPool.submit(() -> {
             synchronized (mLock) {
                 updateConfigurationLocked();
-                sendConfigurationLocked();
+                applyConfigurationExternallyLocked();
             }
 
         }, TAG + ".onStart");
@@ -404,6 +412,22 @@
         return oldNightMode != mNightMode;
     }
 
+    private void registerScreenOffEvent() {
+        mWaitForScreenOff = true;
+        final IntentFilter intentFilter =
+                new IntentFilter(Intent.ACTION_SCREEN_OFF);
+        getContext().registerReceiver(mOnScreenOffHandler, intentFilter);
+    }
+
+    private void unregisterScreenOffEvent() {
+        mWaitForScreenOff = false;
+        try {
+            getContext().unregisterReceiver(mOnScreenOffHandler);
+        } catch (IllegalArgumentException e) {
+            // we ignore this exception if the receiver is unregistered already.
+        }
+    }
+
     private final IUiModeManager.Stub mService = new IUiModeManager.Stub() {
         @Override
         public void enableCarMode(@UiModeManager.EnableCarMode int flags,
@@ -525,11 +549,7 @@
                 synchronized (mLock) {
                     if (mNightMode != mode) {
                         if (mNightMode == UiModeManager.MODE_NIGHT_AUTO) {
-                            try {
-                                getContext().unregisterReceiver(mOnScreenOffHandler);
-                            } catch (IllegalArgumentException e) {
-                                // we ignore this exception if the receiver is unregistered already.
-                            }
+                            unregisterScreenOffEvent();
                         }
                         // Only persist setting if not in car mode
                         if (!mCarModeEnabled) {
@@ -541,12 +561,11 @@
 
                         mNightMode = mode;
                         mNightModeOverride = mode;
-                        //on screen off will update configuration instead
-                        if (mNightMode != UiModeManager.MODE_NIGHT_AUTO) {
+                        // on screen off will update configuration instead
+                        if (mNightMode != UiModeManager.MODE_NIGHT_AUTO || mCar) {
                             updateLocked(0, 0);
                         } else {
-                            getContext().registerReceiver(
-                                    mOnScreenOffHandler, new IntentFilter(ACTION_SCREEN_OFF));
+                            registerScreenOffEvent();
                         }
                     }
                 }
@@ -594,10 +613,7 @@
                 final long ident = Binder.clearCallingIdentity();
                 try {
                     if (mNightMode == UiModeManager.MODE_NIGHT_AUTO) {
-                        try {
-                            getContext().unregisterReceiver(mOnScreenOffHandler);
-                        } catch (IllegalArgumentException e) {
-                        }
+                        unregisterScreenOffEvent();
                         mNightModeOverride = active
                                 ? UiModeManager.MODE_NIGHT_YES : UiModeManager.MODE_NIGHT_NO;
                     } else if (mNightMode == UiModeManager.MODE_NIGHT_NO
@@ -608,7 +624,7 @@
                         mNightMode = UiModeManager.MODE_NIGHT_NO;
                     }
                     updateConfigurationLocked();
-                    sendConfigurationLocked();
+                    applyConfigurationExternallyLocked();
                     return true;
                 } finally {
                     Binder.restoreCallingIdentity(ident);
@@ -863,12 +879,12 @@
         }
 
         mCurUiMode = uiMode;
-        if (!mHoldingConfiguration) {
+        if (!mHoldingConfiguration || !mWaitForScreenOff) {
             mConfiguration.uiMode = uiMode;
         }
     }
 
-    private void sendConfigurationLocked() {
+    private void applyConfigurationExternallyLocked() {
         if (mSetUiMode != mConfiguration.uiMode) {
             mSetUiMode = mConfiguration.uiMode;
             // load splash screen instead of screenshot
@@ -1052,7 +1068,7 @@
         }
 
         // Send the new configuration.
-        sendConfigurationLocked();
+        applyConfigurationExternallyLocked();
 
         // If we did not start a dock app, then start dreaming if supported.
         if (category != null && !dockAppStarted) {
@@ -1130,7 +1146,6 @@
             final int user = UserHandle.getCallingUserId();
             Secure.putIntForUser(getContext().getContentResolver(),
                     OVERRIDE_NIGHT_MODE, mNightModeOverride, user);
-
         }
     }
 
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index 31632dc..177e2d8 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -41,6 +41,7 @@
 import android.util.Slog;
 import android.util.TimeUtils;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.BackgroundThread;
 import com.android.server.EventLogTags;
 
@@ -215,7 +216,9 @@
     private IActivityTaskManager mActivityTaskManager;
     private PackageManager mPackageManager;
 
-    public AutomaticBrightnessController(Callbacks callbacks, Looper looper,
+    private final Injector mInjector;
+
+    AutomaticBrightnessController(Callbacks callbacks, Looper looper,
             SensorManager sensorManager, Sensor lightSensor, BrightnessMappingStrategy mapper,
             int lightSensorWarmUpTime, int brightnessMin, int brightnessMax, float dozeScaleFactor,
             int lightSensorRate, int initialLightSensorRate, long brighteningLightDebounceConfig,
@@ -223,6 +226,24 @@
             HysteresisLevels ambientBrightnessThresholds,
             HysteresisLevels screenBrightnessThresholds, long shortTermModelTimeout,
             PackageManager packageManager) {
+        this(new Injector(), callbacks, looper, sensorManager, lightSensor, mapper,
+                lightSensorWarmUpTime, brightnessMin, brightnessMax, dozeScaleFactor,
+                lightSensorRate, initialLightSensorRate, brighteningLightDebounceConfig,
+                darkeningLightDebounceConfig, resetAmbientLuxAfterWarmUpConfig,
+                ambientBrightnessThresholds, screenBrightnessThresholds, shortTermModelTimeout,
+                packageManager);
+    }
+
+    @VisibleForTesting
+    AutomaticBrightnessController(Injector injector, Callbacks callbacks, Looper looper,
+            SensorManager sensorManager, Sensor lightSensor, BrightnessMappingStrategy mapper,
+            int lightSensorWarmUpTime, int brightnessMin, int brightnessMax, float dozeScaleFactor,
+            int lightSensorRate, int initialLightSensorRate, long brighteningLightDebounceConfig,
+            long darkeningLightDebounceConfig, boolean resetAmbientLuxAfterWarmUpConfig,
+            HysteresisLevels ambientBrightnessThresholds,
+            HysteresisLevels screenBrightnessThresholds, long shortTermModelTimeout,
+            PackageManager packageManager) {
+        mInjector = injector;
         mCallbacks = callbacks;
         mSensorManager = sensorManager;
         mBrightnessMapper = mapper;
@@ -725,8 +746,8 @@
         float value = mBrightnessMapper.getBrightness(mAmbientLux, mForegroundAppPackageName,
                 mForegroundAppCategory);
 
-        int newScreenAutoBrightness =
-                clampScreenBrightness(Math.round(value * PowerManager.BRIGHTNESS_ON));
+        int newScreenAutoBrightness = Math.round(clampScreenBrightness(
+                value * PowerManager.BRIGHTNESS_ON));
 
         // If screenAutoBrightness is set, we should have screen{Brightening,Darkening}Threshold,
         // in which case we ignore the new screen brightness if it doesn't differ enough from the
@@ -750,10 +771,10 @@
             }
 
             mScreenAutoBrightness = newScreenAutoBrightness;
-            mScreenBrighteningThreshold =
-                    mScreenBrightnessThresholds.getBrighteningThreshold(newScreenAutoBrightness);
-            mScreenDarkeningThreshold =
-                    mScreenBrightnessThresholds.getDarkeningThreshold(newScreenAutoBrightness);
+            mScreenBrighteningThreshold = clampScreenBrightness(
+                    mScreenBrightnessThresholds.getBrighteningThreshold(newScreenAutoBrightness));
+            mScreenDarkeningThreshold = clampScreenBrightness(
+                    mScreenBrightnessThresholds.getDarkeningThreshold(newScreenAutoBrightness));
 
             if (sendUpdate) {
                 mCallbacks.updateBrightness();
@@ -761,7 +782,7 @@
         }
     }
 
-    private int clampScreenBrightness(int value) {
+    private float clampScreenBrightness(float value) {
         return MathUtils.constrain(value,
                 mScreenBrightnessRangeMinimum, mScreenBrightnessRangeMaximum);
     }
@@ -839,7 +860,7 @@
         }
         // The ActivityTaskManager's lock tends to get contended, so this is done in a background
         // thread and applied via this thread's handler synchronously.
-        BackgroundThread.getHandler().post(new Runnable() {
+        mInjector.getBackgroundThreadHandler().post(new Runnable() {
             public void run() {
                 try {
                     // The foreground app is the top activity of the focused tasks stack.
@@ -965,6 +986,9 @@
         private int mCount;
 
         public AmbientLightRingBuffer(long lightSensorRate, int ambientLightHorizon) {
+            if (lightSensorRate <= 0) {
+                throw new IllegalArgumentException("lightSensorRate must be above 0");
+            }
             mCapacity = (int) Math.ceil(ambientLightHorizon * BUFFER_SLACK / lightSensorRate);
             mRingLux = new float[mCapacity];
             mRingTime = new long[mCapacity];
@@ -1076,4 +1100,10 @@
             return index;
         }
     }
+
+    public static class Injector {
+        public Handler getBackgroundThreadHandler() {
+            return BackgroundThread.getHandler();
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/display/HysteresisLevels.java b/services/core/java/com/android/server/display/HysteresisLevels.java
index 2db1d03..f0a505d 100644
--- a/services/core/java/com/android/server/display/HysteresisLevels.java
+++ b/services/core/java/com/android/server/display/HysteresisLevels.java
@@ -18,13 +18,16 @@
 
 import android.util.Slog;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 import java.io.PrintWriter;
 import java.util.Arrays;
 
 /**
  * A helper class for handling access to illuminance hysteresis level values.
  */
-final class HysteresisLevels {
+@VisibleForTesting
+public class HysteresisLevels {
     private static final String TAG = "HysteresisLevels";
 
     // Default hysteresis constraints for brightening or darkening.
@@ -60,7 +63,7 @@
     /**
      * Return the brightening hysteresis threshold for the given value level.
      */
-    float getBrighteningThreshold(float value) {
+    public float getBrighteningThreshold(float value) {
         float brightConstant = getReferenceLevel(value, mBrighteningThresholds);
         float brightThreshold = value * (1.0f + brightConstant);
         if (DEBUG) {
@@ -73,7 +76,7 @@
     /**
      * Return the darkening hysteresis threshold for the given value level.
      */
-    float getDarkeningThreshold(float value) {
+    public float getDarkeningThreshold(float value) {
         float darkConstant = getReferenceLevel(value, mDarkeningThresholds);
         float darkThreshold = value * (1.0f - darkConstant);
         if (DEBUG) {
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 1d7c942..308c755 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -410,6 +410,9 @@
                 final DisplayAddress.Physical physicalAddress =
                         DisplayAddress.fromPhysicalDisplayId(mPhysicalDisplayId);
                 mInfo.address = physicalAddress;
+                mInfo.densityDpi = (int) (phys.density * 160 + 0.5f);
+                mInfo.xDpi = phys.xDpi;
+                mInfo.yDpi = phys.yDpi;
 
                 // Assume that all built-in displays that have secure output (eg. HDCP) also
                 // support compositing from gralloc protected buffers.
@@ -436,9 +439,6 @@
                     mInfo.displayCutout = DisplayCutout.fromResourcesRectApproximation(res,
                             mInfo.width, mInfo.height);
                     mInfo.type = Display.TYPE_BUILT_IN;
-                    mInfo.densityDpi = (int)(phys.density * 160 + 0.5f);
-                    mInfo.xDpi = phys.xDpi;
-                    mInfo.yDpi = phys.yDpi;
                     mInfo.touch = DisplayDeviceInfo.TOUCH_INTERNAL;
                 } else {
                     mInfo.displayCutout = null;
@@ -447,7 +447,6 @@
                     mInfo.name = getContext().getResources().getString(
                             com.android.internal.R.string.display_manager_hdmi_display_name);
                     mInfo.touch = DisplayDeviceInfo.TOUCH_EXTERNAL;
-                    mInfo.setAssumedDensityForExternalDisplay(phys.width, phys.height);
 
                     // For demonstration purposes, allow rotation of the external display.
                     // In the future we might allow the user to configure this directly.
diff --git a/services/core/java/com/android/server/integrity/AppIntegrityManagerService.java b/services/core/java/com/android/server/integrity/AppIntegrityManagerService.java
new file mode 100644
index 0000000..005fb69
--- /dev/null
+++ b/services/core/java/com/android/server/integrity/AppIntegrityManagerService.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.integrity;
+
+import android.content.Context;
+
+import com.android.server.SystemService;
+
+/**
+ * Service that manages app integrity rules and verifications.
+ *
+ * @hide
+ */
+public class AppIntegrityManagerService extends SystemService {
+
+    private Context mContext;
+    private AppIntegrityManagerServiceImpl mService;
+
+    public AppIntegrityManagerService(Context context) {
+        super(context);
+        mContext = context;
+    }
+
+    @Override
+    public void onStart() {
+        mService = new AppIntegrityManagerServiceImpl(mContext);
+        // TODO: define and publish a binder service.
+    }
+}
diff --git a/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
new file mode 100644
index 0000000..39c1b85
--- /dev/null
+++ b/services/core/java/com/android/server/integrity/AppIntegrityManagerServiceImpl.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.integrity;
+
+import static android.content.Intent.ACTION_PACKAGE_NEEDS_INTEGRITY_VERIFICATION;
+import static android.content.pm.PackageManager.EXTRA_VERIFICATION_ID;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.util.Slog;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.server.LocalServices;
+
+/** Implementation of {@link AppIntegrityManagerService}. */
+class AppIntegrityManagerServiceImpl {
+    private static final String TAG = "AppIntegrityManagerServiceImpl";
+
+    private final Context mContext;
+    private final Handler mHandler;
+    private final PackageManagerInternal mPackageManagerInternal;
+
+    AppIntegrityManagerServiceImpl(Context context) {
+        mContext = context;
+
+        HandlerThread handlerThread = new HandlerThread("AppIntegrityManagerServiceHandler");
+        handlerThread.start();
+        mHandler = handlerThread.getThreadHandler();
+
+        mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
+
+        IntentFilter integrityVerificationFilter = new IntentFilter();
+        integrityVerificationFilter.addAction(ACTION_PACKAGE_NEEDS_INTEGRITY_VERIFICATION);
+
+        mContext.registerReceiver(
+                new BroadcastReceiver() {
+                    @Override
+                    public void onReceive(Context context, Intent intent) {
+                        if (!ACTION_PACKAGE_NEEDS_INTEGRITY_VERIFICATION.equals(
+                                intent.getAction())) {
+                            return;
+                        }
+                        mHandler.post(() -> handleIntegrityVerification(intent));
+                    }
+                },
+                integrityVerificationFilter,
+                /* broadcastPermission= */ null,
+                mHandler);
+    }
+
+    // protected broadcasts cannot be sent in the test.
+    @VisibleForTesting
+    void handleIntegrityVerification(Intent intent) {
+        int verificationId = intent.getIntExtra(EXTRA_VERIFICATION_ID, -1);
+        // TODO: implement this method.
+        Slog.i(TAG, "Received integrity verification intent " + intent.toString());
+        mPackageManagerInternal.setIntegrityVerificationResult(
+                verificationId, PackageManager.VERIFICATION_ALLOW);
+    }
+}
diff --git a/services/core/java/com/android/server/location/LocationSettingsStore.java b/services/core/java/com/android/server/location/LocationSettingsStore.java
index eb2a37b..3d18d4a 100644
--- a/services/core/java/com/android/server/location/LocationSettingsStore.java
+++ b/services/core/java/com/android/server/location/LocationSettingsStore.java
@@ -23,7 +23,6 @@
 import static android.provider.Settings.Global.LOCATION_LAST_LOCATION_MAX_AGE_MILLIS;
 import static android.provider.Settings.Secure.LOCATION_MODE;
 import static android.provider.Settings.Secure.LOCATION_MODE_OFF;
-import static android.provider.Settings.Secure.LOCATION_PROVIDERS_ALLOWED;
 
 import android.app.ActivityManager;
 import android.content.Context;
@@ -89,7 +88,6 @@
     private final Context mContext;
 
     private final IntegerSecureSetting mLocationMode;
-    private final StringListCachedSecureSetting mLocationProvidersAllowed;
     private final LongGlobalSetting mBackgroundThrottleIntervalMs;
     private final StringListCachedSecureSetting mLocationPackageBlacklist;
     private final StringListCachedSecureSetting mLocationPackageWhitelist;
@@ -101,8 +99,6 @@
         mContext = context;
 
         mLocationMode = new IntegerSecureSetting(context, LOCATION_MODE, handler);
-        mLocationProvidersAllowed = new StringListCachedSecureSetting(context,
-                LOCATION_PROVIDERS_ALLOWED, handler);
         mBackgroundThrottleIntervalMs = new LongGlobalSetting(context,
                 LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS, handler);
         mLocationPackageBlacklist = new StringListCachedSecureSetting(context,
@@ -139,28 +135,6 @@
     }
 
     /**
-     * Retrieve the currently allowed location providers.
-     */
-    public List<String> getLocationProvidersAllowed(int userId) {
-        return mLocationProvidersAllowed.getValueForUser(userId);
-    }
-
-    /**
-     * Add a listener for changes to the currently allowed location providers.
-     */
-    public void addOnLocationProvidersAllowedChangedListener(UserSettingChangedListener listener) {
-        mLocationProvidersAllowed.addListener(listener);
-    }
-
-    /**
-     * Remove a listener for changes to the currently allowed location providers.
-     */
-    public void removeOnLocationProvidersAllowedChangedListener(
-            UserSettingChangedListener listener) {
-        mLocationProvidersAllowed.removeListener(listener);
-    }
-
-    /**
      * Retrieve the background throttle interval.
      */
     public long getBackgroundThrottleIntervalMs() {
@@ -280,9 +254,6 @@
         ipw.print("Location Enabled: ");
         ipw.println(isLocationEnabled(userId));
 
-        ipw.print("Location Providers Allowed: ");
-        ipw.println(getLocationProvidersAllowed(userId));
-
         List<String> locationPackageBlacklist = mLocationPackageBlacklist.getValueForUser(userId);
         if (!locationPackageBlacklist.isEmpty()) {
             ipw.println("Location Blacklisted Packages:");
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 73ab82d..642b500 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -1439,6 +1439,7 @@
     static final int ENABLE_ROLLBACK_TIMEOUT = 22;
     static final int DEFERRED_NO_KILL_POST_DELETE = 23;
     static final int DEFERRED_NO_KILL_INSTALL_OBSERVER = 24;
+    static final int INTEGRITY_VERIFICATION_COMPLETE = 25;
 
     static final int DEFERRED_NO_KILL_POST_DELETE_DELAY_MS = 3 * 1000;
     static final int DEFERRED_NO_KILL_INSTALL_OBSERVER_DELAY_MS = 500;
@@ -1603,6 +1604,10 @@
                     final boolean didRestore = (msg.arg2 != 0);
                     mRunningInstalls.delete(msg.arg1);
 
+                    if (data != null && data.res.freezer != null) {
+                        data.res.freezer.close();
+                    }
+
                     if (data != null && data.mPostInstallRunnable != null) {
                         data.mPostInstallRunnable.run();
                     } else if (data != null) {
@@ -1763,6 +1768,10 @@
 
                     break;
                 }
+                case INTEGRITY_VERIFICATION_COMPLETE: {
+                    // TODO: implement this case.
+                    break;
+                }
                 case START_INTENT_FILTER_VERIFICATIONS: {
                     IFVerificationParams params = (IFVerificationParams) msg.obj;
                     verifyIntentFiltersIfNeeded(params.userId, params.verifierUid, params.replacing,
@@ -3019,8 +3028,7 @@
 
             mWellbeingPackage = getWellbeingPackageName();
             mDocumenterPackage = getDocumenterPackageName();
-            mConfiguratorPackage =
-                    mContext.getString(R.string.config_deviceConfiguratorPackageName);
+            mConfiguratorPackage = getDeviceConfiguratorPackageName();
             mAppPredictionServicePackage = getAppPredictionServicePackageName();
             mIncidentReportApproverPackage = getIncidentReportApproverPackageName();
             mTelephonyPackages = getTelephonyPackageNames();
@@ -13434,35 +13442,10 @@
             // restore if appropriate, then pass responsibility back to the
             // Package Manager to run the post-install observer callbacks
             // and broadcasts.
-            IBackupManager bm = IBackupManager.Stub.asInterface(
-                    ServiceManager.getService(Context.BACKUP_SERVICE));
-            if (bm != null) {
-                // For backwards compatibility as USER_ALL previously routed directly to USER_SYSTEM
-                // in the BackupManager. USER_ALL is used in compatibility tests.
-                if (userId == UserHandle.USER_ALL) {
-                    userId = UserHandle.USER_SYSTEM;
-                }
-                if (DEBUG_INSTALL) {
-                    Log.v(TAG, "token " + token + " to BM for possible restore for user " + userId);
-                }
-                Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "restore", token);
-                try {
-                    if (bm.isBackupServiceActive(userId)) {
-                        bm.restoreAtInstallForUser(
-                                userId, res.pkg.getAppInfoPackageName(), token);
-                    } else {
-                        doRestore = false;
-                    }
-                } catch (RemoteException e) {
-                    // can't happen; the backup manager is local
-                } catch (Exception e) {
-                    Slog.e(TAG, "Exception trying to enqueue restore", e);
-                    doRestore = false;
-                }
-            } else {
-                Slog.e(TAG, "Backup Manager not found!");
-                doRestore = false;
+            if (res.freezer != null) {
+                res.freezer.close();
             }
+            doRestore = performBackupManagerRestore(userId, token, res);
         }
 
         // If this is an update to a package that might be potentially downgraded, then we
@@ -13471,42 +13454,7 @@
         //
         // TODO(narayan): Get this working for cases where userId == UserHandle.USER_ALL.
         if (res.returnCode == PackageManager.INSTALL_SUCCEEDED && !doRestore && update) {
-            IRollbackManager rm = IRollbackManager.Stub.asInterface(
-                    ServiceManager.getService(Context.ROLLBACK_SERVICE));
-
-            final String packageName = res.pkg.getAppInfoPackageName();
-            final String seInfo = res.pkg.getSeInfo();
-            final int[] allUsers = mUserManager.getUserIds();
-            final int[] installedUsers;
-
-            final PackageSetting ps;
-            int appId = -1;
-            long ceDataInode = -1;
-            synchronized (mSettings) {
-                ps = mSettings.getPackageLPr(packageName);
-                if (ps != null) {
-                    appId = ps.appId;
-                    ceDataInode = ps.getCeDataInode(userId);
-                }
-
-                // NOTE: We ignore the user specified in the InstallParam because we know this is
-                // an update, and hence need to restore data for all installed users.
-                installedUsers = ps.queryInstalledUsers(allUsers, true);
-            }
-
-            boolean doSnapshotOrRestore = data != null && data.args != null
-                    && ((data.args.installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0
-                    || (data.args.installFlags & PackageManager.INSTALL_REQUEST_DOWNGRADE) != 0);
-
-            if (ps != null && doSnapshotOrRestore) {
-                try {
-                    rm.snapshotAndRestoreUserData(packageName, installedUsers, appId, ceDataInode,
-                            seInfo, token);
-                } catch (RemoteException re) {
-                    Log.e(TAG, "Error snapshotting/restoring user data: " + re);
-                }
-                doRestore = true;
-            }
+            doRestore = performRollbackManagerRestore(userId, token, res, data);
         }
 
         if (!doRestore) {
@@ -13522,6 +13470,89 @@
     }
 
     /**
+     * Perform Backup Manager restore for a given {@link PackageInstalledInfo}.
+     * Returns whether the restore successfully completed.
+     */
+    private boolean performBackupManagerRestore(int userId, int token, PackageInstalledInfo res) {
+        IBackupManager bm = IBackupManager.Stub.asInterface(
+                ServiceManager.getService(Context.BACKUP_SERVICE));
+        if (bm != null) {
+            // For backwards compatibility as USER_ALL previously routed directly to USER_SYSTEM
+            // in the BackupManager. USER_ALL is used in compatibility tests.
+            if (userId == UserHandle.USER_ALL) {
+                userId = UserHandle.USER_SYSTEM;
+            }
+            if (DEBUG_INSTALL) {
+                Log.v(TAG, "token " + token + " to BM for possible restore for user " + userId);
+            }
+            Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "restore", token);
+            try {
+                if (bm.isBackupServiceActive(userId)) {
+                    bm.restoreAtInstallForUser(
+                            userId, res.pkg.getAppInfoPackageName(), token);
+                } else {
+                    return false;
+                }
+            } catch (RemoteException e) {
+                // can't happen; the backup manager is local
+            } catch (Exception e) {
+                Slog.e(TAG, "Exception trying to enqueue restore", e);
+                return false;
+            }
+        } else {
+            Slog.e(TAG, "Backup Manager not found!");
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Perform Rollback Manager restore for a given {@link PackageInstalledInfo}.
+     * Returns whether the restore successfully completed.
+     */
+    private boolean performRollbackManagerRestore(int userId, int token, PackageInstalledInfo res,
+            PostInstallData data) {
+        IRollbackManager rm = IRollbackManager.Stub.asInterface(
+                ServiceManager.getService(Context.ROLLBACK_SERVICE));
+
+        final String packageName = res.pkg.getAppInfoPackageName();
+        final String seInfo = res.pkg.getSeInfo();
+        final int[] allUsers = mUserManager.getUserIds();
+        final int[] installedUsers;
+
+        final PackageSetting ps;
+        int appId = -1;
+        long ceDataInode = -1;
+        synchronized (mSettings) {
+            ps = mSettings.getPackageLPr(packageName);
+            if (ps != null) {
+                appId = ps.appId;
+                ceDataInode = ps.getCeDataInode(userId);
+            }
+
+            // NOTE: We ignore the user specified in the InstallParam because we know this is
+            // an update, and hence need to restore data for all installed users.
+            installedUsers = ps.queryInstalledUsers(allUsers, true);
+        }
+
+        boolean doSnapshotOrRestore = data != null && data.args != null
+                && ((data.args.installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0
+                || (data.args.installFlags & PackageManager.INSTALL_REQUEST_DOWNGRADE) != 0);
+
+        if (ps != null && doSnapshotOrRestore) {
+            try {
+                rm.snapshotAndRestoreUserData(packageName, installedUsers, appId, ceDataInode,
+                        seInfo, token);
+            } catch (RemoteException re) {
+                Log.e(TAG, "Error snapshotting/restoring user data: " + re);
+                return false;
+            }
+            return true;
+        }
+        return false;
+    }
+
+    /**
      * Callback from PackageSettings whenever an app is first transitioned out of the
      * 'stopped' state.  Normally we just issue the broadcast, but we can't do that if
      * the app was "launched" for a restoreAtInstall operation.  Therefore we check
@@ -14802,6 +14833,7 @@
         ArrayMap<String, PackageInstalledInfo> addedChildPackages;
         // The set of packages consuming this shared library or null if no consumers exist.
         ArrayList<AndroidPackage> libraryConsumers;
+        PackageFreezer freezer;
 
         public void setError(int code, String msg) {
             setReturnCode(code);
@@ -15675,16 +15707,12 @@
                 // TODO(patb): create a more descriptive reason than unknown in future release
                 // mark all non-failure installs as UNKNOWN so we do not treat them as success
                 for (InstallRequest request : requests) {
+                    request.installResult.freezer.close();
                     if (request.installResult.returnCode == PackageManager.INSTALL_SUCCEEDED) {
                         request.installResult.returnCode = PackageManager.INSTALL_UNKNOWN;
                     }
                 }
             }
-            for (PrepareResult result : prepareResults.values()) {
-                if (result.freezer != null) {
-                    result.freezer.close();
-                }
-            }
             Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
         }
     }
@@ -15792,15 +15820,13 @@
         public final ParsedPackage packageToScan;
         public final boolean clearCodeCache;
         public final boolean system;
-        public final PackageFreezer freezer;
         public final PackageSetting originalPs;
         public final PackageSetting disabledPs;
 
         private PrepareResult(boolean replace, int scanFlags,
                 int parseFlags, AndroidPackage existingPackage,
                 ParsedPackage packageToScan, boolean clearCodeCache, boolean system,
-                PackageFreezer freezer, PackageSetting originalPs,
-                PackageSetting disabledPs) {
+                PackageSetting originalPs, PackageSetting disabledPs) {
             this.replace = replace;
             this.scanFlags = scanFlags;
             this.parseFlags = parseFlags;
@@ -15808,7 +15834,6 @@
             this.packageToScan = packageToScan;
             this.clearCodeCache = clearCodeCache;
             this.system = system;
-            this.freezer = freezer;
             this.originalPs = originalPs;
             this.disabledPs = disabledPs;
         }
@@ -16437,9 +16462,10 @@
             shouldCloseFreezerBeforeReturn = false;
 
             return new PrepareResult(replace, targetScanFlags, targetParseFlags,
-                    existingPackage, parsedPackage, replace /* clearCodeCache */, sysPkg, freezer,
+                    existingPackage, parsedPackage, replace /* clearCodeCache */, sysPkg,
                     ps, disabledPs);
         } finally {
+            res.freezer = freezer;
             if (shouldCloseFreezerBeforeReturn) {
                 freezer.close();
             }
@@ -19160,13 +19186,14 @@
 
     @Override
     public String getSystemTextClassifierPackageName() {
-        return mContext.getString(R.string.config_defaultTextClassifierPackage);
+        return ensureSystemPackageName(mContext.getString(
+                R.string.config_defaultTextClassifierPackage));
     }
 
     @Override
     public String[] getSystemTextClassifierPackages() {
-        return mContext.getResources().getStringArray(
-                R.array.config_defaultTextClassifierPackages);
+        return ensureSystemPackageNames(mContext.getResources().getStringArray(
+                R.array.config_defaultTextClassifierPackages));
     }
 
     @Override
@@ -19176,7 +19203,7 @@
         if (flattenedComponentName != null) {
             ComponentName componentName = ComponentName.unflattenFromString(flattenedComponentName);
             if (componentName != null && componentName.getPackageName() != null) {
-                return componentName.getPackageName();
+                return ensureSystemPackageName(componentName.getPackageName());
             }
         }
         return null;
@@ -19201,9 +19228,15 @@
         }
     }
 
+    @Nullable
+    private String getDeviceConfiguratorPackageName() {
+        return ensureSystemPackageName(mContext.getString(
+                R.string.config_deviceConfiguratorPackageName));
+    }
+
     @Override
     public String getWellbeingPackageName() {
-        return mContext.getString(R.string.config_defaultWellbeingPackage);
+        return ensureSystemPackageName(mContext.getString(R.string.config_defaultWellbeingPackage));
     }
 
     @Override
@@ -19218,7 +19251,7 @@
         if (appPredictionServiceComponentName == null) {
             return null;
         }
-        return appPredictionServiceComponentName.getPackageName();
+        return ensureSystemPackageName(appPredictionServiceComponentName.getPackageName());
     }
 
     @Override
@@ -19235,7 +19268,7 @@
         if (systemCaptionsServiceComponentName == null) {
             return null;
         }
-        return systemCaptionsServiceComponentName.getPackageName();
+        return ensureSystemPackageName(systemCaptionsServiceComponentName.getPackageName());
     }
 
     @Override
@@ -19247,7 +19280,8 @@
     }
 
     public String getIncidentReportApproverPackageName() {
-        return mContext.getString(R.string.config_incidentReportApproverPackage);
+        return ensureSystemPackageName(mContext.getString(
+                R.string.config_incidentReportApproverPackage));
     }
 
     @Override
@@ -19257,7 +19291,7 @@
         if (!TextUtils.isEmpty(names)) {
             telephonyPackageNames = names.trim().split(",");
         }
-        return telephonyPackageNames;
+        return ensureSystemPackageNames(telephonyPackageNames);
     }
 
     @Override
@@ -19274,7 +19308,32 @@
         if (contentCaptureServiceComponentName == null) {
             return null;
         }
-        return contentCaptureServiceComponentName.getPackageName();
+        return ensureSystemPackageName(contentCaptureServiceComponentName.getPackageName());
+    }
+
+    @Nullable
+    private String ensureSystemPackageName(@Nullable String packageName) {
+        if (packageName == null) {
+            return null;
+        }
+        if (getPackageInfo(packageName, MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE
+                        | MATCH_DIRECT_BOOT_UNAWARE | MATCH_DISABLED_COMPONENTS,
+                UserHandle.getCallingUserId()) == null) {
+            return null;
+        }
+        return packageName;
+    }
+
+    @Nullable
+    private String[] ensureSystemPackageNames(@Nullable String[] packageNames) {
+        if (packageNames == null) {
+            return null;
+        }
+        final int packageNamesLength = packageNames.length;
+        for (int i = 0; i < packageNamesLength; i++) {
+            packageNames[i] = ensureSystemPackageName(packageNames[i]);
+        }
+        return ArrayUtils.filterNotNull(packageNames, String[]::new);
     }
 
     @Override
@@ -23199,6 +23258,14 @@
                 mSettings.writeLPr();
             }
         }
+
+        @Override
+        public void setIntegrityVerificationResult(int verificationId, int verificationResult) {
+            final Message msg = mHandler.obtainMessage(INTEGRITY_VERIFICATION_COMPLETE);
+            msg.arg1 = verificationId;
+            msg.obj = verificationResult;
+            mHandler.sendMessage(msg);
+        }
     }
 
     @GuardedBy("mLock")
@@ -23233,11 +23300,12 @@
                 PackageSetting ps = it.next();
                 if (ps.getInstalled(userId)) {
                     res[i++] = ps.name;
-                } else {
-                    res = ArrayUtils.removeElement(String.class, res, res[i]);
                 }
             }
-            return res;
+            res = ArrayUtils.trimToSize(res, i);
+            if (res != null) {
+                return res;
+            }
         } catch (PackageManagerException e) {
             // Should not happen
         }
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index 95a5f52..b28a112 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -18,6 +18,7 @@
 
 import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
 import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
+import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ABOVE_SUB_PANEL;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG;
@@ -876,13 +877,15 @@
             case TYPE_ACCESSIBILITY_OVERLAY:
                 // overlay put by accessibility services to intercept user interaction
                 return  30;
+            case TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY:
+                return 31;
             case TYPE_SECURE_SYSTEM_OVERLAY:
-                return  31;
-            case TYPE_BOOT_PROGRESS:
                 return  32;
+            case TYPE_BOOT_PROGRESS:
+                return  33;
             case TYPE_POINTER:
                 // the (mouse) pointer layer
-                return  33;
+                return  34;
             default:
                 Slog.e("WindowManager", "Unknown window type: " + type);
                 return APPLICATION_LAYER;
diff --git a/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java b/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java
index ff91299..a62bb74 100644
--- a/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java
+++ b/services/core/java/com/android/server/power/batterysaver/BatterySaverStateMachine.java
@@ -773,9 +773,9 @@
 
         // Handle triggering the notification to show/hide when appropriate
         if (intReason == BatterySaverController.REASON_DYNAMIC_POWER_SAVINGS_AUTOMATIC_ON) {
-            runOnBgThread(this::triggerDynamicModeNotification);
+            triggerDynamicModeNotification();
         } else if (!enable) {
-            runOnBgThread(this::hideDynamicModeNotification);
+            hideDynamicModeNotification();
         }
 
         if (DEBUG) {
@@ -787,33 +787,42 @@
 
     @VisibleForTesting
     void triggerDynamicModeNotification() {
-        NotificationManager manager = mContext.getSystemService(NotificationManager.class);
-        ensureNotificationChannelExists(manager, DYNAMIC_MODE_NOTIF_CHANNEL_ID,
-                R.string.dynamic_mode_notification_channel_name);
+        // The current lock is the PowerManager lock, which sits very low in the service lock
+        // hierarchy. We shouldn't call out to NotificationManager with the PowerManager lock.
+        runOnBgThread(() -> {
+            NotificationManager manager = mContext.getSystemService(NotificationManager.class);
+            ensureNotificationChannelExists(manager, DYNAMIC_MODE_NOTIF_CHANNEL_ID,
+                    R.string.dynamic_mode_notification_channel_name);
 
-        manager.notifyAsUser(TAG, DYNAMIC_MODE_NOTIFICATION_ID,
-                buildNotification(DYNAMIC_MODE_NOTIF_CHANNEL_ID,
-                        mContext.getResources().getString(R.string.dynamic_mode_notification_title),
-                        R.string.dynamic_mode_notification_summary,
-                        Intent.ACTION_POWER_USAGE_SUMMARY),
-                UserHandle.ALL);
+            manager.notifyAsUser(TAG, DYNAMIC_MODE_NOTIFICATION_ID,
+                    buildNotification(DYNAMIC_MODE_NOTIF_CHANNEL_ID,
+                            mContext.getResources().getString(
+                                    R.string.dynamic_mode_notification_title),
+                            R.string.dynamic_mode_notification_summary,
+                            Intent.ACTION_POWER_USAGE_SUMMARY),
+                    UserHandle.ALL);
+        });
     }
 
     @VisibleForTesting
     void triggerStickyDisabledNotification() {
-        NotificationManager manager = mContext.getSystemService(NotificationManager.class);
-        ensureNotificationChannelExists(manager, BATTERY_SAVER_NOTIF_CHANNEL_ID,
-                R.string.battery_saver_notification_channel_name);
+        // The current lock is the PowerManager lock, which sits very low in the service lock
+        // hierarchy. We shouldn't call out to NotificationManager with the PowerManager lock.
+        runOnBgThread(() -> {
+            NotificationManager manager = mContext.getSystemService(NotificationManager.class);
+            ensureNotificationChannelExists(manager, BATTERY_SAVER_NOTIF_CHANNEL_ID,
+                    R.string.battery_saver_notification_channel_name);
 
-        final String percentage = NumberFormat.getPercentInstance()
-                .format((double) mBatteryLevel / 100.0);
-        manager.notifyAsUser(TAG, STICKY_AUTO_DISABLED_NOTIFICATION_ID,
-                buildNotification(BATTERY_SAVER_NOTIF_CHANNEL_ID,
-                        mContext.getResources().getString(
-                                R.string.battery_saver_charged_notification_title, percentage),
-                        R.string.battery_saver_off_notification_summary,
-                        Settings.ACTION_BATTERY_SAVER_SETTINGS),
-                UserHandle.ALL);
+            final String percentage = NumberFormat.getPercentInstance()
+                    .format((double) mBatteryLevel / 100.0);
+            manager.notifyAsUser(TAG, STICKY_AUTO_DISABLED_NOTIFICATION_ID,
+                    buildNotification(BATTERY_SAVER_NOTIF_CHANNEL_ID,
+                            mContext.getResources().getString(
+                                    R.string.battery_saver_charged_notification_title, percentage),
+                            R.string.battery_saver_off_notification_summary,
+                            Settings.ACTION_BATTERY_SAVER_SETTINGS),
+                    UserHandle.ALL);
+        });
     }
 
     private void ensureNotificationChannelExists(NotificationManager manager,
@@ -854,8 +863,12 @@
     }
 
     private void hideNotification(int notificationId) {
-        NotificationManager manager = mContext.getSystemService(NotificationManager.class);
-        manager.cancel(notificationId);
+        // The current lock is the PowerManager lock, which sits very low in the service lock
+        // hierarchy. We shouldn't call out to NotificationManager with the PowerManager lock.
+        runOnBgThread(() -> {
+            NotificationManager manager = mContext.getSystemService(NotificationManager.class);
+            manager.cancelAsUser(TAG, notificationId, UserHandle.ALL);
+        });
     }
 
     private void setStickyActive(boolean active) {
diff --git a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
index a626166..b84fd79 100644
--- a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
+++ b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
@@ -103,8 +103,7 @@
 
     @Override
     public int onHealthCheckFailed(VersionedPackage failedPackage) {
-        if (getAvailableRollback(mContext.getSystemService(RollbackManager.class), failedPackage)
-                == null) {
+        if (getAvailableRollback(failedPackage) == null) {
             // Don't handle the notification, no rollbacks available for the package
             return PackageHealthObserverImpact.USER_IMPACT_NONE;
         } else {
@@ -115,51 +114,14 @@
 
     @Override
     public boolean execute(VersionedPackage failedPackage, @FailureReasons int rollbackReason) {
-        RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class);
-        VersionedPackage moduleMetadataPackage = getModuleMetadataPackage();
-        RollbackInfo rollback = getAvailableRollback(rollbackManager, failedPackage);
-        int reasonToLog = mapFailureReasonToMetric(rollbackReason);
-
+        RollbackInfo rollback = getAvailableRollback(failedPackage);
         if (rollback == null) {
             Slog.w(TAG, "Expected rollback but no valid rollback found for package: [ "
                     + failedPackage.getPackageName() + "] with versionCode: ["
                     + failedPackage.getVersionCode() + "]");
             return false;
         }
-
-        logEvent(moduleMetadataPackage,
-                StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_INITIATE,
-                reasonToLog, failedPackage.getPackageName());
-        LocalIntentReceiver rollbackReceiver = new LocalIntentReceiver((Intent result) -> {
-            int status = result.getIntExtra(RollbackManager.EXTRA_STATUS,
-                    RollbackManager.STATUS_FAILURE);
-            if (status == RollbackManager.STATUS_SUCCESS) {
-                if (rollback.isStaged()) {
-                    int rollbackId = rollback.getRollbackId();
-                    synchronized (mPendingStagedRollbackIds) {
-                        mPendingStagedRollbackIds.add(rollbackId);
-                    }
-                    BroadcastReceiver listener =
-                            listenForStagedSessionReady(rollbackManager, rollbackId,
-                                    moduleMetadataPackage);
-                    handleStagedSessionChange(rollbackManager, rollbackId, listener,
-                            moduleMetadataPackage);
-                } else {
-                    logEvent(moduleMetadataPackage,
-                            StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS,
-                            reasonToLog, failedPackage.getPackageName());
-                }
-            } else {
-                logEvent(moduleMetadataPackage,
-                        StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE,
-                        reasonToLog, failedPackage.getPackageName());
-            }
-        });
-
-        mHandler.post(() ->
-                rollbackManager.commitRollback(rollback.getRollbackId(),
-                    Collections.singletonList(failedPackage),
-                    rollbackReceiver.getIntentSender()));
+        rollbackPackage(rollback, failedPackage, rollbackReason);
         // Assume rollback executed successfully
         return true;
     }
@@ -188,9 +150,8 @@
         RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class);
         PackageInstaller packageInstaller = mContext.getPackageManager().getPackageInstaller();
         String moduleMetadataPackageName = getModuleMetadataPackageName();
-        VersionedPackage newModuleMetadataPackage = getModuleMetadataPackage();
 
-        if (getAvailableRollback(rollbackManager, newModuleMetadataPackage) != null) {
+        if (!rollbackManager.getAvailableRollbacks().isEmpty()) {
             scheduleCheckAndMitigateNativeCrashes();
         }
 
@@ -242,8 +203,8 @@
         }
     }
 
-    private RollbackInfo getAvailableRollback(RollbackManager rollbackManager,
-            VersionedPackage failedPackage) {
+    private RollbackInfo getAvailableRollback(VersionedPackage failedPackage) {
+        RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class);
         for (RollbackInfo rollback : rollbackManager.getAvailableRollbacks()) {
             for (PackageRollbackInfo packageRollback : rollback.getPackages()) {
                 boolean hasFailedPackage = packageRollback.getPackageName().equals(
@@ -285,7 +246,7 @@
     }
 
     private BroadcastReceiver listenForStagedSessionReady(RollbackManager rollbackManager,
-            int rollbackId, VersionedPackage moduleMetadataPackage) {
+            int rollbackId, @Nullable VersionedPackage moduleMetadataPackage) {
         BroadcastReceiver sessionUpdatedReceiver = new BroadcastReceiver() {
             @Override
             public void onReceive(Context context, Intent intent) {
@@ -300,7 +261,7 @@
     }
 
     private void handleStagedSessionChange(RollbackManager rollbackManager, int rollbackId,
-            BroadcastReceiver listener, VersionedPackage moduleMetadataPackage) {
+            BroadcastReceiver listener, @Nullable VersionedPackage moduleMetadataPackage) {
         PackageInstaller packageInstaller =
                 mContext.getPackageManager().getPackageInstaller();
         List<RollbackInfo> recentRollbacks =
@@ -390,7 +351,7 @@
         mNumberOfNativeCrashPollsRemaining--;
         // Check if native watchdog reported a crash
         if ("1".equals(SystemProperties.get("sys.init.updatable_crashing"))) {
-            execute(getModuleMetadataPackage(), PackageWatchdog.FAILURE_REASON_NATIVE_CRASH);
+            rollbackAll();
             // we stop polling after an attempt to execute rollback, regardless of whether the
             // attempt succeeds or not
         } else {
@@ -402,6 +363,100 @@
     }
 
     /**
+     * Returns true if the package name is the name of a module.
+     */
+    private boolean isModule(String packageName) {
+        PackageManager pm = mContext.getPackageManager();
+        try {
+            return pm.getModuleInfo(packageName, 0) != null;
+        } catch (PackageManager.NameNotFoundException ignore) {
+            return false;
+        }
+    }
+
+    private VersionedPackage getVersionedPackage(String packageName) {
+        try {
+            return new VersionedPackage(packageName, mContext.getPackageManager().getPackageInfo(
+                    packageName, 0 /* flags */).getLongVersionCode());
+        } catch (PackageManager.NameNotFoundException e) {
+            return null;
+        }
+    }
+
+    /**
+     * Rolls back the session that owns {@code failedPackage}
+     *
+     * @param rollback {@code rollbackInfo} of the {@code failedPackage}
+     * @param failedPackage the package that needs to be rolled back
+     */
+    private void rollbackPackage(RollbackInfo rollback, VersionedPackage failedPackage,
+            @FailureReasons int rollbackReason) {
+        final RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class);
+        int reasonToLog = mapFailureReasonToMetric(rollbackReason);
+        final String failedPackageToLog;
+        if (rollbackReason == PackageWatchdog.FAILURE_REASON_NATIVE_CRASH) {
+            failedPackageToLog = SystemProperties.get(
+                    "sys.init.updatable_crashing_process_name", "");
+        } else {
+            failedPackageToLog = failedPackage.getPackageName();
+        }
+        final VersionedPackage logPackage = isModule(failedPackage.getPackageName())
+                ? getModuleMetadataPackage()
+                : null;
+
+        logEvent(logPackage,
+                StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_INITIATE,
+                reasonToLog, failedPackageToLog);
+        final LocalIntentReceiver rollbackReceiver = new LocalIntentReceiver((Intent result) -> {
+            int status = result.getIntExtra(RollbackManager.EXTRA_STATUS,
+                    RollbackManager.STATUS_FAILURE);
+            if (status == RollbackManager.STATUS_SUCCESS) {
+                if (rollback.isStaged()) {
+                    int rollbackId = rollback.getRollbackId();
+                    synchronized (mPendingStagedRollbackIds) {
+                        mPendingStagedRollbackIds.add(rollbackId);
+                    }
+                    BroadcastReceiver listener =
+                            listenForStagedSessionReady(rollbackManager, rollbackId,
+                                    logPackage);
+                    handleStagedSessionChange(rollbackManager, rollbackId, listener,
+                            logPackage);
+                } else {
+                    logEvent(logPackage,
+                            StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_SUCCESS,
+                            reasonToLog, failedPackageToLog);
+                }
+            } else {
+                logEvent(logPackage,
+                        StatsLog.WATCHDOG_ROLLBACK_OCCURRED__ROLLBACK_TYPE__ROLLBACK_FAILURE,
+                        reasonToLog, failedPackageToLog);
+            }
+        });
+
+        mHandler.post(() ->
+                rollbackManager.commitRollback(rollback.getRollbackId(),
+                        Collections.singletonList(failedPackage),
+                        rollbackReceiver.getIntentSender()));
+    }
+
+    private void rollbackAll() {
+        Slog.i(TAG, "Rolling back all available rollbacks");
+        RollbackManager rollbackManager = mContext.getSystemService(RollbackManager.class);
+        List<RollbackInfo> rollbacks = rollbackManager.getAvailableRollbacks();
+
+        for (RollbackInfo rollback : rollbacks) {
+            String samplePackageName = rollback.getPackages().get(0).getPackageName();
+            VersionedPackage sampleVersionedPackage = getVersionedPackage(samplePackageName);
+            if (sampleVersionedPackage == null) {
+                Slog.e(TAG, "Failed to rollback " + samplePackageName);
+                continue;
+            }
+            rollbackPackage(rollback, sampleVersionedPackage,
+                    PackageWatchdog.FAILURE_REASON_NATIVE_CRASH);
+        }
+    }
+
+    /**
      * Since this method can eventually trigger a RollbackManager rollback, it should be called
      * only once boot has completed {@code onBootCompleted} and not earlier, because the install
      * session must be entirely completed before we try to rollback.
diff --git a/services/core/java/com/android/server/storage/StorageSessionController.java b/services/core/java/com/android/server/storage/StorageSessionController.java
index d76836f..46dd366 100644
--- a/services/core/java/com/android/server/storage/StorageSessionController.java
+++ b/services/core/java/com/android/server/storage/StorageSessionController.java
@@ -43,7 +43,6 @@
 import com.android.internal.util.Preconditions;
 
 import java.io.FileDescriptor;
-import java.io.IOException;
 
 /**
  * Controls storage sessions for users initiated by the {@link StorageManagerService}.
@@ -101,18 +100,10 @@
                 connection = new StorageUserConnection(mContext, userId, this);
                 mConnections.put(userId, connection);
             }
-            Slog.i(TAG, "Creating session with id: " + sessionId);
-            connection.createSession(sessionId, new ParcelFileDescriptor(deviceFd),
+            Slog.i(TAG, "Creating and starting session with id: " + sessionId);
+            connection.startSession(sessionId, new ParcelFileDescriptor(deviceFd),
                     vol.getPath().getPath(), vol.getInternalPath().getPath());
         }
-
-        // At boot, a volume can be mounted before user is unlocked, in that case, we create it
-        // above and save it so that we can restart all sessions when the user is unlocked
-        if (mExternalStorageServiceComponent != null) {
-            connection.startSession(sessionId);
-        } else {
-            Slog.i(TAG, "Controller not initialised, session not started " + sessionId);
-        }
     }
 
     /**
@@ -179,23 +170,32 @@
      * a session will be ignored.
      */
     public void onUnlockUser(int userId) throws ExternalStorageServiceException {
+        Slog.i(TAG, "On user unlock " + userId);
+        if (shouldHandle(null) && userId == 0) {
+            initExternalStorageServiceComponent();
+        }
+    }
+
+    /**
+     * Called when a user is in the process is being stopped.
+     *
+     * Does nothing if {@link #shouldHandle} is {@code false}
+     *
+     * This call removes all sessions for the user that is being stopped;
+     * this will make sure that we don't rebind to the service needlessly.
+     */
+    public void onUserStopping(int userId) throws ExternalStorageServiceException {
         if (!shouldHandle(null)) {
             return;
         }
-
-        Slog.i(TAG, "On user unlock " + userId);
-        if (userId == 0) {
-            initExternalStorageServiceComponent();
-        }
-
         StorageUserConnection connection = null;
         synchronized (mLock) {
             connection = mConnections.get(userId);
         }
 
         if (connection != null) {
-            Slog.i(TAG, "Restarting all sessions for user: " + userId);
-            connection.startAllSessions();
+            Slog.i(TAG, "Removing all sessions for user: " + userId);
+            connection.removeAllSessions();
         } else {
             Slog.w(TAG, "No connection found for user: " + userId);
         }
diff --git a/services/core/java/com/android/server/storage/StorageUserConnection.java b/services/core/java/com/android/server/storage/StorageUserConnection.java
index 7c47730..5c44eee 100644
--- a/services/core/java/com/android/server/storage/StorageUserConnection.java
+++ b/services/core/java/com/android/server/storage/StorageUserConnection.java
@@ -34,6 +34,7 @@
 import android.os.ParcelableException;
 import android.os.RemoteCallback;
 import android.os.UserHandle;
+import android.os.storage.StorageManagerInternal;
 import android.service.storage.ExternalStorageService;
 import android.service.storage.IExternalStorageService;
 import android.text.TextUtils;
@@ -41,6 +42,7 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.Preconditions;
+import com.android.server.LocalServices;
 
 import java.io.IOException;
 import java.util.HashMap;
@@ -73,42 +75,25 @@
     }
 
     /**
-     * Creates and stores a storage {@link Session}.
+     * Creates and starts a storage {@link Session}.
      *
      * They must also be cleaned up with {@link #removeSession}.
      *
      * @throws IllegalArgumentException if a {@code Session} with {@code sessionId} already exists
      */
-    public void createSession(String sessionId, ParcelFileDescriptor pfd, String upperPath,
-            String lowerPath) {
+    public void startSession(String sessionId, ParcelFileDescriptor pfd, String upperPath,
+            String lowerPath) throws ExternalStorageServiceException {
         Preconditions.checkNotNull(sessionId);
         Preconditions.checkNotNull(pfd);
         Preconditions.checkNotNull(upperPath);
         Preconditions.checkNotNull(lowerPath);
 
-        synchronized (mLock) {
-            Preconditions.checkArgument(!mSessions.containsKey(sessionId));
-            mSessions.put(sessionId, new Session(sessionId, pfd, upperPath, lowerPath));
-        }
-    }
-
-    /**
-     * Starts an already created storage {@link Session} for {@code sessionId}.
-     *
-     * It is safe to call this multiple times, however if the session is already started,
-     * subsequent calls will be ignored.
-     *
-     * @throws ExternalStorageServiceException if the session failed to start
-     **/
-    public void startSession(String sessionId) throws ExternalStorageServiceException {
-        Session session;
-        synchronized (mLock) {
-            session = mSessions.get(sessionId);
-        }
-
         prepareRemote();
         synchronized (mLock) {
-            mActiveConnection.startSessionLocked(session);
+            Preconditions.checkArgument(!mSessions.containsKey(sessionId));
+            Session session = new Session(sessionId, upperPath, lowerPath);
+            mSessions.put(sessionId, session);
+            mActiveConnection.startSessionLocked(session, pfd);
         }
     }
 
@@ -121,16 +106,10 @@
      **/
     public Session removeSession(String sessionId) {
         synchronized (mLock) {
-            Session session = mSessions.remove(sessionId);
-            if (session != null) {
-                session.close();
-                return session;
-            }
-            return null;
+            return mSessions.remove(sessionId);
         }
     }
 
-
     /**
      * Removes a session and waits for exit
      *
@@ -150,26 +129,30 @@
         }
     }
 
-    /** Starts all available sessions for a user without blocking. Any failures will be ignored. */
-    public void startAllSessions() {
-        try {
-            prepareRemote();
-        } catch (ExternalStorageServiceException e) {
-            Slog.e(TAG, "Failed to start all sessions for user: " + mUserId, e);
-            return;
-        }
-
+    /** Restarts all available sessions for a user without blocking.
+     *
+     * Any failures will be ignored.
+     **/
+    public void resetUserSessions() {
         synchronized (mLock) {
-            Slog.i(TAG, "Starting " + mSessions.size() + " sessions for user: " + mUserId + "...");
-            for (Session session : mSessions.values()) {
-                try {
-                    mActiveConnection.startSessionLocked(session);
-                } catch (IllegalStateException | ExternalStorageServiceException e) {
-                    // TODO: Don't crash process? We could get into process crash loop
-                    Slog.e(TAG, "Failed to start " + session, e);
-                }
+            if (mSessions.isEmpty()) {
+                // Nothing to reset if we have no sessions to restart; we typically
+                // hit this path if the user was consciously shut down.
+                return;
             }
         }
+        StorageManagerInternal sm = LocalServices.getService(StorageManagerInternal.class);
+        sm.resetUser(mUserId);
+    }
+
+    /**
+     * Removes all sessions, without waiting.
+     */
+    public void removeAllSessions() {
+        synchronized (mLock) {
+            Slog.i(TAG, "Removing  " + mSessions.size() + " sessions for user: " + mUserId + "...");
+            mSessions.clear();
+        }
     }
 
     /**
@@ -269,26 +252,37 @@
             return true;
         }
 
-        public void startSessionLocked(Session session) throws ExternalStorageServiceException {
+        public void startSessionLocked(Session session, ParcelFileDescriptor fd)
+                throws ExternalStorageServiceException {
             if (!isActiveLocked(session)) {
+                try {
+                    fd.close();
+                } catch (IOException e) {
+                    // ignore
+                }
                 return;
             }
 
             CountDownLatch latch = new CountDownLatch(1);
-            try (ParcelFileDescriptor dupedPfd = session.pfd.dup()) {
+            try {
                 mRemote.startSession(session.sessionId,
                         FLAG_SESSION_TYPE_FUSE | FLAG_SESSION_ATTRIBUTE_INDEXABLE,
-                        dupedPfd, session.upperPath, session.lowerPath, new RemoteCallback(result ->
+                        fd, session.upperPath, session.lowerPath, new RemoteCallback(result ->
                                 setResultLocked(latch, result)));
                 waitForLatch(latch, "start_session " + session);
                 maybeThrowExceptionLocked();
             } catch (Exception e) {
                 throw new ExternalStorageServiceException("Failed to start session: " + session, e);
+            } finally {
+                try {
+                    fd.close();
+                } catch (IOException e) {
+                    // Ignore
+                }
             }
         }
 
         public void endSessionLocked(Session session) throws ExternalStorageServiceException {
-            session.close();
             if (!isActiveLocked(session)) {
                 // Nothing to end, not started yet
                 return;
@@ -390,7 +384,7 @@
                         // will be called for any required mounts.
                         // Notify StorageManagerService so it can restart all necessary sessions
                         close();
-                        new Thread(StorageUserConnection.this::startAllSessions).start();
+                        resetUserSessions();
                     }
                 };
             }
@@ -411,29 +405,18 @@
         }
     }
 
-    private static final class Session implements AutoCloseable {
+    private static final class Session {
         public final String sessionId;
-        public final ParcelFileDescriptor pfd;
         public final String lowerPath;
         public final String upperPath;
 
-        Session(String sessionId, ParcelFileDescriptor pfd, String upperPath, String lowerPath) {
+        Session(String sessionId, String upperPath, String lowerPath) {
             this.sessionId = sessionId;
-            this.pfd = pfd;
             this.upperPath = upperPath;
             this.lowerPath = lowerPath;
         }
 
         @Override
-        public void close() {
-            try {
-                pfd.close();
-            } catch (IOException e) {
-                Slog.i(TAG, "Failed to close session: " + this);
-            }
-        }
-
-        @Override
         public String toString() {
             return "[SessionId: " + sessionId + ". UpperPath: " + upperPath + ". LowerPath: "
                     + lowerPath + "]";
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index fb573e1..092a844 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -6527,7 +6527,7 @@
         } else if (mCompatDisplayInsets != null) {
             // The override changes can only be obtained from display, because we don't have the
             // difference of full configuration in each hierarchy.
-            final int displayChanges = display.getLastOverrideConfigurationChanges();
+            final int displayChanges = display.getCurrentOverrideConfigurationChanges();
             final int orientationChanges = CONFIG_WINDOW_CONFIGURATION
                     | CONFIG_SCREEN_SIZE | CONFIG_ORIENTATION;
             final boolean hasNonOrienSizeChanged = hasResizeChange(displayChanges)
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java
index bfa72e0..bd0ea3d 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainer.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java
@@ -81,9 +81,6 @@
      */
     private Configuration mFullConfiguration = new Configuration();
 
-    /** The bit mask of the last override fields of full configuration. */
-    private int mLastOverrideConfigurationChanges;
-
     /**
      * Contains merged override configuration settings from the top of the hierarchy down to this
      * particular instance. It is different from {@link #mFullConfiguration} because it starts from
@@ -121,11 +118,6 @@
         return mFullConfiguration;
     }
 
-    /** Returns the last changes from applying override configuration. */
-    int getLastOverrideConfigurationChanges() {
-        return mLastOverrideConfigurationChanges;
-    }
-
     /**
      * Notify that parent config changed and we need to update full configuration.
      * @see #mFullConfiguration
@@ -141,8 +133,7 @@
         mResolvedTmpConfig.setTo(mResolvedOverrideConfiguration);
         resolveOverrideConfiguration(newParentConfig);
         mFullConfiguration.setTo(newParentConfig);
-        mLastOverrideConfigurationChanges =
-                mFullConfiguration.updateFrom(mResolvedOverrideConfiguration);
+        mFullConfiguration.updateFrom(mResolvedOverrideConfiguration);
         if (!mResolvedTmpConfig.equals(mResolvedOverrideConfiguration)) {
             onMergedOverrideConfigurationChanged();
             // This depends on the assumption that change-listeners don't do
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 983f873..bc8e718 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -58,6 +58,7 @@
 import static android.view.WindowManager.LayoutParams.NEEDS_MENU_SET_TRUE;
 import static android.view.WindowManager.LayoutParams.NEEDS_MENU_UNSET;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
+import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_BOOT_PROGRESS;
@@ -93,6 +94,7 @@
 import static com.android.server.wm.DisplayContentProto.ID;
 import static com.android.server.wm.DisplayContentProto.IME_WINDOWS;
 import static com.android.server.wm.DisplayContentProto.OPENING_APPS;
+import static com.android.server.wm.DisplayContentProto.OVERLAY_WINDOWS;
 import static com.android.server.wm.DisplayContentProto.PINNED_STACK_CONTROLLER;
 import static com.android.server.wm.DisplayContentProto.ROTATION;
 import static com.android.server.wm.DisplayContentProto.SCREEN_ROTATION_ANIMATION;
@@ -241,7 +243,21 @@
     /** Unique identifier of this display. */
     private final int mDisplayId;
 
-    /** The containers below are the only child containers the display can have. */
+    /**
+     * Most surfaces will be a child of this window. There are some special layers and windows
+     * which are always on top of others and omitted from Screen-Magnification, for example the
+     * strict mode flash or the magnification overlay itself. Those layers will be children of
+     * {@link #mOverlayContainers} where mWindowContainers contains everything else.
+     */
+    private final WindowContainers mWindowContainers =
+            new WindowContainers("mWindowContainers", mWmService);
+
+    // Contains some special windows which are always on top of others and omitted from
+    // Screen-Magnification, for example the WindowMagnification windows.
+    private final NonAppWindowContainers mOverlayContainers =
+            new NonAppWindowContainers("mOverlayContainers", mWmService);
+
+    /** The containers below are the only child containers {@link #mWindowContainers} can have. */
     // Contains all window containers that are related to apps (Activities)
     private final TaskStackContainers mTaskStackContainers = new TaskStackContainers(mWmService);
     // Contains all non-app window containers that should be displayed above the app containers
@@ -261,7 +277,6 @@
 
     private WindowState mTmpWindow;
     private WindowState mTmpWindow2;
-    private boolean mTmpRecoveringMemory;
     private boolean mUpdateImeTarget;
     private boolean mTmpInitial;
     private int mMaxUiWidth;
@@ -347,6 +362,9 @@
     /** The desired scaling factor for compatible apps. */
     float mCompatibleScreenScale;
 
+    /** @see #getCurrentOverrideConfigurationChanges */
+    private int mCurrentOverrideConfigurationChanges;
+
     /**
      * Orientation forced by some window. If there is no visible window that specifies orientation
      * it is set to {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_UNSPECIFIED}.
@@ -491,20 +509,6 @@
     private ScreenRotationAnimation mScreenRotationAnimation;
 
     /**
-     * We organize all top-level Surfaces in to the following layers.
-     * mOverlayLayer contains a few Surfaces which are always on top of others
-     * and omitted from Screen-Magnification, for example the strict mode flash or
-     * the magnification overlay itself.
-     * {@link #mWindowingLayer} contains everything else.
-     */
-    private SurfaceControl mOverlayLayer;
-
-    /**
-     * See {@link #mOverlayLayer}
-     */
-    private SurfaceControl mWindowingLayer;
-
-    /**
      * Sequence number for the current layout pass.
      */
     int mLayoutSeq = 0;
@@ -911,24 +915,19 @@
                 .setOpaque(true)
                 .setContainerLayer();
         mSurfaceControl = b.setName("Root").setContainerLayer().build();
-        mWindowingLayer = b.setName("Display Windows").setParent(mSurfaceControl).build();
-        mOverlayLayer = b.setName("Display Overlays").setParent(mSurfaceControl).build();
 
         getPendingTransaction()
                 .setLayer(mSurfaceControl, 0)
                 .setLayerStack(mSurfaceControl, mDisplayId)
-                .show(mSurfaceControl)
-                .setLayer(mWindowingLayer, 0)
-                .show(mWindowingLayer)
-                .setLayer(mOverlayLayer, 1)
-                .show(mOverlayLayer);
+                .show(mSurfaceControl);
         getPendingTransaction().apply();
 
         // These are the only direct children we should ever have and they are permanent.
-        super.addChild(mBelowAppWindowsContainers, null);
-        super.addChild(mTaskStackContainers, null);
-        super.addChild(mAboveAppWindowsContainers, null);
-        super.addChild(mImeWindowsContainers, null);
+        super.addChild(mWindowContainers, null);
+        super.addChild(mOverlayContainers, null);
+
+        mWindowContainers.addChildren();
+
         // Sets the display content for the children.
         onDisplayChanged(this);
 
@@ -1002,6 +1001,9 @@
                 case TYPE_INPUT_METHOD_DIALOG:
                     mImeWindowsContainers.addChild(token);
                     break;
+                case TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY:
+                    mOverlayContainers.addChild(token);
+                    break;
                 default:
                     mAboveAppWindowsContainers.addChild(token);
                     break;
@@ -1868,6 +1870,25 @@
         mTaskStackContainers.onStackWindowingModeChanged(stack);
     }
 
+    /**
+     * The value is only valid in the scope {@link #onRequestedOverrideConfigurationChanged} of the
+     * changing hierarchy and the {@link #onConfigurationChanged} of its children.
+     *
+     * @return The current changes ({@link android.content.pm.ActivityInfo.Config}) of requested
+     *         override configuration.
+     */
+    int getCurrentOverrideConfigurationChanges() {
+        return mCurrentOverrideConfigurationChanges;
+    }
+
+    @Override
+    public void onRequestedOverrideConfigurationChanged(Configuration overrideConfiguration) {
+        mCurrentOverrideConfigurationChanges =
+                getRequestedOverrideConfiguration().diff(overrideConfiguration);
+        super.onRequestedOverrideConfigurationChanged(overrideConfiguration);
+        mCurrentOverrideConfigurationChanges = 0;
+    }
+
     @Override
     public void onConfigurationChanged(Configuration newParentConfig) {
         final int lastOrientation = getConfiguration().orientation;
@@ -1879,8 +1900,8 @@
         if (lastOrientation != getConfiguration().orientation) {
             getMetricsLogger().write(
                     new LogMaker(MetricsEvent.ACTION_PHONE_ORIENTATION_CHANGED)
-                    .setSubtype(getConfiguration().orientation)
-                    .addTaggedData(MetricsEvent.FIELD_DISPLAY_ID, getDisplayId()));
+                            .setSubtype(getConfiguration().orientation)
+                            .addTaggedData(MetricsEvent.FIELD_DISPLAY_ID, getDisplayId()));
         }
 
         // If there was no pinned stack, we still need to notify the controller of the display info
@@ -1937,49 +1958,6 @@
         setWindowingMode(windowingMode);
     }
 
-    /**
-     * In split-screen mode we process the IME containers above the docked divider
-     * rather than directly above their target.
-     */
-    private boolean skipTraverseChild(WindowContainer child) {
-        if (child == mImeWindowsContainers && mInputMethodTarget != null
-                && !hasSplitScreenPrimaryStack()) {
-            return true;
-        }
-        return false;
-    }
-
-    @Override
-    boolean forAllWindows(ToBooleanFunction<WindowState> callback, boolean traverseTopToBottom) {
-        // Special handling so we can process IME windows with #forAllImeWindows above their IME
-        // target, or here in order if there isn't an IME target.
-        if (traverseTopToBottom) {
-            for (int i = mChildren.size() - 1; i >= 0; --i) {
-                final DisplayChildWindowContainer child = mChildren.get(i);
-                if (skipTraverseChild(child)) {
-                    continue;
-                }
-
-                if (child.forAllWindows(callback, traverseTopToBottom)) {
-                    return true;
-                }
-            }
-        } else {
-            final int count = mChildren.size();
-            for (int i = 0; i < count; i++) {
-                final DisplayChildWindowContainer child = mChildren.get(i);
-                if (skipTraverseChild(child)) {
-                    continue;
-                }
-
-                if (child.forAllWindows(callback, traverseTopToBottom)) {
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-
     boolean forAllImeWindows(ToBooleanFunction<WindowState> callback, boolean traverseTopToBottom) {
         return mImeWindowsContainers.forAllWindows(callback, traverseTopToBottom);
     }
@@ -2001,7 +1979,7 @@
             if (mLastWindowForcedOrientation != SCREEN_ORIENTATION_UNSPECIFIED) {
                 ProtoLog.v(WM_DEBUG_ORIENTATION,
                         "Display id=%d is frozen, return %d", mDisplayId,
-                                mLastWindowForcedOrientation);
+                        mLastWindowForcedOrientation);
                 // If the display is frozen, some activities may be in the middle of restarting, and
                 // thus have removed their old window. If the window has the flag to hide the lock
                 // screen, then the lock screen can re-appear and inflict its own orientation on us.
@@ -2015,7 +1993,7 @@
                 // momentarily unavailable due to activity relaunch.
                 ProtoLog.v(WM_DEBUG_ORIENTATION,
                         "Display id=%d is frozen while keyguard locked, return %d",
-                                mDisplayId, getLastOrientation());
+                        mDisplayId, getLastOrientation());
                 return getLastOrientation();
             }
         } else {
@@ -2269,7 +2247,7 @@
         forAllWindows(fn, true /* traverseTopToBottom */);
         fn.recycle();
         return FIRST_APPLICATION_WINDOW <= targetWindowType[0]
-                        && targetWindowType[0] <= LAST_APPLICATION_WINDOW;
+                && targetWindowType[0] <= LAST_APPLICATION_WINDOW;
     }
 
     /**
@@ -2418,8 +2396,6 @@
             mPointerEventDispatcher.dispose();
             setRotationAnimation(null);
             mWmService.mAnimator.removeDisplayLocked(mDisplayId);
-            mWindowingLayer.release();
-            mOverlayLayer.release();
             mInputMonitor.onDisplayRemoved();
             // TODO(display-merge): Remove cast
             mWmService.mDisplayNotificationController
@@ -2534,7 +2510,7 @@
         // the minimized docked stack bounds.
         final boolean dockMinimized = mDividerControllerLocked.isMinimizedDock()
                 || (topDockedTask != null && imeOnBottom && !dockedStack.isAdjustedForIme()
-                        && dockedStack.getBounds().height() < topDockedTask.getBounds().height());
+                && dockedStack.getBounds().height() < topDockedTask.getBounds().height());
 
         // The divider could be adjusted for IME position, or be thinner than usual,
         // or both. There are three possible cases:
@@ -2665,6 +2641,10 @@
             final WindowToken windowToken = mImeWindowsContainers.getChildAt(i);
             windowToken.dumpDebug(proto, IME_WINDOWS, logLevel);
         }
+        for (int i = mOverlayContainers.getChildCount() - 1; i >= 0; --i) {
+            final WindowToken windowToken = mOverlayContainers.getChildAt(i);
+            windowToken.dumpDebug(proto, OVERLAY_WINDOWS, logLevel);
+        }
         proto.write(DPI, mBaseDisplayDensity);
         mDisplayInfo.dumpDebug(proto, DISPLAY_INFO);
         proto.write(ROTATION, getRotation());
@@ -2695,31 +2675,31 @@
         pw.print(prefix); pw.print("Display: mDisplayId="); pw.println(mDisplayId);
         final String subPrefix = "  " + prefix;
         pw.print(subPrefix); pw.print("init="); pw.print(mInitialDisplayWidth); pw.print("x");
-            pw.print(mInitialDisplayHeight); pw.print(" "); pw.print(mInitialDisplayDensity);
-            pw.print("dpi");
-            if (mInitialDisplayWidth != mBaseDisplayWidth
-                    || mInitialDisplayHeight != mBaseDisplayHeight
-                    || mInitialDisplayDensity != mBaseDisplayDensity) {
-                pw.print(" base=");
-                pw.print(mBaseDisplayWidth); pw.print("x"); pw.print(mBaseDisplayHeight);
-                pw.print(" "); pw.print(mBaseDisplayDensity); pw.print("dpi");
-            }
-            if (mDisplayScalingDisabled) {
-                pw.println(" noscale");
-            }
-            pw.print(" cur=");
-            pw.print(mDisplayInfo.logicalWidth);
-            pw.print("x"); pw.print(mDisplayInfo.logicalHeight);
-            pw.print(" app=");
-            pw.print(mDisplayInfo.appWidth);
-            pw.print("x"); pw.print(mDisplayInfo.appHeight);
-            pw.print(" rng="); pw.print(mDisplayInfo.smallestNominalAppWidth);
-            pw.print("x"); pw.print(mDisplayInfo.smallestNominalAppHeight);
-            pw.print("-"); pw.print(mDisplayInfo.largestNominalAppWidth);
-            pw.print("x"); pw.println(mDisplayInfo.largestNominalAppHeight);
-            pw.print(subPrefix + "deferred=" + mDeferredRemoval
-                    + " mLayoutNeeded=" + mLayoutNeeded);
-            pw.println(" mTouchExcludeRegion=" + mTouchExcludeRegion);
+        pw.print(mInitialDisplayHeight); pw.print(" "); pw.print(mInitialDisplayDensity);
+        pw.print("dpi");
+        if (mInitialDisplayWidth != mBaseDisplayWidth
+                || mInitialDisplayHeight != mBaseDisplayHeight
+                || mInitialDisplayDensity != mBaseDisplayDensity) {
+            pw.print(" base=");
+            pw.print(mBaseDisplayWidth); pw.print("x"); pw.print(mBaseDisplayHeight);
+            pw.print(" "); pw.print(mBaseDisplayDensity); pw.print("dpi");
+        }
+        if (mDisplayScalingDisabled) {
+            pw.println(" noscale");
+        }
+        pw.print(" cur=");
+        pw.print(mDisplayInfo.logicalWidth);
+        pw.print("x"); pw.print(mDisplayInfo.logicalHeight);
+        pw.print(" app=");
+        pw.print(mDisplayInfo.appWidth);
+        pw.print("x"); pw.print(mDisplayInfo.appHeight);
+        pw.print(" rng="); pw.print(mDisplayInfo.smallestNominalAppWidth);
+        pw.print("x"); pw.print(mDisplayInfo.smallestNominalAppHeight);
+        pw.print("-"); pw.print(mDisplayInfo.largestNominalAppWidth);
+        pw.print("x"); pw.println(mDisplayInfo.largestNominalAppHeight);
+        pw.print(subPrefix + "deferred=" + mDeferredRemoval
+                + " mLayoutNeeded=" + mLayoutNeeded);
+        pw.println(" mTouchExcludeRegion=" + mTouchExcludeRegion);
 
         pw.println();
         pw.print(prefix); pw.print("mLayoutSeq="); pw.println(mLayoutSeq);
@@ -2876,7 +2856,7 @@
         }
         final WindowState win = getWindow(w ->
                 w.mAttrs.type == TYPE_TOAST && w.mOwnerUid == uid && !w.mPermanentlyHidden
-                && !w.mWindowRemovalAllowed);
+                        && !w.mWindowRemovalAllowed);
         return win == null;
     }
 
@@ -2956,7 +2936,7 @@
         }
 
         ProtoLog.v(WM_DEBUG_FOCUS_LIGHT, "Changing focus from %s to %s displayId=%d Callers=%s",
-                    mCurrentFocus, newFocus, getDisplayId(), Debug.getCallers(4));
+                mCurrentFocus, newFocus, getDisplayId(), Debug.getCallers(4));
         final WindowState oldFocus = mCurrentFocus;
         mCurrentFocus = newFocus;
         mLosingFocus.remove(newFocus);
@@ -3258,7 +3238,7 @@
         // target app together.
         final boolean shouldAttachToDisplay = (mMagnificationSpec != null);
         final SurfaceControl newParent =
-                shouldAttachToDisplay ? mWindowingLayer : computeImeParent();
+                shouldAttachToDisplay ? mWindowContainers.getSurfaceControl() : computeImeParent();
         if (newParent != null) {
             getPendingTransaction().reparent(mImeWindowsContainers.mSurfaceControl, newParent);
             scheduleAnimation();
@@ -3283,7 +3263,7 @@
         }
 
         // Otherwise, we just attach it to the display.
-        return mWindowingLayer;
+        return mWindowContainers.getSurfaceControl();
     }
 
     boolean getNeedsMenu(WindowState top, WindowManagerPolicy.WindowState bottom) {
@@ -3430,7 +3410,7 @@
         boolean wallpaperEnabled = mWmService.mContext.getResources().getBoolean(
                 com.android.internal.R.bool.config_enableWallpaperService)
                 && mWmService.mContext.getResources().getBoolean(
-                        com.android.internal.R.bool.config_checkWallpaperAtBoot)
+                com.android.internal.R.bool.config_checkWallpaperAtBoot)
                 && !mWmService.mOnlyCore;
 
         final boolean haveBootMsg = drawnWindowTypes.get(TYPE_BOOT_PROGRESS);
@@ -3621,13 +3601,11 @@
             }
             if (DEBUG_LAYOUT_REPEATS) surfacePlacer.debugLayoutRepeats(
                     "after finishPostLayoutPolicyLw", pendingLayoutChanges);
-                mInsetsStateController.onPostLayout();
+            mInsetsStateController.onPostLayout();
         } while (pendingLayoutChanges != 0);
 
         mTmpApplySurfaceChangesTransactionState.reset();
 
-        mTmpRecoveringMemory = recoveringMemory;
-
         Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "applyWindowSurfaceChanges");
         try {
             forAllWindows(mApplySurfaceChangesTransaction, true /* traverseTopToBottom */);
@@ -4376,7 +4354,7 @@
                 if (mHomeStack != null && mHomeStack.isVisible()
                         && mDividerControllerLocked.isMinimizedDock()
                         && !(mDividerControllerLocked.isHomeStackResizable()
-                            && mHomeStack.matchParentBounds())) {
+                        && mHomeStack.matchParentBounds())) {
                     final int orientation = mHomeStack.getOrientation();
                     if (orientation != SCREEN_ORIENTATION_UNSET) {
                         return orientation;
@@ -4389,14 +4367,14 @@
             if (orientation != SCREEN_ORIENTATION_UNSET
                     && orientation != SCREEN_ORIENTATION_BEHIND) {
                 ProtoLog.v(WM_DEBUG_ORIENTATION,
-                                "App is requesting an orientation, return %d for display id=%d",
-                                orientation, mDisplayId);
+                        "App is requesting an orientation, return %d for display id=%d",
+                        orientation, mDisplayId);
                 return orientation;
             }
 
             ProtoLog.v(WM_DEBUG_ORIENTATION,
-                            "No app is requesting an orientation, return %d for display id=%d",
-                            getLastOrientation(), mDisplayId);
+                    "No app is requesting an orientation, return %d for display id=%d",
+                    getLastOrientation(), mDisplayId);
             // The next app has not been requested to be visible, so we keep the current orientation
             // to prevent freezing/unfreezing the display too early.
             return getLastOrientation();
@@ -4565,7 +4543,7 @@
                         wt.windowType, wt.mOwnerCanManageAppTokens);
 
                 if (needAssignIme && layer >= mWmService.mPolicy.getWindowLayerFromTypeLw(
-                                TYPE_INPUT_METHOD_DIALOG, true)) {
+                        TYPE_INPUT_METHOD_DIALOG, true)) {
                     imeContainer.assignRelativeLayer(t, wt.getSurfaceControl(), -1);
                     needAssignIme = false;
                 }
@@ -4576,6 +4554,126 @@
         }
     }
 
+    private class WindowContainers extends DisplayChildWindowContainer<WindowContainer> {
+        private final String mName;
+
+        WindowContainers(String name, WindowManagerService service) {
+            super(service);
+            mName = name;
+        }
+
+        @Override
+        void assignChildLayers(SurfaceControl.Transaction t) {
+            mBelowAppWindowsContainers.assignLayer(t, 0);
+            mTaskStackContainers.assignLayer(t, 1);
+            mAboveAppWindowsContainers.assignLayer(t, 2);
+
+            final WindowState imeTarget = mInputMethodTarget;
+            boolean needAssignIme = true;
+
+            // In the case where we have an IME target that is not in split-screen mode IME
+            // assignment is easy. We just need the IME to go directly above the target. This way
+            // children of the target will naturally go above the IME and everyone is happy.
+            //
+            // In the case of split-screen windowing mode, we need to elevate the IME above the
+            // docked divider while keeping the app itself below the docked divider, so instead
+            // we use relative layering of the IME targets child windows, and place the IME in
+            // the non-app layer (see {@link AboveAppWindowContainers#assignChildLayers}).
+            //
+            // In the case the IME target is animating, the animation Z order may be different
+            // than the WindowContainer Z order, so it's difficult to be sure we have the correct
+            // IME target. In this case we just layer the IME over all transitions by placing it
+            // in the above applications layer.
+            //
+            // In the case where we have no IME target we assign it where its base layer would
+            // place it in the AboveAppWindowContainers.
+            //
+            // Keep IME window in mAboveAppWindowsContainers as long as app's starting window
+            // exists so it get's layered above the starting window.
+            if (imeTarget != null && !(imeTarget.mActivityRecord != null
+                    && imeTarget.mActivityRecord.hasStartingWindow()) && (
+                    !(imeTarget.inSplitScreenWindowingMode()
+                            || imeTarget.mToken.isAppTransitioning()) && (
+                            imeTarget.getSurfaceControl() != null))) {
+                mImeWindowsContainers.assignRelativeLayer(t, imeTarget.getSurfaceControl(),
+                        // TODO: We need to use an extra level on the app surface to ensure
+                        // this is always above SurfaceView but always below attached window.
+                        1);
+                needAssignIme = false;
+            }
+
+            // Above we have assigned layers to our children, now we ask them to assign
+            // layers to their children.
+            mBelowAppWindowsContainers.assignChildLayers(t);
+            mTaskStackContainers.assignChildLayers(t);
+            mAboveAppWindowsContainers.assignChildLayers(t,
+                    needAssignIme ? mImeWindowsContainers : null);
+            mImeWindowsContainers.assignChildLayers(t);
+        }
+
+        @Override
+        String getName() {
+            return mName;
+        }
+
+        void addChildren() {
+            addChild(mBelowAppWindowsContainers, null);
+            addChild(mTaskStackContainers, null);
+            addChild(mAboveAppWindowsContainers, null);
+            addChild(mImeWindowsContainers, null);
+        }
+
+        /**
+         * In split-screen mode we process the IME containers above the docked divider
+         * rather than directly above their target.
+         */
+        private boolean skipTraverseChild(WindowContainer child) {
+            return child == mImeWindowsContainers && mInputMethodTarget != null
+                    && !hasSplitScreenPrimaryStack();
+        }
+
+        @Override
+        boolean forAllWindows(ToBooleanFunction<WindowState> callback,
+                boolean traverseTopToBottom) {
+            // Special handling so we can process IME windows with #forAllImeWindows above their IME
+            // target, or here in order if there isn't an IME target.
+            if (traverseTopToBottom) {
+                for (int i = mChildren.size() - 1; i >= 0; --i) {
+                    final WindowContainer child = mChildren.get(i);
+                    if (skipTraverseChild(child)) {
+                        continue;
+                    }
+
+                    if (child.forAllWindows(callback, traverseTopToBottom)) {
+                        return true;
+                    }
+                }
+            } else {
+                final int count = mChildren.size();
+                for (int i = 0; i < count; i++) {
+                    Slog.d(TAG, "child " + mChildren.get(i));
+                    final WindowContainer child = mChildren.get(i);
+                    if (skipTraverseChild(child)) {
+                        Slog.d(TAG, "child skipped");
+                        continue;
+                    }
+
+                    if (child.forAllWindows(callback, traverseTopToBottom)) {
+                        return true;
+                    }
+                }
+            }
+            return false;
+        }
+
+        @Override
+        void positionChildAt(int position, WindowContainer child, boolean includingParents) {
+            // Children of the WindowContainers are statically ordered, so the real intention here
+            // is to perform the operation on the display and not the static direct children.
+            getParent().positionChildAt(position, this, includingParents);
+        }
+    }
+
     /**
      * Window container class that contains all containers on this display that are not related to
      * Apps. E.g. status bar.
@@ -4589,7 +4687,7 @@
                 // Tokens with higher base layer are z-ordered on-top.
                 mWmService.mPolicy.getWindowLayerFromTypeLw(token1.windowType,
                         token1.mOwnerCanManageAppTokens)
-                < mWmService.mPolicy.getWindowLayerFromTypeLw(token2.windowType,
+                        < mWmService.mPolicy.getWindowLayerFromTypeLw(token2.windowType,
                         token2.mOwnerCanManageAppTokens) ? -1 : 1;
 
         private final Predicate<WindowState> mGetOrientingWindow = w -> {
@@ -4641,7 +4739,7 @@
                 }
                 ProtoLog.v(WM_DEBUG_ORIENTATION,
                         "%s forcing orientation to %d for display id=%d", win, req,
-                                mDisplayId);
+                        mDisplayId);
                 return (mLastWindowForcedOrientation = req);
             }
 
@@ -4681,11 +4779,6 @@
         }
     }
 
-    SurfaceControl.Builder makeSurface(SurfaceSession s) {
-        return mWmService.makeSurfaceBuilder(s)
-                .setParent(mWindowingLayer);
-    }
-
     @Override
     SurfaceSession getSession() {
         return mSession;
@@ -4700,7 +4793,7 @@
         }
 
         return b.setName(child.getName())
-                .setParent(mWindowingLayer);
+                .setParent(mSurfaceControl);
     }
 
     /**
@@ -4711,14 +4804,14 @@
      */
     SurfaceControl.Builder makeOverlay() {
         return mWmService.makeSurfaceBuilder(mSession)
-            .setParent(mOverlayLayer);
+                .setParent(mOverlayContainers.getSurfaceControl());
     }
 
     /**
-     * Reparents the given surface to mOverlayLayer.
+     * Reparents the given surface to {@link #mOverlayContainers}' SurfaceControl.
      */
     void reparentToOverlay(Transaction transaction, SurfaceControl surface) {
-        transaction.reparent(surface, mOverlayLayer);
+        transaction.reparent(surface, mOverlayContainers.getSurfaceControl());
     }
 
     void applyMagnificationSpec(MagnificationSpec spec) {
@@ -4750,54 +4843,11 @@
 
     @Override
     void assignChildLayers(SurfaceControl.Transaction t) {
+        mWindowContainers.assignLayer(t, 0);
+        mOverlayContainers.assignLayer(t, 1);
 
-        // These are layers as children of "mWindowingLayer"
-        mBelowAppWindowsContainers.assignLayer(t, 0);
-        mTaskStackContainers.assignLayer(t, 1);
-        mAboveAppWindowsContainers.assignLayer(t, 2);
-
-        final WindowState imeTarget = mInputMethodTarget;
-        boolean needAssignIme = true;
-
-        // In the case where we have an IME target that is not in split-screen
-        // mode IME assignment is easy. We just need the IME to go directly above
-        // the target. This way children of the target will naturally go above the IME
-        // and everyone is happy.
-        //
-        // In the case of split-screen windowing mode, we need to elevate the IME above the
-        // docked divider while keeping the app itself below the docked divider, so instead
-        // we use relative layering of the IME targets child windows, and place the
-        // IME in the non-app layer (see {@link AboveAppWindowContainers#assignChildLayers}).
-        //
-        // In the case the IME target is animating, the animation Z order may be different
-        // than the WindowContainer Z order, so it's difficult to be sure we have the correct
-        // IME target. In this case we just layer the IME over all transitions by placing it in the
-        // above applications layer.
-        //
-        // In the case where we have no IME target we assign it where it's base layer would
-        // place it in the AboveAppWindowContainers.
-        //
-        // Keep IME window in mAboveAppWindowsContainers as long as app's starting window exists
-        // so it get's layered above the starting window.
-        if (imeTarget != null
-                && !(imeTarget.mActivityRecord != null && imeTarget.mActivityRecord.hasStartingWindow())
-                && (!(imeTarget.inSplitScreenWindowingMode()
-                             || imeTarget.mToken.isAppTransitioning())
-                && (imeTarget.getSurfaceControl() != null))) {
-            mImeWindowsContainers.assignRelativeLayer(t, imeTarget.getSurfaceControl(),
-                    // TODO: We need to use an extra level on the app surface to ensure
-                    // this is always above SurfaceView but always below attached window.
-                    1);
-            needAssignIme = false;
-        }
-
-        // Above we have assigned layers to our children, now we ask them to assign
-        // layers to their children.
-        mBelowAppWindowsContainers.assignChildLayers(t);
-        mTaskStackContainers.assignChildLayers(t);
-        mAboveAppWindowsContainers.assignChildLayers(t,
-                needAssignIme == true ? mImeWindowsContainers : null);
-        mImeWindowsContainers.assignChildLayers(t);
+        mWindowContainers.assignChildLayers(t);
+        mOverlayContainers.assignChildLayers(t);
     }
 
     /**
@@ -4899,7 +4949,7 @@
         if (mAppTransition.isTransitionSet()) {
             ProtoLog.w(WM_DEBUG_APP_TRANSITIONS,
                     "Execute app transition: %s, displayId: %d Callers=%s",
-                        mAppTransition, mDisplayId, Debug.getCallers(5));
+                    mAppTransition, mDisplayId, Debug.getCallers(5));
             mAppTransition.setReady();
             mWmService.mWindowPlacerLocked.requestTraversal();
         }
@@ -4964,8 +5014,8 @@
     }
 
     /**
-     * Re-parent the DisplayContent's top surfaces, {@link #mWindowingLayer} and
-     * {@link #mOverlayLayer} to the specified SurfaceControl.
+     * Re-parent the DisplayContent's top surface, {@link #mSurfaceControl} to the specified
+     * SurfaceControl.
      *
      * @param win The window which owns the SurfaceControl. This indicates the z-order of the
      *            windows of this display against the windows on the parent display.
@@ -5043,11 +5093,11 @@
 
     @VisibleForTesting
     SurfaceControl getWindowingLayer() {
-        return mWindowingLayer;
+        return mWindowContainers.getSurfaceControl();
     }
 
     SurfaceControl getOverlayLayer() {
-        return mOverlayLayer;
+        return mOverlayContainers.getSurfaceControl();
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
index 1a7d214..399c5d3 100644
--- a/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
+++ b/services/core/java/com/android/server/wm/ScreenRotationAnimation.java
@@ -172,7 +172,7 @@
                     .setContainerLayer()
                     .build();
 
-            mSurfaceControl = displayContent.makeSurface(null)
+            mSurfaceControl = mService.makeSurfaceBuilder(null)
                     .setName("ScreenshotSurface")
                     .setParent(mRotationLayer)
                     .setBufferSize(mWidth, mHeight)
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 95eb4dd..f45eb50 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -16,7 +16,6 @@
 
 package com.android.server.wm;
 
-import static android.Manifest.permission.ACCESS_SURFACE_FLINGER;
 import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS;
 import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
 import static android.Manifest.permission.MANAGE_ACTIVITY_STACKS;
@@ -7815,8 +7814,8 @@
 
     @Override
     public boolean mirrorDisplay(int displayId, SurfaceControl outSurfaceControl) {
-        if (!checkCallingPermission(ACCESS_SURFACE_FLINGER, "mirrorDisplay()")) {
-            throw new SecurityException("Requires ACCESS_SURFACE_FLINGER permission");
+        if (!checkCallingPermission(READ_FRAME_BUFFER, "mirrorDisplay()")) {
+            throw new SecurityException("Requires READ_FRAME_BUFFER permission");
         }
 
         final SurfaceControl displaySc;
@@ -7827,7 +7826,7 @@
                 return false;
             }
 
-            displaySc = displayContent.getSurfaceControl();
+            displaySc = displayContent.getWindowingLayer();
         }
 
         final SurfaceControl mirror = SurfaceControl.mirrorSurface(displaySc);
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 21cacd45..b6e501a 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -109,6 +109,7 @@
 import com.android.server.inputmethod.InputMethodManagerService;
 import com.android.server.inputmethod.InputMethodSystemProperty;
 import com.android.server.inputmethod.MultiClientInputMethodManagerService;
+import com.android.server.integrity.AppIntegrityManagerService;
 import com.android.server.lights.LightsService;
 import com.android.server.media.MediaResourceMonitorService;
 import com.android.server.media.MediaRouterService;
@@ -1129,6 +1130,10 @@
             SignedConfigService.registerUpdateReceiver(mSystemContext);
             t.traceEnd();
 
+            t.traceBegin("AppIntegrityService");
+            mSystemServiceManager.startService(AppIntegrityManagerService.class);
+            t.traceEnd();
+
         } catch (Throwable e) {
             Slog.e("System", "******************************************");
             Slog.e("System", "************ Failure starting core service");
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
index 3518dc5..05b655a 100644
--- a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
@@ -120,11 +120,14 @@
         waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
 
         // This should be public
-        assertDisplay(mListener.addedDisplays.get(0).getDisplayDeviceInfoLocked(), PORT_A, false);
-        // This should be private
-        assertDisplay(mListener.addedDisplays.get(1).getDisplayDeviceInfoLocked(), PORT_B, true);
+        assertDisplayPrivateFlag(mListener.addedDisplays.get(0).getDisplayDeviceInfoLocked(),
+                PORT_A, false);
         // This should be public
-        assertDisplay(mListener.addedDisplays.get(2).getDisplayDeviceInfoLocked(), PORT_C, false);
+        assertDisplayPrivateFlag(mListener.addedDisplays.get(1).getDisplayDeviceInfoLocked(),
+                PORT_B, true);
+        // This should be public
+        assertDisplayPrivateFlag(mListener.addedDisplays.get(2).getDisplayDeviceInfoLocked(),
+                PORT_C, false);
     }
 
     /**
@@ -141,12 +144,14 @@
         waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
 
         // This should be public
-        assertDisplay(mListener.addedDisplays.get(0).getDisplayDeviceInfoLocked(), PORT_A, false);
+        assertDisplayPrivateFlag(mListener.addedDisplays.get(0).getDisplayDeviceInfoLocked(),
+                PORT_A, false);
         // This should be public
-        assertDisplay(mListener.addedDisplays.get(1).getDisplayDeviceInfoLocked(), PORT_C, false);
+        assertDisplayPrivateFlag(mListener.addedDisplays.get(1).getDisplayDeviceInfoLocked(),
+                PORT_C, false);
     }
 
-    private static void assertDisplay(
+    private static void assertDisplayPrivateFlag(
             DisplayDeviceInfo info, int expectedPort, boolean shouldBePrivate) {
         final DisplayAddress.Physical address = (DisplayAddress.Physical) info.address;
         assertNotNull(address);
@@ -155,6 +160,39 @@
         assertEquals(shouldBePrivate, (info.flags & DisplayDeviceInfo.FLAG_PRIVATE) != 0);
     }
 
+    /**
+     * Confirm that external display uses physical density.
+     */
+    @Test
+    public void testDpiValues() throws Exception {
+        // needs default one always
+        setUpDisplay(new DisplayConfig(createDisplayAddress(PORT_A), createDummyDisplayInfo()));
+        setUpDisplay(new DisplayConfig(createDisplayAddress(PORT_B), createDummyDisplayInfo()));
+        updateAvailableDisplays();
+        mAdapter.registerLocked();
+
+        waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS);
+
+        assertDisplayDpi(
+                mListener.addedDisplays.get(0).getDisplayDeviceInfoLocked(), PORT_A, 100, 100,
+                16000);
+        assertDisplayDpi(
+                mListener.addedDisplays.get(1).getDisplayDeviceInfoLocked(), PORT_B, 100, 100,
+                16000);
+    }
+
+    private void assertDisplayDpi(DisplayDeviceInfo info, int expectedPort,
+                                  float expectedXdpi,
+                                  float expectedYDpi,
+                                  int expectedDensityDpi) {
+        final DisplayAddress.Physical physical = (DisplayAddress.Physical) info.address;
+        assertNotNull(physical);
+        assertEquals((byte) expectedPort, physical.getPort());
+        assertEquals(expectedXdpi, info.xDpi, 0.01);
+        assertEquals(expectedYDpi, info.yDpi, 0.01);
+        assertEquals(expectedDensityDpi, info.densityDpi);
+    }
+
     private class DisplayConfig {
         public final DisplayAddress.Physical address;
         public final IBinder displayToken = new Binder();
diff --git a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
new file mode 100644
index 0000000..f6c4d3a
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyFloat;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.hardware.Sensor;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
+import android.os.Handler;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class AutomaticBrightnessControllerTest {
+
+    private static final int BRIGHTNESS_MIN = 1;
+    private static final int BRIGHTNESS_MAX = 255;
+    private static final int LIGHT_SENSOR_RATE = 20;
+    private static final int INITIAL_LIGHT_SENSOR_RATE = 20;
+    private static final int BRIGHTENING_LIGHT_DEBOUNCE_CONFIG = 0;
+    private static final int DARKENING_LIGHT_DEBOUNCE_CONFIG = 0;
+    private static final int SHORT_TERM_MODEL_TIMEOUT = 0;
+    private static final float DOZE_SCALE_FACTOR = 0.0f;
+    private static final boolean RESET_AMBIENT_LUX_AFTER_WARMUP_CONFIG = false;
+
+    private Context mContext;
+    @Mock SensorManager mSensorManager;
+    @Mock BrightnessMappingStrategy mBrightnessMappingStrategy;
+    @Mock HysteresisLevels mAmbientBrightnessThresholds;
+    @Mock HysteresisLevels mScreenBrightnessThresholds;
+    @Mock PackageManager mPackageManager;
+    @Mock Handler mNoopHandler;
+
+    private static final int LIGHT_SENSOR_WARMUP_TIME = 0;
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mContext = InstrumentationRegistry.getContext();
+    }
+
+    private AutomaticBrightnessController setupController(Sensor lightSensor) {
+        AutomaticBrightnessController controller = new AutomaticBrightnessController(
+                new AutomaticBrightnessController.Injector() {
+                    @Override
+                    public Handler getBackgroundThreadHandler() {
+                        return mNoopHandler;
+                    }
+                },
+                () -> { }, mContext.getMainLooper(), mSensorManager, lightSensor,
+                mBrightnessMappingStrategy, LIGHT_SENSOR_WARMUP_TIME, BRIGHTNESS_MIN,
+                BRIGHTNESS_MAX, DOZE_SCALE_FACTOR, LIGHT_SENSOR_RATE, INITIAL_LIGHT_SENSOR_RATE,
+                BRIGHTENING_LIGHT_DEBOUNCE_CONFIG, DARKENING_LIGHT_DEBOUNCE_CONFIG,
+                RESET_AMBIENT_LUX_AFTER_WARMUP_CONFIG, mAmbientBrightnessThresholds,
+                mScreenBrightnessThresholds, SHORT_TERM_MODEL_TIMEOUT, mPackageManager);
+        controller.setLoggingEnabled(true);
+
+        // Configure the brightness controller and grab an instance of the sensor listener,
+        // through which we can deliver fake (for test) sensor values.
+        controller.configure(true /* enable */, null /* configuration */,
+                0 /* brightness */, false /* userChangedBrightness */, 0 /* adjustment */,
+                false /* userChanged */, DisplayPowerRequest.POLICY_BRIGHT);
+
+        return controller;
+    }
+
+    @Test
+    public void testNoHysteresisAtMinBrightness() throws Exception {
+        Sensor lightSensor = TestUtils.createSensor(Sensor.TYPE_LIGHT, "Light Sensor");
+        AutomaticBrightnessController controller = setupController(lightSensor);
+
+        ArgumentCaptor<SensorEventListener> listenerCaptor =
+                ArgumentCaptor.forClass(SensorEventListener.class);
+        verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(lightSensor),
+                eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
+        SensorEventListener listener = listenerCaptor.getValue();
+
+        // Set up system to return 5 as a brightness value
+        float lux1 = 100.0f;
+        float normalizedBrightness1 = 0.02f;
+        when(mAmbientBrightnessThresholds.getBrighteningThreshold(lux1))
+                .thenReturn(lux1);
+        when(mAmbientBrightnessThresholds.getDarkeningThreshold(lux1))
+                .thenReturn(lux1);
+        when(mBrightnessMappingStrategy.getBrightness(eq(lux1), eq(null), anyInt()))
+                .thenReturn(normalizedBrightness1);
+
+        // This is the important bit: When the new brightness is set, make sure the new
+        // brightening threshold is beyond the maximum brightness value...so that we can test that
+        // our threshold clamping works.
+        when(mScreenBrightnessThresholds.getBrighteningThreshold(5)).thenReturn(1.0f);
+
+        // Send new sensor value and verify
+        listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, (int) lux1));
+        assertEquals(5, controller.getAutomaticScreenBrightness());
+
+
+        // Set up system to return 255 as a brightness value
+        float lux2 = 10.0f;
+        float normalizedBrightness2 = 0.0f;
+        when(mAmbientBrightnessThresholds.getBrighteningThreshold(lux2))
+                .thenReturn(lux2);
+        when(mAmbientBrightnessThresholds.getDarkeningThreshold(lux2))
+                .thenReturn(lux2);
+        when(mBrightnessMappingStrategy.getBrightness(anyFloat(), eq(null), anyInt()))
+                .thenReturn(normalizedBrightness2);
+
+        // Send new sensor value and verify
+        listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, (int) lux2));
+        assertEquals(1, controller.getAutomaticScreenBrightness());
+    }
+
+    @Test
+    public void testNoHysteresisAtMaxBrightness() throws Exception {
+        Sensor lightSensor = TestUtils.createSensor(Sensor.TYPE_LIGHT, "Light Sensor");
+        AutomaticBrightnessController controller = setupController(lightSensor);
+
+        ArgumentCaptor<SensorEventListener> listenerCaptor =
+                ArgumentCaptor.forClass(SensorEventListener.class);
+        verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(lightSensor),
+                eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
+        SensorEventListener listener = listenerCaptor.getValue();
+
+        // Set up system to return 250 as a brightness value
+        float lux1 = 100.0f;
+        float normalizedBrightness1 = 0.98f;
+        when(mAmbientBrightnessThresholds.getBrighteningThreshold(lux1))
+                .thenReturn(lux1);
+        when(mAmbientBrightnessThresholds.getDarkeningThreshold(lux1))
+                .thenReturn(lux1);
+        when(mBrightnessMappingStrategy.getBrightness(eq(lux1), eq(null), anyInt()))
+                .thenReturn(normalizedBrightness1);
+
+        // This is the important bit: When the new brightness is set, make sure the new
+        // brightening threshold is beyond the maximum brightness value...so that we can test that
+        // our threshold clamping works.
+        when(mScreenBrightnessThresholds.getBrighteningThreshold(250)).thenReturn(260.0f);
+
+        // Send new sensor value and verify
+        listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, (int) lux1));
+        assertEquals(250, controller.getAutomaticScreenBrightness());
+
+
+        // Set up system to return 255 as a brightness value
+        float lux2 = 110.0f;
+        float normalizedBrightness2 = 1.0f;
+        when(mAmbientBrightnessThresholds.getBrighteningThreshold(lux2))
+                .thenReturn(lux2);
+        when(mAmbientBrightnessThresholds.getDarkeningThreshold(lux2))
+                .thenReturn(lux2);
+        when(mBrightnessMappingStrategy.getBrightness(anyFloat(), eq(null), anyInt()))
+                .thenReturn(normalizedBrightness2);
+
+        // Send new sensor value and verify
+        listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, (int) lux2));
+        assertEquals(255, controller.getAutomaticScreenBrightness());
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
index 4742a73..8d5939a 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -11,7 +11,7 @@
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
- * limitations under the License
+ * limitations under the License.
  */
 
 package com.android.server.display;
diff --git a/services/tests/servicestests/src/com/android/server/display/TestUtils.java b/services/tests/servicestests/src/com/android/server/display/TestUtils.java
new file mode 100644
index 0000000..859dfe3
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/display/TestUtils.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display;
+
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.os.SystemClock;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+public final class TestUtils {
+
+    public static SensorEvent createSensorEvent(Sensor sensor, int lux) throws Exception {
+        final Constructor<SensorEvent> constructor =
+                SensorEvent.class.getDeclaredConstructor(int.class);
+        constructor.setAccessible(true);
+        final SensorEvent event = constructor.newInstance(1);
+        event.sensor = sensor;
+        event.values[0] = lux;
+        event.timestamp = SystemClock.elapsedRealtimeNanos();
+        return event;
+    }
+
+
+    public static void setSensorType(Sensor sensor, int type, String strType) throws Exception {
+        Method setter = Sensor.class.getDeclaredMethod("setType", Integer.TYPE);
+        setter.setAccessible(true);
+        setter.invoke(sensor, type);
+        if (strType != null) {
+            Field f = sensor.getClass().getDeclaredField("mStringType");
+            f.setAccessible(true);
+            f.set(sensor, strType);
+        }
+    }
+
+    public static Sensor createSensor(int type, String strType) throws Exception {
+        Constructor<Sensor> constr = Sensor.class.getDeclaredConstructor();
+        constr.setAccessible(true);
+        Sensor sensor = constr.newInstance();
+        setSensorType(sensor, type, strType);
+        return sensor;
+    }
+
+}
diff --git a/services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientLuxTest.java b/services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientLuxTest.java
index acf2d0e..2565ae3 100644
--- a/services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientLuxTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientLuxTest.java
@@ -16,31 +16,19 @@
 
 package com.android.server.display.whitebalance;
 
-import com.android.internal.R;
-import com.android.server.display.utils.AmbientFilter;
-import com.android.server.display.utils.AmbientFilterStubber;
-import com.google.common.collect.ImmutableList;
-
 import static org.junit.Assert.assertEquals;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.when;
-import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyLong;
 import static org.mockito.Matchers.eq;
-import org.mockito.stubbing.Answer;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.mockito.Spy;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
 
 import android.content.ContextWrapper;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.hardware.Sensor;
-import android.hardware.SensorEvent;
-import android.hardware.SensorEventListener;
 import android.hardware.SensorManager;
 import android.os.Handler;
 import android.os.Looper;
@@ -48,15 +36,22 @@
 
 import androidx.test.InstrumentationRegistry;
 
+import com.android.internal.R;
+import com.android.server.display.TestUtils;
+import com.android.server.display.utils.AmbientFilter;
+import com.android.server.display.utils.AmbientFilterStubber;
+
+import com.google.common.collect.ImmutableList;
+
 import org.junit.Before;
-import org.junit.After;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
 
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Field;
-import java.lang.reflect.Method;
 import java.util.List;
 
 @RunWith(JUnit4.class)
@@ -84,8 +79,8 @@
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
-        mLightSensor = createSensor(Sensor.TYPE_LIGHT, null);
-        mAmbientColorSensor = createSensor(AMBIENT_COLOR_TYPE, AMBIENT_COLOR_TYPE_STR);
+        mLightSensor = TestUtils.createSensor(Sensor.TYPE_LIGHT, null);
+        mAmbientColorSensor = TestUtils.createSensor(AMBIENT_COLOR_TYPE, AMBIENT_COLOR_TYPE_STR);
         mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getContext()));
         mResourcesSpy = spy(mContextSpy.getResources());
         when(mContextSpy.getResources()).thenReturn(mResourcesSpy);
@@ -482,25 +477,6 @@
         }
     }
 
-    private void setSensorType(Sensor sensor, int type, String strType) throws Exception {
-        Method setter = Sensor.class.getDeclaredMethod("setType", Integer.TYPE);
-        setter.setAccessible(true);
-        setter.invoke(sensor, type);
-        if (strType != null) {
-            Field f = sensor.getClass().getDeclaredField("mStringType");
-            f.setAccessible(true);
-            f.set(sensor, strType);
-        }
-    }
-
-    private Sensor createSensor(int type, String strType) throws Exception {
-        Constructor<Sensor> constr = Sensor.class.getDeclaredConstructor();
-        constr.setAccessible(true);
-        Sensor sensor = constr.newInstance();
-        setSensorType(sensor, type, strType);
-        return sensor;
-    }
-
     private TypedArray createTypedArray() throws Exception {
         TypedArray mockArray = mock(TypedArray.class);
         return mockArray;
diff --git a/services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientSensorTest.java b/services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientSensorTest.java
index 6ff4f3b..3e3e535 100644
--- a/services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientSensorTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientSensorTest.java
@@ -28,15 +28,14 @@
 import android.content.ContextWrapper;
 import android.content.res.Resources;
 import android.hardware.Sensor;
-import android.hardware.SensorEvent;
 import android.hardware.SensorEventListener;
 import android.hardware.SensorManager;
 import android.os.Handler;
 import android.os.Looper;
-import android.os.SystemClock;
 
 import androidx.test.InstrumentationRegistry;
 
+import com.android.server.display.TestUtils;
 import com.android.server.display.whitebalance.AmbientSensor.AmbientBrightnessSensor;
 import com.android.server.display.whitebalance.AmbientSensor.AmbientColorTemperatureSensor;
 
@@ -50,9 +49,6 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Field;
-import java.lang.reflect.Method;
 import java.util.List;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
@@ -73,8 +69,8 @@
     @Before
     public void setUp() throws Exception {
         MockitoAnnotations.initMocks(this);
-        mLightSensor = createSensor(Sensor.TYPE_LIGHT, null);
-        mAmbientColorSensor = createSensor(AMBIENT_COLOR_TYPE, AMBIENT_COLOR_TYPE_STR);
+        mLightSensor = TestUtils.createSensor(Sensor.TYPE_LIGHT, null);
+        mAmbientColorSensor = TestUtils.createSensor(AMBIENT_COLOR_TYPE, AMBIENT_COLOR_TYPE_STR);
         mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getContext()));
         mResourcesSpy = spy(mContextSpy.getResources());
         when(mContextSpy.getResources()).thenReturn(mResourcesSpy);
@@ -96,7 +92,7 @@
         // There should be no issues when we callback the listener, even if there is no callback
         // set.
         SensorEventListener listener = captor.getValue();
-        listener.onSensorChanged(createSensorEvent(mLightSensor, 100));
+        listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 100));
     }
 
     @Test
@@ -122,7 +118,7 @@
         verify(mSensorManagerMock).registerListener(captor.capture(), eq(mLightSensor),
                 anyInt(), eq(mHandler));
         SensorEventListener listener = captor.getValue();
-        listener.onSensorChanged(createSensorEvent(mLightSensor, luxValue));
+        listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, luxValue));
         assertTrue(changeSignal.await(5, TimeUnit.SECONDS));
         assertEquals(luxValue, luxReturned[0]);
     }
@@ -155,39 +151,8 @@
         verify(mSensorManagerMock).registerListener(captor.capture(), eq(mAmbientColorSensor),
                 anyInt(), eq(mHandler));
         SensorEventListener listener = captor.getValue();
-        listener.onSensorChanged(createSensorEvent(mAmbientColorSensor, colorTempValue));
+        listener.onSensorChanged(TestUtils.createSensorEvent(mAmbientColorSensor, colorTempValue));
         assertTrue(changeSignal.await(5, TimeUnit.SECONDS));
         assertEquals(colorTempValue, colorTempReturned[0]);
     }
-
-    private SensorEvent createSensorEvent(Sensor sensor, int lux) throws Exception {
-        final Constructor<SensorEvent> constructor =
-                SensorEvent.class.getDeclaredConstructor(int.class);
-        constructor.setAccessible(true);
-        final SensorEvent event = constructor.newInstance(1);
-        event.sensor = sensor;
-        event.values[0] = lux;
-        event.timestamp = SystemClock.elapsedRealtimeNanos();
-        return event;
-    }
-
-
-    private void setSensorType(Sensor sensor, int type, String strType) throws Exception {
-        Method setter = Sensor.class.getDeclaredMethod("setType", Integer.TYPE);
-        setter.setAccessible(true);
-        setter.invoke(sensor, type);
-        if (strType != null) {
-            Field f = sensor.getClass().getDeclaredField("mStringType");
-            f.setAccessible(true);
-            f.set(sensor, strType);
-        }
-    }
-
-    private Sensor createSensor(int type, String strType) throws Exception {
-        Constructor<Sensor> constr = Sensor.class.getDeclaredConstructor();
-        constr.setAccessible(true);
-        Sensor sensor = constr.newInstance();
-        setSensorType(sensor, type, strType);
-        return sensor;
-    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
new file mode 100644
index 0000000..37ff06a
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/integrity/AppIntegrityManagerServiceImplTest.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.integrity;
+
+import static android.content.pm.PackageManager.EXTRA_VERIFICATION_ID;
+
+import static org.mockito.Mockito.verify;
+
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.LocalServices;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+/** Unit test for {@link com.android.server.integrity.AppIntegrityManagerServiceImpl} */
+@RunWith(AndroidJUnit4.class)
+public class AppIntegrityManagerServiceImplTest {
+
+    @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
+
+    @Mock PackageManagerInternal mPackageManagerInternal;
+
+    // under test
+    private AppIntegrityManagerServiceImpl mService;
+
+    @Before
+    public void setup() {
+        LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternal);
+
+        mService = new AppIntegrityManagerServiceImpl(InstrumentationRegistry.getContext());
+    }
+
+    @Test
+    public void integrityVerification_allow() {
+        int verificationId = 2;
+        Intent integrityVerificationIntent = new Intent();
+        integrityVerificationIntent.setAction(Intent.ACTION_PACKAGE_NEEDS_INTEGRITY_VERIFICATION);
+        integrityVerificationIntent.putExtra(EXTRA_VERIFICATION_ID, verificationId);
+
+        // We cannot send the broadcast using the context since it is a protected broadcast and
+        // we will get a security exception.
+        mService.handleIntegrityVerification(integrityVerificationIntent);
+
+        verify(mPackageManagerInternal)
+                .setIntegrityVerificationResult(verificationId, PackageManager.VERIFICATION_ALLOW);
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index e086b28..14c09eb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -244,7 +244,6 @@
         // Add stack with activity.
         final ActivityStack stack = createTaskStackOnDisplay(dc);
         assertEquals(dc.getDisplayId(), stack.getDisplayContent().getDisplayId());
-        assertEquals(dc, stack.getParent().getParent());
         assertEquals(dc, stack.getDisplayContent());
 
         final Task task = createTaskInStack(stack, 0 /* userId */);
@@ -256,7 +255,6 @@
         // Move stack to first display.
         mDisplayContent.moveStackToDisplay(stack, true /* onTop */);
         assertEquals(mDisplayContent.getDisplayId(), stack.getDisplayContent().getDisplayId());
-        assertEquals(mDisplayContent, stack.getParent().getParent());
         assertEquals(mDisplayContent, stack.getDisplayContent());
         assertEquals(mDisplayContent, task.getDisplayContent());
         assertEquals(mDisplayContent, activity.getDisplayContent());
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index d4c81ac..46f95ed 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -16,7 +16,6 @@
 
 package com.android.server.wm;
 
-import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION;
 import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
@@ -27,7 +26,7 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 import static com.android.server.wm.ActivityStack.ActivityState.STOPPED;
 
@@ -36,7 +35,6 @@
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Mockito.when;
 
 import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
@@ -54,7 +52,6 @@
 import org.junit.runner.RunWith;
 
 import java.util.ArrayList;
-import java.util.concurrent.TimeUnit;
 
 /**
  * Tests for Size Compatibility mode.
@@ -318,37 +315,35 @@
     @Test
     public void testResetNonVisibleActivity() {
         setUpApp(new TestActivityDisplay.Builder(mService, 1000, 2500).build());
-        final ActivityDisplay display = mStack.getDisplay();
-        spyOn(display);
-
         prepareUnresizable(1.5f, SCREEN_ORIENTATION_UNSPECIFIED);
+        final ActivityDisplay display = mStack.getDisplay();
+        // Resize the display so the activity is in size compatibility mode.
+        resizeDisplay(display, 900, 1800);
+
         mActivity.setState(STOPPED, "testSizeCompatMode");
         mActivity.mVisibleRequested = false;
         mActivity.app.setReportedProcState(ActivityManager.PROCESS_STATE_CACHED_ACTIVITY);
-        // Make the parent bounds to be different so the activity is in size compatibility mode.
-        mTask.getWindowConfiguration().setAppBounds(new Rect(0, 0, 600, 1200));
 
         // Simulate the display changes orientation.
-        when(display.getLastOverrideConfigurationChanges()).thenReturn(
-                ActivityInfo.CONFIG_SCREEN_SIZE | CONFIG_ORIENTATION
-                        | ActivityInfo.CONFIG_WINDOW_CONFIGURATION);
-        mActivity.onConfigurationChanged(mTask.getConfiguration());
-        when(display.getLastOverrideConfigurationChanges()).thenCallRealMethod();
-        // The override configuration should not change so it is still in size compatibility mode.
+        final Configuration c = new Configuration();
+        display.getDisplayRotation().setRotation(ROTATION_90);
+        display.computeScreenConfiguration(c);
+        display.onRequestedOverrideConfigurationChanged(c);
+        // Size compatibility mode is able to handle orientation change so the process shouldn't be
+        // restarted and the override configuration won't be cleared.
+        verify(mActivity, never()).restartProcessIfVisible();
         assertTrue(mActivity.inSizeCompatMode());
 
         // Change display density
-        final DisplayContent displayContent = mStack.getDisplay().mDisplayContent;
-        displayContent.mBaseDisplayDensity = (int) (0.7f * displayContent.mBaseDisplayDensity);
-        final Configuration c = new Configuration();
-        displayContent.computeScreenConfiguration(c);
+        display.mBaseDisplayDensity = (int) (0.7f * display.mBaseDisplayDensity);
+        display.computeScreenConfiguration(c);
         mService.mAmInternal = mock(ActivityManagerInternal.class);
-        mStack.getDisplay().onRequestedOverrideConfigurationChanged(c);
+        display.onRequestedOverrideConfigurationChanged(c);
 
         // The override configuration should be reset and the activity's process will be killed.
         assertFalse(mActivity.inSizeCompatMode());
         verify(mActivity).restartProcessIfVisible();
-        mLockRule.runWithScissors(mService.mH, () -> { }, TimeUnit.SECONDS.toMillis(3));
+        waitHandlerIdle(mService.mH);
         verify(mService.mAmInternal).killProcess(
                 eq(mActivity.app.mName), eq(mActivity.app.mUid), anyString());
     }
@@ -363,7 +358,6 @@
         ActivityRecord activity = mActivity;
         activity.setState(ActivityStack.ActivityState.RESUMED, "testHandleActivitySizeCompatMode");
         prepareUnresizable(-1.f /* maxAspect */, SCREEN_ORIENTATION_PORTRAIT);
-        ensureActivityConfiguration();
         assertFalse(mActivity.inSizeCompatMode());
 
         final ArrayList<IBinder> compatTokens = new ArrayList<>();
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 91646ad..8b3d30f 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -2092,12 +2092,6 @@
             "allow_metered_network_for_cert_download_bool";
 
     /**
-     * Carrier specified WiFi networks.
-     * @hide
-     */
-    public static final String KEY_CARRIER_WIFI_STRING_ARRAY = "carrier_wifi_string_array";
-
-    /**
      * Time delay (in ms) after which we show the notification to switch the preferred
      * network.
      * @hide
@@ -3046,7 +3040,6 @@
         /**
          * Location information during (and after) an emergency call is only provided over control
          * plane signaling from the network.
-         * @hide
          */
         public static final int SUPL_EMERGENCY_MODE_TYPE_CP_ONLY = 0;
 
@@ -3054,7 +3047,6 @@
          * Location information during (and after) an emergency call is provided over the data
          * plane and serviced by the framework GNSS service, but if it fails, the carrier also
          * supports control plane backup signaling.
-         * @hide
          */
         public static final int SUPL_EMERGENCY_MODE_TYPE_CP_FALLBACK = 1;
 
@@ -3062,7 +3054,6 @@
          * Location information during (and after) an emergency call is provided over the data plane
          * and serviced by the framework GNSS service only. There is no backup signalling over the
          * control plane if it fails.
-         * @hide
          */
         public static final int SUPL_EMERGENCY_MODE_TYPE_DP_ONLY = 2;
 
@@ -3169,11 +3160,21 @@
          * {@link #SUPL_EMERGENCY_MODE_TYPE_CP_ONLY}.
          * <p>
          * The default value for this configuration is {@link #SUPL_EMERGENCY_MODE_TYPE_CP_ONLY}.
-         * @hide
          */
         public static final String KEY_ES_SUPL_CONTROL_PLANE_SUPPORT_INT = KEY_PREFIX
                 + "es_supl_control_plane_support_int";
 
+        /**
+         * A list of roaming PLMNs where SUPL ES mode does not support a control-plane mechanism to
+         * get a user's location in the event that data plane SUPL fails or is otherwise
+         * unavailable.
+         * <p>
+         * A string array of PLMNs that do not support a control-plane mechanism for getting a
+         * user's location for SUPL ES.
+         */
+        public static final String KEY_ES_SUPL_DATA_PLANE_ONLY_ROAMING_PLMN_STRING_ARRAY =
+                KEY_PREFIX + "es_supl_data_plane_only_roaming_plmn_string_array";
+
         private static PersistableBundle getDefaults() {
             PersistableBundle defaults = new PersistableBundle();
             defaults.putBoolean(KEY_PERSIST_LPP_MODE_BOOL, true);
@@ -3190,63 +3191,11 @@
             defaults.putString(KEY_NFW_PROXY_APPS_STRING, "");
             defaults.putInt(KEY_ES_SUPL_CONTROL_PLANE_SUPPORT_INT,
                     SUPL_EMERGENCY_MODE_TYPE_CP_ONLY);
+            defaults.putStringArray(KEY_ES_SUPL_DATA_PLANE_ONLY_ROAMING_PLMN_STRING_ARRAY, null);
             return defaults;
         }
     }
 
-    /**
-     * Wi-Fi configs used in Carrier Wi-Fi application.
-     *
-     * @hide
-     */
-    @SystemApi
-    public static final class Wifi {
-        /** Prefix of all Wifi.KEY_* constants. */
-        public static final String KEY_PREFIX = "wifi.";
-
-        /**
-         * Whenever any information under wifi namespace is changed, the version should be
-         * incremented by 1 so that the device is able to figure out the latest profiles based on
-         * the version.
-         */
-        public static final String KEY_CARRIER_PROFILES_VERSION_INT =
-                KEY_PREFIX + "carrier_profiles_version_int";
-
-        /**
-         * It contains the package name of connection manager that the carrier owns.
-         *
-         * <P>Once it is installed, the profiles installed by Carrier Wi-Fi Application
-         * will be deleted.
-         * Once it is uninstalled, Carrier Wi-Fi Application will re-install the latest profiles.
-         */
-        public static final String KEY_CARRIER_CONNECTION_MANAGER_PACKAGE_STRING =
-                KEY_PREFIX + "carrier_connection_manager_package_string";
-        /**
-         * It is to have the list of wifi networks profiles which contain the information about
-         * the wifi-networks to which carrier wants the device to connect.
-         */
-        public static final String KEY_NETWORK_PROFILES_STRING_ARRAY =
-                KEY_PREFIX + "network_profiles_string_array";
-
-        /**
-         * It is to have the list of Passpoint profiles which contain the information about
-         * the Passpoint networks to which carrier wants the device to connect.
-         */
-        public static final String KEY_PASSPOINT_PROFILES_STRING_ARRAY =
-                KEY_PREFIX + "passpoint_profiles_string_array";
-
-        private static PersistableBundle getDefaults() {
-            PersistableBundle defaults = new PersistableBundle();
-            defaults.putInt(KEY_CARRIER_PROFILES_VERSION_INT, -1);
-            defaults.putString(KEY_CARRIER_CONNECTION_MANAGER_PACKAGE_STRING, null);
-            defaults.putStringArray(KEY_NETWORK_PROFILES_STRING_ARRAY, null);
-            defaults.putStringArray(KEY_PASSPOINT_PROFILES_STRING_ARRAY, null);
-            return defaults;
-        }
-
-        private Wifi() {}
-    }
-
    /**
     * An int array containing CDMA enhanced roaming indicator values for Home (non-roaming) network.
     * The default values come from 3GPP2 C.R1001 table 8.1-1.
@@ -3699,7 +3648,6 @@
         sDefaults.putBoolean(KEY_STK_DISABLE_LAUNCH_BROWSER_BOOL, false);
         sDefaults.putBoolean(KEY_ALLOW_METERED_NETWORK_FOR_CERT_DOWNLOAD_BOOL, false);
         sDefaults.putBoolean(KEY_HIDE_DIGITS_HELPER_TEXT_ON_STK_INPUT_SCREEN_BOOL, true);
-        sDefaults.putStringArray(KEY_CARRIER_WIFI_STRING_ARRAY, null);
         sDefaults.putInt(KEY_PREF_NETWORK_NOTIFICATION_DELAY_INT, -1);
         sDefaults.putInt(KEY_EMERGENCY_NOTIFICATION_DELAY_INT, -1);
         sDefaults.putBoolean(KEY_ALLOW_USSD_REQUESTS_VIA_TELEPHONY_MANAGER_BOOL, true);
@@ -3828,7 +3776,6 @@
         /* Default value is 60 seconds. */
         sDefaults.putLong(KEY_OPPORTUNISTIC_NETWORK_MAX_BACKOFF_TIME_LONG, 60000);
         sDefaults.putAll(Gps.getDefaults());
-        sDefaults.putAll(Wifi.getDefaults());
         sDefaults.putIntArray(KEY_CDMA_ENHANCED_ROAMING_INDICATOR_FOR_HOME_NETWORK_INT_ARRAY,
                 new int[] {
                         1 /* Roaming Indicator Off */
diff --git a/telephony/java/android/telephony/NetworkScanRequest.java b/telephony/java/android/telephony/NetworkScanRequest.java
index 465b6aa..0ceb103 100644
--- a/telephony/java/android/telephony/NetworkScanRequest.java
+++ b/telephony/java/android/telephony/NetworkScanRequest.java
@@ -20,10 +20,10 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 
-import java.util.ArrayList;
-import java.util.Arrays;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Arrays;
 
 /**
  * Defines a request to peform a network scan.
@@ -221,9 +221,11 @@
 
     private NetworkScanRequest(Parcel in) {
         mScanType = in.readInt();
-        mSpecifiers = (RadioAccessSpecifier[]) in.readParcelableArray(
-                Object.class.getClassLoader(),
-                RadioAccessSpecifier.class);
+        Parcelable[] tempSpecifiers = in.readParcelableArray(Object.class.getClassLoader());
+        mSpecifiers = new RadioAccessSpecifier[tempSpecifiers.length];
+        for (int i = 0; i < tempSpecifiers.length; i++) {
+            mSpecifiers[i] = (RadioAccessSpecifier) tempSpecifiers[i];
+        }
         mSearchPeriodicity = in.readInt();
         mMaxSearchTime = in.readInt();
         mIncrementalResults = in.readBoolean();
diff --git a/telephony/java/android/telephony/PhoneNumberRange.java b/telephony/java/android/telephony/PhoneNumberRange.java
index e6f107e..2b199d2 100644
--- a/telephony/java/android/telephony/PhoneNumberRange.java
+++ b/telephony/java/android/telephony/PhoneNumberRange.java
@@ -85,18 +85,18 @@
     }
 
     private PhoneNumberRange(Parcel in) {
-        mCountryCode = in.readStringNoHelper();
-        mPrefix = in.readStringNoHelper();
-        mLowerBound = in.readStringNoHelper();
-        mUpperBound = in.readStringNoHelper();
+        mCountryCode = in.readString();
+        mPrefix = in.readString();
+        mLowerBound = in.readString();
+        mUpperBound = in.readString();
     }
 
     @Override
     public void writeToParcel(Parcel dest, int flags) {
-        dest.writeStringNoHelper(mCountryCode);
-        dest.writeStringNoHelper(mPrefix);
-        dest.writeStringNoHelper(mLowerBound);
-        dest.writeStringNoHelper(mUpperBound);
+        dest.writeString(mCountryCode);
+        dest.writeString(mPrefix);
+        dest.writeString(mLowerBound);
+        dest.writeString(mUpperBound);
     }
 
     @Override
diff --git a/telephony/java/android/telephony/SubscriptionInfo.java b/telephony/java/android/telephony/SubscriptionInfo.java
index ebb5175..b78d279 100644
--- a/telephony/java/android/telephony/SubscriptionInfo.java
+++ b/telephony/java/android/telephony/SubscriptionInfo.java
@@ -665,8 +665,8 @@
             int id = source.readInt();
             String iccId = source.readString();
             int simSlotIndex = source.readInt();
-            CharSequence displayName = source.readCharSequence();
-            CharSequence carrierName = source.readCharSequence();
+            CharSequence displayName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
+            CharSequence carrierName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
             int nameSource = source.readInt();
             int iconTint = source.readInt();
             String number = source.readString();
@@ -685,8 +685,8 @@
             int carrierid = source.readInt();
             int profileClass = source.readInt();
             int subType = source.readInt();
-            String[] ehplmns = source.readStringArray();
-            String[] hplmns = source.readStringArray();
+            String[] ehplmns = source.createStringArray();
+            String[] hplmns = source.createStringArray();
             String groupOwner = source.readString();
             UiccAccessRule[] carrierConfigAccessRules = source.createTypedArray(
                 UiccAccessRule.CREATOR);
@@ -711,8 +711,8 @@
         dest.writeInt(mId);
         dest.writeString(mIccId);
         dest.writeInt(mSimSlotIndex);
-        dest.writeCharSequence(mDisplayName);
-        dest.writeCharSequence(mCarrierName);
+        TextUtils.writeToParcel(mDisplayName, dest, 0);
+        TextUtils.writeToParcel(mCarrierName, dest, 0);
         dest.writeInt(mNameSource);
         dest.writeInt(mIconTint);
         dest.writeString(mNumber);
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index e3981f6..9f5acb3 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -430,14 +430,14 @@
         int modemCount = 1;
         switch (getMultiSimConfiguration()) {
             case UNKNOWN:
-                ConnectivityManager cm = mContext == null ? null : (ConnectivityManager) mContext
-                        .getSystemService(Context.CONNECTIVITY_SERVICE);
+                modemCount = MODEM_COUNT_SINGLE_MODEM;
                 // check for voice and data support, 0 if not supported
-                if (!isVoiceCapable() && !isSmsCapable() && cm != null
-                        && !cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE)) {
-                    modemCount = MODEM_COUNT_NO_MODEM;
-                } else {
-                    modemCount = MODEM_COUNT_SINGLE_MODEM;
+                if (!isVoiceCapable() && !isSmsCapable() && mContext != null) {
+                    ConnectivityManager cm = (ConnectivityManager) mContext
+                            .getSystemService(Context.CONNECTIVITY_SERVICE);
+                    if (cm != null && !cm.isNetworkSupported(ConnectivityManager.TYPE_MOBILE)) {
+                        modemCount = MODEM_COUNT_NO_MODEM;
+                    }
                 }
                 break;
             case DSDS:
@@ -716,31 +716,6 @@
     public static final String EXTRA_INCOMING_NUMBER = "incoming_number";
 
     /**
-     * Broadcast intent action indicating that a precise call state
-     * (cellular) on the device has changed.
-     *
-     * <p>
-     * The {@link #EXTRA_RINGING_CALL_STATE} extra indicates the ringing call state.
-     * The {@link #EXTRA_FOREGROUND_CALL_STATE} extra indicates the foreground call state.
-     * The {@link #EXTRA_BACKGROUND_CALL_STATE} extra indicates the background call state.
-     *
-     * <p class="note">
-     * Requires the READ_PRECISE_PHONE_STATE permission.
-     *
-     * @see #EXTRA_RINGING_CALL_STATE
-     * @see #EXTRA_FOREGROUND_CALL_STATE
-     * @see #EXTRA_BACKGROUND_CALL_STATE
-     *
-     * <p class="note">
-     * Requires the READ_PRECISE_PHONE_STATE permission.
-     * @deprecated use {@link PhoneStateListener#LISTEN_PRECISE_CALL_STATE} instead
-     * @hide
-     */
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    public static final String ACTION_PRECISE_CALL_STATE_CHANGED =
-            "android.intent.action.PRECISE_CALL_STATE";
-
-    /**
      * Broadcast intent action indicating that call disconnect cause has changed.
      *
      * <p>
@@ -762,78 +737,6 @@
     /**
      * The lookup key used with the {@link #ACTION_PRECISE_CALL_STATE_CHANGED} broadcast and
      * {@link PhoneStateListener#onPreciseCallStateChanged(PreciseCallState)} for an integer
-     * containing the state of the current ringing call.
-     *
-     * @see PreciseCallState#PRECISE_CALL_STATE_NOT_VALID
-     * @see PreciseCallState#PRECISE_CALL_STATE_IDLE
-     * @see PreciseCallState#PRECISE_CALL_STATE_ACTIVE
-     * @see PreciseCallState#PRECISE_CALL_STATE_HOLDING
-     * @see PreciseCallState#PRECISE_CALL_STATE_DIALING
-     * @see PreciseCallState#PRECISE_CALL_STATE_ALERTING
-     * @see PreciseCallState#PRECISE_CALL_STATE_INCOMING
-     * @see PreciseCallState#PRECISE_CALL_STATE_WAITING
-     * @see PreciseCallState#PRECISE_CALL_STATE_DISCONNECTED
-     * @see PreciseCallState#PRECISE_CALL_STATE_DISCONNECTING
-     *
-     * <p class="note">
-     * Retrieve with
-     * {@link android.content.Intent#getIntExtra(String name, int defaultValue)}.
-     *
-     * @hide
-     */
-    public static final String EXTRA_RINGING_CALL_STATE = "ringing_state";
-
-    /**
-     * The lookup key used with the {@link #ACTION_PRECISE_CALL_STATE_CHANGED} broadcast and
-     * {@link PhoneStateListener#onPreciseCallStateChanged(PreciseCallState)} for an integer
-     * containing the state of the current foreground call.
-     *
-     * @see PreciseCallState#PRECISE_CALL_STATE_NOT_VALID
-     * @see PreciseCallState#PRECISE_CALL_STATE_IDLE
-     * @see PreciseCallState#PRECISE_CALL_STATE_ACTIVE
-     * @see PreciseCallState#PRECISE_CALL_STATE_HOLDING
-     * @see PreciseCallState#PRECISE_CALL_STATE_DIALING
-     * @see PreciseCallState#PRECISE_CALL_STATE_ALERTING
-     * @see PreciseCallState#PRECISE_CALL_STATE_INCOMING
-     * @see PreciseCallState#PRECISE_CALL_STATE_WAITING
-     * @see PreciseCallState#PRECISE_CALL_STATE_DISCONNECTED
-     * @see PreciseCallState#PRECISE_CALL_STATE_DISCONNECTING
-     *
-     * <p class="note">
-     * Retrieve with
-     * {@link android.content.Intent#getIntExtra(String name, int defaultValue)}.
-     *
-     * @hide
-     */
-    public static final String EXTRA_FOREGROUND_CALL_STATE = "foreground_state";
-
-    /**
-     * The lookup key used with the {@link #ACTION_PRECISE_CALL_STATE_CHANGED} broadcast and
-     * {@link PhoneStateListener#onPreciseCallStateChanged(PreciseCallState)} for an integer
-     * containing the state of the current background call.
-     *
-     * @see PreciseCallState#PRECISE_CALL_STATE_NOT_VALID
-     * @see PreciseCallState#PRECISE_CALL_STATE_IDLE
-     * @see PreciseCallState#PRECISE_CALL_STATE_ACTIVE
-     * @see PreciseCallState#PRECISE_CALL_STATE_HOLDING
-     * @see PreciseCallState#PRECISE_CALL_STATE_DIALING
-     * @see PreciseCallState#PRECISE_CALL_STATE_ALERTING
-     * @see PreciseCallState#PRECISE_CALL_STATE_INCOMING
-     * @see PreciseCallState#PRECISE_CALL_STATE_WAITING
-     * @see PreciseCallState#PRECISE_CALL_STATE_DISCONNECTED
-     * @see PreciseCallState#PRECISE_CALL_STATE_DISCONNECTING
-     *
-     * <p class="note">
-     * Retrieve with
-     * {@link android.content.Intent#getIntExtra(String name, int defaultValue)}.
-     *
-     * @hide
-     */
-    public static final String EXTRA_BACKGROUND_CALL_STATE = "background_state";
-
-    /**
-     * The lookup key used with the {@link #ACTION_PRECISE_CALL_STATE_CHANGED} broadcast and
-     * {@link PhoneStateListener#onPreciseCallStateChanged(PreciseCallState)} for an integer
      * containing the disconnect cause.
      *
      * @see DisconnectCause
@@ -862,88 +765,6 @@
     public static final String EXTRA_PRECISE_DISCONNECT_CAUSE = "precise_disconnect_cause";
 
     /**
-     * Broadcast intent action indicating a data connection has changed,
-     * providing precise information about the connection.
-     *
-     * <p>
-     * The {@link #EXTRA_DATA_STATE} extra indicates the connection state.
-     * The {@link #EXTRA_DATA_NETWORK_TYPE} extra indicates the connection network type.
-     * The {@link #EXTRA_DATA_APN_TYPE} extra indicates the APN type.
-     * The {@link #EXTRA_DATA_APN} extra indicates the APN.
-     * The {@link #EXTRA_DATA_IFACE_PROPERTIES} extra indicates the connection interface.
-     * The {@link #EXTRA_DATA_FAILURE_CAUSE} extra indicates the connection fail cause.
-     *
-     * <p class="note">
-     * Requires the READ_PRECISE_PHONE_STATE permission.
-     *
-     * @see #EXTRA_DATA_STATE
-     * @see #EXTRA_DATA_NETWORK_TYPE
-     * @see #EXTRA_DATA_APN_TYPE
-     * @see #EXTRA_DATA_APN
-     * @see #EXTRA_DATA_IFACE
-     * @see #EXTRA_DATA_FAILURE_CAUSE
-     * @hide
-     *
-     * @deprecated If the app is running in the background, it won't be able to receive this
-     * broadcast. Apps should use ConnectivityManager {@link #registerNetworkCallback(
-     * android.net.NetworkRequest, ConnectivityManager.NetworkCallback)} to listen for network
-     * changes.
-     */
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
-    @Deprecated
-    @UnsupportedAppUsage
-    public static final String ACTION_PRECISE_DATA_CONNECTION_STATE_CHANGED =
-            "android.intent.action.PRECISE_DATA_CONNECTION_STATE_CHANGED";
-
-    /**
-     * The lookup key used with the {@link #ACTION_PRECISE_DATA_CONNECTION_STATE_CHANGED} broadcast
-     * for an integer containing the state of the current data connection.
-     *
-     * @see TelephonyManager#DATA_UNKNOWN
-     * @see TelephonyManager#DATA_DISCONNECTED
-     * @see TelephonyManager#DATA_CONNECTING
-     * @see TelephonyManager#DATA_CONNECTED
-     * @see TelephonyManager#DATA_SUSPENDED
-     *
-     * <p class="note">
-     * Retrieve with
-     * {@link android.content.Intent#getIntExtra(String name, int defaultValue)}.
-     *
-     * @hide
-     */
-    public static final String EXTRA_DATA_STATE = PhoneConstants.STATE_KEY;
-
-    /**
-     * The lookup key used with the {@link #ACTION_PRECISE_DATA_CONNECTION_STATE_CHANGED} broadcast
-     * for an integer containing the network type.
-     *
-     * @see TelephonyManager#NETWORK_TYPE_UNKNOWN
-     * @see TelephonyManager#NETWORK_TYPE_GPRS
-     * @see TelephonyManager#NETWORK_TYPE_EDGE
-     * @see TelephonyManager#NETWORK_TYPE_UMTS
-     * @see TelephonyManager#NETWORK_TYPE_CDMA
-     * @see TelephonyManager#NETWORK_TYPE_EVDO_0
-     * @see TelephonyManager#NETWORK_TYPE_EVDO_A
-     * @see TelephonyManager#NETWORK_TYPE_1xRTT
-     * @see TelephonyManager#NETWORK_TYPE_HSDPA
-     * @see TelephonyManager#NETWORK_TYPE_HSUPA
-     * @see TelephonyManager#NETWORK_TYPE_HSPA
-     * @see TelephonyManager#NETWORK_TYPE_IDEN
-     * @see TelephonyManager#NETWORK_TYPE_EVDO_B
-     * @see TelephonyManager#NETWORK_TYPE_LTE
-     * @see TelephonyManager#NETWORK_TYPE_EHRPD
-     * @see TelephonyManager#NETWORK_TYPE_HSPAP
-     * @see TelephonyManager#NETWORK_TYPE_NR
-     *
-     * <p class="note">
-     * Retrieve with
-     * {@link android.content.Intent#getIntExtra(String name, int defaultValue)}.
-     *
-     * @hide
-     */
-    public static final String EXTRA_DATA_NETWORK_TYPE = PhoneConstants.DATA_NETWORK_TYPE_KEY;
-
-    /**
      * The lookup key used with the {@link #ACTION_PRECISE_DATA_CONNECTION_STATE_CHANGED} broadcast
      * for an String containing the data APN type.
      *
@@ -980,18 +801,6 @@
     public static final String EXTRA_DATA_LINK_PROPERTIES_KEY = PhoneConstants.DATA_LINK_PROPERTIES_KEY;
 
     /**
-     * The lookup key used with the {@link #ACTION_PRECISE_DATA_CONNECTION_STATE_CHANGED} broadcast
-     * for the data connection fail cause.
-     *
-     * <p class="note">
-     * Retrieve with
-     * {@link android.content.Intent#getStringExtra(String name)}.
-     *
-     * @hide
-     */
-    public static final String EXTRA_DATA_FAILURE_CAUSE = PhoneConstants.DATA_FAILURE_CAUSE_KEY;
-
-    /**
      * Broadcast intent action for letting the default dialer to know to show voicemail
      * notification.
      *
diff --git a/test-mock/Android.bp b/test-mock/Android.bp
index aa4174a..616b6b0 100644
--- a/test-mock/Android.bp
+++ b/test-mock/Android.bp
@@ -30,6 +30,7 @@
     libs: [
         "framework-all",
         "app-compat-annotations",
+        "unsupportedappusage",
     ],
 
     api_packages: [
diff --git a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
index 3be7a4a..7b289d8 100644
--- a/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
+++ b/tests/RollbackTest/RollbackTest/src/com/android/tests/rollback/StagedRollbackTest.java
@@ -22,18 +22,14 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import android.Manifest;
-import android.annotation.Nullable;
 import android.content.ComponentName;
-import android.content.Context;
 import android.content.Intent;
-import android.content.pm.PackageInfo;
 import android.content.pm.PackageInstaller;
 import android.content.pm.PackageManager;
 import android.content.rollback.RollbackInfo;
 import android.content.rollback.RollbackManager;
 import android.os.ParcelFileDescriptor;
 import android.provider.DeviceConfig;
-import android.text.TextUtils;
 
 import androidx.test.platform.app.InstrumentationRegistry;
 
@@ -44,7 +40,6 @@
 import com.android.cts.install.lib.Uninstall;
 import com.android.cts.rollback.lib.Rollback;
 import com.android.cts.rollback.lib.RollbackUtils;
-import com.android.internal.R;
 
 import libcore.io.IoUtils;
 
@@ -75,7 +70,6 @@
     private static final String PROPERTY_WATCHDOG_REQUEST_TIMEOUT_MILLIS =
             "watchdog_request_timeout_millis";
 
-    private static final String MODULE_META_DATA_PACKAGE = getModuleMetadataPackageName();
     private static final TestApp NETWORK_STACK = new TestApp("NetworkStack",
             getNetworkStackPackageName(), -1, false, findNetworkStackApk());
 
@@ -186,21 +180,15 @@
     }
 
     /**
-     * Stage install ModuleMetadata package to simulate a Mainline module update.
+     * Stage install an apk with rollback that will be later triggered by unattributable crash.
      */
     @Test
     public void testNativeWatchdogTriggersRollback_Phase1() throws Exception {
-        resetModuleMetadataPackage();
-        Context context = InstrumentationRegistry.getInstrumentation().getContext();
-        PackageInfo metadataPackageInfo = context.getPackageManager().getPackageInfo(
-                MODULE_META_DATA_PACKAGE, 0);
-        String metadataApkPath = metadataPackageInfo.applicationInfo.sourceDir;
-        assertThat(metadataApkPath).isNotNull();
-        assertThat(metadataApkPath).isNotEqualTo("");
+        Uninstall.packages(TestApp.A);
+        Install.single(TestApp.A1).commit();
+        assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
 
-        runShellCommand("pm install "
-                + "-r --enable-rollback --staged --wait "
-                + metadataApkPath);
+        Install.single(TestApp.A2).setEnableRollback().setStaged().commit();
     }
 
     /**
@@ -208,9 +196,10 @@
      */
     @Test
     public void testNativeWatchdogTriggersRollback_Phase2() throws Exception {
+        assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(2);
         RollbackManager rm = RollbackUtils.getRollbackManager();
         assertThat(getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(),
-                        MODULE_META_DATA_PACKAGE)).isNotNull();
+                TestApp.A)).isNotNull();
     }
 
     /**
@@ -218,9 +207,10 @@
      */
     @Test
     public void testNativeWatchdogTriggersRollback_Phase3() throws Exception {
+        assertThat(InstallUtils.getInstalledVersion(TestApp.A)).isEqualTo(1);
         RollbackManager rm = RollbackUtils.getRollbackManager();
         assertThat(getUniqueRollbackInfoForPackage(rm.getRecentlyCommittedRollbacks(),
-                        MODULE_META_DATA_PACKAGE)).isNotNull();
+                TestApp.A)).isNotNull();
     }
 
     @Test
@@ -351,26 +341,6 @@
                         getNetworkStackPackageName())).isNull();
     }
 
-    @Nullable
-    private static String getModuleMetadataPackageName() {
-        String packageName = InstrumentationRegistry.getInstrumentation().getContext()
-                .getResources().getString(R.string.config_defaultModuleMetadataProvider);
-        if (TextUtils.isEmpty(packageName)) {
-            return null;
-        }
-        return packageName;
-    }
-
-    private void resetModuleMetadataPackage() {
-        RollbackManager rm = RollbackUtils.getRollbackManager();
-
-        assertThat(MODULE_META_DATA_PACKAGE).isNotNull();
-        rm.expireRollbackForPackage(MODULE_META_DATA_PACKAGE);
-
-        assertThat(getUniqueRollbackInfoForPackage(rm.getAvailableRollbacks(),
-                MODULE_META_DATA_PACKAGE)).isNull();
-    }
-
     private static void runShellCommand(String cmd) {
         ParcelFileDescriptor pfd = InstrumentationRegistry.getInstrumentation().getUiAutomation()
                 .executeShellCommand(cmd);
diff --git a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
index 449f95e..66b0590 100644
--- a/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
+++ b/wifi/java/android/net/wifi/WifiEnterpriseConfig.java
@@ -22,7 +22,6 @@
 import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.security.Credentials;
 import android.text.TextUtils;
 import android.util.Log;
 
@@ -93,10 +92,26 @@
      */
     public static final String ENGINE_DISABLE = "0";
 
+    /**
+     * Key prefix for CA certificates.
+     * Note: copied from {@link android.security.Credentials#CA_CERTIFICATE} since it is @hide.
+     */
+    private static final String CA_CERTIFICATE = "CACERT_";
+    /**
+     * Key prefix for user certificates.
+     * Note: copied from {@link android.security.Credentials#USER_CERTIFICATE} since it is @hide.
+     */
+    private static final String USER_CERTIFICATE = "USRCERT_";
+    /**
+     * Key prefix for user private and secret keys.
+     * Note: copied from {@link android.security.Credentials#USER_PRIVATE_KEY} since it is @hide.
+     */
+    private static final String USER_PRIVATE_KEY = "USRPKEY_";
+
     /** @hide */
-    public static final String CA_CERT_PREFIX = KEYSTORE_URI + Credentials.CA_CERTIFICATE;
+    public static final String CA_CERT_PREFIX = KEYSTORE_URI + CA_CERTIFICATE;
     /** @hide */
-    public static final String CLIENT_CERT_PREFIX = KEYSTORE_URI + Credentials.USER_CERTIFICATE;
+    public static final String CLIENT_CERT_PREFIX = KEYSTORE_URI + USER_CERTIFICATE;
     /** @hide */
     public static final String CLIENT_CERT_KEY     = "client_cert";
     /** @hide */
@@ -659,7 +674,7 @@
                 if (i > 0) {
                     sb.append(CA_CERT_ALIAS_DELIMITER);
                 }
-                sb.append(encodeCaCertificateAlias(Credentials.CA_CERTIFICATE + aliases[i]));
+                sb.append(encodeCaCertificateAlias(CA_CERTIFICATE + aliases[i]));
             }
             setFieldValue(CA_CERT_KEY, sb.toString(), KEYSTORES_URI);
         }
@@ -693,8 +708,8 @@
             String[] aliases = TextUtils.split(values, CA_CERT_ALIAS_DELIMITER);
             for (int i = 0; i < aliases.length; i++) {
                 aliases[i] = decodeCaCertificateAlias(aliases[i]);
-                if (aliases[i].startsWith(Credentials.CA_CERTIFICATE)) {
-                    aliases[i] = aliases[i].substring(Credentials.CA_CERTIFICATE.length());
+                if (aliases[i].startsWith(CA_CERTIFICATE)) {
+                    aliases[i] = aliases[i].substring(CA_CERTIFICATE.length());
                 }
             }
             return aliases.length != 0 ? aliases : null;
@@ -832,7 +847,7 @@
     @SystemApi
     public void setClientCertificateAlias(@Nullable String alias) {
         setFieldValue(CLIENT_CERT_KEY, alias, CLIENT_CERT_PREFIX);
-        setFieldValue(PRIVATE_KEY_ID_KEY, alias, Credentials.USER_PRIVATE_KEY);
+        setFieldValue(PRIVATE_KEY_ID_KEY, alias, USER_PRIVATE_KEY);
         // Also, set engine parameters
         if (TextUtils.isEmpty(alias)) {
             setFieldValue(ENGINE_KEY, ENGINE_DISABLE);
diff --git a/wifi/java/android/net/wifi/WifiNetworkAgentSpecifier.java b/wifi/java/android/net/wifi/WifiNetworkAgentSpecifier.java
index 24aa23a..04d2e1a 100644
--- a/wifi/java/android/net/wifi/WifiNetworkAgentSpecifier.java
+++ b/wifi/java/android/net/wifi/WifiNetworkAgentSpecifier.java
@@ -23,7 +23,6 @@
 import android.annotation.Nullable;
 import android.net.MacAddress;
 import android.net.MatchAllNetworkSpecifier;
-import android.net.NetworkAgent;
 import android.net.NetworkRequest;
 import android.net.NetworkSpecifier;
 import android.os.Parcel;
@@ -120,7 +119,7 @@
 
     /**
      * Match {@link WifiNetworkSpecifier} in app's {@link NetworkRequest} with the
-     * {@link WifiNetworkAgentSpecifier} in wifi platform's {@link NetworkAgent}.
+     * {@link WifiNetworkAgentSpecifier} in wifi platform's {@link android.net.NetworkAgent}.
      */
     public boolean satisfiesNetworkSpecifier(@NonNull WifiNetworkSpecifier ns) {
         // None of these should be null by construction.