Merge "Move BubbleView methods into BadgedImageView; remove BubbleView"
diff --git a/Android.bp b/Android.bp
index a7ce917..9426a9c 100644
--- a/Android.bp
+++ b/Android.bp
@@ -803,10 +803,9 @@
 }
 
 filegroup {
-    name: "incremental_data_loader_aidl",
+    name: "dataloader_aidl",
     srcs: [
-        "core/java/android/service/incremental/IIncrementalDataLoaderStatusListener.aidl",
-        "core/java/android/service/incremental/IIncrementalDataLoaderService.aidl",
+        "core/java/android/content/pm/IDataLoaderStatusListener.aidl",
     ],
     path: "core/java",
 }
@@ -815,7 +814,27 @@
     name: "libincremental_aidl",
     srcs: [
         ":incremental_aidl",
-        ":incremental_data_loader_aidl",
+    ],
+    imports: [
+        "libdataloader_aidl",
+    ],
+    backend: {
+        java: {
+            sdk_version: "28",
+        },
+        cpp: {
+            enabled: true,
+        },
+        ndk: {
+            enabled: true,
+        },
+    },
+}
+
+aidl_interface {
+    name: "libdataloader_aidl",
+    srcs: [
+        ":dataloader_aidl",
     ],
     backend: {
         java: {
@@ -1675,6 +1694,7 @@
     srcs: [
         ":framework-annotations",
 	"core/java/android/os/HandlerExecutor.java",
+        "core/java/android/util/BackupUtils.java",
         "core/java/android/util/KeyValueListParser.java",
         "core/java/android/util/LocalLog.java",
         "core/java/android/util/Rational.java",
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 1e0b58e..55fa5ed 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -8,5 +8,10 @@
         }
       ]
     }
+  ],
+  "postsubmit-managedprofile-stress": [
+    {
+      "name": "ManagedProfileLifecycleStressTest"
+    }
   ]
 }
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/impl/FakeIcing.java b/apex/appsearch/service/java/com/android/server/appsearch/impl/FakeIcing.java
index 5ac922c..3dbb5cf 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/impl/FakeIcing.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/impl/FakeIcing.java
@@ -24,10 +24,8 @@
 
 import com.google.android.icing.proto.DocumentProto;
 import com.google.android.icing.proto.PropertyProto;
+import com.google.android.icing.proto.SearchResultProto;
 
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 import java.util.Set;
@@ -94,23 +92,25 @@
      * Returns documents containing the given term.
      *
      * @param term A single exact term to look up in the index.
-     * @return The matching documents, or an empty {@code List} if no documents match.
+     * @return A {@link SearchResultProto} containing the matching documents, which may have no
+     *   results if no documents match.
      */
     @NonNull
-    public List<DocumentProto> query(@NonNull String term) {
+    public SearchResultProto query(@NonNull String term) {
         String normTerm = normalizeString(term);
         Set<Integer> docIds = mIndex.get(normTerm);
         if (docIds == null || docIds.isEmpty()) {
-            return Collections.emptyList();
+            return SearchResultProto.getDefaultInstance();
         }
-        List<DocumentProto> matches = new ArrayList<>(docIds.size());
+        SearchResultProto.Builder results = SearchResultProto.newBuilder();
         for (int docId : docIds) {
             DocumentProto document = mDocStore.get(docId);
             if (document != null) {
-                matches.add(document);
+                results.addResults(
+                        SearchResultProto.ResultProto.newBuilder().setDocument(document));
             }
         }
-        return matches;
+        return results.build();
     }
 
     /**
diff --git a/apex/jobscheduler/framework/java/android/os/DeviceIdleManager.java b/apex/jobscheduler/framework/java/android/os/DeviceIdleManager.java
index 8019d4f..4c44334 100644
--- a/apex/jobscheduler/framework/java/android/os/DeviceIdleManager.java
+++ b/apex/jobscheduler/framework/java/android/os/DeviceIdleManager.java
@@ -17,13 +17,10 @@
 package android.os;
 
 import android.annotation.NonNull;
-import android.annotation.RequiresPermission;
 import android.annotation.SystemService;
 import android.annotation.TestApi;
 import android.content.Context;
 
-import java.util.List;
-
 /**
  * Access to the service that keeps track of device idleness and drives low power mode based on
  * that.
@@ -75,21 +72,6 @@
     }
 
     /**
-     * Add the specified packages to the power save whitelist.
-     *
-     * @return the number of packages that were successfully added to the whitelist
-     */
-    @RequiresPermission(android.Manifest.permission.DEVICE_POWER)
-    public int addPowerSaveWhitelistApps(@NonNull List<String> packageNames) {
-        try {
-            return mService.addPowerSaveWhitelistApps(packageNames);
-        } catch (RemoteException e) {
-            e.rethrowFromSystemServer();
-            return 0;
-        }
-    }
-
-    /**
      * Return whether a given package is in the power-save whitelist or not.
      * @hide
      */
diff --git a/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java b/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java
index 7a3ed92..4ffcf8a 100644
--- a/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java
+++ b/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java
@@ -26,6 +26,8 @@
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.Collections;
+import java.util.List;
 
 /**
  * Interface to access and modify the power save whitelist.
@@ -78,6 +80,31 @@
     }
 
     /**
+     * Add the specified package to the power save whitelist.
+     *
+     * @return true if the package was successfully added to the whitelist
+     */
+    @RequiresPermission(android.Manifest.permission.DEVICE_POWER)
+    public boolean addToWhitelist(@NonNull String packageName) {
+        return addToWhitelist(Collections.singletonList(packageName)) == 1;
+    }
+
+    /**
+     * Add the specified packages to the power save whitelist.
+     *
+     * @return the number of packages that were successfully added to the whitelist
+     */
+    @RequiresPermission(android.Manifest.permission.DEVICE_POWER)
+    public int addToWhitelist(@NonNull List<String> packageNames) {
+        try {
+            return mService.addPowerSaveWhitelistApps(packageNames);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+            return 0;
+        }
+    }
+
+    /**
      * Add an app to the temporary whitelist for a short amount of time.
      *
      * @param packageName The package to add to the temp whitelist
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
index bcd8be7..2f8b513 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
@@ -1171,7 +1171,8 @@
     private void fetchCarrierPrivilegedAppsLocked() {
         TelephonyManager telephonyManager =
                 mContext.getSystemService(TelephonyManager.class);
-        mCarrierPrivilegedApps = telephonyManager.getPackagesWithCarrierPrivilegesForAllPhones();
+        mCarrierPrivilegedApps =
+                telephonyManager.getCarrierPrivilegedPackagesForAllActiveSubscriptions();
         mHaveCarrierPrivilegedApps = true;
         if (DEBUG) {
             Slog.d(TAG, "apps with carrier privilege " + mCarrierPrivilegedApps);
diff --git a/apex/sdkext/Android.bp b/apex/sdkext/Android.bp
index b8dcb90..40f3c45 100644
--- a/apex/sdkext/Android.bp
+++ b/apex/sdkext/Android.bp
@@ -15,7 +15,13 @@
 apex {
     name: "com.android.sdkext",
     manifest: "manifest.json",
+    binaries: [ "derive_sdk" ],
     java_libs: [ "framework-sdkext" ],
+    prebuilts: [
+      "com.android.sdkext.ldconfig",
+      "cur_sdkinfo",
+      "derive_sdk.rc",
+    ],
     key: "com.android.sdkext.key",
     certificate: ":com.android.sdkext.certificate",
 }
@@ -30,3 +36,35 @@
     name: "com.android.sdkext.certificate",
     certificate: "com.android.sdkext",
 }
+
+prebuilt_etc {
+    name: "com.android.sdkext.ldconfig",
+    src: "ld.config.txt",
+    filename: "ld.config.txt",
+    installable: false,
+}
+
+python_binary_host {
+    name: "gen_sdkinfo",
+    srcs: [
+        "derive_sdk/sdk.proto",
+        "gen_sdkinfo.py",
+    ],
+    proto: {
+        canonical_path_from_root: false,
+    },
+}
+
+gensrcs {
+    name: "cur_sdkinfo_src",
+    srcs: [""],
+    tools: [ "gen_sdkinfo" ],
+    cmd: "$(location) -v 0 -o $(out)",
+}
+
+prebuilt_etc {
+    name: "cur_sdkinfo",
+    src: ":cur_sdkinfo_src",
+    filename: "sdkinfo.binarypb",
+    installable: false,
+}
diff --git a/apex/sdkext/derive_sdk/Android.bp b/apex/sdkext/derive_sdk/Android.bp
new file mode 100644
index 0000000..c4e3c29
--- /dev/null
+++ b/apex/sdkext/derive_sdk/Android.bp
@@ -0,0 +1,37 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_binary {
+    name: "derive_sdk",
+    srcs: [
+        "derive_sdk.cpp",
+        "sdk.proto",
+    ],
+    proto: {
+        type: "lite",
+    },
+    sdk_version: "current",
+    stl: "c++_static",
+    shared_libs: [ "liblog" ],
+    static_libs: [
+        "libbase_ndk",
+        "libprotobuf-cpp-lite-ndk",
+    ],
+}
+
+prebuilt_etc {
+    name: "derive_sdk.rc",
+    src: "derive_sdk.rc",
+    installable: false,
+}
diff --git a/apex/sdkext/derive_sdk/derive_sdk.cpp b/apex/sdkext/derive_sdk/derive_sdk.cpp
new file mode 100644
index 0000000..0aacebe
--- /dev/null
+++ b/apex/sdkext/derive_sdk/derive_sdk.cpp
@@ -0,0 +1,77 @@
+/*
+ * 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 "derive_sdk"
+
+#include <algorithm>
+#include <dirent.h>
+#include <iostream>
+#include <sys/stat.h>
+#include <vector>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+
+#include "frameworks/base/apex/sdkext/derive_sdk/sdk.pb.h"
+
+using com::android::sdkext::proto::SdkVersion;
+
+int main(int, char**) {
+    std::unique_ptr<DIR, decltype(&closedir)> apex(opendir("/apex"), closedir);
+    if (!apex) {
+        LOG(ERROR) << "Could not read /apex";
+        return EXIT_FAILURE;
+    }
+    struct dirent* de;
+    std::vector<std::string> paths;
+    while ((de = readdir(apex.get()))) {
+        std::string name = de->d_name;
+        if (name[0] == '.' || name.find('@') != std::string::npos) {
+            // Skip <name>@<ver> dirs, as they are bind-mounted to <name>
+            continue;
+        }
+        std::string path = "/apex/" + name + "/etc/sdkinfo.binarypb";
+        struct stat statbuf;
+        if (stat(path.c_str(), &statbuf) == 0) {
+            paths.push_back(path);
+        }
+    }
+
+    std::vector<int> versions;
+    for (const auto& path : paths) {
+        std::string contents;
+        if (!android::base::ReadFileToString(path, &contents, true)) {
+            LOG(ERROR) << "failed to read " << path;
+            continue;
+        }
+        SdkVersion sdk_version;
+        if (!sdk_version.ParseFromString(contents)) {
+            LOG(ERROR) << "failed to parse " << path;
+            continue;
+        }
+        versions.push_back(sdk_version.version());
+    }
+    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)) {
+        LOG(ERROR) << "failed to set sdk_info prop";
+        return EXIT_FAILURE;
+    }
+
+    return EXIT_SUCCESS;
+}
diff --git a/apex/sdkext/derive_sdk/derive_sdk.rc b/apex/sdkext/derive_sdk/derive_sdk.rc
new file mode 100644
index 0000000..1b66794
--- /dev/null
+++ b/apex/sdkext/derive_sdk/derive_sdk.rc
@@ -0,0 +1,3 @@
+service derive_sdk /apex/com.android.sdkext/bin/derive_sdk
+    oneshot
+    disabled
diff --git a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl b/apex/sdkext/derive_sdk/sdk.proto
similarity index 63%
copy from wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl
copy to apex/sdkext/derive_sdk/sdk.proto
index 007ec94..d15b935 100644
--- a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl
+++ b/apex/sdkext/derive_sdk/sdk.proto
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2014, The Android Open Source Project
+/*
+ * Copyright (C) 2019 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
- *     http://www.apache.org/licenses/LICENSE-2.0
+ *      http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
@@ -14,6 +14,12 @@
  * limitations under the License.
  */
 
-package android.net.wifi;
+syntax = "proto3";
+package com.android.sdkext.proto;
 
-parcelable WifiActivityEnergyInfo;
+option java_outer_classname = "SdkProto";
+option optimize_for = LITE_RUNTIME;
+
+message SdkVersion {
+  int32 version = 1;
+}
diff --git a/apex/sdkext/gen_sdkinfo.py b/apex/sdkext/gen_sdkinfo.py
new file mode 100644
index 0000000..5af478b
--- /dev/null
+++ b/apex/sdkext/gen_sdkinfo.py
@@ -0,0 +1,19 @@
+import sdk_pb2
+import sys
+
+if __name__ == '__main__':
+  argv = sys.argv[1:]
+  if not len(argv) == 4 or sorted([argv[0], argv[2]]) != ['-o', '-v']:
+    print('usage: gen_sdkinfo -v <version> -o <output-file>')
+    sys.exit(1)
+
+  for i in range(len(argv)):
+    if sys.argv[i] == '-o':
+      filename = sys.argv[i+1]
+    if sys.argv[i] == '-v':
+      version = int(sys.argv[i+1])
+
+  proto = sdk_pb2.SdkVersion()
+  proto.version = version
+  with open(filename, 'wb') as f:
+    f.write(proto.SerializeToString())
diff --git a/apex/sdkext/ld.config.txt b/apex/sdkext/ld.config.txt
new file mode 100644
index 0000000..b447068
--- /dev/null
+++ b/apex/sdkext/ld.config.txt
@@ -0,0 +1,31 @@
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Bionic loader config file for the sdkext apex.
+
+dir.sdkext = /apex/com.android.sdkext/bin/
+
+[sdkext]
+additional.namespaces = platform
+
+namespace.default.isolated = true
+namespace.default.links = platform
+namespace.default.link.platform.allow_all_shared_libs = true
+
+###############################################################################
+# "platform" namespace: used for NDK libraries
+###############################################################################
+namespace.platform.isolated = true
+namespace.platform.search.paths = /system/${LIB}
+namespace.platform.asan.search.paths = /data/asan/system/${LIB}
+
+# /system/lib/libc.so, etc are symlinks to /apex/com.android.lib/lib/bionic/libc.so, etc.
+# Add /apex/... path to the permitted paths because linker uses realpath(3)
+# to check the accessibility of the lib. We could add this to search.paths
+# instead but that makes the resolution of bionic libs be dependent on
+# the order of /system/lib and /apex/... in search.paths. If /apex/...
+# is after /system/lib, then /apex/... is never tried because libc.so
+# is always found in /system/lib but fails to pass the accessibility test
+# because of its realpath.  It's better to not depend on the ordering if
+# possible.
+namespace.platform.permitted.paths = /apex/com.android.runtime/${LIB}/bionic
+namespace.platform.asan.permitted.paths = /apex/com.android.runtime/${LIB}/bionic
diff --git a/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java b/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
index f11d4f8..f10db6c 100644
--- a/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
+++ b/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
@@ -64,8 +64,7 @@
 import android.net.Network;
 import android.net.NetworkRequest;
 import android.net.NetworkStats;
-import android.net.wifi.IWifiManager;
-import android.net.wifi.WifiActivityEnergyInfo;
+import android.net.wifi.WifiManager;
 import android.os.BatteryStats;
 import android.os.BatteryStatsInternal;
 import android.os.Binder;
@@ -98,6 +97,7 @@
 import android.os.Temperature;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.os.connectivity.WifiActivityEnergyInfo;
 import android.os.storage.DiskInfo;
 import android.os.storage.StorageManager;
 import android.os.storage.VolumeInfo;
@@ -170,6 +170,7 @@
 import java.util.Set;
 import java.util.UUID;
 import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Executor;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
 
@@ -342,7 +343,7 @@
 
     private final KernelWakelockReader mKernelWakelockReader = new KernelWakelockReader();
     private final KernelWakelockStats mTmpWakelockStats = new KernelWakelockStats();
-    private IWifiManager mWifiManager = null;
+    private WifiManager mWifiManager = null;
     private TelephonyManager mTelephony = null;
     @GuardedBy("sStatsdLock")
     private final HashSet<Long> mDeathTimeMillis = new HashSet<>();
@@ -1148,35 +1149,48 @@
     private void pullWifiActivityInfo(
             int tagId, long elapsedNanos, long wallClockNanos,
             List<StatsLogEventWrapper> pulledData) {
-        long token = Binder.clearCallingIdentity();
+        WifiManager wifiManager;
         synchronized (this) {
             if (mWifiManager == null) {
-                mWifiManager =
-                        IWifiManager.Stub.asInterface(
-                                ServiceManager.getService(Context.WIFI_SERVICE));
+                mWifiManager = mContext.getSystemService(WifiManager.class);
             }
+            wifiManager = mWifiManager;
         }
-        if (mWifiManager != null) {
-            try {
-                SynchronousResultReceiver wifiReceiver = new SynchronousResultReceiver("wifi");
-                mWifiManager.requestActivityInfo(wifiReceiver);
-                final WifiActivityEnergyInfo wifiInfo = awaitControllerInfo(wifiReceiver);
-                StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos,
-                        wallClockNanos);
-                e.writeLong(wifiInfo.getTimeSinceBootMillis());
-                e.writeInt(wifiInfo.getStackState());
-                e.writeLong(wifiInfo.getControllerTxDurationMillis());
-                e.writeLong(wifiInfo.getControllerRxDurationMillis());
-                e.writeLong(wifiInfo.getControllerIdleDurationMillis());
-                e.writeLong(wifiInfo.getControllerEnergyUsedMicroJoules());
-                pulledData.add(e);
-            } catch (RemoteException e) {
-                Slog.e(TAG,
-                        "Pulling wifiManager for wifi controller activity energy info has error",
-                        e);
-            } finally {
-                Binder.restoreCallingIdentity(token);
+        if (wifiManager == null) {
+            return;
+        }
+        long token = Binder.clearCallingIdentity();
+        try {
+            SynchronousResultReceiver wifiReceiver = new SynchronousResultReceiver("wifi");
+            wifiManager.getWifiActivityEnergyInfoAsync(
+                    new Executor() {
+                        @Override
+                        public void execute(Runnable runnable) {
+                            // run the listener on the binder thread, if it was run on the main
+                            // thread it would deadlock since we would be waiting on ourselves
+                            runnable.run();
+                        }
+                    },
+                    info -> {
+                        Bundle bundle = new Bundle();
+                        bundle.putParcelable(BatteryStats.RESULT_RECEIVER_CONTROLLER_KEY, info);
+                        wifiReceiver.send(0, bundle);
+                    }
+            );
+            final WifiActivityEnergyInfo wifiInfo = awaitControllerInfo(wifiReceiver);
+            if (wifiInfo == null) {
+                return;
             }
+            StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos);
+            e.writeLong(wifiInfo.getTimeSinceBootMillis());
+            e.writeInt(wifiInfo.getStackState());
+            e.writeLong(wifiInfo.getControllerTxDurationMillis());
+            e.writeLong(wifiInfo.getControllerRxDurationMillis());
+            e.writeLong(wifiInfo.getControllerIdleDurationMillis());
+            e.writeLong(wifiInfo.getControllerEnergyUsedMicroJoules());
+            pulledData.add(e);
+        } finally {
+            Binder.restoreCallingIdentity(token);
         }
     }
 
diff --git a/api/current.txt b/api/current.txt
index 91a253e..5bdb691 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -299,6 +299,7 @@
     field public static final int animateFirstView = 16843477; // 0x10102d5
     field public static final int animateLayoutChanges = 16843506; // 0x10102f2
     field public static final int animateOnClick = 16843356; // 0x101025c
+    field public static final int animatedImageDrawable = 16844298; // 0x101060a
     field public static final int animation = 16843213; // 0x10101cd
     field public static final int animationCache = 16842989; // 0x10100ed
     field public static final int animationDuration = 16843026; // 0x1010112
@@ -723,6 +724,7 @@
     field public static final int host = 16842792; // 0x1010028
     field public static final int hotSpotX = 16844055; // 0x1010517
     field public static final int hotSpotY = 16844056; // 0x1010518
+    field public static final int htmlDescription = 16844299; // 0x101060b
     field public static final int hyphenationFrequency = 16843998; // 0x10104de
     field public static final int icon = 16842754; // 0x1010002
     field @Deprecated public static final int iconPreview = 16843337; // 0x1010249
@@ -2926,6 +2928,7 @@
     method public int describeContents();
     method public static String feedbackTypeToString(int);
     method public static String flagToString(int);
+    method public int getAnimatedImageRes();
     method @Deprecated public boolean getCanRetrieveWindowContent();
     method public int getCapabilities();
     method @Deprecated public String getDescription();
@@ -2935,6 +2938,7 @@
     method public android.content.pm.ResolveInfo getResolveInfo();
     method public String getSettingsActivityName();
     method public String loadDescription(android.content.pm.PackageManager);
+    method @Nullable public String loadHtmlDescription(@NonNull android.content.pm.PackageManager);
     method public CharSequence loadSummary(android.content.pm.PackageManager);
     method public void setInteractiveUiTimeoutMillis(@IntRange(from=0) int);
     method public void setNonInteractiveUiTimeoutMillis(@IntRange(from=0) int);
@@ -5177,12 +5181,12 @@
     method public android.content.Intent getResultData();
   }
 
-  public abstract class IntentService extends android.app.Service {
-    ctor public IntentService(String);
-    method @Nullable public android.os.IBinder onBind(android.content.Intent);
-    method @WorkerThread protected abstract void onHandleIntent(@Nullable android.content.Intent);
-    method public void onStart(@Nullable android.content.Intent, int);
-    method public void setIntentRedelivery(boolean);
+  @Deprecated public abstract class IntentService extends android.app.Service {
+    ctor @Deprecated public IntentService(String);
+    method @Deprecated @Nullable public android.os.IBinder onBind(android.content.Intent);
+    method @Deprecated @WorkerThread protected abstract void onHandleIntent(@Nullable android.content.Intent);
+    method @Deprecated public void onStart(@Nullable android.content.Intent, int);
+    method @Deprecated public void setIntentRedelivery(boolean);
   }
 
   public class KeyguardManager {
@@ -11361,6 +11365,15 @@
     field public int version;
   }
 
+  public final class InstallSourceInfo implements android.os.Parcelable {
+    method public int describeContents();
+    method @Nullable public String getInitiatingPackageName();
+    method @Nullable public String getInstallingPackageName();
+    method @Nullable public String getOriginatingPackageName();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.InstallSourceInfo> CREATOR;
+  }
+
   public class InstrumentationInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable {
     ctor public InstrumentationInfo();
     ctor public InstrumentationInfo(android.content.pm.InstrumentationInfo);
@@ -11725,10 +11738,11 @@
     method public abstract int getComponentEnabledSetting(@NonNull android.content.ComponentName);
     method @NonNull public abstract android.graphics.drawable.Drawable getDefaultActivityIcon();
     method @Nullable public abstract android.graphics.drawable.Drawable getDrawable(@NonNull String, @DrawableRes int, @Nullable android.content.pm.ApplicationInfo);
+    method @NonNull public android.content.pm.InstallSourceInfo getInstallSourceInfo(@NonNull String) throws android.content.pm.PackageManager.NameNotFoundException;
     method @NonNull public abstract java.util.List<android.content.pm.ApplicationInfo> getInstalledApplications(int);
     method @NonNull public java.util.List<android.content.pm.ModuleInfo> getInstalledModules(int);
     method @NonNull public abstract java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int);
-    method @Nullable public abstract String getInstallerPackageName(@NonNull String);
+    method @Deprecated @Nullable public abstract String getInstallerPackageName(@NonNull String);
     method @NonNull public abstract byte[] getInstantAppCookie();
     method public abstract int getInstantAppCookieMaxBytes();
     method @NonNull public abstract android.content.pm.InstrumentationInfo getInstrumentationInfo(@NonNull android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException;
@@ -25060,6 +25074,7 @@
     method @NonNull public java.util.List<byte[]> getSecureStopIds();
     method @NonNull public java.util.List<byte[]> getSecureStops();
     method @android.media.MediaDrm.SecurityLevel public int getSecurityLevel(@NonNull byte[]);
+    method @NonNull public static java.util.List<java.util.UUID> getSupportedCryptoSchemes();
     method public static boolean isCryptoSchemeSupported(@NonNull java.util.UUID);
     method public static boolean isCryptoSchemeSupported(@NonNull java.util.UUID, @NonNull String);
     method public static boolean isCryptoSchemeSupported(@NonNull java.util.UUID, @NonNull String, @android.media.MediaDrm.SecurityLevel int);
@@ -25600,6 +25615,50 @@
     field public static final int MUXER_OUTPUT_WEBM = 1; // 0x1
   }
 
+  public final class MediaParser {
+    method public boolean advance(@NonNull android.media.MediaParser.SeekableInputReader) throws java.io.IOException, java.lang.InterruptedException;
+    method @NonNull public static android.media.MediaParser create(@NonNull android.media.MediaParser.OutputConsumer, @NonNull java.lang.String...);
+    method @NonNull public static android.media.MediaParser createByName(@NonNull String, @NonNull android.media.MediaParser.OutputConsumer);
+    method @Nullable public String getExtractorName();
+    method @NonNull public static java.util.List<java.lang.String> getExtractorNames(@NonNull android.media.MediaFormat);
+    method public void release();
+    method public void seek(@NonNull android.media.MediaParser.SeekPoint);
+  }
+
+  public static interface MediaParser.InputReader {
+    method public long getLength();
+    method public long getPosition();
+    method public int read(@NonNull byte[], int, int) throws java.io.IOException, java.lang.InterruptedException;
+  }
+
+  public static interface MediaParser.OutputConsumer {
+    method public void onFormat(int, @NonNull android.media.MediaFormat);
+    method public void onSampleCompleted(int, long, int, int, int, @Nullable android.media.MediaCodec.CryptoInfo);
+    method public void onSampleData(int, @NonNull android.media.MediaParser.InputReader) throws java.io.IOException, java.lang.InterruptedException;
+    method public void onSeekMap(@NonNull android.media.MediaParser.SeekMap);
+    method public void onTracksFound(int);
+  }
+
+  public static interface 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();
+    field public static final int UNKNOWN_DURATION = -2147483648; // 0x80000000
+  }
+
+  public static final class MediaParser.SeekPoint {
+    field @NonNull public static final android.media.MediaParser.SeekPoint START;
+    field public final long position;
+    field public final long timeUs;
+  }
+
+  public static interface MediaParser.SeekableInputReader extends android.media.MediaParser.InputReader {
+    method public void seekToPosition(long);
+  }
+
+  public static final class MediaParser.UnrecognizedInputFormatException extends java.io.IOException {
+  }
+
   public class MediaPlayer implements android.media.AudioRouting android.media.VolumeAutomation {
     ctor public MediaPlayer();
     method public void addOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener, android.os.Handler);
@@ -29264,12 +29323,14 @@
     method public static long getMobileRxPackets();
     method public static long getMobileTxBytes();
     method public static long getMobileTxPackets();
+    method public static long getRxPackets(@NonNull String);
     method public static int getThreadStatsTag();
     method public static int getThreadStatsUid();
     method public static long getTotalRxBytes();
     method public static long getTotalRxPackets();
     method public static long getTotalTxBytes();
     method public static long getTotalTxPackets();
+    method public static long getTxPackets(@NonNull String);
     method public static long getUidRxBytes(int);
     method public static long getUidRxPackets(int);
     method @Deprecated public static long getUidTcpRxBytes(int);
@@ -36873,6 +36934,7 @@
     method @WorkerThread public static boolean isBlocked(android.content.Context, String);
     method @WorkerThread public static int unblock(android.content.Context, String);
     field public static final String AUTHORITY = "com.android.blockednumber";
+    field public static final android.net.Uri AUTHORITY_URI;
   }
 
   public static class BlockedNumberContract.BlockedNumbers {
@@ -38612,15 +38674,13 @@
     ctor public MediaStore();
     method @Nullable public static android.net.Uri getDocumentUri(@NonNull android.content.Context, @NonNull android.net.Uri);
     method @NonNull public static java.util.Set<java.lang.String> getExternalVolumeNames(@NonNull android.content.Context);
-    method public static boolean getIncludePending(@NonNull android.net.Uri);
     method public static android.net.Uri getMediaScannerUri();
     method @Nullable public static android.net.Uri getMediaUri(@NonNull android.content.Context, @NonNull android.net.Uri);
     method public static boolean getRequireOriginal(@NonNull android.net.Uri);
     method @NonNull public static String getVersion(@NonNull android.content.Context);
     method @NonNull public static String getVersion(@NonNull android.content.Context, @NonNull String);
     method @NonNull public static String getVolumeName(@NonNull android.net.Uri);
-    method @NonNull public static android.net.Uri setIncludePending(@NonNull android.net.Uri);
-    method @NonNull public static android.net.Uri setIncludeTrashed(@NonNull android.net.Uri);
+    method @Deprecated @NonNull public static android.net.Uri setIncludePending(@NonNull android.net.Uri);
     method @NonNull public static android.net.Uri setRequireOriginal(@NonNull android.net.Uri);
     method public static void trash(@NonNull android.content.Context, @NonNull android.net.Uri);
     method public static void trash(@NonNull android.content.Context, @NonNull android.net.Uri, long);
@@ -38656,9 +38716,17 @@
     field public static final String INTENT_ACTION_TEXT_OPEN_FROM_SEARCH = "android.media.action.TEXT_OPEN_FROM_SEARCH";
     field public static final String INTENT_ACTION_VIDEO_CAMERA = "android.media.action.VIDEO_CAMERA";
     field public static final String INTENT_ACTION_VIDEO_PLAY_FROM_SEARCH = "android.media.action.VIDEO_PLAY_FROM_SEARCH";
+    field public static final int MATCH_DEFAULT = 0; // 0x0
+    field public static final int MATCH_EXCLUDE = 2; // 0x2
+    field public static final int MATCH_INCLUDE = 1; // 0x1
+    field public static final int MATCH_ONLY = 3; // 0x3
     field public static final String MEDIA_IGNORE_FILENAME = ".nomedia";
     field public static final String MEDIA_SCANNER_VOLUME = "volume";
     field public static final String META_DATA_STILL_IMAGE_CAMERA_PREWARM_SERVICE = "android.media.still_image_camera_preview_service";
+    field public static final String QUERY_ARG_MATCH_FAVORITE = "android:query-arg-match-favorite";
+    field public static final String QUERY_ARG_MATCH_PENDING = "android:query-arg-match-pending";
+    field public static final String QUERY_ARG_MATCH_TRASHED = "android:query-arg-match-trashed";
+    field public static final String QUERY_ARG_RELATED_URI = "android:query-arg-related-uri";
     field public static final String UNKNOWN_STRING = "<unknown>";
     field public static final String VOLUME_EXTERNAL = "external";
     field public static final String VOLUME_EXTERNAL_PRIMARY = "external_primary";
@@ -43481,6 +43549,7 @@
     method public int getCallProperties();
     method public String getCallerDisplayName();
     method public int getCallerDisplayNamePresentation();
+    method public int getCallerNumberVerificationStatus();
     method public final long getConnectTimeMillis();
     method public long getCreationTimeMillis();
     method public android.telecom.DisconnectCause getDisconnectCause();
@@ -43662,6 +43731,7 @@
     method public final android.telecom.CallAudioState getCallAudioState();
     method public final String getCallerDisplayName();
     method public final int getCallerDisplayNamePresentation();
+    method public int getCallerNumberVerificationStatus();
     method public final android.telecom.Conference getConference();
     method public final java.util.List<android.telecom.Conferenceable> getConferenceables();
     method public final int getConnectionCapabilities();
@@ -43713,6 +43783,7 @@
     method public final void setAudioModeIsVoip(boolean);
     method public final void setAudioRoute(int);
     method public final void setCallerDisplayName(String, int);
+    method public void setCallerNumberVerificationStatus(int);
     method public final void setConferenceableConnections(java.util.List<android.telecom.Connection>);
     method public final void setConferenceables(java.util.List<android.telecom.Conferenceable>);
     method public final void setConnectionCapabilities(int);
@@ -43732,6 +43803,27 @@
     method public final void setVideoProvider(android.telecom.Connection.VideoProvider);
     method public final void setVideoState(int);
     method public static String stateToString(int);
+    field public static final int AUDIO_CODEC_AMR = 1; // 0x1
+    field public static final int AUDIO_CODEC_AMR_WB = 2; // 0x2
+    field public static final int AUDIO_CODEC_EVRC = 4; // 0x4
+    field public static final int AUDIO_CODEC_EVRC_B = 5; // 0x5
+    field public static final int AUDIO_CODEC_EVRC_NW = 7; // 0x7
+    field public static final int AUDIO_CODEC_EVRC_WB = 6; // 0x6
+    field public static final int AUDIO_CODEC_EVS_FB = 20; // 0x14
+    field public static final int AUDIO_CODEC_EVS_NB = 17; // 0x11
+    field public static final int AUDIO_CODEC_EVS_SWB = 19; // 0x13
+    field public static final int AUDIO_CODEC_EVS_WB = 18; // 0x12
+    field public static final int AUDIO_CODEC_G711A = 13; // 0xd
+    field public static final int AUDIO_CODEC_G711AB = 15; // 0xf
+    field public static final int AUDIO_CODEC_G711U = 11; // 0xb
+    field public static final int AUDIO_CODEC_G722 = 14; // 0xe
+    field public static final int AUDIO_CODEC_G723 = 12; // 0xc
+    field public static final int AUDIO_CODEC_G729 = 16; // 0x10
+    field public static final int AUDIO_CODEC_GSM_EFR = 8; // 0x8
+    field public static final int AUDIO_CODEC_GSM_FR = 9; // 0x9
+    field public static final int AUDIO_CODEC_GSM_HR = 10; // 0xa
+    field public static final int AUDIO_CODEC_NONE = 0; // 0x0
+    field public static final int AUDIO_CODEC_QCELP13K = 3; // 0x3
     field public static final int CAPABILITY_CANNOT_DOWNGRADE_VIDEO_TO_AUDIO = 8388608; // 0x800000
     field public static final int CAPABILITY_CAN_PAUSE_VIDEO = 1048576; // 0x100000
     field public static final int CAPABILITY_CAN_PULL_CALL = 16777216; // 0x1000000
@@ -43765,6 +43857,7 @@
     field public static final String EVENT_RTT_AUDIO_INDICATION_CHANGED = "android.telecom.event.RTT_AUDIO_INDICATION_CHANGED";
     field public static final String EXTRA_ANSWERING_DROPS_FG_CALL = "android.telecom.extra.ANSWERING_DROPS_FG_CALL";
     field public static final String EXTRA_ANSWERING_DROPS_FG_CALL_APP_NAME = "android.telecom.extra.ANSWERING_DROPS_FG_CALL_APP_NAME";
+    field public static final String EXTRA_AUDIO_CODEC = "android.telecom.extra.AUDIO_CODEC";
     field public static final String EXTRA_CALL_SUBJECT = "android.telecom.extra.CALL_SUBJECT";
     field public static final String EXTRA_CHILD_ADDRESS = "android.telecom.extra.CHILD_ADDRESS";
     field public static final String EXTRA_IS_RTT_AUDIO_PRESENT = "android.telecom.extra.IS_RTT_AUDIO_PRESENT";
@@ -43786,6 +43879,9 @@
     field public static final int STATE_NEW = 1; // 0x1
     field public static final int STATE_PULLING_CALL = 7; // 0x7
     field public static final int STATE_RINGING = 2; // 0x2
+    field public static final int VERIFICATION_STATUS_FAILED = 2; // 0x2
+    field public static final int VERIFICATION_STATUS_NOT_VERIFIED = 0; // 0x0
+    field public static final int VERIFICATION_STATUS_PASSED = 1; // 0x1
   }
 
   public static final class Connection.RttModifyStatus {
@@ -44533,6 +44629,7 @@
     field public static final String KEY_DEFAULT_VM_NUMBER_STRING = "default_vm_number_string";
     field public static final String KEY_DIAL_STRING_REPLACE_STRING_ARRAY = "dial_string_replace_string_array";
     field public static final String KEY_DISABLE_CDMA_ACTIVATION_CODE_BOOL = "disable_cdma_activation_code_bool";
+    field public static final String KEY_DISABLE_SUPPLEMENTARY_SERVICES_IN_AIRPLANE_MODE_BOOL = "disable_supplementary_services_in_airplane_mode_bool";
     field public static final String KEY_DISCONNECT_CAUSE_PLAY_BUSYTONE_INT_ARRAY = "disconnect_cause_play_busytone_int_array";
     field public static final String KEY_DISPLAY_HD_AUDIO_PROPERTY_BOOL = "display_hd_audio_property_bool";
     field public static final String KEY_DROP_VIDEO_CALL_WHEN_ANSWERING_AUDIO_CALL_BOOL = "drop_video_call_when_answering_audio_call_bool";
@@ -45002,6 +45099,12 @@
     field public static final int SCAN_TYPE_PERIODIC = 1; // 0x1
   }
 
+  public final class PhoneCapability implements android.os.Parcelable {
+    method public int describeContents();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.PhoneCapability> CREATOR;
+  }
+
   public class PhoneNumberFormattingTextWatcher implements android.text.TextWatcher {
     ctor public PhoneNumberFormattingTextWatcher();
     ctor public PhoneNumberFormattingTextWatcher(String);
@@ -46738,30 +46841,30 @@
   public static class Layout.Directions {
   }
 
-  public abstract class LoginFilter implements android.text.InputFilter {
-    method public CharSequence filter(CharSequence, int, int, android.text.Spanned, int, int);
-    method public abstract boolean isAllowed(char);
-    method public void onInvalidCharacter(char);
-    method public void onStart();
-    method public void onStop();
+  @Deprecated public abstract class LoginFilter implements android.text.InputFilter {
+    method @Deprecated public CharSequence filter(CharSequence, int, int, android.text.Spanned, int, int);
+    method @Deprecated public abstract boolean isAllowed(char);
+    method @Deprecated public void onInvalidCharacter(char);
+    method @Deprecated public void onStart();
+    method @Deprecated public void onStop();
   }
 
-  public static class LoginFilter.PasswordFilterGMail extends android.text.LoginFilter {
-    ctor public LoginFilter.PasswordFilterGMail();
-    ctor public LoginFilter.PasswordFilterGMail(boolean);
-    method public boolean isAllowed(char);
+  @Deprecated public static class LoginFilter.PasswordFilterGMail extends android.text.LoginFilter {
+    ctor @Deprecated public LoginFilter.PasswordFilterGMail();
+    ctor @Deprecated public LoginFilter.PasswordFilterGMail(boolean);
+    method @Deprecated public boolean isAllowed(char);
   }
 
-  public static class LoginFilter.UsernameFilterGMail extends android.text.LoginFilter {
-    ctor public LoginFilter.UsernameFilterGMail();
-    ctor public LoginFilter.UsernameFilterGMail(boolean);
-    method public boolean isAllowed(char);
+  @Deprecated public static class LoginFilter.UsernameFilterGMail extends android.text.LoginFilter {
+    ctor @Deprecated public LoginFilter.UsernameFilterGMail();
+    ctor @Deprecated public LoginFilter.UsernameFilterGMail(boolean);
+    method @Deprecated public boolean isAllowed(char);
   }
 
-  public static class LoginFilter.UsernameFilterGeneric extends android.text.LoginFilter {
-    ctor public LoginFilter.UsernameFilterGeneric();
-    ctor public LoginFilter.UsernameFilterGeneric(boolean);
-    method public boolean isAllowed(char);
+  @Deprecated public static class LoginFilter.UsernameFilterGeneric extends android.text.LoginFilter {
+    ctor @Deprecated public LoginFilter.UsernameFilterGeneric();
+    ctor @Deprecated public LoginFilter.UsernameFilterGeneric(boolean);
+    method @Deprecated public boolean isAllowed(char);
   }
 
   public interface NoCopySpan {
diff --git a/api/removed.txt b/api/removed.txt
index e0e26f7..1a2f434 100644
--- a/api/removed.txt
+++ b/api/removed.txt
@@ -438,7 +438,9 @@
   public final class MediaStore {
     method @Deprecated @NonNull public static android.net.Uri createPending(@NonNull android.content.Context, @NonNull android.provider.MediaStore.PendingParams);
     method @Deprecated @NonNull public static java.util.Set<java.lang.String> getAllVolumeNames(@NonNull android.content.Context);
+    method @Deprecated public static boolean getIncludePending(@NonNull android.net.Uri);
     method @Deprecated @NonNull public static android.provider.MediaStore.PendingSession openPending(@NonNull android.content.Context, @NonNull android.net.Uri);
+    method @Deprecated @NonNull public static android.net.Uri setIncludeTrashed(@NonNull android.net.Uri);
   }
 
   public static interface MediaStore.Audio.AudioColumns extends android.provider.MediaStore.MediaColumns {
diff --git a/api/system-current.txt b/api/system-current.txt
index 8bb1983..c7bde93 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1198,6 +1198,7 @@
     field public static final int ACTION_DISMISS = 2; // 0x2
     field public static final int ACTION_LAUNCH = 1; // 0x1
     field public static final int ACTION_PIN = 3; // 0x3
+    field public static final int ACTION_UNPIN = 4; // 0x4
     field @NonNull public static final android.os.Parcelable.Creator<android.app.prediction.AppTargetEvent> CREATOR;
   }
 
@@ -1398,6 +1399,7 @@
   public final class BluetoothHeadset implements android.bluetooth.BluetoothProfile {
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean connect(android.bluetooth.BluetoothDevice);
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean disconnect(android.bluetooth.BluetoothDevice);
+    method @Nullable @RequiresPermission(android.Manifest.permission.BLUETOOTH) public android.bluetooth.BluetoothDevice getActiveDevice();
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public int getConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice);
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int);
     method @Deprecated @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean setPriority(android.bluetooth.BluetoothDevice, int);
@@ -1432,6 +1434,37 @@
     field @Deprecated public static final int PRIORITY_ON = 100; // 0x64
   }
 
+  public final class BluetoothUuid {
+    method public static boolean containsAnyUuid(@Nullable android.os.ParcelUuid[], @Nullable android.os.ParcelUuid[]);
+    method @NonNull public static android.os.ParcelUuid parseUuidFrom(@Nullable byte[]);
+    field @NonNull public static final android.os.ParcelUuid A2DP_SINK;
+    field @NonNull public static final android.os.ParcelUuid A2DP_SOURCE;
+    field @NonNull public static final android.os.ParcelUuid ADV_AUDIO_DIST;
+    field @NonNull public static final android.os.ParcelUuid AVRCP_CONTROLLER;
+    field @NonNull public static final android.os.ParcelUuid AVRCP_TARGET;
+    field @NonNull public static final android.os.ParcelUuid BASE_UUID;
+    field @NonNull public static final android.os.ParcelUuid BNEP;
+    field @NonNull public static final android.os.ParcelUuid HEARING_AID;
+    field @NonNull public static final android.os.ParcelUuid HFP;
+    field @NonNull public static final android.os.ParcelUuid HFP_AG;
+    field @NonNull public static final android.os.ParcelUuid HID;
+    field @NonNull public static final android.os.ParcelUuid HOGP;
+    field @NonNull public static final android.os.ParcelUuid HSP;
+    field @NonNull public static final android.os.ParcelUuid HSP_AG;
+    field @NonNull public static final android.os.ParcelUuid MAP;
+    field @NonNull public static final android.os.ParcelUuid MAS;
+    field @NonNull public static final android.os.ParcelUuid MNS;
+    field @NonNull public static final android.os.ParcelUuid NAP;
+    field @NonNull public static final android.os.ParcelUuid OBEX_OBJECT_PUSH;
+    field @NonNull public static final android.os.ParcelUuid PANU;
+    field @NonNull public static final android.os.ParcelUuid PBAP_PCE;
+    field @NonNull public static final android.os.ParcelUuid PBAP_PSE;
+    field @NonNull public static final android.os.ParcelUuid SAP;
+    field public static final int UUID_BYTES_128_BIT = 16; // 0x10
+    field public static final int UUID_BYTES_16_BIT = 2; // 0x2
+    field public static final int UUID_BYTES_32_BIT = 4; // 0x4
+  }
+
 }
 
 package android.bluetooth.le {
@@ -4487,6 +4520,7 @@
   public class Network implements android.os.Parcelable {
     ctor public Network(@NonNull android.net.Network);
     method @NonNull public android.net.Network getPrivateDnsBypassingCopy();
+    field public final int netId;
   }
 
   public final class NetworkCapabilities implements android.os.Parcelable {
@@ -4731,7 +4765,7 @@
     method @NonNull public java.util.List<android.net.ipsec.ike.IkeTrafficSelector> getOutboundTrafficSelectors();
   }
 
-  public abstract class ChildSessionOptions {
+  public abstract class ChildSessionParams {
   }
 
   public class IkeFqdnIdentification extends android.net.ipsec.ike.IkeIdentification {
@@ -4776,11 +4810,11 @@
   }
 
   public final class IkeSession implements java.lang.AutoCloseable {
-    ctor public IkeSession(@NonNull android.content.Context, @NonNull android.net.ipsec.ike.IkeSessionOptions, @NonNull android.net.ipsec.ike.ChildSessionOptions, @NonNull java.util.concurrent.Executor, @NonNull android.net.ipsec.ike.IkeSessionCallback, @NonNull android.net.ipsec.ike.ChildSessionCallback);
+    ctor public IkeSession(@NonNull android.content.Context, @NonNull android.net.ipsec.ike.IkeSessionParams, @NonNull android.net.ipsec.ike.ChildSessionParams, @NonNull java.util.concurrent.Executor, @NonNull android.net.ipsec.ike.IkeSessionCallback, @NonNull android.net.ipsec.ike.ChildSessionCallback);
     method public void close();
     method public void closeChildSession(@NonNull android.net.ipsec.ike.ChildSessionCallback);
     method public void kill();
-    method public void openChildSession(@NonNull android.net.ipsec.ike.ChildSessionOptions, @NonNull android.net.ipsec.ike.ChildSessionCallback);
+    method public void openChildSession(@NonNull android.net.ipsec.ike.ChildSessionParams, @NonNull android.net.ipsec.ike.ChildSessionCallback);
   }
 
   public interface IkeSessionCallback {
@@ -4798,21 +4832,21 @@
     field public static final int EXTENSION_TYPE_MOBIKE = 2; // 0x2
   }
 
-  public final class IkeSessionOptions {
+  public final class IkeSessionParams {
   }
 
-  public static final class IkeSessionOptions.Builder {
-    ctor public IkeSessionOptions.Builder();
-    method @NonNull public android.net.ipsec.ike.IkeSessionOptions.Builder addSaProposal(@NonNull android.net.ipsec.ike.IkeSaProposal);
-    method @NonNull public android.net.ipsec.ike.IkeSessionOptions build();
-    method @NonNull public android.net.ipsec.ike.IkeSessionOptions.Builder setAuthDigitalSignature(@NonNull java.security.cert.X509Certificate, @NonNull java.security.cert.X509Certificate, @NonNull java.security.PrivateKey);
-    method @NonNull public android.net.ipsec.ike.IkeSessionOptions.Builder setAuthDigitalSignature(@NonNull java.security.cert.X509Certificate, @NonNull java.security.cert.X509Certificate, @NonNull java.util.List<java.security.cert.X509Certificate>, @NonNull java.security.PrivateKey);
-    method @NonNull public android.net.ipsec.ike.IkeSessionOptions.Builder setAuthEap(@NonNull java.security.cert.X509Certificate, @NonNull android.net.eap.EapSessionConfig);
-    method @NonNull public android.net.ipsec.ike.IkeSessionOptions.Builder setAuthPsk(@NonNull byte[]);
-    method @NonNull public android.net.ipsec.ike.IkeSessionOptions.Builder setLocalIdentification(@NonNull android.net.ipsec.ike.IkeIdentification);
-    method @NonNull public android.net.ipsec.ike.IkeSessionOptions.Builder setRemoteIdentification(@NonNull android.net.ipsec.ike.IkeIdentification);
-    method @NonNull public android.net.ipsec.ike.IkeSessionOptions.Builder setServerAddress(@NonNull java.net.InetAddress);
-    method @NonNull public android.net.ipsec.ike.IkeSessionOptions.Builder setUdpEncapsulationSocket(@NonNull android.net.IpSecManager.UdpEncapsulationSocket);
+  public static final class IkeSessionParams.Builder {
+    ctor public IkeSessionParams.Builder();
+    method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder addSaProposal(@NonNull android.net.ipsec.ike.IkeSaProposal);
+    method @NonNull public android.net.ipsec.ike.IkeSessionParams build();
+    method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder setAuthDigitalSignature(@NonNull java.security.cert.X509Certificate, @NonNull java.security.cert.X509Certificate, @NonNull java.security.PrivateKey);
+    method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder setAuthDigitalSignature(@NonNull java.security.cert.X509Certificate, @NonNull java.security.cert.X509Certificate, @NonNull java.util.List<java.security.cert.X509Certificate>, @NonNull java.security.PrivateKey);
+    method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder setAuthEap(@NonNull java.security.cert.X509Certificate, @NonNull android.net.eap.EapSessionConfig);
+    method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder setAuthPsk(@NonNull byte[]);
+    method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder setLocalIdentification(@NonNull android.net.ipsec.ike.IkeIdentification);
+    method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder setRemoteIdentification(@NonNull android.net.ipsec.ike.IkeIdentification);
+    method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder setServerAddress(@NonNull java.net.InetAddress);
+    method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder setUdpEncapsulationSocket(@NonNull android.net.IpSecManager.UdpEncapsulationSocket);
   }
 
   public final class IkeTrafficSelector {
@@ -4849,33 +4883,33 @@
     field public static final int PSEUDORANDOM_FUNCTION_HMAC_SHA1 = 2; // 0x2
   }
 
-  public final class TransportModeChildSessionOptions extends android.net.ipsec.ike.ChildSessionOptions {
+  public final class TransportModeChildSessionParams extends android.net.ipsec.ike.ChildSessionParams {
   }
 
-  public static final class TransportModeChildSessionOptions.Builder {
-    ctor public TransportModeChildSessionOptions.Builder();
-    method @NonNull public android.net.ipsec.ike.TransportModeChildSessionOptions.Builder addInboundTrafficSelectors(@NonNull android.net.ipsec.ike.IkeTrafficSelector);
-    method @NonNull public android.net.ipsec.ike.TransportModeChildSessionOptions.Builder addOutboundTrafficSelectors(@NonNull android.net.ipsec.ike.IkeTrafficSelector);
-    method @NonNull public android.net.ipsec.ike.TransportModeChildSessionOptions.Builder addSaProposal(@NonNull android.net.ipsec.ike.ChildSaProposal);
-    method @NonNull public android.net.ipsec.ike.TransportModeChildSessionOptions build();
+  public static final class TransportModeChildSessionParams.Builder {
+    ctor public TransportModeChildSessionParams.Builder();
+    method @NonNull public android.net.ipsec.ike.TransportModeChildSessionParams.Builder addInboundTrafficSelectors(@NonNull android.net.ipsec.ike.IkeTrafficSelector);
+    method @NonNull public android.net.ipsec.ike.TransportModeChildSessionParams.Builder addOutboundTrafficSelectors(@NonNull android.net.ipsec.ike.IkeTrafficSelector);
+    method @NonNull public android.net.ipsec.ike.TransportModeChildSessionParams.Builder addSaProposal(@NonNull android.net.ipsec.ike.ChildSaProposal);
+    method @NonNull public android.net.ipsec.ike.TransportModeChildSessionParams build();
   }
 
-  public final class TunnelModeChildSessionOptions extends android.net.ipsec.ike.ChildSessionOptions {
+  public final class TunnelModeChildSessionParams extends android.net.ipsec.ike.ChildSessionParams {
   }
 
-  public static final class TunnelModeChildSessionOptions.Builder {
-    ctor public TunnelModeChildSessionOptions.Builder();
-    method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionOptions.Builder addInboundTrafficSelectors(@NonNull android.net.ipsec.ike.IkeTrafficSelector);
-    method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionOptions.Builder addInternalAddressRequest(int);
-    method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionOptions.Builder addInternalAddressRequest(@NonNull java.net.InetAddress, int);
-    method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionOptions.Builder addInternalDhcpServerRequest(int);
-    method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionOptions.Builder addInternalDhcpServerRequest(@NonNull java.net.InetAddress);
-    method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionOptions.Builder addInternalDnsServerRequest(int);
-    method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionOptions.Builder addInternalDnsServerRequest(@NonNull java.net.InetAddress);
-    method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionOptions.Builder addInternalSubnetRequest(int);
-    method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionOptions.Builder addOutboundTrafficSelectors(@NonNull android.net.ipsec.ike.IkeTrafficSelector);
-    method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionOptions.Builder addSaProposal(@NonNull android.net.ipsec.ike.ChildSaProposal);
-    method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionOptions build();
+  public static final class TunnelModeChildSessionParams.Builder {
+    ctor public TunnelModeChildSessionParams.Builder();
+    method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams.Builder addInboundTrafficSelectors(@NonNull android.net.ipsec.ike.IkeTrafficSelector);
+    method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams.Builder addInternalAddressRequest(int);
+    method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams.Builder addInternalAddressRequest(@NonNull java.net.InetAddress, int);
+    method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams.Builder addInternalDhcpServerRequest(int);
+    method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams.Builder addInternalDhcpServerRequest(@NonNull java.net.InetAddress);
+    method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams.Builder addInternalDnsServerRequest(int);
+    method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams.Builder addInternalDnsServerRequest(@NonNull java.net.InetAddress);
+    method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams.Builder addInternalSubnetRequest(int);
+    method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams.Builder addOutboundTrafficSelectors(@NonNull android.net.ipsec.ike.IkeTrafficSelector);
+    method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams.Builder addSaProposal(@NonNull android.net.ipsec.ike.ChildSaProposal);
+    method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams build();
   }
 
 }
@@ -5340,32 +5374,6 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.SoftApInfo> CREATOR;
   }
 
-  public final class WifiActivityEnergyInfo implements android.os.Parcelable {
-    ctor public WifiActivityEnergyInfo(long, int, long, long, long, long, long);
-    method public int describeContents();
-    method public long getControllerEnergyUsedMicroJoules();
-    method public long getControllerIdleDurationMillis();
-    method public long getControllerRxDurationMillis();
-    method public long getControllerScanDurationMillis();
-    method public long getControllerTxDurationMillis();
-    method public int getStackState();
-    method public long getTimeSinceBootMillis();
-    method public boolean isValid();
-    method public void setControllerEnergyUsedMicroJoules(long);
-    method public void setControllerIdleDurationMillis(long);
-    method public void setControllerRxDurationMillis(long);
-    method public void setControllerScanDurationMillis(long);
-    method public void setControllerTxDurationMillis(long);
-    method public void setStackState(int);
-    method public void setTimeSinceBootMillis(long);
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.WifiActivityEnergyInfo> CREATOR;
-    field public static final int STACK_STATE_INVALID = 0; // 0x0
-    field public static final int STACK_STATE_STATE_ACTIVE = 1; // 0x1
-    field public static final int STACK_STATE_STATE_IDLE = 3; // 0x3
-    field public static final int STACK_STATE_STATE_SCANNING = 2; // 0x2
-  }
-
   public final class WifiClient implements android.os.Parcelable {
     method public int describeContents();
     method @NonNull public android.net.MacAddress getMacAddress();
@@ -5502,6 +5510,7 @@
     method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.ACCESS_WIFI_STATE, android.Manifest.permission.READ_WIFI_CREDENTIAL}) public java.util.List<android.net.wifi.WifiConfiguration> getPrivilegedConfiguredNetworks();
     method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public android.net.wifi.SoftApConfiguration getSoftApConfiguration();
     method public int getVerboseLoggingLevel();
+    method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public void getWifiActivityEnergyInfoAsync(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.OnWifiActivityEnergyInfoListener);
     method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public android.net.wifi.WifiConfiguration getWifiApConfiguration();
     method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public int getWifiApState();
     method public boolean isApMacRandomizationSupported();
@@ -5605,6 +5614,10 @@
     method public default void select(@NonNull android.net.wifi.WifiConfiguration);
   }
 
+  public static interface WifiManager.OnWifiActivityEnergyInfoListener {
+    method public void onWifiActivityEnergyInfo(@Nullable android.os.connectivity.WifiActivityEnergyInfo);
+  }
+
   public static interface WifiManager.OnWifiUsabilityStatsListener {
     method public void onWifiUsabilityStats(int, boolean, @NonNull android.net.wifi.WifiUsabilityStatsEntry);
   }
@@ -5672,10 +5685,13 @@
     field public static final int SCAN_TYPE_HIGH_ACCURACY = 2; // 0x2
     field public static final int SCAN_TYPE_LOW_LATENCY = 0; // 0x0
     field public static final int SCAN_TYPE_LOW_POWER = 1; // 0x1
+    field public static final int WIFI_BAND_24_5_6_GHZ = 11; // 0xb
+    field public static final int WIFI_BAND_24_5_WITH_DFS_6_GHZ = 15; // 0xf
     field public static final int WIFI_BAND_24_GHZ = 1; // 0x1
     field public static final int WIFI_BAND_5_GHZ = 2; // 0x2
     field public static final int WIFI_BAND_5_GHZ_DFS_ONLY = 4; // 0x4
     field public static final int WIFI_BAND_5_GHZ_WITH_DFS = 6; // 0x6
+    field public static final int WIFI_BAND_6_GHZ = 8; // 0x8
     field public static final int WIFI_BAND_BOTH = 3; // 0x3
     field public static final int WIFI_BAND_BOTH_WITH_DFS = 7; // 0x7
     field public static final int WIFI_BAND_UNSPECIFIED = 0; // 0x0
@@ -6363,6 +6379,8 @@
   }
 
   public class PowerWhitelistManager {
+    method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public boolean addToWhitelist(@NonNull String);
+    method @RequiresPermission(android.Manifest.permission.DEVICE_POWER) public int addToWhitelist(@NonNull java.util.List<java.lang.String>);
     method @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public void whitelistAppTemporarily(@NonNull String, long);
     method @RequiresPermission(android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST) public long whitelistAppTemporarilyForEvent(@NonNull String, int, @NonNull String);
     field public static final int EVENT_MMS = 2; // 0x2
@@ -6604,6 +6622,32 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.os.connectivity.CellularBatteryStats> CREATOR;
   }
 
+  public final class WifiActivityEnergyInfo implements android.os.Parcelable {
+    ctor public WifiActivityEnergyInfo(long, int, long, long, long, long, long);
+    method public int describeContents();
+    method public long getControllerEnergyUsedMicroJoules();
+    method public long getControllerIdleDurationMillis();
+    method public long getControllerRxDurationMillis();
+    method public long getControllerScanDurationMillis();
+    method public long getControllerTxDurationMillis();
+    method public int getStackState();
+    method public long getTimeSinceBootMillis();
+    method public boolean isValid();
+    method public void setControllerEnergyUsedMicroJoules(long);
+    method public void setControllerIdleDurationMillis(long);
+    method public void setControllerRxDurationMillis(long);
+    method public void setControllerScanDurationMillis(long);
+    method public void setControllerTxDurationMillis(long);
+    method public void setStackState(int);
+    method public void setTimeSinceBootMillis(long);
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.os.connectivity.WifiActivityEnergyInfo> CREATOR;
+    field public static final int STACK_STATE_INVALID = 0; // 0x0
+    field public static final int STACK_STATE_STATE_ACTIVE = 1; // 0x1
+    field public static final int STACK_STATE_STATE_IDLE = 3; // 0x3
+    field public static final int STACK_STATE_STATE_SCANNING = 2; // 0x2
+  }
+
   public final class WifiBatteryStats implements android.os.Parcelable {
     method public int describeContents();
     method public long getEnergyConsumedMaMillis();
@@ -6708,6 +6752,8 @@
   public final class PermissionManager {
     method @IntRange(from=0) @RequiresPermission(android.Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY) public int getRuntimePermissionsVersion();
     method @NonNull public java.util.List<android.permission.PermissionManager.SplitPermissionInfo> getSplitPermissions();
+    method @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS) public void grantDefaultPermissionsToLuiApp(@NonNull String, @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
+    method @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS) public void revokeDefaultPermissionsFromLuiApps(@NonNull String[], @NonNull android.os.UserHandle, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
     method @RequiresPermission(android.Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY) public void setRuntimePermissionsVersion(@IntRange(from=0) int);
   }
 
@@ -6821,7 +6867,6 @@
 package android.provider {
 
   public class BlockedNumberContract {
-    field @NonNull public static final android.net.Uri AUTHORITY_URI;
     field public static final String METHOD_NOTIFY_EMERGENCY_CONTACT = "notify_emergency_contact";
     field public static final String METHOD_SHOULD_SYSTEM_BLOCK_NUMBER = "should_system_block_number";
     field public static final String RES_BLOCK_STATUS = "block_status";
@@ -7069,6 +7114,8 @@
     field public static final String DEVICE_DEMO_MODE = "device_demo_mode";
     field public static final String DEVICE_PROVISIONING_MOBILE_DATA_ENABLED = "device_provisioning_mobile_data";
     field public static final String EUICC_PROVISIONED = "euicc_provisioned";
+    field public static final String EUICC_SUPPORTED_COUNTRIES = "euicc_supported_countries";
+    field public static final String EUICC_UNSUPPORTED_COUNTRIES = "euicc_unsupported_countries";
     field public static final String INSTALL_CARRIER_APP_NOTIFICATION_PERSISTENT = "install_carrier_app_notification_persistent";
     field public static final String INSTALL_CARRIER_APP_NOTIFICATION_SLEEP_MILLIS = "install_carrier_app_notification_sleep_millis";
     field public static final String OTA_DISABLE_AUTOMATIC_UPDATE = "ota_disable_automatic_update";
@@ -8421,6 +8468,10 @@
     field public final double lng;
   }
 
+  public class CellBroadcastIntents {
+    method public static void sendOrderedBroadcastForBackgroundReceivers(@NonNull android.content.Context, @Nullable android.os.UserHandle, @NonNull android.content.Intent, @Nullable String, @Nullable String, @Nullable android.content.BroadcastReceiver, @Nullable android.os.Handler, int, @Nullable String, @Nullable android.os.Bundle);
+  }
+
   public abstract class CellBroadcastService extends android.app.Service {
     ctor public CellBroadcastService();
     method @CallSuper public android.os.IBinder onBind(@Nullable android.content.Intent);
@@ -9376,6 +9427,7 @@
     method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.ImsiEncryptionInfo getCarrierInfoForImsiEncryption(int);
     method public java.util.List<java.lang.String> getCarrierPackageNamesForIntent(android.content.Intent);
     method public java.util.List<java.lang.String> getCarrierPackageNamesForIntentAndPhone(android.content.Intent, int);
+    method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.List<java.lang.String> getCarrierPrivilegedPackagesForAllActiveSubscriptions();
     method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.CarrierRestrictionRules getCarrierRestrictionRules();
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String getCdmaMdn();
     method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public String getCdmaMdn(int);
diff --git a/api/test-current.txt b/api/test-current.txt
index 2d35736..3ce8c5e 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -578,6 +578,7 @@
     field public static final int ACTION_DISMISS = 2; // 0x2
     field public static final int ACTION_LAUNCH = 1; // 0x1
     field public static final int ACTION_PIN = 3; // 0x3
+    field public static final int ACTION_UNPIN = 4; // 0x4
     field @NonNull public static final android.os.Parcelable.Creator<android.app.prediction.AppTargetEvent> CREATOR;
   }
 
@@ -1795,7 +1796,6 @@
   }
 
   public class DeviceIdleManager {
-    method @RequiresPermission("android.permission.DEVICE_POWER") public int addPowerSaveWhitelistApps(@NonNull java.util.List<java.lang.String>);
     method @NonNull public String[] getSystemPowerWhitelist();
     method @NonNull public String[] getSystemPowerWhitelistExceptIdle();
   }
@@ -2044,6 +2044,8 @@
   }
 
   public class PowerWhitelistManager {
+    method @RequiresPermission("android.permission.DEVICE_POWER") public boolean addToWhitelist(@NonNull String);
+    method @RequiresPermission("android.permission.DEVICE_POWER") public int addToWhitelist(@NonNull java.util.List<java.lang.String>);
     method @RequiresPermission("android.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST") public void whitelistAppTemporarily(@NonNull String, long);
     method @RequiresPermission("android.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST") public long whitelistAppTemporarilyForEvent(@NonNull String, int, @NonNull String);
     field public static final int EVENT_MMS = 2; // 0x2
diff --git a/api/test-lint-baseline.txt b/api/test-lint-baseline.txt
index 3aa8187..a8c4db3 100644
--- a/api/test-lint-baseline.txt
+++ b/api/test-lint-baseline.txt
@@ -8,35 +8,35 @@
 ActionValue: android.location.Location#EXTRA_NO_GPS_LOCATION:
     
 ActionValue: android.telephony.ims.ImsCallProfile#EXTRA_ADDITIONAL_CALL_INFO:
-    Inconsistent extra value; expected `android.telephony.ims.extra.ADDITIONAL_CALL_INFO`, was `AdditionalCallInfo`
+    
 ActionValue: android.telephony.ims.ImsCallProfile#EXTRA_CALL_RAT_TYPE:
-    Inconsistent extra value; expected `android.telephony.ims.extra.CALL_RAT_TYPE`, was `CallRadioTech`
+    
 ActionValue: android.telephony.ims.ImsCallProfile#EXTRA_CHILD_NUMBER:
-    Inconsistent extra value; expected `android.telephony.ims.extra.CHILD_NUMBER`, was `ChildNum`
+    
 ActionValue: android.telephony.ims.ImsCallProfile#EXTRA_CNA:
-    Inconsistent extra value; expected `android.telephony.ims.extra.CNA`, was `cna`
+    
 ActionValue: android.telephony.ims.ImsCallProfile#EXTRA_CNAP:
-    Inconsistent extra value; expected `android.telephony.ims.extra.CNAP`, was `cnap`
+    
 ActionValue: android.telephony.ims.ImsCallProfile#EXTRA_CODEC:
-    Inconsistent extra value; expected `android.telephony.ims.extra.CODEC`, was `Codec`
+    
 ActionValue: android.telephony.ims.ImsCallProfile#EXTRA_DIALSTRING:
-    Inconsistent extra value; expected `android.telephony.ims.extra.DIALSTRING`, was `dialstring`
+    
 ActionValue: android.telephony.ims.ImsCallProfile#EXTRA_DISPLAY_TEXT:
-    Inconsistent extra value; expected `android.telephony.ims.extra.DISPLAY_TEXT`, was `DisplayText`
+    
 ActionValue: android.telephony.ims.ImsCallProfile#EXTRA_EMERGENCY_CALL:
-    Inconsistent extra value; expected `android.telephony.ims.extra.EMERGENCY_CALL`, was `e_call`
+    
 ActionValue: android.telephony.ims.ImsCallProfile#EXTRA_IS_CALL_PULL:
-    Inconsistent extra value; expected `android.telephony.ims.extra.IS_CALL_PULL`, was `CallPull`
+    
 ActionValue: android.telephony.ims.ImsCallProfile#EXTRA_OI:
-    Inconsistent extra value; expected `android.telephony.ims.extra.OI`, was `oi`
+    
 ActionValue: android.telephony.ims.ImsCallProfile#EXTRA_OIR:
-    Inconsistent extra value; expected `android.telephony.ims.extra.OIR`, was `oir`
+    
 ActionValue: android.telephony.ims.ImsCallProfile#EXTRA_REMOTE_URI:
-    Inconsistent extra value; expected `android.telephony.ims.extra.REMOTE_URI`, was `remote_uri`
+    
 ActionValue: android.telephony.ims.ImsCallProfile#EXTRA_USSD:
-    Inconsistent extra value; expected `android.telephony.ims.extra.USSD`, was `ussd`
+    
 ActionValue: android.telephony.ims.ImsReasonInfo#EXTRA_MSG_SERVICE_NOT_AUTHORIZED:
-    Inconsistent extra value; expected `android.telephony.ims.extra.MSG_SERVICE_NOT_AUTHORIZED`, was `Forbidden. Not Authorized for Service`
+    
 ActionValue: android.telephony.mbms.vendor.VendorUtils#ACTION_CLEANUP:
     
 ActionValue: android.telephony.mbms.vendor.VendorUtils#ACTION_DOWNLOAD_RESULT_INTERNAL:
@@ -100,13 +100,13 @@
 ArrayReturn: android.security.keystore.AttestationUtils#attestDeviceIds(android.content.Context, int[], byte[]):
     
 ArrayReturn: android.telephony.ims.ImsUtListener#onUtConfigurationCallBarringQueried(int, android.telephony.ims.ImsSsInfo[]) parameter #1:
-    Method parameter should be Collection<ImsSsInfo> (or subclass) instead of raw array; was `android.telephony.ims.ImsSsInfo[]`
+    
 ArrayReturn: android.telephony.ims.ImsUtListener#onUtConfigurationCallForwardQueried(int, android.telephony.ims.ImsCallForwardInfo[]) parameter #1:
-    Method parameter should be Collection<ImsCallForwardInfo> (or subclass) instead of raw array; was `android.telephony.ims.ImsCallForwardInfo[]`
+    
 ArrayReturn: android.telephony.ims.ImsUtListener#onUtConfigurationCallWaitingQueried(int, android.telephony.ims.ImsSsInfo[]) parameter #1:
-    Method parameter should be Collection<ImsSsInfo> (or subclass) instead of raw array; was `android.telephony.ims.ImsSsInfo[]`
+    
 ArrayReturn: android.telephony.ims.stub.ImsRegistrationImplBase#onSubscriberAssociatedUriChanged(android.net.Uri[]) parameter #0:
-    Method parameter should be Collection<Uri> (or subclass) instead of raw array; was `android.net.Uri[]`
+    
 ArrayReturn: android.view.FocusFinder#sort(android.view.View[], int, int, android.view.ViewGroup, boolean) parameter #0:
     
 ArrayReturn: android.view.contentcapture.ViewNode#getAutofillOptions():
@@ -268,7 +268,7 @@
 ConcreteCollection: android.service.autofill.UserData#getFieldClassificationAlgorithms():
     
 ConcreteCollection: android.telephony.ims.ImsConferenceState#mParticipants:
-    Field type is concrete collection (`java.util.HashMap`); must be higher-level interface
+    
 
 
 ContextFirst: android.os.VibrationEffect#get(android.net.Uri, android.content.Context) parameter #1:
@@ -338,9 +338,9 @@
 ExecutorRegistration: android.permission.PermissionControllerManager#getAppPermissions(String, android.permission.PermissionControllerManager.OnGetAppPermissionResultCallback, android.os.Handler):
     
 ExecutorRegistration: android.telephony.ims.stub.ImsCallSessionImplBase#setListener(android.telephony.ims.ImsCallSessionListener):
-    Registration methods should have overload that accepts delivery Executor: `setListener`
+    
 ExecutorRegistration: android.telephony.ims.stub.ImsUtImplBase#setListener(android.telephony.ims.ImsUtListener):
-    Registration methods should have overload that accepts delivery Executor: `setListener`
+    
 ExecutorRegistration: android.telephony.mbms.vendor.MbmsDownloadServiceBase#addProgressListener(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadProgressListener):
     
 ExecutorRegistration: android.telephony.mbms.vendor.MbmsDownloadServiceBase#addStatusListener(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStatusListener):
@@ -460,11 +460,13 @@
 
 
 InternalField: android.telephony.ims.ImsConferenceState#mParticipants:
-    Internal field mParticipants must not be exposed
+    
 
 
 KotlinOperator: android.os.WorkSource#get(int):
     
+KotlinOperator: android.util.SparseArrayMap#get(int, String):
+    Method can be invoked with an indexing operator from Kotlin: `get` (this is usually desirable; just make sure it makes sense for this type of object)
 
 
 ListenerInterface: android.media.audiopolicy.AudioPolicy.AudioPolicyFocusListener:
@@ -474,9 +476,9 @@
 ListenerInterface: android.os.IncidentManager.AuthListener:
     
 ListenerInterface: android.telephony.ims.ImsCallSessionListener:
-    Listeners should be an interface, or otherwise renamed Callback: ImsCallSessionListener
+    
 ListenerInterface: android.telephony.ims.ImsUtListener:
-    Listeners should be an interface, or otherwise renamed Callback: ImsUtListener
+    
 
 
 ListenerLast: android.hardware.camera2.CameraDevice#createCustomCaptureSession(android.hardware.camera2.params.InputConfiguration, java.util.List<android.hardware.camera2.params.OutputConfiguration>, int, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler) parameter #4:
@@ -496,17 +498,17 @@
 
 
 ManagerLookup: android.telephony.ims.ImsMmTelManager#createForSubscriptionId(int):
-    Managers must always be obtained from Context (`createForSubscriptionId`)
+    
 ManagerLookup: android.telephony.ims.ProvisioningManager#createForSubscriptionId(int):
-    Managers must always be obtained from Context (`createForSubscriptionId`)
+    
 
 
 MethodNameTense: android.telephony.ims.feature.CapabilityChangeRequest#getCapabilitiesToEnable():
-    Unexpected tense; probably meant `enabled`, was `getCapabilitiesToEnable`
+    
 
 
 MethodNameUnits: android.telephony.ims.ImsCallForwardInfo#getTimeSeconds():
-    Returned time values must be in milliseconds, was `getTimeSeconds`
+    
 
 
 MinMaxConstant: android.os.UserHandle#MIN_SECONDARY_USER_ID:
@@ -1460,7 +1462,7 @@
 MissingNullability: android.telecom.PhoneAccountSuggestionService#onBind(android.content.Intent) parameter #0:
     
 MissingNullability: android.telephony.CallQuality#writeToParcel(android.os.Parcel, int) parameter #0:
-    Missing nullability on parameter `dest` in method `writeToParcel`
+    
 MissingNullability: android.telephony.DataSpecificRegistrationInfo#writeToParcel(android.os.Parcel, int) parameter #0:
     
 MissingNullability: android.telephony.LteVopsSupportInfo#writeToParcel(android.os.Parcel, int) parameter #0:
@@ -1478,9 +1480,9 @@
 MissingNullability: android.telephony.TelephonyManager#checkCarrierPrivilegesForPackage(String) parameter #0:
     
 MissingNullability: android.telephony.TelephonyManager#getCarrierPackageNamesForIntent(android.content.Intent):
-    Missing nullability on method `getCarrierPackageNamesForIntent` return
+    
 MissingNullability: android.telephony.TelephonyManager#getCarrierPackageNamesForIntent(android.content.Intent) parameter #0:
-    Missing nullability on parameter `intent` in method `getCarrierPackageNamesForIntent`
+    
 MissingNullability: android.telephony.TelephonyManager#getLine1AlphaTag():
     
 MissingNullability: android.telephony.TelephonyManager#getRadioHalVersion():
@@ -1518,315 +1520,315 @@
 MissingNullability: android.telephony.TelephonyManager#setCarrierTestOverride(String, String, String, String, String, String, String, String, String) parameter #8:
     
 MissingNullability: android.telephony.ims.ImsCallForwardInfo#getNumber():
-    Missing nullability on method `getNumber` return
+    
 MissingNullability: android.telephony.ims.ImsCallForwardInfo#writeToParcel(android.os.Parcel, int) parameter #0:
-    Missing nullability on parameter `out` in method `writeToParcel`
+    
 MissingNullability: android.telephony.ims.ImsCallProfile#ImsCallProfile(int, int, android.os.Bundle, android.telephony.ims.ImsStreamMediaProfile) parameter #2:
-    Missing nullability on parameter `callExtras` in method `ImsCallProfile`
+    
 MissingNullability: android.telephony.ims.ImsCallProfile#ImsCallProfile(int, int, android.os.Bundle, android.telephony.ims.ImsStreamMediaProfile) parameter #3:
-    Missing nullability on parameter `mediaProfile` in method `ImsCallProfile`
+    
 MissingNullability: android.telephony.ims.ImsCallProfile#getCallExtra(String):
-    Missing nullability on method `getCallExtra` return
+    
 MissingNullability: android.telephony.ims.ImsCallProfile#getCallExtra(String) parameter #0:
-    Missing nullability on parameter `name` in method `getCallExtra`
+    
 MissingNullability: android.telephony.ims.ImsCallProfile#getCallExtra(String, String):
-    Missing nullability on method `getCallExtra` return
+    
 MissingNullability: android.telephony.ims.ImsCallProfile#getCallExtra(String, String) parameter #0:
-    Missing nullability on parameter `name` in method `getCallExtra`
+    
 MissingNullability: android.telephony.ims.ImsCallProfile#getCallExtra(String, String) parameter #1:
-    Missing nullability on parameter `defaultValue` in method `getCallExtra`
+    
 MissingNullability: android.telephony.ims.ImsCallProfile#getCallExtraBoolean(String) parameter #0:
-    Missing nullability on parameter `name` in method `getCallExtraBoolean`
+    
 MissingNullability: android.telephony.ims.ImsCallProfile#getCallExtraBoolean(String, boolean) parameter #0:
-    Missing nullability on parameter `name` in method `getCallExtraBoolean`
+    
 MissingNullability: android.telephony.ims.ImsCallProfile#getCallExtraInt(String) parameter #0:
-    Missing nullability on parameter `name` in method `getCallExtraInt`
+    
 MissingNullability: android.telephony.ims.ImsCallProfile#getCallExtraInt(String, int) parameter #0:
-    Missing nullability on parameter `name` in method `getCallExtraInt`
+    
 MissingNullability: android.telephony.ims.ImsCallProfile#getCallExtras():
-    Missing nullability on method `getCallExtras` return
+    
 MissingNullability: android.telephony.ims.ImsCallProfile#getMediaProfile():
-    Missing nullability on method `getMediaProfile` return
+    
 MissingNullability: android.telephony.ims.ImsCallProfile#getVideoStateFromImsCallProfile(android.telephony.ims.ImsCallProfile) parameter #0:
-    Missing nullability on parameter `callProfile` in method `getVideoStateFromImsCallProfile`
+    
 MissingNullability: android.telephony.ims.ImsCallProfile#setCallExtra(String, String) parameter #0:
-    Missing nullability on parameter `name` in method `setCallExtra`
+    
 MissingNullability: android.telephony.ims.ImsCallProfile#setCallExtra(String, String) parameter #1:
-    Missing nullability on parameter `value` in method `setCallExtra`
+    
 MissingNullability: android.telephony.ims.ImsCallProfile#setCallExtraBoolean(String, boolean) parameter #0:
-    Missing nullability on parameter `name` in method `setCallExtraBoolean`
+    
 MissingNullability: android.telephony.ims.ImsCallProfile#setCallExtraInt(String, int) parameter #0:
-    Missing nullability on parameter `name` in method `setCallExtraInt`
+    
 MissingNullability: android.telephony.ims.ImsCallProfile#updateCallExtras(android.telephony.ims.ImsCallProfile) parameter #0:
-    Missing nullability on parameter `profile` in method `updateCallExtras`
+    
 MissingNullability: android.telephony.ims.ImsCallProfile#updateCallType(android.telephony.ims.ImsCallProfile) parameter #0:
-    Missing nullability on parameter `profile` in method `updateCallType`
+    
 MissingNullability: android.telephony.ims.ImsCallProfile#updateMediaProfile(android.telephony.ims.ImsCallProfile) parameter #0:
-    Missing nullability on parameter `profile` in method `updateMediaProfile`
+    
 MissingNullability: android.telephony.ims.ImsCallProfile#writeToParcel(android.os.Parcel, int) parameter #0:
-    Missing nullability on parameter `out` in method `writeToParcel`
+    
 MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionConferenceExtendFailed(android.telephony.ims.ImsReasonInfo) parameter #0:
-    Missing nullability on parameter `reasonInfo` in method `callSessionConferenceExtendFailed`
+    
 MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionConferenceExtendReceived(android.telephony.ims.stub.ImsCallSessionImplBase, android.telephony.ims.ImsCallProfile) parameter #0:
-    Missing nullability on parameter `newSession` in method `callSessionConferenceExtendReceived`
+    
 MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionConferenceExtendReceived(android.telephony.ims.stub.ImsCallSessionImplBase, android.telephony.ims.ImsCallProfile) parameter #1:
-    Missing nullability on parameter `profile` in method `callSessionConferenceExtendReceived`
+    
 MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionConferenceExtended(android.telephony.ims.stub.ImsCallSessionImplBase, android.telephony.ims.ImsCallProfile) parameter #0:
-    Missing nullability on parameter `newSession` in method `callSessionConferenceExtended`
+    
 MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionConferenceExtended(android.telephony.ims.stub.ImsCallSessionImplBase, android.telephony.ims.ImsCallProfile) parameter #1:
-    Missing nullability on parameter `profile` in method `callSessionConferenceExtended`
+    
 MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionConferenceStateUpdated(android.telephony.ims.ImsConferenceState) parameter #0:
-    Missing nullability on parameter `state` in method `callSessionConferenceStateUpdated`
+    
 MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionHandover(int, int, android.telephony.ims.ImsReasonInfo) parameter #2:
-    Missing nullability on parameter `reasonInfo` in method `callSessionHandover`
+    
 MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionHandoverFailed(int, int, android.telephony.ims.ImsReasonInfo) parameter #2:
-    Missing nullability on parameter `reasonInfo` in method `callSessionHandoverFailed`
+    
 MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionHeld(android.telephony.ims.ImsCallProfile) parameter #0:
-    Missing nullability on parameter `profile` in method `callSessionHeld`
+    
 MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionHoldFailed(android.telephony.ims.ImsReasonInfo) parameter #0:
-    Missing nullability on parameter `reasonInfo` in method `callSessionHoldFailed`
+    
 MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionHoldReceived(android.telephony.ims.ImsCallProfile) parameter #0:
-    Missing nullability on parameter `profile` in method `callSessionHoldReceived`
+    
 MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionInitiated(android.telephony.ims.ImsCallProfile) parameter #0:
-    Missing nullability on parameter `profile` in method `callSessionInitiated`
+    
 MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionInitiatedFailed(android.telephony.ims.ImsReasonInfo) parameter #0:
-    Missing nullability on parameter `reasonInfo` in method `callSessionInitiatedFailed`
+    
 MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionInviteParticipantsRequestFailed(android.telephony.ims.ImsReasonInfo) parameter #0:
-    Missing nullability on parameter `reasonInfo` in method `callSessionInviteParticipantsRequestFailed`
+    
 MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionMergeComplete(android.telephony.ims.stub.ImsCallSessionImplBase) parameter #0:
-    Missing nullability on parameter `newSession` in method `callSessionMergeComplete`
+    
 MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionMergeFailed(android.telephony.ims.ImsReasonInfo) parameter #0:
-    Missing nullability on parameter `reasonInfo` in method `callSessionMergeFailed`
+    
 MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionMergeStarted(android.telephony.ims.stub.ImsCallSessionImplBase, android.telephony.ims.ImsCallProfile) parameter #0:
-    Missing nullability on parameter `newSession` in method `callSessionMergeStarted`
+    
 MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionMergeStarted(android.telephony.ims.stub.ImsCallSessionImplBase, android.telephony.ims.ImsCallProfile) parameter #1:
-    Missing nullability on parameter `profile` in method `callSessionMergeStarted`
+    
 MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionProgressing(android.telephony.ims.ImsStreamMediaProfile) parameter #0:
-    Missing nullability on parameter `profile` in method `callSessionProgressing`
+    
 MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionRemoveParticipantsRequestFailed(android.telephony.ims.ImsReasonInfo) parameter #0:
-    Missing nullability on parameter `reasonInfo` in method `callSessionRemoveParticipantsRequestFailed`
+    
 MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionResumeFailed(android.telephony.ims.ImsReasonInfo) parameter #0:
-    Missing nullability on parameter `reasonInfo` in method `callSessionResumeFailed`
+    
 MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionResumeReceived(android.telephony.ims.ImsCallProfile) parameter #0:
-    Missing nullability on parameter `profile` in method `callSessionResumeReceived`
+    
 MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionResumed(android.telephony.ims.ImsCallProfile) parameter #0:
-    Missing nullability on parameter `profile` in method `callSessionResumed`
+    
 MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionRttMessageReceived(String) parameter #0:
-    Missing nullability on parameter `rttMessage` in method `callSessionRttMessageReceived`
+    
 MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionRttModifyRequestReceived(android.telephony.ims.ImsCallProfile) parameter #0:
-    Missing nullability on parameter `callProfile` in method `callSessionRttModifyRequestReceived`
+    
 MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionSuppServiceReceived(android.telephony.ims.ImsSuppServiceNotification) parameter #0:
-    Missing nullability on parameter `suppSrvNotification` in method `callSessionSuppServiceReceived`
+    
 MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionTerminated(android.telephony.ims.ImsReasonInfo) parameter #0:
-    Missing nullability on parameter `reasonInfo` in method `callSessionTerminated`
+    
 MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionUpdateFailed(android.telephony.ims.ImsReasonInfo) parameter #0:
-    Missing nullability on parameter `reasonInfo` in method `callSessionUpdateFailed`
+    
 MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionUpdateReceived(android.telephony.ims.ImsCallProfile) parameter #0:
-    Missing nullability on parameter `profile` in method `callSessionUpdateReceived`
+    
 MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionUpdated(android.telephony.ims.ImsCallProfile) parameter #0:
-    Missing nullability on parameter `profile` in method `callSessionUpdated`
+    
 MissingNullability: android.telephony.ims.ImsCallSessionListener#callSessionUssdMessageReceived(int, String) parameter #1:
-    Missing nullability on parameter `ussdMessage` in method `callSessionUssdMessageReceived`
+    
 MissingNullability: android.telephony.ims.ImsConferenceState#getConnectionStateForStatus(String) parameter #0:
-    Missing nullability on parameter `status` in method `getConnectionStateForStatus`
+    
 MissingNullability: android.telephony.ims.ImsConferenceState#mParticipants:
-    Missing nullability on field `mParticipants` in class `class android.telephony.ims.ImsConferenceState`
+    
 MissingNullability: android.telephony.ims.ImsConferenceState#writeToParcel(android.os.Parcel, int) parameter #0:
-    Missing nullability on parameter `out` in method `writeToParcel`
+    
 MissingNullability: android.telephony.ims.ImsExternalCallState#writeToParcel(android.os.Parcel, int) parameter #0:
-    Missing nullability on parameter `out` in method `writeToParcel`
+    
 MissingNullability: android.telephony.ims.ImsReasonInfo#ImsReasonInfo(int, int, String) parameter #2:
-    Missing nullability on parameter `extraMessage` in method `ImsReasonInfo`
+    
 MissingNullability: android.telephony.ims.ImsReasonInfo#getExtraMessage():
-    Missing nullability on method `getExtraMessage` return
+    
 MissingNullability: android.telephony.ims.ImsReasonInfo#writeToParcel(android.os.Parcel, int) parameter #0:
-    Missing nullability on parameter `out` in method `writeToParcel`
+    
 MissingNullability: android.telephony.ims.ImsService#createMmTelFeature(int):
-    Missing nullability on method `createMmTelFeature` return
+    
 MissingNullability: android.telephony.ims.ImsService#createRcsFeature(int):
-    Missing nullability on method `createRcsFeature` return
+    
 MissingNullability: android.telephony.ims.ImsService#getConfig(int):
-    Missing nullability on method `getConfig` return
+    
 MissingNullability: android.telephony.ims.ImsService#getRegistration(int):
-    Missing nullability on method `getRegistration` return
+    
 MissingNullability: android.telephony.ims.ImsService#onUpdateSupportedImsFeatures(android.telephony.ims.stub.ImsFeatureConfiguration) parameter #0:
-    Missing nullability on parameter `c` in method `onUpdateSupportedImsFeatures`
+    
 MissingNullability: android.telephony.ims.ImsService#querySupportedImsFeatures():
-    Missing nullability on method `querySupportedImsFeatures` return
+    
 MissingNullability: android.telephony.ims.ImsSsData#writeToParcel(android.os.Parcel, int) parameter #0:
-    Missing nullability on parameter `out` in method `writeToParcel`
+    
 MissingNullability: android.telephony.ims.ImsSsInfo#writeToParcel(android.os.Parcel, int) parameter #0:
-    Missing nullability on parameter `out` in method `writeToParcel`
+    
 MissingNullability: android.telephony.ims.ImsStreamMediaProfile#copyFrom(android.telephony.ims.ImsStreamMediaProfile) parameter #0:
-    Missing nullability on parameter `profile` in method `copyFrom`
+    
 MissingNullability: android.telephony.ims.ImsStreamMediaProfile#writeToParcel(android.os.Parcel, int) parameter #0:
-    Missing nullability on parameter `out` in method `writeToParcel`
+    
 MissingNullability: android.telephony.ims.ImsSuppServiceNotification#ImsSuppServiceNotification(int, int, int, int, String, String[]) parameter #4:
-    Missing nullability on parameter `number` in method `ImsSuppServiceNotification`
+    
 MissingNullability: android.telephony.ims.ImsSuppServiceNotification#ImsSuppServiceNotification(int, int, int, int, String, String[]) parameter #5:
-    Missing nullability on parameter `history` in method `ImsSuppServiceNotification`
+    
 MissingNullability: android.telephony.ims.ImsSuppServiceNotification#history:
-    Missing nullability on field `history` in class `class android.telephony.ims.ImsSuppServiceNotification`
+    
 MissingNullability: android.telephony.ims.ImsSuppServiceNotification#number:
-    Missing nullability on field `number` in class `class android.telephony.ims.ImsSuppServiceNotification`
+    
 MissingNullability: android.telephony.ims.ImsSuppServiceNotification#writeToParcel(android.os.Parcel, int) parameter #0:
-    Missing nullability on parameter `out` in method `writeToParcel`
+    
 MissingNullability: android.telephony.ims.ImsUtListener#onSupplementaryServiceIndication(android.telephony.ims.ImsSsData) parameter #0:
-    Missing nullability on parameter `ssData` in method `onSupplementaryServiceIndication`
+    
 MissingNullability: android.telephony.ims.ImsUtListener#onUtConfigurationCallBarringQueried(int, android.telephony.ims.ImsSsInfo[]) parameter #1:
-    Missing nullability on parameter `cbInfo` in method `onUtConfigurationCallBarringQueried`
+    
 MissingNullability: android.telephony.ims.ImsUtListener#onUtConfigurationCallForwardQueried(int, android.telephony.ims.ImsCallForwardInfo[]) parameter #1:
-    Missing nullability on parameter `cfInfo` in method `onUtConfigurationCallForwardQueried`
+    
 MissingNullability: android.telephony.ims.ImsUtListener#onUtConfigurationCallWaitingQueried(int, android.telephony.ims.ImsSsInfo[]) parameter #1:
-    Missing nullability on parameter `cwInfo` in method `onUtConfigurationCallWaitingQueried`
+    
 MissingNullability: android.telephony.ims.ImsUtListener#onUtConfigurationQueried(int, android.os.Bundle) parameter #1:
-    Missing nullability on parameter `configuration` in method `onUtConfigurationQueried`
+    
 MissingNullability: android.telephony.ims.ImsUtListener#onUtConfigurationQueryFailed(int, android.telephony.ims.ImsReasonInfo) parameter #1:
-    Missing nullability on parameter `error` in method `onUtConfigurationQueryFailed`
+    
 MissingNullability: android.telephony.ims.ImsUtListener#onUtConfigurationUpdateFailed(int, android.telephony.ims.ImsReasonInfo) parameter #1:
-    Missing nullability on parameter `error` in method `onUtConfigurationUpdateFailed`
+    
 MissingNullability: android.telephony.ims.ImsVideoCallProvider#changeCameraCapabilities(android.telecom.VideoProfile.CameraCapabilities) parameter #0:
-    Missing nullability on parameter `CameraCapabilities` in method `changeCameraCapabilities`
+    
 MissingNullability: android.telephony.ims.ImsVideoCallProvider#onSendSessionModifyRequest(android.telecom.VideoProfile, android.telecom.VideoProfile) parameter #0:
-    Missing nullability on parameter `fromProfile` in method `onSendSessionModifyRequest`
+    
 MissingNullability: android.telephony.ims.ImsVideoCallProvider#onSendSessionModifyRequest(android.telecom.VideoProfile, android.telecom.VideoProfile) parameter #1:
-    Missing nullability on parameter `toProfile` in method `onSendSessionModifyRequest`
+    
 MissingNullability: android.telephony.ims.ImsVideoCallProvider#onSendSessionModifyResponse(android.telecom.VideoProfile) parameter #0:
-    Missing nullability on parameter `responseProfile` in method `onSendSessionModifyResponse`
+    
 MissingNullability: android.telephony.ims.ImsVideoCallProvider#onSetCamera(String) parameter #0:
-    Missing nullability on parameter `cameraId` in method `onSetCamera`
+    
 MissingNullability: android.telephony.ims.ImsVideoCallProvider#onSetCamera(String, int) parameter #0:
-    Missing nullability on parameter `cameraId` in method `onSetCamera`
+    
 MissingNullability: android.telephony.ims.ImsVideoCallProvider#onSetDisplaySurface(android.view.Surface) parameter #0:
-    Missing nullability on parameter `surface` in method `onSetDisplaySurface`
+    
 MissingNullability: android.telephony.ims.ImsVideoCallProvider#onSetPauseImage(android.net.Uri) parameter #0:
-    Missing nullability on parameter `uri` in method `onSetPauseImage`
+    
 MissingNullability: android.telephony.ims.ImsVideoCallProvider#onSetPreviewSurface(android.view.Surface) parameter #0:
-    Missing nullability on parameter `surface` in method `onSetPreviewSurface`
+    
 MissingNullability: android.telephony.ims.ImsVideoCallProvider#receiveSessionModifyRequest(android.telecom.VideoProfile) parameter #0:
-    Missing nullability on parameter `VideoProfile` in method `receiveSessionModifyRequest`
+    
 MissingNullability: android.telephony.ims.ImsVideoCallProvider#receiveSessionModifyResponse(int, android.telecom.VideoProfile, android.telecom.VideoProfile) parameter #1:
-    Missing nullability on parameter `requestedProfile` in method `receiveSessionModifyResponse`
+    
 MissingNullability: android.telephony.ims.ImsVideoCallProvider#receiveSessionModifyResponse(int, android.telecom.VideoProfile, android.telecom.VideoProfile) parameter #2:
-    Missing nullability on parameter `responseProfile` in method `receiveSessionModifyResponse`
+    
 MissingNullability: android.telephony.ims.feature.CapabilityChangeRequest#getCapabilitiesToDisable():
-    Missing nullability on method `getCapabilitiesToDisable` return
+    
 MissingNullability: android.telephony.ims.feature.CapabilityChangeRequest#getCapabilitiesToEnable():
-    Missing nullability on method `getCapabilitiesToEnable` return
+    
 MissingNullability: android.telephony.ims.feature.CapabilityChangeRequest#writeToParcel(android.os.Parcel, int) parameter #0:
-    Missing nullability on parameter `dest` in method `writeToParcel`
+    
 MissingNullability: android.telephony.ims.feature.ImsFeature#changeEnabledCapabilities(android.telephony.ims.feature.CapabilityChangeRequest, android.telephony.ims.feature.ImsFeature.CapabilityCallbackProxy) parameter #0:
-    Missing nullability on parameter `request` in method `changeEnabledCapabilities`
+    
 MissingNullability: android.telephony.ims.feature.ImsFeature#changeEnabledCapabilities(android.telephony.ims.feature.CapabilityChangeRequest, android.telephony.ims.feature.ImsFeature.CapabilityCallbackProxy) parameter #1:
-    Missing nullability on parameter `c` in method `changeEnabledCapabilities`
+    
 MissingNullability: android.telephony.ims.feature.MmTelFeature#queryCapabilityStatus():
-    Missing nullability on method `queryCapabilityStatus` return
+    
 MissingNullability: android.telephony.ims.feature.MmTelFeature.MmTelCapabilities#MmTelCapabilities(android.telephony.ims.feature.ImsFeature.Capabilities) parameter #0:
-    Missing nullability on parameter `c` in method `MmTelCapabilities`
+    
 MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#accept(int, android.telephony.ims.ImsStreamMediaProfile) parameter #1:
-    Missing nullability on parameter `profile` in method `accept`
+    
 MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#deflect(String) parameter #0:
-    Missing nullability on parameter `deflectNumber` in method `deflect`
+    
 MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#extendToConference(String[]) parameter #0:
-    Missing nullability on parameter `participants` in method `extendToConference`
+    
 MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#getCallId():
-    Missing nullability on method `getCallId` return
+    
 MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#getCallProfile():
-    Missing nullability on method `getCallProfile` return
+    
 MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#getImsVideoCallProvider():
-    Missing nullability on method `getImsVideoCallProvider` return
+    
 MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#getLocalCallProfile():
-    Missing nullability on method `getLocalCallProfile` return
+    
 MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#getProperty(String):
-    Missing nullability on method `getProperty` return
+    
 MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#getProperty(String) parameter #0:
-    Missing nullability on parameter `name` in method `getProperty`
+    
 MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#getRemoteCallProfile():
-    Missing nullability on method `getRemoteCallProfile` return
+    
 MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#hold(android.telephony.ims.ImsStreamMediaProfile) parameter #0:
-    Missing nullability on parameter `profile` in method `hold`
+    
 MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#inviteParticipants(String[]) parameter #0:
-    Missing nullability on parameter `participants` in method `inviteParticipants`
+    
 MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#removeParticipants(String[]) parameter #0:
-    Missing nullability on parameter `participants` in method `removeParticipants`
+    
 MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#resume(android.telephony.ims.ImsStreamMediaProfile) parameter #0:
-    Missing nullability on parameter `profile` in method `resume`
+    
 MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#sendDtmf(char, android.os.Message) parameter #1:
-    Missing nullability on parameter `result` in method `sendDtmf`
+    
 MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#sendRttMessage(String) parameter #0:
-    Missing nullability on parameter `rttMessage` in method `sendRttMessage`
+    
 MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#sendRttModifyRequest(android.telephony.ims.ImsCallProfile) parameter #0:
-    Missing nullability on parameter `toProfile` in method `sendRttModifyRequest`
+    
 MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#sendUssd(String) parameter #0:
-    Missing nullability on parameter `ussdMessage` in method `sendUssd`
+    
 MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#setListener(android.telephony.ims.ImsCallSessionListener) parameter #0:
-    Missing nullability on parameter `listener` in method `setListener`
+    
 MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#start(String, android.telephony.ims.ImsCallProfile) parameter #0:
-    Missing nullability on parameter `callee` in method `start`
+    
 MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#start(String, android.telephony.ims.ImsCallProfile) parameter #1:
-    Missing nullability on parameter `profile` in method `start`
+    
 MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#startConference(String[], android.telephony.ims.ImsCallProfile) parameter #0:
-    Missing nullability on parameter `participants` in method `startConference`
+    
 MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#startConference(String[], android.telephony.ims.ImsCallProfile) parameter #1:
-    Missing nullability on parameter `profile` in method `startConference`
+    
 MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase#update(int, android.telephony.ims.ImsStreamMediaProfile) parameter #1:
-    Missing nullability on parameter `profile` in method `update`
+    
 MissingNullability: android.telephony.ims.stub.ImsCallSessionImplBase.State#toString(int):
-    Missing nullability on method `toString` return
+    
 MissingNullability: android.telephony.ims.stub.ImsConfigImplBase#getConfigString(int):
-    Missing nullability on method `getConfigString` return
+    
 MissingNullability: android.telephony.ims.stub.ImsConfigImplBase#notifyProvisionedValueChanged(int, String) parameter #1:
-    Missing nullability on parameter `value` in method `notifyProvisionedValueChanged`
+    
 MissingNullability: android.telephony.ims.stub.ImsConfigImplBase#setConfig(int, String) parameter #1:
-    Missing nullability on parameter `value` in method `setConfig`
+    
 MissingNullability: android.telephony.ims.stub.ImsFeatureConfiguration#getServiceFeatures():
-    Missing nullability on method `getServiceFeatures` return
+    
 MissingNullability: android.telephony.ims.stub.ImsFeatureConfiguration#writeToParcel(android.os.Parcel, int) parameter #0:
-    Missing nullability on parameter `dest` in method `writeToParcel`
+    
 MissingNullability: android.telephony.ims.stub.ImsFeatureConfiguration.Builder#addFeature(int, int):
-    Missing nullability on method `addFeature` return
+    
 MissingNullability: android.telephony.ims.stub.ImsFeatureConfiguration.Builder#build():
-    Missing nullability on method `build` return
+    
 MissingNullability: android.telephony.ims.stub.ImsMultiEndpointImplBase#onImsExternalCallStateUpdate(java.util.List<android.telephony.ims.ImsExternalCallState>) parameter #0:
-    Missing nullability on parameter `externalCallDialogs` in method `onImsExternalCallStateUpdate`
+    
 MissingNullability: android.telephony.ims.stub.ImsRegistrationImplBase#onDeregistered(android.telephony.ims.ImsReasonInfo) parameter #0:
-    Missing nullability on parameter `info` in method `onDeregistered`
+    
 MissingNullability: android.telephony.ims.stub.ImsRegistrationImplBase#onSubscriberAssociatedUriChanged(android.net.Uri[]) parameter #0:
-    Missing nullability on parameter `uris` in method `onSubscriberAssociatedUriChanged`
+    
 MissingNullability: android.telephony.ims.stub.ImsRegistrationImplBase#onTechnologyChangeFailed(int, android.telephony.ims.ImsReasonInfo) parameter #1:
-    Missing nullability on parameter `info` in method `onTechnologyChangeFailed`
+    
 MissingNullability: android.telephony.ims.stub.ImsSmsImplBase#getSmsFormat():
-    Missing nullability on method `getSmsFormat` return
+    
 MissingNullability: android.telephony.ims.stub.ImsSmsImplBase#onSmsReceived(int, String, byte[]) parameter #1:
-    Missing nullability on parameter `format` in method `onSmsReceived`
+    
 MissingNullability: android.telephony.ims.stub.ImsSmsImplBase#onSmsReceived(int, String, byte[]) parameter #2:
-    Missing nullability on parameter `pdu` in method `onSmsReceived`
+    
 MissingNullability: android.telephony.ims.stub.ImsSmsImplBase#onSmsStatusReportReceived(int, String, byte[]) parameter #1:
-    Missing nullability on parameter `format` in method `onSmsStatusReportReceived`
+    
 MissingNullability: android.telephony.ims.stub.ImsSmsImplBase#onSmsStatusReportReceived(int, String, byte[]) parameter #2:
-    Missing nullability on parameter `pdu` in method `onSmsStatusReportReceived`
+    
 MissingNullability: android.telephony.ims.stub.ImsSmsImplBase#onSmsStatusReportReceived(int, int, String, byte[]) parameter #2:
-    Missing nullability on parameter `format` in method `onSmsStatusReportReceived`
+    
 MissingNullability: android.telephony.ims.stub.ImsSmsImplBase#onSmsStatusReportReceived(int, int, String, byte[]) parameter #3:
-    Missing nullability on parameter `pdu` in method `onSmsStatusReportReceived`
+    
 MissingNullability: android.telephony.ims.stub.ImsSmsImplBase#sendSms(int, int, String, String, boolean, byte[]) parameter #2:
-    Missing nullability on parameter `format` in method `sendSms`
+    
 MissingNullability: android.telephony.ims.stub.ImsSmsImplBase#sendSms(int, int, String, String, boolean, byte[]) parameter #3:
-    Missing nullability on parameter `smsc` in method `sendSms`
+    
 MissingNullability: android.telephony.ims.stub.ImsSmsImplBase#sendSms(int, int, String, String, boolean, byte[]) parameter #5:
-    Missing nullability on parameter `pdu` in method `sendSms`
+    
 MissingNullability: android.telephony.ims.stub.ImsUtImplBase#queryCallForward(int, String) parameter #1:
-    Missing nullability on parameter `number` in method `queryCallForward`
+    
 MissingNullability: android.telephony.ims.stub.ImsUtImplBase#setListener(android.telephony.ims.ImsUtListener) parameter #0:
-    Missing nullability on parameter `listener` in method `setListener`
+    
 MissingNullability: android.telephony.ims.stub.ImsUtImplBase#transact(android.os.Bundle) parameter #0:
-    Missing nullability on parameter `ssInfo` in method `transact`
+    
 MissingNullability: android.telephony.ims.stub.ImsUtImplBase#updateCallBarring(int, int, String[]) parameter #2:
-    Missing nullability on parameter `barrList` in method `updateCallBarring`
+    
 MissingNullability: android.telephony.ims.stub.ImsUtImplBase#updateCallBarringForServiceClass(int, int, String[], int) parameter #2:
-    Missing nullability on parameter `barrList` in method `updateCallBarringForServiceClass`
+    
 MissingNullability: android.telephony.ims.stub.ImsUtImplBase#updateCallForward(int, int, String, int, int) parameter #2:
-    Missing nullability on parameter `number` in method `updateCallForward`
+    
 MissingNullability: android.telephony.mbms.DownloadRequest.Builder#setServiceId(String):
     
 MissingNullability: android.telephony.mbms.DownloadRequest.Builder#setServiceId(String) parameter #0:
@@ -2278,7 +2280,7 @@
 NotCloseable: android.os.HwParcel:
     
 NotCloseable: android.telephony.ims.stub.ImsUtImplBase:
-    Classes that release resources (close()) should implement AutoClosable and CloseGuard: class android.telephony.ims.stub.ImsUtImplBase
+    
 
 
 OnNameExpected: android.service.autofill.augmented.AugmentedAutofillService#dump(java.io.PrintWriter, String[]):
@@ -2292,21 +2294,21 @@
 OnNameExpected: android.service.quicksettings.TileService#isQuickSettingsSupported():
     
 OnNameExpected: android.telephony.ims.ImsService#createMmTelFeature(int):
-    If implemented by developer, should follow the on<Something> style; otherwise consider marking final
+    
 OnNameExpected: android.telephony.ims.ImsService#createRcsFeature(int):
-    If implemented by developer, should follow the on<Something> style; otherwise consider marking final
+    
 OnNameExpected: android.telephony.ims.ImsService#disableIms(int):
-    If implemented by developer, should follow the on<Something> style; otherwise consider marking final
+    
 OnNameExpected: android.telephony.ims.ImsService#enableIms(int):
-    If implemented by developer, should follow the on<Something> style; otherwise consider marking final
+    
 OnNameExpected: android.telephony.ims.ImsService#getConfig(int):
-    If implemented by developer, should follow the on<Something> style; otherwise consider marking final
+    
 OnNameExpected: android.telephony.ims.ImsService#getRegistration(int):
-    If implemented by developer, should follow the on<Something> style; otherwise consider marking final
+    
 OnNameExpected: android.telephony.ims.ImsService#querySupportedImsFeatures():
-    If implemented by developer, should follow the on<Something> style; otherwise consider marking final
+    
 OnNameExpected: android.telephony.ims.ImsService#readyForFeatureCreation():
-    If implemented by developer, should follow the on<Something> style; otherwise consider marking final
+    
 OnNameExpected: android.telephony.mbms.vendor.MbmsGroupCallServiceBase#dispose(int):
     
 OnNameExpected: android.telephony.mbms.vendor.MbmsGroupCallServiceBase#initialize(android.telephony.mbms.MbmsGroupCallSessionCallback, int):
@@ -2446,7 +2448,7 @@
 RethrowRemoteException: android.os.IHwBinder#transact(int, android.os.HwParcel, android.os.HwParcel, int):
     
 RethrowRemoteException: android.telephony.ims.ImsService#onUpdateSupportedImsFeatures(android.telephony.ims.stub.ImsFeatureConfiguration):
-    Methods calling system APIs should rethrow `RemoteException` as `RuntimeException` (but do not list it in the throws clause)
+    
 RethrowRemoteException: android.telephony.mbms.vendor.MbmsDownloadServiceBase#addProgressListener(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadProgressListener):
     
 RethrowRemoteException: android.telephony.mbms.vendor.MbmsDownloadServiceBase#addStatusListener(android.telephony.mbms.DownloadRequest, android.telephony.mbms.DownloadStatusListener):
@@ -2526,7 +2528,7 @@
 SamShouldBeLast: android.service.autofill.InternalTransformation#batchApply(android.service.autofill.ValueFinder, android.widget.RemoteViews, java.util.ArrayList<android.util.Pair<java.lang.Integer,android.service.autofill.InternalTransformation>>):
     
 SamShouldBeLast: android.telephony.ims.ImsMmTelManager#getFeatureState(java.util.function.Consumer<java.lang.Integer>, java.util.concurrent.Executor):
-    SAM-compatible parameters (such as parameter 1, "callback", in android.telephony.ims.ImsMmTelManager.getFeatureState) should be last to improve Kotlin interoperability; see https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions
+    
 SamShouldBeLast: android.view.Choreographer#postCallback(int, Runnable, Object):
     
 SamShouldBeLast: android.view.Choreographer#postCallbackDelayed(int, Runnable, Object, long):
@@ -2599,6 +2601,8 @@
     
 UserHandle: android.app.role.RoleManager#removeRoleHolderAsUser(String, String, int, android.os.UserHandle, java.util.concurrent.Executor, java.util.function.Consumer<java.lang.Boolean>):
     
+UserHandle: android.companion.CompanionDeviceManager#isDeviceAssociated(String, android.net.MacAddress, android.os.UserHandle):
+    When a method overload is needed to target a specific UserHandle, callers should be directed to use Context.createPackageContextAsUser() and re-obtain the relevant Manager, and no new API should be added
 UserHandle: android.content.pm.PackageManager#getInstallReason(String, android.os.UserHandle):
     
 UserHandle: android.content.pm.PackageManager#getPermissionFlags(String, String, android.os.UserHandle):
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index 22e1d01..bdb8380 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -175,7 +175,11 @@
             } else if (opt.equals("--no-hidden-api-checks")) {
                 instrument.disableHiddenApiChecks = true;
             } else if (opt.equals("--no-test-api-checks")) {
-                instrument.disableTestApiChecks = true;
+                // TODO(satayev): remove this option, only kept for backwards compatibility with
+                // cached tradefed instance
+                instrument.disableTestApiChecks = false;
+            } else if (opt.equals("--no-test-api-access")) {
+                instrument.disableTestApiChecks = false;
             } else if (opt.equals("--no-isolated-storage")) {
                 instrument.disableIsolatedStorage = true;
             } else if (opt.equals("--user")) {
diff --git a/cmds/am/src/com/android/commands/am/Instrument.java b/cmds/am/src/com/android/commands/am/Instrument.java
index 6afd7c4..2adbc1f 100644
--- a/cmds/am/src/com/android/commands/am/Instrument.java
+++ b/cmds/am/src/com/android/commands/am/Instrument.java
@@ -86,7 +86,7 @@
     String logPath = null;
     public boolean noWindowAnimation = false;
     public boolean disableHiddenApiChecks = false;
-    public boolean disableTestApiChecks = false;
+    public boolean disableTestApiChecks = true;
     public boolean disableIsolatedStorage = false;
     public String abi = null;
     public int userId = UserHandle.USER_CURRENT;
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index 1fd3abf..e8c0e15 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -1275,7 +1275,7 @@
     // Permission check not necessary as it's meant for applications to write to
     // statsd.
     android::util::stats_write(util::APP_BREADCRUMB_REPORTED,
-                               IPCThreadState::self()->getCallingUid(), label,
+                               (int32_t) IPCThreadState::self()->getCallingUid(), label,
                                state);
     return Status::ok();
 }
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index b9051da..2efb789 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -50,6 +50,7 @@
 import "frameworks/base/core/proto/android/stats/launcher/launcher.proto";
 import "frameworks/base/core/proto/android/stats/location/location_enums.proto";
 import "frameworks/base/core/proto/android/stats/mediametrics/mediametrics.proto";
+import "frameworks/base/core/proto/android/stats/mediaprovider/mediaprovider_enums.proto";
 import "frameworks/base/core/proto/android/stats/storage/storage_enums.proto";
 import "frameworks/base/core/proto/android/stats/style/style_enums.proto";
 import "frameworks/base/core/proto/android/telecomm/enums.proto";
@@ -342,6 +343,16 @@
         VmsClientConnectionStateChanged vms_client_connection_state_changed = 230;
         GpsLocationStatusReported gps_location_status_reported = 231;
         GpsTimeToFirstFixReported gps_time_to_first_fix_reported = 232;
+        MediaProviderScanEvent media_provider_scan_event =
+            233 [(log_from_module) = "mediaprovider"];
+        MediaProviderDeletionEvent media_provider_deletion_event =
+            234 [(log_from_module) = "mediaprovider"];
+        MediaProviderPermissionEvent media_provider_permission_event =
+            235 [(log_from_module) = "mediaprovider"];
+        MediaProviderSchemaChange media_provider_schema_change =
+            236 [(log_from_module) = "mediaprovider"];
+        MediaProviderIdleMaintenance media_provider_idle_maintenance =
+            237 [(log_from_module) = "mediaprovider"];
     }
 
     // Pulled events will start at field 10000.
@@ -3799,6 +3810,124 @@
     optional State state  = 2;
 }
 
+/**
+ * Logs when MediaProvider has successfully finished scanning a storage volume.
+ *
+ * Logged from:
+ *   packages/providers/MediaProvider/src/com/android/providers/media/scan/ModernMediaScanner.java
+ */
+message MediaProviderScanEvent {
+    enum Reason {
+        // Scan triggered due to unknown reason
+        UNKNOWN = 0;
+        // Scan triggered due to storage volume being mounted
+        MOUNTED = 1;
+        // Scan triggered due to explicit user action or app request
+        DEMAND = 2;
+        // Scan triggered due to idle maintenance
+        IDLE = 3;
+    }
+
+    // Volume type that this event pertains to
+    optional android.stats.mediaprovider.VolumeType volume_type = 1;
+    // Reason why this scan was triggered
+    optional Reason reason = 2;
+    // Total number of files scanned
+    optional int64 item_count = 3;
+    // Duration of scan, normalized per file
+    optional float normalized_duration_millis = 4;
+    // Number of database inserts, normalized per file
+    optional float normalized_insert_count = 5;
+    // Number of database updates, normalized per file
+    optional float normalized_update_count = 6;
+    // Number of database deletes, normalized per file
+    optional float normalized_delete_count = 7;
+}
+
+/**
+ * Logs when an app has asked MediaProvider to delete media belonging to the user.
+ *
+ * Logged from:
+ *   packages/providers/MediaProvider/src/com/android/providers/media/MediaProvider.java
+ */
+message MediaProviderDeletionEvent {
+    // Volume type that this event pertains to
+    optional android.stats.mediaprovider.VolumeType volume_type = 1;
+    // Device timestamp when this deletion event occurred
+    optional int64 timestamp_millis = 2;
+    // App that requested deletion
+    optional string package_name = 3;
+    // Number of items that were deleted
+    optional int32 item_count = 4;
+}
+
+/**
+ * Logs when an app has asked MediaProvider to grant them access to media belonging to the user.
+ *
+ * Logged from:
+ *   packages/providers/MediaProvider/src/com/android/providers/media/PermissionActivity.java
+ */
+message MediaProviderPermissionEvent {
+    enum Result {
+        UNKNOWN = 0;
+        USER_GRANTED = 1;
+        AUTO_GRANTED = 2;
+        USER_DENIED = 3;
+        USER_DENIED_WITH_PREJUDICE = 4;
+        AUTO_DENIED = 5;
+    }
+
+    // Volume type that this event pertains to
+    optional android.stats.mediaprovider.VolumeType volume_type = 1;
+    // Device timestamp when this permission event occurred
+    optional int64 timestamp_millis = 2;
+    // App that requested permission
+    optional string package_name = 3;
+    // Number of items that were requested
+    optional int32 item_count = 4;
+    // Result of this request
+    optional Result result = 5;
+}
+
+/**
+ * Logs when MediaProvider has finished upgrading or downgrading its database schema.
+ *
+ * Logged from:
+ *   packages/providers/MediaProvider/src/com/android/providers/media/DatabaseHelper.java
+ */
+message MediaProviderSchemaChange {
+    // Volume type that this event pertains to
+    optional android.stats.mediaprovider.VolumeType volume_type = 1;
+    // Old database version code
+    optional int32 version_from = 2;
+    // New database version code
+    optional int32 version_to = 3;
+    // Total number of files in database
+    optional int64 item_count = 4;
+    // Duration of schema change, normalized per file
+    optional float normalized_duration_millis = 5;
+}
+
+/**
+ * Logs when MediaProvider has finished an idle maintenance job.
+ *
+ * Logged from:
+ *   packages/providers/MediaProvider/src/com/android/providers/media/MediaProvider.java
+ */
+message MediaProviderIdleMaintenance {
+    // Volume type that this event pertains to
+    optional android.stats.mediaprovider.VolumeType volume_type = 1;
+
+    // Total number of files in database
+    optional int64 item_count = 2;
+    // Duration of idle maintenance, normalized per file
+    optional float normalized_duration_millis = 3;
+    // Number of thumbnails found to be stale, normalized per file
+    optional float normalized_stale_thumbnails = 4;
+    // Number of items found to be expired, normalized per file
+    optional float normalized_expired_media = 5;
+}
+
 //////////////////////////////////////////////////////////////////////
 // Pulled atoms below this line //
 //////////////////////////////////////////////////////////////////////
@@ -5798,10 +5927,10 @@
     optional int64 request_id = 1;
 
     // UID of package requesting the permission grant
-    optional int32 requesting_uid = 2 [(is_uid) = true];
+    optional int32 uid = 2 [(is_uid) = true];
 
     // Name of package requesting the permission grant
-    optional string requesting_package_name = 3;
+    optional string package_name = 3;
 
     // The permission to be granted
     optional string permission_name = 4;
diff --git a/cmds/telecom/src/com/android/commands/telecom/Telecom.java b/cmds/telecom/src/com/android/commands/telecom/Telecom.java
index a4b3058..1987440 100644
--- a/cmds/telecom/src/com/android/commands/telecom/Telecom.java
+++ b/cmds/telecom/src/com/android/commands/telecom/Telecom.java
@@ -16,10 +16,12 @@
 
 package com.android.commands.telecom;
 
+import android.app.ActivityThread;
 import android.content.ComponentName;
 import android.content.Context;
 import android.net.Uri;
 import android.os.IUserManager;
+import android.os.Looper;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.ServiceManager;
@@ -33,7 +35,6 @@
 
 import com.android.internal.os.BaseCommand;
 import com.android.internal.telecom.ITelecomService;
-import com.android.internal.telephony.ITelephony;
 
 import java.io.PrintStream;
 
@@ -58,7 +59,6 @@
     private static final String COMMAND_SET_TEST_CALL_SCREENING_APP = "set-test-call-screening-app";
     private static final String COMMAND_ADD_OR_REMOVE_CALL_COMPANION_APP =
             "add-or-remove-call-companion-app";
-    private static final String COMMAND_SET_TEST_AUTO_MODE_APP = "set-test-auto-mode-app";
     private static final String COMMAND_SET_PHONE_ACCOUNT_SUGGESTION_COMPONENT =
             "set-phone-acct-suggestion-component";
     private static final String COMMAND_UNREGISTER_PHONE_ACCOUNT = "unregister-phone-account";
@@ -83,7 +83,7 @@
     private ComponentName mComponent;
     private String mAccountId;
     private ITelecomService mTelecomService;
-    private ITelephony mTelephonyService;
+    private TelephonyManager mTelephonyManager;
     private IUserManager mUserManager;
 
     @Override
@@ -99,7 +99,6 @@
                 + "<USER_SN>\n"
                 + "usage: telecom set-test-call-redirection-app <PACKAGE>\n"
                 + "usage: telecom set-test-call-screening-app <PACKAGE>\n"
-                + "usage: telecom set-test-auto-mode-app <PACKAGE>\n"
                 + "usage: telecom set-phone-acct-suggestion-component <COMPONENT>\n"
                 + "usage: telecom add-or-remove-call-companion-app <PACKAGE> <1/0>\n"
                 + "usage: telecom register-sim-phone-account <COMPONENT> <ID> <USER_SN>"
@@ -155,9 +154,10 @@
             return;
         }
 
-        mTelephonyService = ITelephony.Stub.asInterface(
-                ServiceManager.getService(Context.TELEPHONY_SERVICE));
-        if (mTelephonyService == null) {
+        Looper.prepareMainLooper();
+        Context context = ActivityThread.systemMain().getSystemContext();
+        mTelephonyManager = context.getSystemService(TelephonyManager.class);
+        if (mTelephonyManager == null) {
             Log.w(this, "onRun: Can't access telephony service.");
             showError("Error: Could not access the Telephony Service. Is the system running?");
             return;
@@ -191,9 +191,6 @@
             case COMMAND_ADD_OR_REMOVE_CALL_COMPANION_APP:
                 runAddOrRemoveCallCompanionApp();
                 break;
-            case COMMAND_SET_TEST_AUTO_MODE_APP:
-                runSetTestAutoModeApp();
-                break;
             case COMMAND_SET_PHONE_ACCOUNT_SUGGESTION_COMPONENT:
                 runSetTestPhoneAcctSuggestionComponent();
                 break;
@@ -305,11 +302,6 @@
         mTelecomService.addOrRemoveTestCallCompanionApp(packageName, isAddedBool);
     }
 
-    private void runSetTestAutoModeApp() throws RemoteException {
-        final String packageName = nextArg();
-        mTelecomService.setTestAutoModeApp(packageName);
-    }
-
     private void runSetTestPhoneAcctSuggestionComponent() throws RemoteException {
         final String componentName = nextArg();
         mTelecomService.setTestPhoneAcctSuggestionComponent(componentName);
@@ -361,7 +353,7 @@
         }
         int numSims = Integer.parseInt(nextArgRequired());
         System.out.println("Setting sim count to " + numSims + ". Device may reboot");
-        mTelephonyService.switchMultiSimConfig(numSims);
+        mTelephonyManager.switchMultiSimConfig(numSims);
     }
 
     /**
@@ -375,8 +367,7 @@
 
     private void runGetMaxPhones() throws RemoteException {
         // This assumes the max number of SIMs is 2, which it currently is
-        if (TelephonyManager.MULTISIM_ALLOWED
-                == mTelephonyService.isMultiSimSupported("com.android.commands.telecom", null)) {
+        if (TelephonyManager.MULTISIM_ALLOWED == mTelephonyManager.isMultiSimSupported()) {
             System.out.println("2");
         } else {
             System.out.println("1");
diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
index 3db0b26..7fd01db 100644
--- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
@@ -20,6 +20,8 @@
 
 import android.annotation.IntDef;
 import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.UnsupportedAppUsage;
 import android.compat.annotation.ChangeId;
 import android.compat.annotation.EnabledAfter;
@@ -512,6 +514,21 @@
     private static final long REQUEST_ACCESSIBILITY_BUTTON_CHANGE = 136293963L;
 
     /**
+     * Resource id of the animated image of the accessibility service.
+     */
+    private int mAnimatedImageRes;
+
+    /**
+     * Resource id of the html description of the accessibility service.
+     */
+    private int mHtmlDescriptionRes;
+
+    /**
+     * Non localized html description of the accessibility service.
+     */
+    private String mNonLocalizedHtmlDescription;
+
+    /**
      * Creates a new instance.
      */
     public AccessibilityServiceInfo() {
@@ -626,6 +643,20 @@
                     mNonLocalizedSummary = nonLocalizedSummary.toString().trim();
                 }
             }
+            peekedValue = asAttributes.peekValue(
+                    com.android.internal.R.styleable.AccessibilityService_animatedImageDrawable);
+            if (peekedValue != null) {
+                mAnimatedImageRes = peekedValue.resourceId;
+            }
+            peekedValue = asAttributes.peekValue(
+                    com.android.internal.R.styleable.AccessibilityService_htmlDescription);
+            if (peekedValue != null) {
+                mHtmlDescriptionRes = peekedValue.resourceId;
+                final CharSequence nonLocalizedHtmlDescription = peekedValue.coerceToString();
+                if (nonLocalizedHtmlDescription != null) {
+                    mNonLocalizedHtmlDescription = nonLocalizedHtmlDescription.toString().trim();
+                }
+            }
             asAttributes.recycle();
         } catch (NameNotFoundException e) {
             throw new XmlPullParserException( "Unable to create context for: "
@@ -727,6 +758,18 @@
     }
 
     /**
+     * The animated image resource id.
+     * <p>
+     *    <strong>Statically set from
+     *    {@link AccessibilityService#SERVICE_META_DATA meta-data}.</strong>
+     * </p>
+     * @return The animated image resource id.
+     */
+    public int getAnimatedImageRes() {
+        return mAnimatedImageRes;
+    }
+
+    /**
      * Whether this service can retrieve the current window's content.
      * <p>
      *    <strong>Statically set from
@@ -833,6 +876,29 @@
     }
 
     /**
+     * The localized html description of the accessibility service.
+     * <p>
+     *    <strong>Statically set from
+     *    {@link AccessibilityService#SERVICE_META_DATA meta-data}.</strong>
+     * </p>
+     * @return The localized html description.
+     */
+    @Nullable
+    public String loadHtmlDescription(@NonNull PackageManager packageManager) {
+        if (mHtmlDescriptionRes == 0) {
+            return mNonLocalizedHtmlDescription;
+        }
+
+        final ServiceInfo serviceInfo = mResolveInfo.serviceInfo;
+        final CharSequence htmlDescription = packageManager.getText(serviceInfo.packageName,
+                mHtmlDescriptionRes, serviceInfo.applicationInfo);
+        if (htmlDescription != null) {
+            return htmlDescription.toString().trim();
+        }
+        return null;
+    }
+
+    /**
      * Set the recommended time that non-interactive controls need to remain on the screen to
      * support the user.
      * <p>
@@ -915,7 +981,10 @@
         parcel.writeInt(mSummaryResId);
         parcel.writeString(mNonLocalizedSummary);
         parcel.writeInt(mDescriptionResId);
+        parcel.writeInt(mAnimatedImageRes);
+        parcel.writeInt(mHtmlDescriptionRes);
         parcel.writeString(mNonLocalizedDescription);
+        parcel.writeString(mNonLocalizedHtmlDescription);
     }
 
     private void initFromParcel(Parcel parcel) {
@@ -934,7 +1003,10 @@
         mSummaryResId = parcel.readInt();
         mNonLocalizedSummary = parcel.readString();
         mDescriptionResId = parcel.readInt();
+        mAnimatedImageRes = parcel.readInt();
+        mHtmlDescriptionRes = parcel.readInt();
         mNonLocalizedDescription = parcel.readString();
+        mNonLocalizedHtmlDescription = parcel.readString();
     }
 
     @Override
diff --git a/core/java/android/annotation/UnsupportedAppUsage.java b/core/java/android/annotation/UnsupportedAppUsage.java
index 204d71d..1af48cb 100644
--- a/core/java/android/annotation/UnsupportedAppUsage.java
+++ b/core/java/android/annotation/UnsupportedAppUsage.java
@@ -148,6 +148,18 @@
     String publicAlternatives() default "";
 
     /**
+     * Override the default source position when generating an index of the annotations.
+     *
+     * <p>This is intended for use by tools that generate java source code, to point to the
+     * original source position of the annotation, rather than the position within the generated
+     * code. It should never be set manually.
+     *
+     * <p>The format of the value is "path/to/file:startline:startcol:endline:endcol" indicating
+     * the position of the annotation itself.
+     */
+    String overrideSourcePosition() default "";
+
+    /**
      * Container for {@link UnsupportedAppUsage} that allows it to be applied repeatedly to types.
      */
     @Retention(CLASS)
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 0113f69..034826a 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -39,6 +39,7 @@
 import android.content.pm.IPackageManager;
 import android.content.pm.IPackageMoveObserver;
 import android.content.pm.IPackageStatsObserver;
+import android.content.pm.InstallSourceInfo;
 import android.content.pm.InstantAppInfo;
 import android.content.pm.InstrumentationInfo;
 import android.content.pm.IntentFilterVerificationInfo;
@@ -2085,6 +2086,21 @@
     }
 
     @Override
+    @NonNull
+    public InstallSourceInfo getInstallSourceInfo(String packageName) throws NameNotFoundException {
+        final InstallSourceInfo installSourceInfo;
+        try {
+            installSourceInfo = mPM.getInstallSourceInfo(packageName);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+        if (installSourceInfo == null) {
+            throw new NameNotFoundException(packageName);
+        }
+        return installSourceInfo;
+    }
+
+    @Override
     public int getMoveStatus(int moveId) {
         try {
             return mPM.getMoveStatus(moveId);
@@ -2782,7 +2798,7 @@
     public Drawable loadUnbadgedItemIcon(@NonNull PackageItemInfo itemInfo,
             @Nullable ApplicationInfo appInfo) {
         if (itemInfo.showUserIcon != UserHandle.USER_NULL) {
-            // Indicates itemInfo is for a different user (e.g. a profile's parent), so use a 
+            // Indicates itemInfo is for a different user (e.g. a profile's parent), so use a
             // generic user icon (users generally lack permission to view each other's actual icons)
             int targetUserId = itemInfo.showUserIcon;
             return UserIcons.getDefaultUserIcon(
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 50d0dab..aa8a302 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -387,6 +387,7 @@
     void requestInteractiveBugReport();
     void requestFullBugReport();
     void requestRemoteBugReport();
+    boolean launchBugReportHandlerApp();
 
     @UnsupportedAppUsage
     Intent getIntentForIntentSender(in IIntentSender sender);
diff --git a/core/java/android/app/IntentService.java b/core/java/android/app/IntentService.java
index 11c747f..74fb99a 100644
--- a/core/java/android/app/IntentService.java
+++ b/core/java/android/app/IntentService.java
@@ -44,13 +44,6 @@
  * long as necessary (and will not block the application's main loop), but
  * only one request will be processed at a time.
  *
- * <p class="note"><b>Note:</b> IntentService is subject to all the
- * <a href="/preview/features/background.html">background execution limits</a>
- * imposed with Android 8.0 (API level 26). In most cases, you are better off
- * using {@link android.support.v4.app.JobIntentService}, which uses jobs
- * instead of services when running on Android 8.0 or higher.
- * </p>
- *
  * <div class="special reference">
  * <h3>Developer Guides</h3>
  * <p>For a detailed discussion about how to create services, read the
@@ -59,8 +52,14 @@
  * </div>
  *
  * @see android.support.v4.app.JobIntentService
- * @see android.os.AsyncTask
+ *
+ * @deprecated IntentService is subject to all the
+ *   <a href="/preview/features/background.html">background execution limits</a>
+ *   imposed with Android 8.0 (API level 26). Consider using {@link androidx.work.WorkManager}
+ *   or {@link androidx.core.app.JobIntentService}, which uses jobs
+ *   instead of services when running on Android 8.0 or higher.
  */
+@Deprecated
 public abstract class IntentService extends Service {
     private volatile Looper mServiceLooper;
     @UnsupportedAppUsage
diff --git a/core/java/android/app/Service.java b/core/java/android/app/Service.java
index 1f91b3f..9b62e3b 100644
--- a/core/java/android/app/Service.java
+++ b/core/java/android/app/Service.java
@@ -57,7 +57,7 @@
  * networking) operations, it should spawn its own thread in which to do that
  * work.  More information on this can be found in
  * <a href="{@docRoot}guide/topics/fundamentals/processes-and-threads.html">Processes and
- * Threads</a>.  The {@link IntentService} class is available
+ * Threads</a>.  The {@link androidx.core.app.JobIntentService} class is available
  * as a standard implementation of Service that has its own thread where it
  * schedules its work to be done.</p>
  * 
diff --git a/core/java/android/app/StatsManager.java b/core/java/android/app/StatsManager.java
index 11d6528a..2ef0510 100644
--- a/core/java/android/app/StatsManager.java
+++ b/core/java/android/app/StatsManager.java
@@ -51,12 +51,13 @@
     private static final String TAG = "StatsManager";
     private static final boolean DEBUG = false;
 
+    private static final Object sLock = new Object();
     private final Context mContext;
 
-    @GuardedBy("this")
+    @GuardedBy("sLock")
     private IStatsManager mService;
 
-    @GuardedBy("this")
+    @GuardedBy("sLock")
     private IStatsCompanionService mStatsCompanion;
 
     /**
@@ -125,7 +126,7 @@
      */
     @RequiresPermission(allOf = { DUMP, PACKAGE_USAGE_STATS })
     public void addConfig(long configKey, byte[] config) throws StatsUnavailableException {
-        synchronized (this) {
+        synchronized (sLock) {
             try {
                 IStatsManager service = getIStatsManagerLocked();
                 // can throw IllegalArgumentException
@@ -162,7 +163,7 @@
      */
     @RequiresPermission(allOf = { DUMP, PACKAGE_USAGE_STATS })
     public void removeConfig(long configKey) throws StatsUnavailableException {
-        synchronized (this) {
+        synchronized (sLock) {
             try {
                 IStatsManager service = getIStatsManagerLocked();
                 service.removeConfiguration(configKey, mContext.getOpPackageName());
@@ -223,7 +224,7 @@
     public void setBroadcastSubscriber(
             PendingIntent pendingIntent, long configKey, long subscriberId)
             throws StatsUnavailableException {
-        synchronized (this) {
+        synchronized (sLock) {
             try {
                 IStatsManager service = getIStatsManagerLocked();
                 if (pendingIntent != null) {
@@ -277,7 +278,7 @@
     @RequiresPermission(allOf = { DUMP, PACKAGE_USAGE_STATS })
     public void setFetchReportsOperation(PendingIntent pendingIntent, long configKey)
             throws StatsUnavailableException {
-        synchronized (this) {
+        synchronized (sLock) {
             try {
                 IStatsManager service = getIStatsManagerLocked();
                 if (pendingIntent == null) {
@@ -315,7 +316,7 @@
     @RequiresPermission(allOf = { DUMP, PACKAGE_USAGE_STATS })
     public @NonNull long[] setActiveConfigsChangedOperation(@Nullable PendingIntent pendingIntent)
             throws StatsUnavailableException {
-        synchronized (this) {
+        synchronized (sLock) {
             try {
                 IStatsManager service = getIStatsManagerLocked();
                 if (pendingIntent == null) {
@@ -363,7 +364,7 @@
      */
     @RequiresPermission(allOf = { DUMP, PACKAGE_USAGE_STATS })
     public byte[] getReports(long configKey) throws StatsUnavailableException {
-        synchronized (this) {
+        synchronized (sLock) {
             try {
                 IStatsManager service = getIStatsManagerLocked();
                 return service.getData(configKey, mContext.getOpPackageName());
@@ -400,7 +401,7 @@
      */
     @RequiresPermission(allOf = { DUMP, PACKAGE_USAGE_STATS })
     public byte[] getStatsMetadata() throws StatsUnavailableException {
-        synchronized (this) {
+        synchronized (sLock) {
             try {
                 IStatsManager service = getIStatsManagerLocked();
                 return service.getMetadata(mContext.getOpPackageName());
@@ -435,7 +436,7 @@
     @RequiresPermission(allOf = {DUMP, PACKAGE_USAGE_STATS})
     public long[] getRegisteredExperimentIds()
             throws StatsUnavailableException {
-        synchronized (this) {
+        synchronized (sLock) {
             try {
                 IStatsManager service = getIStatsManagerLocked();
                 if (service == null) {
@@ -472,7 +473,7 @@
     @RequiresPermission(allOf = { DUMP, PACKAGE_USAGE_STATS })
     public void setPullerCallback(int atomTag, IStatsPullerCallback callback)
             throws StatsUnavailableException {
-        synchronized (this) {
+        synchronized (sLock) {
             try {
                 IStatsManager service = getIStatsManagerLocked();
                 if (callback == null) {
@@ -515,7 +516,7 @@
         if (additiveFields == null) {
             additiveFields = new int[0];
         }
-        synchronized (this) {
+        synchronized (sLock) {
             IStatsCompanionService service = getIStatsCompanionServiceLocked();
             PullAtomCallbackInternal rec =
                     new PullAtomCallbackInternal(atomTag, callback, executor);
@@ -649,13 +650,13 @@
     private class StatsdDeathRecipient implements IBinder.DeathRecipient {
         @Override
         public void binderDied() {
-            synchronized (this) {
+            synchronized (sLock) {
                 mService = null;
             }
         }
     }
 
-    @GuardedBy("this")
+    @GuardedBy("sLock")
     private IStatsManager getIStatsManagerLocked() throws StatsUnavailableException {
         if (mService != null) {
             return mService;
@@ -672,7 +673,7 @@
         return mService;
     }
 
-    @GuardedBy("this")
+    @GuardedBy("sLock")
     private IStatsCompanionService getIStatsCompanionServiceLocked() {
         if (mStatsCompanion != null) {
             return mStatsCompanion;
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 1829f74..601b658 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -1137,7 +1137,8 @@
         registerService(Context.PERMISSION_SERVICE, PermissionManager.class,
                 new CachedServiceFetcher<PermissionManager>() {
                     @Override
-                    public PermissionManager createService(ContextImpl ctx) {
+                    public PermissionManager createService(ContextImpl ctx)
+                            throws ServiceNotFoundException {
                         IPackageManager packageManager = AppGlobals.getPackageManager();
                         return new PermissionManager(ctx.getOuterContext(), packageManager);
                     }});
diff --git a/core/java/android/app/admin/DeviceAdminInfo.java b/core/java/android/app/admin/DeviceAdminInfo.java
index 00903c4..63bc40b 100644
--- a/core/java/android/app/admin/DeviceAdminInfo.java
+++ b/core/java/android/app/admin/DeviceAdminInfo.java
@@ -55,6 +55,14 @@
     static final String TAG = "DeviceAdminInfo";
 
     /**
+     * A type of policy that this device admin can use: profile owner on an organization-owned
+     * device.
+     *
+     * @hide
+     */
+    public static final int USES_POLICY_ORGANIZATION_OWNED_PROFILE_OWNER = -3;
+
+    /**
      * A type of policy that this device admin can use: device owner meta-policy
      * for an admin that is designated as owner of the device.
      *
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 39dc51e..34ceb08 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -9135,7 +9135,14 @@
     }
 
     /**
-     * Called by device owner to get the MAC address of the Wi-Fi device.
+     * Called by device owner, or profile owner on organization-owned device, to get the MAC
+     * address of the Wi-Fi device.
+     *
+     * NOTE: The MAC address returned here should only be used for inventory management and is
+     * not likely to be the MAC address used by the device to connect to Wi-Fi networks: MAC
+     * addresses used for scanning and connecting to Wi-Fi networks are randomized by default.
+     * To get the randomized MAC address used, call
+     * {@link android.net.wifi.WifiConfiguration#getRandomizedMacAddress}.
      *
      * @param admin Which device owner this request is associated with.
      * @return the MAC address of the Wi-Fi device, or null when the information is not available.
diff --git a/core/java/android/app/prediction/AppTargetEvent.java b/core/java/android/app/prediction/AppTargetEvent.java
index 26ff0c1..f519145 100644
--- a/core/java/android/app/prediction/AppTargetEvent.java
+++ b/core/java/android/app/prediction/AppTargetEvent.java
@@ -38,7 +38,7 @@
     /**
      * @hide
      */
-    @IntDef({ACTION_LAUNCH, ACTION_DISMISS, ACTION_PIN})
+    @IntDef({ACTION_LAUNCH, ACTION_DISMISS, ACTION_PIN, ACTION_UNPIN})
     @Retention(RetentionPolicy.SOURCE)
     public @interface ActionType {}
 
@@ -57,6 +57,11 @@
      */
     public static final int ACTION_PIN = 3;
 
+    /**
+     * Event type constant indicating an app target has been un-pinned.
+     */
+    public static final int ACTION_UNPIN = 4;
+
     private final AppTarget mTarget;
     private final String mLocation;
     private final int mAction;
diff --git a/core/java/android/app/timedetector/ManualTimeSuggestion.java b/core/java/android/app/timedetector/ManualTimeSuggestion.java
index e7d619a..471606da 100644
--- a/core/java/android/app/timedetector/ManualTimeSuggestion.java
+++ b/core/java/android/app/timedetector/ManualTimeSuggestion.java
@@ -85,7 +85,8 @@
 
     @NonNull
     public List<String> getDebugInfo() {
-        return Collections.unmodifiableList(mDebugInfo);
+        return mDebugInfo == null
+                ? Collections.emptyList() : Collections.unmodifiableList(mDebugInfo);
     }
 
     /**
diff --git a/core/java/android/app/timedetector/PhoneTimeSuggestion.java b/core/java/android/app/timedetector/PhoneTimeSuggestion.java
index 233dbbc..dd02af7 100644
--- a/core/java/android/app/timedetector/PhoneTimeSuggestion.java
+++ b/core/java/android/app/timedetector/PhoneTimeSuggestion.java
@@ -23,7 +23,6 @@
 import android.util.TimestampedValue;
 
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 import java.util.Objects;
@@ -52,20 +51,25 @@
             };
 
     private final int mPhoneId;
-    @Nullable private TimestampedValue<Long> mUtcTime;
+    @Nullable private final TimestampedValue<Long> mUtcTime;
     @Nullable private ArrayList<String> mDebugInfo;
 
-    public PhoneTimeSuggestion(int phoneId) {
-        mPhoneId = phoneId;
+    private PhoneTimeSuggestion(Builder builder) {
+        mPhoneId = builder.mPhoneId;
+        mUtcTime = builder.mUtcTime;
+        mDebugInfo = builder.mDebugInfo != null ? new ArrayList<>(builder.mDebugInfo) : null;
     }
 
     private static PhoneTimeSuggestion createFromParcel(Parcel in) {
         int phoneId = in.readInt();
-        PhoneTimeSuggestion suggestion = new PhoneTimeSuggestion(phoneId);
-        suggestion.setUtcTime(in.readParcelable(null /* classLoader */));
+        PhoneTimeSuggestion suggestion = new PhoneTimeSuggestion.Builder(phoneId)
+                .setUtcTime(in.readParcelable(null /* classLoader */))
+                .build();
         @SuppressWarnings("unchecked")
         ArrayList<String> debugInfo = (ArrayList<String>) in.readArrayList(null /* classLoader */);
-        suggestion.mDebugInfo = debugInfo;
+        if (debugInfo != null) {
+            suggestion.addDebugInfo(debugInfo);
+        }
         return suggestion;
     }
 
@@ -85,10 +89,6 @@
         return mPhoneId;
     }
 
-    public void setUtcTime(@Nullable TimestampedValue<Long> utcTime) {
-        mUtcTime = utcTime;
-    }
-
     @Nullable
     public TimestampedValue<Long> getUtcTime() {
         return mUtcTime;
@@ -96,7 +96,8 @@
 
     @NonNull
     public List<String> getDebugInfo() {
-        return Collections.unmodifiableList(mDebugInfo);
+        return mDebugInfo == null
+                ? Collections.emptyList() : Collections.unmodifiableList(mDebugInfo);
     }
 
     /**
@@ -104,11 +105,23 @@
      * information is present in {@link #toString()} but is not considered for
      * {@link #equals(Object)} and {@link #hashCode()}.
      */
-    public void addDebugInfo(String... debugInfos) {
+    public void addDebugInfo(String debugInfo) {
         if (mDebugInfo == null) {
             mDebugInfo = new ArrayList<>();
         }
-        mDebugInfo.addAll(Arrays.asList(debugInfos));
+        mDebugInfo.add(debugInfo);
+    }
+
+    /**
+     * Associates information with the instance that can be useful for debugging / logging. The
+     * information is present in {@link #toString()} but is not considered for
+     * {@link #equals(Object)} and {@link #hashCode()}.
+     */
+    public void addDebugInfo(@NonNull List<String> debugInfo) {
+        if (mDebugInfo == null) {
+            mDebugInfo = new ArrayList<>(debugInfo.size());
+        }
+        mDebugInfo.addAll(debugInfo);
     }
 
     @Override
@@ -137,4 +150,39 @@
                 + ", mDebugInfo=" + mDebugInfo
                 + '}';
     }
+
+    /**
+     * Builds {@link PhoneTimeSuggestion} instances.
+     *
+     * @hide
+     */
+    public static class Builder {
+        private final int mPhoneId;
+        private TimestampedValue<Long> mUtcTime;
+        private List<String> mDebugInfo;
+
+        public Builder(int phoneId) {
+            mPhoneId = phoneId;
+        }
+
+        /** Returns the builder for call chaining. */
+        public Builder setUtcTime(TimestampedValue<Long> utcTime) {
+            mUtcTime = utcTime;
+            return this;
+        }
+
+        /** Returns the builder for call chaining. */
+        public Builder addDebugInfo(@NonNull String debugInfo) {
+            if (mDebugInfo == null) {
+                mDebugInfo = new ArrayList<>();
+            }
+            mDebugInfo.add(debugInfo);
+            return this;
+        }
+
+        /** Returns the {@link PhoneTimeSuggestion}. */
+        public PhoneTimeSuggestion build() {
+            return new PhoneTimeSuggestion(this);
+        }
+    }
 }
diff --git a/core/java/android/app/timezonedetector/TimeZoneDetector.java b/core/java/android/app/timezonedetector/TimeZoneDetector.java
index 909cbc2..387a36b 100644
--- a/core/java/android/app/timezonedetector/TimeZoneDetector.java
+++ b/core/java/android/app/timezonedetector/TimeZoneDetector.java
@@ -17,6 +17,7 @@
 package android.app.timezonedetector;
 
 import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
 import android.annotation.SystemService;
 import android.content.Context;
 import android.os.RemoteException;
@@ -26,10 +27,11 @@
 
 /**
  * The interface through which system components can send signals to the TimeZoneDetectorService.
+ *
  * @hide
  */
 @SystemService(Context.TIME_ZONE_DETECTOR_SERVICE)
-public final class TimeZoneDetector {
+public class TimeZoneDetector {
     private static final String TAG = "timezonedetector.TimeZoneDetector";
     private static final boolean DEBUG = false;
 
@@ -41,10 +43,11 @@
     }
 
     /**
-     * Suggests the current time zone to the detector. The detector may ignore the signal if better
-     * signals are available such as those that come from more reliable sources or were
-     * determined more recently.
+     * Suggests the current time zone, determined using telephony signals, to the detector. The
+     * detector may ignore the signal based on system settings, whether better information is
+     * available, and so on.
      */
+    @RequiresPermission(android.Manifest.permission.SET_TIME_ZONE)
     public void suggestPhoneTimeZone(@NonNull PhoneTimeZoneSuggestion timeZoneSuggestion) {
         if (DEBUG) {
             Log.d(TAG, "suggestPhoneTimeZone called: " + timeZoneSuggestion);
@@ -56,4 +59,28 @@
         }
     }
 
+    /**
+     * Suggests the current time zone, determined for the user's manually information, to the
+     * detector. The detector may ignore the signal based on system settings.
+     */
+    @RequiresPermission(android.Manifest.permission.SET_TIME_ZONE)
+    public void suggestManualTimeZone(@NonNull ManualTimeZoneSuggestion timeZoneSuggestion) {
+        if (DEBUG) {
+            Log.d(TAG, "suggestManualTimeZone called: " + timeZoneSuggestion);
+        }
+        try {
+            mITimeZoneDetectorService.suggestManualTimeZone(timeZoneSuggestion);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * A shared utility method to create a {@link ManualTimeZoneSuggestion}.
+     */
+    public static ManualTimeZoneSuggestion createManualTimeZoneSuggestion(String tzId, String why) {
+        ManualTimeZoneSuggestion suggestion = new ManualTimeZoneSuggestion(tzId);
+        suggestion.addDebugInfo(why);
+        return suggestion;
+    }
 }
diff --git a/core/java/android/bluetooth/BluetoothA2dp.java b/core/java/android/bluetooth/BluetoothA2dp.java
index accdd8d..8ed61b6 100644
--- a/core/java/android/bluetooth/BluetoothA2dp.java
+++ b/core/java/android/bluetooth/BluetoothA2dp.java
@@ -610,7 +610,7 @@
             if (uuids == null) return false;
 
             for (ParcelUuid uuid : uuids) {
-                if (BluetoothUuid.isAvrcpTarget(uuid)) {
+                if (uuid.equals(BluetoothUuid.AVRCP_TARGET)) {
                     return true;
                 }
             }
diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java
index ea3831a..0955b10 100644
--- a/core/java/android/bluetooth/BluetoothHeadset.java
+++ b/core/java/android/bluetooth/BluetoothHeadset.java
@@ -559,7 +559,7 @@
      *
      * <p> The device should already be paired.
      * Priority can be one of {@link BluetoothProfile#PRIORITY_ON} or
-     * {@link BluetoothProfile#PRIORITY_OFF},
+     * {@link BluetoothProfile#PRIORITY_OFF}
      *
      * @param device Paired bluetooth device
      * @param priority
@@ -1133,8 +1133,9 @@
      * is active.
      * @hide
      */
+    @SystemApi
+    @Nullable
     @RequiresPermission(android.Manifest.permission.BLUETOOTH)
-    @UnsupportedAppUsage
     public BluetoothDevice getActiveDevice() {
         if (VDBG) {
             Log.d(TAG, "getActiveDevice");
diff --git a/core/java/android/bluetooth/BluetoothHeadsetClient.java b/core/java/android/bluetooth/BluetoothHeadsetClient.java
index a8e1fd2..7ee29ff 100644
--- a/core/java/android/bluetooth/BluetoothHeadsetClient.java
+++ b/core/java/android/bluetooth/BluetoothHeadsetClient.java
@@ -557,7 +557,7 @@
      * Set priority of the profile
      *
      * <p> The device should already be paired.
-     * Priority can be one of {@link #PRIORITY_ON} or {@link #PRIORITY_OFF},
+     * Priority can be one of {@link #PRIORITY_ON} or {@link #PRIORITY_OFF}
      *
      * @param device Paired bluetooth device
      * @param priority
diff --git a/core/java/android/bluetooth/BluetoothUuid.java b/core/java/android/bluetooth/BluetoothUuid.java
index bc3c9a9..7e96c23 100644
--- a/core/java/android/bluetooth/BluetoothUuid.java
+++ b/core/java/android/bluetooth/BluetoothUuid.java
@@ -16,8 +16,10 @@
 
 package android.bluetooth;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
 import android.annotation.UnsupportedAppUsage;
-import android.os.Build;
 import android.os.ParcelUuid;
 
 import java.nio.ByteBuffer;
@@ -31,6 +33,7 @@
  *
  * @hide
  */
+@SystemApi
 public final class BluetoothUuid {
 
     /* See Bluetooth Assigned Numbers document - SDP section, to get the values of UUIDs
@@ -39,167 +42,157 @@
      * The following 128 bit values are calculated as:
      *  uuid * 2^96 + BASE_UUID
      */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
-    public static final ParcelUuid AudioSink =
+
+    /** @hide */
+    @NonNull
+    @SystemApi
+    public static final ParcelUuid A2DP_SINK =
             ParcelUuid.fromString("0000110B-0000-1000-8000-00805F9B34FB");
-    public static final ParcelUuid AudioSource =
+    /** @hide */
+    @NonNull
+    @SystemApi
+    public static final ParcelUuid A2DP_SOURCE =
             ParcelUuid.fromString("0000110A-0000-1000-8000-00805F9B34FB");
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
-    public static final ParcelUuid AdvAudioDist =
+    /** @hide */
+    @NonNull
+    @SystemApi
+    public static final ParcelUuid ADV_AUDIO_DIST =
             ParcelUuid.fromString("0000110D-0000-1000-8000-00805F9B34FB");
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+    /** @hide */
+    @NonNull
+    @SystemApi
     public static final ParcelUuid HSP =
             ParcelUuid.fromString("00001108-0000-1000-8000-00805F9B34FB");
+    /** @hide */
+    @NonNull
+    @SystemApi
     public static final ParcelUuid HSP_AG =
             ParcelUuid.fromString("00001112-0000-1000-8000-00805F9B34FB");
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
-    public static final ParcelUuid Handsfree =
+    /** @hide */
+    @NonNull
+    @SystemApi
+    public static final ParcelUuid HFP =
             ParcelUuid.fromString("0000111E-0000-1000-8000-00805F9B34FB");
-    public static final ParcelUuid Handsfree_AG =
+    /** @hide */
+    @NonNull
+    @SystemApi
+    public static final ParcelUuid HFP_AG =
             ParcelUuid.fromString("0000111F-0000-1000-8000-00805F9B34FB");
-    public static final ParcelUuid AvrcpController =
+    /** @hide */
+    @NonNull
+    @SystemApi
+    public static final ParcelUuid AVRCP_CONTROLLER =
             ParcelUuid.fromString("0000110E-0000-1000-8000-00805F9B34FB");
-    public static final ParcelUuid AvrcpTarget =
+    /** @hide */
+    @NonNull
+    @SystemApi
+    public static final ParcelUuid AVRCP_TARGET =
             ParcelUuid.fromString("0000110C-0000-1000-8000-00805F9B34FB");
-    @UnsupportedAppUsage
-    public static final ParcelUuid ObexObjectPush =
+    /** @hide */
+    @NonNull
+    @SystemApi
+    public static final ParcelUuid OBEX_OBJECT_PUSH =
             ParcelUuid.fromString("00001105-0000-1000-8000-00805f9b34fb");
-    public static final ParcelUuid Hid =
+    /** @hide */
+    @NonNull
+    @SystemApi
+    public static final ParcelUuid HID =
             ParcelUuid.fromString("00001124-0000-1000-8000-00805f9b34fb");
-    @UnsupportedAppUsage
-    public static final ParcelUuid Hogp =
+    /** @hide */
+    @NonNull
+    @SystemApi
+    public static final ParcelUuid HOGP =
             ParcelUuid.fromString("00001812-0000-1000-8000-00805f9b34fb");
+    /** @hide */
+    @NonNull
+    @SystemApi
     public static final ParcelUuid PANU =
             ParcelUuid.fromString("00001115-0000-1000-8000-00805F9B34FB");
-    @UnsupportedAppUsage
+    /** @hide */
+    @NonNull
+    @SystemApi
     public static final ParcelUuid NAP =
             ParcelUuid.fromString("00001116-0000-1000-8000-00805F9B34FB");
+    /** @hide */
+    @NonNull
+    @SystemApi
     public static final ParcelUuid BNEP =
             ParcelUuid.fromString("0000000f-0000-1000-8000-00805F9B34FB");
+    /** @hide */
+    @NonNull
+    @SystemApi
     public static final ParcelUuid PBAP_PCE =
             ParcelUuid.fromString("0000112e-0000-1000-8000-00805F9B34FB");
-    @UnsupportedAppUsage
+    /** @hide */
+    @NonNull
+    @SystemApi
     public static final ParcelUuid PBAP_PSE =
             ParcelUuid.fromString("0000112f-0000-1000-8000-00805F9B34FB");
+    /** @hide */
+    @NonNull
+    @SystemApi
     public static final ParcelUuid MAP =
             ParcelUuid.fromString("00001134-0000-1000-8000-00805F9B34FB");
+    /** @hide */
+    @NonNull
+    @SystemApi
     public static final ParcelUuid MNS =
             ParcelUuid.fromString("00001133-0000-1000-8000-00805F9B34FB");
+    /** @hide */
+    @NonNull
+    @SystemApi
     public static final ParcelUuid MAS =
             ParcelUuid.fromString("00001132-0000-1000-8000-00805F9B34FB");
+    /** @hide */
+    @NonNull
+    @SystemApi
     public static final ParcelUuid SAP =
             ParcelUuid.fromString("0000112D-0000-1000-8000-00805F9B34FB");
-    public static final ParcelUuid HearingAid =
+    /** @hide */
+    @NonNull
+    @SystemApi
+    public static final ParcelUuid HEARING_AID =
             ParcelUuid.fromString("0000FDF0-0000-1000-8000-00805f9b34fb");
 
+    /** @hide */
+    @NonNull
+    @SystemApi
     public static final ParcelUuid BASE_UUID =
             ParcelUuid.fromString("00000000-0000-1000-8000-00805F9B34FB");
 
-    /** Length of bytes for 16 bit UUID */
-    public static final int UUID_BYTES_16_BIT = 2;
-    /** Length of bytes for 32 bit UUID */
-    public static final int UUID_BYTES_32_BIT = 4;
-    /** Length of bytes for 128 bit UUID */
-    public static final int UUID_BYTES_128_BIT = 16;
-
-    @UnsupportedAppUsage
-    public static final ParcelUuid[] RESERVED_UUIDS = {
-            AudioSink, AudioSource, AdvAudioDist, HSP, Handsfree, AvrcpController, AvrcpTarget,
-            ObexObjectPush, PANU, NAP, MAP, MNS, MAS, SAP};
-
-    @UnsupportedAppUsage
-    public static boolean isAudioSource(ParcelUuid uuid) {
-        return uuid.equals(AudioSource);
-    }
-
-    public static boolean isAudioSink(ParcelUuid uuid) {
-        return uuid.equals(AudioSink);
-    }
-
-    @UnsupportedAppUsage
-    public static boolean isAdvAudioDist(ParcelUuid uuid) {
-        return uuid.equals(AdvAudioDist);
-    }
-
-    public static boolean isHandsfree(ParcelUuid uuid) {
-        return uuid.equals(Handsfree);
-    }
-
-    public static boolean isHeadset(ParcelUuid uuid) {
-        return uuid.equals(HSP);
-    }
-
-    public static boolean isAvrcpController(ParcelUuid uuid) {
-        return uuid.equals(AvrcpController);
-    }
-
-    @UnsupportedAppUsage
-    public static boolean isAvrcpTarget(ParcelUuid uuid) {
-        return uuid.equals(AvrcpTarget);
-    }
-
-    public static boolean isInputDevice(ParcelUuid uuid) {
-        return uuid.equals(Hid);
-    }
-
-    public static boolean isPanu(ParcelUuid uuid) {
-        return uuid.equals(PANU);
-    }
-
-    public static boolean isNap(ParcelUuid uuid) {
-        return uuid.equals(NAP);
-    }
-
-    public static boolean isBnep(ParcelUuid uuid) {
-        return uuid.equals(BNEP);
-    }
-
-    public static boolean isMap(ParcelUuid uuid) {
-        return uuid.equals(MAP);
-    }
-
-    public static boolean isMns(ParcelUuid uuid) {
-        return uuid.equals(MNS);
-    }
-
-    public static boolean isMas(ParcelUuid uuid) {
-        return uuid.equals(MAS);
-    }
-
-    public static boolean isSap(ParcelUuid uuid) {
-        return uuid.equals(SAP);
-    }
-
     /**
-     * Returns true if ParcelUuid is present in uuidArray
+     * Length of bytes for 16 bit UUID
      *
-     * @param uuidArray - Array of ParcelUuids
-     * @param uuid
+     * @hide
      */
-    @UnsupportedAppUsage
-    public static boolean isUuidPresent(ParcelUuid[] uuidArray, ParcelUuid uuid) {
-        if ((uuidArray == null || uuidArray.length == 0) && uuid == null) {
-            return true;
-        }
-
-        if (uuidArray == null) {
-            return false;
-        }
-
-        for (ParcelUuid element : uuidArray) {
-            if (element.equals(uuid)) return true;
-        }
-        return false;
-    }
+    @SystemApi
+    public static final int UUID_BYTES_16_BIT = 2;
+    /**
+     * Length of bytes for 32 bit UUID
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int UUID_BYTES_32_BIT = 4;
+    /**
+     * Length of bytes for 128 bit UUID
+     *
+     * @hide
+     */
+    @SystemApi
+    public static final int UUID_BYTES_128_BIT = 16;
 
     /**
      * Returns true if there any common ParcelUuids in uuidA and uuidB.
      *
      * @param uuidA - List of ParcelUuids
      * @param uuidB - List of ParcelUuids
+     *
+     * @hide
      */
-    @UnsupportedAppUsage
-    public static boolean containsAnyUuid(ParcelUuid[] uuidA, ParcelUuid[] uuidB) {
+    @SystemApi
+    public static boolean containsAnyUuid(@Nullable ParcelUuid[] uuidA,
+            @Nullable ParcelUuid[] uuidB) {
         if (uuidA == null && uuidB == null) return true;
 
         if (uuidA == null) {
@@ -218,29 +211,6 @@
     }
 
     /**
-     * Returns true if all the ParcelUuids in ParcelUuidB are present in
-     * ParcelUuidA
-     *
-     * @param uuidA - Array of ParcelUuidsA
-     * @param uuidB - Array of ParcelUuidsB
-     */
-    public static boolean containsAllUuids(ParcelUuid[] uuidA, ParcelUuid[] uuidB) {
-        if (uuidA == null && uuidB == null) return true;
-
-        if (uuidA == null) {
-            return uuidB.length == 0;
-        }
-
-        if (uuidB == null) return true;
-
-        HashSet<ParcelUuid> uuidSet = new HashSet<ParcelUuid>(Arrays.asList(uuidA));
-        for (ParcelUuid uuid : uuidB) {
-            if (!uuidSet.contains(uuid)) return false;
-        }
-        return true;
-    }
-
-    /**
      * Extract the Service Identifier or the actual uuid from the Parcel Uuid.
      * For example, if 0000110B-0000-1000-8000-00805F9B34FB is the parcel Uuid,
      * this function will return 110B
@@ -248,7 +218,7 @@
      * @param parcelUuid
      * @return the service identifier.
      */
-    public static int getServiceIdentifierFromParcelUuid(ParcelUuid parcelUuid) {
+    private static int getServiceIdentifierFromParcelUuid(ParcelUuid parcelUuid) {
         UUID uuid = parcelUuid.getUuid();
         long value = (uuid.getMostSignificantBits() & 0xFFFFFFFF00000000L) >>> 32;
         return (int) value;
@@ -262,8 +232,12 @@
      * @param uuidBytes Byte representation of uuid.
      * @return {@link ParcelUuid} parsed from bytes.
      * @throws IllegalArgumentException If the {@code uuidBytes} cannot be parsed.
+     *
+     * @hide
      */
-    public static ParcelUuid parseUuidFrom(byte[] uuidBytes) {
+    @NonNull
+    @SystemApi
+    public static ParcelUuid parseUuidFrom(@Nullable byte[] uuidBytes) {
         if (uuidBytes == null) {
             throw new IllegalArgumentException("uuidBytes cannot be null");
         }
@@ -305,6 +279,8 @@
      * @param uuid uuid to parse.
      * @return shortest representation of {@code uuid} as bytes.
      * @throws IllegalArgumentException If the {@code uuid} is null.
+     *
+     * @hide
      */
     public static byte[] uuidToBytes(ParcelUuid uuid) {
         if (uuid == null) {
@@ -345,6 +321,8 @@
      *
      * @param parcelUuid
      * @return true if the parcelUuid can be converted to 16 bit uuid, false otherwise.
+     *
+     * @hide
      */
     @UnsupportedAppUsage
     public static boolean is16BitUuid(ParcelUuid parcelUuid) {
@@ -361,6 +339,8 @@
      *
      * @param parcelUuid
      * @return true if the parcelUuid can be converted to 32 bit uuid, false otherwise.
+     *
+     * @hide
      */
     @UnsupportedAppUsage
     public static boolean is32BitUuid(ParcelUuid parcelUuid) {
@@ -373,4 +353,6 @@
         }
         return ((uuid.getMostSignificantBits() & 0xFFFFFFFFL) == 0x1000L);
     }
+
+    private BluetoothUuid() {}
 }
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 341b520..d370a38 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3427,7 +3427,6 @@
             //@hide: TIME_DETECTOR_SERVICE,
             //@hide: TIME_ZONE_DETECTOR_SERVICE,
             PERMISSION_SERVICE,
-            INCREMENTAL_SERVICE,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface ServiceName {}
@@ -4971,6 +4970,13 @@
 
     /**
      * Use with {@link #getSystemService(String)} to retrieve an
+     * {@link android.content.pm.DataLoaderManager}.
+     * @hide
+     */
+    public static final String DATA_LOADER_MANAGER_SERVICE = "dataloadermanager";
+
+    /**
+     * Use with {@link #getSystemService(String)} to retrieve an
      * {@link android.os.incremental.IncrementalManager}.
      * @hide
      */
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 40aca0e..af7b986 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -4053,6 +4053,13 @@
     public static final String ACTION_SERVICE_STATE = "android.intent.action.SERVICE_STATE";
 
     /**
+     * Used for looking up a Data Loader Service providers.
+     * @hide
+     */
+    @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
+    public static final String ACTION_LOAD_DATA = "android.intent.action.LOAD_DATA";
+
+    /**
      * An int extra used with {@link #ACTION_SERVICE_STATE} which indicates voice registration
      * state.
      * @see android.telephony.ServiceState#STATE_EMERGENCY_ONLY
diff --git a/core/java/android/content/pm/DataLoaderManager.java b/core/java/android/content/pm/DataLoaderManager.java
new file mode 100644
index 0000000..2688038
--- /dev/null
+++ b/core/java/android/content/pm/DataLoaderManager.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Bundle;
+import android.os.RemoteException;
+
+/**
+ * Data loader manager takes care of data loaders of different packages. It provides methods to
+ * initialize a data loader binder service (binding and creating it), to return a binder of the data
+ * loader binder service and to destroy a data loader binder service.
+ * @see com.android.server.pm.DataLoaderManagerService
+ * @hide
+ */
+public class DataLoaderManager {
+    private static final String TAG = "DataLoaderManager";
+    private final IDataLoaderManager mService;
+
+    public DataLoaderManager(IDataLoaderManager service) {
+        mService = service;
+    }
+
+    /**
+     * Finds a data loader binder service and binds to it. This requires PackageManager.
+     *
+     * @param dataLoaderId ID for the new data loader binder service.
+     * @param params       Bundle that contains parameters to configure the data loader service.
+     *                     Must contain:
+     *                     key: "packageName", value: String, package name of data loader service
+     *                     package;
+     *                     key: "extras", value: Bundle, client-specific data structures
+     *
+     * @param listener     Callback for the data loader service to report status back to the
+     *                     caller.
+     * @return false if 1) target ID collides with a data loader that is already bound to data
+     * loader manager; 2) package name is not specified; 3) fails to find data loader package;
+     * or 4) fails to bind to the specified data loader service, otherwise return true.
+     */
+    public boolean initializeDataLoader(int dataLoaderId, @NonNull Bundle params,
+            @NonNull IDataLoaderStatusListener listener) {
+        try {
+            return mService.initializeDataLoader(dataLoaderId, params, listener);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns a binder interface of the data loader binder service, given its ID.
+     */
+    @Nullable
+    public IDataLoader getDataLoader(int dataLoaderId) {
+        try {
+            return mService.getDataLoader(dataLoaderId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Destroys the data loader binder service and removes it from data loader manager service.
+     */
+    @Nullable
+    public void destroyDataLoader(int dataLoaderId) {
+        try {
+            mService.destroyDataLoader(dataLoaderId);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+}
diff --git a/core/java/android/content/pm/IDataLoader.aidl b/core/java/android/content/pm/IDataLoader.aidl
new file mode 100644
index 0000000..60cc9ba9
--- /dev/null
+++ b/core/java/android/content/pm/IDataLoader.aidl
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm;
+
+import android.os.Bundle;
+import android.content.pm.IDataLoaderStatusListener;
+import android.content.pm.InstallationFile;
+import java.util.List;
+
+/**
+ * TODO: update with new APIs
+ * @hide
+ */
+oneway interface IDataLoader {
+   void create(int id, in Bundle params, IDataLoaderStatusListener listener);
+   void start(in List<InstallationFile> fileInfos);
+   void stop();
+   void destroy();
+   void onFileCreated(long inode, in byte[] metadata);
+}
diff --git a/core/java/android/content/pm/IDataLoaderManager.aidl b/core/java/android/content/pm/IDataLoaderManager.aidl
new file mode 100644
index 0000000..f453c9b
--- /dev/null
+++ b/core/java/android/content/pm/IDataLoaderManager.aidl
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm;
+
+import android.os.Bundle;
+import android.content.pm.IDataLoader;
+import android.content.pm.IDataLoaderStatusListener;
+import java.util.List;
+
+/** @hide */
+interface IDataLoaderManager {
+    boolean initializeDataLoader(int id, in Bundle params, IDataLoaderStatusListener listener);
+    IDataLoader getDataLoader(int dataLoaderId);
+    void destroyDataLoader(int dataLoaderId);
+}
\ No newline at end of file
diff --git a/core/java/android/service/incremental/IIncrementalDataLoaderStatusListener.aidl b/core/java/android/content/pm/IDataLoaderStatusListener.aidl
similarity index 80%
rename from core/java/android/service/incremental/IIncrementalDataLoaderStatusListener.aidl
rename to core/java/android/content/pm/IDataLoaderStatusListener.aidl
index f04242d..a60d6ee 100644
--- a/core/java/android/service/incremental/IIncrementalDataLoaderStatusListener.aidl
+++ b/core/java/android/content/pm/IDataLoaderStatusListener.aidl
@@ -14,13 +14,13 @@
  * limitations under the License.
  */
 
-package android.service.incremental;
+package android.content.pm;
 
 /**
- * Callbacks from DataLoaderService to IncrementalService to report data loader status.
+ * Callbacks from a data loader binder service to report data loader status.
  * @hide
  */
-oneway interface IIncrementalDataLoaderStatusListener {
+oneway interface IDataLoaderStatusListener {
     /** Data loader status */
     const int DATA_LOADER_READY = 0;
     const int DATA_LOADER_NOT_READY = 1;
@@ -31,6 +31,6 @@
     const int DATA_LOADER_CONNECTION_OK = 6;
 
     /** Data loader status callback */
-    void onStatusChanged(in int storageId, in int status);
+    void onStatusChanged(in int dataLoaderId, in int status);
 }
 
diff --git a/core/java/android/content/pm/IPackageInstallerSession.aidl b/core/java/android/content/pm/IPackageInstallerSession.aidl
index b740617..0b3c765 100644
--- a/core/java/android/content/pm/IPackageInstallerSession.aidl
+++ b/core/java/android/content/pm/IPackageInstallerSession.aidl
@@ -46,4 +46,5 @@
     int getParentSessionId();
 
     boolean isStaged();
+    void addFile(in String name, long size, in byte[] metadata);
 }
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index a71367d..b3d8eb5 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -26,6 +26,7 @@
 import android.content.pm.InstantAppInfo;
 import android.content.pm.FeatureInfo;
 import android.content.pm.IDexModuleRegisterCallback;
+import android.content.pm.InstallSourceInfo;
 import android.content.pm.IPackageInstaller;
 import android.content.pm.IPackageDeleteObserver;
 import android.content.pm.IPackageDeleteObserver2;
@@ -237,6 +238,8 @@
     @UnsupportedAppUsage
     String getInstallerPackageName(in String packageName);
 
+    InstallSourceInfo getInstallSourceInfo(in String packageName);
+
     void resetApplicationPreferences(int userId);
 
     @UnsupportedAppUsage
diff --git a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl b/core/java/android/content/pm/InstallSourceInfo.aidl
similarity index 74%
copy from wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl
copy to core/java/android/content/pm/InstallSourceInfo.aidl
index 007ec94..21ee4c3 100644
--- a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl
+++ b/core/java/android/content/pm/InstallSourceInfo.aidl
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2014, The Android Open Source Project
+/*
+ * Copyright 2019 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
- *     http://www.apache.org/licenses/LICENSE-2.0
+ *      http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 
-package android.net.wifi;
+package android.content.pm;
 
-parcelable WifiActivityEnergyInfo;
+parcelable InstallSourceInfo;
diff --git a/core/java/android/content/pm/InstallSourceInfo.java b/core/java/android/content/pm/InstallSourceInfo.java
new file mode 100644
index 0000000..4d235f1
--- /dev/null
+++ b/core/java/android/content/pm/InstallSourceInfo.java
@@ -0,0 +1,110 @@
+/*
+ * 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.content.pm;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Information about how an app was installed.
+ * @see PackageManager#getInstallSourceInfo(String)
+ */
+public final class InstallSourceInfo implements Parcelable {
+
+    @Nullable private final String mInitiatingPackageName;
+
+    @Nullable private final String mOriginatingPackageName;
+
+    @Nullable private final String mInstallingPackageName;
+
+    /** @hide */
+    public InstallSourceInfo(@Nullable String initiatingPackageName,
+            @Nullable String originatingPackageName, @Nullable String installingPackageName) {
+        this.mInitiatingPackageName = initiatingPackageName;
+        this.mOriginatingPackageName = originatingPackageName;
+        this.mInstallingPackageName = installingPackageName;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeString(mInitiatingPackageName);
+        dest.writeString(mOriginatingPackageName);
+        dest.writeString(mInstallingPackageName);
+    }
+
+    private InstallSourceInfo(Parcel source) {
+        mInitiatingPackageName = source.readString();
+        mOriginatingPackageName = source.readString();
+        mInstallingPackageName = source.readString();
+    }
+
+    /** The name of the package that requested the installation, or null if not available. */
+    @Nullable
+    public String getInitiatingPackageName() {
+        return mInitiatingPackageName;
+    }
+
+    /**
+     * The name of the package on behalf of which the initiating package requested the installation,
+     * or null if not available.
+     * <p>
+     * For example if a downloaded APK is installed via the Package Installer this could be the
+     * app that performed the download. This value is provided by the initiating package and not
+     * verified by the framework.
+     * <p>
+     * Note that the {@code InstallSourceInfo} returned by
+     * {@link PackageManager#getInstallSourceInfo(String)} will not have this information
+     * available unless the calling application holds the INSTALL_PACKAGES permission.
+     */
+    @Nullable
+    public String getOriginatingPackageName() {
+        return mOriginatingPackageName;
+    }
+
+    /**
+     * The name of the package responsible for the installation (the installer of record), or null
+     * if not available.
+     * Note that this may differ from the initiating package name and can be modified.
+     *
+     * @see PackageManager#setInstallerPackageName(String, String)
+     */
+    @Nullable
+    public String getInstallingPackageName() {
+        return mInstallingPackageName;
+    }
+
+    @NonNull
+    public static final Parcelable.Creator<InstallSourceInfo> CREATOR =
+            new Creator<InstallSourceInfo>() {
+                @Override
+                public InstallSourceInfo createFromParcel(Parcel source) {
+                    return new InstallSourceInfo(source);
+                }
+
+                @Override
+                public InstallSourceInfo[] newArray(int size) {
+                    return new InstallSourceInfo[size];
+                }
+            };
+}
diff --git a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl b/core/java/android/content/pm/InstallationFile.aidl
similarity index 68%
copy from wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl
copy to core/java/android/content/pm/InstallationFile.aidl
index 007ec94..1edff9d 100644
--- a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl
+++ b/core/java/android/content/pm/InstallationFile.aidl
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2014, The Android Open Source Project
+/*
+ * Copyright (C) 2019 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
- *     http://www.apache.org/licenses/LICENSE-2.0
+ *      http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
-package android.net.wifi;
+package android.content.pm;
 
-parcelable WifiActivityEnergyInfo;
+/**
+ * Describes a file which is part of a package installation.
+ */
+parcelable InstallationFile;
+
diff --git a/core/java/android/content/pm/InstallationFile.java b/core/java/android/content/pm/InstallationFile.java
new file mode 100644
index 0000000..ac5fd1e
--- /dev/null
+++ b/core/java/android/content/pm/InstallationFile.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.pm;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Defines the properties of a file in an installation session.
+ * TODO(b/136132412): update with new APIs.
+ *
+ * @hide
+ */
+public final class InstallationFile implements Parcelable {
+    public static final int FILE_TYPE_UNKNOWN = -1;
+    public static final int FILE_TYPE_APK = 0;
+    public static final int FILE_TYPE_LIB = 1;
+    public static final int FILE_TYPE_OBB = 2;
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = {"FILE_TYPE_"}, value = {
+            FILE_TYPE_APK,
+            FILE_TYPE_LIB,
+            FILE_TYPE_OBB,
+    })
+    public @interface FileType {
+    }
+
+    private String mFileName;
+    private @FileType int mFileType;
+    private long mFileSize;
+    private byte[] mMetadata;
+
+    public InstallationFile(@NonNull String fileName, long fileSize,
+            @Nullable byte[] metadata) {
+        mFileName = fileName;
+        mFileSize = fileSize;
+        mMetadata = metadata;
+        if (fileName.toLowerCase().endsWith(".apk")) {
+            mFileType = FILE_TYPE_APK;
+        } else if (fileName.toLowerCase().endsWith(".obb")) {
+            mFileType = FILE_TYPE_OBB;
+        } else if (fileName.toLowerCase().endsWith(".so") && fileName.toLowerCase().startsWith(
+                "lib/")) {
+            mFileType = FILE_TYPE_LIB;
+        } else {
+            mFileType = FILE_TYPE_UNKNOWN;
+        }
+    }
+
+    public @FileType int getFileType() {
+        return mFileType;
+    }
+
+    public @NonNull String getName() {
+        return mFileName;
+    }
+
+    public long getSize() {
+        return mFileSize;
+    }
+
+    public @Nullable byte[] getMetadata() {
+        return mMetadata;
+    }
+
+    private InstallationFile(Parcel source) {
+        mFileName = source.readString();
+        mFileType = source.readInt();
+        mFileSize = source.readLong();
+        mMetadata = source.createByteArray();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        dest.writeString(mFileName);
+        dest.writeInt(mFileType);
+        dest.writeLong(mFileSize);
+        dest.writeByteArray(mMetadata);
+    }
+
+    public static final @NonNull Creator<InstallationFile> CREATOR =
+            new Creator<InstallationFile>() {
+        public InstallationFile createFromParcel(Parcel source) {
+            return new InstallationFile(source);
+        }
+
+        public InstallationFile[] newArray(int size) {
+            return new InstallationFile[size];
+        }
+    };
+
+}
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 4ab6f3c..e9fc8f6 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -50,6 +50,8 @@
 import android.os.RemoteException;
 import android.os.SystemProperties;
 import android.os.UserHandle;
+import android.os.incremental.IncrementalDataLoaderParams;
+import android.os.incremental.IncrementalDataLoaderParamsParcel;
 import android.system.ErrnoException;
 import android.system.Os;
 import android.util.ArraySet;
@@ -1212,6 +1214,27 @@
         }
 
         /**
+         * Configure files for an installation session.
+         *
+         * Currently only for Incremental installation session. Once this method is called,
+         * the files and their paths, as specified in the parameters, will be created and properly
+         * configured in the Incremental File System.
+         *
+         * TODO(b/136132412): update this and InstallationFile class with latest API design.
+         *
+         * @throws IllegalStateException if {@link SessionParams#incrementalParams} is null.
+         *
+         * @hide
+         */
+        public void addFile(@NonNull String name, long size, @NonNull byte[] metadata) {
+            try {
+                mSession.addFile(name, size, metadata);
+            } catch (RemoteException e) {
+                throw e.rethrowFromSystemServer();
+            }
+        }
+
+        /**
          * Release this session object. You can open the session again if it
          * hasn't been finalized.
          */
@@ -1398,6 +1421,8 @@
         public boolean isStaged;
         /** {@hide} */
         public long requiredInstalledVersionCode = PackageManager.VERSION_CODE_HIGHEST;
+        /** {@hide} */
+        public IncrementalDataLoaderParams incrementalParams;
 
         /**
          * Construct parameters for a new package install session.
@@ -1431,6 +1456,12 @@
             isMultiPackage = source.readBoolean();
             isStaged = source.readBoolean();
             requiredInstalledVersionCode = source.readLong();
+            IncrementalDataLoaderParamsParcel dataLoaderParamsParcel = source.readParcelable(
+                    IncrementalDataLoaderParamsParcel.class.getClassLoader());
+            if (dataLoaderParamsParcel != null) {
+                incrementalParams = new IncrementalDataLoaderParams(
+                        dataLoaderParamsParcel);
+            }
         }
 
         /** {@hide} */
@@ -1454,6 +1485,7 @@
             ret.isMultiPackage = isMultiPackage;
             ret.isStaged = isStaged;
             ret.requiredInstalledVersionCode = requiredInstalledVersionCode;
+            ret.incrementalParams = incrementalParams;
             return ret;
         }
 
@@ -1782,6 +1814,16 @@
             return (installFlags & PackageManager.INSTALL_ENABLE_ROLLBACK) != 0;
         }
 
+        /**
+         * Set Incremental data loader params.
+         *
+         * {@hide}
+         */
+        @RequiresPermission(Manifest.permission.INSTALL_PACKAGES)
+        public void setIncrementalParams(@NonNull IncrementalDataLoaderParams incrementalParams) {
+            this.incrementalParams = incrementalParams;
+        }
+
         /** {@hide} */
         public void dump(IndentingPrintWriter pw) {
             pw.printPair("mode", mode);
@@ -1831,6 +1873,11 @@
             dest.writeBoolean(isMultiPackage);
             dest.writeBoolean(isStaged);
             dest.writeLong(requiredInstalledVersionCode);
+            if (incrementalParams != null) {
+                dest.writeParcelable(incrementalParams.getData(), flags);
+            } else {
+                dest.writeParcelable(null, flags);
+            }
         }
 
         public static final Parcelable.Creator<SessionParams>
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index bbfdf91..6e890ba 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -5989,11 +5989,30 @@
      *
      * @param packageName The name of the package to query
      * @throws IllegalArgumentException if the given package name is not installed
+     *
+     * @deprecated use {@link #getInstallSourceInfo(String)} instead
      */
+    @Deprecated
     @Nullable
     public abstract String getInstallerPackageName(@NonNull String packageName);
 
     /**
+     * Retrieves information about how a package was installed or updated.
+     * <p>
+     * If the calling application does not hold the INSTALL_PACKAGES permission then
+     * the result will always return {@code null} from
+     * {@link InstallSourceInfo#getOriginatingPackageName()}.
+     *
+     * @param packageName The name of the package to query
+     * @throws NameNotFoundException if the given package name is not installed
+     */
+    @NonNull
+    public InstallSourceInfo getInstallSourceInfo(@NonNull String packageName)
+            throws NameNotFoundException {
+        throw new UnsupportedOperationException("getInstallSourceInfo not implemented");
+    }
+
+    /**
      * Attempts to clear the user data directory of an application.
      * Since this may take a little while, the result will
      * be posted back to the given observer.  A deletion will fail if the
diff --git a/core/java/android/content/res/loader/ResourceLoaderManager.java b/core/java/android/content/res/loader/ResourceLoaderManager.java
index ddbfa81..592ec09 100644
--- a/core/java/android/content/res/loader/ResourceLoaderManager.java
+++ b/core/java/android/content/res/loader/ResourceLoaderManager.java
@@ -151,6 +151,7 @@
     public void onImplUpdate(ResourcesImpl resourcesImpl) {
         synchronized (mLock) {
             this.mResourcesImpl = resourcesImpl;
+            this.mResourcesImpl.getAssets().setResourceLoaderManager(this);
             updateLoaders();
         }
     }
diff --git a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl b/core/java/android/net/MacAddress.aidl
similarity index 80%
copy from wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl
copy to core/java/android/net/MacAddress.aidl
index 007ec94..48a18a7 100644
--- a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl
+++ b/core/java/android/net/MacAddress.aidl
@@ -1,5 +1,6 @@
 /**
- * Copyright (c) 2014, The Android Open Source Project
+ *
+ * Copyright (C) 2019 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,6 +15,6 @@
  * limitations under the License.
  */
 
-package android.net.wifi;
+package android.net;
 
-parcelable WifiActivityEnergyInfo;
+@JavaOnlyStableParcelable parcelable MacAddress;
diff --git a/core/java/android/net/Network.java b/core/java/android/net/Network.java
index 3f56def..f12ba13 100644
--- a/core/java/android/net/Network.java
+++ b/core/java/android/net/Network.java
@@ -63,7 +63,7 @@
     /**
      * @hide
      */
-    @UnsupportedAppUsage
+    @SystemApi
     public final int netId;
 
     // Objects used to perform per-network operations such as getSocketFactory
diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java
index 1c6a484..bf4884a 100644
--- a/core/java/android/net/TrafficStats.java
+++ b/core/java/android/net/TrafficStats.java
@@ -16,6 +16,7 @@
 
 package android.net;
 
+import android.annotation.NonNull;
 import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
@@ -595,8 +596,15 @@
         return total;
     }
 
-    /** {@hide} */
-    public static long getTxPackets(String iface) {
+    /**
+     * Return the number of packets transmitted on the specified interface since
+     * device boot. Statistics are measured at the network layer, so both TCP and
+     * UDP usage are included.
+     *
+     * @param iface The name of the interface.
+     * @return The number of transmitted packets.
+     */
+    public static long getTxPackets(@NonNull String iface) {
         try {
             return getStatsService().getIfaceStats(iface, TYPE_TX_PACKETS);
         } catch (RemoteException e) {
@@ -604,8 +612,15 @@
         }
     }
 
-    /** {@hide} */
-    public static long getRxPackets(String iface) {
+    /**
+     * Return the number of packets received on the specified interface since
+     * device boot. Statistics are measured at the network layer, so both TCP
+     * and UDP usage are included.
+     *
+     * @param iface The name of the interface.
+     * @return The number of received packets.
+     */
+    public static long getRxPackets(@NonNull String iface) {
         try {
             return getStatsService().getIfaceStats(iface, TYPE_RX_PACKETS);
         } catch (RemoteException e) {
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index 40048d9..e81a505 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -87,6 +87,7 @@
     void setDefaultGuestRestrictions(in Bundle restrictions);
     Bundle getDefaultGuestRestrictions();
     boolean markGuestForDeletion(int userId);
+    UserInfo findCurrentGuestUser();
     boolean isQuietModeEnabled(int userId);
     void setSeedAccountData(int userId, in String accountName,
             in String accountType, in PersistableBundle accountOptions, boolean persist);
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index dbe8dc3..9e9cd92 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -2375,6 +2375,20 @@
     }
 
     /**
+     * Gets the existing guest user if it exists.  This does not include guest users that are dying.
+     * @return The existing guest user if it exists. Null otherwise.
+     * @hide
+     */
+    @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+    public UserInfo findCurrentGuestUser() {
+        try {
+            return mService.findCurrentGuestUser();
+        } catch (RemoteException re) {
+            throw re.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Creates a user with the specified name and options as a profile of another user.
      * Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
      * The type of profile must be specified using the given flags.
diff --git a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl b/core/java/android/os/connectivity/WifiActivityEnergyInfo.aidl
similarity index 94%
rename from wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl
rename to core/java/android/os/connectivity/WifiActivityEnergyInfo.aidl
index 007ec94..ce75c12 100644
--- a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl
+++ b/core/java/android/os/connectivity/WifiActivityEnergyInfo.aidl
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 
-package android.net.wifi;
+package android.os.connectivity;
 
 parcelable WifiActivityEnergyInfo;
diff --git a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.java b/core/java/android/os/connectivity/WifiActivityEnergyInfo.java
similarity index 99%
rename from wifi/java/android/net/wifi/WifiActivityEnergyInfo.java
rename to core/java/android/os/connectivity/WifiActivityEnergyInfo.java
index f0f31fa..7db003d 100644
--- a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.java
+++ b/core/java/android/os/connectivity/WifiActivityEnergyInfo.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package android.net.wifi;
+package android.os.connectivity;
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
diff --git a/core/java/android/os/incremental/IIncrementalServiceProxy.aidl b/core/java/android/os/incremental/IIncrementalServiceProxy.aidl
index 12740ea..ffff52e 100644
--- a/core/java/android/os/incremental/IIncrementalServiceProxy.aidl
+++ b/core/java/android/os/incremental/IIncrementalServiceProxy.aidl
@@ -18,7 +18,7 @@
 
 import android.os.incremental.IncrementalFileSystemControlParcel;
 import android.os.incremental.IncrementalDataLoaderParamsParcel;
-import android.service.incremental.IIncrementalDataLoaderStatusListener;
+import android.content.pm.IDataLoaderStatusListener;
 
 /**
  * Binder service to receive calls from native Incremental Service and handle Java tasks such as
@@ -29,7 +29,7 @@
     boolean prepareDataLoader(int mountId,
         in IncrementalFileSystemControlParcel control,
         in IncrementalDataLoaderParamsParcel params,
-        in IIncrementalDataLoaderStatusListener listener);
+        in IDataLoaderStatusListener listener);
     boolean startDataLoader(int mountId);
     void showHealthBlockedUI(int mountId);
     void destroyDataLoader(int mountId);
diff --git a/core/java/android/os/incremental/IncrementalFileStorages.java b/core/java/android/os/incremental/IncrementalFileStorages.java
new file mode 100644
index 0000000..5bd0748
--- /dev/null
+++ b/core/java/android/os/incremental/IncrementalFileStorages.java
@@ -0,0 +1,280 @@
+/*
+ * 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.os.incremental;
+
+/**
+ * Set up files and directories used in an installation session.
+ * Currently only used by Incremental Installation.
+ * For Incremental installation, the expected outcome of this function is:
+ * 0) All the files are in defaultStorage
+ * 1) All APK files are in the same directory, bound to mApkStorage, and bound to the
+ * InstallerSession's stage dir. The files are linked from mApkStorage to defaultStorage.
+ * 2) All lib files are in the sub directories as their names suggest, and in the same parent
+ * directory as the APK files. The files are linked from mApkStorage to defaultStorage.
+ * 3) OBB files are in another directory that is different from APK files and lib files, bound
+ * to mObbStorage. The files are linked from mObbStorage to defaultStorage.
+ *
+ * @throws IllegalStateException the session is not an Incremental installation session.
+ */
+
+import static dalvik.system.VMRuntime.getInstructionSet;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.pm.InstallationFile;
+import android.os.IVold;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.ArraySet;
+import android.util.Slog;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Random;
+
+/**
+ * This class manages storage instances used during a package installation session.
+ * @hide
+ */
+public final class IncrementalFileStorages {
+    private static final String TAG = "IncrementalFileStorages";
+    private @Nullable IncrementalStorage mDefaultStorage;
+    private @Nullable IncrementalStorage mApkStorage;
+    private @Nullable IncrementalStorage mObbStorage;
+    private @Nullable String mDefaultDir;
+    private @Nullable String mObbDir;
+    private @NonNull IncrementalManager mIncrementalManager;
+    private @Nullable ArraySet<String> mLibDirs;
+    private @NonNull String mPackageName;
+    private @NonNull File mStageDir;
+
+    /**
+     * Set up files and directories used in an installation session.
+     * Currently only used by Incremental Installation.
+     * For Incremental installation, the expected outcome of this function is:
+     * 0) All the files are in defaultStorage
+     * 1) All APK files are in the same directory, bound to mApkStorage, and bound to the
+     * InstallerSession's stage dir. The files are linked from mApkStorage to defaultStorage.
+     * 2) All lib files are in the sub directories as their names suggest, and in the same parent
+     * directory as the APK files. The files are linked from mApkStorage to defaultStorage.
+     * 3) OBB files are in another directory that is different from APK files and lib files, bound
+     * to mObbStorage. The files are linked from mObbStorage to defaultStorage.
+     *
+     * @throws IllegalStateException the session is not an Incremental installation session.
+     */
+    public IncrementalFileStorages(@NonNull String packageName,
+            @NonNull File stageDir,
+            @NonNull IncrementalManager incrementalManager,
+            @NonNull IncrementalDataLoaderParams incrementalDataLoaderParams) {
+        mPackageName = packageName;
+        mStageDir = stageDir;
+        mIncrementalManager = incrementalManager;
+        if (incrementalDataLoaderParams.getPackageName().equals("local")) {
+            final String incrementalPath = incrementalDataLoaderParams.getStaticArgs();
+            mDefaultStorage = mIncrementalManager.openStorage(incrementalPath);
+            mDefaultDir = incrementalPath;
+            return;
+        }
+        mDefaultDir = getTempDir();
+        if (mDefaultDir == null) {
+            return;
+        }
+        mDefaultStorage = mIncrementalManager.createStorage(mDefaultDir,
+                incrementalDataLoaderParams,
+                IncrementalManager.CREATE_MODE_CREATE
+                        | IncrementalManager.CREATE_MODE_TEMPORARY_BIND, false);
+    }
+
+    /**
+     * Adds a file into the installation session. Makes sure it will be placed inside
+     * a proper storage instance, based on its file type.
+     */
+    public void addFile(@NonNull InstallationFile file) throws IOException {
+        if (mDefaultStorage == null) {
+            throw new IOException("Cannot add file because default storage does not exist");
+        }
+        if (file.getFileType() == InstallationFile.FILE_TYPE_APK) {
+            addApkFile(file);
+        } else if (file.getFileType() == InstallationFile.FILE_TYPE_OBB) {
+            addObbFile(file);
+        } else if (file.getFileType() == InstallationFile.FILE_TYPE_LIB) {
+            addLibFile(file);
+        } else {
+            throw new IOException("Unknown file type: " + file.getFileType());
+        }
+    }
+
+    private void addApkFile(@NonNull InstallationFile apk) throws IOException {
+        // Create a storage for APK files and lib files
+        final String stageDirPath = mStageDir.getAbsolutePath();
+        if (mApkStorage == null) {
+            mApkStorage = mIncrementalManager.createStorage(stageDirPath, mDefaultStorage,
+                    IncrementalManager.CREATE_MODE_CREATE
+                            | IncrementalManager.CREATE_MODE_TEMPORARY_BIND);
+            mApkStorage.bind(stageDirPath);
+        }
+
+        if (!new File(mDefaultDir, apk.getName()).exists()) {
+            mDefaultStorage.makeFile(apk.getName(), apk.getSize(),
+                    apk.getMetadata());
+        }
+        // Assuming APK files are already named properly, e.g., "base.apk"
+        mDefaultStorage.makeLink(apk.getName(), mApkStorage, apk.getName());
+    }
+
+    private void addLibFile(@NonNull InstallationFile lib) throws IOException {
+        // TODO(b/136132412): remove this after we have incfs support for lib file mapping
+        if (mApkStorage == null) {
+            throw new IOException("Cannot add lib file without adding an apk file first");
+        }
+        if (mLibDirs == null) {
+            mLibDirs = new ArraySet<>();
+        }
+        String current = "";
+        final Path libDirPath = Paths.get(lib.getName()).getParent();
+        final int numDirComponents = libDirPath.getNameCount();
+        for (int i = 0; i < numDirComponents; i++) {
+            String dirName = libDirPath.getName(i).toString();
+            try {
+                dirName = getInstructionSet(dirName);
+            } catch (IllegalArgumentException ignored) {
+            }
+            current += dirName;
+            if (!mLibDirs.contains(current)) {
+                mDefaultStorage.makeDirectory(current);
+                mApkStorage.makeDirectory(current);
+                mLibDirs.add(current);
+            }
+            current += '/';
+        }
+        String libFilePath = current + Paths.get(lib.getName()).getFileName();
+        mDefaultStorage.makeFile(libFilePath, lib.getSize(), lib.getMetadata());
+        mDefaultStorage.makeLink(libFilePath, mApkStorage, libFilePath);
+    }
+
+    private void addObbFile(@NonNull InstallationFile obb) throws IOException {
+        if (mObbStorage == null) {
+            // Create a storage for OBB files
+            mObbDir = getTempDir();
+            if (mObbDir == null) {
+                throw new IOException("Failed to create obb storage directory.");
+            }
+            mObbStorage = mIncrementalManager.createStorage(
+                    mObbDir, mDefaultStorage,
+                    IncrementalManager.CREATE_MODE_CREATE
+                            | IncrementalManager.CREATE_MODE_TEMPORARY_BIND);
+        }
+        mDefaultStorage.makeFile(obb.getName(), obb.getSize(), obb.getMetadata());
+        mDefaultStorage.makeLink(obb.getName(), mObbStorage, obb.getName());
+    }
+
+    private boolean hasObb() {
+        return (mObbStorage != null && mObbDir != null);
+    }
+
+    /**
+     * Starts loading data for default storage.
+     * TODO(b/136132412): update the implementation with latest API design.
+     */
+    public boolean startLoading() {
+        if (mDefaultStorage == null) {
+            return false;
+        }
+        return mDefaultStorage.startLoading();
+    }
+
+    /**
+     * Sets up obb storage directory and create bindings.
+     */
+    public void finishSetUp() {
+        if (!hasObb()) {
+            return;
+        }
+        final String mainObbDir = String.format("/storage/emulated/0/Android/obb/%s", mPackageName);
+        final String packageObbDirRoot =
+                String.format("/mnt/runtime/%s/emulated/0/Android/obb/", mPackageName);
+        final String[] obbDirs = {
+                packageObbDirRoot + "read",
+                packageObbDirRoot + "write",
+                packageObbDirRoot + "full",
+                packageObbDirRoot + "default",
+                String.format("/data/media/0/Android/obb/%s", mPackageName),
+                mainObbDir,
+        };
+        try {
+            Slog.i(TAG, "Creating obb directory '" + mainObbDir + "'");
+            final IVold vold = IVold.Stub.asInterface(ServiceManager.getServiceOrThrow("vold"));
+            vold.mkdirs(mainObbDir);
+            for (String d : obbDirs) {
+                mObbStorage.bindPermanent(d);
+            }
+        } catch (ServiceManager.ServiceNotFoundException ex) {
+            Slog.e(TAG, "vold service is not found.");
+            cleanUp();
+        } catch (IOException | RemoteException ex) {
+            Slog.e(TAG, "Failed to create obb dir at: " + mainObbDir, ex);
+            cleanUp();
+        }
+    }
+
+    /**
+     * Resets the states and unbinds storage instances for an installation session.
+     * TODO(b/136132412): make sure unnecessary binds are removed but useful storages are kept
+     */
+    public void cleanUp() {
+        if (mDefaultStorage != null && mDefaultDir != null) {
+            try {
+                mDefaultStorage.unBind(mDefaultDir);
+            } catch (IOException ignored) {
+            }
+            mDefaultDir = null;
+            mDefaultStorage = null;
+        }
+        if (mApkStorage != null && mStageDir != null) {
+            try {
+                mApkStorage.unBind(mStageDir.getAbsolutePath());
+            } catch (IOException ignored) {
+            }
+            mApkStorage = null;
+        }
+        if (mObbStorage != null && mObbDir != null) {
+            try {
+                mObbStorage.unBind(mObbDir);
+            } catch (IOException ignored) {
+            }
+            mObbDir = null;
+            mObbStorage = null;
+        }
+    }
+
+    private String getTempDir() {
+        final String tmpDirRoot = "/data/tmp";
+        final Random random = new Random();
+        final Path tmpDir =
+                Paths.get(tmpDirRoot, String.valueOf(random.nextInt(Integer.MAX_VALUE - 1)));
+        try {
+            Files.createDirectories(tmpDir);
+        } catch (Exception ex) {
+            Slog.e(TAG, "Failed to create dir", ex);
+            return null;
+        }
+        return tmpDir.toAbsolutePath().toString();
+    }
+}
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index 6c4ee01..09286fe 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -17,6 +17,7 @@
 package android.permission;
 
 import android.Manifest;
+import android.annotation.CallbackExecutor;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -29,6 +30,8 @@
 import android.content.pm.IPackageManager;
 import android.content.pm.permission.SplitPermissionInfoParcelable;
 import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.UserHandle;
 import android.util.Slog;
 
 import com.android.internal.annotations.Immutable;
@@ -36,6 +39,8 @@
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
 
 /**
  * System level service for accessing the permission capabilities of the platform.
@@ -59,6 +64,8 @@
 
     private final IPackageManager mPackageManager;
 
+    private final IPermissionManager mPermissionManager;
+
     private List<SplitPermissionInfo> mSplitPermissionInfos;
 
     /**
@@ -67,9 +74,12 @@
      * @param context The current context in which to operate.
      * @hide
      */
-    public PermissionManager(@NonNull Context context, IPackageManager packageManager) {
+    public PermissionManager(@NonNull Context context, IPackageManager packageManager)
+            throws ServiceManager.ServiceNotFoundException {
         mContext = context;
         mPackageManager = packageManager;
+        mPermissionManager = IPermissionManager.Stub.asInterface(
+                ServiceManager.getServiceOrThrow("permissionmgr"));
     }
 
     /**
@@ -145,6 +155,48 @@
         return mSplitPermissionInfos;
     }
 
+    /**
+     * Grant default permissions to currently active LUI app
+     * @param packageName The package name for the LUI app
+     * @param user The user handle
+     * @param callback The callback provided by caller to be notified when grant completes
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS)
+    public void grantDefaultPermissionsToLuiApp(
+            @NonNull String packageName, @NonNull UserHandle user,
+            @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback) {
+        try {
+            mPermissionManager.grantDefaultPermissionsToActiveLuiApp(
+                    packageName, user.getIdentifier());
+            executor.execute(() -> callback.accept(true));
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Revoke default permissions to currently active LUI app
+     * @param packageNames The package names for the LUI apps
+     * @param user The user handle
+     * @param callback The callback provided by caller to be notified when grant completes
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS)
+    public void revokeDefaultPermissionsFromLuiApps(
+            @NonNull String[] packageNames, @NonNull UserHandle user,
+            @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback) {
+        try {
+            mPermissionManager.revokeDefaultPermissionsFromLuiApps(
+                    packageNames, user.getIdentifier());
+            executor.execute(() -> callback.accept(true));
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
     private List<SplitPermissionInfo> splitPermissionInfoListToNonParcelableList(
             List<SplitPermissionInfoParcelable> parcelableList) {
         final int size = parcelableList.size();
diff --git a/core/java/android/provider/BlockedNumberContract.java b/core/java/android/provider/BlockedNumberContract.java
index 7995f22..1eb7664 100644
--- a/core/java/android/provider/BlockedNumberContract.java
+++ b/core/java/android/provider/BlockedNumberContract.java
@@ -16,7 +16,6 @@
 package android.provider;
 
 import android.annotation.IntDef;
-import android.annotation.NonNull;
 import android.annotation.SystemApi;
 import android.annotation.WorkerThread;
 import android.content.Context;
@@ -155,12 +154,7 @@
     /** The authority for the blocked number provider */
     public static final String AUTHORITY = "com.android.blockednumber";
 
-    /**
-     * A content:// style uri to the authority for the blocked number provider
-     * @hide
-     */
-    @NonNull
-    @SystemApi
+    /** A content:// style uri to the authority for the blocked number provider */
     public static final Uri AUTHORITY_URI = Uri.parse("content://" + AUTHORITY);
 
     private static final String LOG_TAG = BlockedNumberContract.class.getSimpleName();
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index 01f9c73..2c53025 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -20,6 +20,7 @@
 import android.annotation.CurrentTimeMillisLong;
 import android.annotation.CurrentTimeSecondsLong;
 import android.annotation.DurationMillisLong;
+import android.annotation.IntDef;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -78,6 +79,8 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.text.Collator;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -205,8 +208,10 @@
     public static final String PARAM_DELETE_DATA = "deletedata";
 
     /** {@hide} */
+    @Deprecated
     public static final String PARAM_INCLUDE_PENDING = "includePending";
     /** {@hide} */
+    @Deprecated
     public static final String PARAM_INCLUDE_TRASHED = "includeTrashed";
     /** {@hide} */
     public static final String PARAM_PROGRESS = "progress";
@@ -559,6 +564,101 @@
     public static final String UNKNOWN_STRING = "<unknown>";
 
     /**
+     * Specify a {@link Uri} that is "related" to the current operation being
+     * performed.
+     * <p>
+     * This is typically used to allow an operation that may normally be
+     * rejected, such as making a copy of a pre-existing image located under a
+     * {@link MediaColumns#RELATIVE_PATH} where new images are not allowed.
+     * <p>
+     * It's strongly recommended that when making a copy of pre-existing content
+     * that you define the "original document ID" GUID as defined by the <em>XMP
+     * Media Management</em> standard.
+     * <p>
+     * This key can be placed in a {@link Bundle} of extras and passed to
+     * {@link ContentResolver#insert}.
+     */
+    public static final String QUERY_ARG_RELATED_URI = "android:query-arg-related-uri";
+
+    /**
+     * Specify how {@link MediaColumns#IS_PENDING} items should be filtered when
+     * performing a {@link MediaStore} operation.
+     * <p>
+     * This key can be placed in a {@link Bundle} of extras and passed to
+     * {@link ContentResolver#query}, {@link ContentResolver#update}, or
+     * {@link ContentResolver#delete}.
+     * <p>
+     * By default, pending items are filtered away from operations.
+     */
+    @Match
+    public static final String QUERY_ARG_MATCH_PENDING = "android:query-arg-match-pending";
+
+    /**
+     * Specify how {@link MediaColumns#IS_TRASHED} items should be filtered when
+     * performing a {@link MediaStore} operation.
+     * <p>
+     * This key can be placed in a {@link Bundle} of extras and passed to
+     * {@link ContentResolver#query}, {@link ContentResolver#update}, or
+     * {@link ContentResolver#delete}.
+     * <p>
+     * By default, trashed items are filtered away from operations.
+     */
+    @Match
+    public static final String QUERY_ARG_MATCH_TRASHED = "android:query-arg-match-trashed";
+
+    /**
+     * Specify how {@link MediaColumns#IS_FAVORITE} items should be filtered
+     * when performing a {@link MediaStore} operation.
+     * <p>
+     * This key can be placed in a {@link Bundle} of extras and passed to
+     * {@link ContentResolver#query}, {@link ContentResolver#update}, or
+     * {@link ContentResolver#delete}.
+     * <p>
+     * By default, favorite items are <em>not</em> filtered away from
+     * operations.
+     */
+    @Match
+    public static final String QUERY_ARG_MATCH_FAVORITE = "android:query-arg-match-favorite";
+
+    /** @hide */
+    @IntDef(flag = true, prefix = { "MATCH_" }, value = {
+            MATCH_DEFAULT,
+            MATCH_INCLUDE,
+            MATCH_EXCLUDE,
+            MATCH_ONLY,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface Match {}
+
+    /**
+     * Value indicating that the default matching behavior should be used, as
+     * defined by the key documentation.
+     */
+    public static final int MATCH_DEFAULT = 0;
+
+    /**
+     * Value indicating that operations should include items matching the
+     * criteria defined by this key.
+     * <p>
+     * Note that items <em>not</em> matching the criteria <em>may</em> also be
+     * included depending on the default behavior documented by the key. If you
+     * want to operate exclusively on matching items, use {@link #MATCH_ONLY}.
+     */
+    public static final int MATCH_INCLUDE = 1;
+
+    /**
+     * Value indicating that operations should exclude items matching the
+     * criteria defined by this key.
+     */
+    public static final int MATCH_EXCLUDE = 2;
+
+    /**
+     * Value indicating that operations should only operate on items explicitly
+     * matching the criteria defined by this key.
+     */
+    public static final int MATCH_ONLY = 3;
+
+    /**
      * Update the given {@link Uri} to also include any pending media items from
      * calls such as
      * {@link ContentResolver#query(Uri, String[], Bundle, CancellationSignal)}.
@@ -566,12 +666,16 @@
      *
      * @see MediaColumns#IS_PENDING
      * @see MediaStore#getIncludePending(Uri)
+     * @deprecated consider migrating to {@link #QUERY_ARG_MATCH_PENDING} which
+     *             is more expressive.
      */
+    @Deprecated
     public static @NonNull Uri setIncludePending(@NonNull Uri uri) {
         return setIncludePending(uri.buildUpon()).build();
     }
 
     /** @hide */
+    @Deprecated
     public static @NonNull Uri.Builder setIncludePending(@NonNull Uri.Builder uriBuilder) {
         return uriBuilder.appendQueryParameter(PARAM_INCLUDE_PENDING, "1");
     }
@@ -582,7 +686,11 @@
      *
      * @see MediaColumns#IS_PENDING
      * @see MediaStore#setIncludePending(Uri)
+     * @deprecated consider migrating to {@link #QUERY_ARG_MATCH_PENDING} which
+     *             is more expressive.
+     * @removed
      */
+    @Deprecated
     public static boolean getIncludePending(@NonNull Uri uri) {
         return parseBoolean(uri.getQueryParameter(MediaStore.PARAM_INCLUDE_PENDING));
     }
@@ -597,7 +705,11 @@
      * @see MediaStore#setIncludeTrashed(Uri)
      * @see MediaStore#trash(Context, Uri)
      * @see MediaStore#untrash(Context, Uri)
+     * @deprecated consider migrating to {@link #QUERY_ARG_MATCH_TRASHED} which
+     *             is more expressive.
+     * @removed
      */
+    @Deprecated
     public static @NonNull Uri setIncludeTrashed(@NonNull Uri uri) {
         return uri.buildUpon().appendQueryParameter(PARAM_INCLUDE_TRASHED, "1").build();
     }
@@ -1000,7 +1112,7 @@
          * the field to {@code 0}, or until they expire as defined by
          * {@link #DATE_EXPIRES}.
          *
-         * @see MediaStore#setIncludePending(Uri)
+         * @see MediaStore#QUERY_ARG_MATCH_PENDING
          */
         @Column(Cursor.FIELD_TYPE_INTEGER)
         public static final String IS_PENDING = "is_pending";
@@ -1011,8 +1123,7 @@
          * Trashed items are retained until they expire as defined by
          * {@link #DATE_EXPIRES}.
          *
-         * @see MediaColumns#IS_TRASHED
-         * @see MediaStore#setIncludeTrashed(Uri)
+         * @see MediaStore#QUERY_ARG_MATCH_TRASHED
          * @see MediaStore#trash(Context, Uri)
          * @see MediaStore#untrash(Context, Uri)
          */
@@ -1186,6 +1297,8 @@
         /**
          * Flag indicating if the media item has been marked as being a
          * "favorite" by the user.
+         *
+         * @see MediaStore#QUERY_ARG_MATCH_FAVORITE
          */
         @Column(Cursor.FIELD_TYPE_INTEGER)
         public static final String IS_FAVORITE = "is_favorite";
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index fa8896e..ad8d553 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -8782,6 +8782,22 @@
         public static final String BUGREPORT_IN_POWER_MENU = "bugreport_in_power_menu";
 
         /**
+         * The package name for the custom bugreport handler app. This app must be whitelisted.
+         * This is currently used only by Power Menu short press.
+         *
+         * @hide
+         */
+        public static final String CUSTOM_BUGREPORT_HANDLER_APP = "custom_bugreport_handler_app";
+
+        /**
+         * The user id for the custom bugreport handler app. This is currently used only by Power
+         * Menu short press.
+         *
+         * @hide
+         */
+        public static final String CUSTOM_BUGREPORT_HANDLER_USER = "custom_bugreport_handler_user";
+
+        /**
          * Whether ADB is enabled.
          */
         public static final String ADB_ENABLED = "adb_enabled";
@@ -8918,14 +8934,33 @@
          * List of ISO country codes in which eUICC UI is shown. Country codes should be separated
          * by comma.
          *
-         * <p>Used to hide eUICC UI from users who are currently in countries no carriers support
-         * eUICC.
+         * Note: if {@link #EUICC_SUPPORTED_COUNTRIES} is empty, then {@link
+         * #EUICC_UNSUPPORTED_COUNTRIES} is used.
+         *
+         * <p>Used to hide eUICC UI from users who are currently in countries where no carriers
+         * support eUICC.
+         *
          * @hide
          */
-        //TODO(b/77914569) Changes this to System Api.
+        @SystemApi
         public static final String EUICC_SUPPORTED_COUNTRIES = "euicc_supported_countries";
 
         /**
+         * List of ISO country codes in which eUICC UI is not shown. Country codes should be
+         * separated by comma.
+         *
+         * Note: if {@link #EUICC_SUPPORTED_COUNTRIES} is empty, then {@link
+         * #EUICC_UNSUPPORTED_COUNTRIES} is used.
+         *
+         * <p>Used to hide eUICC UI from users who are currently in countries where no carriers
+         * support eUICC.
+         *
+         * @hide
+         */
+        @SystemApi
+        public static final String EUICC_UNSUPPORTED_COUNTRIES = "euicc_unsupported_countries";
+
+        /**
          * Whether any activity can be resized. When this is true, any
          * activity, regardless of manifest values, can be resized for multi-window.
          * (0 = false, 1 = true)
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index 9e454e6..22f90f6 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -44,7 +44,6 @@
 import android.text.TextUtils;
 import android.util.Patterns;
 
-import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.telephony.SmsApplication;
 
 import java.lang.annotation.Retention;
@@ -1379,7 +1378,7 @@
                 }
 
                 String format = intent.getStringExtra("format");
-                int subId = intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY,
+                int subId = intent.getIntExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX,
                         SubscriptionManager.getDefaultSmsSubscriptionId());
 
                 Rlog.v(TAG, " getMessagesFromIntent sub_id : " + subId);
@@ -4505,7 +4504,7 @@
         /**
          * The current registered voice network operator name in long alphanumeric format.
          * <p>
-         * This is the same as {@link ServiceState#getVoiceOperatorAlphaLong()}.
+         * This is the same as {@link ServiceState#getOperatorAlphaLong()}.
          * @hide
          */
         public static final String VOICE_OPERATOR_ALPHA_LONG = "voice_operator_alpha_long";
@@ -4516,12 +4515,11 @@
          * In GSM/UMTS, short format can be up to 8 characters long. The current registered voice
          * network operator name in long alphanumeric format.
          * <p>
-         * This is the same as {@link ServiceState#getVoiceOperatorAlphaShort()}.
+         * This is the same as {@link ServiceState#getOperatorAlphaShort()}.
          * @hide
          */
         public static final String VOICE_OPERATOR_ALPHA_SHORT = "voice_operator_alpha_short";
 
-
         /**
          * The current registered operator numeric id.
          * <p>
@@ -4535,7 +4533,7 @@
         /**
          * The current registered data network operator name in long alphanumeric format.
          * <p>
-         * This is the same as {@link ServiceState#getDataOperatorAlphaLong()}.
+         * This is the same as {@link ServiceState#getOperatorAlphaLong()}.
          * @hide
          */
         public static final String DATA_OPERATOR_ALPHA_LONG = "data_operator_alpha_long";
@@ -4543,7 +4541,7 @@
         /**
          * The current registered data network operator name in short alphanumeric format.
          * <p>
-         * This is the same as {@link ServiceState#getDataOperatorAlphaShort()}.
+         * This is the same as {@link ServiceState#getOperatorAlphaShort()}.
          * @hide
          */
         public static final String DATA_OPERATOR_ALPHA_SHORT = "data_operator_alpha_short";
@@ -4551,7 +4549,7 @@
         /**
          * The current registered data network operator numeric id.
          * <p>
-         * This is the same as {@link ServiceState#getDataOperatorNumeric()}.
+         * This is the same as {@link ServiceState#getOperatorNumeric()}.
          * @hide
          */
         public static final String DATA_OPERATOR_NUMERIC = "data_operator_numeric";
diff --git a/core/java/android/service/autofill/augmented/FillWindow.java b/core/java/android/service/autofill/augmented/FillWindow.java
index 6a29d48..5d00370 100644
--- a/core/java/android/service/autofill/augmented/FillWindow.java
+++ b/core/java/android/service/autofill/augmented/FillWindow.java
@@ -242,6 +242,7 @@
         synchronized (mLock) {
             if (mDestroyed) return;
             if (mUpdateCalled) {
+                mFillView.setOnClickListener(null);
                 hide();
                 mProxy.report(AutofillProxy.REPORT_EVENT_UI_DESTROYED);
             }
diff --git a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl b/core/java/android/service/controls/BooleanAction.aidl
similarity index 74%
copy from wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl
copy to core/java/android/service/controls/BooleanAction.aidl
index 007ec94..730ad36 100644
--- a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl
+++ b/core/java/android/service/controls/BooleanAction.aidl
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2014, The Android Open Source Project
+/*
+ * Copyright (C) 2019 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
- *     http://www.apache.org/licenses/LICENSE-2.0
+ *      http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 
-package android.net.wifi;
+package android.service.controls;
 
-parcelable WifiActivityEnergyInfo;
+parcelable BooleanAction;
\ No newline at end of file
diff --git a/core/java/android/service/controls/BooleanAction.java b/core/java/android/service/controls/BooleanAction.java
new file mode 100644
index 0000000..8508c63
--- /dev/null
+++ b/core/java/android/service/controls/BooleanAction.java
@@ -0,0 +1,93 @@
+/*
+ * 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.controls;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+
+/**
+ * Action sent by a {@link ToggleTemplate}
+ * @hide
+ */
+public final class BooleanAction extends ControlAction {
+
+    private final boolean mNewState;
+
+    /**
+     * @param templateId the identifier of the {@link ToggleTemplate} that produced this action.
+     * @param newState new value for the state displayed by the {@link ToggleTemplate}.
+     */
+    public BooleanAction(@NonNull String templateId, boolean newState) {
+        this(templateId, newState, null);
+    }
+
+    /**
+     * @param templateId the identifier of the {@link ToggleTemplate} that originated this action.
+     * @param newValue new value for the state displayed by the {@link ToggleTemplate}.
+     * @param challengeValue a value sent by the user along with the action to authenticate. {@code}
+     *                       null is sent when no authentication is needed or has not been
+     *                       requested.
+     */
+    public BooleanAction(@NonNull String templateId, boolean newValue,
+            @Nullable String challengeValue) {
+        super(templateId, challengeValue);
+        mNewState = newValue;
+    }
+
+    BooleanAction(Parcel in) {
+        super(in);
+        mNewState = in.readByte() == 1;
+    }
+
+    /**
+     * The new state set for the button in the corresponding {@link ToggleTemplate}.
+     *
+     * @return {@code true} if the button was toggled from an {@code off} state to an {@code on}
+     *         state.
+     */
+    public boolean getNewState() {
+        return mNewState;
+    }
+
+    /**
+     * @return {@link ControlAction#TYPE_BOOLEAN}
+     */
+    @Override
+    public int getActionType() {
+        return ControlAction.TYPE_BOOLEAN;
+    }
+
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        super.writeToParcel(dest, flags);
+        dest.writeByte(mNewState ? (byte) 1 : (byte) 0);
+    }
+
+    public static final @NonNull Creator<BooleanAction> CREATOR = new Creator<BooleanAction>() {
+        @Override
+        public BooleanAction createFromParcel(Parcel source) {
+            return new BooleanAction(source);
+        }
+
+        @Override
+        public BooleanAction[] newArray(int size) {
+            return new BooleanAction[size];
+        }
+    };
+}
diff --git a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl b/core/java/android/service/controls/Control.aidl
similarity index 81%
copy from wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl
copy to core/java/android/service/controls/Control.aidl
index 007ec94..f4964f2 100644
--- a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl
+++ b/core/java/android/service/controls/Control.aidl
@@ -1,5 +1,5 @@
-/**
- * Copyright (c) 2014, The Android Open Source Project
+/*
+ * Copyright (c) 2019, The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 
-package android.net.wifi;
+package android.service.controls;
 
-parcelable WifiActivityEnergyInfo;
+parcelable Control;
\ No newline at end of file
diff --git a/core/java/android/service/controls/Control.java b/core/java/android/service/controls/Control.java
new file mode 100644
index 0000000..a69408c
--- /dev/null
+++ b/core/java/android/service/controls/Control.java
@@ -0,0 +1,284 @@
+/*
+ * 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.controls;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Intent;
+import android.content.res.ColorStateList;
+import android.graphics.drawable.Icon;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * Represents a physical object that can be represented by a {@link ControlTemplate} and whose
+ * properties may be modified through a {@link ControlAction}.
+ *
+ * The information is provided by a {@link ControlProviderService} and represents static
+ * information (not current status) about the device.
+ * <p>
+ * Each control needs a unique (per provider) identifier that is persistent across reboots of the
+ * system.
+ * <p>
+ * Each {@link Control} will have a name and an icon. The name is usually set up by the user in the
+ * {@link ControlProvider} while the icon is usually decided by the {@link ControlProvider} based
+ * on the type of device.
+ * <p>
+ * The {@link ControlTemplate.TemplateType} provided will be used as a hint when displaying this in
+ * non-interactive situations (for example when there's no state to display). This template is not
+ * the one that will be shown with the current state and provide interactions. That template is set
+ * using {@link ControlState}.
+ * <p>
+ * An {@link Intent} linking to the provider Activity that expands this {@link Control} should be
+ * provided.
+ * @hide
+ */
+public class Control implements Parcelable {
+
+    private final @NonNull String mControlId;
+    private final @NonNull Icon mIcon;
+    private final @NonNull CharSequence mTitle;
+    private final @Nullable ColorStateList mTintColor;
+    private final @NonNull Intent mAppIntent;
+    private final @ControlTemplate.TemplateType int mPrimaryType;
+
+    /**
+     * @param controlId the unique persistent identifier for this object.
+     * @param icon an icon to display identifying the control.
+     * @param title the user facing name of this control (e.g. "Bedroom thermostat").
+     * @param tintColor the color to tint parts of the element UI. If {@code null} is passed, the
+     *                  system accent color will be used.
+     * @param appIntent an intent linking to a page to interact with the corresponding device.
+     * @param primaryType the primary template for this type.
+     */
+    public Control(@NonNull String controlId,
+            @NonNull Icon icon,
+            @NonNull CharSequence title,
+            @Nullable ColorStateList tintColor,
+            @NonNull Intent appIntent,
+            int primaryType) {
+        Preconditions.checkNotNull(controlId);
+        Preconditions.checkNotNull(icon);
+        Preconditions.checkNotNull(title);
+        Preconditions.checkNotNull(appIntent);
+        mControlId = controlId;
+        mIcon = icon;
+        mTitle = title;
+        mTintColor = tintColor;
+        mAppIntent = appIntent;
+        mPrimaryType = primaryType;
+    }
+
+    public Control(Parcel in) {
+        mControlId = in.readString();
+        mIcon = Icon.CREATOR.createFromParcel(in);
+        mTitle = in.readCharSequence();
+        if (in.readByte() == 1) {
+            mTintColor = ColorStateList.CREATOR.createFromParcel(in);
+        } else {
+            mTintColor = null;
+        }
+        mAppIntent = Intent.CREATOR.createFromParcel(in);
+        mPrimaryType = in.readInt();
+    }
+
+    @NonNull
+    public String getControlId() {
+        return mControlId;
+    }
+
+    @NonNull
+    public Icon getIcon() {
+        return mIcon;
+    }
+
+    @NonNull
+    public CharSequence getTitle() {
+        return mTitle;
+    }
+
+    @Nullable
+    public ColorStateList getTint() {
+        return mTintColor;
+    }
+
+    @NonNull
+    public Intent getAppIntent() {
+        return mAppIntent;
+    }
+
+    @ControlTemplate.TemplateType
+    public int getPrimaryType() {
+        return mPrimaryType;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(mControlId);
+        mIcon.writeToParcel(dest, flags);
+        dest.writeCharSequence(mTitle);
+        if (mTintColor != null) {
+            dest.writeByte((byte) 1);
+            mTintColor.writeToParcel(dest, flags);
+        } else {
+            dest.writeByte((byte) 0);
+        }
+        mAppIntent.writeToParcel(dest, flags);
+        dest.writeInt(mPrimaryType);
+    }
+
+    public static final Creator<Control> CREATOR = new Creator<Control>() {
+        @Override
+        public Control createFromParcel(Parcel source) {
+            return new Control(source);
+        }
+
+        @Override
+        public Control[] newArray(int size) {
+            return new Control[size];
+        }
+    };
+
+    /**
+     * Builder class for {@link Control}.
+     *
+     * This class facilitates the creation of {@link Control}. It provides the following
+     * defaults for non-optional parameters:
+     * <ul>
+     *     <li> Title: {@code ""}
+     *     <li> Primary template: {@link ControlTemplate#TYPE_NONE}
+     * </ul>
+     */
+    public static class Builder {
+        private String mControlId;
+        private Icon mIcon;
+        private CharSequence mTitle = "";
+        private ColorStateList mTintColor;
+        private @Nullable Intent mAppIntent;
+        private @ControlTemplate.TemplateType int mPrimaryType = ControlTemplate.TYPE_NONE;
+
+        /**
+         * @param controlId the identifier for the {@link Control}.
+         * @param icon the icon for the {@link Control}.
+         * @param appIntent the intent linking to the device Activity.
+         */
+        public Builder(@NonNull String controlId,
+                @NonNull Icon icon,
+                @NonNull Intent appIntent) {
+            Preconditions.checkNotNull(controlId);
+            Preconditions.checkNotNull(icon);
+            Preconditions.checkNotNull(appIntent);
+            mControlId = controlId;
+            mIcon = icon;
+            mAppIntent = appIntent;
+        }
+
+        /**
+         * Creates a {@link Builder} using an existing {@link Control} as a base.
+         * @param control base for the builder.
+         */
+        public Builder(@NonNull Control control) {
+            Preconditions.checkNotNull(control);
+            mControlId = control.mControlId;
+            mIcon = control.mIcon;
+            mTitle = control.mTitle;
+            mTintColor = control.mTintColor;
+            mAppIntent = control.mAppIntent;
+            mPrimaryType = control.mPrimaryType;
+        }
+
+        /**
+         * @param controlId the identifier for the {@link Control}.
+         * @return {@code this}
+         */
+        public Builder setControlId(@NonNull String controlId) {
+            Preconditions.checkNotNull(controlId);
+            mControlId = controlId;
+            return this;
+        }
+
+        /**
+         * @param icon the icon for the {@link Control}
+         * @return {@code this}
+         */
+        @NonNull
+        public Builder setIcon(@NonNull Icon icon) {
+            Preconditions.checkNotNull(icon);
+            mIcon = icon;
+            return this;
+        }
+
+        /**
+         * @param title the user facing name of the {@link Control}
+         * @return {@code this}
+         */
+        @NonNull
+        public Builder setTitle(@NonNull CharSequence title) {
+            Preconditions.checkNotNull(title);
+            mTitle = title;
+            return this;
+        }
+
+        /**
+         * @param tint colors for tinting parts of the {@link Control} UI. Passing {@code null} will
+         *             default to using the current color accent.
+         * @return {@code this}
+         */
+        @NonNull
+        public Builder setTint(@Nullable ColorStateList tint) {
+            mTintColor = tint;
+            return this;
+        }
+
+        /**
+         * @param appIntent an {@link Intent} linking to an Activity for the {@link Control}
+         * @return {@code this}
+         */
+        @NonNull
+        public Builder setAppIntent(@NonNull Intent appIntent) {
+            Preconditions.checkNotNull(appIntent);
+            mAppIntent = appIntent;
+            return this;
+        }
+
+        /**
+         * @param type type to use as default in the {@link Control}
+         * @return {@code this}
+         */
+        @NonNull
+        public Builder setPrimaryType(@ControlTemplate.TemplateType int type) {
+            mPrimaryType = type;
+            return this;
+        }
+
+        /**
+         * Build a {@link Control}
+         * @return a valid {@link Control}
+         */
+        @NonNull
+        public Control build() {
+            return new Control(mControlId, mIcon, mTitle, mTintColor, mAppIntent, mPrimaryType);
+        }
+    }
+}
diff --git a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl b/core/java/android/service/controls/ControlAction.aidl
similarity index 74%
copy from wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl
copy to core/java/android/service/controls/ControlAction.aidl
index 007ec94..e1a5276 100644
--- a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl
+++ b/core/java/android/service/controls/ControlAction.aidl
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2014, The Android Open Source Project
+/*
+ * Copyright (C) 2019 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
- *     http://www.apache.org/licenses/LICENSE-2.0
+ *      http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 
-package android.net.wifi;
+package android.service.controls;
 
-parcelable WifiActivityEnergyInfo;
+parcelable ControlAction;
\ No newline at end of file
diff --git a/core/java/android/service/controls/ControlAction.java b/core/java/android/service/controls/ControlAction.java
new file mode 100644
index 0000000..8b75955
--- /dev/null
+++ b/core/java/android/service/controls/ControlAction.java
@@ -0,0 +1,189 @@
+/*
+ * 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.controls;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * An abstract action that is executed from a {@link ControlTemplate}.
+ *
+ * The action may have a value to authenticate the input, when the provider has requested it to
+ * complete the action.
+ * @hide
+ */
+public abstract class ControlAction implements Parcelable {
+
+    /**
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({
+            TYPE_BOOLEAN,
+            TYPE_FLOAT
+    })
+    public @interface ActionType {};
+
+    /**
+     * The identifier of {@link BooleanAction}.
+     */
+    public static final @ActionType int TYPE_BOOLEAN = 0;
+
+    /**
+     * The identifier of {@link FloatAction}.
+     */
+    public static final @ActionType int TYPE_FLOAT = 1;
+
+    /**
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({
+            RESPONSE_OK,
+            RESPONSE_FAIL,
+            RESPONSE_CHALLENGE_ACK,
+            RESPONSE_CHALLENGE_PIN,
+            RESPONSE_CHALLENGE_PASSPHRASE
+    })
+    public @interface ResponseResult {};
+
+    /**
+     * Response code for {@link IControlsProviderCallback#onControlActionResponse} indicating that
+     * the action has been performed. The action may still fail later and the state may not change.
+     */
+    public static final @ResponseResult int RESPONSE_OK = 0;
+    /**
+     * Response code for {@link IControlsProviderCallback#onControlActionResponse} indicating that
+     * the action has failed.
+     */
+    public static final @ResponseResult int RESPONSE_FAIL = 1;
+    /**
+     * Response code for {@link IControlsProviderCallback#onControlActionResponse} indicating that
+     * in order for the action to be performed, acknowledgment from the user is required.
+     */
+    public static final @ResponseResult int RESPONSE_CHALLENGE_ACK = 2;
+    /**
+     * Response code for {@link IControlsProviderCallback#onControlActionResponse} indicating that
+     * in order for the action to be performed, a PIN is required.
+     */
+    public static final @ResponseResult int RESPONSE_CHALLENGE_PIN = 3;
+    /**
+     * Response code for {@link IControlsProviderCallback#onControlActionResponse} indicating that
+     * in order for the action to be performed, an alphanumeric passphrase is required.
+     */
+    public static final @ResponseResult int RESPONSE_CHALLENGE_PASSPHRASE = 4;
+
+    /**
+     * The {@link ActionType} associated with this class.
+     */
+    public abstract @ActionType int getActionType();
+
+    private final @NonNull String mTemplateId;
+    private final @Nullable String mChallengeValue;
+
+    private ControlAction() {
+        mTemplateId = "";
+        mChallengeValue = null;
+    }
+
+    /**
+     * @hide
+     */
+    ControlAction(@NonNull String templateId, @Nullable String challengeValue) {
+        Preconditions.checkNotNull(templateId);
+        mTemplateId = templateId;
+        mChallengeValue = challengeValue;
+    }
+
+    /**
+     * @hide
+     */
+    ControlAction(Parcel in) {
+        mTemplateId = in.readString();
+        if (in.readByte() == 1) {
+            mChallengeValue = in.readString();
+        } else {
+            mChallengeValue = null;
+        }
+    }
+
+    /**
+     * The identifier of the {@link ControlTemplate} that originated this action
+     */
+    @NonNull
+    public String getTemplateId() {
+        return mTemplateId;
+    }
+
+    /**
+     * The challenge value used to authenticate certain actions, if available.
+     */
+    @Nullable
+    public String getChallengeValue() {
+        return mChallengeValue;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(getActionType());
+        dest.writeString(mTemplateId);
+        if (mChallengeValue != null) {
+            dest.writeByte((byte) 1);
+            dest.writeString(mChallengeValue);
+        } else {
+            dest.writeByte((byte) 0);
+        }
+    }
+
+    public static final @NonNull Creator<ControlAction> CREATOR = new Creator<ControlAction>() {
+        @Override
+        public ControlAction createFromParcel(Parcel source) {
+            int type = source.readInt();
+            return createActionFromType(type, source);
+        }
+
+        @Override
+        public ControlAction[] newArray(int size) {
+            return new ControlAction[size];
+        }
+    };
+
+    private static ControlAction createActionFromType(@ActionType int type, Parcel source) {
+        switch(type) {
+            case TYPE_BOOLEAN:
+                return BooleanAction.CREATOR.createFromParcel(source);
+            case TYPE_FLOAT:
+                return FloatAction.CREATOR.createFromParcel(source);
+            default:
+                return null;
+        }
+    }
+
+}
diff --git a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl b/core/java/android/service/controls/ControlButton.aidl
similarity index 74%
copy from wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl
copy to core/java/android/service/controls/ControlButton.aidl
index 007ec94..6a7262d 100644
--- a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl
+++ b/core/java/android/service/controls/ControlButton.aidl
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2014, The Android Open Source Project
+/*
+ * Copyright (C) 2019, The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
- *     http://www.apache.org/licenses/LICENSE-2.0
+ *      http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 
-package android.net.wifi;
+package android.service.controls;
 
-parcelable WifiActivityEnergyInfo;
+parcelable ControlButton;
\ No newline at end of file
diff --git a/core/java/android/service/controls/ControlButton.java b/core/java/android/service/controls/ControlButton.java
new file mode 100644
index 0000000..fed3115
--- /dev/null
+++ b/core/java/android/service/controls/ControlButton.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.controls;
+
+import android.annotation.NonNull;
+import android.graphics.drawable.Icon;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * Button element for {@link ControlTemplate}.
+ * @hide
+ */
+public class ControlButton implements Parcelable {
+
+    private final boolean mActive;
+    private final @NonNull Icon mIcon;
+    private final @NonNull CharSequence mContentDescription;
+
+    /**
+     * @param active true if the button should be rendered as active.
+     * @param icon icon to display in the button.
+     * @param contentDescription content description for the button.
+     */
+    public ControlButton(boolean active, @NonNull Icon icon,
+            @NonNull CharSequence contentDescription) {
+        Preconditions.checkNotNull(icon);
+        Preconditions.checkNotNull(contentDescription);
+        mActive = active;
+        mIcon = icon;
+        mContentDescription = contentDescription;
+    }
+
+    /**
+     * Whether the button should be rendered in its active state.
+     */
+    public boolean isActive() {
+        return mActive;
+    }
+
+    /**
+     * The icon for this button.
+     */
+    @NonNull
+    public Icon getIcon() {
+        return mIcon;
+    }
+
+    /**
+     * The content description for this button.
+     */
+    @NonNull
+    public CharSequence getContentDescription() {
+        return mContentDescription;
+    }
+
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeByte(mActive ? (byte) 1 : (byte) 0);
+        mIcon.writeToParcel(dest, flags);
+        dest.writeCharSequence(mContentDescription);
+    }
+
+    ControlButton(Parcel in) {
+        mActive = in.readByte() != 0;
+        mIcon = Icon.CREATOR.createFromParcel(in);
+        mContentDescription = in.readCharSequence();
+    }
+
+    public static final Creator<ControlButton> CREATOR = new Creator<ControlButton>() {
+        @Override
+        public ControlButton createFromParcel(Parcel source) {
+            return new ControlButton(source);
+        }
+
+        @Override
+        public ControlButton[] newArray(int size) {
+            return new ControlButton[size];
+        }
+    };
+}
diff --git a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl b/core/java/android/service/controls/ControlState.aidl
similarity index 81%
copy from wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl
copy to core/java/android/service/controls/ControlState.aidl
index 007ec94..520d85b 100644
--- a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl
+++ b/core/java/android/service/controls/ControlState.aidl
@@ -1,5 +1,5 @@
-/**
- * Copyright (c) 2014, The Android Open Source Project
+/*
+ * Copyright (c) 2019, The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 
-package android.net.wifi;
+package android.service.controls;
 
-parcelable WifiActivityEnergyInfo;
+parcelable ControlState;
\ No newline at end of file
diff --git a/core/java/android/service/controls/ControlState.java b/core/java/android/service/controls/ControlState.java
new file mode 100644
index 0000000..804aef7
--- /dev/null
+++ b/core/java/android/service/controls/ControlState.java
@@ -0,0 +1,318 @@
+/*
+ * 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.controls;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.res.ColorStateList;
+import android.graphics.drawable.Icon;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Current state for a {@link Control}.
+ *
+ * Collects information to render the current state of a {@link Control} as well as possible action
+ * that can be performed on it. Some of the information may temporarily override the defaults
+ * provided by the corresponding {@link Control}, while this state is being displayed.
+ *
+ * Additionally, this can be used to modify information related to the corresponding
+ * {@link Control}.
+ * @hide
+ */
+public final class ControlState implements Parcelable {
+
+    /**
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({
+            STATUS_OK,
+            STATUS_NOT_FOUND,
+            STATUS_ERROR,
+            STATUS_DISABLED,
+    })
+    public @interface Status {};
+
+    /**
+     * The device corresponding to the {@link Control} is responding correctly.
+     */
+    public static final int STATUS_OK = 0;
+
+    /**
+     * The device corresponding to the {@link Control} cannot be found or was removed.
+     */
+    public static final int STATUS_NOT_FOUND = 1;
+
+    /**
+     * The device corresponding to the {@link Control} is in an error state.
+     */
+    public static final int STATUS_ERROR = 2;
+
+    /**
+     * The {@link Control} is currently disabled.
+     */
+    public static final int STATUS_DISABLED = 3;
+
+    private final @NonNull Control mControl;
+    private final @Status int mStatus;
+    private final @NonNull ControlTemplate mControlTemplate;
+    private final @NonNull CharSequence mStatusText;
+    private final @Nullable Icon mOverrideIcon;
+    private final @Nullable ColorStateList mOverrideTint;
+
+    /**
+     * @param control the {@link Control} this state should be applied to. Can be used to
+     *                       update information about the {@link Control}
+     * @param status the current status of the {@link Control}.
+     * @param controlTemplate the template to be used to render the {@link Control}.
+     * @param statusText the text describing the current status.
+     * @param overrideIcon the icon to temporarily override the one provided in
+     *                     {@link Control#getIcon()}. Pass {@code null} to use the icon in
+     *                     {@link Control#getIcon()}.
+     * @param overrideTint the colors to temporarily override those provided in
+     *                            {@link Control#getTint()}. Pass {@code null} to use the colors in
+     *                            {@link Control#getTint()}.
+     */
+    public ControlState(@NonNull Control control,
+            int status,
+            @NonNull ControlTemplate controlTemplate,
+            @NonNull CharSequence statusText,
+            @Nullable Icon overrideIcon,
+            @Nullable ColorStateList overrideTint) {
+        Preconditions.checkNotNull(control);
+        Preconditions.checkNotNull(controlTemplate);
+        Preconditions.checkNotNull(statusText);
+
+        mControl = control;
+        mStatus = status;
+        mControlTemplate = controlTemplate;
+        mOverrideIcon = overrideIcon;
+        mStatusText = statusText;
+        mOverrideTint = overrideTint;
+    }
+
+    ControlState(Parcel in) {
+        mControl = Control.CREATOR.createFromParcel(in);
+        mStatus = in.readInt();
+        mControlTemplate = ControlTemplate.CREATOR.createFromParcel(in);
+        mStatusText = in.readCharSequence();
+        if (in.readByte() == 1) {
+            mOverrideIcon = Icon.CREATOR.createFromParcel(in);
+        } else {
+            mOverrideIcon = null;
+        }
+        if (in.readByte() == 1) {
+            mOverrideTint = ColorStateList.CREATOR.createFromParcel(in);
+        } else {
+            mOverrideTint = null;
+        }
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Status
+    public int getStatus() {
+        return mStatus;
+    }
+
+    @NonNull
+    public ControlTemplate getControlTemplate() {
+        return mControlTemplate;
+    }
+
+    @Nullable
+    public Icon getOverrideIcon() {
+        return mOverrideIcon;
+    }
+
+    @NonNull
+    public CharSequence getStatusText() {
+        return mStatusText;
+    }
+
+    @Nullable
+    public ColorStateList getOverrideTint() {
+        return mOverrideTint;
+    }
+
+    @NonNull
+    public Control getControl() {
+        return mControl;
+    }
+
+    @NonNull
+    public String getControlId() {
+        return mControl.getControlId();
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        mControl.writeToParcel(dest, flags);
+        dest.writeInt(mStatus);
+        mControlTemplate.writeToParcel(dest, flags);
+        dest.writeCharSequence(mStatusText);
+        if (mOverrideIcon != null) {
+            dest.writeByte((byte) 1);
+            mOverrideIcon.writeToParcel(dest, flags);
+        } else {
+            dest.writeByte((byte) 0);
+        }
+        if (mOverrideTint != null) {
+            dest.writeByte((byte) 1);
+            mOverrideTint.writeToParcel(dest, flags);
+        } else {
+            dest.writeByte((byte) 0);
+        }
+    }
+
+    public static final Creator<ControlState> CREATOR = new Creator<ControlState>() {
+        @Override
+        public ControlState createFromParcel(Parcel source) {
+            return new ControlState(source);
+        }
+
+        @Override
+        public ControlState[] newArray(int size) {
+            return new ControlState[size];
+        }
+    };
+
+    /**
+     * Builder class for {@link ControlState}.
+     *
+     * This class facilitates the creation of {@link ControlState}. It provides the following
+     * defaults for non-optional parameters:
+     * <ul>
+     *     <li> Status: {@link ControlState#STATUS_OK}
+     *     <li> Control template: {@link ControlTemplate#NO_TEMPLATE}
+     *     <li> Status text: {@code ""}
+     * </ul>
+     */
+    public static class Builder {
+        private @NonNull Control mControl;
+        private @Status int mStatus = STATUS_OK;
+        private @NonNull ControlTemplate mControlTemplate = ControlTemplate.NO_TEMPLATE;
+        private @NonNull CharSequence mStatusText = "";
+        private @Nullable Icon mOverrideIcon;
+        private @Nullable ColorStateList mOverrideTint;
+
+        /**
+         * @param control the {@link Control} that the resulting {@link ControlState} refers to.
+         */
+        public Builder(@NonNull Control control) {
+            Preconditions.checkNotNull(control);
+            mControl = control;
+        }
+
+        /**
+         * Creates a {@link Builder} using an existing {@link ControlState} as a base.
+         * @param controlState base for the builder.
+         */
+        public Builder(@NonNull ControlState controlState) {
+            Preconditions.checkNotNull(controlState);
+            mControl = controlState.mControl;
+            mControlTemplate = controlState.mControlTemplate;
+            mOverrideIcon = controlState.mOverrideIcon;
+            mStatusText = controlState.mStatusText;
+            mOverrideTint = controlState.mOverrideTint;
+        }
+
+
+        /**
+         * @param control the updated {@link Control} information.
+         * @return {@code this}
+         */
+        @NonNull
+        public Builder setControl(@NonNull Control control) {
+            mControl = control;
+            return this;
+        }
+
+        /**
+         * @param status the current status of the {@link Control}
+         * @return {@code this}
+         */
+        @NonNull
+        public Builder setStatus(@Status int status) {
+            mStatus = status;
+            return this;
+        }
+
+        /**
+         * @param controlTemplate the template to use when rendering the {@code Control}.
+         * @return {@code this}
+         */
+        @NonNull
+        public Builder setControlTemplate(@NonNull ControlTemplate controlTemplate) {
+            Preconditions.checkNotNull(controlTemplate);
+            mControlTemplate = controlTemplate;
+            return this;
+        }
+
+        /**
+         * @param statusText the user-visible description of the status.
+         * @return {@code this}
+         */
+        @NonNull
+        public Builder setStatusText(@NonNull CharSequence statusText) {
+            Preconditions.checkNotNull(statusText);
+            mStatusText = statusText;
+            return this;
+        }
+
+        /**
+         * @param overrideIcon the icon to override the one defined in the corresponding
+         *                            {@code Control}. Pass {@code null} to remove the override.
+         * @return {@code this}
+         */
+        @NonNull
+        public Builder setOverrideIcon(@Nullable Icon overrideIcon) {
+            mOverrideIcon = overrideIcon;
+            return this;
+        }
+
+        /**
+         * @param overrideTint the colors to override the ones defined in the corresponding
+         *                            {@code Control}. Pass {@code null} to remove the override.
+         * @return {@code this}
+         */
+        @NonNull
+        public Builder setOverrideTint(@Nullable ColorStateList overrideTint) {
+            mOverrideTint = overrideTint;
+            return this;
+        }
+
+        /**
+         * @return a new {@link ControlState}
+         */
+        public ControlState build() {
+            return new ControlState(mControl, mStatus, mControlTemplate, mStatusText,
+                    mOverrideIcon, mOverrideTint);
+        }
+    }
+}
+
diff --git a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl b/core/java/android/service/controls/ControlTemplate.aidl
similarity index 81%
copy from wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl
copy to core/java/android/service/controls/ControlTemplate.aidl
index 007ec94..ecb948c 100644
--- a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl
+++ b/core/java/android/service/controls/ControlTemplate.aidl
@@ -1,5 +1,5 @@
-/**
- * Copyright (c) 2014, The Android Open Source Project
+/*
+ * Copyright (c) 2019, The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 
-package android.net.wifi;
+package android.service.controls;
 
-parcelable WifiActivityEnergyInfo;
+parcelable ControlTemplate;
\ No newline at end of file
diff --git a/core/java/android/service/controls/ControlTemplate.java b/core/java/android/service/controls/ControlTemplate.java
new file mode 100644
index 0000000..e559862
--- /dev/null
+++ b/core/java/android/service/controls/ControlTemplate.java
@@ -0,0 +1,167 @@
+/*
+ * 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.controls;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * An abstract input template for a {@link Control}.
+ *
+ * Specifies what layout is presented to the user when a {@link ControlState} is assigned to a
+ * particular {@link Control}.
+ * <p>
+ * Some instances of {@link Control} can originate actions (via user interaction) to modify its
+ * associated state. The actions available to a given {@link Control} in a particular
+ * {@link ControlState} are determined by its {@link ControlTemplate}.
+ * @see ControlAction
+ * @hide
+ */
+public abstract class ControlTemplate implements Parcelable {
+
+    /**
+     * Singleton representing a {@link Control} with no input.
+     */
+    public static final ControlTemplate NO_TEMPLATE = new ControlTemplate("") {
+        @Override
+        public int getTemplateType() {
+            return TYPE_NONE;
+        }
+    };
+
+    /**
+     * @hide
+     */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef({
+            TYPE_NONE,
+            TYPE_TOGGLE,
+            TYPE_RANGE,
+            TYPE_THUMBNAIL,
+            TYPE_DISCRETE_TOGGLE,
+            TYPE_COORD_RANGE
+    })
+    public @interface TemplateType {}
+
+    /**
+     * Type identifier of {@link ControlTemplate#NO_TEMPLATE}.
+     */
+    public static final int TYPE_NONE = 0;
+
+    /**
+     * Type identifier of {@link ToggleTemplate}.
+     */
+    public static final int TYPE_TOGGLE = 1;
+
+    /**
+     * Type identifier of {@link RangeTemplate}.
+     */
+    public static final int TYPE_RANGE = 2;
+
+    /**
+     * Type identifier of {@link ThumbnailTemplate}.
+     */
+    public static final int TYPE_THUMBNAIL = 3;
+
+    /**
+     * Type identifier of {@link DiscreteToggleTemplate}.
+     */
+    public static final int TYPE_DISCRETE_TOGGLE = 4;
+
+    /**
+     * @hide
+     */
+    public static final int TYPE_COORD_RANGE = 5;
+
+    private @NonNull final String mTemplateId;
+
+    /**
+     * @return the identifier for this object.
+     */
+    public String getTemplateId() {
+        return mTemplateId;
+    }
+
+    /**
+     * The {@link TemplateType} associated with this class.
+     */
+    public abstract @TemplateType int getTemplateType();
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(getTemplateType());
+        dest.writeString(mTemplateId);
+    }
+
+    private ControlTemplate() {
+        mTemplateId = "";
+    }
+
+    ControlTemplate(Parcel in) {
+        mTemplateId = in.readString();
+    }
+
+    /**
+     * @hide
+     */
+    ControlTemplate(@NonNull String templateId) {
+        Preconditions.checkNotNull(templateId);
+        mTemplateId = templateId;
+    }
+
+    public static final Creator<ControlTemplate> CREATOR = new Creator<ControlTemplate>() {
+        @Override
+        public ControlTemplate createFromParcel(Parcel source) {
+            int type = source.readInt();
+            return createTemplateFromType(type, source);
+        }
+
+        @Override
+        public ControlTemplate[] newArray(int size) {
+            return new ControlTemplate[size];
+        }
+    };
+
+    private static ControlTemplate createTemplateFromType(@TemplateType int type, Parcel source) {
+        switch(type) {
+            case TYPE_TOGGLE:
+                return ToggleTemplate.CREATOR.createFromParcel(source);
+            case TYPE_RANGE:
+                return RangeTemplate.CREATOR.createFromParcel(source);
+            case TYPE_THUMBNAIL:
+                return ThumbnailTemplate.CREATOR.createFromParcel(source);
+            case TYPE_DISCRETE_TOGGLE:
+                return DiscreteToggleTemplate.CREATOR.createFromParcel(source);
+            case TYPE_NONE:
+                return NO_TEMPLATE;
+            default:
+                return null;
+        }
+    }
+}
diff --git a/core/java/android/service/controls/DiscreteToggleTemplate.java b/core/java/android/service/controls/DiscreteToggleTemplate.java
new file mode 100644
index 0000000..5167af4
--- /dev/null
+++ b/core/java/android/service/controls/DiscreteToggleTemplate.java
@@ -0,0 +1,110 @@
+/*
+ * 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.controls;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * A template for a {@link Control} with two discrete inputs.
+ *
+ * The two inputs represent a <i>Negative</i> input and a <i>Positive</i> input.
+ * <p>
+ * When one of the buttons is actioned, a {@link BooleanAction} will be sent.
+ * {@link BooleanAction#getNewState} will be {@code false} if the button was
+ * {@link DiscreteToggleTemplate#getNegativeButton} and {@code true} if the button was
+ * {@link DiscreteToggleTemplate#getPositiveButton}.
+ * @hide
+ */
+public class DiscreteToggleTemplate extends ControlTemplate {
+
+    private final @NonNull ControlButton mNegativeButton;
+    private final @NonNull ControlButton mPositiveButton;
+
+    /**
+     * @param templateId the identifier for this template object
+     * @param negativeButton a {@ControlButton} for the <i>Negative</i> input
+     * @param positiveButton a {@ControlButton} for the <i>Positive</i> input
+     */
+    public DiscreteToggleTemplate(@NonNull String templateId,
+            @NonNull ControlButton negativeButton,
+            @NonNull ControlButton positiveButton) {
+        super(templateId);
+        Preconditions.checkNotNull(negativeButton);
+        Preconditions.checkNotNull(positiveButton);
+        mNegativeButton = negativeButton;
+        mPositiveButton = positiveButton;
+    }
+
+    DiscreteToggleTemplate(Parcel in) {
+        super(in);
+        this.mNegativeButton = ControlButton.CREATOR.createFromParcel(in);
+        this.mPositiveButton = ControlButton.CREATOR.createFromParcel(in);
+    }
+
+    /**
+     * The {@link ControlButton} associated with the <i>Negative</i> action.
+     */
+    @NonNull
+    public ControlButton getNegativeButton() {
+        return mNegativeButton;
+    }
+
+    /**
+     * The {@link ControlButton} associated with the <i>Positive</i> action.
+     */
+    @NonNull
+    public ControlButton getPositiveButton() {
+        return mPositiveButton;
+    }
+
+    /**
+     * @return {@link ControlTemplate#TYPE_DISCRETE_TOGGLE}
+     */
+    @Override
+    public int getTemplateType() {
+        return TYPE_DISCRETE_TOGGLE;
+    }
+
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        super.writeToParcel(dest, flags);
+        mNegativeButton.writeToParcel(dest, flags);
+        mPositiveButton.writeToParcel(dest, flags);
+    }
+
+    public static final Creator<DiscreteToggleTemplate> CREATOR =
+            new Creator<DiscreteToggleTemplate>() {
+                @Override
+                public DiscreteToggleTemplate createFromParcel(Parcel source) {
+                    return new DiscreteToggleTemplate(source);
+                }
+
+                @Override
+                public DiscreteToggleTemplate[] newArray(int size) {
+                    return new DiscreteToggleTemplate[size];
+                }
+            };
+}
diff --git a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl b/core/java/android/service/controls/FloatAction.aidl
similarity index 74%
copy from wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl
copy to core/java/android/service/controls/FloatAction.aidl
index 007ec94..dbc0f72 100644
--- a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl
+++ b/core/java/android/service/controls/FloatAction.aidl
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2014, The Android Open Source Project
+/*
+ * Copyright (C) 2019 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
- *     http://www.apache.org/licenses/LICENSE-2.0
+ *      http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 
-package android.net.wifi;
+package android.service.controls;
 
-parcelable WifiActivityEnergyInfo;
+parcelable FloatAction;
\ No newline at end of file
diff --git a/core/java/android/service/controls/FloatAction.java b/core/java/android/service/controls/FloatAction.java
new file mode 100644
index 0000000..fe6db10
--- /dev/null
+++ b/core/java/android/service/controls/FloatAction.java
@@ -0,0 +1,90 @@
+/*
+ * 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.controls;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+
+/**
+ * Action sent by a {@link RangeTemplate}.
+ * @hide
+ */
+public final class FloatAction extends ControlAction {
+
+    private final float mNewValue;
+
+    /**
+     * @param templateId the identifier of the {@link RangeTemplate} that produced this action.
+     * @param newValue new value for the state displayed by the {@link RangeTemplate}.
+     */
+    public FloatAction(@NonNull String templateId, float newValue) {
+        this(templateId, newValue, null);
+    }
+
+    /**
+     * @param templateId the identifier of the {@link RangeTemplate} that originated this action.
+     * @param newValue new value for the state of the {@link RangeTemplate}.
+     * @param challengeValue a value sent by the user along with the action to authenticate. {@code}
+     *                       null is sent when no authentication is needed or has not been
+     *                       requested.
+     */
+
+    public FloatAction(@NonNull String templateId, float newValue,
+            @Nullable String challengeValue) {
+        super(templateId, challengeValue);
+        mNewValue = newValue;
+    }
+
+    public FloatAction(Parcel in) {
+        super(in);
+        mNewValue = in.readFloat();
+    }
+
+    /**
+     * The new value set for the range in the corresponding {@link RangeTemplate}.
+     */
+    public float getNewValue() {
+        return mNewValue;
+    }
+
+    /**
+     * @return {@link ControlAction#TYPE_FLOAT}
+     */
+    @Override
+    public int getActionType() {
+        return TYPE_FLOAT;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        super.writeToParcel(dest, flags);
+        dest.writeFloat(mNewValue);
+    }
+
+    public static final @NonNull Creator<FloatAction> CREATOR = new Creator<FloatAction>() {
+        @Override
+        public FloatAction createFromParcel(Parcel source) {
+            return new FloatAction(source);
+        }
+
+        @Override
+        public FloatAction[] newArray(int size) {
+            return new FloatAction[size];
+        }
+    };
+}
diff --git a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl b/core/java/android/service/controls/IControlsProvider.aidl
similarity index 61%
copy from wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl
copy to core/java/android/service/controls/IControlsProvider.aidl
index 007ec94..f778653 100644
--- a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl
+++ b/core/java/android/service/controls/IControlsProvider.aidl
@@ -1,5 +1,5 @@
-/**
- * Copyright (c) 2014, The Android Open Source Project
+/*
+ * Copyright (c) 2019, The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,6 +14,17 @@
  * limitations under the License.
  */
 
-package android.net.wifi;
+package android.service.controls;
 
-parcelable WifiActivityEnergyInfo;
+import android.service.controls.ControlAction;
+
+/** @hide */
+oneway interface IControlsProvider {
+    void load();
+
+    void subscribe(in List<String> controlIds);
+
+    void unsubscribe();
+
+    void onAction(in String controlId, in ControlAction action);
+}
\ No newline at end of file
diff --git a/core/java/android/service/controls/IControlsProviderCallback.aidl b/core/java/android/service/controls/IControlsProviderCallback.aidl
new file mode 100644
index 0000000..3dbb68c
--- /dev/null
+++ b/core/java/android/service/controls/IControlsProviderCallback.aidl
@@ -0,0 +1,29 @@
+/*
+ * 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.controls;
+
+import android.service.controls.Control;
+import android.service.controls.ControlState;
+
+/** @hide */
+oneway interface IControlsProviderCallback {
+    void onLoad(in List<Control> controls);
+
+    void onRefreshState(in List<ControlState> controlStates);
+
+    void onControlActionResponse(in String controlId, int response);
+}
\ No newline at end of file
diff --git a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl b/core/java/android/service/controls/RangeTemplate.aidl
similarity index 81%
copy from wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl
copy to core/java/android/service/controls/RangeTemplate.aidl
index 007ec94..a3d1ca0 100644
--- a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl
+++ b/core/java/android/service/controls/RangeTemplate.aidl
@@ -1,5 +1,5 @@
-/**
- * Copyright (c) 2014, The Android Open Source Project
+/*
+ * Copyright (c) 2019, The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 
-package android.net.wifi;
+package android.service.controls;
 
-parcelable WifiActivityEnergyInfo;
+parcelable RangeTemplate;
\ No newline at end of file
diff --git a/core/java/android/service/controls/RangeTemplate.java b/core/java/android/service/controls/RangeTemplate.java
new file mode 100644
index 0000000..70bf2dd
--- /dev/null
+++ b/core/java/android/service/controls/RangeTemplate.java
@@ -0,0 +1,190 @@
+/*
+ * 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.controls;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+
+import com.android.internal.util.Preconditions;
+
+import java.security.InvalidParameterException;
+
+/**
+ * A template for a {@link Control} with inputs in a "continuous" range of values.
+ *
+ * @see FloatAction
+ * @hide
+ */
+public final class RangeTemplate extends ControlTemplate {
+
+    private final float mMinValue;
+    private final float mMaxValue;
+    private final float mCurrentValue;
+    private final float mStepValue;
+    private final @NonNull CharSequence mFormatString;
+
+    /**
+     * Construct a new {@link RangeTemplate}.
+     *
+     * The range must be valid, meaning:
+     * <ul>
+     *     <li> {@code minValue} < {@code maxValue}
+     *     <li> {@code minValue} < {@code currentValue}
+     *     <li> {@code currentValue} < {@code maxValue}
+     *     <li> 0 < {@code stepValue}
+     * </ul>
+     * <p>
+     * The current value of the Control will be formatted accordingly.
+     *
+     * @param templateId the identifier for this template object
+     * @param minValue minimum value for the input
+     * @param maxValue maximum value for the input
+     * @param currentValue the current value of the {@link ControlState} containing this object.
+     * @param stepValue minimum value of increments/decrements when interacting with this control.
+     * @param formatString a formatting string as per {@link String#format} used to display the
+     *                    {@code currentValue}. If {@code null} is passed, the "%.1f" is used.
+     * @throws InvalidParameterException if the parameters passed do not make a valid range.
+     */
+    public RangeTemplate(@NonNull String templateId,
+            float minValue,
+            float maxValue,
+            float currentValue,
+            float stepValue,
+            @Nullable CharSequence formatString) {
+        super(templateId);
+        Preconditions.checkNotNull(formatString);
+        mMinValue = minValue;
+        mMaxValue = maxValue;
+        mCurrentValue = currentValue;
+        mStepValue = stepValue;
+        if (formatString != null) {
+            mFormatString = formatString;
+        } else {
+            mFormatString = "%.1f";
+        }
+        validate();
+    }
+
+    /**
+     * Construct a new {@link RangeTemplate} from a {@link Parcel}.
+     *
+     * @throws InvalidParameterException if the parameters passed do not make a valid range
+     * @see RangeTemplate#RangeTemplate(String, float, float, float, float, CharSequence)
+     * @hide
+     */
+    RangeTemplate(Parcel in) {
+        super(in);
+        mMinValue = in.readFloat();
+        mMaxValue = in.readFloat();
+        mCurrentValue = in.readFloat();
+        mStepValue = in.readFloat();
+        mFormatString = in.readCharSequence();
+        validate();
+    }
+
+    /**
+     * The minimum value for this range.
+     */
+    public float getMinValue() {
+        return mMinValue;
+    }
+
+    /**
+     * The maximum value for this range.
+     */
+    public float getMaxValue() {
+        return mMaxValue;
+    }
+
+    /**
+     * The current value for this range.
+     */
+    public float getCurrentValue() {
+        return mCurrentValue;
+    }
+
+    /**
+     * The value of the smallest increment or decrement that can be performed on this range.
+     */
+    public float getStepValue() {
+        return mStepValue;
+    }
+
+    /**
+     * Formatter for generating a user visible {@link String} representing the value
+     *         returned by {@link RangeTemplate#getCurrentValue}.
+     * @return a formatting string as specified in {@link String#format}
+     */
+    @NonNull
+    public CharSequence getFormatString() {
+        return mFormatString;
+    }
+
+    /**
+     * @return {@link ControlTemplate#TYPE_RANGE}
+     */
+    @Override
+    public int getTemplateType() {
+        return TYPE_RANGE;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        super.writeToParcel(dest, flags);
+        dest.writeFloat(mMinValue);
+        dest.writeFloat(mMaxValue);
+        dest.writeFloat(mCurrentValue);
+        dest.writeFloat(mStepValue);
+        dest.writeCharSequence(mFormatString);
+    }
+
+    /**
+     * Validate constructor parameters
+     *
+     * @throws InvalidParameterException if the parameters passed do not make a valid range
+     */
+    private void validate() {
+        if (Float.compare(mMinValue, mMaxValue) > 0) {
+            throw new InvalidParameterException(
+                    String.format("minValue=%f > maxValue=%f", mMinValue, mMaxValue));
+        }
+        if (Float.compare(mMinValue, mCurrentValue) > 0) {
+            throw new InvalidParameterException(
+                    String.format("minValue=%f > currentValue=%f", mMinValue, mCurrentValue));
+        }
+        if (Float.compare(mCurrentValue, mMaxValue) > 0) {
+            throw new InvalidParameterException(
+                    String.format("currentValue=%f > maxValue=%f", mCurrentValue, mMaxValue));
+        }
+        if (mStepValue <= 0) {
+            throw new InvalidParameterException(String.format("stepValue=%f <= 0", mStepValue));
+        }
+    }
+
+    public static final Creator<RangeTemplate> CREATOR = new Creator<RangeTemplate>() {
+        @Override
+        public RangeTemplate createFromParcel(Parcel source) {
+            return new RangeTemplate(source);
+        }
+
+        @Override
+        public RangeTemplate[] newArray(int size) {
+            return new RangeTemplate[size];
+        }
+    };
+}
diff --git a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl b/core/java/android/service/controls/ThumbnailTemplate.aidl
similarity index 81%
copy from wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl
copy to core/java/android/service/controls/ThumbnailTemplate.aidl
index 007ec94..fe8c7fe 100644
--- a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl
+++ b/core/java/android/service/controls/ThumbnailTemplate.aidl
@@ -1,5 +1,5 @@
-/**
- * Copyright (c) 2014, The Android Open Source Project
+/*
+ * Copyright (c) 2019, The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 
-package android.net.wifi;
+package android.service.controls;
 
-parcelable WifiActivityEnergyInfo;
+parcelable ThumbnailTemplate;
\ No newline at end of file
diff --git a/core/java/android/service/controls/ThumbnailTemplate.java b/core/java/android/service/controls/ThumbnailTemplate.java
new file mode 100644
index 0000000..796d2de
--- /dev/null
+++ b/core/java/android/service/controls/ThumbnailTemplate.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.controls;
+
+import android.annotation.NonNull;
+import android.graphics.drawable.Icon;
+import android.os.Parcel;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * A template for a {@link Control} that displays an image.
+ * @hide
+ */
+public final class ThumbnailTemplate extends ControlTemplate {
+
+    private final @NonNull Icon mThumbnail;
+    private final @NonNull CharSequence mContentDescription;
+
+    /**
+     * @param templateId the identifier for this template object
+     * @param thumbnail an image to display on the {@link Control}
+     * @param contentDescription a description of the image for accessibility.
+     */
+    public ThumbnailTemplate(@NonNull String templateId, @NonNull Icon thumbnail,
+            @NonNull CharSequence contentDescription) {
+        super(templateId);
+        Preconditions.checkNotNull(thumbnail);
+        Preconditions.checkNotNull(contentDescription);
+        mThumbnail = thumbnail;
+        mContentDescription = contentDescription;
+    }
+
+    ThumbnailTemplate(Parcel in) {
+        super(in);
+        mThumbnail = Icon.CREATOR.createFromParcel(in);
+        mContentDescription = in.readCharSequence();
+    }
+
+    /**
+     * The {@link Icon} (image) displayed by this template.
+     */
+    @NonNull
+    public Icon getThumbnail() {
+        return mThumbnail;
+    }
+
+    /**
+     * The description of the image returned by {@link ThumbnailTemplate#getThumbnail()}
+     */
+    @NonNull
+    public CharSequence getContentDescription() {
+        return mContentDescription;
+    }
+
+    /**
+     * @return {@link ControlTemplate#TYPE_THUMBNAIL}
+     */
+    @Override
+    public int getTemplateType() {
+        return TYPE_THUMBNAIL;
+    }
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        super.writeToParcel(dest, flags);
+        mThumbnail.writeToParcel(dest, flags);
+        dest.writeCharSequence(mContentDescription);
+    }
+
+    public static final Creator<ThumbnailTemplate> CREATOR = new Creator<ThumbnailTemplate>() {
+        @Override
+        public ThumbnailTemplate createFromParcel(Parcel source) {
+            return new ThumbnailTemplate(source);
+        }
+
+        @Override
+        public ThumbnailTemplate[] newArray(int size) {
+            return new ThumbnailTemplate[size];
+        }
+    };
+}
diff --git a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl b/core/java/android/service/controls/ToggleTemplate.aidl
similarity index 81%
copy from wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl
copy to core/java/android/service/controls/ToggleTemplate.aidl
index 007ec94..1c823d9 100644
--- a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl
+++ b/core/java/android/service/controls/ToggleTemplate.aidl
@@ -1,5 +1,5 @@
-/**
- * Copyright (c) 2014, The Android Open Source Project
+/*
+ * Copyright (c) 2019, The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 
-package android.net.wifi;
+package android.service.controls;
 
-parcelable WifiActivityEnergyInfo;
+parcelable ToggleTemplate;
\ No newline at end of file
diff --git a/core/java/android/service/controls/ToggleTemplate.java b/core/java/android/service/controls/ToggleTemplate.java
new file mode 100644
index 0000000..3766bd1
--- /dev/null
+++ b/core/java/android/service/controls/ToggleTemplate.java
@@ -0,0 +1,86 @@
+/*
+ * 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.controls;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * A template for a {@link Control} with a single button that can be toggled between two states.
+ *
+ * The states for the toggle correspond to the states in {@link ControlButton#isActive()}.
+ * An action on this template will originate a {@link BooleanAction} to change that state.
+ *
+ * @see BooleanAction
+ * @hide
+ */
+public final class ToggleTemplate extends ControlTemplate {
+
+    private final @NonNull ControlButton mButton;
+
+    /**
+     * @param templateId the identifier for this template object
+     * @param button a {@ControlButton} that can show the current state and toggle it
+     */
+    public ToggleTemplate(@NonNull String templateId, @NonNull ControlButton button) {
+        super(templateId);
+        Preconditions.checkNotNull(button);
+        mButton = button;
+    }
+
+    ToggleTemplate(Parcel in) {
+        super(in);
+        mButton = ControlButton.CREATOR.createFromParcel(in);
+    }
+
+    /**
+     * The button provided to this object in {@link ToggleTemplate#ToggleTemplate}
+     */
+    @NonNull
+    public ControlButton getButton() {
+        return mButton;
+    }
+
+    /**
+     * @return {@link ControlTemplate#TYPE_TOGGLE}
+     */
+    @Override
+    public int getTemplateType() {
+        return TYPE_TOGGLE;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        super.writeToParcel(dest, flags);
+        mButton.writeToParcel(dest, flags);
+    }
+
+    public static final Creator<ToggleTemplate> CREATOR = new Creator<ToggleTemplate>() {
+        @Override
+        public ToggleTemplate createFromParcel(Parcel source) {
+            return new ToggleTemplate(source);
+        }
+
+        @Override
+        public ToggleTemplate[] newArray(int size) {
+            return new ToggleTemplate[size];
+        }
+    };
+
+}
diff --git a/core/java/android/service/incremental/IIncrementalDataLoaderService.aidl b/core/java/android/service/incremental/IIncrementalDataLoaderService.aidl
deleted file mode 100644
index 723fc59..0000000
--- a/core/java/android/service/incremental/IIncrementalDataLoaderService.aidl
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.service.incremental;
-
-import android.os.incremental.IncrementalDataLoaderParamsParcel;
-import android.os.incremental.IncrementalFileSystemControlParcel;
-import android.service.incremental.IIncrementalDataLoaderStatusListener;
-
-/** @hide */
-oneway interface IIncrementalDataLoaderService {
-   void createDataLoader(in int storageId,
-                 in IncrementalFileSystemControlParcel control,
-                 in IncrementalDataLoaderParamsParcel params,
-                 in IIncrementalDataLoaderStatusListener listener,
-                 in boolean start);
-   void startDataLoader(in int storageId);
-   void stopDataLoader(in int storageId);
-   void destroyDataLoader(in int storageId);
-   void onFileCreated(in int storageId, in long inode, in byte[] metadata);
-}
diff --git a/core/java/android/service/notification/IConditionProvider.aidl b/core/java/android/service/notification/IConditionProvider.aidl
index 3f3c6b8..dd3904f 100644
--- a/core/java/android/service/notification/IConditionProvider.aidl
+++ b/core/java/android/service/notification/IConditionProvider.aidl
@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2014, The Android Open Source Project
+ * Copyright (c) 2019, The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
diff --git a/core/java/android/telephony/CellBroadcastIntents.java b/core/java/android/telephony/CellBroadcastIntents.java
new file mode 100644
index 0000000..4474f3e
--- /dev/null
+++ b/core/java/android/telephony/CellBroadcastIntents.java
@@ -0,0 +1,96 @@
+/*
+ * 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.telephony;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.util.Log;
+
+/**
+ * A static helper class used to send Intents with prepopulated flags.
+ * <p>
+ * This is intended to be used by the CellBroadcastService and will throw a security exception if
+ * used from a UID besides the network stack UID.
+ *
+ * @hide
+ */
+@SystemApi
+public class CellBroadcastIntents {
+    private static final String LOG_TAG = "CellBroadcastIntents";
+
+    /**
+     * @hide
+     */
+    private CellBroadcastIntents() {
+    }
+
+    /**
+     * Returns an intent which can be received by background BroadcastReceivers. This is only
+     * intended to be used by the CellBroadcastService and will throw a security exception if called
+     * from another UID.
+     *
+     * @param context            The context from which to send the broadcast
+     * @param user               The user from which to send the broadcast
+     * @param intent             The Intent to broadcast; all receivers matching this Intent will
+     *                           receive the broadcast.
+     * @param receiverPermission String naming a permissions that a receiver must hold in order to
+     *                           receive your broadcast. If null, no permission is required.
+     * @param receiverAppOp      The app op associated with the broadcast. If null, no appOp is
+     *                           required. If both receiverAppOp and receiverPermission are
+     *                           non-null, a receiver must have both of them to receive the
+     *                           broadcast
+     * @param resultReceiver     Your own BroadcastReceiver to treat as the final receiver of the
+     *                           broadcast.
+     * @param scheduler          A custom Handler with which to schedule the resultReceiver
+     *                           callback; if null it will be scheduled in the Context's main
+     *                           thread.
+     * @param initialCode        An initial value for the result code.  Often Activity.RESULT_OK.
+     * @param initialData        An initial value for the result data.  Often null.
+     * @param initialExtras      An initial value for the result extras.  Often null.
+     */
+    public static void sendOrderedBroadcastForBackgroundReceivers(@NonNull Context context,
+            @Nullable UserHandle user, @NonNull Intent intent, @Nullable String receiverPermission,
+            @Nullable String receiverAppOp, @Nullable BroadcastReceiver resultReceiver,
+            @Nullable Handler scheduler, int initialCode, @Nullable String initialData,
+            @Nullable Bundle initialExtras) {
+        Log.d(LOG_TAG, "sendOrderedBroadcastForBackgroundReceivers intent=" + intent.getAction());
+        int status = context.checkCallingOrSelfPermission(
+                "android.permission.GRANT_RUNTIME_PERMISSIONS_TO_TELEPHONY_DEFAULTS");
+        if (status == PackageManager.PERMISSION_DENIED) {
+            throw new SecurityException(
+                    "Caller does not have permission to send broadcast for background receivers");
+        }
+        intent.setFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+        if (user != null) {
+            context.createContextAsUser(user, 0).sendOrderedBroadcast(intent, receiverPermission,
+                    receiverAppOp, resultReceiver, scheduler, initialCode, initialData,
+                    initialExtras);
+        } else {
+            context.sendOrderedBroadcast(intent, receiverPermission,
+                    receiverAppOp, resultReceiver, scheduler, initialCode, initialData,
+                    initialExtras);
+        }
+    }
+}
diff --git a/telephony/java/android/telephony/DataConnectionRealTimeInfo.aidl b/core/java/android/telephony/DataConnectionRealTimeInfo.aidl
similarity index 100%
rename from telephony/java/android/telephony/DataConnectionRealTimeInfo.aidl
rename to core/java/android/telephony/DataConnectionRealTimeInfo.aidl
diff --git a/telephony/java/android/telephony/DataConnectionRealTimeInfo.java b/core/java/android/telephony/DataConnectionRealTimeInfo.java
similarity index 100%
rename from telephony/java/android/telephony/DataConnectionRealTimeInfo.java
rename to core/java/android/telephony/DataConnectionRealTimeInfo.java
diff --git a/core/java/android/telephony/PhoneStateListener.java b/core/java/android/telephony/PhoneStateListener.java
index a65c8fd..716a522 100644
--- a/core/java/android/telephony/PhoneStateListener.java
+++ b/core/java/android/telephony/PhoneStateListener.java
@@ -286,14 +286,6 @@
     public static final int LISTEN_USER_MOBILE_DATA_STATE                  = 0x00080000;
 
     /**
-     *  Listen for changes to the physical channel configuration.
-     *
-     *  @see #onPhysicalChannelConfigurationChanged
-     *  @hide
-     */
-    public static final int LISTEN_PHYSICAL_CHANNEL_CONFIGURATION          = 0x00100000;
-
-    /**
      *  Listen for changes to the phone capability.
      *
      *  @see #onPhoneCapabilityChanged
@@ -831,24 +823,6 @@
     }
 
     /**
-     * Callback invoked when the current physical channel configuration has changed on the
-     * registered subscription.
-     * Note, the registration subId comes from {@link TelephonyManager} object which registers
-     * PhoneStateListener by {@link TelephonyManager#listen(PhoneStateListener, int)}.
-     * If this TelephonyManager object was created with
-     * {@link TelephonyManager#createForSubscriptionId(int)}, then the callback applies to the
-     * subId. Otherwise, this callback applies to
-     * {@link SubscriptionManager#getDefaultSubscriptionId()}.
-     *
-     * @param configs List of the current {@link PhysicalChannelConfig}s
-     * @hide
-     */
-    public void onPhysicalChannelConfigurationChanged(
-            @NonNull List<PhysicalChannelConfig> configs) {
-        // default implementation empty
-    }
-
-    /**
      * Callback invoked when the current emergency number list has changed on the registered
      * subscription.
      * Note, the registration subId comes from {@link TelephonyManager} object which registers
@@ -1194,15 +1168,6 @@
                     () -> mExecutor.execute(() -> psl.onCarrierNetworkChange(active)));
         }
 
-        public void onPhysicalChannelConfigurationChanged(List<PhysicalChannelConfig> configs) {
-            PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
-            if (psl == null) return;
-
-            Binder.withCleanCallingIdentity(
-                    () -> mExecutor.execute(
-                            () -> psl.onPhysicalChannelConfigurationChanged(configs)));
-        }
-
         public void onEmergencyNumberListChanged(Map emergencyNumberList) {
             PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
             if (psl == null) return;
diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java
index f66a679..9d7b57b 100644
--- a/core/java/android/telephony/TelephonyRegistryManager.java
+++ b/core/java/android/telephony/TelephonyRegistryManager.java
@@ -455,6 +455,19 @@
     }
 
     /**
+     * Sim activation type: voice
+     * @see #notifyVoiceActivationStateChanged
+     * @hide
+     */
+    public static final int SIM_ACTIVATION_TYPE_VOICE = 0;
+    /**
+     * Sim activation type: data
+     * @see #notifyDataActivationStateChanged
+     * @hide
+     */
+    public static final int SIM_ACTIVATION_TYPE_DATA = 1;
+
+    /**
      * Notify data activation state changed on certain subscription.
      * @see TelephonyManager#getDataActivationState()
      *
@@ -469,7 +482,7 @@
         @SimActivationState int activationState) {
         try {
             sRegistry.notifySimActivationStateChangedForPhoneId(slotIndex, subId,
-                TelephonyManager.SIM_ACTIVATION_TYPE_DATA, activationState);
+                    SIM_ACTIVATION_TYPE_DATA, activationState);
         } catch (RemoteException ex) {
             // system process is dead
         }
@@ -490,7 +503,7 @@
         @SimActivationState int activationState) {
         try {
             sRegistry.notifySimActivationStateChangedForPhoneId(slotIndex, subId,
-                TelephonyManager.SIM_ACTIVATION_TYPE_VOICE, activationState);
+                    SIM_ACTIVATION_TYPE_VOICE, activationState);
         } catch (RemoteException ex) {
             // system process is dead
         }
diff --git a/core/java/android/text/LoginFilter.java b/core/java/android/text/LoginFilter.java
index e2d1596..0e4eec44 100644
--- a/core/java/android/text/LoginFilter.java
+++ b/core/java/android/text/LoginFilter.java
@@ -19,7 +19,10 @@
 /**
  * Abstract class for filtering login-related text (user names and passwords)
  * 
+ * @deprecated Password requirements should not be hardcoded in clients. This class also does not
+ * handle non-BMP characters.
  */
+@Deprecated
 public abstract class LoginFilter implements InputFilter {
     private boolean mAppendInvalid;  // whether to append or ignore invalid characters
     /**
@@ -130,7 +133,9 @@
      * account creation. It prevents the user from entering user names with characters other than 
      * [a-zA-Z0-9.]. 
      * 
+     * @deprecated Do not encode assumptions about Google account names into client applications.
      */
+    @Deprecated
     public static class UsernameFilterGMail extends LoginFilter {
         
         public UsernameFilterGMail() {
@@ -190,8 +195,12 @@
     /**
      * This filter is compatible with GMail passwords which restricts characters to 
      * the Latin-1 (ISO8859-1) char set.
-     * 
+     *
+     * @deprecated Do not handle a user's Google password. Refer to
+     *   <a href="https://support.google.com/accounts/answer/32040">Google Help</a> for
+     *   password restriction information.
      */
+    @Deprecated
     public static class PasswordFilterGMail extends LoginFilter {
         
         public PasswordFilterGMail() {
diff --git a/core/java/android/util/StatsEvent.java b/core/java/android/util/StatsEvent.java
index d7ec30c..0a4069d 100644
--- a/core/java/android/util/StatsEvent.java
+++ b/core/java/android/util/StatsEvent.java
@@ -177,7 +177,7 @@
      * @hide
      **/
     @VisibleForTesting
-    public static final int ERROR_ATTRIBUTION_UIDS_TAGS_SIZES_NOT_EQUAL = 0x400;
+    public static final int ERROR_ATTRIBUTION_UIDS_TAGS_SIZES_NOT_EQUAL = 0x1000;
 
     // Size limits.
 
@@ -628,9 +628,9 @@
             if (0 == mErrorMask) {
                 mBuffer.putByte(POS_NUM_ELEMENTS, (byte) mNumElements);
             } else {
-                mBuffer.putByte(0, TYPE_ERRORS);
-                mBuffer.putByte(POS_NUM_ELEMENTS, (byte) 3);
+                mPos += mBuffer.putByte(mPos, TYPE_ERRORS);
                 mPos += mBuffer.putInt(mPos, mErrorMask);
+                mBuffer.putByte(POS_NUM_ELEMENTS, (byte) 3);
                 size = mPos;
             }
 
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 3c93bb7..bc70d63 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -467,6 +467,10 @@
         }
     }
 
+    boolean isAnimating() {
+        return mAnimationDirection != DIRECTION_NONE;
+    }
+
     private InsetsSourceConsumer createConsumerOfType(int type) {
         if (type == ITYPE_IME) {
             return new ImeInsetsSourceConsumer(mState, Transaction::new, this);
@@ -514,6 +518,7 @@
                 } else {
                     hideDirectly(types);
                 }
+                mAnimationDirection = show ? DIRECTION_SHOW : DIRECTION_HIDE;
                 mAnimator = ObjectAnimator.ofObject(
                         controller,
                         new InsetsProperty(),
diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java
index b1caf18..c6d9898 100644
--- a/core/java/android/view/InsetsSourceConsumer.java
+++ b/core/java/android/view/InsetsSourceConsumer.java
@@ -167,7 +167,8 @@
     }
 
     private void applyHiddenToControl() {
-        if (mSourceControl == null || mSourceControl.getLeash() == null) {
+        if (mSourceControl == null || mSourceControl.getLeash() == null
+                || mController.isAnimating()) {
             return;
         }
 
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index fc80e00..9e5e20a 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -442,6 +442,12 @@
     public static final int METADATA_TASK_ID = 3;
 
     /**
+     * Accessibility ID to allow association between surfaces and accessibility tree.
+     * @hide
+     */
+    public static final int METADATA_ACCESSIBILITY_ID = 4;
+
+    /**
      * A wrapper around GraphicBuffer that contains extra information about how to
      * interpret the screenshot GraphicBuffer.
      * @hide
@@ -2651,6 +2657,7 @@
          * @hide
          */
         public Transaction setMetadata(SurfaceControl sc, int key, Parcel data) {
+            sc.checkNotReleased();
             nativeSetMetadata(mNativeObject, sc.mNativeObject, key, data);
             return this;
         }
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index c62e69c..7a817b6 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -1181,7 +1181,8 @@
          * a soft input method, so it will be Z-ordered and positioned
          * independently of any active input method (typically this means it
          * gets Z-ordered on top of the input method, so it can use the full
-         * screen for its content and cover the input method if needed.) */
+         * screen for its content and cover the input method if needed.  You
+         * can use {@link #FLAG_ALT_FOCUSABLE_IM} to modify this behavior. */
         public static final int FLAG_NOT_FOCUSABLE      = 0x00000008;
 
         /** Window flag: this window can never receive touch events. */
@@ -1287,11 +1288,14 @@
          * set for you by Window as described in {@link Window#setFlags}.*/
         public static final int FLAG_LAYOUT_INSET_DECOR = 0x00010000;
 
-        /** Window flag: When set, input method can't interact with the focusable window
-         * and can be placed to use more space and cover the input method.
-         * Note: When combined with {@link #FLAG_NOT_FOCUSABLE}, this flag has no
-         * effect since input method cannot interact with windows having {@link #FLAG_NOT_FOCUSABLE}
-         * flag set.
+        /** Window flag: invert the state of {@link #FLAG_NOT_FOCUSABLE} with
+         * respect to how this window interacts with the current method.  That
+         * is, if FLAG_NOT_FOCUSABLE is set and this flag is set, then the
+         * window will behave as if it needs to interact with the input method
+         * and thus be placed behind/away from it; if FLAG_NOT_FOCUSABLE is
+         * not set and this flag is set, then the window will behave as if it
+         * doesn't need to interact with the input method and can be placed
+         * to use more space and cover the input method.
          */
         public static final int FLAG_ALT_FOCUSABLE_IM = 0x00020000;
 
@@ -1989,12 +1993,16 @@
          *
          * @param flags The current window manager flags.
          *
-         * @return Returns {@code true} if such a window should be behind/interact
-         * with an input method, (@code false} if not.
+         * @return Returns true if such a window should be behind/interact
+         * with an input method, false if not.
          */
         public static boolean mayUseInputMethod(int flags) {
-            return (flags & FLAG_NOT_FOCUSABLE) != FLAG_NOT_FOCUSABLE
-                    && (flags & FLAG_ALT_FOCUSABLE_IM) != FLAG_ALT_FOCUSABLE_IM;
+            switch (flags&(FLAG_NOT_FOCUSABLE|FLAG_ALT_FOCUSABLE_IM)) {
+                case 0:
+                case FLAG_NOT_FOCUSABLE|FLAG_ALT_FOCUSABLE_IM:
+                    return true;
+            }
+            return false;
         }
 
         /**
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 54446e1..9c04b39 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -47,6 +47,7 @@
 import android.service.autofill.AutofillService;
 import android.service.autofill.FillEventHistory;
 import android.service.autofill.UserData;
+import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.DebugUtils;
@@ -61,6 +62,7 @@
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.accessibility.AccessibilityNodeProvider;
 import android.view.accessibility.AccessibilityWindowInfo;
+import android.widget.EditText;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.logging.MetricsLogger;
@@ -1073,6 +1075,8 @@
                 } else if (sVerbose) {
                     Log.v(TAG, "Ignoring visibility change on " + id + ": no tracked views");
                 }
+            } else if (!virtual && isVisible) {
+                startAutofillIfNeededLocked(view);
             }
         }
     }
@@ -1238,9 +1242,11 @@
                 return;
             }
             if (!mEnabled || !isActiveLocked()) {
-                if (sVerbose) {
-                    Log.v(TAG, "notifyValueChanged(" + view.getAutofillId()
-                            + "): ignoring on state " + getStateAsStringLocked());
+                if (!startAutofillIfNeededLocked(view)) {
+                    if (sVerbose) {
+                        Log.v(TAG, "notifyValueChanged(" + view.getAutofillId()
+                                + "): ignoring on state " + getStateAsStringLocked());
+                    }
                 }
                 return;
             }
@@ -1879,6 +1885,37 @@
         }
     }
 
+    @GuardedBy("mLock")
+    private boolean startAutofillIfNeededLocked(View view) {
+        if (mState == STATE_UNKNOWN
+                && mSessionId == NO_SESSION
+                && view instanceof EditText
+                && !TextUtils.isEmpty(((EditText) view).getText())
+                && !view.isFocused()
+                && view.isImportantForAutofill()
+                && view.isLaidOut()
+                && view.isVisibleToUser()) {
+
+            ensureServiceClientAddedIfNeededLocked();
+
+            if (sVerbose) {
+                Log.v(TAG, "startAutofillIfNeededLocked(): enabled=" + mEnabled);
+            }
+            if (mEnabled && !isClientDisablingEnterExitEvent()) {
+                final AutofillId id = view.getAutofillId();
+                final AutofillValue value = view.getAutofillValue();
+                // Starts new session.
+                startSessionLocked(id, /* bounds= */ null, /* value= */ null, /* flags= */ 0);
+                // Updates value.
+                updateSessionLocked(id, /* bounds= */ null, value, ACTION_VALUE_CHANGED,
+                        /* flags= */ 0);
+                addEnteredIdLocked(id);
+                return true;
+            }
+        }
+        return false;
+    }
+
     /**
      * Registers a {@link AutofillCallback} to receive autofill events.
      *
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 187ab46..eb0d9bf 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -152,6 +152,9 @@
     // handles.
     private static final boolean FLAG_USE_MAGNIFIER = true;
 
+    private static final int DELAY_BEFORE_HANDLE_FADES_OUT = 4000;
+    private static final int RECENT_CUT_COPY_DURATION_MS = 15 * 1000; // 15 seconds in millis
+
     static final int BLINK = 500;
     private static final int DRAG_SHADOW_MAX_TEXT_LENGTH = 20;
     private static final float LINE_SLOP_MULTIPLIER_FOR_HANDLEVIEWS = 0.5f;
@@ -326,8 +329,6 @@
     // Global listener that detects changes in the global position of the TextView
     private PositionListener mPositionListener;
 
-    private float mLastDownPositionX, mLastDownPositionY;
-    private float mLastUpPositionX, mLastUpPositionY;
     private float mContextMenuAnchorX, mContextMenuAnchorY;
     Callback mCustomSelectionActionModeCallback;
     Callback mCustomInsertionActionModeCallback;
@@ -336,18 +337,11 @@
     @UnsupportedAppUsage
     boolean mCreatedWithASelection;
 
-    // Indicates the current tap state (first tap, double tap, or triple click).
-    private int mTapState = TAP_STATE_INITIAL;
-    private long mLastTouchUpTime = 0;
-    private static final int TAP_STATE_INITIAL = 0;
-    private static final int TAP_STATE_FIRST_TAP = 1;
-    private static final int TAP_STATE_DOUBLE_TAP = 2;
-    // Only for mouse input.
-    private static final int TAP_STATE_TRIPLE_CLICK = 3;
-
     // The button state as of the last time #onTouchEvent is called.
     private int mLastButtonState;
 
+    private final EditorTouchState mTouchState = new EditorTouchState();
+
     private Runnable mInsertionActionModeRunnable;
 
     // The span controller helps monitoring the changes to which the Editor needs to react:
@@ -1193,10 +1187,10 @@
             logCursor("performLongClick", "handled=%s", handled);
         }
         // Long press in empty space moves cursor and starts the insertion action mode.
-        if (!handled && !isPositionOnText(mLastDownPositionX, mLastDownPositionY)
+        if (!handled && !isPositionOnText(mTouchState.getLastDownX(), mTouchState.getLastDownY())
                 && mInsertionControllerEnabled) {
-            final int offset = mTextView.getOffsetForPosition(mLastDownPositionX,
-                    mLastDownPositionY);
+            final int offset = mTextView.getOffsetForPosition(mTouchState.getLastDownX(),
+                    mTouchState.getLastDownY());
             Selection.setSelection((Spannable) mTextView.getText(), offset);
             getInsertionController().show();
             mIsInsertionActionModeStartPending = true;
@@ -1240,11 +1234,11 @@
     }
 
     float getLastUpPositionX() {
-        return mLastUpPositionX;
+        return mTouchState.getLastUpX();
     }
 
     float getLastUpPositionY() {
-        return mLastUpPositionY;
+        return mTouchState.getLastUpY();
     }
 
     private long getLastTouchOffsets() {
@@ -1279,6 +1273,9 @@
                 // Has to be done before onTakeFocus, which can be overloaded.
                 final int lastTapPosition = getLastTapPosition();
                 if (lastTapPosition >= 0) {
+                    if (TextView.DEBUG_CURSOR) {
+                        logCursor("onFocusChanged", "setting cursor position: %d", lastTapPosition);
+                    }
                     Selection.setSelection((Spannable) mTextView.getText(), lastTapPosition);
                 }
 
@@ -1443,39 +1440,6 @@
         }
     }
 
-    private void updateTapState(MotionEvent event) {
-        final int action = event.getActionMasked();
-        if (action == MotionEvent.ACTION_DOWN) {
-            final boolean isMouse = event.isFromSource(InputDevice.SOURCE_MOUSE);
-            // Detect double tap and triple click.
-            if (((mTapState == TAP_STATE_FIRST_TAP)
-                    || ((mTapState == TAP_STATE_DOUBLE_TAP) && isMouse))
-                            && (SystemClock.uptimeMillis() - mLastTouchUpTime)
-                                    <= ViewConfiguration.getDoubleTapTimeout()) {
-                if (mTapState == TAP_STATE_FIRST_TAP) {
-                    mTapState = TAP_STATE_DOUBLE_TAP;
-                } else {
-                    mTapState = TAP_STATE_TRIPLE_CLICK;
-                }
-                if (TextView.DEBUG_CURSOR) {
-                    logCursor("updateTapState", "ACTION_DOWN: %s tap detected",
-                            (mTapState == TAP_STATE_DOUBLE_TAP ? "double" : "triple"));
-                }
-            } else {
-                mTapState = TAP_STATE_FIRST_TAP;
-                if (TextView.DEBUG_CURSOR) {
-                    logCursor("updateTapState", "ACTION_DOWN: first tap detected");
-                }
-            }
-        }
-        if (action == MotionEvent.ACTION_UP) {
-            mLastTouchUpTime = SystemClock.uptimeMillis();
-            if (TextView.DEBUG_CURSOR) {
-                logCursor("updateTapState", "ACTION_UP");
-            }
-        }
-    }
-
     private boolean shouldFilterOutTouchEvent(MotionEvent event) {
         if (!event.isFromSource(InputDevice.SOURCE_MOUSE)) {
             return false;
@@ -1503,7 +1467,8 @@
             }
             return;
         }
-        updateTapState(event);
+        ViewConfiguration viewConfiguration = ViewConfiguration.get(mTextView.getContext());
+        mTouchState.update(event, viewConfiguration);
         updateFloatingToolbarVisibility(event);
 
         if (hasSelectionController()) {
@@ -1515,15 +1480,7 @@
             mShowSuggestionRunnable = null;
         }
 
-        if (event.getActionMasked() == MotionEvent.ACTION_UP) {
-            mLastUpPositionX = event.getX();
-            mLastUpPositionY = event.getY();
-        }
-
         if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
-            mLastDownPositionX = event.getX();
-            mLastDownPositionY = event.getY();
-
             // Reset this state; it will be re-set if super.onTouchEvent
             // causes focus to move to the view.
             mTouchFocusSelected = false;
@@ -5067,7 +5024,10 @@
         public boolean onTouchEvent(MotionEvent ev) {
             if (TextView.DEBUG_CURSOR) {
                 logCursor(this.getClass().getSimpleName() + ": HandleView: onTouchEvent",
-                        MotionEvent.actionToString(ev.getActionMasked()));
+                        "%d: %s (%f,%f)",
+                        ev.getSequenceNumber(),
+                        MotionEvent.actionToString(ev.getActionMasked()),
+                        ev.getX(), ev.getY());
             }
 
             updateFloatingToolbarVisibility(ev);
@@ -5145,56 +5105,14 @@
     }
 
     private class InsertionHandleView extends HandleView {
-        private static final int DELAY_BEFORE_HANDLE_FADES_OUT = 4000;
-        private static final int RECENT_CUT_COPY_DURATION = 15 * 1000; // seconds
-
         // Used to detect taps on the insertion handle, which will affect the insertion action mode
-        private float mDownPositionX, mDownPositionY;
+        private float mLastDownRawX, mLastDownRawY;
         private Runnable mHider;
 
         public InsertionHandleView(Drawable drawable) {
             super(drawable, drawable, com.android.internal.R.id.insertion_handle);
         }
 
-        @Override
-        public void show() {
-            super.show();
-
-            final long durationSinceCutOrCopy =
-                    SystemClock.uptimeMillis() - TextView.sLastCutCopyOrTextChangedTime;
-
-            // Cancel the single tap delayed runnable.
-            if (mInsertionActionModeRunnable != null
-                    && ((mTapState == TAP_STATE_DOUBLE_TAP)
-                            || (mTapState == TAP_STATE_TRIPLE_CLICK)
-                            || isCursorInsideEasyCorrectionSpan())) {
-                mTextView.removeCallbacks(mInsertionActionModeRunnable);
-            }
-
-            // Prepare and schedule the single tap runnable to run exactly after the double tap
-            // timeout has passed.
-            if ((mTapState != TAP_STATE_DOUBLE_TAP) && (mTapState != TAP_STATE_TRIPLE_CLICK)
-                    && !isCursorInsideEasyCorrectionSpan()
-                    && (durationSinceCutOrCopy < RECENT_CUT_COPY_DURATION)) {
-                if (mTextActionMode == null) {
-                    if (mInsertionActionModeRunnable == null) {
-                        mInsertionActionModeRunnable = new Runnable() {
-                            @Override
-                            public void run() {
-                                startInsertionActionMode();
-                            }
-                        };
-                    }
-                    mTextView.postDelayed(
-                            mInsertionActionModeRunnable,
-                            ViewConfiguration.getDoubleTapTimeout() + 1);
-                }
-
-            }
-
-            hideAfterDelay();
-        }
-
         private void hideAfterDelay() {
             if (mHider == null) {
                 mHider = new Runnable() {
@@ -5250,8 +5168,8 @@
 
             switch (ev.getActionMasked()) {
                 case MotionEvent.ACTION_DOWN:
-                    mDownPositionX = ev.getRawX();
-                    mDownPositionY = ev.getRawY();
+                    mLastDownRawX = ev.getRawX();
+                    mLastDownRawY = ev.getRawY();
                     updateMagnifier(ev);
                     break;
 
@@ -5261,8 +5179,8 @@
 
                 case MotionEvent.ACTION_UP:
                     if (!offsetHasBeenChanged()) {
-                        final float deltaX = mDownPositionX - ev.getRawX();
-                        final float deltaY = mDownPositionY - ev.getRawY();
+                        final float deltaX = mLastDownRawX - ev.getRawX();
+                        final float deltaY = mLastDownRawY - ev.getRawY();
                         final float distanceSquared = deltaX * deltaX + deltaY * deltaY;
 
                         final ViewConfiguration viewConfiguration = ViewConfiguration.get(
@@ -5804,6 +5722,37 @@
         public void show() {
             getHandle().show();
 
+            final long durationSinceCutOrCopy =
+                    SystemClock.uptimeMillis() - TextView.sLastCutCopyOrTextChangedTime;
+
+            // Cancel the single tap delayed runnable.
+            if (mInsertionActionModeRunnable != null
+                    && (mTouchState.isMultiTap() || isCursorInsideEasyCorrectionSpan())) {
+                mTextView.removeCallbacks(mInsertionActionModeRunnable);
+            }
+
+            // Prepare and schedule the single tap runnable to run exactly after the double tap
+            // timeout has passed.
+            if (!mTouchState.isMultiTap()
+                    && !isCursorInsideEasyCorrectionSpan()
+                    && (durationSinceCutOrCopy < RECENT_CUT_COPY_DURATION_MS)) {
+                if (mTextActionMode == null) {
+                    if (mInsertionActionModeRunnable == null) {
+                        mInsertionActionModeRunnable = new Runnable() {
+                            @Override
+                            public void run() {
+                                startInsertionActionMode();
+                            }
+                        };
+                    }
+                    mTextView.postDelayed(
+                            mInsertionActionModeRunnable,
+                            ViewConfiguration.getDoubleTapTimeout() + 1);
+                }
+            }
+
+            getHandle().hideAfterDelay();
+
             if (mSelectionModifierCursorController != null) {
                 mSelectionModifierCursorController.hide();
             }
@@ -5870,7 +5819,6 @@
         // The offsets of that last touch down event. Remembered to start selection there.
         private int mMinTouchOffset, mMaxTouchOffset;
 
-        private float mDownPositionX, mDownPositionY;
         private boolean mGestureStayedInTapRegion;
 
         // Where the user first starts the drag motion.
@@ -5940,13 +5888,18 @@
         }
 
         public void enterDrag(int dragAcceleratorMode) {
+            if (TextView.DEBUG_CURSOR) {
+                logCursor("SelectionModifierCursorController: enterDrag",
+                        "starting selection drag: mode=%s", dragAcceleratorMode);
+            }
+
             // Just need to init the handles / hide insertion cursor.
             show();
             mDragAcceleratorMode = dragAcceleratorMode;
             // Start location of selection.
-            mStartOffset = mTextView.getOffsetForPosition(mLastDownPositionX,
-                    mLastDownPositionY);
-            mLineSelectionIsOn = mTextView.getLineAtCoordinate(mLastDownPositionY);
+            mStartOffset = mTextView.getOffsetForPosition(mTouchState.getLastDownX(),
+                    mTouchState.getLastDownY());
+            mLineSelectionIsOn = mTextView.getLineAtCoordinate(mTouchState.getLastDownY());
             // Don't show the handles until user has lifted finger.
             hide();
 
@@ -5974,36 +5927,20 @@
                                 eventX, eventY);
 
                         // Double tap detection
-                        if (mGestureStayedInTapRegion) {
-                            if (mTapState == TAP_STATE_DOUBLE_TAP
-                                    || mTapState == TAP_STATE_TRIPLE_CLICK) {
-                                final float deltaX = eventX - mDownPositionX;
-                                final float deltaY = eventY - mDownPositionY;
-                                final float distanceSquared = deltaX * deltaX + deltaY * deltaY;
-
-                                ViewConfiguration viewConfiguration = ViewConfiguration.get(
-                                        mTextView.getContext());
-                                int doubleTapSlop = viewConfiguration.getScaledDoubleTapSlop();
-                                boolean stayedInArea =
-                                        distanceSquared < doubleTapSlop * doubleTapSlop;
-
-                                if (stayedInArea && (isMouse || isPositionOnText(eventX, eventY))) {
-                                    if (TextView.DEBUG_CURSOR) {
-                                        logCursor("SelectionModifierCursorController: onTouchEvent",
-                                                "ACTION_DOWN: select and start drag");
-                                    }
-                                    if (mTapState == TAP_STATE_DOUBLE_TAP) {
-                                        selectCurrentWordAndStartDrag();
-                                    } else if (mTapState == TAP_STATE_TRIPLE_CLICK) {
-                                        selectCurrentParagraphAndStartDrag();
-                                    }
-                                    mDiscardNextActionUp = true;
-                                }
+                        if (mGestureStayedInTapRegion
+                                && mTouchState.isMultiTapInSameArea()
+                                && (isMouse || isPositionOnText(eventX, eventY))) {
+                            if (TextView.DEBUG_CURSOR) {
+                                logCursor("SelectionModifierCursorController: onTouchEvent",
+                                        "ACTION_DOWN: select and start drag");
                             }
+                            if (mTouchState.isDoubleTap()) {
+                                selectCurrentWordAndStartDrag();
+                            } else if (mTouchState.isTripleClick()) {
+                                selectCurrentParagraphAndStartDrag();
+                            }
+                            mDiscardNextActionUp = true;
                         }
-
-                        mDownPositionX = eventX;
-                        mDownPositionY = eventY;
                         mGestureStayedInTapRegion = true;
                         mHaventMovedEnoughToStartDrag = true;
                     }
@@ -6025,8 +5962,8 @@
                     final int touchSlop = viewConfig.getScaledTouchSlop();
 
                     if (mGestureStayedInTapRegion || mHaventMovedEnoughToStartDrag) {
-                        final float deltaX = eventX - mDownPositionX;
-                        final float deltaY = eventY - mDownPositionY;
+                        final float deltaX = eventX - mTouchState.getLastDownX();
+                        final float deltaY = eventY - mTouchState.getLastDownY();
                         final float distanceSquared = deltaX * deltaX + deltaY * deltaY;
 
                         if (mGestureStayedInTapRegion) {
@@ -7164,7 +7101,7 @@
         }
     }
 
-    private static void logCursor(String location, @Nullable String msgFormat, Object ... msgArgs) {
+    static void logCursor(String location, @Nullable String msgFormat, Object ... msgArgs) {
         if (msgFormat == null) {
             Log.d(TAG, location);
         } else {
diff --git a/core/java/android/widget/EditorTouchState.java b/core/java/android/widget/EditorTouchState.java
new file mode 100644
index 0000000..f880939
--- /dev/null
+++ b/core/java/android/widget/EditorTouchState.java
@@ -0,0 +1,137 @@
+/*
+ * 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.widget;
+
+import static android.widget.Editor.logCursor;
+
+import static com.android.internal.annotations.VisibleForTesting.Visibility.PACKAGE;
+
+import android.annotation.IntDef;
+import android.view.InputDevice;
+import android.view.MotionEvent;
+import android.view.ViewConfiguration;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Helper class used by {@link Editor} to track state for touch events.
+ *
+ * @hide
+ */
+@VisibleForTesting(visibility = PACKAGE)
+public class EditorTouchState {
+    private float mLastDownX, mLastDownY;
+    private float mLastUpX, mLastUpY;
+    private long mLastUpMillis;
+
+    @IntDef({MultiTapStatus.NONE, MultiTapStatus.FIRST_TAP, MultiTapStatus.DOUBLE_TAP,
+            MultiTapStatus.TRIPLE_CLICK})
+    @Retention(RetentionPolicy.SOURCE)
+    @VisibleForTesting
+    public @interface MultiTapStatus {
+        int NONE = 0;
+        int FIRST_TAP = 1;
+        int DOUBLE_TAP = 2;
+        int TRIPLE_CLICK = 3; // Only for mouse input.
+    }
+    @MultiTapStatus
+    private int mMultiTapStatus = MultiTapStatus.NONE;
+    private boolean mMultiTapInSameArea;
+
+    public float getLastDownX() {
+        return mLastDownX;
+    }
+
+    public float getLastDownY() {
+        return mLastDownY;
+    }
+
+    public float getLastUpX() {
+        return mLastUpX;
+    }
+
+    public float getLastUpY() {
+        return mLastUpY;
+    }
+
+    public boolean isDoubleTap() {
+        return mMultiTapStatus == MultiTapStatus.DOUBLE_TAP;
+    }
+
+    public boolean isTripleClick() {
+        return mMultiTapStatus == MultiTapStatus.TRIPLE_CLICK;
+    }
+
+    public boolean isMultiTap() {
+        return mMultiTapStatus == MultiTapStatus.DOUBLE_TAP
+                || mMultiTapStatus == MultiTapStatus.TRIPLE_CLICK;
+    }
+
+    public boolean isMultiTapInSameArea() {
+        return isMultiTap() && mMultiTapInSameArea;
+    }
+
+    /**
+     * Updates the state based on the new event.
+     */
+    public void update(MotionEvent event, ViewConfiguration viewConfiguration) {
+        final int action = event.getActionMasked();
+        if (action == MotionEvent.ACTION_DOWN) {
+            final boolean isMouse = event.isFromSource(InputDevice.SOURCE_MOUSE);
+            final long millisSinceLastUp = event.getEventTime() - mLastUpMillis;
+            // Detect double tap and triple click.
+            if (millisSinceLastUp <= ViewConfiguration.getDoubleTapTimeout()
+                    && (mMultiTapStatus == MultiTapStatus.FIRST_TAP
+                    || (mMultiTapStatus == MultiTapStatus.DOUBLE_TAP && isMouse))) {
+                if (mMultiTapStatus == MultiTapStatus.FIRST_TAP) {
+                    mMultiTapStatus = MultiTapStatus.DOUBLE_TAP;
+                } else {
+                    mMultiTapStatus = MultiTapStatus.TRIPLE_CLICK;
+                }
+                final float deltaX = event.getX() - mLastDownX;
+                final float deltaY = event.getY() - mLastDownY;
+                final int distanceSquared = (int) ((deltaX * deltaX) + (deltaY * deltaY));
+                int doubleTapSlop = viewConfiguration.getScaledDoubleTapSlop();
+                mMultiTapInSameArea = distanceSquared < doubleTapSlop * doubleTapSlop;
+                if (TextView.DEBUG_CURSOR) {
+                    String status = isDoubleTap() ? "double" : "triple";
+                    String inSameArea = mMultiTapInSameArea ? "in same area" : "not in same area";
+                    logCursor("EditorTouchState", "ACTION_DOWN: %s tap detected, %s",
+                            status, inSameArea);
+                }
+            } else {
+                mMultiTapStatus = MultiTapStatus.FIRST_TAP;
+                mMultiTapInSameArea = false;
+                if (TextView.DEBUG_CURSOR) {
+                    logCursor("EditorTouchState", "ACTION_DOWN: first tap detected");
+                }
+            }
+            mLastDownX = event.getX();
+            mLastDownY = event.getY();
+        } else if (action == MotionEvent.ACTION_UP) {
+            if (TextView.DEBUG_CURSOR) {
+                logCursor("EditorTouchState", "ACTION_UP");
+            }
+            mLastUpX = event.getX();
+            mLastUpY = event.getY();
+            mLastUpMillis = event.getEventTime();
+        }
+    }
+}
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 90e8ef2..ee169f2 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -10860,7 +10860,10 @@
     @Override
     public boolean onTouchEvent(MotionEvent event) {
         if (DEBUG_CURSOR) {
-            logCursor("onTouchEvent", MotionEvent.actionToString(event.getActionMasked()));
+            logCursor("onTouchEvent", "%d: %s (%f,%f)",
+                    event.getSequenceNumber(),
+                    MotionEvent.actionToString(event.getActionMasked()),
+                    event.getX(), event.getY());
         }
 
         final int action = event.getActionMasked();
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index 15b1d75..2f1a15f 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -19,10 +19,10 @@
 import com.android.internal.os.BatteryStatsImpl;
 
 import android.bluetooth.BluetoothActivityEnergyInfo;
-import android.net.wifi.WifiActivityEnergyInfo;
 import android.os.ParcelFileDescriptor;
 import android.os.WorkSource;
 import android.os.connectivity.CellularBatteryStats;
+import android.os.connectivity.WifiActivityEnergyInfo;
 import android.os.connectivity.WifiBatteryStats;
 import android.os.connectivity.GpsBatteryStats;
 import android.os.health.HealthStatsParceler;
diff --git a/core/java/com/android/internal/compat/CompatibilityChangeInfo.java b/core/java/com/android/internal/compat/CompatibilityChangeInfo.java
index e48e2df..16628d7 100644
--- a/core/java/com/android/internal/compat/CompatibilityChangeInfo.java
+++ b/core/java/com/android/internal/compat/CompatibilityChangeInfo.java
@@ -30,6 +30,7 @@
     private final @Nullable String mName;
     private final int mEnableAfterTargetSdk;
     private final boolean mDisabled;
+    private final @Nullable String mDescription;
 
     public long getId() {
         return mChangeId;
@@ -48,12 +49,18 @@
         return mDisabled;
     }
 
+    public String getDescription()  {
+        return mDescription;
+    }
+
     public CompatibilityChangeInfo(
-            Long changeId, String name, int enableAfterTargetSdk, boolean disabled) {
+            Long changeId, String name, int enableAfterTargetSdk, boolean disabled,
+            String description) {
         this.mChangeId = changeId;
         this.mName = name;
         this.mEnableAfterTargetSdk = enableAfterTargetSdk;
         this.mDisabled = disabled;
+        this.mDescription = description;
     }
 
     private CompatibilityChangeInfo(Parcel in) {
@@ -61,6 +68,7 @@
         mName = in.readString();
         mEnableAfterTargetSdk = in.readInt();
         mDisabled = in.readBoolean();
+        mDescription = in.readString();
     }
 
     @Override
@@ -74,6 +82,7 @@
         dest.writeString(mName);
         dest.writeInt(mEnableAfterTargetSdk);
         dest.writeBoolean(mDisabled);
+        dest.writeString(mDescription);
     }
 
     public static final Parcelable.Creator<CompatibilityChangeInfo> CREATOR =
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 25fa65e..bc44fcf 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -33,7 +33,6 @@
 import android.net.INetworkStatsService;
 import android.net.NetworkStats;
 import android.net.Uri;
-import android.net.wifi.WifiActivityEnergyInfo;
 import android.net.wifi.WifiManager;
 import android.os.BatteryManager;
 import android.os.BatteryStats;
@@ -56,6 +55,7 @@
 import android.os.WorkSource.WorkChain;
 import android.os.connectivity.CellularBatteryStats;
 import android.os.connectivity.GpsBatteryStats;
+import android.os.connectivity.WifiActivityEnergyInfo;
 import android.os.connectivity.WifiBatteryStats;
 import android.provider.Settings;
 import android.telephony.CellSignalStrength;
diff --git a/core/java/com/android/internal/telephony/IPhoneStateListener.aidl b/core/java/com/android/internal/telephony/IPhoneStateListener.aidl
index 084a3cc..01f5743 100644
--- a/core/java/com/android/internal/telephony/IPhoneStateListener.aidl
+++ b/core/java/com/android/internal/telephony/IPhoneStateListener.aidl
@@ -21,7 +21,6 @@
 import android.telephony.CellInfo;
 import android.telephony.DataConnectionRealTimeInfo;
 import android.telephony.PhoneCapability;
-import android.telephony.PhysicalChannelConfig;
 import android.telephony.PreciseCallState;
 import android.telephony.PreciseDataConnectionState;
 import android.telephony.ServiceState;
@@ -44,7 +43,6 @@
     void onDataConnectionStateChanged(int state, int networkType);
     void onDataActivity(int direction);
     void onSignalStrengthsChanged(in SignalStrength signalStrength);
-    void onPhysicalChannelConfigurationChanged(in List<PhysicalChannelConfig> configs);
     void onOtaspChanged(in int otaspMode);
     void onCellInfoChanged(in List<CellInfo> cellInfo);
     void onPreciseCallStateChanged(in PreciseCallState callState);
diff --git a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index 9ae0ba5..2f34aa0 100644
--- a/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/core/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -24,7 +24,6 @@
 import android.telephony.CellInfo;
 import android.telephony.ims.ImsReasonInfo;
 import android.telephony.PhoneCapability;
-import android.telephony.PhysicalChannelConfig;
 import android.telephony.ServiceState;
 import android.telephony.SignalStrength;
 import android.telephony.emergency.EmergencyNumber;
@@ -78,8 +77,6 @@
     void notifyOtaspChanged(in int subId, in int otaspMode);
     @UnsupportedAppUsage
     void notifyCellInfo(in List<CellInfo> cellInfo);
-    void notifyPhysicalChannelConfigurationForSubscriber(in int phoneId, in int subId,
-            in List<PhysicalChannelConfig> configs);
     void notifyPreciseCallState(int phoneId, int subId, int ringingCallState,
             int foregroundCallState, int backgroundCallState);
     void notifyDisconnectCause(int phoneId, int subId, int disconnectCause,
diff --git a/core/java/com/android/internal/util/AsyncChannel.java b/core/java/com/android/internal/util/AsyncChannel.java
index b0888f2..121ae1a 100644
--- a/core/java/com/android/internal/util/AsyncChannel.java
+++ b/core/java/com/android/internal/util/AsyncChannel.java
@@ -29,7 +29,7 @@
 import android.os.Message;
 import android.os.Messenger;
 import android.os.RemoteException;
-import android.util.Slog;
+import android.util.Log;
 
 import java.util.Stack;
 
@@ -841,7 +841,7 @@
                     msg.replyTo = sm.mMessenger;
                     synchronized (sm.mHandler.mLockObject) {
                         if (sm.mHandler.mResultMsg != null) {
-                            Slog.wtf(TAG, "mResultMsg should be null here");
+                            Log.wtf(TAG, "mResultMsg should be null here");
                             sm.mHandler.mResultMsg = null;
                         }
                         dstMessenger.send(msg);
@@ -851,9 +851,9 @@
                     }
                 }
             } catch (InterruptedException e) {
-                Slog.e(TAG, "error in sendMessageSynchronously", e);
+                Log.e(TAG, "error in sendMessageSynchronously", e);
             } catch (RemoteException e) {
-                Slog.e(TAG, "error in sendMessageSynchronously", e);
+                Log.e(TAG, "error in sendMessageSynchronously", e);
             }
             sm.recycle();
             return resultMsg;
@@ -939,7 +939,7 @@
      * @param s
      */
     private static void log(String s) {
-        Slog.d(TAG, s);
+        Log.d(TAG, s);
     }
 
     private final class DeathMonitor implements IBinder.DeathRecipient {
diff --git a/core/java/com/android/internal/util/AsyncService.java b/core/java/com/android/internal/util/AsyncService.java
index e39a2bf..58e4a60 100644
--- a/core/java/com/android/internal/util/AsyncService.java
+++ b/core/java/com/android/internal/util/AsyncService.java
@@ -22,7 +22,7 @@
 import android.os.IBinder;
 import android.os.Message;
 import android.os.Messenger;
-import android.util.Slog;
+import android.util.Log;
 
 /**
  * A service that receives Intents and IBinder transactions
@@ -92,7 +92,7 @@
      */
     @Override
     public int onStartCommand(Intent intent, int flags, int startId) {
-        if (DBG) Slog.d(TAG, "onStartCommand");
+        if (DBG) Log.d(TAG, "onStartCommand");
 
         Message msg = mHandler.obtainMessage();
         msg.what = CMD_ASYNC_SERVICE_ON_START_INTENT;
@@ -111,7 +111,7 @@
      */
     @Override
     public void onDestroy() {
-        if (DBG) Slog.d(TAG, "onDestroy");
+        if (DBG) Log.d(TAG, "onDestroy");
 
         Message msg = mHandler.obtainMessage();
         msg.what = CMD_ASYNC_SERVICE_DESTROY;
diff --git a/core/java/com/android/internal/view/BaseIWindow.java b/core/java/com/android/internal/view/BaseIWindow.java
index 57bfcac..2ac2975 100644
--- a/core/java/com/android/internal/view/BaseIWindow.java
+++ b/core/java/com/android/internal/view/BaseIWindow.java
@@ -36,6 +36,8 @@
 
 import dalvik.annotation.compat.UnsupportedAppUsage;
 
+import java.io.IOException;
+
 public class BaseIWindow extends IWindow.Stub {
 
     @UnsupportedAppUsage(maxTargetSdk = android.os.Build.VERSION_CODES.P)
@@ -101,6 +103,13 @@
 
     @Override
     public void executeCommand(String command, String parameters, ParcelFileDescriptor out) {
+        if (out != null) {
+            try {
+                out.closeWithError("Unsupported command " + command);
+            } catch (IOException e) {
+                // Ignore
+            }
+        }
     }
 
     @Override
diff --git a/core/proto/android/server/activitymanagerservice.proto b/core/proto/android/server/activitymanagerservice.proto
index ce2717b..9f31b59 100644
--- a/core/proto/android/server/activitymanagerservice.proto
+++ b/core/proto/android/server/activitymanagerservice.proto
@@ -77,12 +77,14 @@
 message ActivityDisplayProto {
     option (.android.msg_privacy).dest = DEST_AUTOMATIC;
 
-    optional .com.android.server.wm.ConfigurationContainerProto configuration_container = 1;
+    // To be removed soon.
+    optional .com.android.server.wm.ConfigurationContainerProto configuration_container = 1 [deprecated=true];
     optional int32 id = 2;
     repeated ActivityStackProto stacks = 3;
     optional int32 focused_stack_id = 4;
     optional .com.android.server.wm.IdentifierProto resumed_activity = 5;
     optional bool single_task_instance = 6;
+    optional .com.android.server.wm.DisplayContentProto display = 7;
 }
 
 message ActivityStackProto {
diff --git a/core/proto/android/stats/mediaprovider/mediaprovider_enums.proto b/core/proto/android/stats/mediaprovider/mediaprovider_enums.proto
new file mode 100644
index 0000000..138782b
--- /dev/null
+++ b/core/proto/android/stats/mediaprovider/mediaprovider_enums.proto
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+syntax = "proto2";
+package android.stats.mediaprovider;
+option java_multiple_files = true;
+
+enum VolumeType {
+    // Volume is unknown
+    UNKNOWN = 0;
+    // Volume is MediaStore.VOLUME_INTERNAL
+    INTERNAL = 1;
+    // Volume is MediaStore.VOLUME_EXTERNAL_PRIMARY
+    EXTERNAL_PRIMARY = 2;
+    // Volume is non-primary external storage
+    EXTERNAL_OTHER = 3;
+}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 9dc2319..f6e91ef 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -5070,6 +5070,10 @@
                  android:permission="android.permission.BIND_JOB_SERVICE" >
         </service>
 
+        <service android:name="com.android.server.usage.UsageStatsIdleService"
+                 android:permission="android.permission.BIND_JOB_SERVICE" >
+        </service>
+
         <service android:name="com.android.server.net.watchlist.ReportWatchlistJobService"
                  android:permission="android.permission.BIND_JOB_SERVICE" >
         </service>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 234ffee..38d3e9c 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3750,6 +3750,14 @@
              </p>
          -->
         <attr name="canRequestFingerprintGestures" format="boolean" />
+
+        <!-- Animated image of the accessibility service purpose or behavior, to help users
+             understand how the service can help them.-->
+        <attr name="animatedImageDrawable" format="reference"/>
+        <!-- Html description of the accessibility service, to help users understand
+             how the service can help them.-->
+        <attr name="htmlDescription" format="string"/>
+
         <!-- Short description of the accessibility service purpose or behavior.-->
         <attr name="description" />
         <!-- Brief summary of the accessibility service purpose or behavior. -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index fc9fa85..10a7584 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3695,6 +3695,8 @@
          instead of 'Emergency calls only' when SIM is unready. -->
     <string-array translatable="false" name="config_display_no_service_when_sim_unready">
         <item>"DE"</item>
+        <item>"GB"</item>
+        <item>"JP"</item>
     </string-array>
 
     <!-- Class names of device specific services inheriting com.android.server.SystemService. The
@@ -4179,4 +4181,10 @@
     <string-array name="config_integrityRuleProviderPackages" translatable="false">
         <!-- Add packages here -->
     </string-array>
+
+    <!-- When true, enables the whitelisted app to handle bug reports from power menu short press. -->
+    <bool name="config_bugReportHandlerEnabled">false</bool>
+
+    <!-- The package name for the default bug report handler app from power menu short press. This app must be whitelisted. -->
+    <string name="config_defaultBugReportHandlerApp" translatable="false"></string>
 </resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 57e0e1b..78c4efe 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3002,6 +3002,8 @@
       <public name="forceQueryable" />
       <!-- @hide @SystemApi -->
       <public name="resourcesMap" />
+      <public name="animatedImageDrawable"/>
+      <public name="htmlDescription"/>
     </public-group>
 
     <public-group type="drawable" first-id="0x010800b5">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index dcf7379..0f5da39 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1014,23 +1014,29 @@
     <string name="permdesc_readContacts" product="tablet">Allows the app to read
       data about your contacts stored on your tablet, including the frequency
       with which you\'ve called, emailed, or communicated in other ways with
-      specific individuals. This permission allows apps to save your contact
+      specific individuals. Apps will also have access to the accounts on your
+      tablet that have created contacts. This may include accounts created by
+      apps you have installed. This permission allows apps to save your contact
       data, and malicious apps may share contact data without your
       knowledge.</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permdesc_readContacts" product="tv">Allows the app to read
       data about your contacts stored on your Android TV device, including the frequency
       with which you\'ve called, emailed, or communicated in other ways with
-      specific individuals. This permission allows apps to save your contact
-      data, and malicious apps may share contact data without your
+      specific individuals. Apps will also have access to the accounts on your
+      Android TV device that have created contacts. This may include accounts
+      created by apps you have installed. This permission allows apps to save
+      your contact data, and malicious apps may share contact data without your
       knowledge.</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permdesc_readContacts" product="default">Allows the app to
       read data about your contacts stored on your phone, including the
       frequency with which you\'ve called, emailed, or communicated in other ways
-      with specific individuals. This permission allows apps to save your
-      contact data, and malicious apps may share contact data without your
-      knowledge.</string>
+      with specific individuals. Apps will also have access to the accounts
+      on your phone that have created contacts. This may include accounts
+      created by apps you have installed. This permission allows apps to
+      save your contact data, and malicious apps may share contact data
+      without your knowledge.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_writeContacts">modify your contacts</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index c6c9094..083f33c 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3751,6 +3751,10 @@
 
   <java-symbol type="bool" name="config_showBuiltinWirelessChargingAnim" />
 
+  <!-- For bug report handler -->
+  <java-symbol type="bool" name="config_bugReportHandlerEnabled" />
+  <java-symbol type="string" name="config_defaultBugReportHandlerApp" />
+
   <java-symbol type="string" name="usb_device_resolve_prompt_warn" />
 
   <!-- For Accessibility system actions -->
diff --git a/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/ResourceLoaderChangesTest.kt b/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/ResourceLoaderChangesTest.kt
index e01e254..0c3d34e 100644
--- a/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/ResourceLoaderChangesTest.kt
+++ b/core/tests/ResourceLoaderTests/src/android/content/res/loader/test/ResourceLoaderChangesTest.kt
@@ -31,7 +31,6 @@
 import androidx.test.runner.lifecycle.Stage
 import com.google.common.truth.Truth.assertThat
 import org.junit.After
-import org.junit.Assume
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
@@ -43,7 +42,6 @@
 import java.util.concurrent.FutureTask
 import java.util.concurrent.TimeUnit
 
-// @Ignore("UiAutomation is crashing with not connected, not sure why")
 @RunWith(Parameterized::class)
 class ResourceLoaderChangesTest : ResourceLoaderTestBase() {
 
@@ -75,7 +73,7 @@
     @Before
     @After
     fun disableOverlay() {
-//        enableOverlay(OVERLAY_PACKAGE, false)
+        enableOverlay(OVERLAY_PACKAGE, false)
     }
 
     @Test
@@ -156,25 +154,32 @@
 
     // All these tests assert for the exact same loaders/values, so extract that logic out
     private fun verifySameBeforeAndAfter(block: () -> Resources) {
-        // TODO(chiuwinson): atest doesn't work with @Ignore, UiAutomation not connected error
-        Assume.assumeFalse(true)
+        fun Resources.resource() = this.getString(android.R.string.cancel)
+        fun Resources.asset() = this.assets.open("Asset.txt").reader().readText()
 
-        val originalValue = resources.getString(android.R.string.cancel)
+        val originalResource = resources.resource()
+        val originalAsset = resources.asset()
 
-        val loader = "stringOne".openLoader()
-        addLoader(loader)
+        val loaderResource = "stringOne".openLoader()
+        val loaderAsset = "assetOne".openLoader(dataType = DataType.ASSET)
+        addLoader(loaderResource)
+        addLoader(loaderAsset)
 
         val oldLoaders = resources.loaders
-        val oldValue = resources.getString(android.R.string.cancel)
+        val oldResource = resources.resource()
+        val oldAsset = resources.asset()
 
-        assertThat(oldValue).isNotEqualTo(originalValue)
+        assertThat(oldResource).isNotEqualTo(originalResource)
+        assertThat(oldAsset).isNotEqualTo(originalAsset)
 
         val newResources = block()
 
         val newLoaders = newResources.loaders
-        val newValue = newResources.getString(android.R.string.cancel)
+        val newResource = newResources.resource()
+        val newAsset = newResources.asset()
 
-        assertThat(newValue).isEqualTo(oldValue)
+        assertThat(newResource).isEqualTo(oldResource)
+        assertThat(newAsset).isEqualTo(oldAsset)
         assertThat(newLoaders).isEqualTo(oldLoaders)
     }
 
diff --git a/core/tests/coretests/src/android/app/timedetector/ManualTimeSuggestionTest.java b/core/tests/coretests/src/android/app/timedetector/ManualTimeSuggestionTest.java
new file mode 100644
index 0000000..de6f8f7
--- /dev/null
+++ b/core/tests/coretests/src/android/app/timedetector/ManualTimeSuggestionTest.java
@@ -0,0 +1,66 @@
+/*
+ * 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.app.timedetector;
+
+import static android.app.timezonedetector.ParcelableTestSupport.assertRoundTripParcelable;
+import static android.app.timezonedetector.ParcelableTestSupport.roundTripParcelable;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+
+import android.util.TimestampedValue;
+
+import org.junit.Test;
+
+public class ManualTimeSuggestionTest {
+
+    private static final TimestampedValue<Long> ARBITRARY_TIME =
+            new TimestampedValue<>(1111L, 2222L);
+
+    @Test
+    public void testEquals() {
+        ManualTimeSuggestion one = new ManualTimeSuggestion(ARBITRARY_TIME);
+        assertEquals(one, one);
+
+        ManualTimeSuggestion two = new ManualTimeSuggestion(ARBITRARY_TIME);
+        assertEquals(one, two);
+        assertEquals(two, one);
+
+        TimestampedValue<Long> differentTime = new TimestampedValue<>(
+                ARBITRARY_TIME.getReferenceTimeMillis() + 1,
+                ARBITRARY_TIME.getValue());
+        ManualTimeSuggestion three = new ManualTimeSuggestion(differentTime);
+        assertNotEquals(one, three);
+        assertNotEquals(three, one);
+
+        // DebugInfo must not be considered in equals().
+        one.addDebugInfo("Debug info 1");
+        two.addDebugInfo("Debug info 2");
+        assertEquals(one, two);
+    }
+
+    @Test
+    public void testParcelable() {
+        ManualTimeSuggestion suggestion = new ManualTimeSuggestion(ARBITRARY_TIME);
+        assertRoundTripParcelable(suggestion);
+
+        // DebugInfo should also be stored (but is not checked by equals()
+        suggestion.addDebugInfo("This is debug info");
+        ManualTimeSuggestion rtSuggestion = roundTripParcelable(suggestion);
+        assertEquals(suggestion.getDebugInfo(), rtSuggestion.getDebugInfo());
+    }
+}
diff --git a/core/tests/coretests/src/android/app/timedetector/PhoneTimeSuggestionTest.java b/core/tests/coretests/src/android/app/timedetector/PhoneTimeSuggestionTest.java
index c9a86dc..bee270e 100644
--- a/core/tests/coretests/src/android/app/timedetector/PhoneTimeSuggestionTest.java
+++ b/core/tests/coretests/src/android/app/timedetector/PhoneTimeSuggestionTest.java
@@ -16,11 +16,12 @@
 
 package android.app.timedetector;
 
+import static android.app.timezonedetector.ParcelableTestSupport.assertRoundTripParcelable;
+import static android.app.timezonedetector.ParcelableTestSupport.roundTripParcelable;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
 
-import android.os.Parcel;
-import android.os.Parcelable;
 import android.util.TimestampedValue;
 
 import org.junit.Test;
@@ -30,53 +31,67 @@
 
     @Test
     public void testEquals() {
-        PhoneTimeSuggestion one = new PhoneTimeSuggestion(PHONE_ID);
-        assertEquals(one, one);
+        PhoneTimeSuggestion.Builder builder1 = new PhoneTimeSuggestion.Builder(PHONE_ID);
+        {
+            PhoneTimeSuggestion one = builder1.build();
+            assertEquals(one, one);
+        }
 
-        PhoneTimeSuggestion two = new PhoneTimeSuggestion(PHONE_ID);
-        assertEquals(one, two);
-        assertEquals(two, one);
+        PhoneTimeSuggestion.Builder builder2 = new PhoneTimeSuggestion.Builder(PHONE_ID);
+        {
+            PhoneTimeSuggestion one = builder1.build();
+            PhoneTimeSuggestion two = builder2.build();
+            assertEquals(one, two);
+            assertEquals(two, one);
+        }
 
-        one.setUtcTime(new TimestampedValue<>(1111L, 2222L));
-        assertEquals(one, one);
+        builder1.setUtcTime(new TimestampedValue<>(1111L, 2222L));
+        {
+            PhoneTimeSuggestion one = builder1.build();
+            assertEquals(one, one);
+        }
 
-        two.setUtcTime(new TimestampedValue<>(1111L, 2222L));
-        assertEquals(one, two);
-        assertEquals(two, one);
+        builder2.setUtcTime(new TimestampedValue<>(1111L, 2222L));
+        {
+            PhoneTimeSuggestion one = builder1.build();
+            PhoneTimeSuggestion two = builder2.build();
+            assertEquals(one, two);
+            assertEquals(two, one);
+        }
 
-        PhoneTimeSuggestion three = new PhoneTimeSuggestion(PHONE_ID + 1);
-        three.setUtcTime(new TimestampedValue<>(1111L, 2222L));
-        assertNotEquals(one, three);
-        assertNotEquals(three, one);
+        PhoneTimeSuggestion.Builder builder3 = new PhoneTimeSuggestion.Builder(PHONE_ID + 1);
+        builder3.setUtcTime(new TimestampedValue<>(1111L, 2222L));
+        {
+            PhoneTimeSuggestion one = builder1.build();
+            PhoneTimeSuggestion three = builder3.build();
+            assertNotEquals(one, three);
+            assertNotEquals(three, one);
+        }
 
         // DebugInfo must not be considered in equals().
-        one.addDebugInfo("Debug info 1");
-        two.addDebugInfo("Debug info 2");
-        assertEquals(one, two);
+        builder1.addDebugInfo("Debug info 1");
+        builder2.addDebugInfo("Debug info 2");
+        {
+            PhoneTimeSuggestion one = builder1.build();
+            PhoneTimeSuggestion two = builder2.build();
+            assertEquals(one, two);
+        }
     }
 
     @Test
     public void testParcelable() {
-        PhoneTimeSuggestion one = new PhoneTimeSuggestion(PHONE_ID);
-        assertEquals(one, roundTripParcelable(one));
+        PhoneTimeSuggestion.Builder builder = new PhoneTimeSuggestion.Builder(PHONE_ID);
+        assertRoundTripParcelable(builder.build());
 
-        one.setUtcTime(new TimestampedValue<>(1111L, 2222L));
-        assertEquals(one, roundTripParcelable(one));
+        builder.setUtcTime(new TimestampedValue<>(1111L, 2222L));
+        assertRoundTripParcelable(builder.build());
 
         // DebugInfo should also be stored (but is not checked by equals()
-        one.addDebugInfo("This is debug info");
-        PhoneTimeSuggestion two = roundTripParcelable(one);
-        assertEquals(one.getDebugInfo(), two.getDebugInfo());
-    }
-
-    @SuppressWarnings("unchecked")
-    private static <T extends Parcelable> T roundTripParcelable(T one) {
-        Parcel parcel = Parcel.obtain();
-        parcel.writeTypedObject(one, 0);
-        parcel.setDataPosition(0);
-
-        T toReturn = (T) parcel.readTypedObject(PhoneTimeSuggestion.CREATOR);
-        parcel.recycle();
-        return toReturn;
+        {
+            PhoneTimeSuggestion suggestion1 = builder.build();
+            builder.addDebugInfo("This is debug info");
+            PhoneTimeSuggestion rtSuggestion1 = roundTripParcelable(suggestion1);
+            assertEquals(suggestion1.getDebugInfo(), rtSuggestion1.getDebugInfo());
+        }
     }
 }
diff --git a/core/tests/coretests/src/android/service/controls/ControlActionTest.java b/core/tests/coretests/src/android/service/controls/ControlActionTest.java
new file mode 100644
index 0000000..ef4912f
--- /dev/null
+++ b/core/tests/coretests/src/android/service/controls/ControlActionTest.java
@@ -0,0 +1,69 @@
+/*
+ * 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.controls;
+
+import static junit.framework.Assert.assertTrue;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import android.os.Parcel;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ControlActionTest {
+
+    private static final String TEST_ID = "TEST_ID";
+
+    @Test
+    public void testUnparcelingCorrectClass_boolean() {
+        ControlAction toParcel = new BooleanAction(TEST_ID, true);
+
+        ControlAction fromParcel = parcelAndUnparcel(toParcel);
+
+        assertEquals(ControlAction.TYPE_BOOLEAN, fromParcel.getActionType());
+        assertTrue(fromParcel instanceof BooleanAction);
+    }
+
+    @Test
+    public void testUnparcelingCorrectClass_float() {
+        ControlAction toParcel = new FloatAction(TEST_ID, 1);
+
+        ControlAction fromParcel = parcelAndUnparcel(toParcel);
+
+        assertEquals(ControlAction.TYPE_FLOAT, fromParcel.getActionType());
+        assertTrue(fromParcel instanceof FloatAction);
+    }
+
+    private ControlAction parcelAndUnparcel(ControlAction toParcel) {
+        Parcel parcel = Parcel.obtain();
+
+        assertNotNull(parcel);
+
+        parcel.setDataPosition(0);
+        toParcel.writeToParcel(parcel, 0);
+        parcel.setDataPosition(0);
+
+        return ControlAction.CREATOR.createFromParcel(parcel);
+    }
+}
diff --git a/core/tests/coretests/src/android/service/controls/ControlTemplateTest.java b/core/tests/coretests/src/android/service/controls/ControlTemplateTest.java
new file mode 100644
index 0000000..4fa4e1d
--- /dev/null
+++ b/core/tests/coretests/src/android/service/controls/ControlTemplateTest.java
@@ -0,0 +1,139 @@
+/*
+ * 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.controls;
+
+import static junit.framework.Assert.assertTrue;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import android.annotation.DrawableRes;
+import android.graphics.drawable.Icon;
+import android.os.Parcel;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.frameworks.coretests.R;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+
+import java.security.InvalidParameterException;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class ControlTemplateTest {
+
+    private static final String PACKAGE_NAME = "com.android.frameworks.coretests";
+    private static final @DrawableRes int TEST_ICON_ID = R.drawable.box;
+    private static final String TEST_ID = "TEST_ID";
+    private static final CharSequence TEST_CONTENT_DESCRIPTION = "TEST_CONTENT_DESCRIPTION";
+    private Icon mIcon;
+    private ControlButton mControlButton;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        mIcon = Icon.createWithResource(PACKAGE_NAME, TEST_ICON_ID);
+        mControlButton = new ControlButton(true, mIcon, TEST_CONTENT_DESCRIPTION);
+    }
+
+    @Test
+    public void testUnparcelingCorrectClass_none() {
+        ControlTemplate toParcel = ControlTemplate.NO_TEMPLATE;
+
+        ControlTemplate fromParcel = parcelAndUnparcel(toParcel);
+
+        assertEquals(ControlTemplate.NO_TEMPLATE, fromParcel);
+    }
+
+    @Test
+    public void testUnparcelingCorrectClass_toggle() {
+        ControlTemplate toParcel = new ToggleTemplate(TEST_ID, mControlButton);
+
+        ControlTemplate fromParcel = parcelAndUnparcel(toParcel);
+
+        assertEquals(ControlTemplate.TYPE_TOGGLE, fromParcel.getTemplateType());
+        assertTrue(fromParcel instanceof ToggleTemplate);
+    }
+
+    @Test
+    public void testUnparcelingCorrectClass_range() {
+        ControlTemplate toParcel = new RangeTemplate(TEST_ID, 0, 2, 1, 1, "%f");
+
+        ControlTemplate fromParcel = parcelAndUnparcel(toParcel);
+
+        assertEquals(ControlTemplate.TYPE_RANGE, fromParcel.getTemplateType());
+        assertTrue(fromParcel instanceof RangeTemplate);
+    }
+
+    @Test(expected = InvalidParameterException.class)
+    public void testRangeParameters_minMax() {
+        RangeTemplate range = new RangeTemplate(TEST_ID, 2, 0, 1, 1, "%f");
+    }
+
+    @Test(expected = InvalidParameterException.class)
+    public void testRangeParameters_minCurrent() {
+        RangeTemplate range = new RangeTemplate(TEST_ID, 0, 2, -1, 1, "%f");
+    }
+
+    @Test(expected = InvalidParameterException.class)
+    public void testRangeParameters_maxCurrent() {
+        RangeTemplate range = new RangeTemplate(TEST_ID, 0, 2, 3, 1, "%f");
+    }
+
+    @Test(expected = InvalidParameterException.class)
+    public void testRangeParameters_negativeStep() {
+        RangeTemplate range = new RangeTemplate(TEST_ID, 0, 2, 1, -1, "%f");
+    }
+
+    @Test
+    public void testUnparcelingCorrectClass_thumbnail() {
+        ControlTemplate toParcel = new ThumbnailTemplate(TEST_ID, mIcon, TEST_CONTENT_DESCRIPTION);
+
+        ControlTemplate fromParcel = parcelAndUnparcel(toParcel);
+
+        assertEquals(ControlTemplate.TYPE_THUMBNAIL, fromParcel.getTemplateType());
+        assertTrue(fromParcel instanceof ThumbnailTemplate);
+    }
+
+    @Test
+    public void testUnparcelingCorrectClass_discreteToggle() {
+        ControlTemplate toParcel =
+                new DiscreteToggleTemplate(TEST_ID, mControlButton, mControlButton);
+
+        ControlTemplate fromParcel = parcelAndUnparcel(toParcel);
+
+        assertEquals(ControlTemplate.TYPE_DISCRETE_TOGGLE, fromParcel.getTemplateType());
+        assertTrue(fromParcel instanceof DiscreteToggleTemplate);
+    }
+
+    private ControlTemplate parcelAndUnparcel(ControlTemplate toParcel) {
+        Parcel parcel = Parcel.obtain();
+
+        assertNotNull(parcel);
+
+        parcel.setDataPosition(0);
+        toParcel.writeToParcel(parcel, 0);
+        parcel.setDataPosition(0);
+
+        return ControlTemplate.CREATOR.createFromParcel(parcel);
+    }
+}
diff --git a/core/tests/coretests/src/android/util/StatsEventTest.java b/core/tests/coretests/src/android/util/StatsEventTest.java
index 93f11db..097bada 100644
--- a/core/tests/coretests/src/android/util/StatsEventTest.java
+++ b/core/tests/coretests/src/android/util/StatsEventTest.java
@@ -53,8 +53,8 @@
         final ByteBuffer buffer =
                 ByteBuffer.wrap(statsEvent.getBytes()).order(ByteOrder.LITTLE_ENDIAN);
 
-        assertWithMessage("Root element in buffer is not TYPE_ERRORS")
-                .that(buffer.get()).isEqualTo(StatsEvent.TYPE_ERRORS);
+        assertWithMessage("Root element in buffer is not TYPE_OBJECT")
+                .that(buffer.get()).isEqualTo(StatsEvent.TYPE_OBJECT);
 
         assertWithMessage("Incorrect number of elements in root object")
                 .that(buffer.get()).isEqualTo(3);
@@ -71,6 +71,9 @@
         assertWithMessage("Incorrect atom id")
                 .that(buffer.getInt()).isEqualTo(expectedAtomId);
 
+        assertWithMessage("Third element is not errors type")
+                .that(buffer.get()).isEqualTo(StatsEvent.TYPE_ERRORS);
+
         final int errorMask = buffer.getInt();
 
         assertWithMessage("ERROR_NO_ATOM_ID should be the only error in the error mask")
diff --git a/core/tests/coretests/src/android/widget/EditorTouchStateTest.java b/core/tests/coretests/src/android/widget/EditorTouchStateTest.java
new file mode 100644
index 0000000..6d50e3a
--- /dev/null
+++ b/core/tests/coretests/src/android/widget/EditorTouchStateTest.java
@@ -0,0 +1,229 @@
+/*
+ * 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.widget;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+
+import android.view.InputDevice;
+import android.view.MotionEvent;
+import android.view.ViewConfiguration;
+import android.widget.EditorTouchState.MultiTapStatus;
+
+import androidx.test.filters.SmallTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+@SmallTest
+public class EditorTouchStateTest {
+
+    private EditorTouchState mTouchState;
+    private ViewConfiguration mConfig;
+
+    @Before
+    public void before() throws Exception {
+        mTouchState = new EditorTouchState();
+        mConfig = new ViewConfiguration();
+    }
+
+    @Test
+    public void testUpdate_singleTap() throws Exception {
+        // Simulate an ACTION_DOWN event.
+        long event1Time = 1000;
+        MotionEvent event1 = downEvent(event1Time, event1Time, 20f, 30f);
+        mTouchState.update(event1, mConfig);
+        assertSingleTap(mTouchState, 20f, 30f, 0, 0);
+
+        // Simulate an ACTION_UP event.
+        long event2Time = 1001;
+        MotionEvent event2 = upEvent(event1Time, event2Time, 20f, 30f);
+        mTouchState.update(event2, mConfig);
+        assertSingleTap(mTouchState, 20f, 30f, 20f, 30f);
+
+        // Generate an ACTION_DOWN event whose time is after the double-tap timeout.
+        long event3Time = event2Time + ViewConfiguration.getDoubleTapTimeout() + 1;
+        MotionEvent event3 = downEvent(event3Time, event3Time, 22f, 33f);
+        mTouchState.update(event3, mConfig);
+        assertSingleTap(mTouchState, 22f, 33f, 20f, 30f);
+    }
+
+    @Test
+    public void testUpdate_doubleTap_sameArea() throws Exception {
+        // Simulate an ACTION_DOWN event.
+        long event1Time = 1000;
+        MotionEvent event1 = downEvent(event1Time, event1Time, 20f, 30f);
+        mTouchState.update(event1, mConfig);
+        assertSingleTap(mTouchState, 20f, 30f, 0, 0);
+
+        // Simulate an ACTION_UP event.
+        long event2Time = 1001;
+        MotionEvent event2 = upEvent(event1Time, event2Time, 20f, 30f);
+        mTouchState.update(event2, mConfig);
+        assertSingleTap(mTouchState, 20f, 30f, 20f, 30f);
+
+        // Generate an ACTION_DOWN event whose time is within the double-tap timeout.
+        long event3Time = 1002;
+        MotionEvent event3 = downEvent(event3Time, event3Time, 22f, 33f);
+        mTouchState.update(event3, mConfig);
+        assertTap(mTouchState, 22f, 33f, 20f, 30f,
+                MultiTapStatus.DOUBLE_TAP, true);
+    }
+
+    @Test
+    public void testUpdate_doubleTap_notSameArea() throws Exception {
+        // Simulate an ACTION_DOWN event.
+        long event1Time = 1000;
+        MotionEvent event1 = downEvent(event1Time, event1Time, 20f, 30f);
+        mTouchState.update(event1, mConfig);
+        assertSingleTap(mTouchState, 20f, 30f, 0, 0);
+
+        // Simulate an ACTION_UP event.
+        long event2Time = 1001;
+        MotionEvent event2 = upEvent(event1Time, event2Time, 20f, 30f);
+        mTouchState.update(event2, mConfig);
+        assertSingleTap(mTouchState, 20f, 30f, 20f, 30f);
+
+        // Generate an ACTION_DOWN event whose time is within the double-tap timeout.
+        long event3Time = 1002;
+        MotionEvent event3 = downEvent(event3Time, event3Time, 200f, 300f);
+        mTouchState.update(event3, mConfig);
+        assertTap(mTouchState, 200f, 300f, 20f, 30f,
+                MultiTapStatus.DOUBLE_TAP, false);
+
+        // Simulate an ACTION_UP event.
+        long event4Time = 1003;
+        MotionEvent event4 = upEvent(event3Time, event4Time, 200f, 300f);
+        mTouchState.update(event4, mConfig);
+        assertTap(mTouchState, 200f, 300f, 200f, 300f,
+                MultiTapStatus.DOUBLE_TAP, false);
+    }
+
+    @Test
+    public void testUpdate_tripleClick_mouse() throws Exception {
+        // Simulate an ACTION_DOWN event.
+        long event1Time = 1000;
+        MotionEvent event1 = downEvent(event1Time, event1Time, 20f, 30f);
+        event1.setSource(InputDevice.SOURCE_MOUSE);
+        mTouchState.update(event1, mConfig);
+        assertSingleTap(mTouchState, 20f, 30f, 0, 0);
+
+        // Simulate an ACTION_UP event.
+        long event2Time = 1001;
+        MotionEvent event2 = upEvent(event1Time, event2Time, 20f, 30f);
+        event2.setSource(InputDevice.SOURCE_MOUSE);
+        mTouchState.update(event2, mConfig);
+        assertSingleTap(mTouchState, 20f, 30f, 20f, 30f);
+
+        // Generate a second ACTION_DOWN event whose time is within the double-tap timeout.
+        long event3Time = 1002;
+        MotionEvent event3 = downEvent(event3Time, event3Time, 21f, 31f);
+        event3.setSource(InputDevice.SOURCE_MOUSE);
+        mTouchState.update(event3, mConfig);
+        assertTap(mTouchState, 21f, 31f, 20f, 30f,
+                MultiTapStatus.DOUBLE_TAP, true);
+
+        // Simulate an ACTION_UP event.
+        long event4Time = 1003;
+        MotionEvent event4 = upEvent(event3Time, event4Time, 21f, 31f);
+        event4.setSource(InputDevice.SOURCE_MOUSE);
+        mTouchState.update(event4, mConfig);
+        assertTap(mTouchState, 21f, 31f, 21f, 31f,
+                MultiTapStatus.DOUBLE_TAP, true);
+
+        // Generate a third ACTION_DOWN event whose time is within the double-tap timeout.
+        long event5Time = 1004;
+        MotionEvent event5 = downEvent(event5Time, event5Time, 22f, 32f);
+        event5.setSource(InputDevice.SOURCE_MOUSE);
+        mTouchState.update(event5, mConfig);
+        assertTap(mTouchState, 22f, 32f, 21f, 31f,
+                MultiTapStatus.TRIPLE_CLICK, true);
+    }
+
+    @Test
+    public void testUpdate_tripleClick_touch() throws Exception {
+        // Simulate an ACTION_DOWN event.
+        long event1Time = 1000;
+        MotionEvent event1 = downEvent(event1Time, event1Time, 20f, 30f);
+        mTouchState.update(event1, mConfig);
+        assertSingleTap(mTouchState, 20f, 30f, 0, 0);
+
+        // Simulate an ACTION_UP event.
+        long event2Time = 1001;
+        MotionEvent event2 = upEvent(event1Time, event2Time, 20f, 30f);
+        mTouchState.update(event2, mConfig);
+        assertSingleTap(mTouchState, 20f, 30f, 20f, 30f);
+
+        // Generate a second ACTION_DOWN event whose time is within the double-tap timeout.
+        long event3Time = 1002;
+        MotionEvent event3 = downEvent(event3Time, event3Time, 21f, 31f);
+        mTouchState.update(event3, mConfig);
+        assertTap(mTouchState, 21f, 31f, 20f, 30f,
+                MultiTapStatus.DOUBLE_TAP, true);
+
+        // Simulate an ACTION_UP event.
+        long event4Time = 1003;
+        MotionEvent event4 = upEvent(event3Time, event4Time, 21f, 31f);
+        mTouchState.update(event4, mConfig);
+        assertTap(mTouchState, 21f, 31f, 21f, 31f,
+                MultiTapStatus.DOUBLE_TAP, true);
+
+        // Generate a third ACTION_DOWN event whose time is within the double-tap timeout.
+        long event5Time = 1004;
+        MotionEvent event5 = downEvent(event5Time, event5Time, 22f, 32f);
+        mTouchState.update(event5, mConfig);
+        assertTap(mTouchState, 22f, 32f, 21f, 31f,
+                MultiTapStatus.FIRST_TAP, false);
+    }
+
+    private static MotionEvent downEvent(long downTime, long eventTime, float x, float y) {
+        return MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_DOWN, x, y, 0);
+    }
+
+    private static MotionEvent upEvent(long downTime, long eventTime, float x, float y) {
+        return MotionEvent.obtain(downTime, eventTime, MotionEvent.ACTION_UP, x, y, 0);
+    }
+
+    private static void assertSingleTap(EditorTouchState touchState, float lastDownX,
+            float lastDownY, float lastUpX, float lastUpY) {
+        assertThat(touchState.getLastDownX(), is(lastDownX));
+        assertThat(touchState.getLastDownY(), is(lastDownY));
+        assertThat(touchState.getLastUpX(), is(lastUpX));
+        assertThat(touchState.getLastUpY(), is(lastUpY));
+        assertThat(touchState.isDoubleTap(), is(false));
+        assertThat(touchState.isTripleClick(), is(false));
+        assertThat(touchState.isMultiTap(), is(false));
+        assertThat(touchState.isMultiTapInSameArea(), is(false));
+    }
+
+    private static void assertTap(EditorTouchState touchState,
+            float lastDownX, float lastDownY, float lastUpX, float lastUpY,
+            @MultiTapStatus int multiTapStatus, boolean isMultiTapInSameArea) {
+        assertThat(touchState.getLastDownX(), is(lastDownX));
+        assertThat(touchState.getLastDownY(), is(lastDownY));
+        assertThat(touchState.getLastUpX(), is(lastUpX));
+        assertThat(touchState.getLastUpY(), is(lastUpY));
+        assertThat(touchState.isDoubleTap(), is(multiTapStatus == MultiTapStatus.DOUBLE_TAP));
+        assertThat(touchState.isTripleClick(), is(multiTapStatus == MultiTapStatus.TRIPLE_CLICK));
+        assertThat(touchState.isMultiTap(), is(multiTapStatus == MultiTapStatus.DOUBLE_TAP
+                || multiTapStatus == MultiTapStatus.TRIPLE_CLICK));
+        assertThat(touchState.isMultiTapInSameArea(), is(isMultiTapInSameArea));
+    }
+}
diff --git a/data/etc/Android.bp b/data/etc/Android.bp
index c49d663..ff521be 100644
--- a/data/etc/Android.bp
+++ b/data/etc/Android.bp
@@ -104,7 +104,7 @@
 
 prebuilt_etc {
     name: "privapp_whitelist_com.android.settings",
-    product_specific: true,
+    system_ext_specific: true,
     sub_dir: "permissions",
     src: "com.android.settings.xml",
     filename_from_src: true,
diff --git a/data/etc/CleanSpec.mk b/data/etc/CleanSpec.mk
index cffb1ba..3fe421e 100644
--- a/data/etc/CleanSpec.mk
+++ b/data/etc/CleanSpec.mk
@@ -49,6 +49,8 @@
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/product/etc/permissions/com.android.emergency.xml)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/product/etc/permissions/com.android.provision.xml)
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/product/etc/permissions/com.android.provision.xml)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/product/etc/permissions/com.android.settings.xml)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/product/etc/permissions/com.android.settings.xml)
 # ******************************************************************
 # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST ABOVE THIS BANNER
 # ******************************************************************
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index f2a6452..cf3f51d 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -339,8 +339,10 @@
         <permission name="android.permission.SYSTEM_CAMERA" />
         <!-- Permission required to test ExplicitHealthCheckServiceImpl. -->
         <permission name="android.permission.BIND_EXPLICIT_HEALTH_CHECK_SERVICE"/>
-        <!-- Permission required for UiModeManager cts test. -->
+        <!-- Permission required for UiModeManager CTS test. -->
         <permission name="android.permission.ENTER_CAR_MODE_PRIORITIZED"/>
+        <!-- Permission required for Telecom car mode CTS tests. -->
+        <permission name="android.permission.CONTROL_INCALL_EXPERIENCE"/>
     </privapp-permissions>
 
     <privapp-permissions package="com.android.statementservice">
diff --git a/media/Android.bp b/media/Android.bp
index 022fa9b..1912930 100644
--- a/media/Android.bp
+++ b/media/Android.bp
@@ -45,8 +45,8 @@
 filegroup {
     name: "updatable-media-srcs",
     srcs: [
-        ":mediasession2-srcs",
         ":mediaparser-srcs",
+        ":mediasession2-srcs",
     ],
 }
 
@@ -73,7 +73,8 @@
     name: "mediaparser-srcs",
     srcs: [
         "apex/java/android/media/MediaParser.java"
-    ]
+    ],
+    path: "apex/java"
 }
 
 metalava_updatable_media_args = " --error UnhiddenSystemApi " +
diff --git a/media/apex/java/android/media/MediaParser.java b/media/apex/java/android/media/MediaParser.java
index c06e283..8824269 100644
--- a/media/apex/java/android/media/MediaParser.java
+++ b/media/apex/java/android/media/MediaParser.java
@@ -15,10 +15,47 @@
  */
 package android.media;
 
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.Uri;
+import android.text.TextUtils;
 import android.util.Pair;
+import android.util.SparseArray;
 
+import com.google.android.exoplayer2.C;
+import com.google.android.exoplayer2.Format;
+import com.google.android.exoplayer2.extractor.DefaultExtractorInput;
+import com.google.android.exoplayer2.extractor.Extractor;
+import com.google.android.exoplayer2.extractor.ExtractorInput;
+import com.google.android.exoplayer2.extractor.ExtractorOutput;
+import com.google.android.exoplayer2.extractor.PositionHolder;
+import com.google.android.exoplayer2.extractor.SeekMap.SeekPoints;
+import com.google.android.exoplayer2.extractor.TrackOutput;
+import com.google.android.exoplayer2.extractor.amr.AmrExtractor;
+import com.google.android.exoplayer2.extractor.flv.FlvExtractor;
+import com.google.android.exoplayer2.extractor.mkv.MatroskaExtractor;
+import com.google.android.exoplayer2.extractor.mp3.Mp3Extractor;
+import com.google.android.exoplayer2.extractor.mp4.FragmentedMp4Extractor;
+import com.google.android.exoplayer2.extractor.mp4.Mp4Extractor;
+import com.google.android.exoplayer2.extractor.ogg.OggExtractor;
+import com.google.android.exoplayer2.extractor.ts.Ac3Extractor;
+import com.google.android.exoplayer2.extractor.ts.Ac4Extractor;
+import com.google.android.exoplayer2.extractor.ts.AdtsExtractor;
+import com.google.android.exoplayer2.extractor.ts.PsExtractor;
+import com.google.android.exoplayer2.extractor.ts.TsExtractor;
+import com.google.android.exoplayer2.extractor.wav.WavExtractor;
+import com.google.android.exoplayer2.upstream.DataSource;
+import com.google.android.exoplayer2.upstream.DataSpec;
+import com.google.android.exoplayer2.upstream.TransferListener;
+import com.google.android.exoplayer2.util.ParsableByteArray;
+
+import java.io.EOFException;
 import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.Collections;
+import java.util.LinkedHashMap;
 import java.util.List;
+import java.util.Map;
 
 /**
  * Parses media container formats and extracts contained media samples and metadata.
@@ -32,16 +69,93 @@
  * <p>Users must implement the following to use this class.
  *
  * <ul>
- *   <li>{@link Input}: Provides the media containers bytes to parse.
- *   <li>{@link OutputCallback}: Provides a sink for all extracted data and metadata.
+ *   <li>{@link InputReader}: Provides the media container's bytes to parse.
+ *   <li>{@link OutputConsumer}: Provides a sink for all extracted data and metadata.
  * </ul>
  *
- * TODO: Add usage example here.
+ * <p>The following code snippet includes a usage example:
+ *
+ * <pre>
+ * MyOutputConsumer myOutputConsumer = new MyOutputConsumer();
+ * MyInputReader myInputReader = new MyInputReader("www.example.com");
+ * MediaParser mediaParser = MediaParser.create(myOutputConsumer);
+ *
+ * while (mediaParser.advance(myInputReader)) {}
+ *
+ * mediaParser.release();
+ * mediaParser = null;
+ * </pre>
+ *
+ * <p>The following code snippet provides a rudimentary {@link OutputConsumer} sample implementation
+ * which extracts and publishes all video samples:
+ *
+ * <pre>
+ *
+ * class VideoOutputConsumer implements MediaParser.OutputConsumer {
+ *
+ *     private static final int MAXIMUM_SAMPLE_SIZE = ...;
+ *     private byte[] sampleDataBuffer = new byte[MAXIMUM_SAMPLE_SIZE];
+ *     private int videoTrackIndex = -1;
+ *     private int bytesWrittenCount = 0;
+ *
+ *     \@Override
+ *     public void onSeekMap(int i, @NonNull MediaFormat mediaFormat) { \/* Do nothing. *\/ }
+ *
+ *     \@Override
+ *     public void onFormat(int i, @NonNull MediaFormat mediaFormat) {
+ *       if (videoTrackIndex == -1 && mediaFormat
+ *           .getString(MediaFormat.KEY_MIME, \/* defaultValue= *\/ "").startsWith("video/")) {
+ *         videoTrackIndex = i;
+ *       }
+ *     }
+ *
+ *     \@Override
+ *     public void onSampleData(int trackIndex, @NonNull InputReader inputReader)
+ *         throws IOException, InterruptedException {
+ *       int numberOfBytesToRead = (int) inputReader.getLength();
+ *       if (videoTrackIndex != trackIndex) {
+ *         // Discard contents.
+ *         inputReader.read(\/* bytes= *\/ null, \/* offset= *\/ 0, numberOfBytesToRead);
+ *       }
+ *       int bytesRead = inputReader.read(sampleDataBuffer, bytesWrittenCount, numberOfBytesToRead);
+ *       bytesWrittenCount += bytesRead;
+ *     }
+ *
+ *     \@Override
+ *     public void onSampleCompleted(
+ *         int trackIndex,
+ *         long timeUs,
+ *         int flags,
+ *         int size,
+ *         int offset,
+ *         \@Nullable CryptoInfo cryptoData) {
+ *       if (videoTrackIndex != trackIndex) {
+ *         return; // It's not the video track. Ignore.
+ *       }
+ *       byte[] sampleData = new byte[size];
+ *       System.arraycopy(sampleDataBuffer, bytesWrittenCount - size - offset, sampleData, \/*
+ *       destPos= *\/ 0, size);
+ *       // Place trailing bytes at the start of the buffer.
+ *       System.arraycopy(
+ *           sampleDataBuffer,
+ *           bytesWrittenCount - offset,
+ *           sampleDataBuffer,
+ *           \/* destPos= *\/ 0,
+ *           \/* size= *\/ offset);
+ *       publishSample(sampleData, timeUs, flags);
+ *     }
+ *   }
+ *
+ * </pre>
  */
-// @HiddenApi
 public final class MediaParser {
 
-    /** Maps seek positions to corresponding positions in the stream. */
+    /**
+     * Maps seek positions to {@link SeekPoint SeekPoints} in the stream.
+     *
+     * <p>A {@link SeekPoint} is a position in the stream from which a player may successfully start
+     * playing media samples.
+     */
     public interface SeekMap {
 
         /** Returned by {@link #getDurationUs()} when the duration is unknown. */
@@ -62,13 +176,14 @@
          * <p>{@code getSeekPoints(timeUs).first} contains the latest seek point for samples with
          * timestamp equal to or smaller than {@code timeUs}.
          *
-         * <p>{@code getSeekPoints(timeUs).second} contains the earlies seek point for samples with
+         * <p>{@code getSeekPoints(timeUs).second} contains the earliest seek point for samples with
          * timestamp equal to or greater than {@code timeUs}. If a seek point exists for {@code
          * timeUs}, the returned pair will contain the same {@link SeekPoint} twice.
          *
          * @param timeUs A seek time in microseconds.
          * @return The corresponding {@link SeekPoint SeekPoints}.
          */
+        @NonNull
         Pair<SeekPoint, SeekPoint> getSeekPoints(long timeUs);
     }
 
@@ -76,30 +191,30 @@
     public static final class SeekPoint {
 
         /** A {@link SeekPoint} whose time and byte offset are both set to 0. */
-        public static final SeekPoint START = new SeekPoint(0, 0);
+        public static final @NonNull SeekPoint START = new SeekPoint(0, 0);
 
         /** The time of the seek point, in microseconds. */
-        public final long mTimeUs;
+        public final long timeUs;
 
         /** The byte offset of the seek point. */
-        public final long mPosition;
+        public final long position;
 
         /**
          * @param timeUs The time of the seek point, in microseconds.
          * @param position The byte offset of the seek point.
          */
-        public SeekPoint(long timeUs, long position) {
-            this.mTimeUs = timeUs;
-            this.mPosition = position;
+        private SeekPoint(long timeUs, long position) {
+            this.timeUs = timeUs;
+            this.position = position;
         }
 
         @Override
-        public String toString() {
-            return "[timeUs=" + mTimeUs + ", position=" + mPosition + "]";
+        public @NonNull String toString() {
+            return "[timeUs=" + timeUs + ", position=" + position + "]";
         }
 
         @Override
-        public boolean equals(Object obj) {
+        public boolean equals(@Nullable Object obj) {
             if (this == obj) {
                 return true;
             }
@@ -107,25 +222,26 @@
                 return false;
             }
             SeekPoint other = (SeekPoint) obj;
-            return mTimeUs == other.mTimeUs && mPosition == other.mPosition;
+            return timeUs == other.timeUs && position == other.position;
         }
 
         @Override
         public int hashCode() {
-            int result = (int) mTimeUs;
-            result = 31 * result + (int) mPosition;
+            int result = (int) timeUs;
+            result = 31 * result + (int) position;
             return result;
         }
     }
 
     /** Provides input data to {@link MediaParser}. */
-    public interface Input {
+    public interface InputReader {
 
         /**
          * Reads up to {@code readLength} bytes of data and stores them into {@code buffer},
          * starting at index {@code offset}.
          *
-         * <p>The call will block until at least one byte of data has been read.
+         * <p>This method blocks until at least one byte is read, the end of input is detected, or
+         * an exception is thrown. The read position advances to the first unread byte.
          *
          * @param buffer The buffer into which the read data should be stored.
          * @param offset The start offset into {@code buffer} at which data should be written.
@@ -134,7 +250,7 @@
          *     of the input has been reached.
          * @throws java.io.IOException If an error occurs reading from the source.
          */
-        int read(byte[] buffer, int offset, int readLength)
+        int read(@NonNull byte[] buffer, int offset, int readLength)
                 throws IOException, InterruptedException;
 
         /** Returns the current read position (byte offset) in the stream. */
@@ -144,22 +260,39 @@
         long getLength();
     }
 
-    /** Receives extracted media sample data and metadata from {@link MediaParser}. */
-    public interface OutputCallback {
+    /** {@link InputReader} that allows setting the read position. */
+    public interface SeekableInputReader extends InputReader {
 
         /**
-         * Called when the number of tracks is defined.
+         * Sets the read position at the given {@code position}.
          *
-         * @param numberOfTracks The number of tracks in the stream.
+         * <p>{@link #advance} will immediately return after calling this method.
+         *
+         * @param position The position to seek to, in bytes.
          */
-        void onTracksFound(int numberOfTracks);
+        void seekToPosition(long position);
+    }
+
+    /** Receives extracted media sample data and metadata from {@link MediaParser}. */
+    public interface OutputConsumer {
 
         /**
          * Called when a {@link SeekMap} has been extracted from the stream.
          *
+         * <p>This method is called at least once before any samples are {@link #onSampleCompleted
+         * complete}. May be called multiple times after that in order to add {@link SeekPoint
+         * SeekPoints}.
+         *
          * @param seekMap The extracted {@link SeekMap}.
          */
-        void onSeekMap(SeekMap seekMap);
+        void onSeekMap(@NonNull SeekMap seekMap);
+
+        /**
+         * Called when the number of tracks is found.
+         *
+         * @param numberOfTracks The number of tracks in the stream.
+         */
+        void onTracksFound(int numberOfTracks);
 
         /**
          * Called when the {@link MediaFormat} of the track is extracted from the stream.
@@ -167,7 +300,7 @@
          * @param trackIndex The index of the track for which the {@link MediaFormat} was found.
          * @param format The extracted {@link MediaFormat}.
          */
-        void onFormat(int trackIndex, MediaFormat format);
+        void onFormat(int trackIndex, @NonNull MediaFormat format);
 
         /**
          * Called to write sample data to the output.
@@ -176,16 +309,15 @@
          * thrown {@link IOException} caused by reading from {@code input}.
          *
          * @param trackIndex The index of the track to which the sample data corresponds.
-         * @param input The {@link Input} from which to read the data.
-         * @return
+         * @param inputReader The {@link InputReader} from which to read the data.
          */
-        int onSampleData(int trackIndex, Input input) throws IOException, InterruptedException;
+        void onSampleData(int trackIndex, @NonNull InputReader inputReader)
+                throws IOException, InterruptedException;
 
         /**
-         * Defines the boundaries and metadata of an extracted sample.
+         * Called once all the data of a sample has been passed to {@link #onSampleData}.
          *
-         * <p>The corresponding sample data will have already been passed to the output via calls to
-         * {@link #onSampleData}.
+         * <p>Also includes sample metadata, like presentation timestamp and flags.
          *
          * @param trackIndex The index of the track to which the sample corresponds.
          * @param timeUs The media timestamp associated with the sample, in microseconds.
@@ -203,57 +335,22 @@
                 int flags,
                 int size,
                 int offset,
-                MediaCodec.CryptoInfo cryptoData);
-    }
-
-    /**
-     * Controls the behavior of extractors' implementations.
-     *
-     * <p>DESIGN NOTE: For setting flags like workarounds and special behaviors for adaptive
-     * streaming.
-     */
-    public static final class Parameters {
-
-        // TODO: Implement.
-
-    }
-
-    /** Holds the result of an {@link #advance} invocation. */
-    public static final class ResultHolder {
-
-        /** Creates a new instance with {@link #result} holding {@link #ADVANCE_RESULT_CONTINUE}. */
-        public ResultHolder() {
-            result = ADVANCE_RESULT_CONTINUE;
-        }
-
-        /**
-         * May hold {@link #ADVANCE_RESULT_END_OF_INPUT}, {@link #ADVANCE_RESULT_CONTINUE}, {@link
-         * #ADVANCE_RESULT_SEEK}.
-         */
-        public int result;
-
-        /**
-         * If {@link #result} holds {@link #ADVANCE_RESULT_SEEK}, holds the stream position required
-         * from the passed {@link Input} to the next {@link #advance} call. If {@link #result} does
-         * not hold {@link #ADVANCE_RESULT_SEEK}, the value of this variable is undefined and should
-         * be ignored.
-         */
-        public long seekPosition;
+                @Nullable MediaCodec.CryptoInfo cryptoData);
     }
 
     /**
      * Thrown if all extractors implementations provided to {@link #create} failed to sniff the
      * input content.
      */
-    // @HiddenApi
     public static final class UnrecognizedInputFormatException extends IOException {
 
         /**
          * Creates a new instance which signals that the extractors with the given names failed to
          * parse the input.
          */
-        public static UnrecognizedInputFormatException createForExtractors(
-                String... extractorNames) {
+        @NonNull
+        private static UnrecognizedInputFormatException createForExtractors(
+                @NonNull String... extractorNames) {
             StringBuilder builder = new StringBuilder();
             builder.append("None of the available extractors ( ");
             builder.append(extractorNames[0]);
@@ -270,21 +367,9 @@
         }
     }
 
-    // Public constants.
+    // Private constants.
 
-    /**
-     * Returned by {@link #advance} if the {@link Input} passed to the next {@link #advance} is
-     * required to provide data continuing from the position in the stream reached by the returning
-     * call.
-     */
-    public static final int ADVANCE_RESULT_CONTINUE = -1;
-    /** Returned by {@link #advance} if the end of the {@link Input} was reached. */
-    public static final int ADVANCE_RESULT_END_OF_INPUT = -2;
-    /**
-     * Returned by {@link #advance} when its next call expects a specific stream position, which
-     * will be held by {@link ResultHolder#seekPosition}.
-     */
-    public static final int ADVANCE_RESULT_SEEK = -3;
+    private static final Map<String, ExtractorFactory> EXTRACTOR_FACTORIES_BY_NAME;
 
     // Instance creation methods.
 
@@ -293,13 +378,15 @@
      * instance will attempt extraction without sniffing the content.
      *
      * @param name The name of the extractor that will be associated with the created instance.
-     * @param outputCallback The {@link OutputCallback} to which track data and samples are pushed.
-     * @param parameters Parameters that control specific aspects of the behavior of the extractors.
+     * @param outputConsumer The {@link OutputConsumer} to which track data and samples are pushed.
      * @return A new instance.
+     * @throws IllegalArgumentException If an invalid name is provided.
      */
-    public static MediaParser createByName(
-            String name, OutputCallback outputCallback, Parameters parameters) {
-        throw new UnsupportedOperationException();
+    public static @NonNull MediaParser createByName(
+            @NonNull String name, @NonNull OutputConsumer outputConsumer) {
+        String[] nameAsArray = new String[] {name};
+        assertValidNames(nameAsArray);
+        return new MediaParser(outputConsumer, /* sniff= */ false, name);
     }
 
     /**
@@ -307,30 +394,46 @@
      * the first {@link #advance} call. Extractor implementations will sniff the content in order of
      * appearance in {@code extractorNames}.
      *
-     * @param outputCallback The {@link OutputCallback} to track data and samples are obtained.
-     * @param parameters Parameters that control specific aspects of the behavior of the extractors.
+     * @param outputConsumer The {@link OutputConsumer} to which extracted data is output.
      * @param extractorNames The names of the extractors to sniff the content with. If empty, a
      *     default array of names is used.
      * @return A new instance.
      */
-    public static MediaParser create(
-            OutputCallback outputCallback, Parameters parameters, String... extractorNames) {
-        throw new UnsupportedOperationException();
+    public static @NonNull MediaParser create(
+            @NonNull OutputConsumer outputConsumer, @NonNull String... extractorNames) {
+        assertValidNames(extractorNames);
+        if (extractorNames.length == 0) {
+            extractorNames = EXTRACTOR_FACTORIES_BY_NAME.keySet().toArray(new String[0]);
+        }
+        return new MediaParser(outputConsumer, /* sniff= */ true, extractorNames);
     }
 
     // Misc static methods.
 
     /**
      * Returns an immutable list with the names of the extractors that are suitable for container
-     * formats with the given {@code mimeTypes}. If an empty string is passed, all available
-     * extractors' names are returned.
+     * formats with the given {@link MediaFormat}.
      *
-     * <p>TODO: Replace string with media type object.
+     * <p>TODO: List which properties are taken into account. E.g. MimeType.
      */
-    public static List<String> getExtractorNames(String mimeTypes) {
+    public static @NonNull List<String> getExtractorNames(@NonNull MediaFormat mediaFormat) {
         throw new UnsupportedOperationException();
     }
 
+    // Private fields.
+
+    private final OutputConsumer mOutputConsumer;
+    private final String[] mExtractorNamesPool;
+    private final PositionHolder mPositionHolder;
+    private final InputReadingDataSource mDataSource;
+    private final ExtractorInputAdapter mScratchExtractorInputAdapter;
+    private final ParsableByteArrayAdapter mScratchParsableByteArrayAdapter;
+    private String mExtractorName;
+    private Extractor mExtractor;
+    private ExtractorInput mExtractorInput;
+    private long mPendingSeekPosition;
+    private long mPendingSeekTimeUs;
+
     // Public methods.
 
     /**
@@ -344,8 +447,8 @@
      * @return The name of the backing extractor implementation, or null if the backing extractor
      *     implementation has not yet been selected.
      */
-    public String getExtractorName() {
-        throw new UnsupportedOperationException();
+    public @Nullable String getExtractorName() {
+        return mExtractorName;
     }
 
     /**
@@ -357,26 +460,85 @@
      * <p>If this instance was created using {@link #create}. the first call to this method will
      * sniff the content with the extractors with the provided names.
      *
-     * @param input The {@link Input} from which to obtain the media container data.
-     * @param resultHolder The {@link ResultHolder} into which the result of the operation will be
-     *     written.
+     * @param seekableInputReader The {@link SeekableInputReader} from which to obtain the media
+     *     container data.
+     * @return Whether there is any data left to extract. Returns false if the end of input has been
+     *     reached.
      * @throws UnrecognizedInputFormatException
      */
-    public void advance(Input input, ResultHolder resultHolder)
+    public boolean advance(@NonNull SeekableInputReader seekableInputReader)
             throws IOException, InterruptedException {
-        throw new UnsupportedOperationException();
+        if (mExtractorInput == null) {
+            // TODO: For efficiency, the same implementation should be used, by providing a
+            // clearBuffers() method, or similar.
+            mExtractorInput =
+                    new DefaultExtractorInput(
+                            mDataSource,
+                            seekableInputReader.getPosition(),
+                            seekableInputReader.getLength());
+        }
+        mDataSource.mInputReader = seekableInputReader;
+
+        if (mExtractor == null) {
+            for (String extractorName : mExtractorNamesPool) {
+                Extractor extractor =
+                        EXTRACTOR_FACTORIES_BY_NAME.get(extractorName).createInstance();
+                try {
+                    if (extractor.sniff(mExtractorInput)) {
+                        mExtractorName = extractorName;
+                        mExtractor = extractor;
+                        mExtractor.init(new ExtractorOutputAdapter());
+                        break;
+                    }
+                } catch (EOFException e) {
+                    // Do nothing.
+                } catch (IOException | InterruptedException e) {
+                    throw new IllegalStateException(e);
+                } finally {
+                    mExtractorInput.resetPeekPosition();
+                }
+            }
+            if (mExtractor == null) {
+                UnrecognizedInputFormatException.createForExtractors(mExtractorNamesPool);
+            }
+            return true;
+        }
+
+        if (isPendingSeek()) {
+            mExtractor.seek(mPendingSeekPosition, mPendingSeekTimeUs);
+            removePendingSeek();
+        }
+
+        mPositionHolder.position = seekableInputReader.getPosition();
+        int result = mExtractor.read(mExtractorInput, mPositionHolder);
+        if (result == Extractor.RESULT_END_OF_INPUT) {
+            return false;
+        }
+        if (result == Extractor.RESULT_SEEK) {
+            mExtractorInput = null;
+            seekableInputReader.seekToPosition(mPositionHolder.position);
+        }
+        return true;
     }
 
     /**
      * Seeks within the media container being extracted.
      *
-     * <p>Following a call to this method, the {@link Input} passed to the next invocation of {@link
-     * #advance} must provide data starting from {@link SeekPoint#mPosition} in the stream.
+     * <p>{@link SeekPoint SeekPoints} can be obtained from the {@link SeekMap} passed to {@link
+     * OutputConsumer#onSeekMap(SeekMap)}.
+     *
+     * <p>Following a call to this method, the {@link InputReader} passed to the next invocation of
+     * {@link #advance} must provide data starting from {@link SeekPoint#position} in the stream.
      *
      * @param seekPoint The {@link SeekPoint} to seek to.
      */
-    public void seek(SeekPoint seekPoint) {
-        throw new UnsupportedOperationException();
+    public void seek(@NonNull SeekPoint seekPoint) {
+        if (mExtractor == null) {
+            mPendingSeekPosition = seekPoint.position;
+            mPendingSeekTimeUs = seekPoint.timeUs;
+        } else {
+            mExtractor.seek(seekPoint.position, seekPoint.timeUs);
+        }
     }
 
     /**
@@ -386,6 +548,359 @@
      * invoked. DESIGN NOTE: Should be removed. There shouldn't be any resource for releasing.
      */
     public void release() {
-        throw new UnsupportedOperationException();
+        mExtractorInput = null;
+        mExtractor = null;
+    }
+
+    // Private methods.
+
+    private MediaParser(
+            OutputConsumer outputConsumer, boolean sniff, String... extractorNamesPool) {
+        mOutputConsumer = outputConsumer;
+        mExtractorNamesPool = extractorNamesPool;
+        if (!sniff) {
+            mExtractorName = extractorNamesPool[0];
+            mExtractor = EXTRACTOR_FACTORIES_BY_NAME.get(mExtractorName).createInstance();
+        }
+        mPositionHolder = new PositionHolder();
+        mDataSource = new InputReadingDataSource();
+        removePendingSeek();
+        mScratchExtractorInputAdapter = new ExtractorInputAdapter();
+        mScratchParsableByteArrayAdapter = new ParsableByteArrayAdapter();
+    }
+
+    private boolean isPendingSeek() {
+        return mPendingSeekPosition >= 0;
+    }
+
+    private void removePendingSeek() {
+        mPendingSeekPosition = -1;
+        mPendingSeekTimeUs = -1;
+    }
+
+    // Private classes.
+
+    private static final class InputReadingDataSource implements DataSource {
+
+        public InputReader mInputReader;
+
+        @Override
+        public void addTransferListener(TransferListener transferListener) {
+            // Do nothing.
+        }
+
+        @Override
+        public long open(DataSpec dataSpec) {
+            throw new UnsupportedOperationException();
+        }
+
+        @Override
+        public int read(byte[] buffer, int offset, int readLength) throws IOException {
+            // TODO: Reevaluate interruption in Input.
+            try {
+                return mInputReader.read(buffer, offset, readLength);
+            } catch (InterruptedException e) {
+                // TODO: Remove.
+                throw new RuntimeException();
+            }
+        }
+
+        @Override
+        public Uri getUri() {
+            return null;
+        }
+
+        @Override
+        public Map<String, List<String>> getResponseHeaders() {
+            return null;
+        }
+
+        @Override
+        public void close() {
+            throw new UnsupportedOperationException();
+        }
+    }
+
+    private final class ExtractorOutputAdapter implements ExtractorOutput {
+
+        private final SparseArray<TrackOutput> mTrackOutputAdapters;
+        private boolean mTracksEnded;
+
+        private ExtractorOutputAdapter() {
+            mTrackOutputAdapters = new SparseArray<>();
+        }
+
+        @Override
+        public TrackOutput track(int id, int type) {
+            TrackOutput trackOutput = mTrackOutputAdapters.get(id);
+            if (trackOutput == null) {
+                trackOutput = new TrackOutputAdapter(mTrackOutputAdapters.size());
+                mTrackOutputAdapters.put(id, trackOutput);
+            }
+            return trackOutput;
+        }
+
+        @Override
+        public void endTracks() {
+            mOutputConsumer.onTracksFound(mTrackOutputAdapters.size());
+        }
+
+        @Override
+        public void seekMap(com.google.android.exoplayer2.extractor.SeekMap exoplayerSeekMap) {
+            mOutputConsumer.onSeekMap(new ExoToMediaParserSeekMapAdapter(exoplayerSeekMap));
+        }
+    }
+
+    private class TrackOutputAdapter implements TrackOutput {
+
+        private final int mTrackIndex;
+
+        private TrackOutputAdapter(int trackIndex) {
+            mTrackIndex = trackIndex;
+        }
+
+        @Override
+        public void format(Format format) {
+            mOutputConsumer.onFormat(mTrackIndex, toMediaFormat(format));
+        }
+
+        @Override
+        public int sampleData(ExtractorInput input, int length, boolean allowEndOfInput)
+                throws IOException, InterruptedException {
+            mScratchExtractorInputAdapter.setExtractorInput(input, length);
+            long positionBeforeReading = mScratchExtractorInputAdapter.getPosition();
+            mOutputConsumer.onSampleData(mTrackIndex, mScratchExtractorInputAdapter);
+            return (int) (mScratchExtractorInputAdapter.getPosition() - positionBeforeReading);
+        }
+
+        @Override
+        public void sampleData(ParsableByteArray data, int length) {
+            mScratchParsableByteArrayAdapter.resetWithByteArray(data, length);
+            try {
+                mOutputConsumer.onSampleData(mTrackIndex, mScratchParsableByteArrayAdapter);
+            } catch (IOException | InterruptedException e) {
+                // Unexpected.
+                throw new RuntimeException(e);
+            }
+        }
+
+        @Override
+        public void sampleMetadata(
+                long timeUs, int flags, int size, int offset, CryptoData encryptionData) {
+            mOutputConsumer.onSampleCompleted(
+                    mTrackIndex, timeUs, flags, size, offset, toCryptoInfo(encryptionData));
+        }
+    }
+
+    private static final class ExtractorInputAdapter implements InputReader {
+
+        private ExtractorInput mExtractorInput;
+        private int mCurrentPosition;
+        private long mLength;
+
+        public void setExtractorInput(ExtractorInput extractorInput, long length) {
+            mExtractorInput = extractorInput;
+            mCurrentPosition = 0;
+            mLength = length;
+        }
+
+        // Input implementation.
+
+        @Override
+        public int read(byte[] buffer, int offset, int readLength)
+                throws IOException, InterruptedException {
+            int readBytes = mExtractorInput.read(buffer, offset, readLength);
+            mCurrentPosition += readBytes;
+            return readBytes;
+        }
+
+        @Override
+        public long getPosition() {
+            return mCurrentPosition;
+        }
+
+        @Override
+        public long getLength() {
+            return mLength - mCurrentPosition;
+        }
+    }
+
+    private static final class ParsableByteArrayAdapter implements InputReader {
+
+        private ParsableByteArray mByteArray;
+        private long mLength;
+        private int mCurrentPosition;
+
+        public void resetWithByteArray(ParsableByteArray byteArray, long length) {
+            mByteArray = byteArray;
+            mCurrentPosition = 0;
+            mLength = length;
+        }
+
+        // Input implementation.
+
+        @Override
+        public int read(byte[] buffer, int offset, int readLength) {
+            mByteArray.readBytes(buffer, offset, readLength);
+            mCurrentPosition += readLength;
+            return readLength;
+        }
+
+        @Override
+        public long getPosition() {
+            return mCurrentPosition;
+        }
+
+        @Override
+        public long getLength() {
+            return mLength - mCurrentPosition;
+        }
+    }
+
+    /** Creates extractor instances. */
+    private interface ExtractorFactory {
+
+        /** Returns a new extractor instance. */
+        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) {
+
+        // TODO: Add if (value != Format.NO_VALUE);
+
+        MediaFormat result = new MediaFormat();
+        result.setInteger(MediaFormat.KEY_BIT_RATE, format.bitrate);
+        result.setInteger(MediaFormat.KEY_CHANNEL_COUNT, format.channelCount);
+        if (format.colorInfo != null) {
+            result.setInteger(MediaFormat.KEY_COLOR_TRANSFER, format.colorInfo.colorTransfer);
+            result.setInteger(MediaFormat.KEY_COLOR_RANGE, format.colorInfo.colorRange);
+            result.setInteger(MediaFormat.KEY_COLOR_STANDARD, format.colorInfo.colorSpace);
+            if (format.colorInfo.hdrStaticInfo != null) {
+                result.setByteBuffer(
+                        MediaFormat.KEY_HDR_STATIC_INFO,
+                        ByteBuffer.wrap(format.colorInfo.hdrStaticInfo));
+            }
+        }
+        result.setString(MediaFormat.KEY_MIME, format.sampleMimeType);
+        result.setFloat(MediaFormat.KEY_FRAME_RATE, format.frameRate);
+        result.setInteger(MediaFormat.KEY_WIDTH, format.width);
+        result.setInteger(MediaFormat.KEY_HEIGHT, format.height);
+        List<byte[]> initData = format.initializationData;
+        if (initData != null) {
+            for (int i = 0; i < initData.size(); i++) {
+                result.setByteBuffer("csd-" + i, ByteBuffer.wrap(initData.get(i)));
+            }
+        }
+        result.setString(MediaFormat.KEY_LANGUAGE, format.language);
+        result.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, format.maxInputSize);
+        result.setInteger(MediaFormat.KEY_PCM_ENCODING, format.pcmEncoding);
+        result.setInteger(MediaFormat.KEY_ROTATION, format.rotationDegrees);
+        result.setInteger(MediaFormat.KEY_SAMPLE_RATE, format.sampleRate);
+
+        int selectionFlags = format.selectionFlags;
+        // We avoid setting selection flags in the MediaFormat, unless explicitly signaled by the
+        // extractor.
+        if ((selectionFlags & C.SELECTION_FLAG_AUTOSELECT) != 0) {
+            result.setInteger(MediaFormat.KEY_IS_AUTOSELECT, 1);
+        }
+        if ((selectionFlags & C.SELECTION_FLAG_DEFAULT) != 0) {
+            result.setInteger(MediaFormat.KEY_IS_DEFAULT, 1);
+        }
+        if ((selectionFlags & C.SELECTION_FLAG_FORCED) != 0) {
+            result.setInteger(MediaFormat.KEY_IS_FORCED_SUBTITLE, 1);
+        }
+
+        // LACK OF SUPPORT FOR:
+        //    format.accessibilityChannel;
+        //    format.codecs;
+        //    format.containerMimeType;
+        //    format.drmInitData;
+        //    format.encoderDelay;
+        //    format.encoderPadding;
+        //    format.id;
+        //    format.metadata;
+        //    format.pixelWidthHeightRatio;
+        //    format.roleFlags;
+        //    format.stereoMode;
+        //    format.subsampleOffsetUs;
+        return result;
+    }
+
+    private static int toFrameworkFlags(int flags) {
+        // TODO: Implement.
+        return 0;
+    }
+
+    private static MediaCodec.CryptoInfo toCryptoInfo(TrackOutput.CryptoData encryptionData) {
+        // TODO: Implement.
+        return null;
+    }
+
+    /** Returns a new {@link SeekPoint} equivalent to the given {@code exoPlayerSeekPoint}. */
+    private static SeekPoint toSeekPoint(
+            com.google.android.exoplayer2.extractor.SeekPoint exoPlayerSeekPoint) {
+        return new SeekPoint(exoPlayerSeekPoint.timeUs, exoPlayerSeekPoint.position);
+    }
+
+    private static void assertValidNames(@NonNull String[] names) {
+        for (String name : names) {
+            if (!EXTRACTOR_FACTORIES_BY_NAME.containsKey(name)) {
+                throw new IllegalArgumentException(
+                        "Invalid extractor name: "
+                                + name
+                                + ". Supported extractors are: "
+                                + TextUtils.join(", ", EXTRACTOR_FACTORIES_BY_NAME.keySet())
+                                + ".");
+            }
+        }
+    }
+
+    // Static initialization.
+
+    static {
+        // Using a LinkedHashMap to keep the insertion order when iterating over the keys.
+        LinkedHashMap<String, ExtractorFactory> extractorFactoriesByName = new LinkedHashMap<>();
+        extractorFactoriesByName.put("exo.Ac3Extractor", Ac3Extractor::new);
+        extractorFactoriesByName.put("exo.Ac4Extractor", Ac4Extractor::new);
+        extractorFactoriesByName.put("exo.AdtsExtractor", AdtsExtractor::new);
+        extractorFactoriesByName.put("exo.AmrExtractor", AmrExtractor::new);
+        extractorFactoriesByName.put("exo.FlvExtractor", FlvExtractor::new);
+        extractorFactoriesByName.put("exo.FragmentedMp4Extractor", FragmentedMp4Extractor::new);
+        extractorFactoriesByName.put("exo.MatroskaExtractor", MatroskaExtractor::new);
+        extractorFactoriesByName.put("exo.Mp3Extractor", Mp3Extractor::new);
+        extractorFactoriesByName.put("exo.Mp4Extractor", Mp4Extractor::new);
+        extractorFactoriesByName.put("exo.OggExtractor", OggExtractor::new);
+        extractorFactoriesByName.put("exo.PsExtractor", PsExtractor::new);
+        extractorFactoriesByName.put("exo.TsExtractor", TsExtractor::new);
+        extractorFactoriesByName.put("exo.WavExtractor", WavExtractor::new);
+        EXTRACTOR_FACTORIES_BY_NAME = Collections.unmodifiableMap(extractorFactoriesByName);
     }
 }
diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java
index 63657a6..6523e30 100644
--- a/media/java/android/media/MediaDrm.java
+++ b/media/java/android/media/MediaDrm.java
@@ -36,9 +36,12 @@
 import java.lang.annotation.RetentionPolicy;
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
+import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.UUID;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.Executor;
@@ -203,6 +206,16 @@
                 securityLevel);
     }
 
+    /**
+     * @return list of crypto schemes (as {@link UUID}s) for which
+     * {@link #isCryptoSchemeSupported(UUID)} returns true; each {@link UUID}
+     * can be used as input to create {@link MediaDrm} objects via {@link #MediaDrm(UUID)}.
+     */
+    public static final @NonNull List<UUID> getSupportedCryptoSchemes(){
+        byte[] uuidBytes = getSupportedCryptoSchemesNative();
+        return getUUIDsFromByteArray(uuidBytes);
+    }
+
     private static final byte[] getByteArrayFromUUID(@NonNull UUID uuid) {
         long msb = uuid.getMostSignificantBits();
         long lsb = uuid.getLeastSignificantBits();
@@ -216,6 +229,28 @@
         return uuidBytes;
     }
 
+    private static final UUID getUUIDFromByteArray(@NonNull byte[] uuidBytes, int off) {
+        long msb = 0;
+        long lsb = 0;
+
+        for (int i = 0; i < 8; ++i) {
+            msb = (msb << 8) | (0xffl & uuidBytes[off + i]);
+            lsb = (lsb << 8) | (0xffl & uuidBytes[off + i + 8]);
+        }
+
+        return new UUID(msb, lsb);
+    }
+
+    private static final List<UUID> getUUIDsFromByteArray(@NonNull byte[] uuidBytes) {
+        Set<UUID> uuids = new LinkedHashSet<>();
+        for (int off = 0; off < uuidBytes.length; off+=16) {
+            uuids.add(getUUIDFromByteArray(uuidBytes, off));
+        }
+        return new ArrayList<>(uuids);
+    }
+
+    private static final native byte[] getSupportedCryptoSchemesNative();
+
     private static final native boolean isCryptoSchemeSupportedNative(
             @NonNull byte[] uuid, @Nullable String mimeType, @SecurityLevel int securityLevel);
 
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index 064ac75..12b3e67 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -59,6 +59,7 @@
         "libsonivox",
         "android.hardware.cas@1.0",
         "android.hardware.cas.native@1.0",
+        "android.hardware.drm@1.3",
         "android.hidl.memory@1.0",
         "android.hidl.token@1.0-utils",
     ],
diff --git a/media/jni/android_media_MediaDrm.cpp b/media/jni/android_media_MediaDrm.cpp
index acda18e..f38a29c 100644
--- a/media/jni/android_media_MediaDrm.cpp
+++ b/media/jni/android_media_MediaDrm.cpp
@@ -27,6 +27,7 @@
 #include "jni.h"
 #include <nativehelper/JNIHelp.h>
 
+#include <android/hardware/drm/1.3/IDrmFactory.h>
 #include <binder/Parcel.h>
 #include <binder/PersistableBundle.h>
 #include <cutils/properties.h>
@@ -38,7 +39,7 @@
 #include <mediadrm/IDrm.h>
 
 using ::android::os::PersistableBundle;
-
+namespace drm = ::android::hardware::drm;
 
 namespace android {
 
@@ -971,6 +972,26 @@
     return level;
 }
 
+static jbyteArray android_media_MediaDrm_getSupportedCryptoSchemesNative(JNIEnv *env) {
+    std::vector<uint8_t> bv;
+    for (auto &factory : DrmUtils::MakeDrmFactories()) {
+        sp<drm::V1_3::IDrmFactory> factoryV1_3 = drm::V1_3::IDrmFactory::castFrom(factory);
+        if (factoryV1_3 == nullptr) {
+            continue;
+        }
+        factoryV1_3->getSupportedCryptoSchemes(
+            [&](const hardware::hidl_vec<hardware::hidl_array<uint8_t, 16>>& schemes) {
+                for (const auto &scheme : schemes) {
+                    bv.insert(bv.end(), scheme.data(), scheme.data() + scheme.size());
+                }
+            });
+    }
+
+    jbyteArray jUuidBytes = env->NewByteArray(bv.size());
+    env->SetByteArrayRegion(jUuidBytes, 0, bv.size(), reinterpret_cast<const jbyte *>(bv.data()));
+    return jUuidBytes;
+}
+
 static jboolean android_media_MediaDrm_isCryptoSchemeSupportedNative(
         JNIEnv *env, jobject /* thiz */, jbyteArray uuidObj, jstring jmimeType,
         jint jSecurityLevel) {
@@ -1941,6 +1962,9 @@
     { "native_setup", "(Ljava/lang/Object;[BLjava/lang/String;)V",
       (void *)android_media_MediaDrm_native_setup },
 
+    { "getSupportedCryptoSchemesNative", "()[B",
+      (void *)android_media_MediaDrm_getSupportedCryptoSchemesNative },
+
     { "isCryptoSchemeSupportedNative", "([BLjava/lang/String;I)Z",
       (void *)android_media_MediaDrm_isCryptoSchemeSupportedNative },
 
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index bcb76d7..c8532e0 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -119,11 +119,11 @@
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.phone.ScrimController;
 import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.StatusBarComponent;
 import com.android.systemui.statusbar.phone.StatusBarIconController;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
 import com.android.systemui.statusbar.phone.StatusBarNotificationActivityStarter;
 import com.android.systemui.statusbar.phone.StatusBarWindowController;
-import com.android.systemui.statusbar.phone.StatusBarWindowViewController;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
@@ -140,6 +140,7 @@
 import java.util.Optional;
 
 import javax.inject.Named;
+import javax.inject.Provider;
 
 import dagger.Lazy;
 
@@ -156,7 +157,6 @@
     private static final float FLING_SPEED_UP_FACTOR = 0.6f;
 
     private final ScrimController mScrimController;
-    private final StatusBarWindowViewController mStatusBarWindowViewController;
     private final LockscreenLockIconController mLockscreenLockIconController;
 
     private float mOpeningVelocity = DEFAULT_FLING_VELOCITY;
@@ -174,7 +174,6 @@
 
     private final Object mQueueLock = new Object();
     private final CarNavigationBarController mCarNavigationBarController;
-    private final Lazy<DrivingStateHelper> mDrivingStateHelperLazy;
     private final Lazy<PowerManagerHelper> mPowerManagerHelperLazy;
     private final CarServiceProvider mCarServiceProvider;
 
@@ -289,7 +288,6 @@
             NotificationListener notificationListener,
             ConfigurationController configurationController,
             StatusBarWindowController statusBarWindowController,
-            StatusBarWindowViewController statusBarWindowViewController,
             LockscreenLockIconController lockscreenLockIconController,
             DozeParameters dozeParameters,
             ScrimController scrimController,
@@ -302,6 +300,7 @@
             VolumeComponent volumeComponent,
             CommandQueue commandQueue,
             Optional<Recents> recents,
+            Provider<StatusBarComponent.Builder> statusBarComponentBuilder,
             PluginManager pluginManager,
             RemoteInputUriController remoteInputUriController,
             Optional<Divider> dividerOptional,
@@ -314,7 +313,6 @@
             DismissCallbackRegistry dismissCallbackRegistry,
             /* Car Settings injected components. */
             CarServiceProvider carServiceProvider,
-            Lazy<DrivingStateHelper> drivingStateHelperLazy,
             Lazy<PowerManagerHelper> powerManagerHelperLazy,
             Lazy<FullscreenUserSwitcher> fullscreenUserSwitcherLazy,
             CarNavigationBarController carNavigationBarController) {
@@ -368,7 +366,6 @@
                 notificationListener,
                 configurationController,
                 statusBarWindowController,
-                statusBarWindowViewController,
                 lockscreenLockIconController,
                 dozeParameters,
                 scrimController,
@@ -382,6 +379,7 @@
                 volumeComponent,
                 commandQueue,
                 recents,
+                statusBarComponentBuilder,
                 pluginManager,
                 remoteInputUriController,
                 dividerOptional,
@@ -392,11 +390,9 @@
                 viewMediatorCallback,
                 dismissCallbackRegistry);
         mScrimController = scrimController;
-        mStatusBarWindowViewController = statusBarWindowViewController;
         mLockscreenLockIconController = lockscreenLockIconController;
         mDeviceProvisionedController = deviceProvisionedController;
         mCarServiceProvider = carServiceProvider;
-        mDrivingStateHelperLazy = drivingStateHelperLazy;
         mPowerManagerHelperLazy = powerManagerHelperLazy;
         mFullscreenUserSwitcherLazy = fullscreenUserSwitcherLazy;
         mCarNavigationBarController = carNavigationBarController;
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java
index 6529868..eff60fa 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBarModule.java
@@ -78,11 +78,11 @@
 import com.android.systemui.statusbar.phone.NotificationGroupAlertTransferHelper;
 import com.android.systemui.statusbar.phone.NotificationGroupManager;
 import com.android.systemui.statusbar.phone.ScrimController;
+import com.android.systemui.statusbar.phone.StatusBarComponent;
 import com.android.systemui.statusbar.phone.StatusBarIconController;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
 import com.android.systemui.statusbar.phone.StatusBarNotificationActivityStarter;
 import com.android.systemui.statusbar.phone.StatusBarWindowController;
-import com.android.systemui.statusbar.phone.StatusBarWindowViewController;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
@@ -96,6 +96,7 @@
 import java.util.Optional;
 
 import javax.inject.Named;
+import javax.inject.Provider;
 import javax.inject.Singleton;
 
 import dagger.Lazy;
@@ -162,7 +163,6 @@
             NotificationListener notificationListener,
             ConfigurationController configurationController,
             StatusBarWindowController statusBarWindowController,
-            StatusBarWindowViewController statusBarWindowViewController,
             LockscreenLockIconController lockscreenLockIconController,
             DozeParameters dozeParameters,
             ScrimController scrimController,
@@ -175,6 +175,7 @@
             VolumeComponent volumeComponent,
             CommandQueue commandQueue,
             Optional<Recents> recentsOptional,
+            Provider<StatusBarComponent.Builder> statusBarComponentBuilder,
             PluginManager pluginManager,
             RemoteInputUriController remoteInputUriController,
             Optional<Divider> dividerOptional,
@@ -186,7 +187,6 @@
             ViewMediatorCallback viewMediatorCallback,
             DismissCallbackRegistry dismissCallbackRegistry,
             CarServiceProvider carServiceProvider,
-            Lazy<DrivingStateHelper> drivingStateHelperLazy,
             Lazy<PowerManagerHelper> powerManagerHelperLazy,
             Lazy<FullscreenUserSwitcher> fullscreenUserSwitcherLazy,
             CarNavigationBarController carNavigationBarController) {
@@ -240,7 +240,6 @@
                 notificationListener,
                 configurationController,
                 statusBarWindowController,
-                statusBarWindowViewController,
                 lockscreenLockIconController,
                 dozeParameters,
                 scrimController,
@@ -253,6 +252,7 @@
                 volumeComponent,
                 commandQueue,
                 recentsOptional,
+                statusBarComponentBuilder,
                 pluginManager,
                 remoteInputUriController,
                 dividerOptional,
@@ -263,7 +263,6 @@
                 viewMediatorCallback,
                 dismissCallbackRegistry,
                 carServiceProvider,
-                drivingStateHelperLazy,
                 powerManagerHelperLazy,
                 fullscreenUserSwitcherLazy,
                 carNavigationBarController);
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/DrivingStateHelper.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/DrivingStateHelper.java
deleted file mode 100644
index 60934ab..0000000
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/DrivingStateHelper.java
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.statusbar.car;
-
-import android.car.Car;
-import android.car.drivingstate.CarDrivingStateEvent;
-import android.car.drivingstate.CarDrivingStateManager;
-import android.car.drivingstate.CarDrivingStateManager.CarDrivingStateEventListener;
-import android.util.Log;
-
-import androidx.annotation.NonNull;
-
-import com.android.systemui.car.CarServiceProvider;
-
-import javax.inject.Inject;
-import javax.inject.Singleton;
-
-/**
- * Helper class for connecting to the {@link CarDrivingStateManager} and listening for driving state
- * changes.
- */
-@Singleton
-public class DrivingStateHelper {
-    public static final String TAG = "DrivingStateHelper";
-
-    private final CarServiceProvider mCarServiceProvider;
-
-    private CarDrivingStateManager mDrivingStateManager;
-    private CarDrivingStateEventListener mDrivingStateHandler;
-
-    @Inject
-    public DrivingStateHelper(CarServiceProvider carServiceProvider) {
-        mCarServiceProvider = carServiceProvider;
-    }
-
-    /**
-     * Sets the {@link CarDrivingStateEventListener}. Should be set before calling {@link
-     * #connectToCarService()}.
-     */
-    public void setCarDrivingStateEventListener(
-            @NonNull CarDrivingStateEventListener carDrivingStateEventListener) {
-        mDrivingStateHandler = carDrivingStateEventListener;
-    }
-
-    /**
-     * Queries {@link CarDrivingStateManager} for current driving state. Returns {@code true} if car
-     * is idling or moving, {@code false} otherwise.
-     */
-    public boolean isCurrentlyDriving() {
-        if (mDrivingStateManager == null) {
-            return false;
-        }
-        CarDrivingStateEvent currentState = mDrivingStateManager.getCurrentCarDrivingState();
-        if (currentState != null) {
-            return currentState.eventValue == CarDrivingStateEvent.DRIVING_STATE_IDLING
-                    || currentState.eventValue == CarDrivingStateEvent.DRIVING_STATE_MOVING;
-        }
-        return false; // Default to false.
-    }
-
-    /**
-     * Establishes connection with the Car service.
-     */
-    public void connectToCarService() {
-        mCarServiceProvider.addListener(mCarServiceLifecycleListener);
-    }
-
-    private final CarServiceProvider.CarServiceOnConnectedListener mCarServiceLifecycleListener =
-            car -> {
-                logD("Car Service connected");
-                mDrivingStateManager = (CarDrivingStateManager) car.getCarManager(
-                        Car.CAR_DRIVING_STATE_SERVICE);
-                if (mDrivingStateManager != null) {
-                    mDrivingStateManager.registerListener(mDrivingStateHandler);
-                    mDrivingStateHandler.onDrivingStateChanged(
-                            mDrivingStateManager.getCurrentCarDrivingState());
-                } else {
-                    Log.e(TAG, "CarDrivingStateService service not available");
-                }
-            };
-
-    private void logD(String message) {
-        if (Log.isLoggable(TAG, Log.DEBUG)) {
-            Log.d(TAG, message);
-        }
-    }
-}
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
index 7500bcd..0a5f80f 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
@@ -21,6 +21,7 @@
 import static android.os.UserManager.DISALLOW_ADD_USER;
 import static android.os.UserManager.SWITCHABILITY_STATUS_OK;
 
+import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.AlertDialog;
 import android.app.AlertDialog.Builder;
@@ -266,7 +267,10 @@
 
                 if (userRecord.mIsStartGuestSession) {
                     notifyUserSelected(userRecord);
-                    mCarUserManagerHelper.startGuestSession(mGuestName);
+                    UserInfo guest = createNewOrFindExistingGuest(mContext);
+                    if (guest != null) {
+                        mCarUserManagerHelper.switchToUser(guest);
+                    }
                     return;
                 }
 
@@ -381,6 +385,24 @@
             return circleIcon;
         }
 
+        /**
+         * Finds the existing Guest user, or creates one if it doesn't exist.
+         * @param context App context
+         * @return UserInfo representing the Guest user
+         */
+        @Nullable
+        public UserInfo createNewOrFindExistingGuest(Context context) {
+            // CreateGuest will return null if a guest already exists.
+            UserInfo newGuest = mUserManager.createGuest(context, mGuestName);
+            if (newGuest != null) {
+                new UserIconProvider().assignDefaultIcon(
+                        mUserManager, context.getResources(), newGuest);
+                return newGuest;
+            }
+
+            return mUserManager.findCurrentGuestUser();
+        }
+
         @Override
         public void onClick(DialogInterface dialog, int which) {
             if (which == BUTTON_POSITIVE) {
diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserIconProvider.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserIconProvider.java
index 9464eab..9018290 100644
--- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserIconProvider.java
+++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/UserIconProvider.java
@@ -88,7 +88,7 @@
      * @param userInfo User whose avatar is set to default icon.
      * @return Bitmap of the user icon.
      */
-    private Bitmap assignDefaultIcon(
+    public Bitmap assignDefaultIcon(
             UserManager userManager, Resources resources, UserInfo userInfo) {
         Bitmap bitmap = userInfo.isGuest()
                 ? getGuestUserDefaultIcon(resources)
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
index 785dd56..96aee51 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
@@ -44,8 +44,8 @@
     private final CachedBluetoothDeviceManager mDeviceManager;
 
     static final ParcelUuid[] SINK_UUIDS = {
-        BluetoothUuid.AudioSink,
-        BluetoothUuid.AdvAudioDist,
+        BluetoothUuid.A2DP_SINK,
+        BluetoothUuid.ADV_AUDIO_DIST,
     };
 
     static final String NAME = "A2DP";
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java
index 4ce9d3e..55765dd 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpSinkProfile.java
@@ -40,8 +40,8 @@
     private final CachedBluetoothDeviceManager mDeviceManager;
 
     static final ParcelUuid[] SRC_UUIDS = {
-        BluetoothUuid.AudioSource,
-        BluetoothUuid.AdvAudioDist,
+        BluetoothUuid.A2DP_SOURCE,
+        BluetoothUuid.ADV_AUDIO_DIST,
     };
 
     static final String NAME = "A2DPSink";
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothDeviceFilter.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothDeviceFilter.java
index 8dec86a..b8ad321 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothDeviceFilter.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothDeviceFilter.java
@@ -22,6 +22,8 @@
 import android.os.ParcelUuid;
 import android.util.Log;
 
+import com.android.internal.util.ArrayUtils;
+
 /**
  * BluetoothDeviceFilter contains a static method that returns a
  * Filter object that returns whether or not the BluetoothDevice
@@ -130,7 +132,7 @@
         @Override
         boolean matches(ParcelUuid[] uuids, BluetoothClass btClass) {
             if (uuids != null) {
-                if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.ObexObjectPush)) {
+                if (ArrayUtils.contains(uuids, BluetoothUuid.OBEX_OBJECT_PUSH)) {
                     return true;
                 }
             }
@@ -144,7 +146,7 @@
         @Override
         boolean matches(ParcelUuid[] uuids, BluetoothClass btClass) {
             if (uuids != null) {
-                if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.PANU)) {
+                if (ArrayUtils.contains(uuids, BluetoothUuid.PANU)) {
                     return true;
                 }
             }
@@ -158,7 +160,7 @@
         @Override
         boolean matches(ParcelUuid[] uuids, BluetoothClass btClass) {
             if (uuids != null) {
-                if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.NAP)) {
+                if (ArrayUtils.contains(uuids, BluetoothUuid.NAP)) {
                     return true;
                 }
             }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index 833c4ac..0666596 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -35,6 +35,7 @@
 
 import androidx.annotation.VisibleForTesting;
 
+import com.android.internal.util.ArrayUtils;
 import com.android.settingslib.R;
 import com.android.settingslib.Utils;
 
@@ -685,9 +686,9 @@
         ParcelUuid[] uuids = mDevice.getUuids();
 
         long timeout = MAX_UUID_DELAY_FOR_AUTO_CONNECT;
-        if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.Hogp)) {
+        if (ArrayUtils.contains(uuids, BluetoothUuid.HOGP)) {
             timeout = MAX_HOGP_DELAY_FOR_AUTO_CONNECT;
-        } else if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.HearingAid)) {
+        } else if (ArrayUtils.contains(uuids, BluetoothUuid.HEARING_AID)) {
             timeout = MAX_HEARING_AIDS_DELAY_FOR_AUTO_CONNECT;
         }
 
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
index c1933fd..9f7b718 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
@@ -45,7 +45,7 @@
 
     static final ParcelUuid[] UUIDS = {
         BluetoothUuid.HSP,
-        BluetoothUuid.Handsfree,
+        BluetoothUuid.HFP,
     };
 
     static final String NAME = "HEADSET";
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java
index 4bdbc31..860b77d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HfpClientProfile.java
@@ -44,7 +44,7 @@
 
     static final ParcelUuid[] SRC_UUIDS = {
         BluetoothUuid.HSP_AG,
-        BluetoothUuid.Handsfree_AG,
+        BluetoothUuid.HFP_AG,
     };
 
     static final String NAME = "HEADSET_CLIENT";
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
index 29c6d71..ae2acbe 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java
@@ -30,8 +30,8 @@
 import android.bluetooth.BluetoothPan;
 import android.bluetooth.BluetoothPbap;
 import android.bluetooth.BluetoothPbapClient;
-import android.bluetooth.BluetoothSap;
 import android.bluetooth.BluetoothProfile;
+import android.bluetooth.BluetoothSap;
 import android.bluetooth.BluetoothUuid;
 import android.content.Context;
 import android.content.Intent;
@@ -40,6 +40,7 @@
 
 import androidx.annotation.VisibleForTesting;
 
+import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.CollectionUtils;
 
 import java.util.ArrayList;
@@ -471,43 +472,40 @@
         }
 
         if (mHeadsetProfile != null) {
-            if ((BluetoothUuid.isUuidPresent(localUuids, BluetoothUuid.HSP_AG) &&
-                    BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.HSP)) ||
-                    (BluetoothUuid.isUuidPresent(localUuids, BluetoothUuid.Handsfree_AG) &&
-                            BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.Handsfree))) {
+            if ((ArrayUtils.contains(localUuids, BluetoothUuid.HSP_AG)
+                    && ArrayUtils.contains(uuids, BluetoothUuid.HSP))
+                    || (ArrayUtils.contains(localUuids, BluetoothUuid.HFP_AG)
+                    && ArrayUtils.contains(uuids, BluetoothUuid.HFP))) {
                 profiles.add(mHeadsetProfile);
                 removedProfiles.remove(mHeadsetProfile);
             }
         }
 
         if ((mHfpClientProfile != null) &&
-                BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.Handsfree_AG) &&
-                BluetoothUuid.isUuidPresent(localUuids, BluetoothUuid.Handsfree)) {
+                ArrayUtils.contains(uuids, BluetoothUuid.HFP_AG)
+                && ArrayUtils.contains(localUuids, BluetoothUuid.HFP)) {
             profiles.add(mHfpClientProfile);
             removedProfiles.remove(mHfpClientProfile);
         }
 
-        if (BluetoothUuid.containsAnyUuid(uuids, A2dpProfile.SINK_UUIDS) &&
-            mA2dpProfile != null) {
+        if (BluetoothUuid.containsAnyUuid(uuids, A2dpProfile.SINK_UUIDS) && mA2dpProfile != null) {
             profiles.add(mA2dpProfile);
             removedProfiles.remove(mA2dpProfile);
         }
 
-        if (BluetoothUuid.containsAnyUuid(uuids, A2dpSinkProfile.SRC_UUIDS) &&
-                mA2dpSinkProfile != null) {
+        if (BluetoothUuid.containsAnyUuid(uuids, A2dpSinkProfile.SRC_UUIDS)
+                && mA2dpSinkProfile != null) {
                 profiles.add(mA2dpSinkProfile);
                 removedProfiles.remove(mA2dpSinkProfile);
         }
 
-        if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.ObexObjectPush) &&
-            mOppProfile != null) {
+        if (ArrayUtils.contains(uuids, BluetoothUuid.OBEX_OBJECT_PUSH) && mOppProfile != null) {
             profiles.add(mOppProfile);
             removedProfiles.remove(mOppProfile);
         }
 
-        if ((BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.Hid) ||
-             BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.Hogp)) &&
-            mHidProfile != null) {
+        if ((ArrayUtils.contains(uuids, BluetoothUuid.HID)
+                || ArrayUtils.contains(uuids, BluetoothUuid.HOGP)) && mHidProfile != null) {
             profiles.add(mHidProfile);
             removedProfiles.remove(mHidProfile);
         }
@@ -520,8 +518,8 @@
 
         if(isPanNapConnected)
             if(DEBUG) Log.d(TAG, "Valid PAN-NAP connection exists.");
-        if ((BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.NAP) &&
-            mPanProfile != null) || isPanNapConnected) {
+        if ((ArrayUtils.contains(uuids, BluetoothUuid.NAP) && mPanProfile != null)
+                || isPanNapConnected) {
             profiles.add(mPanProfile);
             removedProfiles.remove(mPanProfile);
         }
@@ -545,20 +543,18 @@
             removedProfiles.remove(mMapClientProfile);
         }
 
-        if ((mPbapClientProfile != null) &&
-                BluetoothUuid.isUuidPresent(localUuids, BluetoothUuid.PBAP_PCE) &&
-                BluetoothUuid.containsAnyUuid(uuids, PbapClientProfile.SRC_UUIDS)) {
+        if ((mPbapClientProfile != null) && ArrayUtils.contains(localUuids, BluetoothUuid.PBAP_PCE)
+                && BluetoothUuid.containsAnyUuid(uuids, PbapClientProfile.SRC_UUIDS)) {
             profiles.add(mPbapClientProfile);
             removedProfiles.remove(mPbapClientProfile);
         }
 
-        if (BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.HearingAid) &&
-            mHearingAidProfile != null) {
+        if (ArrayUtils.contains(uuids, BluetoothUuid.HEARING_AID) && mHearingAidProfile != null) {
             profiles.add(mHearingAidProfile);
             removedProfiles.remove(mHearingAidProfile);
         }
 
-        if (mSapProfile != null && BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.SAP)) {
+        if (mSapProfile != null && ArrayUtils.contains(uuids, BluetoothUuid.SAP)) {
             profiles.add(mSapProfile);
             removedProfiles.remove(mSapProfile);
         }
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java
index 17104e4..d91226e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PbapServerProfile.java
@@ -46,7 +46,7 @@
     // The UUIDs indicate that remote device might access pbap server
     static final ParcelUuid[] PBAB_CLIENT_UUIDS = {
         BluetoothUuid.HSP,
-        BluetoothUuid.Handsfree,
+        BluetoothUuid.HFP,
         BluetoothUuid.PBAP_PCE
     };
 
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManagerTest.java
index 5d5872e..fd5b053 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManagerTest.java
@@ -132,7 +132,7 @@
         mShadowBluetoothAdapter.setSupportedProfiles(generateList(
                 new int[] {BluetoothProfile.HID_HOST}));
         mProfileManager.updateLocalProfiles();
-        ParcelUuid[] uuids = new ParcelUuid[]{BluetoothUuid.Hid};
+        ParcelUuid[] uuids = new ParcelUuid[]{BluetoothUuid.HID};
         ParcelUuid[] localUuids = new ParcelUuid[]{};
         List<LocalBluetoothProfile> profiles = new ArrayList<>();
         List<LocalBluetoothProfile> removedProfiles = new ArrayList<>();
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
index 1527de1..1f68742 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/GlobalSettings.java
@@ -70,5 +70,7 @@
         Settings.Global.CHARGING_VIBRATION_ENABLED,
         Settings.Global.AWARE_ALLOWED,
         Settings.Global.NOTIFICATION_BUBBLES,
+        Settings.Global.CUSTOM_BUGREPORT_HANDLER_APP,
+        Settings.Global.CUSTOM_BUGREPORT_HANDLER_USER,
     };
 }
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
index 4a0ed6f..3d278db 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
@@ -149,5 +149,7 @@
         VALIDATORS.put(
                 Global.POWER_BUTTON_VERY_LONG_PRESS, new InclusiveIntegerRangeValidator(0, 1));
         VALIDATORS.put(Global.NOTIFICATION_BUBBLES, BOOLEAN_VALIDATOR);
+        VALIDATORS.put(Global.CUSTOM_BUGREPORT_HANDLER_APP, ANY_STRING_VALIDATOR);
+        VALIDATORS.put(Global.CUSTOM_BUGREPORT_HANDLER_USER, ANY_INTEGER_VALIDATOR);
     }
 }
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 540726b..c23a494 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -268,6 +268,7 @@
                     Settings.Global.ERROR_LOGCAT_PREFIX,
                     Settings.Global.EUICC_PROVISIONED,
                     Settings.Global.EUICC_SUPPORTED_COUNTRIES,
+                    Settings.Global.EUICC_UNSUPPORTED_COUNTRIES,
                     Settings.Global.EUICC_FACTORY_RESET_TIMEOUT_MILLIS,
                     Settings.Global.EUICC_REMOVING_INVISIBLE_PROFILES_TIMEOUT_MILLIS,
                     Settings.Global.FANCY_IME_ANIMATIONS,
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 1a658f4..7f1d528 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -215,9 +215,12 @@
     <!-- Permission required for CTS test - CrossProfileAppsHostSideTest -->
     <uses-permission android:name="android.permission.INTERACT_ACROSS_PROFILES"/>
 
-    <!-- Permission requried for CTS test - UiModeManagerTest -->
+    <!-- Permission required for CTS test - UiModeManagerTest -->
     <uses-permission android:name="android.permission.ENTER_CAR_MODE_PRIORITIZED"/>
 
+      <!-- Permission required for CTS test - CarModeInCallServiceTest -->
+    <uses-permission android:name="android.permission.CONTROL_INCALL_EXPERIENCE"/>
+
     <application android:label="@string/app_label"
                 android:theme="@android:style/Theme.DeviceDefault.DayNight"
                 android:defaultToDeviceProtectedStorage="true"
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 0a671d9..6f68038 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -288,6 +288,11 @@
             android:exported="false"
             android:permission="com.android.systemui.permission.SELF" />
 
+        <service android:name=".assist.AssistHandleService"
+            android:exported="true"
+            android:enabled="false"
+        />
+
         <!-- started from PhoneWindowManager
              TODO: Should have an android:permission attribute -->
         <service android:name=".screenshot.TakeScreenshotService"
diff --git a/packages/SystemUI/res/values/arrays_tv.xml b/packages/SystemUI/res/values/arrays_tv.xml
index 1fe6141..95716c8 100644
--- a/packages/SystemUI/res/values/arrays_tv.xml
+++ b/packages/SystemUI/res/values/arrays_tv.xml
@@ -36,5 +36,6 @@
 
     <string-array name="audio_recording_disclosure_exempt_apps" translatable="false">
       <item>com.google.android.katniss</item>
+      <item>com.google.android.apps.mediashell</item>
     </string-array>
 </resources>
diff --git a/packages/SystemUI/src/com/android/systemui/ForegroundServiceController.java b/packages/SystemUI/src/com/android/systemui/ForegroundServiceController.java
index c1a23c8..41dd5bbf 100644
--- a/packages/SystemUI/src/com/android/systemui/ForegroundServiceController.java
+++ b/packages/SystemUI/src/com/android/systemui/ForegroundServiceController.java
@@ -37,7 +37,7 @@
  */
 @Singleton
 public class ForegroundServiceController {
-    private static final int[] APP_OPS = new int[] {AppOpsManager.OP_CAMERA,
+    public static final int[] APP_OPS = new int[] {AppOpsManager.OP_CAMERA,
             AppOpsManager.OP_SYSTEM_ALERT_WINDOW,
             AppOpsManager.OP_RECORD_AUDIO,
             AppOpsManager.OP_COARSE_LOCATION,
@@ -139,6 +139,8 @@
             }
         }
 
+        // TODO: (b/145659174) remove when moving to NewNotifPipeline. Replaced by
+        //  ForegroundCoordinator
         // Update appOp if there's an associated pending or visible notification:
         final String foregroundKey = getStandardLayoutKey(userId, packageName);
         if (foregroundKey != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java b/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java
index b983966..8105faa 100644
--- a/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java
+++ b/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener.java
@@ -27,6 +27,8 @@
 import com.android.internal.statusbar.NotificationVisibility;
 import com.android.systemui.statusbar.notification.NotificationEntryListener;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotifCollection;
+import com.android.systemui.statusbar.notification.collection.NotifCollectionListener;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 
 import javax.inject.Inject;
@@ -46,9 +48,13 @@
     @Inject
     public ForegroundServiceNotificationListener(Context context,
             ForegroundServiceController foregroundServiceController,
-            NotificationEntryManager notificationEntryManager) {
+            NotificationEntryManager notificationEntryManager,
+            NotifCollection notifCollection) {
         mContext = context;
         mForegroundServiceController = foregroundServiceController;
+
+        // TODO: (b/145659174) remove mEntryManager when moving to NewNotifPipeline. Replaced by
+        //  ForegroundCoordinator
         mEntryManager = notificationEntryManager;
         mEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
             @Override
@@ -69,8 +75,24 @@
                 removeNotification(entry.getSbn());
             }
         });
-
         mEntryManager.addNotificationLifetimeExtender(new ForegroundServiceLifetimeExtender());
+
+        notifCollection.addCollectionListener(new NotifCollectionListener() {
+            @Override
+            public void onEntryAdded(NotificationEntry entry) {
+                addNotification(entry, entry.getImportance());
+            }
+
+            @Override
+            public void onEntryUpdated(NotificationEntry entry) {
+                updateNotification(entry, entry.getImportance());
+            }
+
+            @Override
+            public void onEntryRemoved(NotificationEntry entry, int reason, boolean removedByUser) {
+                removeNotification(entry.getSbn());
+            }
+        });
     }
 
     /**
@@ -152,6 +174,8 @@
                 true /* create if not found */);
     }
 
+    // TODO: (b/145659174) remove when moving to NewNotifPipeline. Replaced by
+    //  ForegroundCoordinator
     private void tagForeground(NotificationEntry entry) {
         final StatusBarNotification sbn = entry.getSbn();
         ArraySet<Integer> activeOps = mForegroundServiceController.getAppOps(
diff --git a/packages/SystemUI/src/com/android/systemui/ForegroundServicesUserState.java b/packages/SystemUI/src/com/android/systemui/ForegroundServicesUserState.java
index a8ae654..2ef46dc 100644
--- a/packages/SystemUI/src/com/android/systemui/ForegroundServicesUserState.java
+++ b/packages/SystemUI/src/com/android/systemui/ForegroundServicesUserState.java
@@ -24,7 +24,7 @@
 /**
  * Struct to track relevant packages and notifications for a userid's foreground services.
  */
-class ForegroundServicesUserState {
+public class ForegroundServicesUserState {
     // shelf life of foreground services before they go bad
     private static final long FG_SERVICE_GRACE_MILLIS = 5000;
 
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistHandleService.kt b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleService.kt
new file mode 100644
index 0000000..9ceafc6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistHandleService.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.assist
+
+import android.app.Service
+import android.content.Intent
+import android.os.IBinder
+import dagger.Lazy
+import javax.inject.Inject
+
+class AssistHandleService @Inject constructor(private val assistManager: Lazy<AssistManager>)
+    : Service() {
+
+    private val binder = object : IAssistHandleService.Stub() {
+        override fun requestAssistHandles() {
+            assistManager.get().requestAssistHandles()
+        }
+    }
+
+    override fun onBind(intent: Intent?): IBinder? {
+        return binder
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistModule.java b/packages/SystemUI/src/com/android/systemui/assist/AssistModule.java
index 6f5a17d..96939b01 100644
--- a/packages/SystemUI/src/com/android/systemui/assist/AssistModule.java
+++ b/packages/SystemUI/src/com/android/systemui/assist/AssistModule.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.assist;
 
+import android.app.Service;
 import android.content.Context;
 import android.os.Handler;
 import android.os.HandlerThread;
@@ -33,8 +34,11 @@
 import javax.inject.Named;
 import javax.inject.Singleton;
 
+import dagger.Binds;
 import dagger.Module;
 import dagger.Provides;
+import dagger.multibindings.ClassKey;
+import dagger.multibindings.IntoMap;
 
 /** Module for dagger injections related to the Assistant. */
 @Module
@@ -87,4 +91,9 @@
     static Clock provideSystemClock() {
         return SystemClock::uptimeMillis;
     }
+
+    @Binds
+    @IntoMap
+    @ClassKey(AssistHandleService.class)
+    abstract Service bindAssistHandleService(AssistHandleService assistHandleService);
 }
diff --git a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl b/packages/SystemUI/src/com/android/systemui/assist/IAssistHandleService.aidl
similarity index 63%
copy from wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl
copy to packages/SystemUI/src/com/android/systemui/assist/IAssistHandleService.aidl
index 007ec94..ef07d9d 100644
--- a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl
+++ b/packages/SystemUI/src/com/android/systemui/assist/IAssistHandleService.aidl
@@ -1,5 +1,5 @@
 /**
- * Copyright (c) 2014, The Android Open Source Project
+ * Copyright (c) 2009, The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,6 +14,11 @@
  * limitations under the License.
  */
 
-package android.net.wifi;
+package com.android.systemui.assist;
 
-parcelable WifiActivityEnergyInfo;
+/** Interface implemented by AssisthandleService and called by on-device intelligence. */
+interface IAssistHandleService {
+
+    /** Request that the Assistant Handles be shown. */
+    oneway void requestAssistHandles();
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
index ef2868b..f3a7ca9 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
@@ -199,7 +199,7 @@
 
         mExpandedView = (BubbleExpandedView) inflater.inflate(
                 R.layout.bubble_expanded_view, stackView, false /* attachToRoot */);
-        mExpandedView.setBubble(this, stackView, mAppName);
+        mExpandedView.setBubble(this, stackView);
 
         mInflated = true;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 1025321..2966aef 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -694,10 +694,11 @@
         @Override
         public void onPendingEntryAdded(NotificationEntry entry) {
             boolean previouslyUserCreated = mUserCreatedBubbles.contains(entry.getKey());
-            BubbleExperimentConfig.adjustForExperiments(mContext, entry, previouslyUserCreated);
+            boolean wasAdjusted = BubbleExperimentConfig.adjustForExperiments(
+                    mContext, entry, previouslyUserCreated);
 
             if (mNotificationInterruptionStateProvider.shouldBubbleUp(entry)
-                    && canLaunchInActivityView(mContext, entry)) {
+                    && (canLaunchInActivityView(mContext, entry) || wasAdjusted)) {
                 updateBubble(entry);
             }
         }
@@ -705,10 +706,11 @@
         @Override
         public void onPreEntryUpdated(NotificationEntry entry) {
             boolean previouslyUserCreated = mUserCreatedBubbles.contains(entry.getKey());
-            BubbleExperimentConfig.adjustForExperiments(mContext, entry, previouslyUserCreated);
+            boolean wasAdjusted = BubbleExperimentConfig.adjustForExperiments(
+                    mContext, entry, previouslyUserCreated);
 
             boolean shouldBubble = mNotificationInterruptionStateProvider.shouldBubbleUp(entry)
-                    && canLaunchInActivityView(mContext, entry);
+                    && (canLaunchInActivityView(mContext, entry) || wasAdjusted);
             if (!shouldBubble && mBubbleData.hasBubbleWithKey(entry.getKey())) {
                 // It was previously a bubble but no longer a bubble -- lets remove it
                 removeBubble(entry.getKey(), DISMISS_NO_LONGER_BUBBLE);
@@ -1016,11 +1018,6 @@
         PendingIntent intent = entry.getBubbleMetadata() != null
                 ? entry.getBubbleMetadata().getIntent()
                 : null;
-        return canLaunchIntentInActivityView(context, entry, intent);
-    }
-
-    static boolean canLaunchIntentInActivityView(Context context, NotificationEntry entry,
-            PendingIntent intent) {
         if (intent == null) {
             Log.w(TAG, "Unable to create bubble -- no intent: " + entry.getKey());
             return false;
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
index a69ff00..efc955d 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
@@ -31,15 +31,12 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.graphics.Color;
 import android.graphics.Insets;
 import android.graphics.Point;
 import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
 import android.graphics.drawable.ShapeDrawable;
 import android.os.RemoteException;
 import android.service.notification.StatusBarNotification;
@@ -102,9 +99,7 @@
     private int mExpandedViewTouchSlop;
 
     private Bubble mBubble;
-    private PackageManager mPm;
     private String mAppName;
-    private Drawable mAppIcon;
 
     private BubbleController mBubbleController = Dependency.get(BubbleController.class);
     private WindowManager mWindowManager;
@@ -212,7 +207,6 @@
     public BubbleExpandedView(Context context, AttributeSet attrs, int defStyleAttr,
             int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
-        mPm = context.getPackageManager();
         mDisplaySize = new Point();
         mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
         // Get the real size -- this includes screen decorations (notches, statusbar, navbar).
@@ -350,29 +344,14 @@
     /**
      * Sets the bubble used to populate this view.
      */
-    public void setBubble(Bubble bubble, BubbleStackView stackView, String appName) {
+    public void setBubble(Bubble bubble, BubbleStackView stackView) {
         if (DEBUG_BUBBLE_EXPANDED_VIEW) {
             Log.d(TAG, "setBubble: bubble=" + (bubble != null ? bubble.getKey() : "null"));
         }
-
         mStackView = stackView;
         mBubble = bubble;
-        mAppName = appName;
+        mAppName = bubble.getAppName();
 
-        try {
-            ApplicationInfo info = mPm.getApplicationInfo(
-                    bubble.getPackageName(),
-                    PackageManager.MATCH_UNINSTALLED_PACKAGES
-                            | PackageManager.MATCH_DISABLED_COMPONENTS
-                            | PackageManager.MATCH_DIRECT_BOOT_UNAWARE
-                            | PackageManager.MATCH_DIRECT_BOOT_AWARE);
-            mAppIcon = mPm.getApplicationIcon(info);
-        } catch (PackageManager.NameNotFoundException e) {
-            // Do nothing.
-        }
-        if (mAppIcon == null) {
-            mAppIcon = mPm.getDefaultActivityIcon();
-        }
         applyThemeAttrs();
         showSettingsIcon();
         updateExpandedView();
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java
index 17d6737..fd7fff4 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExperimentConfig.java
@@ -21,7 +21,6 @@
 import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_MANIFEST;
 import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED;
 
-import static com.android.systemui.bubbles.BubbleController.canLaunchIntentInActivityView;
 import static com.android.systemui.bubbles.BubbleDebugConfig.DEBUG_EXPERIMENTS;
 import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_BUBBLES;
 import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
@@ -62,7 +61,7 @@
     private static final boolean ALLOW_ANY_NOTIF_TO_BUBBLE_DEFAULT = false;
 
     private static final String ALLOW_MESSAGE_NOTIFS_TO_BUBBLE = "allow_message_notifs_to_bubble";
-    private static final boolean ALLOW_MESSAGE_NOTIFS_TO_BUBBLE_DEFAULT = false;
+    private static final boolean ALLOW_MESSAGE_NOTIFS_TO_BUBBLE_DEFAULT = true;
 
     private static final String ALLOW_SHORTCUTS_TO_BUBBLE = "allow_shortcuts_to_bubble";
     private static final boolean ALLOW_SHORTCUT_TO_BUBBLE_DEFAULT = false;
@@ -107,8 +106,10 @@
      * If {@link #allowAnyNotifToBubble(Context)} is true, this method creates and adds
      * {@link android.app.Notification.BubbleMetadata} to the notification entry as long as
      * the notification has necessary info for BubbleMetadata.
+     *
+     * @return whether an adjustment was made.
      */
-    static void adjustForExperiments(Context context, NotificationEntry entry,
+    static boolean adjustForExperiments(Context context, NotificationEntry entry,
             boolean previouslyUserCreated) {
         Notification.BubbleMetadata metadata = null;
         boolean addedMetadata = false;
@@ -176,7 +177,9 @@
                 Log.d(TAG, "Setting FLAG_BUBBLE for: " + entry.getKey());
             }
             entry.setFlagBubble(true);
+            return true;
         }
+        return addedMetadata;
     }
 
     static Notification.BubbleMetadata createFromNotif(Context context, NotificationEntry entry) {
@@ -193,7 +196,7 @@
                     ? notification.getLargeIcon()
                     : notification.getSmallIcon();
         }
-        if (canLaunchIntentInActivityView(context, entry, intent)) {
+        if (intent != null) {
             return new Notification.BubbleMetadata.Builder()
                     .setDesiredHeight(BUBBLE_HEIGHT)
                     .setIcon(icon)
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 9bd729e..442313d 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -35,7 +35,9 @@
 import com.android.systemui.statusbar.notification.people.PeopleHubModule;
 import com.android.systemui.statusbar.phone.KeyguardLiftController;
 import com.android.systemui.statusbar.phone.StatusBar;
+import com.android.systemui.statusbar.phone.StatusBarComponent;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
+import com.android.systemui.util.concurrency.ConcurrencyModule;
 import com.android.systemui.util.sensors.AsyncSensorManager;
 import com.android.systemui.util.time.SystemClock;
 import com.android.systemui.util.time.SystemClockImpl;
@@ -52,7 +54,9 @@
  * implementation.
  */
 @Module(includes = {AssistModule.class,
-                    PeopleHubModule.class})
+                    ConcurrencyModule.class,
+                    PeopleHubModule.class},
+        subcomponents = {StatusBarComponent.class})
 public abstract class SystemUIModule {
 
     @Binds
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIRootComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIRootComponent.java
index e9265749..e50e0fe 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIRootComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIRootComponent.java
@@ -25,7 +25,6 @@
 import com.android.systemui.SystemUIAppComponentFactory;
 import com.android.systemui.SystemUIFactory;
 import com.android.systemui.fragments.FragmentService;
-import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.policy.ConfigurationController;
 import com.android.systemui.util.InjectionInflationController;
 
@@ -73,12 +72,6 @@
     Dependency.DependencyInjector createDependency();
 
     /**
-     * Injects the StatusBar.
-     */
-    @Singleton
-    StatusBar.StatusBarInjector getStatusBarInjector();
-
-    /**
      * FragmentCreator generates all Fragments that need injection.
      */
     @Singleton
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/Background.java b/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/Background.java
new file mode 100644
index 0000000..141c901
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/Background.java
@@ -0,0 +1,30 @@
+/*
+ * 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.dagger.qualifiers;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+
+import javax.inject.Qualifier;
+
+@Qualifier
+@Documented
+@Retention(RUNTIME)
+public @interface Background {
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/Main.java b/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/Main.java
new file mode 100644
index 0000000..7b09774
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dagger/qualifiers/Main.java
@@ -0,0 +1,30 @@
+/*
+ * 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.dagger.qualifiers;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+
+import javax.inject.Qualifier;
+
+@Qualifier
+@Documented
+@Retention(RUNTIME)
+public @interface Main {
+}
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index b5845947..63a7771 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -669,7 +669,10 @@
                         // Take an "interactive" bugreport.
                         MetricsLogger.action(mContext,
                                 MetricsEvent.ACTION_BUGREPORT_FROM_POWER_MENU_INTERACTIVE);
-                        ActivityManager.getService().requestInteractiveBugReport();
+                        if (!ActivityManager.getService().launchBugReportHandlerApp()) {
+                            Log.w(TAG, "Bugreport handler could not be launched");
+                            ActivityManager.getService().requestInteractiveBugReport();
+                        }
                     } catch (RemoteException e) {
                     }
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java
index 99c55f1..f815b5d 100644
--- a/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/glwallpaper/ImageRevealHelper.java
@@ -79,12 +79,6 @@
         });
     }
 
-    private void animate() {
-        mAnimator.cancel();
-        mAnimator.setFloatValues(mReveal, mAwake ? MAX_REVEAL : MIN_REVEAL);
-        mAnimator.start();
-    }
-
     public float getReveal() {
         return mReveal;
     }
@@ -93,8 +87,8 @@
         if (DEBUG) {
             Log.d(TAG, "updateAwake: awake=" + awake + ", duration=" + duration);
         }
+        mAnimator.cancel();
         mAwake = awake;
-        mAnimator.setDuration(duration);
         if (duration == 0) {
             // We are transiting from home to aod or aod to home directly,
             // we don't need to do transition in these cases.
@@ -103,7 +97,9 @@
             mRevealListener.onRevealStateChanged();
             mRevealListener.onRevealEnd();
         } else {
-            animate();
+            mAnimator.setDuration(duration);
+            mAnimator.setFloatValues(mReveal, mAwake ? MAX_REVEAL : MIN_REVEAL);
+            mAnimator.start();
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
index 0a9100f..f30c181 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
@@ -25,7 +25,6 @@
 import android.content.pm.ResolveInfo;
 import android.graphics.drawable.Drawable;
 import android.os.Build;
-import android.os.Handler;
 import android.provider.Settings;
 import android.service.quicksettings.Tile;
 import android.service.quicksettings.TileService;
@@ -34,8 +33,8 @@
 import android.widget.Button;
 
 import com.android.systemui.R;
-import com.android.systemui.dagger.qualifiers.BgHandler;
-import com.android.systemui.dagger.qualifiers.MainHandler;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.plugins.qs.QSTile;
 import com.android.systemui.plugins.qs.QSTile.State;
 import com.android.systemui.qs.QSTileHost;
@@ -47,6 +46,7 @@
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.List;
+import java.util.concurrent.Executor;
 
 import javax.inject.Inject;
 
@@ -55,8 +55,8 @@
 
     private final ArrayList<TileInfo> mTiles = new ArrayList<>();
     private final ArraySet<String> mSpecs = new ArraySet<>();
-    private final Handler mBgHandler;
-    private final Handler mMainHandler;
+    private final Executor mMainExecutor;
+    private final Executor mBgExecutor;
     private final Context mContext;
     private TileStateListener mListener;
 
@@ -64,10 +64,10 @@
 
     @Inject
     public TileQueryHelper(Context context,
-            @MainHandler Handler mainHandler, @BgHandler Handler bgHandler) {
+            @Main Executor mainExecutor, @Background Executor bgExecutor) {
         mContext = context;
-        mMainHandler = mainHandler;
-        mBgHandler = bgHandler;
+        mMainExecutor = mainExecutor;
+        mBgExecutor = bgExecutor;
     }
 
     public void setListener(TileStateListener listener) {
@@ -126,7 +126,7 @@
             tilesToAdd.add(tile);
         }
 
-        mBgHandler.post(() -> {
+        mBgExecutor.execute(() -> {
             for (QSTile tile : tilesToAdd) {
                 final QSTile.State state = tile.getState().copy();
                 // Ignore the current state and get the generic label instead.
@@ -139,7 +139,7 @@
     }
 
     private void addPackageTiles(final QSTileHost host) {
-        mBgHandler.post(() -> {
+        mBgExecutor.execute(() -> {
             Collection<QSTile> params = host.getTiles();
             PackageManager pm = mContext.getPackageManager();
             List<ResolveInfo> services = pm.queryIntentServicesAsUser(
@@ -185,7 +185,7 @@
 
     private void notifyTilesChanged(final boolean finished) {
         final ArrayList<TileInfo> tilesToReturn = new ArrayList<>(mTiles);
-        mMainHandler.post(() -> {
+        mMainExecutor.execute(() -> {
             if (mListener != null) {
                 mListener.onTilesChanged(tilesToReturn);
             }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java
index fcdd234..f3e2f10 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java
@@ -162,6 +162,8 @@
             if (runningTask.supportsSplitScreenMultiWindow) {
                 if (ActivityManagerWrapper.getInstance().setTaskWindowingModeSplitScreenPrimary(
                         runningTask.id, stackCreateMode, initialBounds)) {
+                    mDividerOptional.ifPresent(Divider::onDockedTopTask);
+
                     // The overview service is handling split screen, so just skip the wait for the
                     // first draw and notify the divider to start animating now
                     mDividerOptional.ifPresent(Divider::onRecentsDrawn);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
index 341c49a..2005d79 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/FeatureFlags.java
@@ -17,14 +17,13 @@
 package com.android.systemui.statusbar;
 
 import android.annotation.NonNull;
-import android.os.Handler;
-import android.os.HandlerExecutor;
 import android.provider.DeviceConfig;
 import android.util.ArrayMap;
 
-import com.android.systemui.dagger.qualifiers.BgHandler;
+import com.android.systemui.dagger.qualifiers.Background;
 
 import java.util.Map;
+import java.util.concurrent.Executor;
 
 import javax.inject.Inject;
 import javax.inject.Singleton;
@@ -49,10 +48,10 @@
     private final Map<String, Boolean> mCachedDeviceConfigFlags = new ArrayMap<>();
 
     @Inject
-    public FeatureFlags(@BgHandler Handler bgHandler) {
+    public FeatureFlags(@Background Executor executor) {
         DeviceConfig.addOnPropertiesChangedListener(
                 "systemui",
-                new HandlerExecutor(bgHandler),
+                executor,
                 this::onPropertiesChanged);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java
index 9f5cf68..61043fb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java
@@ -36,6 +36,7 @@
 import com.android.systemui.Dependency;
 import com.android.systemui.assist.AssistHandleViewController;
 import com.android.systemui.dagger.qualifiers.MainHandler;
+import com.android.systemui.fragments.FragmentHostManager;
 import com.android.systemui.plugins.DarkIconDispatcher;
 import com.android.systemui.statusbar.CommandQueue.Callbacks;
 import com.android.systemui.statusbar.phone.AutoHideController;
@@ -168,6 +169,9 @@
             View navigationWindow = navBar.getView().getRootView();
             WindowManagerGlobal.getInstance()
                     .removeView(navigationWindow, true /* immediate */);
+            // Also remove FragmentHostState here in case that onViewDetachedFromWindow has not yet
+            // invoked after display removal.
+            FragmentHostManager.removeAndDestroy(navigationWindow);
             mNavigationBars.remove(displayId);
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
index 4cc5b21..ff4ce94 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
@@ -65,8 +65,18 @@
 
     boolean needsRedaction(NotificationEntry entry);
 
+    /**
+     * Has the given user chosen to allow their private (full) notifications to be shown even
+     * when the lockscreen is in "public" (secure & locked) mode?
+     */
     boolean userAllowsPrivateNotificationsInPublic(int currentUserId);
 
+    /**
+     * Has the given user chosen to allow notifications to be shown even when the lockscreen is in
+     * "public" (secure & locked) mode?
+     */
+    boolean userAllowsNotificationsInPublic(int userId);
+
     /** Notified when the current user changes. */
     interface UserChangedListener {
         void onUserChanged(int userId);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
index f5710a8..0f3f6b7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerImpl.java
@@ -435,7 +435,7 @@
      * Has the given user chosen to allow notifications to be shown even when the lockscreen is in
      * "public" (secure & locked) mode?
      */
-    private boolean userAllowsNotificationsInPublic(int userHandle) {
+    public boolean userAllowsNotificationsInPublic(int userHandle) {
         if (isCurrentProfile(userHandle) && userHandle != mCurrentUserId) {
             return true;
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index a98f826..d3a9c2c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -62,6 +62,7 @@
 import com.android.systemui.statusbar.phone.ScrimController;
 import com.android.systemui.statusbar.phone.ScrimState;
 import com.android.systemui.statusbar.phone.ShadeController;
+import com.android.systemui.statusbar.phone.StatusBar;
 import com.android.systemui.statusbar.phone.StatusBarWindowController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
 
@@ -122,6 +123,7 @@
     private final Context mContext;
     private final MediaSessionManager mMediaSessionManager;
     private final ArrayList<MediaListener> mMediaListeners;
+    private final Lazy<StatusBar> mStatusBarLazy;
     private final MediaArtworkProcessor mMediaArtworkProcessor;
     private final Set<AsyncTask<?, ?, ?>> mProcessArtworkTasks = new ArraySet<>();
 
@@ -182,6 +184,7 @@
     public NotificationMediaManager(
             Context context,
             Lazy<ShadeController> shadeController,
+            Lazy<StatusBar> statusBarLazy,
             Lazy<StatusBarWindowController> statusBarWindowController,
             NotificationEntryManager notificationEntryManager,
             MediaArtworkProcessor mediaArtworkProcessor,
@@ -195,6 +198,8 @@
         // TODO: use MediaSessionManager.SessionListener to hook us up to future updates
         // in session state
         mShadeController = shadeController;
+        // TODO: use KeyguardStateController#isOccluded to remove this dependency
+        mStatusBarLazy = statusBarLazy;
         mStatusBarWindowController = statusBarWindowController;
         mEntryManager = notificationEntryManager;
         notificationEntryManager.addNotificationEntryListener(new NotificationEntryListener() {
@@ -526,9 +531,8 @@
             }
         }
 
-        ShadeController shadeController = mShadeController.get();
         StatusBarWindowController windowController = mStatusBarWindowController.get();
-        boolean hideBecauseOccluded = shadeController != null && shadeController.isOccluded();
+        boolean hideBecauseOccluded = mStatusBarLazy.get().isOccluded();
 
         final boolean hasArtwork = artworkDrawable != null;
         mColorExtractor.setHasMediaArtwork(hasMediaArtwork);
@@ -599,6 +603,7 @@
                 if (DEBUG_MEDIA) {
                     Log.v(TAG, "DEBUG_MEDIA: Fading out album artwork");
                 }
+                ShadeController shadeController = mShadeController.get();
                 boolean cannotAnimateDoze = shadeController != null
                         && shadeController.isDozing()
                         && !ScrimState.AOD.getAnimateChange();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java
index e5f44bd..b61c1ef 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationFilter.java
@@ -36,7 +36,10 @@
 import javax.inject.Inject;
 import javax.inject.Singleton;
 
-/** Component which manages the various reasons a notification might be filtered out. */
+/** Component which manages the various reasons a notification might be filtered out.*/
+// TODO: delete NotificationFilter.java after migrating to new NotifPipeline b/145659174.
+//  Notification filtering is taken care of across the different Coordinators (mostly
+//  KeyguardCoordinator.java)
 @Singleton
 public class NotificationFilter {
 
@@ -109,7 +112,7 @@
             return true;
         }
 
-        if (entry.isSuspended()) {
+        if (entry.getRanking().isSuspended()) {
             return true;
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationListController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationListController.java
index 3f7fd1a..7b1dc07 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationListController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationListController.java
@@ -64,6 +64,8 @@
         }
     };
 
+    // TODO: (b/145659174) remove after moving to NewNotifPipeline. Replaced by
+    //  DeviceProvisionedCoordinator
     private final DeviceProvisionedListener mDeviceProvisionedListener =
             new DeviceProvisionedListener() {
                 @Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/GroupEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/GroupEntry.java
index f9f3266..9ae3882 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/GroupEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/GroupEntry.java
@@ -18,6 +18,8 @@
 
 import android.annotation.Nullable;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
@@ -34,7 +36,8 @@
     private final List<NotificationEntry> mUnmodifiableChildren =
             Collections.unmodifiableList(mChildren);
 
-    GroupEntry(String key) {
+    @VisibleForTesting
+    public GroupEntry(String key) {
         super(key);
     }
 
@@ -52,7 +55,8 @@
         return mUnmodifiableChildren;
     }
 
-    void setSummary(@Nullable NotificationEntry summary) {
+    @VisibleForTesting
+    public void setSummary(@Nullable NotificationEntry summary) {
         mSummary = summary;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java
index dc68c4b..6ce7fd9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ListEntry.java
@@ -18,6 +18,8 @@
 
 import android.annotation.Nullable;
 
+import com.android.internal.annotations.VisibleForTesting;
+
 /**
  * Abstract superclass for top-level entries, i.e. things that can appear in the final notification
  * list shown to users. In practice, this means either GroupEntries or NotificationEntries.
@@ -49,7 +51,8 @@
         return mParent;
     }
 
-    void setParent(@Nullable GroupEntry parent) {
+    @VisibleForTesting
+    public void setParent(@Nullable GroupEntry parent) {
         mParent = parent;
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index 3eb55ef..232fb6d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -275,10 +275,6 @@
         return mRanking.getSuppressedVisualEffects();
     }
 
-    public boolean isSuspended() {
-        return mRanking.isSuspended();
-    }
-
     /** @see Ranking#canBubble() */
     public boolean canBubble() {
         return mRanking.canBubble();
@@ -951,6 +947,15 @@
     }
 
     /**
+     * Whether or not this row represents a system notification. Note that if this is
+     * {@code null}, that means we were either unable to retrieve the info or have yet to
+     * retrieve the info.
+     */
+    public Boolean isSystemNotification() {
+        return mIsSystemNotification;
+    }
+
+    /**
      * Set this notification to be sensitive.
      *
      * @param sensitive true if the content of this notification is sensitive right now
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt
index 8bce528..48a4882 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManager.kt
@@ -16,36 +16,28 @@
 
 package com.android.systemui.statusbar.notification.collection
 
-import android.app.Notification
 import android.app.NotificationManager.IMPORTANCE_DEFAULT
 import android.app.NotificationManager.IMPORTANCE_HIGH
 import android.app.NotificationManager.IMPORTANCE_LOW
 import android.app.NotificationManager.IMPORTANCE_MIN
-import android.app.Person
 import android.service.notification.NotificationListenerService.Ranking
 import android.service.notification.NotificationListenerService.RankingMap
 import android.service.notification.StatusBarNotification
 import com.android.internal.annotations.VisibleForTesting
-
 import com.android.systemui.statusbar.NotificationMediaManager
 import com.android.systemui.statusbar.notification.NotificationFilter
 import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager
 import com.android.systemui.statusbar.notification.logging.NotifEvent
 import com.android.systemui.statusbar.notification.logging.NotifLog
+import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
 import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_ALERTING
 import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_PEOPLE
 import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_SILENT
 import com.android.systemui.statusbar.phone.NotificationGroupManager
 import com.android.systemui.statusbar.policy.HeadsUpManager
-
-import java.util.Objects
-import java.util.ArrayList
-
-import javax.inject.Inject
-
-import kotlin.Comparator
-
 import dagger.Lazy
+import java.util.Objects
+import javax.inject.Inject
 
 private const val TAG = "NotifRankingManager"
 
@@ -64,7 +56,8 @@
     private val headsUpManager: HeadsUpManager,
     private val notifFilter: NotificationFilter,
     private val notifLog: NotifLog,
-    sectionsFeatureManager: NotificationSectionsFeatureManager
+    sectionsFeatureManager: NotificationSectionsFeatureManager,
+    private val peopleNotificationIdentifier: PeopleNotificationIdentifier
 ) {
 
     var rankingMap: RankingMap? = null
@@ -79,6 +72,9 @@
         val aRank = a.ranking.rank
         val bRank = b.ranking.rank
 
+        val aIsPeople = a.isPeopleNotification()
+        val bIsPeople = b.isPeopleNotification()
+
         val aMedia = isImportantMedia(a)
         val bMedia = isImportantMedia(b)
 
@@ -88,25 +84,19 @@
         val aHeadsUp = a.isRowHeadsUp
         val bHeadsUp = b.isRowHeadsUp
 
-        if (usePeopleFiltering && a.isPeopleNotification() != b.isPeopleNotification()) {
-            if (a.isPeopleNotification()) -1 else 1
-        } else if (aHeadsUp != bHeadsUp) {
-            if (aHeadsUp) -1 else 1
-        } else if (aHeadsUp) {
+        when {
+            usePeopleFiltering && aIsPeople != bIsPeople -> if (aIsPeople) -1 else 1
+            aHeadsUp != bHeadsUp -> if (aHeadsUp) -1 else 1
             // Provide consistent ranking with headsUpManager
-            headsUpManager.compare(a, b)
-        } else if (aMedia != bMedia) {
+            aHeadsUp -> headsUpManager.compare(a, b)
             // Upsort current media notification.
-            if (aMedia) -1 else 1
-        } else if (aSystemMax != bSystemMax) {
+            aMedia != bMedia -> if (aMedia) -1 else 1
             // Upsort PRIORITY_MAX system notifications
-            if (aSystemMax) -1 else 1
-        } else if (a.isHighPriority != b.isHighPriority) {
-            -1 * java.lang.Boolean.compare(a.isHighPriority, b.isHighPriority)
-        } else if (aRank != bRank) {
-            aRank - bRank
-        } else {
-            nb.notification.`when`.compareTo(na.notification.`when`)
+            aSystemMax != bSystemMax -> if (aSystemMax) -1 else 1
+            a.isHighPriority != b.isHighPriority ->
+                -1 * a.isHighPriority.compareTo(b.isHighPriority)
+            aRank != bRank -> aRank - bRank
+            else -> nb.notification.`when`.compareTo(na.notification.`when`)
         }
     }
 
@@ -138,10 +128,9 @@
         val c = entry.channel
         val n = entry.sbn.notification
 
-        if (((n.isForegroundService && entry.ranking.importance >= IMPORTANCE_LOW) ||
-            n.hasMediaSession() ||
-            n.hasPerson() ||
-            n.hasStyle(Notification.MessagingStyle::class.java))) {
+        if ((n.isForegroundService && entry.ranking.importance >= IMPORTANCE_LOW) ||
+                n.hasMediaSession() ||
+                entry.isPeopleNotification()) {
             // Users who have long pressed and demoted to silent should not see the notification
             // in the top section
             if (c != null && c.hasUserSetImportance()) {
@@ -204,7 +193,7 @@
         isMedia: Boolean,
         isSystemMax: Boolean
     ) {
-        if (usePeopleFiltering && entry.hasAssociatedPeople()) {
+        if (usePeopleFiltering && entry.isPeopleNotification()) {
             entry.bucket = BUCKET_PEOPLE
         } else if (isHeadsUp || isMedia || isSystemMax || entry.isHighPriority) {
             entry.bucket = BUCKET_ALERTING
@@ -235,6 +224,11 @@
             }
         }
     }
+
+    private fun NotificationEntry.isPeopleNotification() =
+            sbn.isPeopleNotification()
+    private fun StatusBarNotification.isPeopleNotification() =
+            peopleNotificationIdentifier.isPeopleNotification(this)
 }
 
 // Convenience functions
@@ -245,16 +239,3 @@
 private fun StatusBarNotification.isSystemNotification(): Boolean {
     return "android" == packageName || "com.android.systemui" == packageName
 }
-
-private fun Notification.hasPerson(): Boolean {
-    val people: ArrayList<Person> =
-            (extras?.getParcelableArrayList(Notification.EXTRA_PEOPLE_LIST)) ?: ArrayList()
-    return people.isNotEmpty()
-}
-
-private fun Notification.hasStyle(targetStyleClass: Class<*>): Boolean {
-    return targetStyleClass == notificationStyle
-}
-
-private fun NotificationEntry.isPeopleNotification(): Boolean =
-        sbn.notification.hasStyle(Notification.MessagingStyle::class.java)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/Coordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/Coordinator.java
new file mode 100644
index 0000000..898918e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/Coordinator.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.coordinator;
+
+import com.android.systemui.statusbar.notification.collection.NotifCollection;
+import com.android.systemui.statusbar.notification.collection.NotifCollectionListener;
+import com.android.systemui.statusbar.notification.collection.NotifLifetimeExtender;
+import com.android.systemui.statusbar.notification.collection.init.NewNotifPipeline;
+import com.android.systemui.statusbar.notification.collection.listbuilder.NotifListBuilder;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable;
+
+/**
+ * Interface for registering callbacks to the {@link NewNotifPipeline}.
+ *
+ * This includes registering:
+ *  {@link Pluggable}s to the {@link NotifListBuilder}
+ *  {@link NotifCollectionListener}s and {@link NotifLifetimeExtender}s to {@link NotifCollection}
+ */
+public interface Coordinator {
+
+    /**
+     * Called after the NewNotifPipeline is initialized.
+     * Coordinators should register their {@link Pluggable}s to the notifListBuilder
+     * and their {@link NotifCollectionListener}s and {@link NotifLifetimeExtender}s
+     * to the notifCollection in this method.
+     */
+    void attach(NotifCollection notifCollection, NotifListBuilder notifListBuilder);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DeviceProvisionedCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DeviceProvisionedCoordinator.java
new file mode 100644
index 0000000..511aafc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/DeviceProvisionedCoordinator.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.coordinator;
+
+import android.Manifest;
+import android.app.AppGlobals;
+import android.app.Notification;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
+import android.os.RemoteException;
+import android.service.notification.StatusBarNotification;
+
+import com.android.systemui.statusbar.notification.collection.NotifCollection;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.listbuilder.NotifListBuilder;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * Filters out most notifications when the device is unprovisioned.
+ * Special notifications with extra permissions and tags won't be filtered out even when the
+ * device is unprovisioned.
+ */
+@Singleton
+public class DeviceProvisionedCoordinator implements Coordinator {
+    private static final String TAG = "DeviceProvisionedCoordinator";
+
+    private final DeviceProvisionedController mDeviceProvisionedController;
+
+    @Inject
+    public DeviceProvisionedCoordinator(DeviceProvisionedController deviceProvisionedController) {
+        mDeviceProvisionedController = deviceProvisionedController;
+    }
+
+    @Override
+    public void attach(NotifCollection notifCollection, NotifListBuilder notifListBuilder) {
+        mDeviceProvisionedController.addCallback(mDeviceProvisionedListener);
+
+        notifListBuilder.addFilter(mNotifFilter);
+    }
+
+    protected final NotifFilter mNotifFilter = new NotifFilter(TAG) {
+        @Override
+        public boolean shouldFilterOut(NotificationEntry entry, long now) {
+            return !mDeviceProvisionedController.isDeviceProvisioned()
+                    && !showNotificationEvenIfUnprovisioned(entry.getSbn());
+        }
+    };
+
+    /**
+     * Only notifications coming from packages with permission
+     * android.permission.NOTIFICATION_DURING_SETUP that also have special tags
+     * marking them as relevant for setup are allowed to show when device is unprovisioned
+     */
+    private boolean showNotificationEvenIfUnprovisioned(StatusBarNotification sbn) {
+        final boolean hasPermission = checkUidPermission(AppGlobals.getPackageManager(),
+                Manifest.permission.NOTIFICATION_DURING_SETUP,
+                sbn.getUid()) == PackageManager.PERMISSION_GRANTED;
+        return hasPermission
+                && sbn.getNotification().extras.getBoolean(Notification.EXTRA_ALLOW_DURING_SETUP);
+    }
+
+    private static int checkUidPermission(IPackageManager packageManager, String permission,
+            int uid) {
+        try {
+            return packageManager.checkUidPermission(permission, uid);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    private final DeviceProvisionedController.DeviceProvisionedListener mDeviceProvisionedListener =
+            new DeviceProvisionedController.DeviceProvisionedListener() {
+                @Override
+                public void onDeviceProvisionedChanged() {
+                    mNotifFilter.invalidateList();
+                }
+            };
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ForegroundCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ForegroundCoordinator.java
new file mode 100644
index 0000000..4803cf4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ForegroundCoordinator.java
@@ -0,0 +1,243 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.coordinator;
+
+import android.app.Notification;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.service.notification.StatusBarNotification;
+import android.util.ArraySet;
+
+import com.android.systemui.ForegroundServiceController;
+import com.android.systemui.appops.AppOpsController;
+import com.android.systemui.dagger.qualifiers.BgHandler;
+import com.android.systemui.dagger.qualifiers.MainHandler;
+import com.android.systemui.statusbar.notification.collection.NotifCollection;
+import com.android.systemui.statusbar.notification.collection.NotifCollectionListener;
+import com.android.systemui.statusbar.notification.collection.NotifLifetimeExtender;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.listbuilder.NotifListBuilder;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * Handles ForegroundService interactions with notifications.
+ *  Tags notifications with appOps.
+ *  Lifetime extends notifications associated with an ongoing ForegroundService.
+ *  Filters out notifications that represent foreground services that are no longer running
+ *
+ * Previously this logic lived in
+ *  frameworks/base/packages/SystemUI/src/com/android/systemui/ForegroundServiceController
+ *  frameworks/base/packages/SystemUI/src/com/android/systemui/ForegroundServiceNotificationListener
+ *  frameworks/base/packages/SystemUI/src/com/android/systemui/ForegroundServiceLifetimeExtender
+ */
+@Singleton
+public class ForegroundCoordinator implements Coordinator {
+    private static final String TAG = "ForegroundNotificationCoordinator";
+
+    private final ForegroundServiceController mForegroundServiceController;
+    private final AppOpsController mAppOpsController;
+    private final Handler mMainHandler;
+    private final Handler mBgHandler;
+
+    private NotifCollection mNotifCollection;
+
+    @Inject
+    public ForegroundCoordinator(
+            ForegroundServiceController foregroundServiceController,
+            AppOpsController appOpsController,
+            @MainHandler Handler mainHandler,
+            @BgHandler Handler bgHandler) {
+        mForegroundServiceController = foregroundServiceController;
+        mAppOpsController = appOpsController;
+        mMainHandler = mainHandler;
+        mBgHandler = bgHandler;
+    }
+
+    @Override
+    public void attach(NotifCollection notifCollection, NotifListBuilder notifListBuilder) {
+        mNotifCollection = notifCollection;
+
+        // extend the lifetime of foreground notification services to show for at least 5 seconds
+        mNotifCollection.addNotificationLifetimeExtender(mForegroundLifetimeExtender);
+
+        // listen for new notifications to add appOps
+        mNotifCollection.addCollectionListener(mNotifCollectionListener);
+
+        // when appOps change, update any relevant notifications to update appOps for
+        mAppOpsController.addCallback(ForegroundServiceController.APP_OPS, this::onAppOpsChanged);
+
+        // filter out foreground service notifications that aren't necessary anymore
+        notifListBuilder.addFilter(mNotifFilter);
+    }
+
+    /**
+     * Filters out notifications that represent foreground services that are no longer running.
+     */
+    protected final NotifFilter mNotifFilter = new NotifFilter(TAG) {
+        @Override
+        public boolean shouldFilterOut(NotificationEntry entry, long now) {
+            StatusBarNotification sbn = entry.getSbn();
+            if (mForegroundServiceController.isDisclosureNotification(sbn)
+                    && !mForegroundServiceController.isDisclosureNeededForUser(sbn.getUserId())) {
+                return true;
+            }
+
+            if (mForegroundServiceController.isSystemAlertNotification(sbn)) {
+                final String[] apps = sbn.getNotification().extras.getStringArray(
+                        Notification.EXTRA_FOREGROUND_APPS);
+                if (apps != null && apps.length >= 1) {
+                    if (!mForegroundServiceController.isSystemAlertWarningNeeded(
+                            sbn.getUserId(), apps[0])) {
+                        return true;
+                    }
+                }
+            }
+            return false;
+        }
+    };
+
+    /**
+     * Extends the lifetime of foreground notification services such that they show for at least
+     * five seconds
+     */
+    private final NotifLifetimeExtender mForegroundLifetimeExtender = new NotifLifetimeExtender() {
+        private static final int MIN_FGS_TIME_MS = 5000;
+        private OnEndLifetimeExtensionCallback mEndCallback;
+        private Map<String, Runnable> mEndRunnables = new HashMap<>();
+
+        @Override
+        public String getName() {
+            return TAG;
+        }
+
+        @Override
+        public void setCallback(OnEndLifetimeExtensionCallback callback) {
+            mEndCallback = callback;
+        }
+
+        @Override
+        public boolean shouldExtendLifetime(NotificationEntry entry, int reason) {
+            if ((entry.getSbn().getNotification().flags
+                    & Notification.FLAG_FOREGROUND_SERVICE) == 0) {
+                return false;
+            }
+
+            final long currTime = System.currentTimeMillis();
+            final boolean extendLife = currTime - entry.getSbn().getPostTime() < MIN_FGS_TIME_MS;
+
+            if (extendLife) {
+                if (!mEndRunnables.containsKey(entry.getKey())) {
+                    final Runnable runnable = new Runnable() {
+                        @Override
+                        public void run() {
+                            mEndCallback.onEndLifetimeExtension(mForegroundLifetimeExtender, entry);
+                        }
+                    };
+                    mEndRunnables.put(entry.getKey(), runnable);
+                    mBgHandler.postDelayed(runnable, MIN_FGS_TIME_MS
+                            - (currTime - entry.getSbn().getPostTime()));
+                }
+            }
+
+            return extendLife;
+        }
+
+        @Override
+        public void cancelLifetimeExtension(NotificationEntry entry) {
+            if (mEndRunnables.containsKey(entry.getKey())) {
+                Runnable endRunnable = mEndRunnables.remove(entry.getKey());
+                mBgHandler.removeCallbacks(endRunnable);
+            }
+        }
+    };
+
+    /**
+     * Adds appOps to incoming and updating notifications
+     */
+    private NotifCollectionListener mNotifCollectionListener = new NotifCollectionListener() {
+        @Override
+        public void onEntryAdded(NotificationEntry entry) {
+            tagForeground(entry);
+        }
+
+        @Override
+        public void onEntryUpdated(NotificationEntry entry) {
+            tagForeground(entry);
+        }
+
+        private void tagForeground(NotificationEntry entry) {
+            final StatusBarNotification sbn = entry.getSbn();
+            // note: requires that the ForegroundServiceController is updating their appOps first
+            ArraySet<Integer> activeOps = mForegroundServiceController.getAppOps(sbn.getUserId(),
+                    sbn.getPackageName());
+            if (activeOps != null) {
+                synchronized (entry.mActiveAppOps) {
+                    entry.mActiveAppOps.clear();
+                    entry.mActiveAppOps.addAll(activeOps);
+                }
+            }
+        }
+    };
+
+    /**
+     * Update the appOp for the posted notification associated with the current foreground service
+     * @param code code for appOp to add/remove
+     * @param uid of user the notification is sent to
+     * @param packageName package that created the notification
+     * @param active whether the appOpCode is active or not
+     */
+    private void onAppOpsChanged(int code, int uid, String packageName, boolean active) {
+        int userId = UserHandle.getUserId(uid);
+
+        // Update appOp if there's an associated posted notification:
+        final String foregroundKey = mForegroundServiceController.getStandardLayoutKey(userId,
+                packageName);
+        if (foregroundKey != null) {
+            final NotificationEntry entry = findNotificationEntryWithKey(foregroundKey);
+            if (entry != null
+                    && uid == entry.getSbn().getUid()
+                    && packageName.equals(entry.getSbn().getPackageName())) {
+                boolean changed;
+                synchronized (entry.mActiveAppOps) {
+                    if (active) {
+                        changed = entry.mActiveAppOps.add(code);
+                    } else {
+                        changed = entry.mActiveAppOps.remove(code);
+                    }
+                }
+                if (changed) {
+                    mMainHandler.post(mNotifFilter::invalidateList);
+                }
+            }
+        }
+    }
+
+    private NotificationEntry findNotificationEntryWithKey(String key) {
+        for (NotificationEntry entry : mNotifCollection.getNotifs()) {
+            if (entry.getKey().equals(key)) {
+                return entry;
+            }
+        }
+        return null;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java
new file mode 100644
index 0000000..6daf3fc
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.java
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.coordinator;
+
+import static android.app.Notification.VISIBILITY_SECRET;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.service.notification.StatusBarNotification;
+
+import androidx.annotation.MainThread;
+
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.notification.NotificationUtils;
+import com.android.systemui.statusbar.notification.collection.NotifCollection;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.listbuilder.NotifListBuilder;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * Filters low priority and privacy-sensitive notifications from the lockscreen.
+ */
+@Singleton
+public class KeyguardCoordinator implements Coordinator {
+    private static final String TAG = "KeyguardNotificationCoordinator";
+
+    private final Context mContext;
+    private final Handler mMainHandler;
+    private final KeyguardStateController mKeyguardStateController;
+    private final NotificationLockscreenUserManager mLockscreenUserManager;
+    private final BroadcastDispatcher mBroadcastDispatcher;
+    private final StatusBarStateController mStatusBarStateController;
+    private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+
+    @Inject
+    public KeyguardCoordinator(
+            Context context,
+            @MainThread Handler mainThreadHandler,
+            KeyguardStateController keyguardStateController,
+            NotificationLockscreenUserManager lockscreenUserManager,
+            BroadcastDispatcher broadcastDispatcher,
+            StatusBarStateController statusBarStateController,
+            KeyguardUpdateMonitor keyguardUpdateMonitor) {
+        mContext = context;
+        mMainHandler = mainThreadHandler;
+        mKeyguardStateController = keyguardStateController;
+        mLockscreenUserManager = lockscreenUserManager;
+
+        mBroadcastDispatcher = broadcastDispatcher;
+        mStatusBarStateController = statusBarStateController;
+        mKeyguardUpdateMonitor = keyguardUpdateMonitor;
+    }
+
+    @Override
+    public void attach(NotifCollection notifCollection, NotifListBuilder notifListBuilder) {
+        setupInvalidateNotifListCallbacks();
+        notifListBuilder.addFilter(mNotifFilter);
+    }
+
+    protected final NotifFilter mNotifFilter = new NotifFilter(TAG) {
+        @Override
+        public boolean shouldFilterOut(NotificationEntry entry, long now) {
+            final StatusBarNotification sbn = entry.getSbn();
+
+            // FILTER OUT the notification when the notification isn't for the current profile
+            if (!mLockscreenUserManager.isCurrentProfile(sbn.getUserId())) {
+                return true;
+            }
+
+            // FILTER OUT the notification when the keyguard is showing and...
+            if (mKeyguardStateController.isShowing()) {
+                // ... user settings or the device policy manager doesn't allow lockscreen
+                // notifications;
+                if (!mLockscreenUserManager.shouldShowLockscreenNotifications()) {
+                    return true;
+                }
+
+                final int currUserId = mLockscreenUserManager.getCurrentUserId();
+                final int notifUserId = (sbn.getUser().getIdentifier() == UserHandle.USER_ALL)
+                        ? currUserId : sbn.getUser().getIdentifier();
+
+                // ... user is in lockdown
+                if (mKeyguardUpdateMonitor.isUserInLockdown(currUserId)
+                        || mKeyguardUpdateMonitor.isUserInLockdown(notifUserId)) {
+                    return true;
+                }
+
+                // ... device is in public mode and the user's settings doesn't allow
+                // notifications to show in public mode
+                if (mLockscreenUserManager.isLockscreenPublicMode(currUserId)
+                        || mLockscreenUserManager.isLockscreenPublicMode(notifUserId)) {
+                    if (entry.getRanking().getVisibilityOverride() == VISIBILITY_SECRET) {
+                        return true;
+                    }
+
+                    if (!mLockscreenUserManager.userAllowsNotificationsInPublic(currUserId)
+                            || !mLockscreenUserManager.userAllowsNotificationsInPublic(
+                            notifUserId)) {
+                        return true;
+                    }
+                }
+
+                // ... neither this notification nor its summary have high enough priority
+                // to be shown on the lockscreen
+                // TODO: grouping hasn't happened yet (b/145134683)
+                if (entry.getParent() != null) {
+                    final NotificationEntry summary = entry.getParent().getRepresentativeEntry();
+                    if (priorityExceedsLockscreenShowingThreshold(summary)) {
+                        return false;
+                    }
+                }
+                return !priorityExceedsLockscreenShowingThreshold(entry);
+            }
+            return false;
+        }
+    };
+
+    private boolean priorityExceedsLockscreenShowingThreshold(NotificationEntry entry) {
+        if (entry == null) {
+            return false;
+        }
+        if (NotificationUtils.useNewInterruptionModel(mContext)
+                && hideSilentNotificationsOnLockscreen()) {
+            // TODO: make sure in the NewNotifPipeline that entry.isHighPriority() has been
+            //  correctly updated before reaching this point (b/145134683)
+            return entry.isHighPriority();
+        } else {
+            return !entry.getRanking().isAmbient();
+        }
+    }
+
+    private boolean hideSilentNotificationsOnLockscreen() {
+        return Settings.Secure.getInt(mContext.getContentResolver(),
+                Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, 1) == 0;
+    }
+
+    private void setupInvalidateNotifListCallbacks() {
+        // register onKeyguardShowing callback
+        mKeyguardStateController.addCallback(mKeyguardCallback);
+        mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateCallback);
+
+        // register lockscreen settings changed callbacks:
+        final ContentObserver settingsObserver = new ContentObserver(mMainHandler) {
+            @Override
+            public void onChange(boolean selfChange, Uri uri) {
+                if (mKeyguardStateController.isShowing()) {
+                    invalidateListFromFilter("Settings " + uri + " changed");
+                }
+            }
+        };
+
+        mContext.getContentResolver().registerContentObserver(
+                Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS),
+                false,
+                settingsObserver,
+                UserHandle.USER_ALL);
+
+        mContext.getContentResolver().registerContentObserver(
+                Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS),
+                true,
+                settingsObserver,
+                UserHandle.USER_ALL);
+
+        mContext.getContentResolver().registerContentObserver(
+                Settings.Global.getUriFor(Settings.Global.ZEN_MODE),
+                false,
+                settingsObserver);
+
+        // register (maybe) public mode changed callbacks:
+        mStatusBarStateController.addCallback(mStatusBarStateListener);
+        mBroadcastDispatcher.registerReceiver(new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                if (mKeyguardStateController.isShowing()) {
+                    // maybe public mode changed
+                    invalidateListFromFilter(intent.getAction());
+                }
+            }}, new IntentFilter(Intent.ACTION_USER_SWITCHED));
+    }
+
+    private void invalidateListFromFilter(String reason) {
+        mNotifFilter.invalidateList();
+    }
+
+    private final KeyguardStateController.Callback mKeyguardCallback =
+            new KeyguardStateController.Callback() {
+        @Override
+        public void onUnlockedChanged() {
+            invalidateListFromFilter("onUnlockedChanged");
+        }
+
+        @Override
+        public void onKeyguardShowingChanged() {
+            invalidateListFromFilter("onKeyguardShowingChanged");
+        }
+    };
+
+    private final StatusBarStateController.StateListener mStatusBarStateListener =
+            new StatusBarStateController.StateListener() {
+                @Override
+                public void onStateChanged(int newState) {
+                    // maybe public mode changed
+                    invalidateListFromFilter("onStatusBarStateChanged");
+                }
+    };
+
+    private final KeyguardUpdateMonitorCallback mKeyguardUpdateCallback =
+            new KeyguardUpdateMonitorCallback() {
+        @Override
+        public void onStrongAuthStateChanged(int userId) {
+            // maybe lockdown mode changed
+            invalidateListFromFilter("onStrongAuthStateChanged");
+        }
+    };
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java
new file mode 100644
index 0000000..13247193
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.coordinator;
+
+import com.android.systemui.Dumpable;
+import com.android.systemui.statusbar.notification.collection.NotifCollection;
+import com.android.systemui.statusbar.notification.collection.NotifCollectionListener;
+import com.android.systemui.statusbar.notification.collection.NotifLifetimeExtender;
+import com.android.systemui.statusbar.notification.collection.listbuilder.NotifListBuilder;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.Pluggable;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * Handles the attachment of the {@link NotifListBuilder} and {@link NotifCollection} to the
+ * {@link Coordinator}s, so that the Coordinators can register their respective callbacks.
+ */
+@Singleton
+public class NotifCoordinators implements Dumpable {
+    private static final String TAG = "NotifCoordinators";
+    private final List<Coordinator> mCoordinators = new ArrayList<>();
+
+    /**
+     * Creates all the coordinators.
+     */
+    @Inject
+    public NotifCoordinators(
+            KeyguardCoordinator keyguardCoordinator,
+            RankingCoordinator rankingCoordinator,
+            ForegroundCoordinator foregroundCoordinator,
+            DeviceProvisionedCoordinator deviceProvisionedCoordinator) {
+        mCoordinators.add(keyguardCoordinator);
+        mCoordinators.add(rankingCoordinator);
+        mCoordinators.add(foregroundCoordinator);
+        mCoordinators.add(deviceProvisionedCoordinator);
+        // TODO: add new Coordinators here! (b/145134683, b/112656837)
+    }
+
+    /**
+     * Sends the initialized notifListBuilder and notifCollection to each
+     * coordinator to indicate the notifListBuilder is ready to accept {@link Pluggable}s
+     * and the notifCollection is ready to accept {@link NotifCollectionListener}s and
+     * {@link NotifLifetimeExtender}s.
+     */
+    public void attach(NotifCollection notifCollection, NotifListBuilder notifListBuilder) {
+        for (Coordinator c : mCoordinators) {
+            c.attach(notifCollection, notifListBuilder);
+        }
+    }
+
+    @Override
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        pw.println(TAG + ":");
+        for (Coordinator c : mCoordinators) {
+            pw.println("\t" + c.getClass());
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java
new file mode 100644
index 0000000..c390f96
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/RankingCoordinator.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.coordinator;
+
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.notification.collection.NotifCollection;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.listbuilder.NotifListBuilder;
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * Filters out NotificationEntries based on its Ranking.
+ */
+@Singleton
+public class RankingCoordinator implements Coordinator {
+    private static final String TAG = "RankingNotificationCoordinator";
+
+    private final StatusBarStateController mStatusBarStateController;
+
+    @Inject
+    public RankingCoordinator(StatusBarStateController statusBarStateController) {
+        mStatusBarStateController = statusBarStateController;
+    }
+
+    @Override
+    public void attach(NotifCollection notifCollection, NotifListBuilder notifListBuilder) {
+        mStatusBarStateController.addCallback(mStatusBarStateCallback);
+
+        notifListBuilder.addFilter(mNotifFilter);
+    }
+
+    /**
+     * Checks whether to filter out the given notification based the notification's Ranking object.
+     * NotifListBuilder invalidates the notification list each time the ranking is updated,
+     * so we don't need to explicitly invalidate this filter on ranking update.
+     */
+    protected final NotifFilter mNotifFilter = new NotifFilter(TAG) {
+        @Override
+        public boolean shouldFilterOut(NotificationEntry entry, long now) {
+            // App suspended from Ranking
+            if (entry.getRanking().isSuspended()) {
+                return true;
+            }
+
+            // Dozing + DND Settings from Ranking object
+            if (mStatusBarStateController.isDozing() && entry.shouldSuppressAmbient()) {
+                return true;
+            }
+
+            if (!mStatusBarStateController.isDozing() && entry.shouldSuppressNotificationList()) {
+                return true;
+            }
+
+            return false;
+        }
+    };
+
+
+    private final StatusBarStateController.StateListener mStatusBarStateCallback =
+            new StatusBarStateController.StateListener() {
+                @Override
+                public void onDozingChanged(boolean isDozing) {
+                    mNotifFilter.invalidateList();
+                }
+            };
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NewNotifPipeline.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NewNotifPipeline.java
index 3b3e7e2..5fc55da 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NewNotifPipeline.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/init/NewNotifPipeline.java
@@ -23,6 +23,7 @@
 import com.android.systemui.statusbar.NotificationListener;
 import com.android.systemui.statusbar.notification.collection.NotifCollection;
 import com.android.systemui.statusbar.notification.collection.NotifListBuilderImpl;
+import com.android.systemui.statusbar.notification.collection.coordinator.NotifCoordinators;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
@@ -37,6 +38,7 @@
 public class NewNotifPipeline implements Dumpable {
     private final NotifCollection mNotifCollection;
     private final NotifListBuilderImpl mNotifPipeline;
+    private final NotifCoordinators mNotifPluggableCoordinators;
     private final DumpController mDumpController;
 
     private final FakePipelineConsumer mFakePipelineConsumer = new FakePipelineConsumer();
@@ -45,9 +47,11 @@
     public NewNotifPipeline(
             NotifCollection notifCollection,
             NotifListBuilderImpl notifPipeline,
+            NotifCoordinators notifCoordinators,
             DumpController dumpController) {
         mNotifCollection = notifCollection;
         mNotifPipeline = notifPipeline;
+        mNotifPluggableCoordinators = notifCoordinators;
         mDumpController = dumpController;
     }
 
@@ -57,6 +61,7 @@
         mFakePipelineConsumer.attach(mNotifPipeline);
         mNotifPipeline.attach(mNotifCollection);
         mNotifCollection.attach(notificationService);
+        mNotifPluggableCoordinators.attach(mNotifCollection, mNotifPipeline);
 
         Log.d(TAG, "Notif pipeline initialized");
 
@@ -66,6 +71,7 @@
     @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         mFakePipelineConsumer.dump(fd, pw, args);
+        mNotifPluggableCoordinators.dump(fd, pw, args);
     }
 
     private static final String TAG = "NewNotifPipeline";
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java
index fdd51e9e..8e9a051e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.java
@@ -318,7 +318,7 @@
                 }
                 mParent.addView(mPeopleHubView, targetIndex);
                 return true;
-            } else if (currentHubIndex != targetIndex - 1) {
+            } else if (currentHubIndex != targetIndex) {
                 if (currentHubIndex < targetIndex) {
                     targetIndex--;
                 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
index 063ad85..09ebb64 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DozeServiceHost.java
@@ -88,10 +88,9 @@
     private final PulseExpansionHandler mPulseExpansionHandler;
     private final StatusBarWindowController mStatusBarWindowController;
     private final NotificationWakeUpCoordinator mNotificationWakeUpCoordinator;
-    private final StatusBarWindowViewController mStatusBarWindowViewController;
+    private StatusBarWindowViewController mStatusBarWindowViewController;
     private final LockscreenLockIconController mLockscreenLockIconController;
     private NotificationIconAreaController mNotificationIconAreaController;
-    private StatusBarWindowView mStatusBarWindow;
     private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
     private NotificationPanelView mNotificationPanel;
     private View mAmbientIndicationContainer;
@@ -112,7 +111,6 @@
             PulseExpansionHandler pulseExpansionHandler,
             StatusBarWindowController statusBarWindowController,
             NotificationWakeUpCoordinator notificationWakeUpCoordinator,
-            StatusBarWindowViewController statusBarWindowViewController,
             LockscreenLockIconController lockscreenLockIconController) {
         super();
         mDozeLog = dozeLog;
@@ -132,7 +130,6 @@
         mPulseExpansionHandler = pulseExpansionHandler;
         mStatusBarWindowController = statusBarWindowController;
         mNotificationWakeUpCoordinator = notificationWakeUpCoordinator;
-        mStatusBarWindowViewController = statusBarWindowViewController;
         mLockscreenLockIconController = lockscreenLockIconController;
     }
 
@@ -143,14 +140,14 @@
      */
     public void initialize(StatusBar statusBar,
             NotificationIconAreaController notificationIconAreaController,
-            StatusBarWindowView statusBarWindow,
             StatusBarKeyguardViewManager statusBarKeyguardViewManager,
+            StatusBarWindowViewController statusBarWindowViewController,
             NotificationPanelView notificationPanel, View ambientIndicationContainer) {
         mStatusBar = statusBar;
         mNotificationIconAreaController = notificationIconAreaController;
-        mStatusBarWindow = statusBarWindow;
         mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
         mNotificationPanel = notificationPanel;
+        mStatusBarWindowViewController = statusBarWindowViewController;
         mAmbientIndicationContainer = ambientIndicationContainer;
         mBiometricUnlockController = mBiometricUnlockControllerLazy.get();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
index 4d6b54c..f3e9b6b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
@@ -134,6 +134,7 @@
         if (fd != null) {
             try {
                 BitmapFactory.Options options = new BitmapFactory.Options();
+                options.inPreferredConfig = Bitmap.Config.HARDWARE;
                 return LoaderResult.success(BitmapFactory.decodeFileDescriptor(
                         fd.getFileDescriptor(), null, options));
             } catch (OutOfMemoryError e) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeController.java
index f359fe7..b31ce6a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ShadeController.java
@@ -77,14 +77,6 @@
     void goToKeyguard();
 
     /**
-     * When the keyguard is showing and covered by something (bouncer, keyguard activity, etc.) it
-     * is occluded. This is controlled by {@link com.android.server.policy.PhoneWindowManager}
-     *
-     * @return whether the keyguard is currently occluded
-     */
-    boolean isOccluded();
-
-    /**
      * Notify the shade controller that the current user changed
      *
      * @param newUserId userId of the new user
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 170261e..5c71a57 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -237,9 +237,9 @@
 import java.util.Optional;
 
 import javax.inject.Named;
+import javax.inject.Provider;
 
 import dagger.Lazy;
-import dagger.Subcomponent;
 
 public class StatusBar extends SystemUI implements DemoMode,
         ActivityStarter, KeyguardStateController.Callback,
@@ -377,9 +377,10 @@
     private final FalsingManager mFalsingManager;
     private final BroadcastDispatcher mBroadcastDispatcher;
     private final ConfigurationController mConfigurationController;
-    private final StatusBarWindowViewController mStatusBarWindowViewController;
+    protected StatusBarWindowViewController mStatusBarWindowViewController;
     private final DozeParameters mDozeParameters;
     private final Lazy<BiometricUnlockController> mBiometricUnlockControllerLazy;
+    private final Provider<StatusBarComponent.Builder> mStatusBarComponentBuilder;
     private final PluginManager mPluginManager;
     private final RemoteInputUriController mRemoteInputUriController;
     private final Optional<Divider> mDividerOptional;
@@ -583,6 +584,8 @@
                     }
                 }
 
+                // TODO: (b/145659174) remove when moving to NewNotifPipeline. Replaced by
+                //  KeyguardCoordinator
                 @Override
                 public void onStrongAuthStateChanged(int userId) {
                     super.onStrongAuthStateChanged(userId);
@@ -659,7 +662,6 @@
             NotificationListener notificationListener,
             ConfigurationController configurationController,
             StatusBarWindowController statusBarWindowController,
-            StatusBarWindowViewController statusBarWindowViewController,
             LockscreenLockIconController lockscreenLockIconController,
             DozeParameters dozeParameters,
             ScrimController scrimController,
@@ -673,6 +675,7 @@
             VolumeComponent volumeComponent,
             CommandQueue commandQueue,
             Optional<Recents> recentsOptional,
+            Provider<StatusBarComponent.Builder> statusBarComponentBuilder,
             PluginManager pluginManager,
             RemoteInputUriController remoteInputUriController,
             Optional<Divider> dividerOptional,
@@ -732,7 +735,6 @@
         mNotificationListener = notificationListener;
         mConfigurationController = configurationController;
         mStatusBarWindowController = statusBarWindowController;
-        mStatusBarWindowViewController = statusBarWindowViewController;
         mLockscreenLockIconController = lockscreenLockIconController;
         mDozeServiceHost = dozeServiceHost;
         mPowerManager = powerManager;
@@ -746,6 +748,7 @@
         mVolumeComponent = volumeComponent;
         mCommandQueue = commandQueue;
         mRecentsOptional = recentsOptional;
+        mStatusBarComponentBuilder = statusBarComponentBuilder;
         mPluginManager = pluginManager;
         mRemoteInputUriController = remoteInputUriController;
         mDividerOptional = dividerOptional;
@@ -895,7 +898,8 @@
 
         mKeyguardUpdateMonitor.registerCallback(mUpdateCallback);
         mDozeServiceHost.initialize(this, mNotificationIconAreaController,
-                mStatusBarWindow, mStatusBarKeyguardViewManager,
+                mStatusBarKeyguardViewManager,
+                mStatusBarWindowViewController,
                 mNotificationPanel, mAmbientIndicationContainer);
 
         Dependency.get(ActivityStarterDelegate.class).setActivityStarterImpl(this);
@@ -962,7 +966,7 @@
         updateResources();
         updateTheme();
 
-        inflateStatusBarWindow(context);
+        inflateStatusBarWindow();
         mStatusBarWindowViewController.setService(this);
         mStatusBarWindow.setOnTouchListener(getStatusBarWindowTouchListener());
 
@@ -1233,7 +1237,8 @@
         mPresenter = new StatusBarNotificationPresenter(mContext, mNotificationPanel,
                 mHeadsUpManager, mStatusBarWindow, mStackScroller, mDozeScrimController,
                 mScrimController, mActivityLaunchAnimator, mDynamicPrivacyController,
-                mNotificationAlertingManager, rowBinder, mKeyguardStateController, mCommandQueue);
+                mNotificationAlertingManager, rowBinder, mKeyguardStateController,
+                this /* statusBar */, mCommandQueue);
 
         mNotificationListController =
                 new NotificationListController(
@@ -1246,7 +1251,7 @@
 
         mNotificationActivityStarter =
                 mStatusBarNotificationActivityStarterBuilder
-                        .setShadeController(this)
+                        .setStatusBar(this)
                         .setActivityLaunchAnimator(mActivityLaunchAnimator)
                         .setNotificationPresenter(mPresenter)
                         .build();
@@ -1380,8 +1385,11 @@
                 mNotificationPanel);
     }
 
-    protected void inflateStatusBarWindow(Context context) {
+    private void inflateStatusBarWindow() {
         mStatusBarWindow = mSuperStatusBarViewFactory.getStatusBarWindowView();
+        StatusBarComponent statusBarComponent = mStatusBarComponentBuilder.get()
+                .statusBarWindowView(mStatusBarWindow).build();
+        mStatusBarWindowViewController = statusBarComponent.getStatusBarWindowViewController();
         mStatusBarWindowViewController.setupExpandedStatusBar();
     }
 
@@ -1751,7 +1759,12 @@
         return mAmbientIndicationContainer;
     }
 
-    @Override
+    /**
+     * When the keyguard is showing and covered by a "showWhenLocked" activity it
+     * is occluded. This is controlled by {@link com.android.server.policy.PhoneWindowManager}
+     *
+     * @return whether the keyguard is currently occluded
+     */
     public boolean isOccluded() {
         return mIsOccluded;
     }
@@ -4391,11 +4404,6 @@
         return mGutsManager;
     }
 
-    @Subcomponent
-    public interface StatusBarInjector {
-        void createStatusBar(StatusBar statusbar);
-    }
-
     boolean isTransientShown() {
         return mTransientShown;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarComponent.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarComponent.java
new file mode 100644
index 0000000..f3c843c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarComponent.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.phone;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+
+import javax.inject.Scope;
+
+import dagger.BindsInstance;
+import dagger.Subcomponent;
+
+/**
+ * Dagger subcomponent tied to the lifecycle of StatusBar views.
+ */
+@Subcomponent
+public interface StatusBarComponent {
+    /**
+     * Builder for {@link StatusBarComponent}.
+     */
+    @Subcomponent.Builder
+    interface Builder {
+        @BindsInstance Builder statusBarWindowView(StatusBarWindowView statusBarWindowView);
+        StatusBarComponent build();
+    }
+
+    /**
+     * Scope annotation for singleton items within the StatusBarComponent.
+     */
+    @Documented
+    @Retention(RUNTIME)
+    @Scope
+    @interface StatusBarScope {}
+
+    /**
+     * Creates a StatusBarWindowViewController.
+     */
+    @StatusBarScope
+    StatusBarWindowViewController getStatusBarWindowViewController();
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarModule.java
index 5d69409..312c85f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarModule.java
@@ -77,6 +77,7 @@
 import java.util.Optional;
 
 import javax.inject.Named;
+import javax.inject.Provider;
 import javax.inject.Singleton;
 
 import dagger.Lazy;
@@ -143,7 +144,6 @@
             NotificationListener notificationListener,
             ConfigurationController configurationController,
             StatusBarWindowController statusBarWindowController,
-            StatusBarWindowViewController statusBarWindowViewController,
             LockscreenLockIconController lockscreenLockIconController,
             DozeParameters dozeParameters,
             ScrimController scrimController,
@@ -157,6 +157,7 @@
             VolumeComponent volumeComponent,
             CommandQueue commandQueue,
             Optional<Recents> recentsOptional,
+            Provider<StatusBarComponent.Builder> statusBarComponentBuilder,
             PluginManager pluginManager,
             RemoteInputUriController remoteInputUriController,
             Optional<Divider> dividerOptional,
@@ -217,7 +218,6 @@
                 notificationListener,
                 configurationController,
                 statusBarWindowController,
-                statusBarWindowViewController,
                 lockscreenLockIconController,
                 dozeParameters,
                 scrimController,
@@ -231,6 +231,7 @@
                 volumeComponent,
                 commandQueue,
                 recentsOptional,
+                statusBarComponentBuilder,
                 pluginManager,
                 remoteInputUriController,
                 dividerOptional,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
index 863874e..e283258 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -93,6 +93,7 @@
     private final NotificationRemoteInputManager mRemoteInputManager;
     private final NotificationLockscreenUserManager mLockscreenUserManager;
     private final ShadeController mShadeController;
+    private final StatusBar mStatusBar;
     private final KeyguardStateController mKeyguardStateController;
     private final ActivityStarter mActivityStarter;
     private final NotificationEntryManager mEntryManager;
@@ -125,7 +126,8 @@
             IDreamManager dreamManager, NotificationRemoteInputManager remoteInputManager,
             StatusBarRemoteInputCallback remoteInputCallback, NotificationGroupManager groupManager,
             NotificationLockscreenUserManager lockscreenUserManager,
-            ShadeController shadeController, KeyguardStateController keyguardStateController,
+            ShadeController shadeController, StatusBar statusBar,
+            KeyguardStateController keyguardStateController,
             NotificationInterruptionStateProvider notificationInterruptionStateProvider,
             MetricsLogger metricsLogger, LockPatternUtils lockPatternUtils,
             Handler mainThreadHandler, Handler backgroundHandler,
@@ -142,6 +144,8 @@
         mRemoteInputManager = remoteInputManager;
         mLockscreenUserManager = lockscreenUserManager;
         mShadeController = shadeController;
+        // TODO: use KeyguardStateController#isOccluded to remove this dependency
+        mStatusBar = statusBar;
         mKeyguardStateController = keyguardStateController;
         mActivityStarter = activityStarter;
         mEntryManager = entryManager;
@@ -198,7 +202,7 @@
         final boolean afterKeyguardGone = isActivityIntent
                 && mActivityIntentHelper.wouldLaunchResolverActivity(intent.getIntent(),
                 mLockscreenUserManager.getCurrentUserId());
-        final boolean wasOccluded = mShadeController.isOccluded();
+        final boolean wasOccluded = mStatusBar.isOccluded();
         boolean showOverLockscreen = mKeyguardStateController.isShowing() && intent != null
                 && mActivityIntentHelper.wouldShowOverLockscreen(intent.getIntent(),
                 mLockscreenUserManager.getCurrentUserId());
@@ -253,7 +257,7 @@
             mShadeController.addPostCollapseAction(runnable);
             mShadeController.collapsePanel(true /* animate */);
         } else if (mKeyguardStateController.isShowing()
-                && mShadeController.isOccluded()) {
+                && mStatusBar.isOccluded()) {
             mShadeController.addAfterKeyguardGoneRunnable(runnable);
             mShadeController.collapsePanel();
         } else {
@@ -384,7 +388,7 @@
                         .addNextIntentWithParentStack(intent)
                         .startActivities(getActivityOptions(
                                 mActivityLaunchAnimator.getLaunchAnimation(
-                                        row, mShadeController.isOccluded())),
+                                        row, mStatusBar.isOccluded())),
                                 new UserHandle(UserHandle.getUserId(appUid)));
                 mActivityLaunchAnimator.setLaunchResult(launchResult, true /* isActivityIntent */);
                 if (shouldCollapse()) {
@@ -518,6 +522,7 @@
         private ShadeController mShadeController;
         private NotificationPresenter mNotificationPresenter;
         private ActivityLaunchAnimator mActivityLaunchAnimator;
+        private StatusBar mStatusBar;
 
         @Inject
         public Builder(Context context,
@@ -567,8 +572,11 @@
             mBubbleController = bubbleController;
             mSuperStatusBarViewFactory = superStatusBarViewFactory;
         }
-        public Builder setShadeController(ShadeController shadeController) {
-            mShadeController = shadeController;
+
+        /** Sets the status bar to use as {@link StatusBar} and {@link ShadeController}. */
+        public Builder setStatusBar(StatusBar statusBar) {
+            mStatusBar = statusBar;
+            mShadeController = statusBar;
             return this;
         }
 
@@ -600,6 +608,7 @@
                     mGroupManager,
                     mLockscreenUserManager,
                     mShadeController,
+                    mStatusBar,
                     mKeyguardStateController,
                     mNotificationInterruptionStateProvider,
                     mMetricsLogger,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
index 30e26e5..6650cf6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenter.java
@@ -113,6 +113,7 @@
     private final DozeScrimController mDozeScrimController;
     private final ScrimController mScrimController;
     private final Context mContext;
+    private final StatusBar mStatusBar;
     private final CommandQueue mCommandQueue;
 
     private final AccessibilityManager mAccessibilityManager;
@@ -140,12 +141,15 @@
             NotificationAlertingManager notificationAlertingManager,
             NotificationRowBinderImpl notificationRowBinder,
             KeyguardStateController keyguardStateController,
+            StatusBar statusBar,
             CommandQueue commandQueue) {
         mContext = context;
         mKeyguardStateController = keyguardStateController;
         mNotificationPanel = panel;
         mHeadsUpManager = headsUp;
         mDynamicPrivacyController = dynamicPrivacyController;
+        // TODO: use KeyguardStateController#isOccluded to remove this dependency
+        mStatusBar = statusBar;
         mCommandQueue = commandQueue;
         mAboveShelfObserver = new AboveShelfObserver(stackScroller);
         mActivityLaunchAnimator = activityLaunchAnimator;
@@ -330,7 +334,7 @@
     }
 
     public boolean canHeadsUp(NotificationEntry entry, StatusBarNotification sbn) {
-        if (mShadeController.isOccluded()) {
+        if (mStatusBar.isOccluded()) {
             boolean devicePublic = mLockscreenUserManager.
                     isLockscreenPublicMode(mLockscreenUserManager.getCurrentUserId());
             boolean userPublic = devicePublic
@@ -356,7 +360,7 @@
             } else {
                 // we only allow head-up on the lockscreen if it doesn't have a fullscreen intent
                 return !mKeyguardStateController.isShowing()
-                        || mShadeController.isOccluded();
+                        || mStatusBar.isOccluded();
             }
         }
         return true;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowViewController.java
index feac5da..3935df0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowViewController.java
@@ -43,7 +43,6 @@
 import com.android.systemui.statusbar.DragDownHelper;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.PulseExpansionHandler;
-import com.android.systemui.statusbar.SuperStatusBarViewFactory;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
@@ -57,14 +56,13 @@
 import java.io.PrintWriter;
 
 import javax.inject.Inject;
-import javax.inject.Singleton;
 
 import dagger.Lazy;
 
 /**
  * Controller for {@link StatusBarWindowView}.
  */
-@Singleton
+//@Singleton
 public class StatusBarWindowViewController {
     private final InjectionInflationController mInjectionInflationController;
     private final NotificationWakeUpCoordinator mCoordinator;
@@ -116,9 +114,9 @@
             DozeLog dozeLog,
             DozeParameters dozeParameters,
             CommandQueue commandQueue,
-            SuperStatusBarViewFactory superStatusBarViewFactory,
             Lazy<ShadeController> shadeControllerLazy,
-            DockManager dockManager) {
+            DockManager dockManager,
+            StatusBarWindowView statusBarWindowView) {
         mInjectionInflationController = injectionInflationController;
         mCoordinator = coordinator;
         mPulseExpansionHandler = pulseExpansionHandler;
@@ -134,7 +132,7 @@
         mDozeLog = dozeLog;
         mDozeParameters = dozeParameters;
         mCommandQueue = commandQueue;
-        mView = superStatusBarViewFactory.getStatusBarWindowView();
+        mView = statusBarWindowView;
         mShadeControllerLazy = shadeControllerLazy;
         mDockManager = dockManager;
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
index 3f25bb6..b0cd90c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
@@ -50,9 +50,9 @@
 
 import java.io.PrintWriter;
 import java.util.BitSet;
-import java.util.concurrent.Executor;
-import java.util.Objects;
 import java.util.List;
+import java.util.Objects;
+import java.util.concurrent.Executor;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
@@ -552,8 +552,8 @@
         // If this is the data subscription, update the currentState data name
         if (mCurrentState.networkNameData.equals(mNetworkNameDefault) && mServiceState != null
                 && mCurrentState.dataSim
-                && !TextUtils.isEmpty(mServiceState.getDataOperatorAlphaShort())) {
-            mCurrentState.networkNameData = mServiceState.getDataOperatorAlphaShort();
+                && !TextUtils.isEmpty(mServiceState.getOperatorAlphaShort())) {
+            mCurrentState.networkNameData = mServiceState.getOperatorAlphaShort();
         }
 
         notifyListenersIfNecessary();
diff --git a/packages/SystemUI/src/com/android/systemui/util/concurrency/ConcurrencyModule.java b/packages/SystemUI/src/com/android/systemui/util/concurrency/ConcurrencyModule.java
new file mode 100644
index 0000000..24f49ff
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/ConcurrencyModule.java
@@ -0,0 +1,88 @@
+/*
+ * 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.util.concurrency;
+
+import android.content.Context;
+import android.os.Handler;
+
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.BgHandler;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.dagger.qualifiers.MainHandler;
+
+import java.util.concurrent.Executor;
+
+import dagger.Module;
+import dagger.Provides;
+
+/**
+ * Dagger Module for classes found within the concurrent package.
+ */
+@Module
+public abstract class ConcurrencyModule {
+    /**
+     * Provide a Background-Thread Executor by default.
+     */
+    @Provides
+    public static Executor provideExecutor(@BgHandler Handler handler) {
+        return new ExecutorImpl(handler);
+    }
+
+    /**
+     * Provide a Background-Thread Executor.
+     */
+    @Provides
+    @Background
+    public static Executor provideBackgroundExecutor(@BgHandler Handler handler) {
+        return new ExecutorImpl(handler);
+    }
+
+    /**
+     * Provide a Main-Thread Executor.
+     */
+    @Provides
+    @Main
+    public static Executor provideMainExecutor(Context context) {
+        return context.getMainExecutor();
+    }
+
+    /**
+     * Provide a Background-Thread Executor by default.
+     */
+    @Provides
+    public static DelayableExecutor provideDelayableExecutor(@BgHandler Handler handler) {
+        return new ExecutorImpl(handler);
+    }
+
+    /**
+     * Provide a Background-Thread Executor.
+     */
+    @Provides
+    @Background
+    public static DelayableExecutor provideBackgroundDelayableExecutor(@BgHandler Handler handler) {
+        return new ExecutorImpl(handler);
+    }
+
+    /**
+     * Provide a Main-Thread Executor.
+     */
+    @Provides
+    @Main
+    public static DelayableExecutor provideMainDelayableExecutor(@MainHandler Handler handler) {
+        return new ExecutorImpl(handler);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/concurrency/DelayableExecutor.java b/packages/SystemUI/src/com/android/systemui/util/concurrency/DelayableExecutor.java
new file mode 100644
index 0000000..2d6c4a6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/DelayableExecutor.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.systemui.util.concurrency;
+
+import java.util.concurrent.Executor;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A sub-class of {@link Executor} that allows Runnables to be delayed and/or cancelled.
+ */
+public interface DelayableExecutor extends Executor {
+    /**
+     * Execute supplied Runnable on the Executors thread after a specified delay.
+     *
+     * See {@link android.os.Handler#postDelayed(Runnable, long)}.
+     *
+     * @return A Runnable that, when run, removes the supplied argument from the Executor queue.
+     */
+    default Runnable executeDelayed(Runnable r, long delayMillis) {
+        return executeDelayed(r, delayMillis, TimeUnit.MILLISECONDS);
+    }
+
+    /**
+     * Execute supplied Runnable on the Executors thread after a specified delay.
+     *
+     * See {@link android.os.Handler#postDelayed(Runnable, long)}.
+     *
+     * @return A Runnable that, when run, removes the supplied argument from the Executor queue..
+     */
+    Runnable executeDelayed(Runnable r, long delay, TimeUnit unit);
+
+    /**
+     * Execute supplied Runnable on the Executors thread at a specified uptime.
+     *
+     * See {@link android.os.Handler#postAtTime(Runnable, long)}.
+     *
+     * @return A Runnable that, when run, removes the supplied argument from the Executor queue.
+     */
+    default Runnable executeAtTime(Runnable r, long uptime) {
+        return executeAtTime(r, uptime, TimeUnit.MILLISECONDS);
+    }
+
+    /**
+     * Execute supplied Runnable on the Executors thread at a specified uptime.
+     *
+     * See {@link android.os.Handler#postAtTime(Runnable, long)}.
+     *
+     * @return A Runnable that, when run, removes the supplied argument from the Executor queue.
+     */
+    Runnable executeAtTime(Runnable r, long uptimeMillis, TimeUnit unit);
+
+    /**
+     * Remove all pending Runnables.
+     *
+     */
+    void removeAll();
+
+}
diff --git a/packages/SystemUI/src/com/android/systemui/util/concurrency/ExecutorImpl.java b/packages/SystemUI/src/com/android/systemui/util/concurrency/ExecutorImpl.java
new file mode 100644
index 0000000..2a99de3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/concurrency/ExecutorImpl.java
@@ -0,0 +1,58 @@
+/*
+ * 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.util.concurrency;
+
+import android.os.Handler;
+import android.os.HandlerExecutor;
+import android.os.Message;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Implementations of {@link DelayableExecutor} for SystemUI.
+ */
+public class ExecutorImpl extends HandlerExecutor implements DelayableExecutor {
+    private final Handler mHandler;
+
+    public ExecutorImpl(Handler handler) {
+        super(handler);
+        mHandler = handler;
+    }
+
+    @Override
+    public Runnable executeDelayed(Runnable r, long delay, TimeUnit unit) {
+        Object token = new Object();
+        Message m = mHandler.obtainMessage(0, token);
+        mHandler.sendMessageDelayed(m, unit.toMillis(delay));
+
+        return () -> mHandler.removeCallbacksAndMessages(token);
+    }
+
+    @Override
+    public Runnable executeAtTime(Runnable r, long uptimeMillis, TimeUnit unit) {
+        Object token = new Object();
+        Message m = mHandler.obtainMessage(0, token);
+        mHandler.sendMessageAtTime(m, unit.toMillis(uptimeMillis));
+
+        return () -> mHandler.removeCallbacksAndMessages(token);
+    }
+
+    @Override
+    public void removeAll() {
+        mHandler.removeCallbacksAndMessages(null);
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java
index 0990e22..4cb5472 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ForegroundServiceControllerTest.java
@@ -49,6 +49,7 @@
 import com.android.systemui.statusbar.NotificationEntryBuilder;
 import com.android.systemui.statusbar.notification.NotificationEntryListener;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotifCollection;
 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
 
 import junit.framework.Assert;
@@ -70,6 +71,7 @@
     @Mock private NotificationEntryManager mEntryManager;
     @Mock private AppOpsController mAppOpsController;
     @Mock private Handler mMainHandler;
+    @Mock private NotifCollection mNotifCollection;
 
     @Before
     public void setUp() throws Exception {
@@ -79,7 +81,7 @@
         MockitoAnnotations.initMocks(this);
         mFsc = new ForegroundServiceController(mEntryManager, mAppOpsController, mMainHandler);
         mListener = new ForegroundServiceNotificationListener(
-                mContext, mFsc, mEntryManager);
+                mContext, mFsc, mEntryManager, mNotifCollection);
         ArgumentCaptor<NotificationEntryListener> entryListenerCaptor =
                 ArgumentCaptor.forClass(NotificationEntryListener.class);
         verify(mEntryManager).addNotificationEntryListener(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
index b41512c..39ce8c1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/customize/TileQueryHelperTest.java
@@ -36,13 +36,9 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.pm.ServiceInfo;
-import android.os.Handler;
-import android.os.Looper;
 import android.provider.Settings;
 import android.service.quicksettings.Tile;
 import android.testing.AndroidTestingRunner;
-import android.testing.TestableLooper;
-import android.testing.TestableLooper.RunWithLooper;
 import android.text.TextUtils;
 import android.util.ArraySet;
 
@@ -52,6 +48,8 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.plugins.qs.QSTile;
 import com.android.systemui.qs.QSTileHost;
+import com.android.systemui.util.concurrency.FakeExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -69,7 +67,6 @@
 
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
-@RunWithLooper
 public class TileQueryHelperTest extends SysuiTestCase {
     private static final String CURRENT_TILES = "wifi,dnd,nfc";
     private static final String ONLY_STOCK_TILES = "wifi,dnd";
@@ -98,14 +95,13 @@
     private ArgumentCaptor<List<TileQueryHelper.TileInfo>> mCaptor;
 
     private QSTile.State mState;
-    private TestableLooper mBGLooper;
     private TileQueryHelper mTileQueryHelper;
-    private Handler mMainHandler;
+    private FakeExecutor mMainExecutor;
+    private FakeExecutor mBgExecutor;
 
     @Before
     public void setup() {
         MockitoAnnotations.initMocks(this);
-        mBGLooper = TestableLooper.get(this);
         mContext.setMockPackageManager(mPackageManager);
 
         mState = new QSTile.State();
@@ -123,9 +119,11 @@
                 }
         ).when(mQSTileHost).createTile(anyString());
 
-        mMainHandler = new Handler(Looper.getMainLooper());
-        mTileQueryHelper = new TileQueryHelper(mContext, mMainHandler,
-                new Handler(mBGLooper.getLooper()));
+        FakeSystemClock clock = new FakeSystemClock();
+        clock.setAutoIncrement(false);
+        mMainExecutor = new FakeExecutor(clock);
+        mBgExecutor = new FakeExecutor(clock);
+        mTileQueryHelper = new TileQueryHelper(mContext, mMainExecutor, mBgExecutor);
         mTileQueryHelper.setListener(mListener);
     }
 
@@ -138,8 +136,7 @@
     public void testIsFinished_trueAfterQuerying() {
         mTileQueryHelper.queryTiles(mQSTileHost);
 
-        mBGLooper.processAllMessages();
-        waitForIdleSync(mMainHandler);
+        FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor);
 
         assertTrue(mTileQueryHelper.isFinished());
     }
@@ -148,8 +145,7 @@
     public void testQueryTiles_callsListenerTwice() {
         mTileQueryHelper.queryTiles(mQSTileHost);
 
-        mBGLooper.processAllMessages();
-        waitForIdleSync(mMainHandler);
+        FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor);
 
         verify(mListener, times(2)).onTilesChanged(any());
     }
@@ -163,8 +159,7 @@
 
         mTileQueryHelper.queryTiles(mQSTileHost);
 
-        mBGLooper.processAllMessages();
-        waitForIdleSync(mMainHandler);
+        FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor);
 
         assertTrue(mTileQueryHelper.isFinished());
     }
@@ -178,8 +173,7 @@
 
         mTileQueryHelper.queryTiles(mQSTileHost);
 
-        mBGLooper.processAllMessages();
-        waitForIdleSync(mMainHandler);
+        FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor);
 
         verify(mListener, atLeastOnce()).onTilesChanged(mCaptor.capture());
         List<String> specs = new ArrayList<>();
@@ -199,8 +193,7 @@
 
         mTileQueryHelper.queryTiles(mQSTileHost);
 
-        mBGLooper.processAllMessages();
-        waitForIdleSync(mMainHandler);
+        FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor);
 
         verify(mListener, atLeastOnce()).onTilesChanged(mCaptor.capture());
         List<String> specs = new ArrayList<>();
@@ -220,8 +213,7 @@
 
         mTileQueryHelper.queryTiles(mQSTileHost);
 
-        mBGLooper.processAllMessages();
-        waitForIdleSync(mMainHandler);
+        FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor);
 
         verify(mListener, atLeastOnce()).onTilesChanged(mCaptor.capture());
         List<String> specs = new ArrayList<>();
@@ -251,8 +243,7 @@
                 "");
 
         mTileQueryHelper.queryTiles(mQSTileHost);
-        mBGLooper.processAllMessages();
-        waitForIdleSync(mMainHandler);
+        FakeExecutor.exhaustExecutors(mMainExecutor, mBgExecutor);
 
         verify(mListener, atLeastOnce()).onTilesChanged(mCaptor.capture());
         List<TileQueryHelper.TileInfo> tileInfos = mCaptor.getValue();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
index a98945f..e0dfe7e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
@@ -85,6 +85,7 @@
 import com.android.systemui.statusbar.notification.collection.NotificationRowBinderImpl;
 import com.android.systemui.statusbar.notification.logging.NotifLog;
 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
+import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
 import com.android.systemui.statusbar.notification.row.RowInflaterTask;
@@ -228,7 +229,8 @@
                         mHeadsUpManager,
                         mock(NotificationFilter.class),
                         mNotifLog,
-                        mock(NotificationSectionsFeatureManager.class)),
+                        mock(NotificationSectionsFeatureManager.class),
+                        mock(PeopleNotificationIdentifier.class)),
                 mEnvironment
         );
         Dependency.get(InitController.class).executePostInitTasks();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt
index 01b2f89..cda1538e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotificationRankingManagerTest.kt
@@ -20,7 +20,6 @@
 import android.app.NotificationChannel
 import android.app.NotificationManager.IMPORTANCE_DEFAULT
 import android.app.NotificationManager.IMPORTANCE_LOW
-import android.app.Person
 import android.service.notification.NotificationListenerService.RankingMap
 import android.service.notification.StatusBarNotification
 import android.testing.AndroidTestingRunner
@@ -36,6 +35,7 @@
 import com.android.systemui.statusbar.notification.NotificationFilter
 import com.android.systemui.statusbar.notification.NotificationSectionsFeatureManager
 import com.android.systemui.statusbar.notification.logging.NotifLog
+import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
 import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_ALERTING
 import com.android.systemui.statusbar.notification.stack.NotificationSectionsManager.BUCKET_SILENT
 import com.android.systemui.statusbar.phone.NotificationGroupManager
@@ -52,42 +52,39 @@
 
 @SmallTest
 @RunWith(AndroidTestingRunner::class)
-class NotificationRankingManagerTest
-    : SysuiTestCase() {
+class NotificationRankingManagerTest : SysuiTestCase() {
 
-    private var lazyMedia: Lazy<NotificationMediaManager> = Lazy {
-        mock<NotificationMediaManager>(NotificationMediaManager::class.java)
+    private val lazyMedia: Lazy<NotificationMediaManager> = Lazy {
+        mock(NotificationMediaManager::class.java)
     }
-
-    private val rankingManager = TestableNotificationRankingManager(
-            lazyMedia,
-            mock<NotificationGroupManager>(NotificationGroupManager::class.java),
-            mock<HeadsUpManager>(HeadsUpManager::class.java),
-            mock<NotificationFilter>(NotificationFilter::class.java),
-            mock<NotifLog>(NotifLog::class.java),
-            mock<NotificationSectionsFeatureManager>(NotificationSectionsFeatureManager::class.java)
-    )
+    private lateinit var personNotificationIdentifier: PeopleNotificationIdentifier
+    private lateinit var rankingManager: TestableNotificationRankingManager
 
     @Before
     fun setup() {
+        personNotificationIdentifier =
+                mock(PeopleNotificationIdentifier::class.java)
+        rankingManager = TestableNotificationRankingManager(
+                lazyMedia,
+                mock(NotificationGroupManager::class.java),
+                mock(HeadsUpManager::class.java),
+                mock(NotificationFilter::class.java),
+                mock(NotifLog::class.java),
+                mock(NotificationSectionsFeatureManager::class.java),
+                personNotificationIdentifier
+        )
     }
 
     @Test
     fun testPeopleNotification_isHighPriority() {
-        val person = Person.Builder()
-                .setName("name")
-                .setKey("abc")
-                .setUri("uri")
-                .setBot(true)
-                .build()
-
         val notification = Notification.Builder(mContext, "test")
-                .addPerson(person)
                 .build()
 
         val sbn = StatusBarNotification("pkg", "pkg", 0, "tag", 0, 0,
                 notification, mContext.user, "", 0)
 
+        `when`(personNotificationIdentifier.isPeopleNotification(sbn)).thenReturn(true)
+
         val e = NotificationEntryBuilder()
                 .setNotification(notification)
                 .setSbn(sbn)
@@ -97,27 +94,9 @@
     }
 
     @Test
-    fun messagingStyleHighPriority() {
-
-        val notif = Notification.Builder(mContext, "test")
-                .setStyle(Notification.MessagingStyle(""))
-                .build()
-
-        val sbn = StatusBarNotification("pkg", "pkg", 0, "tag", 0, 0,
-                notif, mContext.getUser(), "", 0)
-
-        val e = NotificationEntryBuilder()
-                .setNotification(notif)
-                .setSbn(sbn)
-                .build()
-
-        assertTrue(rankingManager.isHighPriority2(e))
-    }
-
-    @Test
     fun lowForegroundHighPriority() {
         val notification = mock(Notification::class.java)
-        `when`<Boolean>(notification.isForegroundService).thenReturn(true)
+        `when`(notification.isForegroundService).thenReturn(true)
 
         val sbn = StatusBarNotification("pkg", "pkg", 0, "tag", 0, 0,
                 notification, mContext.user, "", 0)
@@ -136,15 +115,7 @@
 
     @Test
     fun userChangeTrumpsHighPriorityCharacteristics() {
-        val person = Person.Builder()
-                .setName("name")
-                .setKey("abc")
-                .setUri("uri")
-                .setBot(true)
-                .build()
-
         val notification = Notification.Builder(mContext, "test")
-                .addPerson(person)
                 .setStyle(Notification.MessagingStyle(""))
                 .setFlag(Notification.FLAG_FOREGROUND_SERVICE, true)
                 .build()
@@ -152,6 +123,8 @@
         val sbn = StatusBarNotification("pkg", "pkg", 0, "tag", 0, 0,
                 notification, mContext.user, "", 0)
 
+        `when`(personNotificationIdentifier.isPeopleNotification(sbn)).thenReturn(true)
+
         val channel = NotificationChannel("a", "a", IMPORTANCE_LOW)
         channel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE)
 
@@ -297,14 +270,16 @@
         headsUpManager: HeadsUpManager,
         filter: NotificationFilter,
         notifLog: NotifLog,
-        sectionsFeatureManager: NotificationSectionsFeatureManager
+        sectionsFeatureManager: NotificationSectionsFeatureManager,
+        peopleNotificationIdentifier: PeopleNotificationIdentifier
     ) : NotificationRankingManager(
         mediaManager,
         groupManager,
         headsUpManager,
         filter,
         notifLog,
-        sectionsFeatureManager
+        sectionsFeatureManager,
+        peopleNotificationIdentifier
     ) {
 
         fun isHighPriority2(e: NotificationEntry): Boolean {
@@ -315,4 +290,4 @@
             rankingMap = r
         }
     }
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.java
new file mode 100644
index 0000000..87b3783d
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.java
@@ -0,0 +1,243 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.coordinator;
+
+import static android.app.Notification.VISIBILITY_PUBLIC;
+import static android.app.Notification.VISIBILITY_SECRET;
+import static android.app.NotificationManager.IMPORTANCE_HIGH;
+import static android.app.NotificationManager.IMPORTANCE_MIN;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.Mockito.when;
+
+import android.os.Handler;
+import android.os.UserHandle;
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.NotificationEntryBuilder;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager;
+import com.android.systemui.statusbar.RankingBuilder;
+import com.android.systemui.statusbar.notification.collection.GroupEntry;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class KeyguardCoordinatorTest extends SysuiTestCase {
+    private static final int NOTIF_USER_ID = 0;
+    private static final int CURR_USER_ID = 1;
+
+    @Mock private Handler mMainHandler;
+    @Mock private KeyguardStateController mKeyguardStateController;
+    @Mock private NotificationLockscreenUserManager mLockscreenUserManager;
+    @Mock private BroadcastDispatcher mBroadcastDispatcher;
+    @Mock private StatusBarStateController mStatusBarStateController;
+    @Mock private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+
+    private NotificationEntry mEntry;
+    private KeyguardCoordinator mKeyguardNotificationCoordinator;
+
+    @Before
+    public void setup() {
+        MockitoAnnotations.initMocks(this);
+        mKeyguardNotificationCoordinator = new KeyguardCoordinator(
+                mContext, mMainHandler, mKeyguardStateController, mLockscreenUserManager,
+                mBroadcastDispatcher, mStatusBarStateController,
+                mKeyguardUpdateMonitor);
+
+        mEntry = new NotificationEntryBuilder()
+                .setUser(new UserHandle(NOTIF_USER_ID))
+                .build();
+    }
+
+    @Test
+    public void unfilteredState() {
+        // GIVEN an 'unfiltered-keyguard-showing' state
+        setupUnfilteredState();
+
+        // THEN don't filter out the entry
+        assertFalse(mKeyguardNotificationCoordinator.mNotifFilter.shouldFilterOut(mEntry, 0));
+    }
+
+    @Test
+    public void notificationNotForCurrentProfile() {
+        // GIVEN the notification isn't for the given user
+        setupUnfilteredState();
+        when(mLockscreenUserManager.isCurrentProfile(NOTIF_USER_ID)).thenReturn(false);
+
+        // THEN filter out the entry
+        assertTrue(mKeyguardNotificationCoordinator.mNotifFilter.shouldFilterOut(mEntry, 0));
+    }
+
+    @Test
+    public void keyguardNotShowing() {
+        // GIVEN the lockscreen isn't showing
+        setupUnfilteredState();
+        when(mKeyguardStateController.isShowing()).thenReturn(false);
+
+        // THEN don't filter out the entry
+        assertFalse(mKeyguardNotificationCoordinator.mNotifFilter.shouldFilterOut(mEntry, 0));
+    }
+
+    @Test
+    public void doNotShowLockscreenNotifications() {
+        // GIVEN an 'unfiltered-keyguard-showing' state
+        setupUnfilteredState();
+
+        // WHEN we shouldn't show any lockscreen notifications
+        when(mLockscreenUserManager.shouldShowLockscreenNotifications()).thenReturn(false);
+
+        // THEN filter out the entry
+        assertTrue(mKeyguardNotificationCoordinator.mNotifFilter.shouldFilterOut(mEntry, 0));
+    }
+
+    @Test
+    public void lockdown() {
+        // GIVEN an 'unfiltered-keyguard-showing' state
+        setupUnfilteredState();
+
+        // WHEN the notification's user is in lockdown:
+        when(mKeyguardUpdateMonitor.isUserInLockdown(NOTIF_USER_ID)).thenReturn(true);
+
+        // THEN filter out the entry
+        assertTrue(mKeyguardNotificationCoordinator.mNotifFilter.shouldFilterOut(mEntry, 0));
+    }
+
+    @Test
+    public void publicMode_settingsDisallow() {
+        // GIVEN an 'unfiltered-keyguard-showing' state
+        setupUnfilteredState();
+
+        // WHEN the notification's user is in public mode and settings are configured to disallow
+        // notifications in public mode
+        when(mLockscreenUserManager.isLockscreenPublicMode(NOTIF_USER_ID)).thenReturn(true);
+        when(mLockscreenUserManager.userAllowsNotificationsInPublic(NOTIF_USER_ID))
+                .thenReturn(false);
+
+        // THEN filter out the entry
+        assertTrue(mKeyguardNotificationCoordinator.mNotifFilter.shouldFilterOut(mEntry, 0));
+    }
+
+    @Test
+    public void publicMode_notifDisallowed() {
+        // GIVEN an 'unfiltered-keyguard-showing' state
+        setupUnfilteredState();
+
+        // WHEN the notification's user is in public mode and settings are configured to disallow
+        // notifications in public mode
+        when(mLockscreenUserManager.isLockscreenPublicMode(CURR_USER_ID)).thenReturn(true);
+        mEntry.setRanking(new RankingBuilder()
+                .setKey(mEntry.getKey())
+                .setVisibilityOverride(VISIBILITY_SECRET).build());
+
+        // THEN filter out the entry
+        assertTrue(mKeyguardNotificationCoordinator.mNotifFilter.shouldFilterOut(mEntry, 0));
+    }
+
+    @Test
+    public void doesNotExceedThresholdToShow() {
+        // GIVEN an 'unfiltered-keyguard-showing' state
+        setupUnfilteredState();
+
+        // WHEN the notification doesn't exceed the threshold to show on the lockscreen
+        mEntry.setRanking(new RankingBuilder()
+                .setKey(mEntry.getKey())
+                .setImportance(IMPORTANCE_MIN)
+                .build());
+
+        // THEN filter out the entry
+        assertTrue(mKeyguardNotificationCoordinator.mNotifFilter.shouldFilterOut(mEntry, 0));
+    }
+
+    @Test
+    public void summaryExceedsThresholdToShow() {
+        // GIVEN an 'unfiltered-keyguard-showing' state
+        setupUnfilteredState();
+
+        // WHEN the notification doesn't exceed the threshold to show on the lockscreen
+        // but its summary does
+        mEntry.setRanking(new RankingBuilder()
+                .setKey(mEntry.getKey())
+                .setImportance(IMPORTANCE_MIN)
+                .build());
+
+        final NotificationEntry summary = new NotificationEntryBuilder().build();
+        summary.setRanking(new RankingBuilder()
+                .setKey(summary.getKey())
+                .setImportance(IMPORTANCE_HIGH)
+                .build());
+        final GroupEntry group = new GroupEntry(mEntry.getSbn().getGroupKey());
+        group.setSummary(summary);
+        mEntry.setParent(group);
+
+        // THEN don't filter out the entry
+        assertFalse(mKeyguardNotificationCoordinator.mNotifFilter.shouldFilterOut(mEntry, 0));
+    }
+
+    /**
+     * setup a state where the notification will not be filtered by the
+     * KeyguardNotificationCoordinator when the keyguard is showing.
+     */
+    private void setupUnfilteredState() {
+        // notification is for current profile
+        when(mLockscreenUserManager.isCurrentProfile(NOTIF_USER_ID)).thenReturn(true);
+
+        // keyguard is showing
+        when(mKeyguardStateController.isShowing()).thenReturn(true);
+
+        // show notifications on the lockscreen
+        when(mLockscreenUserManager.shouldShowLockscreenNotifications()).thenReturn(true);
+
+        // neither the current user nor the notification's user is in lockdown
+        when(mLockscreenUserManager.getCurrentUserId()).thenReturn(CURR_USER_ID);
+        when(mKeyguardUpdateMonitor.isUserInLockdown(NOTIF_USER_ID)).thenReturn(false);
+        when(mKeyguardUpdateMonitor.isUserInLockdown(CURR_USER_ID)).thenReturn(false);
+
+        // not in public mode
+        when(mLockscreenUserManager.isLockscreenPublicMode(CURR_USER_ID)).thenReturn(false);
+        when(mLockscreenUserManager.isLockscreenPublicMode(NOTIF_USER_ID)).thenReturn(false);
+
+        // entry's ranking - should show on all lockscreens
+        // + priority of the notification exceeds the threshold to be shown on the lockscreen
+        mEntry.setRanking(new RankingBuilder()
+                .setKey(mEntry.getKey())
+                .setVisibilityOverride(VISIBILITY_PUBLIC)
+                .setImportance(IMPORTANCE_HIGH)
+                .build());
+
+        // settings allows notifications in public mode
+        when(mLockscreenUserManager.userAllowsNotificationsInPublic(CURR_USER_ID)).thenReturn(true);
+        when(mLockscreenUserManager.userAllowsNotificationsInPublic(NOTIF_USER_ID))
+                .thenReturn(true);
+
+        // notification doesn't have a summary
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index d20a37a..d2b4a20 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -74,6 +74,7 @@
 import com.android.systemui.statusbar.notification.collection.NotificationRankingManager;
 import com.android.systemui.statusbar.notification.logging.NotifLog;
 import com.android.systemui.statusbar.notification.people.PeopleHubSectionFooterViewAdapter;
+import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
 import com.android.systemui.statusbar.notification.row.FooterView;
 import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager;
@@ -164,7 +165,8 @@
                         mHeadsUpManager,
                         mock(NotificationFilter.class),
                         mock(NotifLog.class),
-                        mock(NotificationSectionsFeatureManager.class)
+                        mock(NotificationSectionsFeatureManager.class),
+                        mock(PeopleNotificationIdentifier.class)
                 ),
                 mock(NotificationEntryManager.KeyguardEnvironment.class));
         mDependency.injectTestDependency(NotificationEntryManager.class, mEntryManager);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
index 222fcb6..46f6cfe 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DozeServiceHostTest.java
@@ -85,7 +85,6 @@
     @Mock private StatusBar mStatusBar;
     @Mock private NotificationIconAreaController mNotificationIconAreaController;
     @Mock private StatusBarWindowViewController mStatusBarWindowViewController;
-    @Mock private StatusBarWindowView mStatusBarWindow;
     @Mock private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
     @Mock private NotificationPanelView mNotificationPanel;
     @Mock private View mAmbientIndicationContainer;
@@ -101,10 +100,11 @@
                 mKeyguardViewMediator, () -> mAssistManager, mDozeScrimController,
                 mKeyguardUpdateMonitor, mVisualStabilityManager, mPulseExpansionHandler,
                 mStatusBarWindowController, mNotificationWakeUpCoordinator,
-                mStatusBarWindowViewController, mLockscreenLockIconController);
+                mLockscreenLockIconController);
 
-        mDozeServiceHost.initialize(mStatusBar, mNotificationIconAreaController, mStatusBarWindow,
-                mStatusBarKeyguardViewManager, mNotificationPanel, mAmbientIndicationContainer);
+        mDozeServiceHost.initialize(mStatusBar, mNotificationIconAreaController,
+                mStatusBarKeyguardViewManager, mStatusBarWindowViewController, mNotificationPanel,
+                mAmbientIndicationContainer);
     }
 
     @Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
index 6f5cfbe..77cdc02 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
@@ -102,7 +102,7 @@
     @Mock
     private RemoteInputController mRemoteInputController;
     @Mock
-    private ShadeController mShadeController;
+    private StatusBar mStatusBar;
     @Mock
     private KeyguardStateController mKeyguardStateController;
     @Mock
@@ -175,7 +175,7 @@
                 mock(NotificationInterruptionStateProvider.class), mock(MetricsLogger.class),
                 mock(LockPatternUtils.class), mHandler, mHandler, mActivityIntentHelper,
                 mBubbleController, mSuperStatusBarViewFactory))
-                .setShadeController(mShadeController)
+                .setStatusBar(mStatusBar)
                 .setNotificationPresenter(mock(NotificationPresenter.class))
                 .setActivityLaunchAnimator(mock(ActivityLaunchAnimator.class))
         .build();
@@ -186,11 +186,11 @@
 
         // set up addAfterKeyguardGoneRunnable to synchronously invoke the Runnable arg
         doAnswer(answerVoid(Runnable::run))
-                .when(mShadeController).addAfterKeyguardGoneRunnable(any(Runnable.class));
+                .when(mStatusBar).addAfterKeyguardGoneRunnable(any(Runnable.class));
 
         // set up addPostCollapseAction to synchronously invoke the Runnable arg
         doAnswer(answerVoid(Runnable::run))
-                .when(mShadeController).addPostCollapseAction(any(Runnable.class));
+                .when(mStatusBar).addPostCollapseAction(any(Runnable.class));
 
         // set up Handler to synchronously invoke the Runnable arg
         doAnswer(answerVoid(Runnable::run))
@@ -209,13 +209,13 @@
         sbn.getNotification().flags |= Notification.FLAG_AUTO_CANCEL;
 
         when(mKeyguardStateController.isShowing()).thenReturn(true);
-        when(mShadeController.isOccluded()).thenReturn(true);
+        when(mStatusBar.isOccluded()).thenReturn(true);
 
         // When
         mNotificationActivityStarter.onNotificationClicked(sbn, mNotificationRow);
 
         // Then
-        verify(mShadeController, atLeastOnce()).collapsePanel();
+        verify(mStatusBar, atLeastOnce()).collapsePanel();
 
         verify(mContentIntent).sendAndReturnResult(
                 any(Context.class),
@@ -250,7 +250,7 @@
         verify(mBubbleController).expandStackAndSelectBubble(eq(sbn.getKey()));
 
         // This is called regardless, and simply short circuits when there is nothing to do.
-        verify(mShadeController, atLeastOnce()).collapsePanel();
+        verify(mStatusBar, atLeastOnce()).collapsePanel();
 
         verify(mAssistManager).hideAssist();
 
@@ -272,7 +272,7 @@
         // Given
         sbn.getNotification().contentIntent = null;
         when(mKeyguardStateController.isShowing()).thenReturn(true);
-        when(mShadeController.isOccluded()).thenReturn(true);
+        when(mStatusBar.isOccluded()).thenReturn(true);
 
         // When
         mNotificationActivityStarter.onNotificationClicked(sbn, mBubbleNotificationRow);
@@ -280,7 +280,7 @@
         // Then
         verify(mBubbleController).expandStackAndSelectBubble(eq(sbn.getKey()));
 
-        verify(mShadeController, atLeastOnce()).collapsePanel();
+        verify(mStatusBar, atLeastOnce()).collapsePanel();
 
         verify(mAssistManager).hideAssist();
 
@@ -302,7 +302,7 @@
         // Given
         sbn.getNotification().contentIntent = mContentIntent;
         when(mKeyguardStateController.isShowing()).thenReturn(true);
-        when(mShadeController.isOccluded()).thenReturn(true);
+        when(mStatusBar.isOccluded()).thenReturn(true);
 
         // When
         mNotificationActivityStarter.onNotificationClicked(sbn, mBubbleNotificationRow);
@@ -310,7 +310,7 @@
         // Then
         verify(mBubbleController).expandStackAndSelectBubble(eq(sbn.getKey()));
 
-        verify(mShadeController, atLeastOnce()).collapsePanel();
+        verify(mStatusBar, atLeastOnce()).collapsePanel();
 
         verify(mAssistManager).hideAssist();
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
index 1c02b60..14f5795 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationPresenterTest.java
@@ -71,10 +71,11 @@
 public class StatusBarNotificationPresenterTest extends SysuiTestCase {
 
 
-    private StatusBarNotificationPresenter mStatusBar;
+    private StatusBarNotificationPresenter mStatusBarNotificationPresenter;
     private CommandQueue mCommandQueue;
     private FakeMetricsLogger mMetricsLogger;
     private ShadeController mShadeController = mock(ShadeController.class);
+    private StatusBar mStatusBar = mock(StatusBar.class);
 
     @Before
     public void setup() {
@@ -105,14 +106,14 @@
 
         StatusBarWindowView statusBarWindowView = mock(StatusBarWindowView.class);
         when(statusBarWindowView.getResources()).thenReturn(mContext.getResources());
-        mStatusBar = new StatusBarNotificationPresenter(mContext,
+        mStatusBarNotificationPresenter = new StatusBarNotificationPresenter(mContext,
                 mock(NotificationPanelView.class), mock(HeadsUpManagerPhone.class),
                 statusBarWindowView, mock(NotificationListContainerViewGroup.class),
                 mock(DozeScrimController.class), mock(ScrimController.class),
                 mock(ActivityLaunchAnimator.class), mock(DynamicPrivacyController.class),
                 mock(NotificationAlertingManager.class),
                 mock(NotificationRowBinderImpl.class), mock(KeyguardStateController.class),
-                mCommandQueue);
+                mStatusBar, mCommandQueue);
     }
 
     @Test
@@ -129,7 +130,7 @@
         TestableLooper.get(this).processAllMessages();
 
         assertFalse("The panel shouldn't allow heads up while disabled",
-                mStatusBar.canHeadsUp(entry, entry.getSbn()));
+                mStatusBarNotificationPresenter.canHeadsUp(entry, entry.getSbn()));
     }
 
     @Test
@@ -146,13 +147,13 @@
         TestableLooper.get(this).processAllMessages();
 
         assertFalse("The panel shouldn't allow heads up while notitifcation shade disabled",
-                mStatusBar.canHeadsUp(entry, entry.getSbn()));
+                mStatusBarNotificationPresenter.canHeadsUp(entry, entry.getSbn()));
     }
 
     @Test
     public void onActivatedMetrics() {
         ActivatableNotificationView view =  mock(ActivatableNotificationView.class);
-        mStatusBar.onActivated(view);
+        mStatusBarNotificationPresenter.onActivated(view);
 
         MetricsAsserts.assertHasLog("missing lockscreen note tap log",
                 mMetricsLogger.getLogs(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index e78fb9e..be68097 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -145,6 +145,8 @@
 import java.util.ArrayList;
 import java.util.Optional;
 
+import javax.inject.Provider;
+
 import dagger.Lazy;
 
 @SmallTest
@@ -227,6 +229,9 @@
     @Mock private VolumeComponent mVolumeComponent;
     @Mock private CommandQueue mCommandQueue;
     @Mock private Recents mRecents;
+    @Mock private Provider<StatusBarComponent.Builder> mStatusBarComponentBuilderProvider;
+    @Mock private StatusBarComponent.Builder mStatusBarComponentBuilder;
+    @Mock private StatusBarComponent mStatusBarComponent;
     @Mock private PluginManager mPluginManager;
     @Mock private Divider mDivider;
     @Mock private SuperStatusBarViewFactory mSuperStatusBarViewFactory;
@@ -300,6 +305,11 @@
         when(mLockscreenWallpaperLazy.get()).thenReturn(mLockscreenWallpaper);
         when(mBiometricUnlockControllerLazy.get()).thenReturn(mBiometricUnlockController);
 
+        when(mStatusBarComponentBuilderProvider.get()).thenReturn(mStatusBarComponentBuilder);
+        when(mStatusBarComponentBuilder.build()).thenReturn(mStatusBarComponent);
+        when(mStatusBarComponent.getStatusBarWindowViewController()).thenReturn(
+                mStatusBarWindowViewController);
+
         mStatusBar = new StatusBar(
                 mContext,
                 mFeatureFlags,
@@ -354,7 +364,6 @@
                 mNotificationListener,
                 configurationController,
                 mStatusBarWindowController,
-                mStatusBarWindowViewController,
                 mLockscreenLockIconController,
                 mDozeParameters,
                 mScrimController,
@@ -367,6 +376,7 @@
                 mVolumeComponent,
                 mCommandQueue,
                 Optional.of(mRecents),
+                mStatusBarComponentBuilderProvider,
                 mPluginManager,
                 mRemoteInputUriController,
                 Optional.of(mDivider),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowViewTest.java
index ee9ea9f..529333e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarWindowViewTest.java
@@ -36,7 +36,6 @@
 import com.android.systemui.statusbar.DragDownHelper;
 import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.PulseExpansionHandler;
-import com.android.systemui.statusbar.SuperStatusBarViewFactory;
 import com.android.systemui.statusbar.SysuiStatusBarStateController;
 import com.android.systemui.statusbar.notification.DynamicPrivacyController;
 import com.android.systemui.statusbar.notification.NotificationEntryManager;
@@ -74,7 +73,6 @@
     @Mock private StatusBar mStatusBar;
     @Mock private DozeLog mDozeLog;
     @Mock private DozeParameters mDozeParameters;
-    @Mock private SuperStatusBarViewFactory mSuperStatusBarViewFactory;
     @Mock private DockManager mDockManager;
 
     @Before
@@ -85,7 +83,6 @@
         when(mStatusBar.isDozing()).thenReturn(false);
         mDependency.injectTestDependency(ShadeController.class, mShadeController);
 
-        when(mSuperStatusBarViewFactory.getStatusBarWindowView()).thenReturn(mView);
         when(mDockManager.isDocked()).thenReturn(false);
 
         mController = new StatusBarWindowViewController(
@@ -105,9 +102,9 @@
                 mDozeLog,
                 mDozeParameters,
                 new CommandQueue(mContext),
-                mSuperStatusBarViewFactory,
                 () -> mShadeController,
-                mDockManager);
+                mDockManager,
+                mView);
         mController.setupExpandedStatusBar();
         mController.setService(mStatusBar);
         mController.setDragDownHelper(mDragDownHelper);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
index ab74caa..95b055c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerDataTest.java
@@ -500,7 +500,7 @@
     public void testUpdateDataNetworkName() {
         setupDefaultSignal();
         String newDataName = "TestDataName";
-        when(mServiceState.getDataOperatorAlphaShort()).thenReturn(newDataName);
+        when(mServiceState.getOperatorAlphaShort()).thenReturn(newDataName);
         updateServiceState();
         assertDataNetworkNameEquals(newDataName);
     }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeExecutor.java b/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeExecutor.java
new file mode 100644
index 0000000..3ed6c5b
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeExecutor.java
@@ -0,0 +1,227 @@
+/*
+ * 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.util.concurrency;
+
+import com.android.systemui.util.time.FakeSystemClock;
+import com.android.systemui.util.time.FakeSystemClock.ClockTickListener;
+
+import java.util.Collections;
+import java.util.PriorityQueue;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class FakeExecutor implements DelayableExecutor {
+    private final FakeSystemClock mClock;
+    private PriorityQueue<QueuedRunnable> mQueuedRunnables = new PriorityQueue<>();
+    private boolean mIgnoreClockUpdates;
+
+    private ClockTickListener mClockTickListener = new ClockTickListener() {
+        @Override
+        public void onUptimeMillis(long uptimeMillis) {
+            if (!mIgnoreClockUpdates) {
+                runAllReady();
+            }
+        }
+    };
+
+    /**
+     * Initializes a fake executor.
+     *
+     * @param clock FakeSystemClock allowing control over delayed runnables. It is strongly
+     *              recommended that this clock have its auto-increment setting set to false to
+     *              prevent unexpected advancement of the time.
+     */
+    public FakeExecutor(FakeSystemClock clock) {
+        mClock = clock;
+        mClock.addListener(mClockTickListener);
+    }
+
+    /**
+     * Runs a single runnable if it's scheduled to run according to the internal clock.
+     *
+     * If constructed to advance the clock automatically, this will advance the clock enough to
+     * run the next pending item.
+     *
+     * This method does not advance the clock past the item that was run.
+     *
+     * @return Returns true if an item was run.
+     */
+    public boolean runNextReady() {
+        if (!mQueuedRunnables.isEmpty() && mQueuedRunnables.peek().mWhen <= mClock.uptimeMillis()) {
+            mQueuedRunnables.poll().mRunnable.run();
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * Runs all Runnables that are scheduled to run according to the internal clock.
+     *
+     * If constructed to advance the clock automatically, this will advance the clock enough to
+     * run all the pending items. This method does not advance the clock past items that were
+     * run. It is equivalent to calling {@link #runNextReady()} in a loop.
+     *
+     * @return Returns the number of items that ran.
+     */
+    public int runAllReady() {
+        int num = 0;
+        while (runNextReady()) {
+            num++;
+        }
+
+        return num;
+    }
+
+    /**
+     * Advances the internal clock to the next item to run.
+     *
+     * The clock will only move forward. If the next item is set to run in the past or there is no
+     * next item, the clock does not change.
+     *
+     * Note that this will cause one or more items to actually run.
+     *
+     * @return The delta in uptimeMillis that the clock advanced, or 0 if the clock did not advance.
+     */
+    public long advanceClockToNext() {
+        if (mQueuedRunnables.isEmpty()) {
+            return 0;
+        }
+
+        long startTime = mClock.uptimeMillis();
+        long nextTime = mQueuedRunnables.peek().mWhen;
+        if (nextTime <= startTime) {
+            return 0;
+        }
+        updateClock(nextTime);
+
+        return nextTime - startTime;
+    }
+
+
+    /**
+     * Advances the internal clock to the last item to run.
+     *
+     * The clock will only move forward. If the last item is set to run in the past or there is no
+     * next item, the clock does not change.
+     *
+     * @return The delta in uptimeMillis that the clock advanced, or 0 if the clock did not advance.
+     */
+    public long advanceClockToLast() {
+        if (mQueuedRunnables.isEmpty()) {
+            return 0;
+        }
+
+        long startTime = mClock.uptimeMillis();
+        long nextTime = Collections.max(mQueuedRunnables).mWhen;
+        if (nextTime <= startTime) {
+            return 0;
+        }
+
+        updateClock(nextTime);
+
+        return nextTime - startTime;
+    }
+
+    /**
+     * Returns the number of un-executed runnables waiting to run.
+     */
+    public int numPending() {
+        return mQueuedRunnables.size();
+    }
+
+    @Override
+    public Runnable executeDelayed(Runnable r, long delay, TimeUnit unit) {
+        if (delay < 0) {
+            delay = 0;
+        }
+        return executeAtTime(r, mClock.uptimeMillis() + unit.toMillis(delay));
+    }
+
+    @Override
+    public Runnable executeAtTime(Runnable r, long uptime, TimeUnit unit) {
+        long uptimeMillis = unit.toMillis(uptime);
+
+        QueuedRunnable container = new QueuedRunnable(r, uptimeMillis);
+
+        mQueuedRunnables.offer(container);
+
+        return () -> mQueuedRunnables.remove(container);
+    }
+
+    @Override
+    public void execute(Runnable command) {
+        executeDelayed(command, 0);
+    }
+
+    @Override
+    public void removeAll() {
+        mQueuedRunnables.clear();
+    }
+
+    /**
+     * Run all Executors in a loop until they all report they have no ready work to do.
+     *
+     * Useful if you have Executors the post work to other Executors, and you simply want to
+     * run them all until they stop posting work.
+     */
+    public static void exhaustExecutors(FakeExecutor ...executors) {
+        boolean didAnything;
+        do {
+            didAnything = false;
+            for (FakeExecutor executor : executors) {
+                didAnything = didAnything || executor.runAllReady() != 0;
+            }
+        } while (didAnything);
+    }
+
+    private void updateClock(long nextTime) {
+        mIgnoreClockUpdates = true;
+        mClock.setUptimeMillis(nextTime);
+        mIgnoreClockUpdates = false;
+    }
+
+    private static class QueuedRunnable implements Comparable<QueuedRunnable> {
+        private static AtomicInteger sCounter = new AtomicInteger();
+
+        Runnable mRunnable;
+        long mWhen;
+        private int mCounter;
+
+        private QueuedRunnable(Runnable r, long when) {
+            mRunnable = r;
+            mWhen = when;
+
+            // PrioirityQueue orders items arbitrarily when equal. We want to ensure that
+            // otherwise-equal elements are ordered according to their insertion order. Because this
+            // class only is constructed right before insertion, we use a static counter to track
+            // insertion order of otherwise equal elements.
+            mCounter = sCounter.incrementAndGet();
+        }
+
+        @Override
+        public int compareTo(QueuedRunnable other) {
+            long diff = mWhen - other.mWhen;
+
+            if (diff == 0) {
+                return mCounter - other.mCounter;
+            }
+
+            return diff > 0 ? 1 : -1;
+        }
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeExecutorTest.java b/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeExecutorTest.java
new file mode 100644
index 0000000..7fd69424
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/concurrency/FakeExecutorTest.java
@@ -0,0 +1,366 @@
+/*
+ * 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.util.concurrency;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.util.time.FakeSystemClock;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import kotlin.jvm.functions.Function4;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class FakeExecutorTest extends SysuiTestCase {
+    @Before
+    public void setUp() throws Exception {
+    }
+
+    /**
+     * Test FakeExecutor that receives non-delayed items to execute.
+     */
+    @Test
+    public void testNoDelay() {
+        FakeSystemClock clock = new FakeSystemClock();
+        clock.setAutoIncrement(false);
+        FakeExecutor fakeExecutor = new FakeExecutor(clock);
+        RunnableImpl runnable = new RunnableImpl();
+
+        assertEquals(0, clock.uptimeMillis());
+        assertEquals(0, runnable.mRunCount);
+
+        // Execute two runnables. They should not run and should be left pending.
+        fakeExecutor.execute(runnable);
+        assertEquals(0, runnable.mRunCount);
+        assertEquals(0, clock.uptimeMillis());
+        assertEquals(1, fakeExecutor.numPending());
+        fakeExecutor.execute(runnable);
+        assertEquals(0, runnable.mRunCount);
+        assertEquals(0, clock.uptimeMillis());
+        assertEquals(2, fakeExecutor.numPending());
+
+        // Run one pending runnable.
+        assertTrue(fakeExecutor.runNextReady());
+        assertEquals(1, runnable.mRunCount);
+        assertEquals(0, clock.uptimeMillis());
+        assertEquals(1, fakeExecutor.numPending());
+        // Run a second pending runnable.
+        assertTrue(fakeExecutor.runNextReady());
+        assertEquals(2, runnable.mRunCount);
+        assertEquals(0, clock.uptimeMillis());
+        assertEquals(0, fakeExecutor.numPending());
+
+        // No more runnables to run.
+        assertFalse(fakeExecutor.runNextReady());
+
+        // Add two more runnables.
+        fakeExecutor.execute(runnable);
+        fakeExecutor.execute(runnable);
+        assertEquals(2, runnable.mRunCount);
+        assertEquals(0, clock.uptimeMillis());
+        assertEquals(2, fakeExecutor.numPending());
+        // Execute all pending runnables in batch.
+        assertEquals(2, fakeExecutor.runAllReady());
+        assertEquals(4, runnable.mRunCount);
+        assertEquals(0, clock.uptimeMillis());
+        assertEquals(0, fakeExecutor.runAllReady());
+    }
+
+    /**
+     * Test FakeExecutor that is told to delay execution on items.
+     */
+    @Test
+    public void testDelayed() {
+        FakeSystemClock clock = new FakeSystemClock();
+        clock.setAutoIncrement(false);
+        FakeExecutor fakeExecutor = new FakeExecutor(clock);
+        RunnableImpl runnable = new RunnableImpl();
+
+        // Add three delayed runnables.
+        fakeExecutor.executeDelayed(runnable, 1);
+        fakeExecutor.executeDelayed(runnable, 50);
+        fakeExecutor.executeDelayed(runnable, 100);
+        assertEquals(0, runnable.mRunCount);
+        assertEquals(0, clock.uptimeMillis());
+        assertEquals(3, fakeExecutor.numPending());
+        // Delayed runnables should not advance the clock and therefore should not run.
+        assertFalse(fakeExecutor.runNextReady());
+        assertEquals(0, fakeExecutor.runAllReady());
+        assertEquals(3, fakeExecutor.numPending());
+
+        // Advance the clock to the next runnable. One runnable should execute.
+        assertEquals(1, fakeExecutor.advanceClockToNext());
+        assertEquals(1, fakeExecutor.runAllReady());
+        assertEquals(2, fakeExecutor.numPending());
+        assertEquals(1, runnable.mRunCount);
+        // Advance the clock to the last runnable.
+        assertEquals(99, fakeExecutor.advanceClockToLast());
+        assertEquals(2, fakeExecutor.runAllReady());
+        // Now all remaining runnables should execute.
+        assertEquals(0, fakeExecutor.numPending());
+        assertEquals(3, runnable.mRunCount);
+    }
+
+    /**
+     * Test FakeExecutor that is told to delay execution on items.
+     */
+    @Test
+    public void testDelayed_AdvanceAndRun() {
+        FakeSystemClock clock = new FakeSystemClock();
+        clock.setAutoIncrement(false);
+        FakeExecutor fakeExecutor = new FakeExecutor(clock);
+        RunnableImpl runnable = new RunnableImpl();
+
+        // Add three delayed runnables.
+        fakeExecutor.executeDelayed(runnable, 1);
+        fakeExecutor.executeDelayed(runnable, 50);
+        fakeExecutor.executeDelayed(runnable, 100);
+        assertEquals(0, runnable.mRunCount);
+        assertEquals(0, clock.uptimeMillis());
+        assertEquals(3, fakeExecutor.numPending());
+        // Delayed runnables should not advance the clock and therefore should not run.
+        assertFalse(fakeExecutor.runNextReady());
+        assertEquals(0, fakeExecutor.runAllReady());
+        assertEquals(3, fakeExecutor.numPending());
+
+        // Advance the clock to the next runnable. Check that it is run.
+        assertEquals(1, fakeExecutor.advanceClockToNext());
+        assertEquals(1, fakeExecutor.runAllReady());
+        assertEquals(1, clock.uptimeMillis());
+        assertEquals(2, fakeExecutor.numPending());
+        assertEquals(1, runnable.mRunCount);
+        assertEquals(49, fakeExecutor.advanceClockToNext());
+        assertEquals(1, fakeExecutor.runAllReady());
+        assertEquals(50, clock.uptimeMillis());
+        assertEquals(1, fakeExecutor.numPending());
+        assertEquals(2, runnable.mRunCount);
+        assertEquals(50, fakeExecutor.advanceClockToNext());
+        assertEquals(1, fakeExecutor.runAllReady());
+        assertEquals(100, clock.uptimeMillis());
+        assertEquals(0, fakeExecutor.numPending());
+        assertEquals(3, runnable.mRunCount);
+
+        // Nothing left to do
+        assertEquals(0, fakeExecutor.advanceClockToNext());
+        assertEquals(0, fakeExecutor.runAllReady());
+        assertEquals(100, clock.uptimeMillis());
+        assertEquals(0, fakeExecutor.numPending());
+        assertEquals(3, runnable.mRunCount);
+    }
+
+    /**
+     * Test execution order.
+     */
+    @Test
+    public void testExecutionOrder() {
+        FakeSystemClock clock = new FakeSystemClock();
+        clock.setAutoIncrement(false);
+        FakeExecutor fakeExecutor = new FakeExecutor(clock);
+        RunnableImpl runnableA = new RunnableImpl();
+        RunnableImpl runnableB = new RunnableImpl();
+        RunnableImpl runnableC = new RunnableImpl();
+        RunnableImpl runnableD = new RunnableImpl();
+
+        Function4<Integer, Integer, Integer, Integer, Void> checkRunCounts =
+                (Integer argA, Integer argB, Integer argC, Integer argD) -> {
+                    assertEquals("RunnableA run count wrong", argA.intValue(), runnableA.mRunCount);
+                    assertEquals("RunnableB run count wrong", argB.intValue(), runnableB.mRunCount);
+                    assertEquals("RunnableC run count wrong", argC.intValue(), runnableC.mRunCount);
+                    assertEquals("RunnableD run count wrong", argD.intValue(), runnableD.mRunCount);
+                    return null;
+                };
+
+        assertEquals(0, clock.uptimeMillis());
+        checkRunCounts.invoke(0, 0, 0, 0);
+
+        fakeExecutor.execute(runnableA);
+        fakeExecutor.execute(runnableB);
+        fakeExecutor.execute(runnableC);
+        fakeExecutor.execute(runnableD);
+
+        fakeExecutor.runNextReady();
+        checkRunCounts.invoke(1, 0, 0, 0);
+        fakeExecutor.runNextReady();
+        checkRunCounts.invoke(1, 1, 0, 0);
+        fakeExecutor.runNextReady();
+        checkRunCounts.invoke(1, 1, 1, 0);
+        fakeExecutor.runNextReady();
+        checkRunCounts.invoke(1, 1, 1, 1);
+
+        fakeExecutor.executeDelayed(runnableA, 100);
+        fakeExecutor.execute(runnableB);
+        fakeExecutor.executeDelayed(runnableC, 50);
+        fakeExecutor.execute(runnableD);
+
+        fakeExecutor.advanceClockToNext();
+        fakeExecutor.runAllReady();
+        checkRunCounts.invoke(1, 2, 1, 2);
+        fakeExecutor.advanceClockToNext();
+        fakeExecutor.runAllReady();
+        checkRunCounts.invoke(1, 2, 2, 2);
+        fakeExecutor.advanceClockToNext();
+        fakeExecutor.runAllReady();
+        checkRunCounts.invoke(2, 2, 2, 2);
+
+        fakeExecutor.execute(runnableA);
+        fakeExecutor.executeAtTime(runnableB, 0);  // this is in the past!
+        fakeExecutor.executeAtTime(runnableC, 1000);
+        fakeExecutor.executeAtTime(runnableD, 500);
+
+        fakeExecutor.advanceClockToNext();
+        fakeExecutor.runAllReady();
+        checkRunCounts.invoke(3, 3, 2, 2);
+        fakeExecutor.advanceClockToNext();
+        fakeExecutor.runAllReady();
+        checkRunCounts.invoke(3, 3, 2, 3);
+        fakeExecutor.advanceClockToNext();
+        fakeExecutor.runAllReady();
+        checkRunCounts.invoke(3, 3, 3, 3);
+    }
+
+    /**
+     * Test removing a single item.
+     */
+    @Test
+    public void testRemoval_single() {
+        FakeSystemClock clock = new FakeSystemClock();
+        clock.setAutoIncrement(false);
+        FakeExecutor fakeExecutor = new FakeExecutor(clock);
+        RunnableImpl runnable = new RunnableImpl();
+        Runnable removeFunction;
+
+        // Nothing to remove.
+        assertEquals(0, runnable.mRunCount);
+        assertEquals(0, fakeExecutor.numPending());
+
+        // Two pending items that have not yet run.
+        // We will try to remove the second item.
+        fakeExecutor.executeDelayed(runnable, 100);
+        removeFunction = fakeExecutor.executeDelayed(runnable, 200);
+        assertEquals(2, fakeExecutor.numPending());
+        assertEquals(0, runnable.mRunCount);
+
+        // Remove the item.
+        removeFunction.run();
+        assertEquals(1, fakeExecutor.numPending());
+        assertEquals(0, runnable.mRunCount);
+
+        // One item to run.
+        fakeExecutor.advanceClockToLast();
+        fakeExecutor.runAllReady();
+        assertEquals(0, fakeExecutor.numPending());
+        assertEquals(1, runnable.mRunCount);
+
+        // Nothing to remove.
+        removeFunction.run();
+        fakeExecutor.runAllReady();
+        assertEquals(0, fakeExecutor.numPending());
+        assertEquals(1, runnable.mRunCount);
+    }
+
+    /**
+     * Test removing multiple items.
+     */
+    @Test
+    public void testRemoval_multi() {
+        FakeSystemClock clock = new FakeSystemClock();
+        clock.setAutoIncrement(false);
+        FakeExecutor fakeExecutor = new FakeExecutor(clock);
+        List<Runnable> removeFunctions = new ArrayList<>();
+        RunnableImpl runnable = new RunnableImpl();
+
+        // Nothing to remove.
+        assertEquals(0, runnable.mRunCount);
+        assertEquals(0, fakeExecutor.numPending());
+
+        // Three pending items that have not yet run.
+        // We will try to remove the first and third items.
+        removeFunctions.add(fakeExecutor.executeDelayed(runnable, 100));
+        fakeExecutor.executeDelayed(runnable, 200);
+        removeFunctions.add(fakeExecutor.executeDelayed(runnable, 300));
+        assertEquals(3, fakeExecutor.numPending());
+        assertEquals(0, runnable.mRunCount);
+
+        // Remove the items.
+        removeFunctions.forEach(Runnable::run);
+        assertEquals(1, fakeExecutor.numPending());
+        assertEquals(0, runnable.mRunCount);
+
+        // One item to run.
+        fakeExecutor.advanceClockToLast();
+        fakeExecutor.runAllReady();
+        assertEquals(0, fakeExecutor.numPending());
+        assertEquals(1, runnable.mRunCount);
+
+        // Nothing to remove.
+        removeFunctions.forEach(Runnable::run);
+        assertEquals(0, fakeExecutor.numPending());
+        assertEquals(1, runnable.mRunCount);
+    }
+
+    /**
+     * Test removing everything
+     */
+    @Test
+    public void testRemoval_all() {
+        FakeSystemClock clock = new FakeSystemClock();
+        clock.setAutoIncrement(false);
+        FakeExecutor fakeExecutor = new FakeExecutor(clock);
+        RunnableImpl runnable = new RunnableImpl();
+
+        // Nothing to remove.
+        assertEquals(0, runnable.mRunCount);
+        assertEquals(0, fakeExecutor.numPending());
+
+        // Two pending items that have not yet run.
+        fakeExecutor.executeDelayed(runnable, 100);
+        fakeExecutor.executeDelayed(runnable, 200);
+        assertEquals(2, fakeExecutor.numPending());
+        assertEquals(0, runnable.mRunCount);
+
+        // Remove the items.
+        fakeExecutor.removeAll();
+
+        // Nothing to run
+        fakeExecutor.advanceClockToLast();
+        assertEquals(0, fakeExecutor.runAllReady());
+        assertEquals(0, fakeExecutor.numPending());
+        assertEquals(0, runnable.mRunCount);
+    }
+
+    private static class RunnableImpl implements Runnable {
+        int mRunCount;
+
+        @Override
+        public void run() {
+            mRunCount++;
+        }
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/util/time/FakeSystemClock.java b/packages/SystemUI/tests/src/com/android/systemui/util/time/FakeSystemClock.java
index 7b5417c..65e5902 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/util/time/FakeSystemClock.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/time/FakeSystemClock.java
@@ -16,6 +16,9 @@
 
 package com.android.systemui.util.time;
 
+import java.util.ArrayList;
+import java.util.List;
+
 public class FakeSystemClock implements SystemClock {
     private boolean mAutoIncrement = true;
 
@@ -26,11 +29,13 @@
     private long mCurrentThreadTimeMicro;
     private long mCurrentTimeMicro;
 
+    List<ClockTickListener> mListeners = new ArrayList<>();
+
     @Override
     public long uptimeMillis() {
         long value = mUptimeMillis;
         if (mAutoIncrement) {
-            mUptimeMillis++;
+            setUptimeMillis(mUptimeMillis + 1);
         }
         return value;
     }
@@ -39,7 +44,7 @@
     public long elapsedRealtime() {
         long value = mElapsedRealtime;
         if (mAutoIncrement) {
-            mElapsedRealtime++;
+            setElapsedRealtime(mElapsedRealtime + 1);
         }
         return value;
     }
@@ -48,7 +53,7 @@
     public long elapsedRealtimeNanos() {
         long value = mElapsedRealtimeNanos;
         if (mAutoIncrement) {
-            mElapsedRealtimeNanos++;
+            setElapsedRealtimeNanos(mElapsedRealtimeNanos + 1);
         }
         return value;
     }
@@ -57,7 +62,7 @@
     public long currentThreadTimeMillis() {
         long value = mCurrentThreadTimeMillis;
         if (mAutoIncrement) {
-            mCurrentThreadTimeMillis++;
+            setCurrentThreadTimeMillis(mCurrentThreadTimeMillis + 1);
         }
         return value;
     }
@@ -66,7 +71,7 @@
     public long currentThreadTimeMicro() {
         long value = mCurrentThreadTimeMicro;
         if (mAutoIncrement) {
-            mCurrentThreadTimeMicro++;
+            setCurrentThreadTimeMicro(mCurrentThreadTimeMicro + 1);
         }
         return value;
     }
@@ -75,37 +80,90 @@
     public long currentTimeMicro() {
         long value = mCurrentTimeMicro;
         if (mAutoIncrement) {
-            mCurrentTimeMicro++;
+            setCurrentTimeMicro(mCurrentTimeMicro + 1);
         }
         return value;
     }
 
     public void setUptimeMillis(long uptimeMillis) {
         mUptimeMillis = uptimeMillis;
+        for (ClockTickListener listener : mListeners) {
+            listener.onUptimeMillis(mUptimeMillis);
+        }
     }
 
     public void setElapsedRealtime(long elapsedRealtime) {
         mElapsedRealtime = elapsedRealtime;
+        for (ClockTickListener listener : mListeners) {
+            listener.onElapsedRealtime(mElapsedRealtime);
+        }
     }
 
     public void setElapsedRealtimeNanos(long elapsedRealtimeNanos) {
         mElapsedRealtimeNanos = elapsedRealtimeNanos;
+        for (ClockTickListener listener : mListeners) {
+            listener.onElapsedRealtimeNanos(mElapsedRealtimeNanos);
+        }
     }
 
     public void setCurrentThreadTimeMillis(long currentThreadTimeMillis) {
         mCurrentThreadTimeMillis = currentThreadTimeMillis;
+        for (ClockTickListener listener : mListeners) {
+            listener.onCurrentThreadTimeMillis(mCurrentThreadTimeMillis);
+        }
     }
 
     public void setCurrentThreadTimeMicro(long currentThreadTimeMicro) {
         mCurrentThreadTimeMicro = currentThreadTimeMicro;
+        for (ClockTickListener listener : mListeners) {
+            listener.onCurrentThreadTimeMicro(mCurrentThreadTimeMicro);
+        }
     }
 
     public void setCurrentTimeMicro(long currentTimeMicro) {
         mCurrentTimeMicro = currentTimeMicro;
+        for (ClockTickListener listener : mListeners) {
+            listener.onCurrentTimeMicro(mCurrentTimeMicro);
+        }
     }
 
     /** If true, each call to get____ will be one higher than the previous call to that method. */
     public void setAutoIncrement(boolean autoIncrement) {
         mAutoIncrement = autoIncrement;
     }
+
+    public void addListener(ClockTickListener listener) {
+        mListeners.add(listener);
+    }
+
+    public void removeListener(ClockTickListener listener) {
+        mListeners.remove(listener);
+    }
+
+    /** Alert all the listeners about the current time. */
+    public void synchronizeListeners() {
+        for (ClockTickListener listener : mListeners) {
+            listener.onUptimeMillis(mUptimeMillis);
+            listener.onElapsedRealtime(mElapsedRealtime);
+            listener.onElapsedRealtimeNanos(mElapsedRealtimeNanos);
+            listener.onCurrentThreadTimeMillis(mCurrentThreadTimeMillis);
+            listener.onCurrentThreadTimeMicro(mCurrentThreadTimeMicro);
+            listener.onCurrentTimeMicro(mCurrentTimeMicro);
+        }
+    }
+
+
+    public interface ClockTickListener {
+        default void onUptimeMillis(long uptimeMillis) {}
+
+        default void onElapsedRealtime(long elapsedRealtime) {}
+
+        default void onElapsedRealtimeNanos(long elapsedRealtimeNanos) {}
+
+        default void onCurrentThreadTimeMillis(long currentThreadTimeMillis) {}
+
+        default void onCurrentThreadTimeMicro(long currentThreadTimeMicro) {}
+
+        default void onCurrentTimeMicro(long currentTimeMicro) {}
+    }
 }
diff --git a/services/Android.bp b/services/Android.bp
index 041631b..3b56607 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -96,3 +96,8 @@
     name: "services-platform-compat-config",
     src: ":services",
 }
+
+filegroup {
+    name: "art-profile",
+    srcs: ["art-profile"],
+}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
index 339fc96..8ce92a3 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityWindowManager.java
@@ -972,6 +972,9 @@
         if (shouldComputeWindows) {
             mWindowManagerInternal.computeWindowsForAccessibility(displayId);
         }
+
+        mWindowManagerInternal.setAccessibilityIdToSurfaceMetadata(
+                windowToken.asBinder(), windowId);
         return windowId;
     }
 
@@ -991,7 +994,7 @@
             final int removedWindowId = removeAccessibilityInteractionConnectionInternalLocked(
                     token, mGlobalWindowTokens, mGlobalInteractionConnections);
             if (removedWindowId >= 0) {
-                onAccessibilityInteractionConnectionRemovedLocked(removedWindowId);
+                onAccessibilityInteractionConnectionRemovedLocked(removedWindowId, token);
                 if (DEBUG) {
                     Slog.i(LOG_TAG, "Removed global connection for pid:" + Binder.getCallingPid()
                             + " with windowId: " + removedWindowId + " and token: "
@@ -1007,7 +1010,8 @@
                                 getWindowTokensForUserLocked(userId),
                                 getInteractionConnectionsForUserLocked(userId));
                 if (removedWindowIdForUser >= 0) {
-                    onAccessibilityInteractionConnectionRemovedLocked(removedWindowIdForUser);
+                    onAccessibilityInteractionConnectionRemovedLocked(
+                            removedWindowIdForUser, token);
                     if (DEBUG) {
                         Slog.i(LOG_TAG, "Removed user connection for pid:" + Binder.getCallingPid()
                                 + " with windowId: " + removedWindowIdForUser + " and userId:"
@@ -1069,18 +1073,21 @@
      * @param userId The userId to remove
      */
     private void removeAccessibilityInteractionConnectionLocked(int windowId, int userId) {
+        IBinder window = null;
         if (userId == UserHandle.USER_ALL) {
+            window = mGlobalWindowTokens.get(windowId);
             mGlobalWindowTokens.remove(windowId);
             mGlobalInteractionConnections.remove(windowId);
         } else {
             if (isValidUserForWindowTokensLocked(userId)) {
+                window = getWindowTokensForUserLocked(userId).get(windowId);
                 getWindowTokensForUserLocked(userId).remove(windowId);
             }
             if (isValidUserForInteractionConnectionsLocked(userId)) {
                 getInteractionConnectionsForUserLocked(userId).remove(windowId);
             }
         }
-        onAccessibilityInteractionConnectionRemovedLocked(windowId);
+        onAccessibilityInteractionConnectionRemovedLocked(windowId, window);
         if (DEBUG) {
             Slog.i(LOG_TAG, "Removing interaction connection to windowId: " + windowId);
         }
@@ -1091,12 +1098,17 @@
      *
      * @param windowId Removed windowId
      */
-    private void onAccessibilityInteractionConnectionRemovedLocked(int windowId) {
+    private void onAccessibilityInteractionConnectionRemovedLocked(
+            int windowId, @Nullable IBinder binder) {
         // Active window will not update, if windows callback is unregistered.
         // Update active window to invalid, when its a11y interaction connection is removed.
         if (!isTrackingWindowsLocked() && windowId >= 0 && mActiveWindowId == windowId) {
             mActiveWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
         }
+        if (binder != null) {
+            mWindowManagerInternal.setAccessibilityIdToSurfaceMetadata(
+                    binder, AccessibilityWindowInfo.UNDEFINED_WINDOW_ID);
+        }
     }
 
     /**
diff --git a/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java b/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java
index 19ac0d3..1754926 100644
--- a/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java
+++ b/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java
@@ -17,6 +17,8 @@
 package com.android.server.accessibility;
 
 import android.accessibilityservice.AccessibilityService;
+import android.app.PendingIntent;
+import android.app.RemoteAction;
 import android.app.StatusBarManager;
 import android.content.Context;
 import android.hardware.input.InputManager;
@@ -25,81 +27,272 @@
 import android.os.Looper;
 import android.os.PowerManager;
 import android.os.SystemClock;
+import android.util.ArrayMap;
+import android.util.Slog;
 import android.view.InputDevice;
 import android.view.KeyCharacterMap;
 import android.view.KeyEvent;
+import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
 
+import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ScreenshotHelper;
 import com.android.server.LocalServices;
 import com.android.server.statusbar.StatusBarManagerInternal;
 import com.android.server.wm.WindowManagerInternal;
 
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
 import java.util.function.Supplier;
 
 /**
- * Handle the back-end of AccessibilityService#performGlobalAction
+ * Handle the back-end of system AccessibilityAction.
+ *
+ * This class should support three use cases with combined usage of new API and legacy API:
+ *
+ * Use case 1: SystemUI doesn't use the new system action registration API. Accessibility
+ *             service doesn't use the new system action API to obtain action list. Accessibility
+ *             service uses legacy global action id to perform predefined system actions.
+ * Use case 2: SystemUI uses the new system action registration API to register available system
+ *             actions. Accessibility service doesn't use the new system action API to obtain action
+ *             list. Accessibility service uses legacy global action id to trigger the system
+ *             actions registered by SystemUI.
+ * Use case 3: SystemUI doesn't use the new system action registration API.Accessibility service
+ *             obtains the available system actions using new AccessibilityService API and trigger
+ *             the predefined system actions.
  */
 public class SystemActionPerformer {
+    private static final String TAG = "SystemActionPerformer";
+
+    interface SystemActionsChangedListener {
+        void onSystemActionsChanged();
+    }
+    private final SystemActionsChangedListener mListener;
+
+    private final Object mSystemActionLock = new Object();
+    // Resource id based ActionId -> RemoteAction
+    @GuardedBy("mSystemActionLock")
+    private final Map<Integer, RemoteAction> mRegisteredSystemActions = new ArrayMap<>();
+
+    // Legacy system actions.
+    private final AccessibilityAction mLegacyHomeAction;
+    private final AccessibilityAction mLegacyBackAction;
+    private final AccessibilityAction mLegacyRecentsAction;
+    private final AccessibilityAction mLegacyNotificationsAction;
+    private final AccessibilityAction mLegacyQuickSettingsAction;
+    private final AccessibilityAction mLegacyPowerDialogAction;
+    private final AccessibilityAction mLegacyToggleSplitScreenAction;
+    private final AccessibilityAction mLegacyLockScreenAction;
+    private final AccessibilityAction mLegacyTakeScreenshotAction;
+
     private final WindowManagerInternal mWindowManagerService;
     private final Context mContext;
     private Supplier<ScreenshotHelper> mScreenshotHelperSupplier;
 
-    public SystemActionPerformer(Context context, WindowManagerInternal windowManagerInternal) {
-        mContext = context;
-        mWindowManagerService = windowManagerInternal;
-        mScreenshotHelperSupplier = null;
+    public SystemActionPerformer(
+            Context context,
+            WindowManagerInternal windowManagerInternal) {
+      this(context, windowManagerInternal, null, null);
     }
 
     // Used to mock ScreenshotHelper
     @VisibleForTesting
-    public SystemActionPerformer(Context context, WindowManagerInternal windowManagerInternal,
+    public SystemActionPerformer(
+            Context context,
+            WindowManagerInternal windowManagerInternal,
             Supplier<ScreenshotHelper> screenshotHelperSupplier) {
-        this(context, windowManagerInternal);
+        this(context, windowManagerInternal, screenshotHelperSupplier, null);
+    }
+
+    public SystemActionPerformer(
+            Context context,
+            WindowManagerInternal windowManagerInternal,
+            Supplier<ScreenshotHelper> screenshotHelperSupplier,
+            SystemActionsChangedListener listener) {
+        mContext = context;
+        mWindowManagerService = windowManagerInternal;
+        mListener = listener;
         mScreenshotHelperSupplier = screenshotHelperSupplier;
+
+        mLegacyHomeAction = new AccessibilityAction(
+                AccessibilityService.GLOBAL_ACTION_HOME,
+                mContext.getResources().getString(
+                        R.string.accessibility_system_action_home_label));
+        mLegacyBackAction = new AccessibilityAction(
+                AccessibilityService.GLOBAL_ACTION_BACK,
+                mContext.getResources().getString(
+                        R.string.accessibility_system_action_back_label));
+        mLegacyRecentsAction = new AccessibilityAction(
+                AccessibilityService.GLOBAL_ACTION_RECENTS,
+                mContext.getResources().getString(
+                        R.string.accessibility_system_action_recents_label));
+        mLegacyNotificationsAction = new AccessibilityAction(
+                AccessibilityService.GLOBAL_ACTION_NOTIFICATIONS,
+                mContext.getResources().getString(
+                        R.string.accessibility_system_action_notifications_label));
+        mLegacyQuickSettingsAction = new AccessibilityAction(
+                AccessibilityService.GLOBAL_ACTION_QUICK_SETTINGS,
+                mContext.getResources().getString(
+                        R.string.accessibility_system_action_quick_settings_label));
+        mLegacyPowerDialogAction = new AccessibilityAction(
+                AccessibilityService.GLOBAL_ACTION_POWER_DIALOG,
+                mContext.getResources().getString(
+                        R.string.accessibility_system_action_power_dialog_label));
+        mLegacyToggleSplitScreenAction = new AccessibilityAction(
+                AccessibilityService.GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN,
+                mContext.getResources().getString(
+                        R.string.accessibility_system_action_toggle_split_screen_label));
+        mLegacyLockScreenAction = new AccessibilityAction(
+                AccessibilityService.GLOBAL_ACTION_LOCK_SCREEN,
+                mContext.getResources().getString(
+                        R.string.accessibility_system_action_lock_screen_label));
+        mLegacyTakeScreenshotAction = new AccessibilityAction(
+                AccessibilityService.GLOBAL_ACTION_TAKE_SCREENSHOT,
+                mContext.getResources().getString(
+                        R.string.accessibility_system_action_screenshot_label));
     }
 
     /**
-     * Performe the system action matching the given action id.
+     * This method is called to register a system action. If a system action is already registered
+     * with the given id, the existing system action will be overwritten.
      */
-    public boolean performSystemAction(int action) {
+    void registerSystemAction(int id, RemoteAction action) {
+        synchronized (mSystemActionLock) {
+            mRegisteredSystemActions.put(id, action);
+        }
+        if (mListener != null) {
+            mListener.onSystemActionsChanged();
+        }
+    }
+
+    /**
+     * This method is called to unregister a system action previously registered through
+     * registerSystemAction.
+     */
+    void unregisterSystemAction(int id) {
+        synchronized (mSystemActionLock) {
+            mRegisteredSystemActions.remove(id);
+        }
+        if (mListener != null) {
+            mListener.onSystemActionsChanged();
+        }
+    }
+
+    /**
+     * This method returns the list of available system actions.
+     */
+    List<AccessibilityAction> getSystemActions() {
+        List<AccessibilityAction> systemActions = new ArrayList<>();
+        synchronized (mSystemActionLock) {
+            for (Map.Entry<Integer, RemoteAction> entry : mRegisteredSystemActions.entrySet()) {
+                AccessibilityAction systemAction = new AccessibilityAction(
+                        entry.getKey(),
+                        entry.getValue().getTitle());
+                systemActions.add(systemAction);
+            }
+
+            // add AccessibilitySystemAction entry for legacy system actions if not overwritten
+            addLegacySystemActions(systemActions);
+        }
+        return systemActions;
+    }
+
+    private void addLegacySystemActions(List<AccessibilityAction> systemActions) {
+        if (!mRegisteredSystemActions.containsKey(AccessibilityService.GLOBAL_ACTION_BACK)) {
+            systemActions.add(mLegacyBackAction);
+        }
+        if (!mRegisteredSystemActions.containsKey(AccessibilityService.GLOBAL_ACTION_HOME)) {
+            systemActions.add(mLegacyHomeAction);
+        }
+        if (!mRegisteredSystemActions.containsKey(AccessibilityService.GLOBAL_ACTION_RECENTS)) {
+            systemActions.add(mLegacyRecentsAction);
+        }
+        if (!mRegisteredSystemActions.containsKey(
+                AccessibilityService.GLOBAL_ACTION_NOTIFICATIONS)) {
+            systemActions.add(mLegacyNotificationsAction);
+        }
+        if (!mRegisteredSystemActions.containsKey(
+                AccessibilityService.GLOBAL_ACTION_QUICK_SETTINGS)) {
+            systemActions.add(mLegacyQuickSettingsAction);
+        }
+        if (!mRegisteredSystemActions.containsKey(
+                AccessibilityService.GLOBAL_ACTION_POWER_DIALOG)) {
+            systemActions.add(mLegacyPowerDialogAction);
+        }
+        if (!mRegisteredSystemActions.containsKey(
+                AccessibilityService.GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN)) {
+            systemActions.add(mLegacyToggleSplitScreenAction);
+        }
+        if (!mRegisteredSystemActions.containsKey(
+                AccessibilityService.GLOBAL_ACTION_LOCK_SCREEN)) {
+            systemActions.add(mLegacyLockScreenAction);
+        }
+        if (!mRegisteredSystemActions.containsKey(
+                AccessibilityService.GLOBAL_ACTION_TAKE_SCREENSHOT)) {
+            systemActions.add(mLegacyTakeScreenshotAction);
+        }
+    }
+
+    /**
+     * Trigger the registered action by the matching action id.
+     */
+    public boolean performSystemAction(int actionId) {
         final long identity = Binder.clearCallingIdentity();
         try {
-            switch (action) {
-                case AccessibilityService.GLOBAL_ACTION_BACK: {
-                    sendDownAndUpKeyEvents(KeyEvent.KEYCODE_BACK);
-                }
-                return true;
-                case AccessibilityService.GLOBAL_ACTION_HOME: {
-                    sendDownAndUpKeyEvents(KeyEvent.KEYCODE_HOME);
-                }
-                return true;
-                case AccessibilityService.GLOBAL_ACTION_RECENTS: {
-                    return openRecents();
-                }
-                case AccessibilityService.GLOBAL_ACTION_NOTIFICATIONS: {
-                    expandNotifications();
-                }
-                return true;
-                case AccessibilityService.GLOBAL_ACTION_QUICK_SETTINGS: {
-                    expandQuickSettings();
-                }
-                return true;
-                case AccessibilityService.GLOBAL_ACTION_POWER_DIALOG: {
-                    showGlobalActions();
-                }
-                return true;
-                case AccessibilityService.GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN: {
-                    return toggleSplitScreen();
-                }
-                case AccessibilityService.GLOBAL_ACTION_LOCK_SCREEN: {
-                    return lockScreen();
-                }
-                case AccessibilityService.GLOBAL_ACTION_TAKE_SCREENSHOT: {
-                    return takeScreenshot();
+            synchronized (mSystemActionLock) {
+                // If a system action is registered with the given actionId, call the corresponding
+                // RemoteAction.
+                RemoteAction registeredAction = mRegisteredSystemActions.get(actionId);
+                if (registeredAction != null) {
+                    try {
+                        registeredAction.getActionIntent().send();
+                        return true;
+                    } catch (PendingIntent.CanceledException ex) {
+                        Slog.e(TAG,
+                                "canceled PendingIntent for global action "
+                                        + registeredAction.getTitle(),
+                                ex);
+                    }
+                    return false;
                 }
             }
-            return false;
+
+            // No RemoteAction registered with the given actionId, try the default legacy system
+            // actions.
+            switch (actionId) {
+                case AccessibilityService.GLOBAL_ACTION_BACK: {
+                    sendDownAndUpKeyEvents(KeyEvent.KEYCODE_BACK);
+                    return true;
+                }
+                case AccessibilityService.GLOBAL_ACTION_HOME: {
+                    sendDownAndUpKeyEvents(KeyEvent.KEYCODE_HOME);
+                    return true;
+                }
+                case AccessibilityService.GLOBAL_ACTION_RECENTS:
+                    return openRecents();
+                case AccessibilityService.GLOBAL_ACTION_NOTIFICATIONS: {
+                    expandNotifications();
+                    return true;
+                }
+                case AccessibilityService.GLOBAL_ACTION_QUICK_SETTINGS: {
+                    expandQuickSettings();
+                    return true;
+                }
+                case AccessibilityService.GLOBAL_ACTION_POWER_DIALOG: {
+                    showGlobalActions();
+                    return true;
+                }
+                case AccessibilityService.GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN:
+                    return toggleSplitScreen();
+                case AccessibilityService.GLOBAL_ACTION_LOCK_SCREEN:
+                    return lockScreen();
+                case AccessibilityService.GLOBAL_ACTION_TAKE_SCREENSHOT:
+                    return takeScreenshot();
+                default:
+                    return false;
+            }
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
diff --git a/services/core/java/android/app/usage/UsageStatsManagerInternal.java b/services/core/java/android/app/usage/UsageStatsManagerInternal.java
index 6641b5b..2f8c506 100644
--- a/services/core/java/android/app/usage/UsageStatsManagerInternal.java
+++ b/services/core/java/android/app/usage/UsageStatsManagerInternal.java
@@ -281,4 +281,13 @@
             return mUsageRemaining;
         }
     }
+
+    /**
+     * Called by {@link com.android.server.usage.UsageStatsIdleService} when the device is idle to
+     * prune usage stats data for uninstalled packages.
+     *
+     * @param userId the user associated with the job
+     * @return {@code true} if the pruning was successful, {@code false} otherwise
+     */
+    public abstract boolean pruneUninstalledPackagesData(@UserIdInt int userId);
 }
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 0ad275f..753c117 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -6464,20 +6464,77 @@
                    newNetwork.name(), score, newNetwork.getCurrentScore()));
         }
 
+        // Notify requested networks are available after the default net is switched, but
+        // before LegacyTypeTracker sends legacy broadcasts
+        for (NetworkRequestInfo nri : addedRequests) notifyNetworkAvailable(newNetwork, nri);
+
         // Second pass: process all listens.
         if (wasBackgroundNetwork != newNetwork.isBackgroundNetwork()) {
-            // If the network went from background to foreground or vice versa, we need to update
-            // its foreground state. It is safe to do this after rematching the requests because
-            // NET_CAPABILITY_FOREGROUND does not affect requests, as is not a requestable
-            // capability and does not affect the network's score (see the Slog.wtf call above).
-            updateCapabilities(score, newNetwork, newNetwork.networkCapabilities);
+            // TODO : most of the following is useless because the only thing that changed
+            // here is whether the network is a background network. Clean this up.
+
+            NetworkCapabilities newNc = mixInCapabilities(newNetwork,
+                    newNetwork.networkCapabilities);
+
+            if (Objects.equals(newNetwork.networkCapabilities, newNc)) return;
+
+            final int oldPermission = getNetworkPermission(newNetwork.networkCapabilities);
+            final int newPermission = getNetworkPermission(newNc);
+            if (oldPermission != newPermission && newNetwork.created && !newNetwork.isVPN()) {
+                try {
+                    mNMS.setNetworkPermission(newNetwork.network.netId, newPermission);
+                } catch (RemoteException e) {
+                    loge("Exception in setNetworkPermission: " + e);
+                }
+            }
+
+            final NetworkCapabilities prevNc;
+            synchronized (newNetwork) {
+                prevNc = newNetwork.networkCapabilities;
+                newNetwork.setNetworkCapabilities(newNc);
+            }
+
+            updateUids(newNetwork, prevNc, newNc);
+
+            if (newNetwork.getCurrentScore() == score
+                    && newNc.equalRequestableCapabilities(prevNc)) {
+                // If the requestable capabilities haven't changed, and the score hasn't changed,
+                // then the change we're processing can't affect any requests, it can only affect
+                // the listens on this network.
+                processListenRequests(newNetwork, true);
+            } else {
+                rematchAllNetworksAndRequests();
+                notifyNetworkCallbacks(newNetwork, ConnectivityManager.CALLBACK_CAP_CHANGED);
+            }
+
+            if (prevNc != null) {
+                final boolean oldMetered = prevNc.isMetered();
+                final boolean newMetered = newNc.isMetered();
+                final boolean meteredChanged = oldMetered != newMetered;
+
+                if (meteredChanged) {
+                    maybeNotifyNetworkBlocked(newNetwork, oldMetered, newMetered,
+                            mRestrictBackground, mRestrictBackground);
+                }
+
+                final boolean roamingChanged = prevNc.hasCapability(NET_CAPABILITY_NOT_ROAMING)
+                        != newNc.hasCapability(NET_CAPABILITY_NOT_ROAMING);
+
+                // Report changes that are interesting for network statistics tracking.
+                if (meteredChanged || roamingChanged) {
+                    notifyIfacesChangedForNetworkStats();
+                }
+            }
+
+            if (!newNc.hasTransport(TRANSPORT_VPN)) {
+                // Tell VPNs about updated capabilities, since they may need to
+                // bubble those changes through.
+                updateAllVpnsCapabilities();
+            }
+
         } else {
             processListenRequests(newNetwork, false);
         }
-
-        // do this after the default net is switched, but
-        // before LegacyTypeTracker sends legacy broadcasts
-        for (NetworkRequestInfo nri : addedRequests) notifyNetworkAvailable(newNetwork, nri);
     }
 
     /**
diff --git a/services/core/java/com/android/server/OWNERS b/services/core/java/com/android/server/OWNERS
index a9c38bc..76e0c13 100644
--- a/services/core/java/com/android/server/OWNERS
+++ b/services/core/java/com/android/server/OWNERS
@@ -4,3 +4,6 @@
 # Vibrator / Threads
 per-file VibratorService.java, DisplayThread.java = michaelwr@google.com
 per-file VibratorService.java, DisplayThread.java = ogunwale@google.com
+
+# Zram writeback
+per-file ZramWriteback.java = minchan@google.com, rajekumar@google.com, srnvs@google.com
diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/services/core/java/com/android/server/PackageWatchdog.java
index e2fe76e..12debbf 100644
--- a/services/core/java/com/android/server/PackageWatchdog.java
+++ b/services/core/java/com/android/server/PackageWatchdog.java
@@ -42,6 +42,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.XmlUtils;
 
 import libcore.io.IoUtils;
@@ -200,7 +201,8 @@
     }
 
     /**
-     * Registers {@code observer} to listen for package failures
+     * Registers {@code observer} to listen for package failures. Add a new ObserverInternal for
+     * this observer if it does not already exist.
      *
      * <p>Observers are expected to call this on boot. It does not specify any packages but
      * it will resume observing any packages requested from a previous boot.
@@ -210,6 +212,11 @@
             ObserverInternal internalObserver = mAllObservers.get(observer.getName());
             if (internalObserver != null) {
                 internalObserver.registeredObserver = observer;
+            } else {
+                internalObserver = new ObserverInternal(observer.getName(), new ArrayList<>());
+                internalObserver.registeredObserver = observer;
+                mAllObservers.put(observer.getName(), internalObserver);
+                syncState("added new observer");
             }
         }
     }
@@ -415,6 +422,14 @@
          * watchdog may drop observing packages with the old name.
          */
         String getName();
+
+        /**
+         * An observer will not be pruned if this is set, even if the observer is not explicitly
+         * monitoring any packages.
+         */
+        default boolean isPersistent() {
+            return false;
+        }
     }
 
     long getTriggerFailureCount() {
@@ -626,7 +641,8 @@
             if (!failedPackages.isEmpty()) {
                 onHealthCheckFailed(observer, failedPackages);
             }
-            if (observer.packages.isEmpty()) {
+            if (observer.packages.isEmpty() && (observer.registeredObserver == null
+                    || !observer.registeredObserver.isPersistent())) {
                 Slog.i(TAG, "Discarding observer " + observer.name + ". All packages expired");
                 it.remove();
             }
@@ -802,6 +818,21 @@
         }
     }
 
+    /** Dump status of every observer in mAllObservers. */
+    public void dump(IndentingPrintWriter pw) {
+        pw.println("Package Watchdog status");
+        pw.increaseIndent();
+        synchronized (mLock) {
+            for (String observerName : mAllObservers.keySet()) {
+                pw.println("Observer name: " + observerName);
+                pw.increaseIndent();
+                ObserverInternal observerInternal = mAllObservers.get(observerName);
+                observerInternal.dump(pw);
+                pw.decreaseIndent();
+            }
+        }
+    }
+
     /**
      * Represents an observer monitoring a set of packages along with the failure thresholds for
      * each package.
@@ -944,6 +975,22 @@
             }
             return new ObserverInternal(observerName, packages);
         }
+
+        /** Dumps information about this observer and the packages it watches. */
+        public void dump(IndentingPrintWriter pw) {
+            boolean isPersistent = registeredObserver != null && registeredObserver.isPersistent();
+            pw.println("Persistent: " + isPersistent);
+            for (String packageName : packages.keySet()) {
+                MonitoredPackage p = packages.get(packageName);
+                pw.println(packageName +  ": ");
+                pw.increaseIndent();
+                pw.println("# Failures: " + p.mFailureHistory.size());
+                pw.println("Monitoring duration remaining: " + p.mDurationMs + "ms");
+                pw.println("Explicit health check duration: " + p.mHealthCheckDurationMs + "ms");
+                pw.println("Health check state: " + p.toString(p.mHealthCheckState));
+                pw.decreaseIndent();
+            }
+        }
     }
 
     @Retention(SOURCE)
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 24cd21e..1f7d9ea 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -17,6 +17,8 @@
 package com.android.server;
 
 import static android.telephony.TelephonyManager.ACTION_MULTI_SIM_CONFIG_CHANGED;
+import static android.telephony.TelephonyRegistryManager.SIM_ACTIVATION_TYPE_DATA;
+import static android.telephony.TelephonyRegistryManager.SIM_ACTIVATION_TYPE_VOICE;
 
 import static java.util.Arrays.copyOf;
 
@@ -51,7 +53,6 @@
 import android.telephony.LocationAccessPolicy;
 import android.telephony.PhoneCapability;
 import android.telephony.PhoneStateListener;
-import android.telephony.PhysicalChannelConfig;
 import android.telephony.PreciseCallState;
 import android.telephony.PreciseDataConnectionState;
 import android.telephony.PreciseDisconnectCause;
@@ -214,8 +215,6 @@
 
     private ArrayList<List<CellInfo>> mCellInfo = null;
 
-    private ArrayList<List<PhysicalChannelConfig>> mPhysicalChannelConfigs;
-
     private Map<Integer, List<EmergencyNumber>> mEmergencyNumberList;
 
     private EmergencyNumber[] mOutgoingSmsEmergencyNumber;
@@ -425,7 +424,6 @@
         if (mNumPhones < oldNumPhones) {
             cutListToSize(mCellInfo, mNumPhones);
             cutListToSize(mImsReasonInfo, mNumPhones);
-            cutListToSize(mPhysicalChannelConfigs, mNumPhones);
             return;
         }
 
@@ -446,7 +444,6 @@
             mCellInfo.add(i, null);
             mImsReasonInfo.add(i, null);
             mSrvccState[i] = TelephonyManager.SRVCC_STATE_HANDOVER_NONE;
-            mPhysicalChannelConfigs.add(i, new ArrayList<>());
             mOtaspMode[i] = TelephonyManager.OTASP_UNKNOWN;
             mCallDisconnectCause[i] = DisconnectCause.NOT_VALID;
             mCallPreciseDisconnectCause[i] = PreciseDisconnectCause.NOT_VALID;
@@ -523,7 +520,6 @@
         mPreciseDataConnectionState = new PreciseDataConnectionState[numPhones];
         mCellInfo = new ArrayList<>();
         mImsReasonInfo = new ArrayList<>();
-        mPhysicalChannelConfigs = new ArrayList<>();
         mEmergencyNumberList = new HashMap<>();
         mOutgoingCallEmergencyNumber = new EmergencyNumber[numPhones];
         mOutgoingSmsEmergencyNumber = new EmergencyNumber[numPhones];
@@ -543,7 +539,6 @@
             mCellInfo.add(i, null);
             mImsReasonInfo.add(i, null);
             mSrvccState[i] = TelephonyManager.SRVCC_STATE_HANDOVER_NONE;
-            mPhysicalChannelConfigs.add(i, new ArrayList<>());
             mOtaspMode[i] = TelephonyManager.OTASP_UNKNOWN;
             mCallDisconnectCause[i] = DisconnectCause.NOT_VALID;
             mCallPreciseDisconnectCause[i] = PreciseDisconnectCause.NOT_VALID;
@@ -962,14 +957,6 @@
                             remove(r.binder);
                         }
                     }
-                    if ((events & PhoneStateListener.LISTEN_PHYSICAL_CHANNEL_CONFIGURATION) != 0) {
-                        try {
-                            r.callback.onPhysicalChannelConfigurationChanged(
-                                    mPhysicalChannelConfigs.get(phoneId));
-                        } catch (RemoteException ex) {
-                            remove(r.binder);
-                        }
-                    }
                     if ((events & PhoneStateListener.LISTEN_EMERGENCY_NUMBER_LIST) != 0) {
                         try {
                             r.callback.onEmergencyNumberListChanged(mEmergencyNumberList);
@@ -1216,10 +1203,10 @@
         synchronized (mRecords) {
             if (validatePhoneId(phoneId)) {
                 switch (activationType) {
-                    case TelephonyManager.SIM_ACTIVATION_TYPE_VOICE:
+                    case SIM_ACTIVATION_TYPE_VOICE:
                         mVoiceActivationState[phoneId] = activationState;
                         break;
-                    case TelephonyManager.SIM_ACTIVATION_TYPE_DATA:
+                    case SIM_ACTIVATION_TYPE_DATA:
                         mDataActivationState[phoneId] = activationState;
                         break;
                     default:
@@ -1232,10 +1219,10 @@
                                 + " state=" + activationState);
                     }
                     try {
-                        if ((activationType == TelephonyManager.SIM_ACTIVATION_TYPE_VOICE) &&
-                                r.matchPhoneStateListenerEvent(
-                                        PhoneStateListener.LISTEN_VOICE_ACTIVATION_STATE) &&
-                                idMatch(r.subId, subId, phoneId)) {
+                        if ((activationType == SIM_ACTIVATION_TYPE_VOICE)
+                                && r.matchPhoneStateListenerEvent(
+                                        PhoneStateListener.LISTEN_VOICE_ACTIVATION_STATE)
+                                && idMatch(r.subId, subId, phoneId)) {
                             if (DBG) {
                                 log("notifyVoiceActivationStateForPhoneId: callback.onVASC r=" + r
                                         + " subId=" + subId + " phoneId=" + phoneId
@@ -1243,10 +1230,10 @@
                             }
                             r.callback.onVoiceActivationStateChanged(activationState);
                         }
-                        if ((activationType == TelephonyManager.SIM_ACTIVATION_TYPE_DATA) &&
-                                r.matchPhoneStateListenerEvent(
-                                        PhoneStateListener.LISTEN_DATA_ACTIVATION_STATE) &&
-                                idMatch(r.subId, subId, phoneId)) {
+                        if ((activationType == SIM_ACTIVATION_TYPE_DATA)
+                                && r.matchPhoneStateListenerEvent(
+                                        PhoneStateListener.LISTEN_DATA_ACTIVATION_STATE)
+                                && idMatch(r.subId, subId, phoneId)) {
                             if (DBG) {
                                 log("notifyDataActivationStateForPhoneId: callback.onDASC r=" + r
                                         + " subId=" + subId + " phoneId=" + phoneId
@@ -1394,43 +1381,6 @@
         }
     }
 
-    /**
-     * Notify physical channel configuration according to subscripton ID and phone ID
-     */
-    public void notifyPhysicalChannelConfigurationForSubscriber(int phoneId, int subId,
-            List<PhysicalChannelConfig> configs) {
-        if (!checkNotifyPermission("notifyPhysicalChannelConfiguration()")) {
-            return;
-        }
-
-        if (VDBG) {
-            log("notifyPhysicalChannelConfiguration: subId=" + subId + " phoneId=" + phoneId
-                    + " configs=" + configs);
-        }
-
-        synchronized (mRecords) {
-            if (validatePhoneId(phoneId)) {
-                mPhysicalChannelConfigs.set(phoneId, configs);
-                for (Record r : mRecords) {
-                    if (r.matchPhoneStateListenerEvent(
-                            PhoneStateListener.LISTEN_PHYSICAL_CHANNEL_CONFIGURATION)
-                            && idMatch(r.subId, subId, phoneId)) {
-                        try {
-                            if (DBG_LOC) {
-                                log("notifyPhysicalChannelConfiguration: mPhysicalChannelConfigs="
-                                        + configs + " r=" + r);
-                            }
-                            r.callback.onPhysicalChannelConfigurationChanged(configs);
-                        } catch (RemoteException ex) {
-                            mRemoveList.add(r.binder);
-                        }
-                    }
-                }
-            }
-            handleRemoveListLocked();
-        }
-    }
-
     @Override
     public void notifyMessageWaitingChangedForPhoneId(int phoneId, int subId, boolean mwi) {
         if (!checkNotifyPermission("notifyMessageWaitingChanged()")) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 53a5fc6..663e8a2 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -8365,6 +8365,31 @@
         requestBugReportWithDescription(null, null, BugreportParams.BUGREPORT_MODE_REMOTE);
     }
 
+    /**
+     * Launches a bugreport-whitelisted app to handle a bugreport.
+     *
+     * <p>Allows a bug report handler app to take bugreports on the user's behalf. The handler can
+     * be predefined in the config, meant to be launched with the primary user. The user can
+     * override this with a different (or same) handler app on possibly a different user. This is
+     * useful for capturing bug reports from work profile, for instance.
+     *
+     * @return true if there is a bugreport-whitelisted app to handle a bugreport, or false
+     * otherwise.
+     */
+    @Override
+    public boolean launchBugReportHandlerApp() {
+        if (!BugReportHandlerUtil.isBugReportHandlerEnabled(mContext)) {
+            return false;
+        }
+
+        // Always log caller, even if it does not have permission to dump.
+        Slog.i(TAG, "launchBugReportHandlerApp requested by UID " + Binder.getCallingUid());
+        enforceCallingPermission(android.Manifest.permission.DUMP,
+                "launchBugReportHandlerApp");
+
+        return BugReportHandlerUtil.launchBugReportHandlerApp(mContext);
+    }
+
     public void registerProcessObserver(IProcessObserver observer) {
         enforceCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER,
                 "registerProcessObserver()");
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 908ec6b..7f1d5a3 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -3065,7 +3065,7 @@
             pw.println("          even if in the background.");
             pw.println("  instrument [-r] [-e <NAME> <VALUE>] [-p <FILE>] [-w]");
             pw.println("          [--user <USER_ID> | current]");
-            pw.println("          [--no-hidden-api-checks [--no-test-api-checks]]");
+            pw.println("          [--no-hidden-api-checks [--no-test-api-access]]");
             pw.println("          [--no-isolated-storage]");
             pw.println("          [--no-window-animation] [--abi <ABI>] <COMPONENT>");
             pw.println("      Start an Instrumentation.  Typically this target <COMPONENT> is in the");
@@ -3085,7 +3085,7 @@
             pw.println("      --user <USER_ID> | current: Specify user instrumentation runs in;");
             pw.println("          current user if not specified.");
             pw.println("      --no-hidden-api-checks: disable restrictions on use of hidden API.");
-            pw.println("      --no-test-api-checks: disable restrictions to test APIs, if hidden");
+            pw.println("      --no-test-api-access: do not allow access to test APIs, if hidden");
             pw.println("          API checks are enabled.");
             pw.println("      --no-isolated-storage: don't use isolated storage sandbox and ");
             pw.println("          mount full external storage");
diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
index 638111e..38030c2 100644
--- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
+++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
@@ -15,22 +15,20 @@
  */
 package com.android.server.am;
 
-import static android.net.wifi.WifiManager.WIFI_FEATURE_LINK_LAYER_STATS;
-
 import android.annotation.Nullable;
 import android.bluetooth.BluetoothActivityEnergyInfo;
 import android.bluetooth.BluetoothAdapter;
 import android.content.Context;
-import android.net.wifi.IWifiManager;
-import android.net.wifi.WifiActivityEnergyInfo;
+import android.net.wifi.WifiManager;
 import android.os.BatteryStats;
+import android.os.Bundle;
 import android.os.Parcelable;
 import android.os.Process;
-import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SynchronousResultReceiver;
 import android.os.SystemClock;
 import android.os.ThreadLocalWorkSource;
+import android.os.connectivity.WifiActivityEnergyInfo;
 import android.telephony.ModemActivityInfo;
 import android.telephony.TelephonyManager;
 import android.util.IntArray;
@@ -45,6 +43,7 @@
 import libcore.util.EmptyArray;
 
 import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Executor;
 import java.util.concurrent.Executors;
 import java.util.concurrent.Future;
 import java.util.concurrent.ScheduledExecutorService;
@@ -117,7 +116,7 @@
     private final Object mWorkerLock = new Object();
 
     @GuardedBy("mWorkerLock")
-    private IWifiManager mWifiManager = null;
+    private WifiManager mWifiManager = null;
 
     @GuardedBy("mWorkerLock")
     private TelephonyManager mTelephony = null;
@@ -411,21 +410,33 @@
 
         if ((updateFlags & BatteryStatsImpl.ExternalStatsSync.UPDATE_WIFI) != 0) {
             // We were asked to fetch WiFi data.
-            if (mWifiManager == null) {
-                mWifiManager = IWifiManager.Stub.asInterface(ServiceManager.getService(
-                        Context.WIFI_SERVICE));
+            if (mWifiManager == null && ServiceManager.getService(Context.WIFI_SERVICE) != null) {
+                // this code is reached very early in the boot process, before Wifi Service has
+                // been registered. Check that ServiceManager.getService() returns a non null
+                // value before calling mContext.getSystemService(), since otherwise
+                // getSystemService() will throw a ServiceNotFoundException.
+                mWifiManager = mContext.getSystemService(WifiManager.class);
             }
 
-            if (mWifiManager != null) {
-                try {
-                    // Only fetch WiFi power data if it is supported.
-                    if ((mWifiManager.getSupportedFeatures() & WIFI_FEATURE_LINK_LAYER_STATS) != 0) {
-                        wifiReceiver = new SynchronousResultReceiver("wifi");
-                        mWifiManager.requestActivityInfo(wifiReceiver);
-                    }
-                } catch (RemoteException e) {
-                    // Oh well.
-                }
+            // Only fetch WiFi power data if it is supported.
+            if (mWifiManager != null && mWifiManager.isEnhancedPowerReportingSupported()) {
+                SynchronousResultReceiver tempWifiReceiver = new SynchronousResultReceiver("wifi");
+                mWifiManager.getWifiActivityEnergyInfoAsync(
+                        new Executor() {
+                            @Override
+                            public void execute(Runnable runnable) {
+                                // run the listener on the binder thread, if it was run on the main
+                                // thread it would deadlock since we would be waiting on ourselves
+                                runnable.run();
+                            }
+                        },
+                        info -> {
+                            Bundle bundle = new Bundle();
+                            bundle.putParcelable(BatteryStats.RESULT_RECEIVER_CONTROLLER_KEY, info);
+                            tempWifiReceiver.send(0, bundle);
+                        }
+                );
+                wifiReceiver = tempWifiReceiver;
             }
             synchronized (mStats) {
                 mStats.updateRailStatsLocked();
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 8c60d0c..37026fd 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -23,7 +23,6 @@
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
-import android.net.wifi.WifiActivityEnergyInfo;
 import android.os.BatteryStats;
 import android.os.BatteryStatsInternal;
 import android.os.Binder;
@@ -43,6 +42,7 @@
 import android.os.WorkSource;
 import android.os.connectivity.CellularBatteryStats;
 import android.os.connectivity.GpsBatteryStats;
+import android.os.connectivity.WifiActivityEnergyInfo;
 import android.os.connectivity.WifiBatteryStats;
 import android.os.health.HealthStatsParceler;
 import android.os.health.HealthStatsWriter;
diff --git a/services/core/java/com/android/server/am/BugReportHandlerUtil.java b/services/core/java/com/android/server/am/BugReportHandlerUtil.java
new file mode 100644
index 0000000..ba89fce
--- /dev/null
+++ b/services/core/java/com/android/server/am/BugReportHandlerUtil.java
@@ -0,0 +1,179 @@
+/*
+ * 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.am;
+
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+
+import android.app.BroadcastOptions;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.Binder;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.util.ArraySet;
+import android.util.Slog;
+
+import com.android.server.SystemConfig;
+
+import java.util.List;
+
+/**
+ * Static utility methods related to BugReportHandler.
+ */
+public final class BugReportHandlerUtil {
+    private static final String TAG = TAG_WITH_CLASS_NAME ? "BugReportHandlerUtil" : TAG_AM;
+    private static final String SHELL_APP_PACKAGE = "com.android.shell";
+    private static final String INTENT_BUGREPORT_REQUESTED =
+            "com.android.internal.intent.action.BUGREPORT_REQUESTED";
+
+    /**
+     * Check is BugReportHandler enabled on the device.
+     *
+     * @param context Context
+     * @return true if BugReportHandler is enabled, or false otherwise
+     */
+    static boolean isBugReportHandlerEnabled(Context context) {
+        return context.getResources().getBoolean(
+                com.android.internal.R.bool.config_bugReportHandlerEnabled);
+    }
+
+    /**
+     * Launches a bugreport-whitelisted app to handle a bugreport.
+     *
+     * <p>Allows a bug report handler app to take bugreports on the user's behalf. The handler can
+     * be predefined in the config, meant to be launched with the primary user. The user can
+     * override this with a different (or same) handler app on possibly a different user. This is
+     * useful for capturing bug reports from work profile, for instance.
+     *
+     * @param context Context
+     * @return true if there is a bugreport-whitelisted app to handle a bugreport, or false
+     * otherwise
+     */
+    static boolean launchBugReportHandlerApp(Context context) {
+        if (!isBugReportHandlerEnabled(context)) {
+            return false;
+        }
+
+        String handlerApp = getCustomBugReportHandlerApp(context);
+        if (isShellApp(handlerApp)) {
+            return false;
+        }
+
+        int handlerUser = getCustomBugReportHandlerUser(context);
+        if (!isValidBugReportHandlerApp(handlerApp)) {
+            handlerApp = getDefaultBugReportHandlerApp(context);
+            handlerUser = UserHandle.USER_SYSTEM;
+        } else if (getBugReportHandlerAppReceivers(context, handlerApp, handlerUser).isEmpty()) {
+            // It looks like the settings are outdated, reset outdated settings.
+            //
+            // i.e.
+            // If user chooses which profile and which bugreport-whitelisted app in that
+            // profile to handle a bugreport, then user remove the profile.
+            // === RESULT ===
+            // The chosen bugreport handler app is outdated because the profile is removed,
+            // so reset the chosen app and profile
+            handlerApp = getDefaultBugReportHandlerApp(context);
+            handlerUser = UserHandle.USER_SYSTEM;
+            resetCustomBugreportHandlerAppAndUser(context);
+        }
+
+        if (isShellApp(handlerApp) || !isValidBugReportHandlerApp(handlerApp)
+                || getBugReportHandlerAppReceivers(context, handlerApp, handlerUser).isEmpty()) {
+            return false;
+        }
+
+        Slog.i(TAG, "Launching bug report handler app: " + handlerApp);
+        Intent intent = new Intent(INTENT_BUGREPORT_REQUESTED);
+        intent.setPackage(handlerApp);
+        intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+        intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+        // Send broadcast to the receiver while allowing starting activity from background
+        final BroadcastOptions options = BroadcastOptions.makeBasic();
+        options.setBackgroundActivityStartsAllowed(true);
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            context.sendBroadcastAsUser(intent, UserHandle.of(handlerUser),
+                    android.Manifest.permission.DUMP,
+                    options.toBundle());
+        } catch (RuntimeException e) {
+            Slog.e(TAG, "Error while trying to launch bugreport handler app.", e);
+            return false;
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+        return true;
+    }
+
+    private static String getCustomBugReportHandlerApp(Context context) {
+        // Get the package of custom bugreport handler app
+        return Settings.Global.getString(context.getContentResolver(),
+                Settings.Global.CUSTOM_BUGREPORT_HANDLER_APP);
+    }
+
+    private static int getCustomBugReportHandlerUser(Context context) {
+        return Settings.Global.getInt(context.getContentResolver(),
+                Settings.Global.CUSTOM_BUGREPORT_HANDLER_USER, UserHandle.USER_NULL);
+    }
+
+    private static boolean isShellApp(String app) {
+        return SHELL_APP_PACKAGE.equals(app);
+    }
+
+    private static boolean isValidBugReportHandlerApp(String app) {
+        return !TextUtils.isEmpty(app) && isBugreportWhitelistedApp(app);
+    }
+
+    private static boolean isBugreportWhitelistedApp(String app) {
+        // Verify the app is bugreport-whitelisted
+        final ArraySet<String> whitelistedApps = SystemConfig.getInstance()
+                .getBugreportWhitelistedPackages();
+        return whitelistedApps.contains(app);
+    }
+
+    private static List<ResolveInfo> getBugReportHandlerAppReceivers(Context context,
+            String handlerApp, int handlerUser) {
+        // Use the app package and the user id to retrieve the receiver that can handle a
+        // broadcast of the intent.
+        Intent intent = new Intent(INTENT_BUGREPORT_REQUESTED);
+        intent.setPackage(handlerApp);
+        return context.getPackageManager()
+                .queryBroadcastReceiversAsUser(intent, PackageManager.MATCH_SYSTEM_ONLY,
+                        handlerUser);
+    }
+
+    private static String getDefaultBugReportHandlerApp(Context context) {
+        return context.getResources().getString(
+                com.android.internal.R.string.config_defaultBugReportHandlerApp);
+    }
+
+    private static void resetCustomBugreportHandlerAppAndUser(Context context) {
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            Settings.Global.putString(context.getContentResolver(),
+                    Settings.Global.CUSTOM_BUGREPORT_HANDLER_APP,
+                    getDefaultBugReportHandlerApp(context));
+            Settings.Global.putInt(context.getContentResolver(),
+                    Settings.Global.CUSTOM_BUGREPORT_HANDLER_USER, UserHandle.USER_SYSTEM);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 0591704..12f4656 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -376,7 +376,11 @@
                 ConnectionRecord cr = pr.connections.valueAt(i);
                 ProcessRecord service = (cr.flags & ServiceInfo.FLAG_ISOLATED_PROCESS) != 0
                         ? cr.binding.service.isolatedProc : cr.binding.service.app;
-                if (service == null || service == pr || (containsCycle |= service.mReachable)) {
+                if (service == null || service == pr) {
+                    continue;
+                }
+                containsCycle |= service.mReachable;
+                if (service.mReachable) {
                     continue;
                 }
                 if ((cr.flags & (Context.BIND_WAIVE_PRIORITY
@@ -394,6 +398,10 @@
                 if (provider == null || provider == pr || (containsCycle |= provider.mReachable)) {
                     continue;
                 }
+                containsCycle |= provider.mReachable;
+                if (provider.mReachable) {
+                    continue;
+                }
                 queue.offer(provider);
                 provider.mReachable = true;
             }
@@ -482,12 +490,15 @@
         // need to reset cycle state before calling computeOomAdjLocked because of service conns
         for (int i = numProc - 1; i >= 0; i--) {
             ProcessRecord app = activeProcesses.get(i);
-            app.containsCycle = false;
             app.mReachable = false;
-            app.setCurRawProcState(PROCESS_STATE_CACHED_EMPTY);
-            app.setCurRawAdj(ProcessList.UNKNOWN_ADJ);
-            app.setCapability = PROCESS_CAPABILITY_NONE;
-            app.resetCachedInfo();
+            // No need to compute again it has been evaluated in previous iteration
+            if (app.adjSeq != mAdjSeq) {
+                app.containsCycle = false;
+                app.setCurRawProcState(PROCESS_STATE_CACHED_EMPTY);
+                app.setCurRawAdj(ProcessList.UNKNOWN_ADJ);
+                app.setCapability = PROCESS_CAPABILITY_NONE;
+                app.resetCachedInfo();
+            }
         }
         for (int i = numProc - 1; i >= 0; i--) {
             ProcessRecord app = activeProcesses.get(i);
diff --git a/services/core/java/com/android/server/compat/CompatChange.java b/services/core/java/com/android/server/compat/CompatChange.java
index 95582f7..2eec419 100644
--- a/services/core/java/com/android/server/compat/CompatChange.java
+++ b/services/core/java/com/android/server/compat/CompatChange.java
@@ -55,7 +55,7 @@
     private Map<String, Boolean> mPackageOverrides;
 
     public CompatChange(long changeId) {
-        this(changeId, null, -1, false);
+        this(changeId, null, -1, false, null);
     }
 
     /**
@@ -66,8 +66,8 @@
      * @param disabled If {@code true}, overrides any {@code enableAfterTargetSdk} set.
      */
     public CompatChange(long changeId, @Nullable String name, int enableAfterTargetSdk,
-            boolean disabled) {
-        super(changeId, name, enableAfterTargetSdk, disabled);
+            boolean disabled, String description) {
+        super(changeId, name, enableAfterTargetSdk, disabled, description);
     }
 
     /**
@@ -75,7 +75,7 @@
      */
     public CompatChange(Change change) {
         super(change.getId(), change.getName(), change.getEnableAfterTargetSdk(),
-                change.getDisabled());
+                change.getDisabled(), change.getDescription());
     }
 
     void registerListener(ChangeListener listener) {
diff --git a/services/core/java/com/android/server/compat/CompatConfig.java b/services/core/java/com/android/server/compat/CompatConfig.java
index 39c6e75..cf83dd6 100644
--- a/services/core/java/com/android/server/compat/CompatConfig.java
+++ b/services/core/java/com/android/server/compat/CompatConfig.java
@@ -319,7 +319,8 @@
                 changeInfos[i] = new CompatibilityChangeInfo(change.getId(),
                         change.getName(),
                         change.getEnableAfterTargetSdk(),
-                        change.getDisabled());
+                        change.getDisabled(),
+                        change.getDescription());
             }
             return changeInfos;
         }
diff --git a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
index bc83780..2179518 100644
--- a/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
+++ b/services/core/java/com/android/server/connectivity/NetworkNotificationManager.java
@@ -31,7 +31,6 @@
 import android.net.StringNetworkSpecifier;
 import android.net.wifi.WifiInfo;
 import android.os.UserHandle;
-import android.telephony.AccessNetworkConstants.TransportType;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
@@ -105,8 +104,7 @@
         return -1;
     }
 
-    // TODO: Remove @TransportType or change it to @Transport.
-    private static String getTransportName(@TransportType int transportType) {
+    private static String getTransportName(final int transportType) {
         Resources r = Resources.getSystem();
         String[] networkTypes = r.getStringArray(R.array.network_switch_type_name);
         try {
diff --git a/services/core/java/com/android/server/integrity/model/BitInputStream.java b/services/core/java/com/android/server/integrity/model/BitInputStream.java
new file mode 100644
index 0000000..09bc7e8
--- /dev/null
+++ b/services/core/java/com/android/server/integrity/model/BitInputStream.java
@@ -0,0 +1,66 @@
+/*
+ * 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.model;
+
+/** A wrapper class for reading a stream of bits. */
+public class BitInputStream {
+
+    private byte[] mRuleBytes;
+    private long mBitPointer;
+
+    public BitInputStream(byte[] ruleBytes) {
+        this.mRuleBytes = ruleBytes;
+        this.mBitPointer = 0;
+    }
+
+    /**
+     * Read the next number of bits from the stream.
+     *
+     * @param numOfBits The number of bits to read.
+     * @return The value read from the stream.
+     */
+    public int getNext(int numOfBits) {
+        int component = 0;
+        int count = 0;
+
+        int idx = (int) (mBitPointer / 8);
+        int offset = 7 - (int) (mBitPointer % 8);
+
+        while (count++ < numOfBits) {
+            if (idx >= mRuleBytes.length) {
+                throw new IllegalArgumentException(String.format("Invalid byte index: %d", idx));
+            }
+
+            component <<= 1;
+            component |= (mRuleBytes[idx] >>> offset) & 1;
+
+            offset--;
+            if (offset == -1) {
+                idx++;
+                offset = 7;
+            }
+        }
+
+        mBitPointer += numOfBits;
+        return component;
+    }
+
+    /** Check if there are bits left in the stream. */
+    public boolean hasNext() {
+        return mBitPointer / 8 < mRuleBytes.length;
+    }
+}
diff --git a/services/core/java/com/android/server/integrity/model/BitOutputStream.java b/services/core/java/com/android/server/integrity/model/BitOutputStream.java
new file mode 100644
index 0000000..ecb9189
--- /dev/null
+++ b/services/core/java/com/android/server/integrity/model/BitOutputStream.java
@@ -0,0 +1,86 @@
+/*
+ * 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.model;
+
+import java.util.BitSet;
+
+/** A wrapper class for writing a stream of bits. */
+public class BitOutputStream {
+
+    private BitSet mBitSet;
+    private int mIndex;
+
+    public BitOutputStream() {
+        mBitSet = new BitSet();
+        mIndex = 0;
+    }
+
+    /**
+     * Set the next number of bits in the stream to value.
+     *
+     * @param numOfBits The number of bits used to represent the value.
+     * @param value The value to convert to bits.
+     */
+    public void setNext(int numOfBits, int value) {
+        if (numOfBits <= 0) {
+            return;
+        }
+        int offset = 1 << (numOfBits - 1);
+        while (numOfBits-- > 0) {
+            mBitSet.set(mIndex, (value & offset) != 0);
+            offset >>= 1;
+            mIndex++;
+        }
+    }
+
+    /**
+     * Set the next bit in the stream to value.
+     *
+     * @param value The value to set the bit to.
+     */
+    public void setNext(boolean value) {
+        mBitSet.set(mIndex, value);
+        mIndex++;
+    }
+
+    /** Set the next bit in the stream to true. */
+    public void setNext() {
+        setNext(/* value= */ true);
+    }
+
+    /** Convert BitSet in big-endian to ByteArray in big-endian. */
+    public byte[] toByteArray() {
+        int bitSetSize = mBitSet.length();
+        int numOfBytes = bitSetSize / 8;
+        if (bitSetSize % 8 != 0) {
+            numOfBytes++;
+        }
+        byte[] bytes = new byte[numOfBytes];
+        for (int i = 0; i < mBitSet.length(); i++) {
+            if (mBitSet.get(i)) {
+                bytes[i / 8] |= 1 << (7 - (i % 8));
+            }
+        }
+        return bytes;
+    }
+
+    /** Clear the stream. */
+    public void clear() {
+        mBitSet.clear();
+        mIndex = 0;
+    }
+}
diff --git a/services/core/java/com/android/server/integrity/model/ComponentBitSize.java b/services/core/java/com/android/server/integrity/model/ComponentBitSize.java
new file mode 100644
index 0000000..d47ce2d
--- /dev/null
+++ b/services/core/java/com/android/server/integrity/model/ComponentBitSize.java
@@ -0,0 +1,41 @@
+/*
+ * 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.model;
+
+import android.content.integrity.Rule;
+
+/**
+ * A helper class containing information about the binary representation of different {@link Rule}
+ * components.
+ */
+public final class ComponentBitSize {
+    public static final int FORMAT_VERSION_BITS = 5;
+    public static final int EFFECT_BITS = 3;
+    public static final int KEY_BITS = 4;
+    public static final int OPERATOR_BITS = 3;
+    public static final int CONNECTOR_BITS = 2;
+    public static final int SEPARATOR_BITS = 2;
+    public static final int VALUE_SIZE_BITS = 5;
+    public static final int IS_HASHED_BITS = 1;
+
+    public static final int ATOMIC_FORMULA_START = 0;
+    public static final int COMPOUND_FORMULA_START = 1;
+    public static final int COMPOUND_FORMULA_END = 2;
+
+    public static final int DEFAULT_FORMAT_VERSION = 1;
+    public static final int SIGNAL_BIT = 1;
+}
diff --git a/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java b/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java
index aad177e..8aa0751 100644
--- a/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java
+++ b/services/core/java/com/android/server/integrity/parser/RuleBinaryParser.java
@@ -16,23 +16,132 @@
 
 package com.android.server.integrity.parser;
 
+import static com.android.server.integrity.model.ComponentBitSize.ATOMIC_FORMULA_START;
+import static com.android.server.integrity.model.ComponentBitSize.COMPOUND_FORMULA_END;
+import static com.android.server.integrity.model.ComponentBitSize.COMPOUND_FORMULA_START;
+import static com.android.server.integrity.model.ComponentBitSize.CONNECTOR_BITS;
+import static com.android.server.integrity.model.ComponentBitSize.EFFECT_BITS;
+import static com.android.server.integrity.model.ComponentBitSize.FORMAT_VERSION_BITS;
+import static com.android.server.integrity.model.ComponentBitSize.IS_HASHED_BITS;
+import static com.android.server.integrity.model.ComponentBitSize.KEY_BITS;
+import static com.android.server.integrity.model.ComponentBitSize.OPERATOR_BITS;
+import static com.android.server.integrity.model.ComponentBitSize.SEPARATOR_BITS;
+import static com.android.server.integrity.model.ComponentBitSize.SIGNAL_BIT;
+import static com.android.server.integrity.model.ComponentBitSize.VALUE_SIZE_BITS;
+
+import android.content.integrity.AtomicFormula;
+import android.content.integrity.CompoundFormula;
+import android.content.integrity.Formula;
 import android.content.integrity.Rule;
 
+import com.android.server.integrity.model.BitInputStream;
+
 import java.io.InputStream;
+import java.util.ArrayList;
 import java.util.List;
 
 /** A helper class to parse rules into the {@link Rule} model from Binary representation. */
 public class RuleBinaryParser implements RuleParser {
 
     @Override
-    public List<Rule> parse(byte[] ruleBytes) {
-        // TODO: Implement binary text parser.
-        return null;
+    public List<Rule> parse(byte[] ruleBytes) throws RuleParseException {
+        try {
+            BitInputStream bitInputStream = new BitInputStream(ruleBytes);
+            return parseRules(bitInputStream);
+        } catch (Exception e) {
+            throw new RuleParseException(e.getMessage(), e);
+        }
     }
 
     @Override
-    public List<Rule> parse(InputStream inputStream) {
-        // TODO: Implement stream parser.
-        return null;
+    public List<Rule> parse(InputStream inputStream) throws RuleParseException {
+        try {
+            byte[] ruleBytes = new byte[inputStream.available()];
+            inputStream.read(ruleBytes);
+            return parse(ruleBytes);
+        } catch (Exception e) {
+            throw new RuleParseException(e.getMessage(), e);
+        }
+    }
+
+    private List<Rule> parseRules(BitInputStream bitInputStream) {
+        List<Rule> parsedRules = new ArrayList<>();
+
+        // Read the rule binary file format version.
+        bitInputStream.getNext(FORMAT_VERSION_BITS);
+
+        while (bitInputStream.hasNext()) {
+            if (bitInputStream.getNext(SIGNAL_BIT) == 1) {
+                parsedRules.add(parseRule(bitInputStream));
+            }
+        }
+
+        return parsedRules;
+    }
+
+    private Rule parseRule(BitInputStream bitInputStream) {
+        Formula formula = parseFormula(bitInputStream);
+        int effect = bitInputStream.getNext(EFFECT_BITS);
+
+        if (bitInputStream.getNext(SIGNAL_BIT) != 1) {
+            throw new IllegalArgumentException("A rule must end with a '1' bit.");
+        }
+
+        return new Rule(formula, effect);
+    }
+
+    private Formula parseFormula(BitInputStream bitInputStream) {
+        int separator = bitInputStream.getNext(SEPARATOR_BITS);
+        switch (separator) {
+            case ATOMIC_FORMULA_START:
+                return parseAtomicFormula(bitInputStream);
+            case COMPOUND_FORMULA_START:
+                return parseCompoundFormula(bitInputStream);
+            case COMPOUND_FORMULA_END:
+                return null;
+            default:
+                throw new IllegalArgumentException(
+                        String.format("Unknown formula separator: %s", separator));
+        }
+    }
+
+    private CompoundFormula parseCompoundFormula(BitInputStream bitInputStream) {
+        int connector = bitInputStream.getNext(CONNECTOR_BITS);
+        List<Formula> formulas = new ArrayList<>();
+
+        Formula parsedFormula = parseFormula(bitInputStream);
+        while (parsedFormula != null) {
+            formulas.add(parsedFormula);
+            parsedFormula = parseFormula(bitInputStream);
+        }
+
+        return new CompoundFormula(connector, formulas);
+    }
+
+    private AtomicFormula parseAtomicFormula(BitInputStream bitInputStream) {
+        int key = bitInputStream.getNext(KEY_BITS);
+        int operator = bitInputStream.getNext(OPERATOR_BITS);
+
+        boolean isHashedValue = bitInputStream.getNext(IS_HASHED_BITS) == 1;
+        int valueSize = bitInputStream.getNext(VALUE_SIZE_BITS);
+        StringBuilder value = new StringBuilder();
+        while (valueSize-- > 0) {
+            value.append((char) bitInputStream.getNext(/* numOfBits= */ 8));
+        }
+
+        switch (key) {
+            case AtomicFormula.PACKAGE_NAME:
+            case AtomicFormula.APP_CERTIFICATE:
+            case AtomicFormula.INSTALLER_NAME:
+            case AtomicFormula.INSTALLER_CERTIFICATE:
+                return new AtomicFormula.StringAtomicFormula(key, value.toString(), isHashedValue);
+            case AtomicFormula.VERSION_CODE:
+                return new AtomicFormula.IntAtomicFormula(
+                        key, operator, Integer.parseInt(value.toString()));
+            case AtomicFormula.PRE_INSTALLED:
+                return new AtomicFormula.BooleanAtomicFormula(key, value.toString().equals("1"));
+            default:
+                throw new IllegalArgumentException(String.format("Unknown key: %d", key));
+        }
     }
 }
diff --git a/services/core/java/com/android/server/integrity/parser/RuleXmlParser.java b/services/core/java/com/android/server/integrity/parser/RuleXmlParser.java
index 2e99d0f..d405583 100644
--- a/services/core/java/com/android/server/integrity/parser/RuleXmlParser.java
+++ b/services/core/java/com/android/server/integrity/parser/RuleXmlParser.java
@@ -41,7 +41,7 @@
     private static final String NAMESPACE = "";
     private static final String RULE_LIST_TAG = "RL";
     private static final String RULE_TAG = "R";
-    private static final String OPEN_FORMULA_TAG = "OF";
+    private static final String COMPOUND_FORMULA_TAG = "OF";
     private static final String ATOMIC_FORMULA_TAG = "AF";
     private static final String EFFECT_ATTRIBUTE = "E";
     private static final String KEY_ATTRIBUTE = "K";
@@ -118,8 +118,8 @@
 
             if (eventType == XmlPullParser.START_TAG) {
                 switch (nodeName) {
-                    case OPEN_FORMULA_TAG:
-                        formula = parseOpenFormula(parser);
+                    case COMPOUND_FORMULA_TAG:
+                        formula = parseCompoundFormula(parser);
                         break;
                     case ATOMIC_FORMULA_TAG:
                         formula = parseAtomicFormula(parser);
@@ -137,7 +137,7 @@
         return new Rule(formula, effect);
     }
 
-    private static Formula parseOpenFormula(XmlPullParser parser)
+    private static Formula parseCompoundFormula(XmlPullParser parser)
             throws IOException, XmlPullParserException {
         int connector =
                 Integer.parseInt(extractAttributeValue(parser, CONNECTOR_ATTRIBUTE).orElse("-1"));
@@ -147,7 +147,8 @@
         while ((eventType = parser.next()) != XmlPullParser.END_DOCUMENT) {
             String nodeName = parser.getName();
 
-            if (eventType == XmlPullParser.END_TAG && parser.getName().equals(OPEN_FORMULA_TAG)) {
+            if (eventType == XmlPullParser.END_TAG
+                    && parser.getName().equals(COMPOUND_FORMULA_TAG)) {
                 break;
             }
 
@@ -156,8 +157,8 @@
                     case ATOMIC_FORMULA_TAG:
                         formulas.add(parseAtomicFormula(parser));
                         break;
-                    case OPEN_FORMULA_TAG:
-                        formulas.add(parseOpenFormula(parser));
+                    case COMPOUND_FORMULA_TAG:
+                        formulas.add(parseCompoundFormula(parser));
                         break;
                     default:
                         throw new RuntimeException(
diff --git a/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java b/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java
index d4f41eb..b988fd4 100644
--- a/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java
+++ b/services/core/java/com/android/server/integrity/serializer/RuleBinarySerializer.java
@@ -16,24 +16,155 @@
 
 package com.android.server.integrity.serializer;
 
+import static com.android.server.integrity.model.ComponentBitSize.ATOMIC_FORMULA_START;
+import static com.android.server.integrity.model.ComponentBitSize.COMPOUND_FORMULA_END;
+import static com.android.server.integrity.model.ComponentBitSize.COMPOUND_FORMULA_START;
+import static com.android.server.integrity.model.ComponentBitSize.CONNECTOR_BITS;
+import static com.android.server.integrity.model.ComponentBitSize.DEFAULT_FORMAT_VERSION;
+import static com.android.server.integrity.model.ComponentBitSize.EFFECT_BITS;
+import static com.android.server.integrity.model.ComponentBitSize.FORMAT_VERSION_BITS;
+import static com.android.server.integrity.model.ComponentBitSize.KEY_BITS;
+import static com.android.server.integrity.model.ComponentBitSize.OPERATOR_BITS;
+import static com.android.server.integrity.model.ComponentBitSize.SEPARATOR_BITS;
+import static com.android.server.integrity.model.ComponentBitSize.VALUE_SIZE_BITS;
+
+import android.content.integrity.AtomicFormula;
+import android.content.integrity.CompoundFormula;
+import android.content.integrity.Formula;
 import android.content.integrity.Rule;
 
+import com.android.server.integrity.model.BitOutputStream;
+
+import java.io.ByteArrayOutputStream;
 import java.io.OutputStream;
+import java.nio.charset.StandardCharsets;
 import java.util.List;
 import java.util.Optional;
 
-/** A helper class to serialize rules from the {@link Rule} model to Xml representation. */
+/** A helper class to serialize rules from the {@link Rule} model to Binary representation. */
 public class RuleBinarySerializer implements RuleSerializer {
 
+    // Get the byte representation for a list of rules, and write them to an output stream.
     @Override
     public void serialize(
-            List<Rule> rules, Optional<Integer> formatVersion, OutputStream outputStream) {
-        // TODO: Implement stream serializer.
+            List<Rule> rules, Optional<Integer> formatVersion, OutputStream outputStream)
+            throws RuleSerializeException {
+        try {
+            BitOutputStream bitOutputStream = new BitOutputStream();
+
+            int formatVersionValue = formatVersion.orElse(DEFAULT_FORMAT_VERSION);
+            bitOutputStream.setNext(FORMAT_VERSION_BITS, formatVersionValue);
+            outputStream.write(bitOutputStream.toByteArray());
+
+            for (Rule rule : rules) {
+                bitOutputStream.clear();
+                serializeRule(rule, bitOutputStream);
+                outputStream.write(bitOutputStream.toByteArray());
+            }
+        } catch (Exception e) {
+            throw new RuleSerializeException(e.getMessage(), e);
+        }
     }
 
+    // Get the byte representation for a list of rules.
     @Override
-    public byte[] serialize(List<Rule> rules, Optional<Integer> formatVersion) {
-        // TODO: Implement text serializer.
-        return null;
+    public byte[] serialize(List<Rule> rules, Optional<Integer> formatVersion)
+            throws RuleSerializeException {
+        try {
+            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+            serialize(rules, formatVersion, byteArrayOutputStream);
+            return byteArrayOutputStream.toByteArray();
+        } catch (Exception e) {
+            throw new RuleSerializeException(e.getMessage(), e);
+        }
+    }
+
+    private void serializeRule(Rule rule, BitOutputStream bitOutputStream) {
+        if (rule == null) {
+            throw new IllegalArgumentException("Null rule can not be serialized");
+        }
+
+        // Start with a '1' bit to mark the start of a rule.
+        bitOutputStream.setNext();
+
+        serializeFormula(rule.getFormula(), bitOutputStream);
+        bitOutputStream.setNext(EFFECT_BITS, rule.getEffect());
+
+        // End with a '1' bit to mark the end of a rule.
+        bitOutputStream.setNext();
+    }
+
+    private void serializeFormula(Formula formula, BitOutputStream bitOutputStream) {
+        if (formula instanceof AtomicFormula) {
+            serializeAtomicFormula((AtomicFormula) formula, bitOutputStream);
+        } else if (formula instanceof CompoundFormula) {
+            serializeCompoundFormula((CompoundFormula) formula, bitOutputStream);
+        } else {
+            throw new IllegalArgumentException(
+                    String.format("Invalid formula type: %s", formula.getClass()));
+        }
+    }
+
+    private void serializeCompoundFormula(
+            CompoundFormula compoundFormula, BitOutputStream bitOutputStream) {
+        if (compoundFormula == null) {
+            throw new IllegalArgumentException("Null compound formula can not be serialized");
+        }
+
+        bitOutputStream.setNext(SEPARATOR_BITS, COMPOUND_FORMULA_START);
+        bitOutputStream.setNext(CONNECTOR_BITS, compoundFormula.getConnector());
+        for (Formula formula : compoundFormula.getFormulas()) {
+            serializeFormula(formula, bitOutputStream);
+        }
+        bitOutputStream.setNext(SEPARATOR_BITS, COMPOUND_FORMULA_END);
+    }
+
+    private void serializeAtomicFormula(
+            AtomicFormula atomicFormula, BitOutputStream bitOutputStream) {
+        if (atomicFormula == null) {
+            throw new IllegalArgumentException("Null atomic formula can not be serialized");
+        }
+
+        bitOutputStream.setNext(SEPARATOR_BITS, ATOMIC_FORMULA_START);
+        bitOutputStream.setNext(KEY_BITS, atomicFormula.getKey());
+        if (atomicFormula instanceof AtomicFormula.StringAtomicFormula) {
+            AtomicFormula.StringAtomicFormula stringAtomicFormula =
+                    (AtomicFormula.StringAtomicFormula) atomicFormula;
+            bitOutputStream.setNext(OPERATOR_BITS, AtomicFormula.EQ);
+            serializeValue(
+                    stringAtomicFormula.getValue(),
+                    stringAtomicFormula.getIsHashedValue(),
+                    bitOutputStream);
+        } else if (atomicFormula instanceof AtomicFormula.IntAtomicFormula) {
+            AtomicFormula.IntAtomicFormula intAtomicFormula =
+                    (AtomicFormula.IntAtomicFormula) atomicFormula;
+            bitOutputStream.setNext(OPERATOR_BITS, intAtomicFormula.getOperator());
+            serializeValue(
+                    String.valueOf(intAtomicFormula.getValue()),
+                    /* isHashedValue= */ false,
+                    bitOutputStream);
+        } else if (atomicFormula instanceof AtomicFormula.BooleanAtomicFormula) {
+            AtomicFormula.BooleanAtomicFormula booleanAtomicFormula =
+                    (AtomicFormula.BooleanAtomicFormula) atomicFormula;
+            bitOutputStream.setNext(OPERATOR_BITS, AtomicFormula.EQ);
+            serializeValue(
+                    booleanAtomicFormula.getValue() ? "1" : "0",
+                    /* isHashedValue= */ false,
+                    bitOutputStream);
+        } else {
+            throw new IllegalArgumentException(
+                    String.format("Invalid atomic formula type: %s", atomicFormula.getClass()));
+        }
+    }
+
+    private void serializeValue(
+            String value, boolean isHashedValue, BitOutputStream bitOutputStream) {
+        byte[] valueBytes = value.getBytes(StandardCharsets.UTF_8);
+
+        bitOutputStream.setNext(isHashedValue);
+        bitOutputStream.setNext(VALUE_SIZE_BITS, valueBytes.length);
+        for (byte valueByte : valueBytes) {
+            bitOutputStream.setNext(/* numOfBits= */ 8, valueByte);
+        }
     }
 }
diff --git a/services/core/java/com/android/server/integrity/serializer/RuleXmlSerializer.java b/services/core/java/com/android/server/integrity/serializer/RuleXmlSerializer.java
index 3ec9cf2..72068ce 100644
--- a/services/core/java/com/android/server/integrity/serializer/RuleXmlSerializer.java
+++ b/services/core/java/com/android/server/integrity/serializer/RuleXmlSerializer.java
@@ -39,7 +39,7 @@
 
     private static final String RULE_LIST_TAG = "RL";
     private static final String RULE_TAG = "R";
-    private static final String OPEN_FORMULA_TAG = "OF";
+    private static final String COMPOUND_FORMULA_TAG = "OF";
     private static final String ATOMIC_FORMULA_TAG = "AF";
     private static final String EFFECT_ATTRIBUTE = "E";
     private static final String KEY_ATTRIBUTE = "K";
@@ -78,13 +78,13 @@
     private void serializeRules(List<Rule> rules, XmlSerializer xmlSerializer) throws IOException {
         xmlSerializer.startTag(NAMESPACE, RULE_LIST_TAG);
         for (Rule rule : rules) {
-            serialize(rule, xmlSerializer);
+            serializeRule(rule, xmlSerializer);
         }
         xmlSerializer.endTag(NAMESPACE, RULE_LIST_TAG);
         xmlSerializer.endDocument();
     }
 
-    private void serialize(Rule rule, XmlSerializer xmlSerializer) throws IOException {
+    private void serializeRule(Rule rule, XmlSerializer xmlSerializer) throws IOException {
         if (rule == null) {
             return;
         }
@@ -98,25 +98,25 @@
         if (formula instanceof AtomicFormula) {
             serializeAtomicFormula((AtomicFormula) formula, xmlSerializer);
         } else if (formula instanceof CompoundFormula) {
-            serializeOpenFormula((CompoundFormula) formula, xmlSerializer);
+            serializeCompoundFormula((CompoundFormula) formula, xmlSerializer);
         } else {
             throw new IllegalArgumentException(
                     String.format("Invalid formula type: %s", formula.getClass()));
         }
     }
 
-    private void serializeOpenFormula(CompoundFormula compoundFormula, XmlSerializer xmlSerializer)
-            throws IOException {
+    private void serializeCompoundFormula(
+            CompoundFormula compoundFormula, XmlSerializer xmlSerializer) throws IOException {
         if (compoundFormula == null) {
             return;
         }
-        xmlSerializer.startTag(NAMESPACE, OPEN_FORMULA_TAG);
+        xmlSerializer.startTag(NAMESPACE, COMPOUND_FORMULA_TAG);
         serializeAttributeValue(
                 CONNECTOR_ATTRIBUTE, String.valueOf(compoundFormula.getConnector()), xmlSerializer);
         for (Formula formula : compoundFormula.getFormulas()) {
             serializeFormula(formula, xmlSerializer);
         }
-        xmlSerializer.endTag(NAMESPACE, OPEN_FORMULA_TAG);
+        xmlSerializer.endTag(NAMESPACE, COMPOUND_FORMULA_TAG);
     }
 
     private void serializeAtomicFormula(AtomicFormula atomicFormula, XmlSerializer xmlSerializer)
diff --git a/services/core/java/com/android/server/location/ContextHubClientBroker.java b/services/core/java/com/android/server/location/ContextHubClientBroker.java
index 675e59e..45d9bae 100644
--- a/services/core/java/com/android/server/location/ContextHubClientBroker.java
+++ b/services/core/java/com/android/server/location/ContextHubClientBroker.java
@@ -29,10 +29,12 @@
 import android.hardware.location.IContextHubClient;
 import android.hardware.location.IContextHubClientCallback;
 import android.hardware.location.NanoAppMessage;
+import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.Log;
 
+import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.function.Supplier;
 
 /**
@@ -97,6 +99,16 @@
     private final PendingIntentRequest mPendingIntentRequest;
 
     /*
+     * The host package associated with this client.
+     */
+    private final String mPackage;
+
+    /*
+     * True if a PendingIntent has been cancelled.
+     */
+    private AtomicBoolean mIsPendingIntentCancelled = new AtomicBoolean(false);
+
+    /*
      * Helper class to manage registered PendingIntent requests from the client.
      */
     private class PendingIntentRequest {
@@ -110,11 +122,14 @@
          */
         private long mNanoAppId;
 
+        private boolean mValid = false;
+
         PendingIntentRequest() {}
 
         PendingIntentRequest(PendingIntent pendingIntent, long nanoAppId) {
             mPendingIntent = pendingIntent;
             mNanoAppId = nanoAppId;
+            mValid = true;
         }
 
         public long getNanoAppId() {
@@ -132,6 +147,10 @@
         public void clear() {
             mPendingIntent = null;
         }
+
+        public boolean isValid() {
+            return mValid;
+        }
     }
 
     /* package */ ContextHubClientBroker(
@@ -145,6 +164,7 @@
         mHostEndPointId = hostEndPointId;
         mCallbackInterface = callback;
         mPendingIntentRequest = new PendingIntentRequest();
+        mPackage = mContext.getPackageManager().getNameForUid(Binder.getCallingUid());
     }
 
     /* package */ ContextHubClientBroker(
@@ -157,6 +177,7 @@
         mAttachedContextHubInfo = contextHubInfo;
         mHostEndPointId = hostEndPointId;
         mPendingIntentRequest = new PendingIntentRequest(pendingIntent, nanoAppId);
+        mPackage = pendingIntent.getCreatorPackage();
     }
 
     /**
@@ -313,6 +334,13 @@
     }
 
     /**
+     * @return true if the client is a PendingIntent client that has been cancelled.
+     */
+    /* package */ boolean isPendingIntentCancelled() {
+        return mIsPendingIntentCancelled.get();
+    }
+
+    /**
      * Helper function to invoke a specified client callback, if the connection is open.
      *
      * @param consumer the consumer specifying the callback to invoke
@@ -392,6 +420,7 @@
                     Manifest.permission.LOCATION_HARDWARE /* requiredPermission */,
                     null /* options */);
         } catch (PendingIntent.CanceledException e) {
+            mIsPendingIntentCancelled.set(true);
             // The PendingIntent is no longer valid
             Log.w(TAG, "PendingIntent has been canceled, unregistering from client"
                     + " (host endpoint ID " + mHostEndPointId + ")");
@@ -419,4 +448,20 @@
             mRegistered = false;
         }
     }
+
+    @Override
+    public String toString() {
+        String out = "[ContextHubClient ";
+        out += "endpointID: " + getHostEndPointId() + ", ";
+        out += "contextHub: " + getAttachedContextHubId() + ", ";
+        if (mPendingIntentRequest.isValid()) {
+            out += "intentCreatorPackage: " + mPackage + ", ";
+            out += "nanoAppId: 0x" + Long.toHexString(mPendingIntentRequest.getNanoAppId());
+        } else {
+            out += "package: " + mPackage;
+        }
+        out += "]";
+
+        return out;
+    }
 }
diff --git a/services/core/java/com/android/server/location/ContextHubClientManager.java b/services/core/java/com/android/server/location/ContextHubClientManager.java
index 00b7d62..46db8dc 100644
--- a/services/core/java/com/android/server/location/ContextHubClientManager.java
+++ b/services/core/java/com/android/server/location/ContextHubClientManager.java
@@ -16,6 +16,7 @@
 
 package com.android.server.location;
 
+import android.annotation.IntDef;
 import android.app.PendingIntent;
 import android.content.Context;
 import android.hardware.contexthub.V1_0.ContextHubMsg;
@@ -27,7 +28,12 @@
 import android.os.RemoteException;
 import android.util.Log;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Calendar;
+import java.util.Iterator;
 import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentLinkedDeque;
 import java.util.function.Consumer;
 
 /**
@@ -71,6 +77,79 @@
      */
     private int mNextHostEndPointId = 0;
 
+    /*
+     * The list of previous registration records.
+     */
+    private static final int NUM_CLIENT_RECORDS = 20;
+    private final ConcurrentLinkedEvictingDeque<RegistrationRecord> mRegistrationRecordDeque =
+            new ConcurrentLinkedEvictingDeque<>(NUM_CLIENT_RECORDS);
+
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = { "ACTION_" }, value = {
+            ACTION_REGISTERED,
+            ACTION_UNREGISTERED,
+            ACTION_CANCELLED,
+    })
+    public @interface Action {}
+    public static final int ACTION_REGISTERED = 0;
+    public static final int ACTION_UNREGISTERED = 1;
+    public static final int ACTION_CANCELLED = 2;
+
+    /**
+     * Helper class to make a ConcurrentLinkedDeque fixed-size, evicting old entries when full.
+     */
+    private class ConcurrentLinkedEvictingDeque<E> extends ConcurrentLinkedDeque<E> {
+        private int mSize;
+
+        ConcurrentLinkedEvictingDeque(int size) {
+            mSize = size;
+        }
+
+        @Override
+        public boolean add(E elem) {
+            synchronized (this) {
+                if (size() == mSize) {
+                    poll();
+                }
+
+                return super.add(elem);
+            }
+        }
+    }
+
+    /**
+     * A container class to store a record of ContextHubClient registration.
+     */
+    private class RegistrationRecord {
+        private final String mBroker;
+        private final int mAction;
+        private final String mDate;
+
+        RegistrationRecord(String broker, @Action int action) {
+            mBroker = broker;
+            mAction = action;
+            Calendar instance = Calendar.getInstance();
+            mDate = String.format("%02d", instance.get(Calendar.MONTH) + 1) // Jan == 0
+                + "/" + String.format("%02d", instance.get(Calendar.DAY_OF_MONTH))
+                + " " + String.format("%02d", instance.get(Calendar.HOUR_OF_DAY))
+                + ":" + String.format("%02d", instance.get(Calendar.MINUTE))
+                + ":" + String.format("%02d", instance.get(Calendar.SECOND))
+                + "." + String.format("%03d", instance.get(Calendar.MILLISECOND));
+        }
+
+        @Override
+        public String toString() {
+            String out = "";
+            out += mDate + " ";
+            out += mAction == ACTION_REGISTERED ? "+ " : "- ";
+            out += mBroker;
+            if (mAction == ACTION_CANCELLED) {
+                out += " (cancelled)";
+            }
+            return out;
+        }
+    }
+
     /* package */ ContextHubClientManager(
             Context context, IContexthub contextHubProxy) {
         mContext = context;
@@ -96,6 +175,8 @@
                     mContext, mContextHubProxy, this /* clientManager */, contextHubInfo,
                     hostEndPointId, clientCallback);
             mHostEndPointIdToClientMap.put(hostEndPointId, broker);
+            mRegistrationRecordDeque.add(
+                    new RegistrationRecord(broker.toString(), ACTION_REGISTERED));
         }
 
         try {
@@ -136,6 +217,8 @@
                         hostEndPointId, pendingIntent, nanoAppId);
                 mHostEndPointIdToClientMap.put(hostEndPointId, broker);
                 registerString = "Registered";
+                mRegistrationRecordDeque.add(
+                        new RegistrationRecord(broker.toString(), ACTION_REGISTERED));
             }
         }
 
@@ -178,6 +261,13 @@
      * @param hostEndPointId the host endpoint ID of the client that has died
      */
     /* package */ void unregisterClient(short hostEndPointId) {
+        ContextHubClientBroker broker = mHostEndPointIdToClientMap.get(hostEndPointId);
+        if (broker != null) {
+            @Action int action =
+                    broker.isPendingIntentCancelled() ? ACTION_CANCELLED : ACTION_UNREGISTERED;
+            mRegistrationRecordDeque.add(new RegistrationRecord(broker.toString(), action));
+        }
+
         if (mHostEndPointIdToClientMap.remove(hostEndPointId) != null) {
             Log.d(TAG, "Unregistered client with host endpoint ID " + hostEndPointId);
         } else {
@@ -285,4 +375,20 @@
 
         return null;
     }
+
+    @Override
+    public String toString() {
+        String out = "";
+        for (ContextHubClientBroker broker : mHostEndPointIdToClientMap.values()) {
+            out += broker + "\n";
+        }
+
+        out += "\nRegistration history:\n";
+        Iterator<RegistrationRecord> it = mRegistrationRecordDeque.descendingIterator();
+        while (it.hasNext()) {
+            out += it.next() + "\n";
+        }
+
+        return out;
+    }
 }
diff --git a/services/core/java/com/android/server/location/ContextHubService.java b/services/core/java/com/android/server/location/ContextHubService.java
index 36b0342..787a800 100644
--- a/services/core/java/com/android/server/location/ContextHubService.java
+++ b/services/core/java/com/android/server/location/ContextHubService.java
@@ -795,6 +795,10 @@
         // Dump nanoAppHash
         mNanoAppStateManager.foreachNanoAppInstanceInfo((info) -> pw.println(info));
 
+        pw.println("");
+        pw.println("=================== CLIENTS ====================");
+        pw.println(mClientManager);
+
         // dump eventLog
     }
 
diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java
index ec11a97..14ef2d3a 100644
--- a/services/core/java/com/android/server/pm/AppsFilter.java
+++ b/services/core/java/com/android/server/pm/AppsFilter.java
@@ -136,8 +136,11 @@
             DeviceConfig.addOnPropertiesChangedListener(
                     NAMESPACE_PACKAGE_MANAGER_SERVICE, FgThread.getExecutor(),
                     properties -> {
-                        synchronized (FeatureConfigImpl.this) {
-                            mFeatureEnabled = properties.getBoolean(FILTERING_ENABLED_NAME, false);
+                        if (properties.getKeyset().contains(FILTERING_ENABLED_NAME)) {
+                            synchronized (FeatureConfigImpl.this) {
+                                mFeatureEnabled = properties.getBoolean(FILTERING_ENABLED_NAME,
+                                        false);
+                            }
                         }
                     });
         }
diff --git a/services/core/java/com/android/server/pm/DataLoaderManagerService.java b/services/core/java/com/android/server/pm/DataLoaderManagerService.java
new file mode 100644
index 0000000..0719797
--- /dev/null
+++ b/services/core/java/com/android/server/pm/DataLoaderManagerService.java
@@ -0,0 +1,246 @@
+/*
+ * 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.pm;
+
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IDataLoader;
+import android.content.pm.IDataLoaderManager;
+import android.content.pm.IDataLoaderStatusListener;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.Slog;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.SystemService;
+
+import java.util.List;
+
+/**
+ * Data loader manager service manages data loader binder services.
+ *
+ * @hide
+ */
+public class DataLoaderManagerService extends SystemService {
+    private static final String TAG = "DataLoaderManager";
+    private final Context mContext;
+    private final DataLoaderManagerBinderService mBinderService;
+    private final Object mLock = new Object();
+    @GuardedBy("mLock")
+    private SparseArray<DataLoaderServiceConnection> mServiceConnections;
+
+    public DataLoaderManagerService(Context context) {
+        super(context);
+        mContext = context;
+        mBinderService = new DataLoaderManagerBinderService();
+    }
+
+    @Override
+    public void onStart() {
+        publishBinderService(Context.DATA_LOADER_MANAGER_SERVICE, mBinderService);
+    }
+
+    final class DataLoaderManagerBinderService extends IDataLoaderManager.Stub {
+        @Override
+        public boolean initializeDataLoader(int dataLoaderId, Bundle params,
+                IDataLoaderStatusListener listener) {
+            synchronized (mLock) {
+                if (mServiceConnections == null) {
+                    mServiceConnections = new SparseArray<>();
+                }
+                if (mServiceConnections.get(dataLoaderId) != null) {
+                    Slog.e(TAG, "Data loader of ID=" + dataLoaderId + " already exists.");
+                    return false;
+                }
+            }
+            CharSequence packageNameSeq = params.getCharSequence("packageName");
+            if (packageNameSeq == null) {
+                Slog.e(TAG, "Must specify package name.");
+                return false;
+            }
+            String packageName = packageNameSeq.toString();
+            ComponentName dataLoaderComponent = getDataLoaderServiceName(packageName);
+            if (dataLoaderComponent == null) {
+                return false;
+            }
+            // Binds to the specific data loader service
+            DataLoaderServiceConnection connection =
+                    new DataLoaderServiceConnection(dataLoaderId, params, listener);
+            Intent intent = new Intent();
+            intent.setComponent(dataLoaderComponent);
+            if (!mContext.bindServiceAsUser(intent, connection, Context.BIND_AUTO_CREATE,
+                    UserHandle.of(UserHandle.getCallingUserId()))) {
+                Slog.e(TAG, "Failed to bind to data loader binder service.");
+                mContext.unbindService(connection);
+                return false;
+            }
+            return true;
+        }
+
+        /**
+         * Find the ComponentName of the data loader service provider, given its package name.
+         *
+         * @param packageName the package name of the provider.
+         * @return ComponentName of the data loader service provider. Null if provider not found.
+         */
+        private @Nullable ComponentName getDataLoaderServiceName(String packageName) {
+            final PackageManager pm = mContext.getPackageManager();
+            if (pm == null) {
+                Slog.e(TAG, "PackageManager is not available.");
+                return null;
+            }
+            Intent intent = new Intent(Intent.ACTION_LOAD_DATA);
+            intent.setPackage(packageName);
+            List<ResolveInfo> services =
+                    pm.queryIntentServicesAsUser(intent, 0, UserHandle.getCallingUserId());
+            if (services == null || services.isEmpty()) {
+                Slog.e(TAG,
+                        "Failed to find data loader service provider in package " + packageName);
+                return null;
+            }
+
+            // TODO(b/136132412): better way to enable privileged data loaders in tests
+            boolean checkLoader =
+                    android.os.SystemProperties.getBoolean("incremental.check_loader", false);
+            int numServices = services.size();
+            for (int i = 0; i < numServices; i++) {
+                ResolveInfo ri = services.get(i);
+                ComponentName componentName = new ComponentName(
+                        ri.serviceInfo.packageName, ri.serviceInfo.name);
+                // There should only be one matching provider inside the given package.
+                // If there's more than one, return the first one found.
+                try {
+                    ApplicationInfo ai = pm.getApplicationInfo(componentName.getPackageName(), 0);
+                    if (checkLoader && !ai.isPrivilegedApp()) {
+                        Slog.w(TAG,
+                                "Data loader: " + componentName.getPackageName()
+                                        + " is not a privileged app, skipping.");
+                        continue;
+                    }
+                    return componentName;
+                } catch (PackageManager.NameNotFoundException ex) {
+                    Slog.w(TAG,
+                            "Privileged data loader: " + componentName.getPackageName()
+                                    + " not found, skipping.");
+                }
+
+            }
+            Slog.e(TAG, "Didn't find any matching data loader service provider.");
+            return null;
+        }
+
+        /**
+         * Returns the binder object of a data loader, specified by its ID.
+         */
+        @Override
+        public @Nullable IDataLoader getDataLoader(int dataLoaderId) {
+            synchronized (mLock) {
+                if (mServiceConnections == null) {
+                    return null;
+                }
+                DataLoaderServiceConnection serviceConnection = mServiceConnections.get(
+                        dataLoaderId, null);
+                if (serviceConnection == null) {
+                    return null;
+                }
+                return serviceConnection.getDataLoader();
+            }
+        }
+
+        /**
+         * Destroys a data loader binder service, specified by its ID.
+         */
+        @Override
+        public void destroyDataLoader(int dataLoaderId) {
+            synchronized (mLock) {
+                if (mServiceConnections == null) {
+                    return;
+                }
+                DataLoaderServiceConnection serviceConnection = mServiceConnections.get(
+                        dataLoaderId, null);
+
+                if (serviceConnection == null) {
+                    return;
+                }
+                serviceConnection.destroy();
+            }
+        }
+    }
+
+    class DataLoaderServiceConnection implements ServiceConnection {
+        final int mId;
+        final Bundle mParams;
+        final IDataLoaderStatusListener mListener;
+        IDataLoader mDataLoader;
+
+        DataLoaderServiceConnection(int id, Bundle params, IDataLoaderStatusListener listener) {
+            mId = id;
+            mParams = params;
+            mListener = listener;
+            mDataLoader = null;
+        }
+
+        @Override
+        public void onServiceConnected(ComponentName className, IBinder service) {
+            mDataLoader = IDataLoader.Stub.asInterface(service);
+            synchronized (mLock) {
+                mServiceConnections.append(mId, this);
+            }
+            try {
+                mDataLoader.create(mId, mParams, mListener);
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Failed to create data loader service.", e);
+            }
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName arg0) {
+            remove();
+        }
+
+        IDataLoader getDataLoader() {
+            return mDataLoader;
+        }
+
+        void destroy() {
+            try {
+                mDataLoader.destroy();
+            } catch (RemoteException ignored) {
+            }
+            mContext.unbindService(this);
+        }
+
+        private void remove() {
+            synchronized (mLock) {
+                mServiceConnections.remove(mId);
+                if (mServiceConnections.size() == 0) {
+                    mServiceConnections = null;
+                }
+            }
+            mParams.clear();
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 4852947..883baf7 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -54,6 +54,7 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.IPackageInstallObserver2;
 import android.content.pm.IPackageInstallerSession;
+import android.content.pm.InstallationFile;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageInstaller;
 import android.content.pm.PackageInstaller.SessionInfo;
@@ -81,6 +82,8 @@
 import android.os.RevocableFileDescriptor;
 import android.os.SystemProperties;
 import android.os.UserHandle;
+import android.os.incremental.IncrementalFileStorages;
+import android.os.incremental.IncrementalManager;
 import android.os.storage.StorageManager;
 import android.stats.devicepolicy.DevicePolicyEnums;
 import android.system.ErrnoException;
@@ -309,6 +312,8 @@
     @GuardedBy("mLock")
     private boolean mVerityFound;
 
+    private IncrementalFileStorages mIncrementalFileStorages;
+
     private static final FileFilter sAddedFilter = new FileFilter() {
         @Override
         public boolean accept(File file) {
@@ -471,6 +476,18 @@
         mStagedSessionErrorCode = stagedSessionErrorCode;
         mStagedSessionErrorMessage =
                 stagedSessionErrorMessage != null ? stagedSessionErrorMessage : "";
+
+        // TODO(b/136132412): sanity check if session should not be incremental
+        if (!params.isStaged && params.incrementalParams != null
+                && !params.incrementalParams.getPackageName().isEmpty()) {
+            IncrementalManager incrementalManager = (IncrementalManager) mContext.getSystemService(
+                    Context.INCREMENTAL_SERVICE);
+            if (incrementalManager != null) {
+                mIncrementalFileStorages =
+                        new IncrementalFileStorages(mPackageName, stageDir, incrementalManager,
+                                params.incrementalParams);
+            }
+        }
     }
 
     public SessionInfo generateInfo() {
@@ -874,10 +891,18 @@
             }
         }
 
+        if (mIncrementalFileStorages != null) {
+            mIncrementalFileStorages.finishSetUp();
+        }
+
         mHandler.obtainMessage(MSG_SEAL, statusReceiver).sendToTarget();
     }
 
     private void handleSeal(@NonNull IntentSender statusReceiver) {
+        // TODO(b/136132412): update with new APIs
+        if (mIncrementalFileStorages != null) {
+            mIncrementalFileStorages.startLoading();
+        }
         if (!markAsCommitted(statusReceiver)) {
             return;
         }
@@ -1492,6 +1517,7 @@
                 computeProgressLocked(true);
 
                 // Unpack native libraries
+                // TODO(b/136132412): skip for incremental installation
                 extractNativeLibraries(stageDir, params.abiOverride, mayInheritNativeLibs());
             }
 
@@ -2182,7 +2208,6 @@
             }
             destroyInternal();
         }
-
         dispatchSessionFinished(INSTALL_FAILED_ABORTED, "Session was abandoned", null);
     }
 
@@ -2268,6 +2293,20 @@
         return mParentSessionId;
     }
 
+    @Override
+    public void addFile(@NonNull String name, long size, @NonNull byte[] metadata) {
+        if (mIncrementalFileStorages == null) {
+            throw new IllegalStateException(
+                    "Cannot add Incremental File to a non-Incremental session.");
+        }
+        try {
+            mIncrementalFileStorages.addFile(new InstallationFile(name, size, metadata));
+        } catch (IOException ex) {
+            throw new IllegalStateException(
+                    "Failed to add and configure Incremental File: " + name, ex);
+        }
+    }
+
     private void dispatchSessionFinished(int returnCode, String msg, Bundle extras) {
         final IntentSender statusReceiver;
         final String packageName;
@@ -2390,6 +2429,9 @@
         // since these packages are supposed to be read on reboot.
         // Those dirs are deleted when the staged session has reached a final state.
         if (stageDir != null && !params.isStaged) {
+            if (mIncrementalFileStorages != null) {
+                mIncrementalFileStorages.cleanUp();
+            }
             try {
                 mPm.mInstaller.rmPackageDir(stageDir.getAbsolutePath());
             } catch (InstallerException ignored) {
@@ -2403,6 +2445,9 @@
                 mSessionProvider.getSession(childSessionId).cleanStageDir();
             }
         } else {
+            if (mIncrementalFileStorages != null) {
+                mIncrementalFileStorages.cleanUp();
+            }
             try {
                 mPm.mInstaller.rmPackageDir(stageDir.getAbsolutePath());
             } catch (InstallerException ignored) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 4e0e4ff..c5e7942 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -153,6 +153,7 @@
 import android.content.pm.IPackageManagerNative;
 import android.content.pm.IPackageMoveObserver;
 import android.content.pm.IPackageStatsObserver;
+import android.content.pm.InstallSourceInfo;
 import android.content.pm.InstantAppInfo;
 import android.content.pm.InstantAppRequest;
 import android.content.pm.InstrumentationInfo;
@@ -20128,19 +20129,95 @@
     public String getInstallerPackageName(String packageName) {
         final int callingUid = Binder.getCallingUid();
         synchronized (mLock) {
-            final PackageSetting ps = mSettings.mPackages.get(packageName);
-            if (shouldFilterApplicationLocked(
-                    ps, callingUid, UserHandle.getUserId(callingUid))) {
-                return null;
+            final InstallSource installSource = getInstallSourceLocked(packageName, callingUid);
+            if (installSource == null) {
+                throw new IllegalArgumentException("Unknown package: " + packageName);
             }
-            // InstallerPackageName for Apex is not stored in PackageManager
-            if (ps == null && mApexManager.isApexPackage(packageName)) {
-                return null;
+            String installerPackageName = installSource.installerPackageName;
+            if (installerPackageName != null) {
+                final PackageSetting ps = mSettings.mPackages.get(installerPackageName);
+                if (ps == null || shouldFilterApplicationLocked(ps, callingUid,
+                        UserHandle.getUserId(callingUid))) {
+                    installerPackageName = null;
+                }
             }
-            return mSettings.getInstallerPackageNameLPr(packageName);
+            return installerPackageName;
         }
     }
 
+    @Override
+    @Nullable
+    public InstallSourceInfo getInstallSourceInfo(String packageName) {
+        final int callingUid = Binder.getCallingUid();
+        final int userId = UserHandle.getUserId(callingUid);
+
+        String installerPackageName;
+        String initiatingPackageName;
+        String originatingPackageName;
+
+        synchronized (mLock) {
+            final InstallSource installSource = getInstallSourceLocked(packageName, callingUid);
+            if (installSource == null) {
+                return null;
+            }
+
+            installerPackageName = installSource.installerPackageName;
+            if (installerPackageName != null) {
+                final PackageSetting ps = mSettings.mPackages.get(installerPackageName);
+                if (ps == null || shouldFilterApplicationLocked(ps, callingUid, userId)) {
+                    installerPackageName = null;
+                }
+            }
+
+            // All installSource strings are interned, so == is ok here
+            if (installSource.initiatingPackageName == installSource.installerPackageName) {
+                // The installer and initiator will often be the same, and when they are
+                // we can skip doing the same check again.
+                initiatingPackageName = installerPackageName;
+            } else {
+                initiatingPackageName = installSource.initiatingPackageName;
+                final PackageSetting ps = mSettings.mPackages.get(initiatingPackageName);
+                if (ps == null || shouldFilterApplicationLocked(ps, callingUid, userId)) {
+                    initiatingPackageName = null;
+                }
+
+            }
+            originatingPackageName = installSource.originatingPackageName;
+            if (originatingPackageName != null) {
+                final PackageSetting ps = mSettings.mPackages.get(originatingPackageName);
+                if (ps == null || shouldFilterApplicationLocked(ps, callingUid, userId)) {
+                    originatingPackageName = null;
+                }
+            }
+        }
+
+        if (originatingPackageName != null && mContext.checkCallingOrSelfPermission(
+                Manifest.permission.INSTALL_PACKAGES) != PackageManager.PERMISSION_GRANTED) {
+            originatingPackageName = null;
+        }
+
+        return new InstallSourceInfo(initiatingPackageName, originatingPackageName,
+                installerPackageName);
+    }
+
+    @GuardedBy("mLock")
+    @Nullable
+    private InstallSource getInstallSourceLocked(String packageName, int callingUid) {
+        final PackageSetting ps = mSettings.mPackages.get(packageName);
+
+        // Installer info for Apex is not stored in PackageManager
+        if (ps == null && mApexManager.isApexPackage(packageName)) {
+            return InstallSource.EMPTY;
+        }
+
+        if (ps == null || shouldFilterApplicationLocked(ps, callingUid,
+                UserHandle.getUserId(callingUid))) {
+            return null;
+        }
+
+        return ps.installSource;
+    }
+
     public boolean isOrphaned(String packageName) {
         // reader
         synchronized (mLock) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 1b73602..b961096 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -1854,22 +1854,23 @@
         if (internal.isApexPackage(packageName)) {
             internal.uninstallApex(
                     packageName, versionCode, translatedUserId, receiver.getIntentSender(), flags);
-        } else if ((flags & PackageManager.DELETE_ALL_USERS) != 0) {
-            final PackageInfo info = mInterface.getPackageInfo(packageName,
-                    PackageManager.MATCH_STATIC_SHARED_LIBRARIES, translatedUserId);
-            if (info == null) {
-                pw.println("Failure [not installed for " + translatedUserId + "]");
-                return 1;
+        } else {
+            if ((flags & PackageManager.DELETE_ALL_USERS) == 0) {
+                final PackageInfo info = mInterface.getPackageInfo(packageName,
+                        PackageManager.MATCH_STATIC_SHARED_LIBRARIES, translatedUserId);
+                if (info == null) {
+                    pw.println("Failure [not installed for " + translatedUserId + "]");
+                    return 1;
+                }
+                final boolean isSystem =
+                        (info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
+                // If we are being asked to delete a system app for just one
+                // user set flag so it disables rather than reverting to system
+                // version of the app.
+                if (isSystem) {
+                    flags |= PackageManager.DELETE_SYSTEM_APP;
+                }
             }
-            final boolean isSystem =
-                    (info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
-            // If we are being asked to delete a system app for just one
-            // user set flag so it disables rather than reverting to system
-            // version of the app.
-            if (isSystem) {
-                flags |= PackageManager.DELETE_SYSTEM_APP;
-            }
-
             mInterface.getPackageInstaller().uninstall(new VersionedPackage(packageName,
                             versionCode), null /*callerPackageName*/, flags,
                     receiver.getIntentSender(), translatedUserId);
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index f67d3c0..6e67687 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -4257,14 +4257,6 @@
         return userState.isMatch(componentInfo, flags);
     }
 
-    String getInstallerPackageNameLPr(String packageName) {
-        final PackageSetting pkg = mPackages.get(packageName);
-        if (pkg == null) {
-            throw new IllegalArgumentException("Unknown package: " + packageName);
-        }
-        return pkg.installSource.installerPackageName;
-    }
-
     boolean isOrphaned(String packageName) {
         final PackageSetting pkg = mPackages.get(packageName);
         if (pkg == null) {
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 14cbe75..ad4411c 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -3452,8 +3452,13 @@
     /**
      * Find the current guest user. If the Guest user is partial,
      * then do not include it in the results as it is about to die.
+     *
+     * @return The current guest user.  Null if it doesn't exist.
+     * @hide
      */
-    private UserInfo findCurrentGuestUser() {
+    @Override
+    public UserInfo findCurrentGuestUser() {
+        checkManageUsersPermission("findCurrentGuestUser");
         synchronized (mUsersLock) {
             final int size = mUsers.size();
             for (int i = 0; i < size; i++) {
diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java
index 3c4e3f6..9b9f93f 100644
--- a/services/core/java/com/android/server/policy/PermissionPolicyService.java
+++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java
@@ -29,10 +29,8 @@
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
 import android.app.AppOpsManager;
-import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
-import android.content.IntentFilter;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
@@ -171,23 +169,6 @@
         } catch (RemoteException doesNotHappen) {
             Slog.wtf(LOG_TAG, "Cannot set up app-ops listener");
         }
-
-        IntentFilter intentFilter = new IntentFilter();
-        intentFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
-        intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
-        intentFilter.addDataScheme("package");
-
-        getContext().registerReceiverAsUser(new BroadcastReceiver() {
-            @Override
-            public void onReceive(Context context, Intent intent) {
-                UserHandle user =
-                        UserHandle.getUserHandleForUid(intent.getIntExtra(Intent.EXTRA_UID, -1));
-                new PermissionControllerManager(
-                        getUserContext(getContext(), user), FgThread.getHandler())
-                        .updateUserSensitive();
-            }
-        }, UserHandle.ALL, intentFilter, null, null);
-
     }
 
     /**
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 114a16a..14617d3 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -203,8 +203,11 @@
     // System Property indicating that retail demo mode is currently enabled.
     private static final String SYSTEM_PROPERTY_RETAIL_DEMO_ENABLED = "sys.retaildemo.enabled";
 
-    // Possible reasons for shutting down or reboot for use in REBOOT_PROPERTY(sys.boot.reason)
-    // which is set by bootstat
+    // System property for last reboot reason
+    private static final String SYSTEM_PROPERTY_REBOOT_REASON = "sys.boot.reason";
+
+    // Possible reasons for shutting down or reboot for use in
+    // SYSTEM_PROPERTY_REBOOT_REASON(sys.boot.reason) which is set by bootstat
     private static final String REASON_SHUTDOWN = "shutdown";
     private static final String REASON_REBOOT = "reboot";
     private static final String REASON_USERREQUESTED = "shutdown,userrequested";
@@ -225,9 +228,6 @@
     private static final int HALT_MODE_REBOOT = 1;
     private static final int HALT_MODE_REBOOT_SAFE_MODE = 2;
 
-    // property for last reboot reason
-    private static final String REBOOT_PROPERTY = "sys.boot.reason";
-
     private final Context mContext;
     private final ServiceThread mHandlerThread;
     private final PowerManagerHandler mHandler;
@@ -240,6 +240,7 @@
     private final BinderService mBinderService;
     private final LocalService mLocalService;
     private final NativeWrapper mNativeWrapper;
+    private final SystemPropertiesWrapper mSystemProperties;
     private final Injector mInjector;
 
     private LightsManager mLightsManager;
@@ -756,6 +757,20 @@
         InattentiveSleepWarningController createInattentiveSleepWarningController() {
             return new InattentiveSleepWarningController();
         }
+
+        public SystemPropertiesWrapper createSystemPropertiesWrapper() {
+            return new SystemPropertiesWrapper() {
+                @Override
+                public String get(String key, String def) {
+                    return SystemProperties.get(key, def);
+                }
+
+                @Override
+                public void set(String key, String val) {
+                    SystemProperties.set(key, val);
+                }
+            };
+        }
     }
 
     final Constants mConstants;
@@ -781,6 +796,7 @@
         mBinderService = new BinderService();
         mLocalService = new LocalService();
         mNativeWrapper = injector.createNativeWrapper();
+        mSystemProperties = injector.createSystemPropertiesWrapper();
         mInjector = injector;
 
         mHandlerThread = new ServiceThread(TAG,
@@ -816,7 +832,7 @@
             mHalInteractiveModeEnabled = true;
 
             mWakefulness = WAKEFULNESS_AWAKE;
-            sQuiescent = SystemProperties.get(SYSTEM_PROPERTY_QUIESCENT, "0").equals("1");
+            sQuiescent = mSystemProperties.get(SYSTEM_PROPERTY_QUIESCENT, "0").equals("1");
 
             mNativeWrapper.nativeInit(this);
             mNativeWrapper.nativeSetAutoSuspend(false);
@@ -1067,8 +1083,9 @@
         }
 
         final String retailDemoValue = UserManager.isDeviceInDemoMode(mContext) ? "1" : "0";
-        if (!retailDemoValue.equals(SystemProperties.get(SYSTEM_PROPERTY_RETAIL_DEMO_ENABLED))) {
-            SystemProperties.set(SYSTEM_PROPERTY_RETAIL_DEMO_ENABLED, retailDemoValue);
+        if (!retailDemoValue.equals(
+                mSystemProperties.get(SYSTEM_PROPERTY_RETAIL_DEMO_ENABLED, null))) {
+            mSystemProperties.set(SYSTEM_PROPERTY_RETAIL_DEMO_ENABLED, retailDemoValue);
         }
 
         mScreenBrightnessModeSetting = Settings.System.getIntForUser(resolver,
@@ -4820,7 +4837,7 @@
 
             final long ident = Binder.clearCallingIdentity();
             try {
-                return getLastShutdownReasonInternal(REBOOT_PROPERTY);
+                return getLastShutdownReasonInternal();
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -5054,12 +5071,8 @@
     }
 
     @VisibleForTesting
-    // lastRebootReasonProperty argument to permit testing
-    int getLastShutdownReasonInternal(String lastRebootReasonProperty) {
-        String line = SystemProperties.get(lastRebootReasonProperty);
-        if (line == null) {
-            return PowerManager.SHUTDOWN_REASON_UNKNOWN;
-        }
+    int getLastShutdownReasonInternal() {
+        String line = mSystemProperties.get(SYSTEM_PROPERTY_REBOOT_REASON, null);
         switch (line) {
             case REASON_SHUTDOWN:
                 return PowerManager.SHUTDOWN_REASON_SHUTDOWN;
diff --git a/services/core/java/com/android/server/power/SystemPropertiesWrapper.java b/services/core/java/com/android/server/power/SystemPropertiesWrapper.java
new file mode 100644
index 0000000..1acf798
--- /dev/null
+++ b/services/core/java/com/android/server/power/SystemPropertiesWrapper.java
@@ -0,0 +1,51 @@
+/*
+ * 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.power;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.SystemProperties;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * Wrapper interface to access {@link SystemProperties}.
+ *
+ * @hide
+ */
+@VisibleForTesting
+interface SystemPropertiesWrapper {
+    /**
+     * Get the String value for the given {@code key}.
+     *
+     * @param key the key to lookup
+     * @param def the default value in case the property is not set or empty
+     * @return if the {@code key} isn't found, return {@code def} if it isn't null, or an empty
+     * string otherwise
+     */
+    @NonNull
+    String get(@NonNull String key, @Nullable String def);
+
+    /**
+     * Set the value for the given {@code key} to {@code val}.
+     *
+     * @throws IllegalArgumentException if the {@code val} exceeds 91 characters
+     * @throws RuntimeException if the property cannot be set, for example, if it was blocked by
+     * SELinux. libc will log the underlying reason.
+     */
+    void set(@NonNull String key, @Nullable String val);
+}
diff --git a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
index 00779ec..3f5e2a4 100644
--- a/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
+++ b/services/core/java/com/android/server/rollback/RollbackManagerServiceImpl.java
@@ -57,6 +57,7 @@
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.server.LocalServices;
+import com.android.server.PackageWatchdog;
 import com.android.server.Watchdog;
 import com.android.server.pm.Installer;
 
@@ -1200,6 +1201,8 @@
             for (Rollback rollback : mRollbacks) {
                 rollback.dump(ipw);
             }
+            ipw.println();
+            PackageWatchdog.getInstance(mContext).dump(ipw);
         }
     }
 
diff --git a/services/core/java/com/android/server/timedetector/SimpleTimeDetectorStrategy.java b/services/core/java/com/android/server/timedetector/SimpleTimeDetectorStrategy.java
index 0b970bf..340fe3d 100644
--- a/services/core/java/com/android/server/timedetector/SimpleTimeDetectorStrategy.java
+++ b/services/core/java/com/android/server/timedetector/SimpleTimeDetectorStrategy.java
@@ -150,7 +150,8 @@
             if (!mCallback.isAutoTimeDetectionEnabled()) {
                 if (DBG) {
                     Slog.d(LOG_TAG, "Auto time detection is not enabled."
-                            + " time=" + time
+                            + " origin=" + origin
+                            + ", time=" + time
                             + ", cause=" + cause);
                 }
                 return;
@@ -159,7 +160,8 @@
             if (mCallback.isAutoTimeDetectionEnabled()) {
                 if (DBG) {
                     Slog.d(LOG_TAG, "Auto time detection is enabled."
-                            + " time=" + time
+                            + " origin=" + origin
+                            + ", time=" + time
                             + ", cause=" + cause);
                 }
                 return;
@@ -232,16 +234,16 @@
 
     @Override
     public synchronized void dump(@NonNull PrintWriter pw, @Nullable String[] args) {
-        pw.println("mLastPhoneSuggestion=" + mLastPhoneSuggestion);
-        pw.println("mLastAutoSystemClockTimeSet=" + mLastAutoSystemClockTimeSet);
-        pw.println("mLastAutoSystemClockTime=" + mLastAutoSystemClockTime);
-        pw.println("mLastAutoSystemClockTimeSendNetworkBroadcast="
+        IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
+        ipw.println("TimeDetectorStrategy:");
+        ipw.increaseIndent(); // level 1
+
+        ipw.println("mLastPhoneSuggestion=" + mLastPhoneSuggestion);
+        ipw.println("mLastAutoSystemClockTimeSet=" + mLastAutoSystemClockTimeSet);
+        ipw.println("mLastAutoSystemClockTime=" + mLastAutoSystemClockTime);
+        ipw.println("mLastAutoSystemClockTimeSendNetworkBroadcast="
                 + mLastAutoSystemClockTimeSendNetworkBroadcast);
 
-        IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " ");
-
-        ipw.println("TimeDetectorStrategyImpl logs:");
-        ipw.increaseIndent(); // level 1
 
         ipw.println("Time change log:");
         ipw.increaseIndent(); // level 2
@@ -249,6 +251,7 @@
         ipw.decreaseIndent(); // level 2
 
         ipw.decreaseIndent(); // level 1
+        ipw.flush();
     }
 
     @GuardedBy("this")
diff --git a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
index 32cee2d..0a6c2e7 100644
--- a/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
+++ b/services/core/java/com/android/server/timedetector/TimeDetectorStrategy.java
@@ -39,6 +39,12 @@
 
     /**
      * The interface used by the strategy to interact with the surrounding service.
+     *
+     * <p>Note: Because the system properties-derived value {@link #isAutoTimeDetectionEnabled()}
+     * can be modified independently and from different threads (and processes!). its use is prone
+     * to race conditions. That will be true until the responsibility for setting their values is
+     * moved to {@link TimeDetectorStrategy}. There are similar issues with
+     * {@link #systemClockMillis()} while any process can modify the system clock.
      */
     interface Callback {
 
diff --git a/services/core/java/com/android/server/utils/TimingsTraceAndSlog.java b/services/core/java/com/android/server/utils/TimingsTraceAndSlog.java
index 03c96e9..6bdb5ce 100644
--- a/services/core/java/com/android/server/utils/TimingsTraceAndSlog.java
+++ b/services/core/java/com/android/server/utils/TimingsTraceAndSlog.java
@@ -55,7 +55,16 @@
      * Default constructor using {@code system_server} tags.
      */
     public TimingsTraceAndSlog() {
-        this(SYSTEM_SERVER_TIMING_TAG, Trace.TRACE_TAG_SYSTEM_SERVER);
+        this(SYSTEM_SERVER_TIMING_TAG);
+    }
+
+    /**
+     * Custom constructor using {@code system_server} trace tag.
+     *
+     * @param tag {@code logcat} tag
+     */
+    public TimingsTraceAndSlog(@NonNull String tag) {
+        this(tag, Trace.TRACE_TAG_SYSTEM_SERVER);
     }
 
     /**
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index d66aa18..03139d2e 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -104,6 +104,7 @@
 import com.android.server.FgThread;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
+import com.android.server.utils.TimingsTraceAndSlog;
 import com.android.server.wm.WindowManagerInternal;
 
 import libcore.io.IoUtils;
@@ -1785,25 +1786,26 @@
 
     @Override
     public void onUnlockUser(final int userId) {
-        synchronized (mLock) {
-            if (mCurrentUserId == userId) {
-                if (mWaitingForUnlock) {
-                    // the desired wallpaper is not direct-boot aware, load it now
-                    final WallpaperData systemWallpaper =
-                            getWallpaperSafeLocked(userId, FLAG_SYSTEM);
-                    switchWallpaper(systemWallpaper, null);
-                    notifyCallbacksLocked(systemWallpaper);
-                }
+        TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG);
+        t.traceBegin("on-unlock-user-" + userId);
+        try {
+            synchronized (mLock) {
+                if (mCurrentUserId == userId) {
+                    if (mWaitingForUnlock) {
+                        // the desired wallpaper is not direct-boot aware, load it now
+                        final WallpaperData systemWallpaper =
+                                getWallpaperSafeLocked(userId, FLAG_SYSTEM);
+                        switchWallpaper(systemWallpaper, null);
+                        notifyCallbacksLocked(systemWallpaper);
+                    }
 
-                // Make sure that the SELinux labeling of all the relevant files is correct.
-                // This corrects for mislabeling bugs that might have arisen from move-to
-                // operations involving the wallpaper files.  This isn't timing-critical,
-                // so we do it in the background to avoid holding up the user unlock operation.
-                if (!mUserRestorecon.get(userId)) {
-                    mUserRestorecon.put(userId, true);
-                    Runnable relabeler = new Runnable() {
-                        @Override
-                        public void run() {
+                    // Make sure that the SELinux labeling of all the relevant files is correct.
+                    // This corrects for mislabeling bugs that might have arisen from move-to
+                    // operations involving the wallpaper files.  This isn't timing-critical,
+                    // so we do it in the background to avoid holding up the user unlock operation.
+                    if (!mUserRestorecon.get(userId)) {
+                        mUserRestorecon.put(userId, true);
+                        Runnable relabeler = () -> {
                             final File wallpaperDir = getWallpaperDir(userId);
                             for (String filename : sPerUserFiles) {
                                 File f = new File(wallpaperDir, filename);
@@ -1811,11 +1813,13 @@
                                     SELinux.restorecon(f);
                                 }
                             }
-                        }
-                    };
-                    BackgroundThread.getHandler().post(relabeler);
+                        };
+                        BackgroundThread.getHandler().post(relabeler);
+                    }
                 }
             }
+        } finally {
+            t.traceEnd();
         }
     }
 
@@ -1833,31 +1837,37 @@
     }
 
     void switchUser(int userId, IRemoteCallback reply) {
-        final WallpaperData systemWallpaper;
-        final WallpaperData lockWallpaper;
-        synchronized (mLock) {
-            if (mCurrentUserId == userId) {
-                return;
+        TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG);
+        t.traceBegin("switch-user-" + userId);
+        try {
+            final WallpaperData systemWallpaper;
+            final WallpaperData lockWallpaper;
+            synchronized (mLock) {
+                if (mCurrentUserId == userId) {
+                    return;
+                }
+                mCurrentUserId = userId;
+                systemWallpaper = getWallpaperSafeLocked(userId, FLAG_SYSTEM);
+                final WallpaperData tmpLockWallpaper = mLockWallpaperMap.get(userId);
+                lockWallpaper = tmpLockWallpaper == null ? systemWallpaper : tmpLockWallpaper;
+                // Not started watching yet, in case wallpaper data was loaded for other reasons.
+                if (systemWallpaper.wallpaperObserver == null) {
+                    systemWallpaper.wallpaperObserver = new WallpaperObserver(systemWallpaper);
+                    systemWallpaper.wallpaperObserver.startWatching();
+                }
+                switchWallpaper(systemWallpaper, reply);
             }
-            mCurrentUserId = userId;
-            systemWallpaper = getWallpaperSafeLocked(userId, FLAG_SYSTEM);
-            final WallpaperData tmpLockWallpaper = mLockWallpaperMap.get(userId);
-            lockWallpaper = tmpLockWallpaper == null ? systemWallpaper : tmpLockWallpaper;
-            // Not started watching yet, in case wallpaper data was loaded for other reasons.
-            if (systemWallpaper.wallpaperObserver == null) {
-                systemWallpaper.wallpaperObserver = new WallpaperObserver(systemWallpaper);
-                systemWallpaper.wallpaperObserver.startWatching();
-            }
-            switchWallpaper(systemWallpaper, reply);
-        }
 
-        // Offload color extraction to another thread since switchUser will be called
-        // from the main thread.
-        FgThread.getHandler().post(() -> {
-            notifyWallpaperColorsChanged(systemWallpaper, FLAG_SYSTEM);
-            notifyWallpaperColorsChanged(lockWallpaper, FLAG_LOCK);
-            notifyWallpaperColorsChanged(mFallbackWallpaper, FLAG_SYSTEM);
-        });
+            // Offload color extraction to another thread since switchUser will be called
+            // from the main thread.
+            FgThread.getHandler().post(() -> {
+                notifyWallpaperColorsChanged(systemWallpaper, FLAG_SYSTEM);
+                notifyWallpaperColorsChanged(lockWallpaper, FLAG_LOCK);
+                notifyWallpaperColorsChanged(mFallbackWallpaper, FLAG_SYSTEM);
+            });
+        } finally {
+            t.traceEnd();
+        }
     }
 
     void switchWallpaper(WallpaperData wallpaper, IRemoteCallback reply) {
diff --git a/services/core/java/com/android/server/wm/ActivityDisplay.java b/services/core/java/com/android/server/wm/ActivityDisplay.java
index db94cf1..9599bad 100644
--- a/services/core/java/com/android/server/wm/ActivityDisplay.java
+++ b/services/core/java/com/android/server/wm/ActivityDisplay.java
@@ -17,8 +17,6 @@
 package com.android.server.wm;
 
 import static android.app.ActivityTaskManager.INVALID_STACK_ID;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
@@ -34,7 +32,7 @@
 import static android.view.Display.FLAG_PRIVATE;
 import static android.view.Display.REMOVE_MODE_DESTROY_CONTENT;
 
-import static com.android.server.am.ActivityDisplayProto.CONFIGURATION_CONTAINER;
+import static com.android.server.am.ActivityDisplayProto.DISPLAY;
 import static com.android.server.am.ActivityDisplayProto.FOCUSED_STACK_ID;
 import static com.android.server.am.ActivityDisplayProto.ID;
 import static com.android.server.am.ActivityDisplayProto.RESUMED_ACTIVITY;
@@ -52,6 +50,7 @@
 import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_FOCUS_LIGHT;
 import static com.android.server.wm.RootActivityContainer.FindTaskResult;
 import static com.android.server.wm.RootActivityContainer.TAG_STATES;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DISPLAY;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
 
@@ -85,23 +84,19 @@
  * Exactly one of these classes per Display in the system. Capable of holding zero or more
  * attached {@link ActivityStack}s.
  */
-class ActivityDisplay extends ConfigurationContainer<ActivityStack> {
+class ActivityDisplay extends DisplayContent {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityDisplay" : TAG_ATM;
     private static final String TAG_STACK = TAG + POSTFIX_STACK;
 
     static final int POSITION_TOP = Integer.MAX_VALUE;
     static final int POSITION_BOTTOM = Integer.MIN_VALUE;
 
-
     /**
      * Counter for next free stack ID to use for dynamic activity stacks. Unique across displays.
      */
     private static int sNextFreeStackId = 0;
 
-    private ActivityTaskManagerService mService;
     private RootActivityContainer mRootActivityContainer;
-    // TODO: Remove once unification is complete.
-    DisplayContent mDisplayContent;
     /** Actual Display this object tracks. */
     int mDisplayId;
     Display mDisplay;
@@ -111,7 +106,6 @@
      * stacks, bottommost behind. Accessed directly by ActivityManager package classes. Any calls
      * changing the list should also call {@link #onStackOrderChanged()}.
      */
-    private final ArrayList<ActivityStack> mStacks = new ArrayList<>();
     private ArrayList<OnStackOrderChangedListener> mStackOrderChangedCallbacks = new ArrayList<>();
 
     /** Array of all UIDs that are present on the display. */
@@ -157,13 +151,6 @@
      */
     private ActivityStack mLastFocusedStack;
 
-    // Cached reference to some special stacks we tend to get a lot so we don't need to loop
-    // through the list to find them.
-    private ActivityStack mHomeStack = null;
-    private ActivityStack mRecentsStack = null;
-    private ActivityStack mPinnedStack = null;
-    private ActivityStack mSplitScreenPrimaryStack = null;
-
     // Used in updating the display size
     private Point mTmpDisplaySize = new Point();
 
@@ -173,15 +160,24 @@
     private final FindTaskResult mTmpFindTaskResult = new FindTaskResult();
 
     ActivityDisplay(RootActivityContainer root, Display display) {
+        super(display, root.mWindowManager);
         mRootActivityContainer = root;
-        mService = root.mService;
         mDisplayId = display.getDisplayId();
         mDisplay = display;
-        mDisplayContent = mService.mWindowManager.mRoot.createDisplayContent(mDisplay, this);
-        mDisplayContent.reconfigureDisplayLocked();
-        onRequestedOverrideConfigurationChanged(
-                mDisplayContent.getRequestedOverrideConfiguration());
-        mService.mWindowManager.mDisplayNotificationController.dispatchDisplayAdded(this);
+
+        if (DEBUG_DISPLAY) Slog.v(TAG_WM, "Adding display=" + display);
+
+        mWmService.mDisplayWindowSettings.applySettingsToDisplayLocked(this);
+
+        if (mWmService.mDisplayManagerInternal != null) {
+            mWmService.mDisplayManagerInternal
+                .setDisplayInfoOverrideFromWindowManager(mDisplayId, getDisplayInfo());
+            configureDisplayPolicy();
+        }
+
+        reconfigureDisplayLocked();
+        onRequestedOverrideConfigurationChanged(getRequestedOverrideConfiguration());
+        mWmService.mDisplayNotificationController.dispatchDisplayAdded(this);
     }
 
     void onDisplayChanged() {
@@ -190,7 +186,7 @@
         if (displayId != DEFAULT_DISPLAY) {
             final int displayState = mDisplay.getState();
             if (displayState == Display.STATE_OFF && mOffToken == null) {
-                mOffToken = mService.acquireSleepToken("Display-off", displayId);
+                mOffToken = mAtmService.acquireSleepToken("Display-off", displayId);
             } else if (displayState == Display.STATE_ON && mOffToken != null) {
                 mOffToken.release();
                 mOffToken = null;
@@ -199,98 +195,72 @@
 
         mDisplay.getRealSize(mTmpDisplaySize);
         setBounds(0, 0, mTmpDisplaySize.x, mTmpDisplaySize.y);
-        if (mDisplayContent != null) {
-            mDisplayContent.updateDisplayInfo();
-            mService.mWindowManager.requestTraversal();
-        }
+        updateDisplayInfo();
+        mWmService.requestTraversal();
     }
 
-    // TODO(display-unify): Merge with addChild below.
-    void addChild(ActivityStack stack, int position) {
-        addChild(stack, position, false /*fromDc*/);
+    void addStack(ActivityStack stack, int position) {
+        setStackOnDisplay(stack, position);
+        positionStackAt(stack, position);
+        mAtmService.updateSleepIfNeededLocked();
     }
 
-    void addChild(ActivityStack stack, int position, boolean fromDc) {
-        boolean toTop = position == POSITION_TOP;
-        if (position == POSITION_BOTTOM) {
-            position = 0;
-        } else if (toTop) {
-            position = getChildCount();
+    void onStackRemoved(ActivityStack stack) {
+        if (DEBUG_STACK) {
+            Slog.v(TAG_STACK, "removeStack: detaching " + stack + " from displayId=" + mDisplayId);
         }
-        if (DEBUG_STACK) Slog.v(TAG_STACK, "addChild: attaching " + stack
-                + " to displayId=" + mDisplayId + " position=" + position);
-        addStackReferenceIfNeeded(stack);
-        if (!fromDc) {
-            mDisplayContent.setStackOnDisplay(stack, position);
-        }
-        positionChildAt(stack, position);
-        mService.updateSleepIfNeededLocked();
-    }
-
-    // TODO(display-unify): Merge with removeChild below.
-    void onChildRemoved(ActivityStack stack) {
-        if (!mStacks.remove(stack)) {
-            // Stack no longer here!
-            return;
-        }
-
-        if (DEBUG_STACK) Slog.v(TAG_STACK, "removeChild: detaching " + stack
-                + " from displayId=" + mDisplayId);
         if (mPreferredTopFocusableStack == stack) {
             mPreferredTopFocusableStack = null;
         }
-        removeStackReferenceIfNeeded(stack);
         releaseSelfIfNeeded();
-        mService.updateSleepIfNeededLocked();
+        mAtmService.updateSleepIfNeededLocked();
         onStackOrderChanged(stack);
     }
 
-    void removeChild(ActivityStack stack) {
-        mDisplayContent.removeStackFromDisplay(stack);
+    void positionStackAtTop(ActivityStack stack, boolean includingParents) {
+        positionStackAtTop(stack, includingParents, null /* updateLastFocusedStackReason */);
     }
 
-    void positionChildAtTop(ActivityStack stack, boolean includingParents) {
-        positionChildAtTop(stack, includingParents, null /* updateLastFocusedStackReason */);
-    }
-
-    void positionChildAtTop(ActivityStack stack, boolean includingParents,
+    void positionStackAtTop(ActivityStack stack, boolean includingParents,
             String updateLastFocusedStackReason) {
-        positionChildAt(stack, getChildCount(), includingParents, updateLastFocusedStackReason);
+        positionStackAt(stack, getStackCount(), includingParents, updateLastFocusedStackReason);
     }
 
-    void positionChildAtBottom(ActivityStack stack) {
-        positionChildAtBottom(stack, null /* updateLastFocusedStackReason */);
+    void positionStackAtBottom(ActivityStack stack) {
+        positionStackAtBottom(stack, null /* updateLastFocusedStackReason */);
     }
 
-    void positionChildAtBottom(ActivityStack stack, String updateLastFocusedStackReason) {
-        positionChildAt(stack, 0, false /* includingParents */, updateLastFocusedStackReason);
+    void positionStackAtBottom(ActivityStack stack, String updateLastFocusedStackReason) {
+        positionStackAt(stack, 0, false /* includingParents */, updateLastFocusedStackReason);
     }
 
-    private void positionChildAt(ActivityStack stack, int position) {
-        positionChildAt(stack, position, false /* includingParents */,
+    private void positionStackAt(ActivityStack stack, int position) {
+        positionStackAt(stack, position, false /* includingParents */,
                 null /* updateLastFocusedStackReason */);
     }
 
-    private void positionChildAt(ActivityStack stack, int position, boolean includingParents,
+    private void positionStackAt(ActivityStack stack, int position, boolean includingParents,
             String updateLastFocusedStackReason) {
         // TODO: Keep in sync with WindowContainer.positionChildAt(), once we change that to adjust
         //       the position internally, also update the logic here
         final ActivityStack prevFocusedStack = updateLastFocusedStackReason != null
                 ? getFocusedStack() : null;
-        final boolean wasContained = mStacks.remove(stack);
-        if (mSingleTaskInstance && getChildCount() > 0) {
+        final boolean wasContained = getIndexOf(stack) >= 0;
+        if (mSingleTaskInstance && getStackCount() == 1 && !wasContained) {
             throw new IllegalStateException(
-                    "positionChildAt: Can only have one child on display=" + this);
+                    "positionStackAt: Can only have one task on display=" + this);
         }
-        final int insertPosition = getTopInsertPosition(stack, position);
-        mStacks.add(insertPosition, stack);
+
+        // Since positionChildAt() is called during the creation process of pinned stacks,
+        // ActivityStack#getStack() can be null.
+        positionStackAt(position, stack, includingParents);
 
         // The insert position may be adjusted to non-top when there is always-on-top stack. Since
         // the original position is preferred to be top, the stack should have higher priority when
         // we are looking for top focusable stack. The condition {@code wasContained} restricts the
         // preferred stack is set only when moving an existing stack to top instead of adding a new
         // stack that may be too early (e.g. in the middle of launching or reparenting).
-        if (wasContained && position >= getChildCount() - 1 && stack.isFocusableAndVisible()) {
+        if (wasContained && position >= getStackCount() - 1 && stack.isFocusableAndVisible()) {
             mPreferredTopFocusableStack = stack;
         } else if (mPreferredTopFocusableStack == stack) {
             mPreferredTopFocusableStack = null;
@@ -307,67 +277,14 @@
             }
         }
 
-        // Since positionChildAt() is called during the creation process of pinned stacks,
-        // ActivityStack#getStack() can be null.
-        if (mDisplayContent != null) {
-            mDisplayContent.positionStackAt(insertPosition, stack, includingParents);
-        }
         onStackOrderChanged(stack);
     }
 
-    private int getTopInsertPosition(ActivityStack stack, int candidatePosition) {
-        int position = getChildCount();
-        if (stack.inPinnedWindowingMode()) {
-            // Stack in pinned windowing mode is z-ordered on-top of all other stacks so okay to
-            // just return the candidate position.
-            return Math.min(position, candidatePosition);
-        }
-        while (position > 0) {
-            final ActivityStack targetStack = getChildAt(position - 1);
-            if (!targetStack.isAlwaysOnTop()) {
-                // We reached a stack that isn't always-on-top.
-                break;
-            }
-            if (stack.isAlwaysOnTop() && !targetStack.inPinnedWindowingMode()) {
-                // Always on-top non-pinned windowing mode stacks can go anywhere below pinned stack.
-                break;
-            }
-            position--;
-        }
-        return Math.min(position, candidatePosition);
-    }
-
-    <T extends ActivityStack> T getStack(int stackId) {
-        for (int i = getChildCount() - 1; i >= 0; --i) {
-            final ActivityStack stack = getChildAt(i);
+    ActivityStack getStack(int stackId) {
+        for (int i = getStackCount() - 1; i >= 0; --i) {
+            final ActivityStack stack = getStackAt(i);
             if (stack.mStackId == stackId) {
-                return (T) stack;
-            }
-        }
-        return null;
-    }
-
-    /**
-     * @return the topmost stack on the display that is compatible with the input windowing mode and
-     * activity type. {@code null} means no compatible stack on the display.
-     * @see ConfigurationContainer#isCompatible(int, int)
-     */
-    <T extends ActivityStack> T getStack(int windowingMode, int activityType) {
-        if (activityType == ACTIVITY_TYPE_HOME) {
-            return (T) mHomeStack;
-        } else if (activityType == ACTIVITY_TYPE_RECENTS) {
-            return (T) mRecentsStack;
-        }
-        if (windowingMode == WINDOWING_MODE_PINNED) {
-            return (T) mPinnedStack;
-        } else if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
-            return (T) mSplitScreenPrimaryStack;
-        }
-
-        for (int i = getChildCount() - 1; i >= 0; --i) {
-            final ActivityStack stack = getChildAt(i);
-            if (stack.isCompatible(windowingMode, activityType)) {
-                return (T) stack;
+                return stack;
             }
         }
         return null;
@@ -388,10 +305,10 @@
      * @see #getStack(int, int)
      * @see #createStack(int, int, boolean)
      */
-    <T extends ActivityStack> T getOrCreateStack(int windowingMode, int activityType,
+    ActivityStack getOrCreateStack(int windowingMode, int activityType,
             boolean onTop) {
         if (!alwaysCreateStack(windowingMode, activityType)) {
-            T stack = getStack(windowingMode, activityType);
+            ActivityStack stack = getStack(windowingMode, activityType);
             if (stack != null) {
                 return stack;
             }
@@ -404,7 +321,7 @@
      * if a compatible stack doesn't exist.
      * @see #getOrCreateStack(int, int, boolean)
      */
-    <T extends ActivityStack> T getOrCreateStack(@Nullable ActivityRecord r,
+    ActivityStack getOrCreateStack(@Nullable ActivityRecord r,
             @Nullable ActivityOptions options, @Nullable Task candidateTask, int activityType,
             boolean onTop) {
         // First preference is the windowing mode in the activity options if set.
@@ -433,9 +350,9 @@
      * @param onTop If true the stack will be created at the top of the display, else at the bottom.
      * @return The newly created stack.
      */
-    <T extends ActivityStack> T createStack(int windowingMode, int activityType, boolean onTop) {
+    ActivityStack createStack(int windowingMode, int activityType, boolean onTop) {
 
-        if (mSingleTaskInstance && getChildCount() > 0) {
+        if (mSingleTaskInstance && getStackCount() > 0) {
             // Create stack on default display instead since this display can only contain 1 stack.
             // TODO: Kinda a hack, but better that having the decision at each call point. Hoping
             // this goes away once ActivityView is no longer using virtual displays.
@@ -452,17 +369,17 @@
         if (activityType != ACTIVITY_TYPE_STANDARD) {
             // For now there can be only one stack of a particular non-standard activity type on a
             // display. So, get that ignoring whatever windowing mode it is currently in.
-            T stack = getStack(WINDOWING_MODE_UNDEFINED, activityType);
+            ActivityStack stack = getStack(WINDOWING_MODE_UNDEFINED, activityType);
             if (stack != null) {
                 throw new IllegalArgumentException("Stack=" + stack + " of activityType="
                         + activityType + " already on display=" + this + ". Can't have multiple.");
             }
         }
 
-        if (!isWindowingModeSupported(windowingMode, mService.mSupportsMultiWindow,
-                mService.mSupportsSplitScreenMultiWindow,
-                mService.mSupportsFreeformWindowManagement, mService.mSupportsPictureInPicture,
-                activityType)) {
+        if (!isWindowingModeSupported(windowingMode, mAtmService.mSupportsMultiWindow,
+                mAtmService.mSupportsSplitScreenMultiWindow,
+                mAtmService.mSupportsFreeformWindowManagement,
+                mAtmService.mSupportsPictureInPicture, activityType)) {
             throw new IllegalArgumentException("Can't create stack for unsupported windowingMode="
                     + windowingMode);
         }
@@ -472,13 +389,13 @@
     }
 
     @VisibleForTesting
-    <T extends ActivityStack> T createStackUnchecked(int windowingMode, int activityType,
+    ActivityStack createStackUnchecked(int windowingMode, int activityType,
             int stackId, boolean onTop) {
         if (windowingMode == WINDOWING_MODE_PINNED && activityType != ACTIVITY_TYPE_STANDARD) {
             throw new IllegalArgumentException("Stack with windowing mode cannot with non standard "
                     + "activity type.");
         }
-        return (T) new ActivityStack(this, stackId,
+        return new ActivityStack(this, stackId,
                 mRootActivityContainer.mStackSupervisor, windowingMode, activityType, onTop);
     }
 
@@ -491,8 +408,8 @@
             return mPreferredTopFocusableStack;
         }
 
-        for (int i = getChildCount() - 1; i >= 0; --i) {
-            final ActivityStack stack = getChildAt(i);
+        for (int i = getStackCount() - 1; i >= 0; --i) {
+            final ActivityStack stack = getStackAt(i);
             if (stack.isFocusableAndVisible()) {
                 return stack;
             }
@@ -506,8 +423,8 @@
                 ? currentFocus.getWindowingMode() : WINDOWING_MODE_UNDEFINED;
 
         ActivityStack candidate = null;
-        for (int i = getChildCount() - 1; i >= 0; --i) {
-            final ActivityStack stack = getChildAt(i);
+        for (int i = getStackCount() - 1; i >= 0; --i) {
+            final ActivityStack stack = getStackAt(i);
             if (ignoreCurrent && stack == currentFocus) {
                 continue;
             }
@@ -562,8 +479,8 @@
     }
 
     boolean allResumedActivitiesComplete() {
-        for (int stackNdx = getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-            final ActivityRecord r = getChildAt(stackNdx).getResumedActivity();
+        for (int stackNdx = getStackCount() - 1; stackNdx >= 0; --stackNdx) {
+            final ActivityRecord r = getStackAt(stackNdx).getResumedActivity();
             if (r != null && !r.isState(RESUMED)) {
                 return false;
             }
@@ -589,8 +506,8 @@
      */
     boolean pauseBackStacks(boolean userLeaving, ActivityRecord resuming) {
         boolean someActivityPaused = false;
-        for (int stackNdx = getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-            final ActivityStack stack = getChildAt(stackNdx);
+        for (int stackNdx = getStackCount() - 1; stackNdx >= 0; --stackNdx) {
+            final ActivityStack stack = getStackAt(stackNdx);
             final ActivityRecord resumedActivity = stack.getResumedActivity();
             if (resumedActivity != null
                     && (stack.getVisibility(resuming) != STACK_VISIBILITY_VISIBLE
@@ -610,8 +527,8 @@
     void findTaskLocked(final ActivityRecord r, final boolean isPreferredDisplay,
             FindTaskResult result) {
         mTmpFindTaskResult.clear();
-        for (int stackNdx = getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-            final ActivityStack stack = getChildAt(stackNdx);
+        for (int stackNdx = getStackCount() - 1; stackNdx >= 0; --stackNdx) {
+            final ActivityStack stack = getStackAt(stackNdx);
             if (!r.hasCompatibleActivityType(stack)) {
                 if (DEBUG_TASKS) {
                     Slog.d(TAG_TASKS, "Skipping stack: (mismatch activity/stack) " + stack);
@@ -654,8 +571,8 @@
         final ArrayList<ActivityStack> stacks = new ArrayList<>();
         for (int j = windowingModes.length - 1 ; j >= 0; --j) {
             final int windowingMode = windowingModes[j];
-            for (int i = getChildCount() - 1; i >= 0; --i) {
-                final ActivityStack stack = getChildAt(i);
+            for (int i = getStackCount() - 1; i >= 0; --i) {
+                final ActivityStack stack = getStackAt(i);
                 if (!stack.isActivityTypeStandardOrUndefined()) {
                     continue;
                 }
@@ -682,8 +599,8 @@
         final ArrayList<ActivityStack> stacks = new ArrayList<>();
         for (int j = activityTypes.length - 1 ; j >= 0; --j) {
             final int activityType = activityTypes[j];
-            for (int i = getChildCount() - 1; i >= 0; --i) {
-                final ActivityStack stack = getChildAt(i);
+            for (int i = getStackCount() - 1; i >= 0; --i) {
+                final ActivityStack stack = getStackAt(i);
                 if (stack.getActivityType() == activityType) {
                     stacks.add(stack);
                 }
@@ -695,67 +612,12 @@
         }
     }
 
-    void onStackWindowingModeChanged(ActivityStack stack) {
-        removeStackReferenceIfNeeded(stack);
-        addStackReferenceIfNeeded(stack);
-    }
-
-    private void addStackReferenceIfNeeded(ActivityStack stack) {
-        final int activityType = stack.getActivityType();
-        final int windowingMode = stack.getWindowingMode();
-
-        if (activityType == ACTIVITY_TYPE_HOME) {
-            if (mHomeStack != null && mHomeStack != stack) {
-                throw new IllegalArgumentException("addStackReferenceIfNeeded: home stack="
-                        + mHomeStack + " already exist on display=" + this + " stack=" + stack);
-            }
-            mHomeStack = stack;
-        } else if (activityType == ACTIVITY_TYPE_RECENTS) {
-            if (mRecentsStack != null && mRecentsStack != stack) {
-                throw new IllegalArgumentException("addStackReferenceIfNeeded: recents stack="
-                        + mRecentsStack + " already exist on display=" + this + " stack=" + stack);
-            }
-            mRecentsStack = stack;
-        }
-        if (windowingMode == WINDOWING_MODE_PINNED) {
-            if (mPinnedStack != null && mPinnedStack != stack) {
-                throw new IllegalArgumentException("addStackReferenceIfNeeded: pinned stack="
-                        + mPinnedStack + " already exist on display=" + this
-                        + " stack=" + stack);
-            }
-            mPinnedStack = stack;
-        } else if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
-            if (mSplitScreenPrimaryStack != null && mSplitScreenPrimaryStack != stack) {
-                throw new IllegalArgumentException("addStackReferenceIfNeeded:"
-                        + " split-screen-primary" + " stack=" + mSplitScreenPrimaryStack
-                        + " already exist on display=" + this + " stack=" + stack);
-            }
-            mSplitScreenPrimaryStack = stack;
-            onSplitScreenModeActivated();
-        }
-    }
-
-    private void removeStackReferenceIfNeeded(ActivityStack stack) {
-        if (stack == mHomeStack) {
-            mHomeStack = null;
-        } else if (stack == mRecentsStack) {
-            mRecentsStack = null;
-        } else if (stack == mPinnedStack) {
-            mPinnedStack = null;
-        } else if (stack == mSplitScreenPrimaryStack) {
-            mSplitScreenPrimaryStack = null;
-            // Inform the reset of the system that split-screen mode was dismissed so things like
-            // resizing all the other stacks can take place.
-            onSplitScreenModeDismissed();
-        }
-    }
-
-    private void onSplitScreenModeDismissed() {
-        mService.deferWindowLayout();
+    void onSplitScreenModeDismissed() {
+        mAtmService.deferWindowLayout();
         try {
             // Adjust the windowing mode of any stack in secondary split-screen to fullscreen.
-            for (int i = getChildCount() - 1; i >= 0; --i) {
-                final ActivityStack otherStack = getChildAt(i);
+            for (int i = getStackCount() - 1; i >= 0; --i) {
+                final ActivityStack otherStack = getStackAt(i);
                 if (!otherStack.inSplitScreenSecondaryWindowingMode()) {
                     continue;
                 }
@@ -766,26 +628,28 @@
         } finally {
             final ActivityStack topFullscreenStack =
                     getTopStackInWindowingMode(WINDOWING_MODE_FULLSCREEN);
-            if (topFullscreenStack != null && mHomeStack != null && !isTopStack(mHomeStack)) {
+            final ActivityStack homeStack = getHomeStack();
+            if (topFullscreenStack != null && homeStack != null && !isTopStack(homeStack)) {
                 // Whenever split-screen is dismissed we want the home stack directly behind the
                 // current top fullscreen stack so it shows up when the top stack is finished.
                 // TODO: Would be better to use ActivityDisplay.positionChildAt() for this, however
                 // ActivityDisplay doesn't have a direct controller to WM side yet. We can switch
                 // once we have that.
-                mHomeStack.moveToFront("onSplitScreenModeDismissed");
+                homeStack.moveToFront("onSplitScreenModeDismissed");
                 topFullscreenStack.moveToFront("onSplitScreenModeDismissed");
             }
-            mService.continueWindowLayout();
+            mAtmService.continueWindowLayout();
         }
     }
 
-    private void onSplitScreenModeActivated() {
-        mService.deferWindowLayout();
+    void onSplitScreenModeActivated() {
+        mAtmService.deferWindowLayout();
         try {
             // Adjust the windowing mode of any affected by split-screen to split-screen secondary.
-            for (int i = getChildCount() - 1; i >= 0; --i) {
-                final ActivityStack otherStack = getChildAt(i);
-                if (otherStack == mSplitScreenPrimaryStack
+            final ActivityStack splitScreenPrimaryStack = getSplitScreenPrimaryStack();
+            for (int i = getStackCount() - 1; i >= 0; --i) {
+                final ActivityStack otherStack = getStackAt(i);
+                if (otherStack == splitScreenPrimaryStack
                         || !otherStack.affectedBySplitScreenResize()) {
                     continue;
                 }
@@ -795,7 +659,7 @@
                         false /* creating */);
             }
         } finally {
-            mService.continueWindowLayout();
+            mAtmService.continueWindowLayout();
         }
     }
 
@@ -890,10 +754,10 @@
     int validateWindowingMode(int windowingMode, @Nullable ActivityRecord r, @Nullable Task task,
             int activityType) {
         // Make sure the windowing mode we are trying to use makes sense for what is supported.
-        boolean supportsMultiWindow = mService.mSupportsMultiWindow;
-        boolean supportsSplitScreen = mService.mSupportsSplitScreenMultiWindow;
-        boolean supportsFreeform = mService.mSupportsFreeformWindowManagement;
-        boolean supportsPip = mService.mSupportsPictureInPicture;
+        boolean supportsMultiWindow = mAtmService.mSupportsMultiWindow;
+        boolean supportsSplitScreen = mAtmService.mSupportsSplitScreenMultiWindow;
+        boolean supportsFreeform = mAtmService.mSupportsFreeformWindowManagement;
+        boolean supportsPip = mAtmService.mSupportsPictureInPicture;
         if (supportsMultiWindow) {
             if (task != null) {
                 supportsMultiWindow = task.isResizeable();
@@ -927,21 +791,13 @@
         return WINDOWING_MODE_UNDEFINED;
     }
 
-    /**
-     * Get the topmost stack on the display. It may be different from focused stack, because
-     * some stacks are not focusable (e.g. PiP).
-     */
-    ActivityStack getTopStack() {
-        return mStacks.isEmpty() ? null : getChildAt(getChildCount() - 1);
-    }
-
     boolean isTopStack(ActivityStack stack) {
         return stack == getTopStack();
     }
 
     boolean isTopNotPinnedStack(ActivityStack stack) {
-        for (int i = getChildCount() - 1; i >= 0; --i) {
-            final ActivityStack current = getChildAt(i);
+        for (int i = getStackCount() - 1; i >= 0; --i) {
+            final ActivityStack current = getStackAt(i);
             if (!current.inPinnedWindowingMode()) {
                 return current == stack;
             }
@@ -950,8 +806,8 @@
     }
 
     ActivityStack getTopStackInWindowingMode(int windowingMode) {
-        for (int i = getChildCount() - 1; i >= 0; --i) {
-            final ActivityStack current = getChildAt(i);
+        for (int i = getStackCount() - 1; i >= 0; --i) {
+            final ActivityStack current = getStackAt(i);
             if (windowingMode == current.getWindowingMode()) {
                 return current;
             }
@@ -981,8 +837,8 @@
 
         // Look in other focusable stacks.
         if (topRunning == null) {
-            for (int i = getChildCount() - 1; i >= 0; --i) {
-                final ActivityStack stack = getChildAt(i);
+            for (int i = getStackCount() - 1; i >= 0; --i) {
+                final ActivityStack stack = getStackAt(i);
                 // Only consider focusable stacks other than the current focused one.
                 if (stack == focusedStack || !stack.isFocusable()) {
                     continue;
@@ -1005,22 +861,18 @@
         return topRunning;
     }
 
-    int getIndexOf(ActivityStack stack) {
-        return mStacks.indexOf(stack);
-    }
-
     boolean updateDisplayOverrideConfigurationLocked() {
         Configuration values = new Configuration();
-        mDisplayContent.computeScreenConfiguration(values);
+        computeScreenConfiguration(values);
 
-        mService.mH.sendMessage(PooledLambda.obtainMessage(
-                ActivityManagerInternal::updateOomLevelsForDisplay, mService.mAmInternal,
+        mAtmService.mH.sendMessage(PooledLambda.obtainMessage(
+                ActivityManagerInternal::updateOomLevelsForDisplay, mAtmService.mAmInternal,
                 mDisplayId));
 
         Settings.System.clearConfiguration(values);
         updateDisplayOverrideConfigurationLocked(values, null /* starting */,
-                false /* deferResume */, mService.mTmpUpdateConfigurationResult);
-        return mService.mTmpUpdateConfigurationResult.changes != 0;
+                false /* deferResume */, mAtmService.mTmpUpdateConfigurationResult);
+        return mAtmService.mTmpUpdateConfigurationResult.changes != 0;
     }
 
     /**
@@ -1034,14 +886,14 @@
         int changes = 0;
         boolean kept = true;
 
-        mService.deferWindowLayout();
+        mAtmService.deferWindowLayout();
         try {
             if (values != null) {
                 if (mDisplayId == DEFAULT_DISPLAY) {
                     // Override configuration of the default display duplicates global config, so
                     // we're calling global config update instead for default display. It will also
                     // apply the correct override config.
-                    changes = mService.updateGlobalConfigurationLocked(values,
+                    changes = mAtmService.updateGlobalConfigurationLocked(values,
                             false /* initLocale */, false /* persistent */,
                             UserHandle.USER_NULL /* userId */, deferResume);
                 } else {
@@ -1049,9 +901,9 @@
                 }
             }
 
-            kept = mService.ensureConfigAndVisibilityAfterUpdate(starting, changes);
+            kept = mAtmService.ensureConfigAndVisibilityAfterUpdate(starting, changes);
         } finally {
-            mService.continueWindowLayout();
+            mAtmService.continueWindowLayout();
         }
 
         if (result != null) {
@@ -1071,17 +923,17 @@
 
             final boolean isDensityChange = (changes & ActivityInfo.CONFIG_DENSITY) != 0;
             if (isDensityChange && mDisplayId == DEFAULT_DISPLAY) {
-                mService.mAppWarnings.onDensityChanged();
+                mAtmService.mAppWarnings.onDensityChanged();
 
                 // Post message to start process to avoid possible deadlock of calling into AMS with
                 // the ATMS lock held.
                 final Message msg = PooledLambda.obtainMessage(
                         ActivityManagerInternal::killAllBackgroundProcessesExcept,
-                        mService.mAmInternal, N,
+                        mAtmService.mAmInternal, N,
                         ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND);
-                mService.mH.sendMessage(msg);
+                mAtmService.mH.sendMessage(msg);
             }
-            mService.mWindowManager.mDisplayNotificationController.dispatchDisplayChanged(
+            mWmService.mDisplayNotificationController.dispatchDisplayChanged(
                     this, getConfiguration());
         }
         return changes;
@@ -1092,43 +944,29 @@
         final int currRotation =
                 getRequestedOverrideConfiguration().windowConfiguration.getRotation();
         if (currRotation != ROTATION_UNDEFINED
-                && currRotation != overrideConfiguration.windowConfiguration.getRotation()
-                && mDisplayContent != null) {
-            mDisplayContent.applyRotationLocked(currRotation,
+                && currRotation != overrideConfiguration.windowConfiguration.getRotation()) {
+            applyRotationLocked(currRotation,
                     overrideConfiguration.windowConfiguration.getRotation());
         }
         super.onRequestedOverrideConfigurationChanged(overrideConfiguration);
-        if (mDisplayContent != null) {
-            mService.mWindowManager.setNewDisplayOverrideConfiguration(
-                    overrideConfiguration, mDisplayContent);
-        }
-        mService.addWindowLayoutReasons(
+        mWmService.setNewDisplayOverrideConfiguration(overrideConfiguration, this);
+        mAtmService.addWindowLayoutReasons(
                 ActivityTaskManagerService.LAYOUT_REASON_CONFIG_CHANGED);
     }
 
     @Override
     public void onConfigurationChanged(Configuration newParentConfig) {
         // update resources before cascade so that docked/pinned stacks use the correct info
-        if (mDisplayContent != null) {
-            mDisplayContent.preOnConfigurationChanged();
-        }
+        preOnConfigurationChanged();
         super.onConfigurationChanged(newParentConfig);
     }
 
     void onLockTaskPackagesUpdated() {
-        for (int i = getChildCount() - 1; i >= 0; --i) {
-            getChildAt(i).onLockTaskPackagesUpdated();
+        for (int i = getStackCount() - 1; i >= 0; --i) {
+            getStackAt(i).onLockTaskPackagesUpdated();
         }
     }
 
-    /** We are in the process of exiting split-screen mode. */
-    void onExitingSplitScreenMode() {
-        // Remove reference to the primary-split-screen stack so it no longer has any effect on the
-        // display. For example, we want to be able to create fullscreen stack for standard activity
-        // types when exiting split-screen mode.
-        mSplitScreenPrimaryStack = null;
-    }
-
     /** Checks whether the given activity is in size compatibility mode and notifies the change. */
     void handleActivitySizeCompatModeIfNeeded(ActivityRecord r) {
         if (!r.isState(RESUMED) || r.getWindowingMode() != WINDOWING_MODE_FULLSCREEN) {
@@ -1137,7 +975,7 @@
         }
         if (!r.inSizeCompatMode()) {
             if (mLastCompatModeActivity != null) {
-                mService.getTaskChangeNotificationController()
+                mAtmService.getTaskChangeNotificationController()
                         .notifySizeCompatModeActivityChanged(mDisplayId, null /* activityToken */);
             }
             mLastCompatModeActivity = null;
@@ -1147,44 +985,13 @@
             return;
         }
         mLastCompatModeActivity = r;
-        mService.getTaskChangeNotificationController()
+        mAtmService.getTaskChangeNotificationController()
                 .notifySizeCompatModeActivityChanged(mDisplayId, r.appToken);
     }
 
-    ActivityStack getSplitScreenPrimaryStack() {
-        return mSplitScreenPrimaryStack;
-    }
-
-    boolean hasSplitScreenPrimaryStack() {
-        return mSplitScreenPrimaryStack != null;
-    }
-
-    ActivityStack getPinnedStack() {
-        return mPinnedStack;
-    }
-
-    boolean hasPinnedStack() {
-        return mPinnedStack != null;
-    }
-
     @Override
     public String toString() {
-        return "ActivityDisplay={" + mDisplayId + " numStacks=" + getChildCount() + "}";
-    }
-
-    @Override
-    protected int getChildCount() {
-        return mStacks.size();
-    }
-
-    @Override
-    protected ActivityStack getChildAt(int index) {
-        return mStacks.get(index);
-    }
-
-    @Override
-    protected ConfigurationContainer getParent() {
-        return mRootActivityContainer;
+        return "ActivityDisplay={" + mDisplayId + " numStacks=" + getStackCount() + "}";
     }
 
     boolean isPrivate() {
@@ -1227,10 +1034,10 @@
         final ActivityDisplay toDisplay = mRootActivityContainer.getDefaultDisplay();
         mRootActivityContainer.mStackSupervisor.beginDeferResume();
         try {
-            int numStacks = getChildCount();
+            int numStacks = getStackCount();
             // Keep the order from bottom to top.
             for (int stackNdx = 0; stackNdx < numStacks; stackNdx++) {
-                final ActivityStack stack = getChildAt(stackNdx);
+                final ActivityStack stack = getStackAt(stackNdx);
                 // Always finish non-standard type stacks.
                 if (destroyContentOnRemoval || !stack.isActivityTypeStandardOrUndefined()) {
                     stack.finishAllActivitiesImmediately();
@@ -1241,14 +1048,14 @@
                     final int windowingMode = toDisplay.hasSplitScreenPrimaryStack()
                             ? WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
                             : WINDOWING_MODE_UNDEFINED;
-                    stack.reparent(toDisplay.mDisplayContent, true /* onTop */);
+                    stack.reparent(toDisplay, true /* onTop */);
                     stack.setWindowingMode(windowingMode);
                     lastReparentedStack = stack;
                 }
                 // Stacks may be removed from this display. Ensure each stack will be processed and
                 // the loop will end.
-                stackNdx -= numStacks - getChildCount();
-                numStacks = getChildCount();
+                stackNdx -= numStacks - getStackCount();
+                numStacks = getStackCount();
             }
         } finally {
             mRootActivityContainer.mStackSupervisor.endDeferResume();
@@ -1265,24 +1072,23 @@
         if (!mAllSleepTokens.isEmpty()) {
             mRootActivityContainer.mSleepTokens.removeAll(mAllSleepTokens);
             mAllSleepTokens.clear();
-            mService.updateSleepIfNeededLocked();
+            mAtmService.updateSleepIfNeededLocked();
         }
     }
 
     private void releaseSelfIfNeeded() {
-        if (!mRemoved || mDisplayContent == null) {
+        if (!mRemoved) {
             return;
         }
 
-        final ActivityStack stack = getChildCount() == 1 ? getChildAt(0) : null;
+        final ActivityStack stack = getStackCount() == 1 ? getStackAt(0) : null;
         if (stack != null && stack.isActivityTypeHome() && stack.getAllTasks().isEmpty()) {
             // Release this display if an empty home stack is the only thing left.
             // Since it is the last stack, this display will be released along with the stack
             // removal.
             stack.removeIfPossible();
-        } else if (mStacks.isEmpty()) {
-            mDisplayContent.removeIfPossible();
-            mDisplayContent = null;
+        } else if (getTopStack() == null) {
+            removeIfPossible();
             mRootActivityContainer.removeChild(this);
             mRootActivityContainer.mStackSupervisor
                     .getKeyguardController().onDisplayRemoved(mDisplayId);
@@ -1302,14 +1108,6 @@
     private static void addActivityUid(ActivityRecord r, IntArray uids) {
         uids.add(r.getUid());
     }
-    /**
-     * Checks if system decorations should be shown on this display.
-     *
-     * @see Display#FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS
-     */
-    boolean supportsSystemDecorations() {
-        return mDisplayContent.supportsSystemDecorations();
-    }
 
     @VisibleForTesting
     boolean shouldDestroyContentOnRemove() {
@@ -1317,14 +1115,11 @@
     }
 
     boolean shouldSleep() {
-        return (mStacks.isEmpty() || !mAllSleepTokens.isEmpty())
-                && (mService.mRunningVoice == null);
+        return (getStackCount() == 0 || !mAllSleepTokens.isEmpty())
+                && (mAtmService.mRunningVoice == null);
     }
 
     void setFocusedApp(ActivityRecord r, boolean moveFocusNow) {
-        if (mDisplayContent == null) {
-            return;
-        }
         final ActivityRecord newFocus;
         final IBinder token = r.appToken;
         if (token == null) {
@@ -1332,7 +1127,7 @@
                     mDisplayId);
             newFocus = null;
         } else {
-            newFocus = mService.mWindowManager.mRoot.getActivityRecord(token);
+            newFocus = mWmService.mRoot.getActivityRecord(token);
             if (newFocus == null) {
                 Slog.w(TAG_WM, "Attempted to set focus to non-existing app token: " + token
                         + ", displayId=" + mDisplayId);
@@ -1342,9 +1137,9 @@
                             moveFocusNow, mDisplayId);
         }
 
-        final boolean changed = mDisplayContent.setFocusedApp(newFocus);
+        final boolean changed = setFocusedApp(newFocus);
         if (moveFocusNow && changed) {
-            mService.mWindowManager.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL,
+            mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL,
                     true /*updateInputWindows*/);
         }
     }
@@ -1354,8 +1149,8 @@
      *         already top-most.
      */
     ActivityStack getStackAbove(ActivityStack stack) {
-        final int stackIndex = mStacks.indexOf(stack) + 1;
-        return (stackIndex < getChildCount()) ? getChildAt(stackIndex) : null;
+        final int stackIndex = getIndexOf(stack) + 1;
+        return (stackIndex < getStackCount()) ? getStackAt(stackIndex) : null;
     }
 
     /**
@@ -1369,12 +1164,12 @@
         }
 
         // Move the stack to the bottom to not affect the following visibility checks
-        positionChildAtBottom(stack);
+        positionStackAtBottom(stack);
 
         // Find the next position where the stack should be placed
-        final int numStacks = getChildCount();
+        final int numStacks = getStackCount();
         for (int stackNdx = 0; stackNdx < numStacks; stackNdx++) {
-            final ActivityStack s = getChildAt(stackNdx);
+            final ActivityStack s = getStackAt(stackNdx);
             if (s == stack) {
                 continue;
             }
@@ -1383,7 +1178,7 @@
                     winMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
             if (s.shouldBeVisible(null) && isValidWindowingMode) {
                 // Move the provided stack to behind this stack
-                positionChildAt(stack, Math.max(0, stackNdx - 1));
+                positionStackAt(stack, Math.max(0, stackNdx - 1));
                 break;
             }
         }
@@ -1403,25 +1198,26 @@
         // list, so we need to adjust the insertion index to account for the removed index
         // TODO: Remove this logic when WindowContainer.positionChildAt() is updated to adjust the
         //       position internally
-        final int stackIndex = mStacks.indexOf(stack);
-        final int behindStackIndex = mStacks.indexOf(behindStack);
+        final int stackIndex = getIndexOf(stack);
+        final int behindStackIndex = getIndexOf(behindStack);
         final int insertIndex = stackIndex <= behindStackIndex
                 ? behindStackIndex - 1 : behindStackIndex;
-        positionChildAt(stack, Math.max(0, insertIndex));
+        positionStackAt(stack, Math.max(0, insertIndex));
     }
 
     void ensureActivitiesVisible(ActivityRecord starting, int configChanges,
             boolean preserveWindows, boolean notifyClients) {
-        for (int stackNdx = getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-            final ActivityStack stack = getChildAt(stackNdx);
+        for (int stackNdx = getStackCount() - 1; stackNdx >= 0; --stackNdx) {
+            final ActivityStack stack = getStackAt(stackNdx);
             stack.ensureActivitiesVisible(starting, configChanges, preserveWindows,
                     notifyClients);
         }
     }
 
     void moveHomeStackToFront(String reason) {
-        if (mHomeStack != null) {
-            mHomeStack.moveToFront(reason);
+        final ActivityStack homeStack = getHomeStack();
+        if (homeStack != null) {
+            homeStack.moveToFront(reason);
         }
     }
 
@@ -1439,25 +1235,21 @@
     }
 
     @Nullable
-    ActivityStack getHomeStack() {
-        return mHomeStack;
-    }
-
-    @Nullable
     ActivityRecord getHomeActivity() {
         return getHomeActivityForUser(mRootActivityContainer.mCurrentUser);
     }
 
     @Nullable
     ActivityRecord getHomeActivityForUser(int userId) {
-        if (mHomeStack == null) {
+        final ActivityStack homeStack = getHomeStack();
+        if (homeStack == null) {
             return null;
         }
 
         final PooledPredicate p = PooledLambda.obtainPredicate(
                 ActivityDisplay::isHomeActivityForUser, PooledLambda.__(ActivityRecord.class),
                 userId);
-        final ActivityRecord r = mHomeStack.getActivity(p);
+        final ActivityRecord r = homeStack.getActivity(p);
         p.recycle();
         return r;
     }
@@ -1502,32 +1294,14 @@
         }
     }
 
-    /**
-     * See {@link DisplayContent#deferUpdateImeTarget()}
-     */
-    public void deferUpdateImeTarget() {
-        if (mDisplayContent != null) {
-            mDisplayContent.deferUpdateImeTarget();
-        }
-    }
-
-    /**
-     * See {@link DisplayContent#deferUpdateImeTarget()}
-     */
-    public void continueUpdateImeTarget() {
-        if (mDisplayContent != null) {
-            mDisplayContent.continueUpdateImeTarget();
-        }
-    }
-
     void setDisplayToSingleTaskInstance() {
-        final int childCount = getChildCount();
+        final int childCount = getStackCount();
         if (childCount > 1) {
             throw new IllegalArgumentException("Display already has multiple stacks. display="
                     + this);
         }
         if (childCount > 0) {
-            final ActivityStack stack = getChildAt(0);
+            final ActivityStack stack = getStackAt(0);
             if (stack.getChildCount() > 1) {
                 throw new IllegalArgumentException("Display stack already has multiple tasks."
                         + " display=" + this + " stack=" + stack);
@@ -1544,8 +1318,8 @@
 
     @VisibleForTesting
     void removeAllTasks() {
-        for (int i = getChildCount() - 1; i >= 0; --i) {
-            final ActivityStack stack = getChildAt(i);
+        for (int i = getStackCount() - 1; i >= 0; --i) {
+            final ActivityStack stack = getStackAt(i);
             final ArrayList<Task> tasks = stack.getAllTasks();
             for (int j = tasks.size() - 1; j >= 0; --j) {
                 stack.removeChild(tasks.get(j), "removeAllTasks");
@@ -1554,20 +1328,24 @@
     }
 
     public void dump(PrintWriter pw, String prefix) {
-        pw.println(prefix + "displayId=" + mDisplayId + " stacks=" + getChildCount()
+        pw.println(prefix + "displayId=" + mDisplayId + " stacks=" + getStackCount()
                 + (mSingleTaskInstance ? " mSingleTaskInstance" : ""));
         final String myPrefix = prefix + " ";
-        if (mHomeStack != null) {
-            pw.println(myPrefix + "mHomeStack=" + mHomeStack);
+        ActivityStack stack = getHomeStack();
+        if (stack != null) {
+            pw.println(myPrefix + "mHomeStack=" + stack);
         }
-        if (mRecentsStack != null) {
-            pw.println(myPrefix + "mRecentsStack=" + mRecentsStack);
+        stack = getRecentsStack();
+        if (stack != null) {
+            pw.println(myPrefix + "mRecentsStack=" + stack);
         }
-        if (mPinnedStack != null) {
-            pw.println(myPrefix + "mPinnedStack=" + mPinnedStack);
+        stack = getPinnedStack();
+        if (stack != null) {
+            pw.println(myPrefix + "mPinnedStack=" + stack);
         }
-        if (mSplitScreenPrimaryStack != null) {
-            pw.println(myPrefix + "mSplitScreenPrimaryStack=" + mSplitScreenPrimaryStack);
+        stack = getSplitScreenPrimaryStack();
+        if (stack != null) {
+            pw.println(myPrefix + "mSplitScreenPrimaryStack=" + stack);
         }
         if (mPreferredTopFocusableStack != null) {
             pw.println(myPrefix + "mPreferredTopFocusableStack=" + mPreferredTopFocusableStack);
@@ -1578,8 +1356,8 @@
     }
 
     public void dumpStacks(PrintWriter pw) {
-        for (int i = getChildCount() - 1; i >= 0; --i) {
-            pw.print(getChildAt(i).mStackId);
+        for (int i = getStackCount() - 1; i >= 0; --i) {
+            pw.print(getStackAt(i).mStackId);
             if (i > 0) {
                 pw.print(",");
             }
@@ -1589,7 +1367,7 @@
     public void writeToProto(ProtoOutputStream proto, long fieldId,
             @WindowTraceLogLevel int logLevel) {
         final long token = proto.start(fieldId);
-        super.writeToProto(proto, CONFIGURATION_CONTAINER, logLevel);
+        writeToProtoInner(proto, DISPLAY, logLevel);
         proto.write(ID, mDisplayId);
         proto.write(SINGLE_TASK_INSTANCE, mSingleTaskInstance);
         final ActivityStack focusedStack = getFocusedStack();
@@ -1602,8 +1380,8 @@
         } else {
             proto.write(FOCUSED_STACK_ID, INVALID_STACK_ID);
         }
-        for (int stackNdx = getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-            final ActivityStack stack = getChildAt(stackNdx);
+        for (int stackNdx = getStackCount() - 1; stackNdx >= 0; --stackNdx) {
+            final ActivityStack stack = getStackAt(stackNdx);
             stack.writeToProto(proto, STACKS, logLevel);
         }
         proto.end(token);
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 963e090..5377db4 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -2237,7 +2237,7 @@
             }
         }
         return (getWindowConfiguration().canReceiveKeys() || isAlwaysFocusable())
-                && getParent() != null;
+                && getDisplay() != null;
     }
 
     /** Move activity with its stack to front and make the stack focused. */
@@ -2256,7 +2256,8 @@
             return false;
         }
 
-        if (mRootActivityContainer.getTopResumedActivity() == this) {
+        if (mRootActivityContainer.getTopResumedActivity() == this
+                && getDisplayContent().mFocusedApp == this) {
             if (DEBUG_FOCUS) {
                 Slog.d(TAG_FOCUS, "moveActivityStackToFront: already on top, activity=" + this);
             }
@@ -2420,7 +2421,7 @@
                     final ActivityDisplay display = stack.getDisplay();
                     next = display.topRunningActivity();
                     if (next != null) {
-                        display.positionChildAtTop(next.getActivityStack(),
+                        display.positionStackAtTop(next.getActivityStack(),
                                 false /* includingParents */, "finish-display-top");
                     }
                 }
@@ -5688,8 +5689,6 @@
     @Override
     boolean isWaitingForTransitionStart() {
         final DisplayContent dc = getDisplayContent();
-        // TODO(display-unify): Test for null can be removed once unification is done.
-        if (dc == null) return false;
         return dc.mAppTransition.isTransitionSet()
                 && (dc.mOpeningApps.contains(this)
                 || dc.mClosingApps.contains(this)
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index bb3126b..918d40b 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -750,7 +750,7 @@
         // stacks on a wrong display.
         mDisplayId = display.mDisplayId;
         setActivityType(activityType);
-        display.addChild(this, onTop ? POSITION_TOP : POSITION_BOTTOM);
+        display.addStack(this, onTop ? POSITION_TOP : POSITION_BOTTOM);
         setWindowingMode(windowingMode, false /* animate */, false /* showRecents */,
                 false /* enteringSplitScreenMode */, false /* deferEnsuringVisibility */,
                 true /* creating */);
@@ -879,7 +879,6 @@
                         newBounds /* outStackBounds */, mTmpRect2 /* outTempTaskBounds */);
                 hasNewOverrideBounds = true;
             }
-            display.onStackWindowingModeChanged(this);
         }
         if (hasNewOverrideBounds) {
             if (inSplitScreenPrimaryWindowingMode()) {
@@ -896,7 +895,7 @@
             // Since always on top is only on when the stack is freeform or pinned, the state
             // can be toggled when the windowing mode changes. We must make sure the stack is
             // placed properly when always on top state changes.
-            display.positionChildAtTop(this, false /* includingParents */);
+            display.positionStackAtTop(this, false /* includingParents */);
         }
     }
 
@@ -1337,7 +1336,7 @@
         }
 
         final boolean movingTask = task != null;
-        display.positionChildAtTop(this, !movingTask /* includingParents */, reason);
+        display.positionStackAtTop(this, !movingTask /* includingParents */, reason);
         if (movingTask) {
             // This also moves the entire hierarchy branch to top, including parents
             positionChildAtTop(task);
@@ -1353,7 +1352,7 @@
             return;
         }
 
-        getDisplay().positionChildAtBottom(this, reason);
+        getDisplay().positionStackAtBottom(this, reason);
         if (task != null) {
             positionChildAtBottom(task);
         }
@@ -1848,8 +1847,8 @@
         boolean shouldBeVisible = true;
         final int windowingMode = getWindowingMode();
         final boolean isAssistantType = isActivityTypeAssistant();
-        for (int i = display.getChildCount() - 1; i >= 0; --i) {
-            final ActivityStack other = display.getChildAt(i);
+        for (int i = display.getStackCount() - 1; i >= 0; --i) {
+            final ActivityStack other = display.getStackAt(i);
             final boolean hasRunningActivities = other.topRunningActivityLocked() != null;
             if (other == this) {
                 // Should be visible if there is no other stack occluding it, unless it doesn't
@@ -3010,7 +3009,7 @@
         if (index == 0) {
             return false;
         }
-        final ActivityStack stackBehind = display.getChildAt(index - 1);
+        final ActivityStack stackBehind = display.getStackAt(index - 1);
         return stackBehind.isActivityTypeStandard();
     }
 
@@ -3768,7 +3767,7 @@
 
         if (!hasChild()) {
             // Stack is now empty...
-          removeIfPossible();
+            removeIfPossible();
         }
 
         moveHomeStackToFrontIfNeeded(topFocused, display, reason);
@@ -3887,7 +3886,7 @@
         // always on top windows. Since the position the stack should be inserted into is calculated
         // properly in {@link ActivityDisplay#getTopInsertPosition()} in both cases, we can just
         // request that the stack is put at top here.
-        display.positionChildAtTop(this, false /* includingParents */);
+        display.positionStackAtTop(this, false /* includingParents */);
     }
 
     /** NOTE: Should only be called from {@link Task#reparent}. */
@@ -4019,7 +4018,7 @@
             final Task task = mChildren.get(0);
             setWindowingMode(WINDOWING_MODE_UNDEFINED);
 
-            getDisplay().positionChildAtTop(this, false /* includingParents */);
+            getDisplay().positionStackAtTop(this, false /* includingParents */);
 
             mStackSupervisor.scheduleUpdatePictureInPictureModeIfNeeded(task, this);
             MetricsLoggerWrapper.logPictureInPictureFullScreen(mService.mContext,
@@ -4456,20 +4455,14 @@
         }
     }
 
-    // TODO(display-unify): Remove after display unification.
-    protected void onParentChanged(ActivityDisplay newParent, ActivityDisplay oldParent) {
-        onParentChanged(
-                newParent != null ? newParent.mDisplayContent : null,
-                oldParent != null ? oldParent.mDisplayContent : null);
-    }
-
     @Override
     protected void onParentChanged(
             ConfigurationContainer newParent, ConfigurationContainer oldParent) {
+        // TODO(display-merge): Remove cast
         final ActivityDisplay display = newParent != null
-                ? ((WindowContainer) newParent).getDisplayContent().mActivityDisplay : null;
+                ? (ActivityDisplay) ((WindowContainer) newParent).getDisplayContent() : null;
         final ActivityDisplay oldDisplay = oldParent != null
-                ? ((WindowContainer) oldParent).getDisplayContent().mActivityDisplay : null;
+                ? (ActivityDisplay) ((WindowContainer) oldParent).getDisplayContent() : null;
 
         mDisplayId = (display != null) ? display.mDisplayId : INVALID_DISPLAY;
         mPrevDisplayId = (oldDisplay != null) ? oldDisplay.mDisplayId : INVALID_DISPLAY;
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index d98aa6f..7356368 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -1500,13 +1500,11 @@
                     mRootActivityContainer.getActivityDisplay(toDisplayId);
 
             if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
-                // Tell the display we are exiting split-screen mode.
-                toDisplay.onExitingSplitScreenMode();
                 // We are moving all tasks from the docked stack to the fullscreen stack,
                 // which is dismissing the docked stack, so resize all other stacks to
                 // fullscreen here already so we don't end up with resize trashing.
-                for (int i = toDisplay.getChildCount() - 1; i >= 0; --i) {
-                    final ActivityStack otherStack = toDisplay.getChildAt(i);
+                for (int i = toDisplay.getStackCount() - 1; i >= 0; --i) {
+                    final ActivityStack otherStack = toDisplay.getStackAt(i);
                     if (!otherStack.inSplitScreenSecondaryWindowingMode()) {
                         continue;
                     }
@@ -1654,8 +1652,8 @@
                 // screen controls and is also the same for all stacks.
                 final ActivityDisplay display = mRootActivityContainer.getDefaultDisplay();
                 final Rect otherTaskRect = new Rect();
-                for (int i = display.getChildCount() - 1; i >= 0; --i) {
-                    final ActivityStack current = display.getChildAt(i);
+                for (int i = display.getStackCount() - 1; i >= 0; --i) {
+                    final ActivityStack current = display.getStackAt(i);
                     if (!current.inSplitScreenSecondaryWindowingMode()) {
                         continue;
                     }
@@ -1740,7 +1738,7 @@
              * to the fullscreen stack.  This is to guarantee that when we are removing a stack,
              * that the client receives onStop() before it is reparented.  We do this by detaching
              * the stack from the display so that it will be considered invisible when
-             * ensureActivitiesVisible() is called, and all of its activitys will be marked
+             * ensureActivitiesVisible() is called, and all of its activities will be marked
              * invisible as well and added to the stopping list.  After which we process the
              * stopping list by handling the idle.
              */
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index 8164bf4..baa2955 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1132,7 +1132,7 @@
             request.outActivity[0] = mLastStartActivityRecord;
         }
 
-        return getExternalResult(mLastStartActivityResult);
+        return mLastStartActivityResult;
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index b046b08..2aa9ea5 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -138,7 +138,6 @@
 import static com.android.server.wm.utils.RegionUtils.rectListToRegion;
 
 import android.animation.AnimationHandler;
-import android.annotation.CallSuper;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -221,7 +220,7 @@
  * particular Display.
  */
 class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowContainer>
-        implements WindowManagerPolicy.DisplayContentInfo, ConfigurationContainerListener {
+        implements WindowManagerPolicy.DisplayContentInfo {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "DisplayContent" : TAG_WM;
 
     /** The default scaling mode that scales content automatically. */
@@ -236,11 +235,10 @@
     @Retention(RetentionPolicy.SOURCE)
     @interface ForceScalingMode {}
 
-    /** Unique identifier of this stack. */
-    private final int mDisplayId;
+    ActivityTaskManagerService mAtmService;
 
-    // TODO: Remove once unification is complete.
-    ActivityDisplay mActivityDisplay;
+    /** Unique identifier of this display. */
+    private final int mDisplayId;
 
     /** The containers below are the only child containers the display can have. */
     // Contains all window containers that are related to apps (Activities)
@@ -671,10 +669,12 @@
             getDisplayPolicy().layoutWindowLw(w, null, mDisplayFrames);
             w.mLayoutSeq = mLayoutSeq;
 
-            // If this is the first layout, we need to initialize the last inset values as
-            // otherwise we'd immediately cause an unnecessary resize.
+            // If this is the first layout, we need to initialize the last frames and inset values,
+            // as otherwise we'd immediately cause an unnecessary resize.
             if (firstLayout) {
+                w.updateLastFrames();
                 w.updateLastInsetValues();
+                w.updateLocationInParentDisplayIfNeeded();
             }
 
             if (w.mActivityRecord != null) {
@@ -842,16 +842,15 @@
      * @param service You know.
      * @param activityDisplay The ActivityDisplay for the display container.
      */
-    DisplayContent(Display display, WindowManagerService service,
-            ActivityDisplay activityDisplay) {
+    DisplayContent(Display display, WindowManagerService service) {
         super(service);
-        mActivityDisplay = activityDisplay;
         if (service.mRoot.getDisplayContent(display.getDisplayId()) != null) {
             throw new IllegalArgumentException("Display with ID=" + display.getDisplayId()
                     + " already exists=" + service.mRoot.getDisplayContent(display.getDisplayId())
                     + " new=" + display);
         }
 
+        mAtmService = mWmService.mAtmService;
         mDisplay = display;
         mDisplayId = display.getDisplayId();
         mWallpaperController = new WallpaperController(mWmService, this);
@@ -1125,20 +1124,6 @@
         mAppTransitionController.registerRemoteAnimations(definition);
     }
 
-    /**
-     * The display content may have configuration set from {@link #DisplayWindowSettings}. This
-     * callback let the owner of container know there is existing configuration to prevent the
-     * values from being replaced by the initializing {@link #ActivityDisplay}.
-     */
-    void initializeDisplayOverrideConfiguration() {
-        if (mActivityDisplay == null) {
-            return;
-        }
-        mActivityDisplay.onRequestedOverrideConfigurationChanged(
-                getResolvedOverrideConfiguration());
-        mActivityDisplay.registerConfigurationChangeListener(this);
-    }
-
     void reconfigureDisplayLocked() {
         if (!isReady()) {
             return;
@@ -1162,13 +1147,15 @@
     }
 
     void sendNewConfiguration() {
-        if (!isReady() || mActivityDisplay == null) {
+        if (!isReady()) {
             return;
         }
         if (mDisplayRotation.isWaitingForRemoteRotation()) {
             return;
         }
-        final boolean configUpdated = mActivityDisplay.updateDisplayOverrideConfigurationLocked();
+        // TODO(display-merge): Remove cast
+        final boolean configUpdated =
+                ((ActivityDisplay) this).updateDisplayOverrideConfigurationLocked();
         if (configUpdated) {
             return;
         }
@@ -1199,7 +1186,8 @@
 
         if (handled && requestingContainer instanceof ActivityRecord) {
             final ActivityRecord activityRecord = (ActivityRecord) requestingContainer;
-            final boolean kept = mActivityDisplay.updateDisplayOverrideConfigurationLocked(
+            // TODO(display-merge): Remove cast
+            final boolean kept = ((ActivityDisplay) this).updateDisplayOverrideConfigurationLocked(
                     config, activityRecord, false /* deferResume */, null /* result */);
             activityRecord.frozenBeforeDestroy = true;
             if (!kept) {
@@ -1208,7 +1196,8 @@
         } else {
             // We have a new configuration to push so we need to update ATMS for now.
             // TODO: Clean up display configuration push between ATMS and WMS after unification.
-            mActivityDisplay.updateDisplayOverrideConfigurationLocked(
+            // TODO(display-merge): Remove cast
+            ((ActivityDisplay) this.mDisplayContent).updateDisplayOverrideConfigurationLocked(
                     config, null /* starting */, false /* deferResume */, null);
         }
         return handled;
@@ -1797,12 +1786,15 @@
         return mTaskStackContainers.getHomeStack();
     }
 
+    ActivityStack getRecentsStack() {
+        return mTaskStackContainers.getRecentsStack();
+    }
+
     /**
-     * @return The primary split-screen stack, but only if it is visible, and {@code null} otherwise.
+     * @return The primary split-screen stack, and {@code null} otherwise.
      */
     ActivityStack getSplitScreenPrimaryStack() {
-        ActivityStack stack = mTaskStackContainers.getSplitScreenPrimaryStack();
-        return (stack != null && stack.isVisible()) ? stack : null;
+        return mTaskStackContainers.getSplitScreenPrimaryStack();
     }
 
     boolean hasSplitScreenPrimaryStack() {
@@ -1841,6 +1833,22 @@
         return mTaskStackContainers.getStack(windowingMode, activityType);
     }
 
+    protected int getStackCount() {
+        return mTaskStackContainers.mChildren.size();
+    }
+
+    protected ActivityStack getStackAt(int index) {
+        return mTaskStackContainers.mChildren.get(index);
+    }
+
+    int getIndexOf(ActivityStack stack) {
+        return mTaskStackContainers.getIndexOf(stack);
+    }
+
+    void removeStack(ActivityStack stack) {
+        mTaskStackContainers.removeChild(stack);
+    }
+
     @VisibleForTesting
     WindowList<ActivityStack> getStacks() {
         return mTaskStackContainers.mChildren;
@@ -2208,11 +2216,6 @@
         stack.reparent(mTaskStackContainers, onTop ? POSITION_TOP: POSITION_BOTTOM);
     }
 
-    // TODO(display-unify): No longer needed then.
-    void removeStackFromDisplay(ActivityStack stack) {
-        mTaskStackContainers.removeChild(stack);
-    }
-
     @Override
     protected void addChild(DisplayChildWindowContainer child,
             Comparator<DisplayChildWindowContainer> comparator) {
@@ -2364,9 +2367,6 @@
     void removeImmediately() {
         mRemovingDisplay = true;
         try {
-            if (mActivityDisplay != null) {
-                mActivityDisplay.unregisterConfigurationChangeListener(this);
-            }
             if (mParentWindow != null) {
                 mParentWindow.removeEmbeddedDisplayContent(this);
             }
@@ -2386,7 +2386,9 @@
             mWindowingLayer.release();
             mOverlayLayer.release();
             mInputMonitor.onDisplayRemoved();
-            mWmService.mDisplayNotificationController.dispatchDisplayRemoved(mActivityDisplay);
+            // TODO(display-merge): Remove cast
+            mWmService.mDisplayNotificationController
+                .dispatchDisplayRemoved((ActivityDisplay) this);
         } finally {
             mDisplayReady = false;
             mRemovingDisplay = false;
@@ -2599,9 +2601,8 @@
         }
     }
 
-    @CallSuper
-    @Override
-    public void writeToProto(ProtoOutputStream proto, long fieldId,
+    // TODO(proto-merge): Remove once protos for ActivityDisplay and DisplayContent are merged.
+    public void writeToProtoInner(ProtoOutputStream proto, long fieldId,
             @WindowTraceLogLevel int logLevel) {
         // Critical log level logs only visible elements to mitigate performance overheard
         if (logLevel == WindowTraceLogLevel.CRITICAL && !isVisible()) {
@@ -3929,6 +3930,7 @@
         // Cached reference to some special stacks we tend to get a lot so we don't need to loop
         // through the list to find them.
         private ActivityStack mHomeStack = null;
+        private ActivityStack mRecentsStack = null;
         private ActivityStack mPinnedStack = null;
         private ActivityStack mSplitScreenPrimaryStack = null;
 
@@ -3936,12 +3938,6 @@
             super(service);
         }
 
-        @Override
-        public void onConfigurationChanged(Configuration newParentConfig) {
-            // TODO(display-unify): Remove after unification.
-            onConfigurationChanged(newParentConfig, mActivityDisplay == null /*forwardToChildren*/);
-        }
-
         /**
          * Returns the topmost stack on the display that is compatible with the input windowing mode
          * and activity type. Null is no compatible stack on the display.
@@ -3976,6 +3972,10 @@
                     ? mTaskStackContainers.getChildAt(mTaskStackContainers.getChildCount() - 1) : null;
         }
 
+        int getIndexOf(ActivityStack stack) {
+            return mTaskStackContainers.mChildren.indexOf(stack);
+        }
+
         ActivityStack getHomeStack() {
             if (mHomeStack == null && mDisplayId == DEFAULT_DISPLAY) {
                 Slog.e(TAG_WM, "getHomeStack: Returning null from this=" + this);
@@ -3983,6 +3983,10 @@
             return mHomeStack;
         }
 
+        ActivityStack getRecentsStack() {
+            return mRecentsStack;
+        }
+
         ActivityStack getPinnedStack() {
             return mPinnedStack;
         }
@@ -4018,6 +4022,13 @@
 
                 }
                 mHomeStack = stack;
+            } else if (stack.isActivityTypeRecents()) {
+                if (mRecentsStack != null && mRecentsStack != stack) {
+                    throw new IllegalArgumentException(
+                        "addStackReferenceIfNeeded: recents stack=" + mRecentsStack
+                            + " already exist on display=" + this + " stack=" + stack);
+                }
+                mRecentsStack = stack;
             }
             final int windowingMode = stack.getWindowingMode();
             if (windowingMode == WINDOWING_MODE_PINNED) {
@@ -4034,17 +4045,23 @@
                             + " already exist on display=" + this + " stack=" + stack);
                 }
                 mSplitScreenPrimaryStack = stack;
+                // TODO(display-merge): Remove cast
+                ((ActivityDisplay) this.mDisplayContent).onSplitScreenModeActivated();
                 mDividerControllerLocked.notifyDockedStackExistsChanged(true);
             }
         }
 
-        private void removeStackReferenceIfNeeded(ActivityStack stack) {
+        void removeStackReferenceIfNeeded(ActivityStack stack) {
             if (stack == mHomeStack) {
                 mHomeStack = null;
+            } else if (stack == mRecentsStack) {
+                mRecentsStack = null;
             } else if (stack == mPinnedStack) {
                 mPinnedStack = null;
             } else if (stack == mSplitScreenPrimaryStack) {
                 mSplitScreenPrimaryStack = null;
+                // TODO(display-merge): Remove cast
+                ((ActivityDisplay) this.mDisplayContent).onSplitScreenModeDismissed();
                 // Re-set the split-screen create mode whenever the split-screen stack is removed.
                 mWmService.setDockedStackCreateStateLocked(
                         SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT, null /* initialBounds */);
@@ -4058,9 +4075,6 @@
             position = findPositionForStack(position, stack, true /* adding */);
 
             super.addChild(stack, position);
-            if (mActivityDisplay != null) {
-                mActivityDisplay.addChild(stack, position, true /*fromDc*/);
-            }
 
             // The reparenting case is handled in WindowContainer.
             if (!stack.mReparenting) {
@@ -4072,9 +4086,8 @@
         @Override
         protected void removeChild(ActivityStack stack) {
             super.removeChild(stack);
-            if (mActivityDisplay != null) {
-                mActivityDisplay.onChildRemoved(stack);
-            }
+            // TODO(display-merge): Remove cast
+            ((ActivityDisplay) this.mDisplayContent).onStackRemoved(stack);
             removeStackReferenceIfNeeded(stack);
         }
 
@@ -4087,7 +4100,7 @@
         @Override
         void positionChildAt(int position, ActivityStack child, boolean includingParents) {
             if (child.getWindowConfiguration().isAlwaysOnTop()
-                    && position != POSITION_TOP) {
+                    && position != POSITION_TOP && position != mChildren.size()) {
                 // This stack is always-on-top, override the default behavior.
                 Slog.w(TAG_WM, "Ignoring move of always-on-top stack=" + this + " to bottom");
 
@@ -4097,7 +4110,11 @@
                 super.positionChildAt(currentPosition, child, false /* includingParents */);
                 return;
             }
-
+            // We don't allow untrusted display to top when task stack moves to top,
+            // until user tapping this display to change display position as top intentionally.
+            if (isUntrustedVirtualDisplay() && !getParent().isOnTop()) {
+                includingParents = false;
+            }
             final int targetPosition = findPositionForStack(position, child, false /* adding */);
             super.positionChildAt(targetPosition, child, includingParents);
 
@@ -4133,7 +4150,12 @@
             final int topChildPosition = mChildren.size() - 1;
             int belowAlwaysOnTopPosition = POSITION_BOTTOM;
             for (int i = topChildPosition; i >= 0; --i) {
-                if (getStacks().get(i) != stack && !getStacks().get(i).isAlwaysOnTop()) {
+                // Since a stack could be repositioned while being one of the child, return
+                // current index if that's the same stack we are positioning and it is always on
+                // top.
+                final boolean sameStack = getStacks().get(i) == stack;
+                if ((sameStack && stack.isAlwaysOnTop())
+                        || (!sameStack && !getStacks().get(i).isAlwaysOnTop())) {
                     belowAlwaysOnTopPosition = i;
                     break;
                 }
@@ -4158,10 +4180,6 @@
                         POSITION_BOTTOM ? belowAlwaysOnTopPosition : 0;
             }
 
-            int targetPosition = requestedPosition;
-            targetPosition = Math.min(targetPosition, maxPosition);
-            targetPosition = Math.max(targetPosition, minPosition);
-
             // Cap the requested position to something reasonable for the previous position check
             // below.
             if (requestedPosition == POSITION_TOP) {
@@ -4170,6 +4188,10 @@
                 requestedPosition = 0;
             }
 
+            int targetPosition = requestedPosition;
+            targetPosition = Math.min(targetPosition, maxPosition);
+            targetPosition = Math.max(targetPosition, minPosition);
+
             int prevPosition = getStacks().indexOf(stack);
             // The positions we calculated above (maxPosition, minPosition) do not take into
             // consideration the following edge cases.
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 77e557b..2283041 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -2215,9 +2215,22 @@
     }
 
     private void offsetInputMethodWindowLw(WindowState win, DisplayFrames displayFrames) {
+        final int rotation = displayFrames.mRotation;
+        final int navBarPosition = navigationBarPosition(displayFrames.mDisplayWidth,
+                displayFrames.mDisplayHeight, rotation);
+
         int top = Math.max(win.getDisplayFrameLw().top, win.getContentFrameLw().top);
         top += win.getGivenContentInsetsLw().top;
         displayFrames.mContent.bottom = Math.min(displayFrames.mContent.bottom, top);
+        if (navBarPosition == NAV_BAR_BOTTOM) {
+            // Always account for the nav bar frame height on the bottom since in all navigation
+            // modes we make room to show the dismiss-ime button, even if the IME does not report
+            // insets (ie. when floating)
+            final int uimode = mService.mPolicy.getUiMode();
+            final int navFrameHeight = getNavigationBarFrameHeight(rotation, uimode);
+            displayFrames.mContent.bottom = Math.min(displayFrames.mContent.bottom,
+                    displayFrames.mUnrestricted.bottom - navFrameHeight);
+        }
         displayFrames.mVoiceContent.bottom = Math.min(displayFrames.mVoiceContent.bottom, top);
         top = win.getVisibleFrameLw().top;
         top += win.getGivenVisibleInsetsLw().top;
diff --git a/services/core/java/com/android/server/wm/DisplayWindowListenerController.java b/services/core/java/com/android/server/wm/DisplayWindowListenerController.java
index 2da76ea..78fea74 100644
--- a/services/core/java/com/android/server/wm/DisplayWindowListenerController.java
+++ b/services/core/java/com/android/server/wm/DisplayWindowListenerController.java
@@ -63,7 +63,7 @@
         mDisplayListeners.finishBroadcast();
     }
 
-    void dispatchDisplayChanged(ActivityDisplay display, Configuration newConfig) {
+    void dispatchDisplayChanged(DisplayContent display, Configuration newConfig) {
         // Only report changed if this has actually been added to the hierarchy already.
         boolean isInHierarchy = false;
         for (int i = 0; i < display.getParent().getChildCount(); ++i) {
@@ -78,7 +78,7 @@
         for (int i = 0; i < count; ++i) {
             try {
                 mDisplayListeners.getBroadcastItem(i).onDisplayConfigurationChanged(
-                        display.mDisplayId, newConfig);
+                        display.getDisplayId(), newConfig);
             } catch (RemoteException e) {
             }
         }
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index 7645de5..318d88e 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -531,8 +531,8 @@
          * occlusion state.
          */
         private ActivityStack getStackForControllingOccluding(ActivityDisplay display) {
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
+            for (int stackNdx = display.getStackCount() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = display.getStackAt(stackNdx);
                 if (stack != null && stack.isFocusableAndVisible()
                         && !stack.inPinnedWindowingMode()) {
                     return stack;
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index f4c867c..d5bbe6b 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -475,8 +475,8 @@
      * @return The top stack that is not always-on-top.
      */
     private ActivityStack getTopNonAlwaysOnTopStack() {
-        for (int i = mDefaultDisplay.getChildCount() - 1; i >= 0; i--) {
-            final ActivityStack s = mDefaultDisplay.getChildAt(i);
+        for (int i = mDefaultDisplay.getStackCount() - 1; i >= 0; i--) {
+            final ActivityStack s = mDefaultDisplay.getStackAt(i);
             if (s.getWindowConfiguration().isAlwaysOnTop()) {
                 continue;
             }
diff --git a/services/core/java/com/android/server/wm/RootActivityContainer.java b/services/core/java/com/android/server/wm/RootActivityContainer.java
index 5fd59fa..40e6dcc 100644
--- a/services/core/java/com/android/server/wm/RootActivityContainer.java
+++ b/services/core/java/com/android/server/wm/RootActivityContainer.java
@@ -52,7 +52,6 @@
 import static com.android.server.wm.ActivityStackSupervisor.dumpHistoryList;
 import static com.android.server.wm.ActivityStackSupervisor.printThisActivity;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RECENTS;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_RELEASE;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STACK;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STATES;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_TASKS;
@@ -770,10 +769,12 @@
             starting.frozenBeforeDestroy = true;
         }
 
-        if (displayContent != null && displayContent.mActivityDisplay != null) {
+        if (displayContent != null) {
             // Update the configuration of the activities on the display.
-            return displayContent.mActivityDisplay.updateDisplayOverrideConfigurationLocked(config,
-                    starting, deferResume, null /* result */);
+            // TODO(display-merge): Remove cast
+            return ((ActivityDisplay) displayContent)
+                .updateDisplayOverrideConfigurationLocked(config, starting, deferResume,
+                    null /* result */);
         } else {
             return true;
         }
@@ -790,8 +791,8 @@
         for (int i = mActivityDisplays.size() - 1; i >= 0; i--) {
             final ActivityDisplay display = mActivityDisplays.get(i);
             // Traverse all stacks on a display.
-            for (int j = display.getChildCount() - 1; j >= 0; --j) {
-                final ActivityStack stack = display.getChildAt(j);
+            for (int j = display.getStackCount() - 1; j >= 0; --j) {
+                final ActivityStack stack = display.getStackAt(j);
                 // Get top activity from a visible stack and add it to the list.
                 if (stack.shouldBeVisible(null /* starting */)) {
                     final ActivityRecord top = stack.getTopNonFinishingActivity();
@@ -861,8 +862,8 @@
         WindowProcessController fgApp = null;
         for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
             final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
+            for (int stackNdx = display.getStackCount() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = display.getStackAt(stackNdx);
                 if (isTopDisplayFocusedStack(stack)) {
                     final ActivityRecord resumedActivity = stack.getResumedActivity();
                     if (resumedActivity != null) {
@@ -988,8 +989,8 @@
         mStackSupervisor.mStartingUsers.add(uss);
         for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
             final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
+            for (int stackNdx = display.getStackCount() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = display.getStackAt(stackNdx);
                 stack.switchUser(userId);
                 Task task = stack.topTask();
                 if (task != null) {
@@ -1056,7 +1057,7 @@
                     + " to its current displayId=" + displayId);
         }
 
-        if (activityDisplay.isSingleTaskInstance() && activityDisplay.getChildCount() > 0) {
+        if (activityDisplay.isSingleTaskInstance() && activityDisplay.getStackCount() > 0) {
             // We don't allow moving stacks to single instance display that already has a child.
             Slog.e(TAG, "Can not move stack=" + stack
                     + " to single task instance display=" + activityDisplay);
@@ -1229,8 +1230,8 @@
             final ActivityDisplay display = mActivityDisplays.get(displayNdx);
             // It is possible that request to finish activity might also remove its task and stack,
             // so we need to be careful with indexes in the loop and check child count every time.
-            for (int stackNdx = 0; stackNdx < display.getChildCount(); ++stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
+            for (int stackNdx = 0; stackNdx < display.getStackCount(); ++stackNdx) {
+                final ActivityStack stack = display.getStackAt(stackNdx);
                 final Task t = stack.finishTopCrashedActivityLocked(app, reason);
                 if (stack == focusedStack || finishedTask == null) {
                     finishedTask = t;
@@ -1260,8 +1261,8 @@
         for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
             boolean resumedOnDisplay = false;
             final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
+            for (int stackNdx = display.getStackCount() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = display.getStackAt(stackNdx);
                 final ActivityRecord topRunningActivity = stack.topRunningActivityLocked();
                 if (!stack.isFocusableAndVisible() || topRunningActivity == null) {
                     continue;
@@ -1314,8 +1315,8 @@
             }
 
             // Set the sleeping state of the stacks on the display.
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
+            for (int stackNdx = display.getStackCount() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = display.getStackAt(stackNdx);
                 if (displayShouldSleep) {
                     stack.goToSleepIfPossible(false /* shuttingDown */);
                 } else {
@@ -1355,9 +1356,9 @@
         }
     }
 
-    protected <T extends ActivityStack> T getStack(int stackId) {
+    protected ActivityStack getStack(int stackId) {
         for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
-            final T stack = mActivityDisplays.get(i).getStack(stackId);
+            final ActivityStack stack = mActivityDisplays.get(i).getStack(stackId);
             if (stack != null) {
                 return stack;
             }
@@ -1366,9 +1367,10 @@
     }
 
     /** @see ActivityDisplay#getStack(int, int) */
-    private <T extends ActivityStack> T getStack(int windowingMode, int activityType) {
+    ActivityStack getStack(int windowingMode, int activityType) {
         for (int i = mActivityDisplays.size() - 1; i >= 0; --i) {
-            final T stack = mActivityDisplays.get(i).getStack(windowingMode, activityType);
+            final ActivityStack stack =
+                    mActivityDisplays.get(i).getStack(windowingMode, activityType);
             if (stack != null) {
                 return stack;
             }
@@ -1376,7 +1378,7 @@
         return null;
     }
 
-    private <T extends ActivityStack> T getStack(int windowingMode, int activityType,
+    private ActivityStack getStack(int windowingMode, int activityType,
             int displayId) {
         ActivityDisplay display = getActivityDisplay(displayId);
         if (display == null) {
@@ -1449,8 +1451,8 @@
         if (displayId == INVALID_DISPLAY) {
             for (int displayNdx = 0; displayNdx < mActivityDisplays.size(); ++displayNdx) {
                 final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-                for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                    final ActivityStack stack = display.getChildAt(stackNdx);
+                for (int stackNdx = display.getStackCount() - 1; stackNdx >= 0; --stackNdx) {
+                    final ActivityStack stack = display.getStackAt(stackNdx);
                     list.add(getStackInfo(stack));
                 }
             }
@@ -1460,8 +1462,8 @@
         if (display == null) {
             return list;
         }
-        for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-            final ActivityStack stack = display.getChildAt(stackNdx);
+        for (int stackNdx = display.getStackCount() - 1; stackNdx >= 0; --stackNdx) {
+            final ActivityStack stack = display.getStackAt(stackNdx);
             list.add(getStackInfo(stack));
         }
         return list;
@@ -1549,9 +1551,9 @@
     ActivityStack findStackBehind(ActivityStack stack) {
         final ActivityDisplay display = getActivityDisplay(stack.mDisplayId);
         if (display != null) {
-            for (int i = display.getChildCount() - 1; i >= 0; i--) {
-                if (display.getChildAt(i) == stack && i > 0) {
-                    return display.getChildAt(i - 1);
+            for (int i = display.getStackCount() - 1; i >= 0; i--) {
+                if (display.getStackAt(i) == stack && i > 0) {
+                    return display.getStackAt(i - 1);
                 }
             }
         }
@@ -1575,7 +1577,7 @@
     }
 
     // TODO: remove after object merge with RootWindowContainer
-    void onChildPositionChanged(ActivityDisplay display, int position) {
+    void onChildPositionChanged(DisplayContent display, int position) {
         // Assume AM lock is held from positionChildAt of controller in each hierarchy.
         if (display != null) {
             positionChildAt(display, position);
@@ -1583,18 +1585,20 @@
     }
 
     /** Change the z-order of the given display. */
-    private void positionChildAt(ActivityDisplay display, int position) {
+    private void positionChildAt(DisplayContent display, int position) {
         if (position >= mActivityDisplays.size()) {
             position = mActivityDisplays.size() - 1;
         } else if (position < 0) {
             position = 0;
         }
 
+        // TODO(display-merge): Remove cast
+        final ActivityDisplay activityDisplay = (ActivityDisplay) display;
         if (mActivityDisplays.isEmpty()) {
-            mActivityDisplays.add(display);
+            mActivityDisplays.add(activityDisplay);
         } else if (mActivityDisplays.get(position) != display) {
             mActivityDisplays.remove(display);
-            mActivityDisplays.add(position, display);
+            mActivityDisplays.add(position, activityDisplay);
         }
         mStackSupervisor.updateTopResumedActivityIfNeeded();
     }
@@ -1709,8 +1713,8 @@
     void scheduleDestroyAllActivities(WindowProcessController app, String reason) {
         for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
             final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
+            for (int stackNdx = display.getStackCount() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = display.getStackAt(stackNdx);
                 stack.scheduleDestroyActivities(app, reason);
             }
         }
@@ -1722,15 +1726,15 @@
         boolean allSleep = true;
         for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
             final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
+            for (int stackNdx = display.getStackCount() - 1; stackNdx >= 0; --stackNdx) {
                 // Stacks and activities could be removed while putting activities to sleep if
                 // the app process was gone. This prevents us getting exception by accessing an
                 // invalid stack index.
-                if (stackNdx >= display.getChildCount()) {
+                if (stackNdx >= display.getStackCount()) {
                     continue;
                 }
 
-                final ActivityStack stack = display.getChildAt(stackNdx);
+                final ActivityStack stack = display.getStackAt(stackNdx);
                 if (allowDelay) {
                     allSleep &= stack.goToSleepIfPossible(shuttingDown);
                 } else {
@@ -1972,8 +1976,8 @@
                 r.getActivityType());
 
         // Return the topmost valid stack on the display.
-        for (int i = activityDisplay.getChildCount() - 1; i >= 0; --i) {
-            final ActivityStack stack = activityDisplay.getChildAt(i);
+        for (int i = activityDisplay.getStackCount() - 1; i >= 0; --i) {
+            final ActivityStack stack = activityDisplay.getStackAt(i);
             if (isValidLaunchStack(stack, r, windowingMode)) {
                 return stack;
             }
@@ -2110,8 +2114,8 @@
         boolean hasVisibleActivities = false;
         for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
             final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
+            for (int stackNdx = display.getStackCount() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = display.getStackAt(stackNdx);
                 hasVisibleActivities |= stack.handleAppDiedLocked(app);
             }
         }
@@ -2223,9 +2227,9 @@
     void finishVoiceTask(IVoiceInteractionSession session) {
         for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
             final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            final int numStacks = display.getChildCount();
+            final int numStacks = display.getStackCount();
             for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
+                final ActivityStack stack = display.getStackAt(stackNdx);
                 stack.finishVoiceTask(session);
             }
         }
@@ -2290,8 +2294,8 @@
         boolean foundResumed = false;
         for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
             final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
+            for (int stackNdx = display.getStackCount() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = display.getStackAt(stackNdx);
                 final ActivityRecord r = stack.getResumedActivity();
                 if (r != null) {
                     if (!r.nowVisible) {
@@ -2308,8 +2312,8 @@
         boolean pausing = true;
         for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
             final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
+            for (int stackNdx = display.getStackCount() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = display.getStackAt(stackNdx);
                 final ActivityRecord r = stack.mPausingActivity;
                 if (r != null && !r.isState(PAUSED, STOPPED, STOPPING)) {
                     if (DEBUG_STATES) {
@@ -2336,8 +2340,8 @@
         try {
             for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
                 final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-                for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                    final ActivityStack stack = display.getChildAt(stackNdx);
+                for (int stackNdx = display.getStackCount() - 1; stackNdx >= 0; --stackNdx) {
+                    final ActivityStack stack = display.getStackAt(stackNdx);
                     final List<Task> tasks = stack.getAllTasks();
                     for (int taskNdx = tasks.size() - 1; taskNdx >= 0; taskNdx--) {
                         final Task task = tasks.get(taskNdx);
@@ -2380,8 +2384,8 @@
     void cancelInitializingActivities() {
         for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
             final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
+            for (int stackNdx = display.getStackCount() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = display.getStackAt(stackNdx);
                 stack.cancelInitializingActivities();
             }
         }
@@ -2413,8 +2417,8 @@
         int numDisplays = mActivityDisplays.size();
         for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
             final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
+            for (int stackNdx = display.getStackCount() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = display.getStackAt(stackNdx);
                 final Task task = stack.taskForIdLocked(id);
                 if (task == null) {
                     continue;
@@ -2471,8 +2475,8 @@
         int numDisplays = mActivityDisplays.size();
         for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
             final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
+            for (int stackNdx = display.getStackCount() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = display.getStackAt(stackNdx);
                 final ActivityRecord r = stack.isInStackLocked(token);
                 if (r != null) {
                     return r;
@@ -2554,8 +2558,8 @@
             int numDisplays = mActivityDisplays.size();
             for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
                 final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-                for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                    final ActivityStack stack = display.getChildAt(stackNdx);
+                for (int stackNdx = display.getStackCount() - 1; stackNdx >= 0; --stackNdx) {
+                    final ActivityStack stack = display.getStackAt(stackNdx);
                     if (!dumpVisibleStacksOnly || stack.shouldBeVisible(null)) {
                         activities.addAll(stack.getDumpActivitiesLocked(name));
                     }
@@ -2606,8 +2610,8 @@
             pw.print("Display #"); pw.print(activityDisplay.mDisplayId);
             pw.println(" (activities from top to bottom):");
             final ActivityDisplay display = mActivityDisplays.get(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
+            for (int stackNdx = display.getStackCount() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = display.getStackAt(stackNdx);
                 pw.println();
                 printed = stack.dump(fd, pw, dumpAll, dumpClient, dumpPackage, needSep);
                 needSep = printed;
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 5ec9599..5a104289 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -37,7 +37,6 @@
 import static com.android.server.wm.RootWindowContainerProto.DISPLAYS;
 import static com.android.server.wm.RootWindowContainerProto.WINDOWS;
 import static com.android.server.wm.RootWindowContainerProto.WINDOW_CONTAINER;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_DISPLAY;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT_REPEATS;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER_LIGHT;
 import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_TRACE;
@@ -71,7 +70,6 @@
 import android.util.Slog;
 import android.util.SparseIntArray;
 import android.util.proto.ProtoOutputStream;
-import android.view.Display;
 import android.view.DisplayInfo;
 import android.view.SurfaceControl;
 import android.view.WindowManager;
@@ -204,6 +202,12 @@
     }
 
     @Override
+    boolean isOnTop() {
+        // Considered always on top
+        return true;
+    }
+
+    @Override
     void onChildPositionChanged(WindowContainer child) {
         mWmService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL,
                 !mWmService.mPerDisplayFocusEnabled /* updateInputWindows */);
@@ -219,36 +223,6 @@
         return null;
     }
 
-    DisplayContent createDisplayContent(final Display display, ActivityDisplay activityDisplay) {
-        final int displayId = display.getDisplayId();
-
-        // In select scenarios, it is possible that a DisplayContent will be created on demand
-        // rather than waiting for the controller. In this case, associate the controller and return
-        // the existing display.
-        final DisplayContent existing = getDisplayContent(displayId);
-
-        if (existing != null) {
-            existing.mActivityDisplay = activityDisplay;
-            existing.initializeDisplayOverrideConfiguration();
-            return existing;
-        }
-
-        final DisplayContent dc = new DisplayContent(display, mWmService, activityDisplay);
-
-        if (DEBUG_DISPLAY) Slog.v(TAG_WM, "Adding display=" + display);
-
-        mWmService.mDisplayWindowSettings.applySettingsToDisplayLocked(dc);
-        dc.initializeDisplayOverrideConfiguration();
-
-        if (mWmService.mDisplayManagerInternal != null) {
-            mWmService.mDisplayManagerInternal.setDisplayInfoOverrideFromWindowManager(
-                    displayId, dc.getDisplayInfo());
-            dc.configureDisplayPolicy();
-        }
-
-        return dc;
-    }
-
     /**
      * Called when DisplayWindowSettings values may change.
      */
@@ -262,7 +236,6 @@
                 continue;
             }
 
-            displayContent.initializeDisplayOverrideConfiguration();
             displayContent.reconfigureDisplayLocked();
 
             // We need to update global configuration as well if config of default display has
@@ -1039,7 +1012,7 @@
             final int count = mChildren.size();
             for (int i = 0; i < count; ++i) {
                 final DisplayContent displayContent = mChildren.get(i);
-                displayContent.writeToProto(proto, DISPLAYS, logLevel);
+                displayContent.writeToProtoInner(proto, DISPLAYS, logLevel);
             }
         }
         if (logLevel == WindowTraceLogLevel.ALL) {
@@ -1059,7 +1032,7 @@
     void positionChildAt(int position, DisplayContent child, boolean includingParents) {
         super.positionChildAt(position, child, includingParents);
         if (mRootActivityContainer != null) {
-            mRootActivityContainer.onChildPositionChanged(child.mActivityDisplay, position);
+            mRootActivityContainer.onChildPositionChanged(child, position);
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/RunningTasks.java b/services/core/java/com/android/server/wm/RunningTasks.java
index f2678bb..ca9d91e 100644
--- a/services/core/java/com/android/server/wm/RunningTasks.java
+++ b/services/core/java/com/android/server/wm/RunningTasks.java
@@ -52,8 +52,8 @@
         final int numDisplays = activityDisplays.size();
         for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
             final ActivityDisplay display = activityDisplays.get(displayNdx);
-            for (int stackNdx = display.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
-                final ActivityStack stack = display.getChildAt(stackNdx);
+            for (int stackNdx = display.getStackCount() - 1; stackNdx >= 0; --stackNdx) {
+                final ActivityStack stack = display.getStackAt(stackNdx);
                 mTmpStackTasks.clear();
                 stack.getRunningTasks(mTmpStackTasks, ignoreActivityType, ignoreWindowingMode,
                         callingUid, allowed, crossUser, profileIds);
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 88a38e0..6e8d0b7 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -968,6 +968,11 @@
         final ActivityStack oldStack = ((ActivityStack) oldParent);
         final ActivityStack newStack = ((ActivityStack) newParent);
 
+        // Task is going to be removed, clean it up before detaching from hierarchy.
+        if (oldParent != null && newParent == null) {
+            cleanUpResourcesForDestroy();
+        }
+
         mStack = newStack;
 
         super.onParentChanged(newParent, oldParent);
@@ -1012,12 +1017,6 @@
             updateOverrideConfigurationFromLaunchBounds();
         }
 
-        // Task is being removed.
-        if (oldParent != null && newParent == null) {
-            cleanUpResourcesForDestroy();
-        }
-
-
         // Update task bounds if needed.
         adjustBoundsForDisplayChangeIfNeeded(getDisplayContent());
 
diff --git a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
index 8ad8972..9d19cfe 100644
--- a/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
+++ b/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java
@@ -705,8 +705,8 @@
     private void adjustBoundsToAvoidConflictInDisplay(@NonNull ActivityDisplay display,
             @NonNull Rect inOutBounds) {
         final List<Rect> taskBoundsToCheck = new ArrayList<>();
-        for (int i = 0; i < display.getChildCount(); ++i) {
-            final ActivityStack stack = display.getChildAt(i);
+        for (int i = 0; i < display.getStackCount(); ++i) {
+            final ActivityStack stack = display.getStackAt(i);
             if (!stack.inFreeformWindowingMode()) {
                 continue;
             }
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 5c1491e..1b4ca41 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -288,10 +288,8 @@
         final DisplayContent dc = newParent.getDisplayContent();
 
         mReparenting = true;
-        // Oddly enough we add to the new parent before removing from the old parent to avoid
-        // issues...
-        newParent.addChild(this, position);
         oldParent.removeChild(this);
+        newParent.addChild(this, position);
         mReparenting = false;
 
         // Relayout display(s)
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 46faf3b..ea90e49 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -550,4 +550,10 @@
      * the next time the activities are opened.
      */
     public abstract void clearSnapshotCache();
+
+    /**
+     * Assigns accessibility ID a window surface as a layer metadata.
+     */
+    public abstract void setAccessibilityIdToSurfaceMetadata(
+            IBinder windowToken, int accessibilityWindowId);
 }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 4ec50f0..51221dd 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -1732,20 +1732,7 @@
             }
         }
 
-        DisplayContent displayContent = mRoot.getDisplayContent(displayId);
-
-        // Create an instance if possible instead of waiting for the ActivityManagerService to drive
-        // the creation.
-        if (displayContent == null) {
-            final Display display = mDisplayManager.getDisplay(displayId);
-
-            if (display != null) {
-                displayContent = mRoot.createDisplayContent(display, null /* activityDisplay */);
-                displayContent.reconfigureDisplayLocked();
-            }
-        }
-
-        return displayContent;
+        return mAtmService.mRootActivityContainer.getActivityDisplayOrCreate(displayId);
     }
 
     private boolean doesAddToastWindowRequireToken(String packageName, int callingUid,
@@ -2350,9 +2337,8 @@
 
             win.setLastReportedMergedConfiguration(mergedConfiguration);
 
-            // Update the last frames and inset values here because the values are sent back to the
-            // client. The last values represent the last client state.
-            win.updateLastFrames();
+            // Update the last inset values here because the values are sent back to the client.
+            // The last inset values represent the last client state
             win.updateLastInsetValues();
 
             win.getCompatFrame(outFrame);
@@ -7412,6 +7398,27 @@
         public @Nullable KeyInterceptionInfo getKeyInterceptionInfoFromToken(IBinder inputToken) {
             return mKeyInterceptionInfoForToken.get(inputToken);
         }
+
+        @Override
+        public void setAccessibilityIdToSurfaceMetadata(
+                IBinder windowToken, int accessibilityWindowId) {
+            synchronized (mGlobalLock) {
+                final WindowState state = mWindowMap.get(windowToken);
+                if (state == null) {
+                    Slog.w(TAG, "Cannot find window which accessibility connection is added to");
+                    return;
+                }
+                try (SurfaceControl.Transaction t = new SurfaceControl.Transaction()) {
+                    t.setMetadata(
+                            state.mSurfaceControl,
+                            SurfaceControl.METADATA_ACCESSIBILITY_ID,
+                            accessibilityWindowId);
+                    t.apply();
+                } finally {
+                    SurfaceControl.closeTransaction();
+                }
+            }
+        }
     }
 
     void registerAppFreezeListener(AppFreezeListener listener) {
@@ -7626,12 +7633,15 @@
             // it as if the host window was tapped.
             touchedWindow = mEmbeddedWindowController.getHostWindow(touchedToken);
         }
-        if (touchedWindow == null || !touchedWindow.canReceiveKeys()) {
+
+        if (touchedWindow == null || !touchedWindow.canReceiveKeys(true /* fromUserTouch */)) {
+            // If the window that received the input event cannot receive keys, don't move the
+            // display it's on to the top since that window won't be able to get focus anyway.
             return;
         }
 
-        handleTaskFocusChange(touchedWindow.getTask());
         handleDisplayFocusChange(touchedWindow);
+        handleTaskFocusChange(touchedWindow.getTask());
     }
 
     private void handleTaskFocusChange(Task task) {
@@ -7659,12 +7669,6 @@
             return;
         }
 
-        if (!window.canReceiveKeys()) {
-            // If the window that received the input event cannot receive keys, don't move the
-            // display it's on to the top since that window won't be able to get focus anyway.
-            return;
-        }
-
         final WindowContainer parent = displayContent.getParent();
         if (parent != null && parent.getTopChild() != displayContent) {
             parent.positionChildAt(WindowContainer.POSITION_TOP, displayContent,
@@ -7676,7 +7680,8 @@
             // to do so because it seems possible to resume activities as part of a larger
             // transaction and it's too early to resume based on current order when performing
             // updateTopResumedActivityIfNeeded().
-            displayContent.mActivityDisplay.ensureActivitiesVisible(null /* starting */,
+            // TODO(display-merge): Remove cast
+            ((ActivityDisplay) displayContent).ensureActivitiesVisible(null /* starting */,
                     0 /* configChanges */, !PRESERVE_WINDOWS, true /* notifyClients */);
         }
     }
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index ce26e5f..dcf4b38 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -35,6 +35,7 @@
 import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
 import static android.view.WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW;
 import static android.view.WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON;
+import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
 import static android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND;
 import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD;
 import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
@@ -2162,12 +2163,13 @@
             return false;
         }
 
-        // Can be an IME target only if:
-        // 1. FLAG_NOT_FOCUSABLE is not set
-        // 2. FLAG_ALT_FOCUSABLE_IM is not set
-        // 3. not a starting window.
-        if (!WindowManager.LayoutParams.mayUseInputMethod(mAttrs.flags)
-                || mAttrs.type == TYPE_APPLICATION_STARTING) {
+        final int fl = mAttrs.flags & (FLAG_NOT_FOCUSABLE | FLAG_ALT_FOCUSABLE_IM);
+        final int type = mAttrs.type;
+
+        // Can only be an IME target if both FLAG_NOT_FOCUSABLE and FLAG_ALT_FOCUSABLE_IM are set or
+        // both are cleared...and not a starting window.
+        if (fl != 0 && fl != (FLAG_NOT_FOCUSABLE | FLAG_ALT_FOCUSABLE_IM)
+                && type != TYPE_APPLICATION_STARTING) {
             return false;
         }
 
@@ -2616,11 +2618,22 @@
 
     @Override
     public boolean canReceiveKeys() {
-        return isVisibleOrAdding()
+        return canReceiveKeys(false /* fromUserTouch */);
+    }
+
+    public boolean canReceiveKeys(boolean fromUserTouch) {
+        final boolean canReceiveKeys = isVisibleOrAdding()
                 && (mViewVisibility == View.VISIBLE) && !mRemoveOnExit
                 && ((mAttrs.flags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) == 0)
                 && (mActivityRecord == null || mActivityRecord.windowsAreFocusable())
                 && !cantReceiveTouchInput();
+        if (!canReceiveKeys) {
+            return false;
+        }
+        // Do not allow untrusted virtual display to receive keys unless user intentionally
+        // touches the display.
+        return fromUserTouch || getDisplayContent().isOnTop()
+                || !getDisplayContent().isUntrustedVirtualDisplay();
     }
 
     @Override
diff --git a/services/core/jni/com_android_server_VibratorService.cpp b/services/core/jni/com_android_server_VibratorService.cpp
index 612a1e7..dcff5a1 100644
--- a/services/core/jni/com_android_server_VibratorService.cpp
+++ b/services/core/jni/com_android_server_VibratorService.cpp
@@ -338,7 +338,7 @@
     if (auto hal = getHal<aidl::IVibrator>()) {
         int32_t lengthMs;
         sp<AidlVibratorCallback> effectCallback = new AidlVibratorCallback(env, vibration);
-        aidl::Effect effectType(static_cast<aidl::Effect>(strength));
+        aidl::Effect effectType(static_cast<aidl::Effect>(effect));
         aidl::EffectStrength effectStrength(static_cast<aidl::EffectStrength>(strength));
 
         auto status = hal->call(&aidl::IVibrator::perform, effectType, effectStrength, effectCallback, &lengthMs);
diff --git a/services/core/xsd/platform-compat-config.xsd b/services/core/xsd/platform-compat-config.xsd
index ee39e50..a70568f 100644
--- a/services/core/xsd/platform-compat-config.xsd
+++ b/services/core/xsd/platform-compat-config.xsd
@@ -28,6 +28,7 @@
                 <xs:attribute type="xs:string" name="name" use="required"/>
                 <xs:attribute type="xs:boolean" name="disabled"/>
                 <xs:attribute type="xs:int" name="enableAfterTargetSdk"/>
+                <xs:attribute type="xs:string" name="description"/>
             </xs:extension>
         </xs:simpleContent>
     </xs:complexType>
diff --git a/services/core/xsd/platform-compat-schema/current.txt b/services/core/xsd/platform-compat-schema/current.txt
index 8456785..3a33f63 100644
--- a/services/core/xsd/platform-compat-schema/current.txt
+++ b/services/core/xsd/platform-compat-schema/current.txt
@@ -3,11 +3,13 @@
 
   public class Change {
     ctor public Change();
+    method public String getDescription();
     method public boolean getDisabled();
     method public int getEnableAfterTargetSdk();
     method public long getId();
     method public String getName();
     method public String getValue();
+    method public void setDescription(String);
     method public void setDisabled(boolean);
     method public void setEnableAfterTargetSdk(int);
     method public void setId(long);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 2499ad8..cb599be 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -139,6 +139,8 @@
 import android.app.backup.IBackupManager;
 import android.app.timedetector.ManualTimeSuggestion;
 import android.app.timedetector.TimeDetector;
+import android.app.timezonedetector.ManualTimeZoneSuggestion;
+import android.app.timezonedetector.TimeZoneDetector;
 import android.app.trust.TrustManager;
 import android.app.usage.UsageStatsManagerInternal;
 import android.compat.annotation.ChangeId;
@@ -1962,6 +1964,10 @@
             return mContext.getSystemService(TimeDetector.class);
         }
 
+        TimeZoneDetector getTimeZoneDetector() {
+            return mContext.getSystemService(TimeZoneDetector.class);
+        }
+
         ConnectivityManager getConnectivityManager() {
             return mContext.getSystemService(ConnectivityManager.class);
         }
@@ -2748,6 +2754,9 @@
             return null;
         }
 
+        // Code for handling failure from getActiveAdminWithPolicyForUidLocked to find an admin
+        // that satisfies the required policy.
+        // Throws a security exception with the right error message.
         if (who != null) {
             final int userId = UserHandle.getUserId(callingUid);
             final DevicePolicyData policy = getUserData(userId);
@@ -2763,6 +2772,10 @@
                 throw new SecurityException("Admin " + admin.info.getComponent()
                         + " does not own the profile");
             }
+            if (reqPolicy == DeviceAdminInfo.USES_POLICY_ORGANIZATION_OWNED_PROFILE_OWNER) {
+                throw new SecurityException("Admin " + admin.info.getComponent()
+                        + " is not the profile owner on organization-owned device");
+            }
             if (DA_DISALLOWED_POLICIES.contains(reqPolicy) && !isDeviceOwner && !isProfileOwner) {
                 throw new SecurityException("Admin " + admin.info.getComponent()
                         + " is not a device owner or profile owner, so may not use policy: "
@@ -2869,12 +2882,16 @@
         ensureLocked();
         final boolean ownsDevice = isDeviceOwner(admin.info.getComponent(), userId);
         final boolean ownsProfile = isProfileOwner(admin.info.getComponent(), userId);
+        final boolean ownsProfileOnOrganizationOwnedDevice =
+                    isProfileOwnerOfOrganizationOwnedDevice(admin.info.getComponent(), userId);
 
         if (reqPolicy == DeviceAdminInfo.USES_POLICY_DEVICE_OWNER) {
             return ownsDevice;
+        } else if (reqPolicy == DeviceAdminInfo.USES_POLICY_ORGANIZATION_OWNED_PROFILE_OWNER) {
+            return ownsDevice || ownsProfileOnOrganizationOwnedDevice;
         } else if (reqPolicy == DeviceAdminInfo.USES_POLICY_PROFILE_OWNER) {
             // DO always has the PO power.
-            return ownsDevice || ownsProfile;
+            return ownsDevice || ownsProfileOnOrganizationOwnedDevice || ownsProfile;
         } else {
             boolean allowedToUsePolicy = ownsDevice || ownsProfile
                     || !DA_DISALLOWED_POLICIES.contains(reqPolicy)
@@ -5574,6 +5591,13 @@
         }
     }
 
+    private void enforceDeviceOwnerOrProfileOwnerOnOrganizationOwnedDevice(ComponentName who) {
+        synchronized (getLockObject()) {
+            getActiveAdminForCallerLocked(
+                    who, DeviceAdminInfo.USES_POLICY_ORGANIZATION_OWNED_PROFILE_OWNER);
+        }
+    }
+
     private void enforceProfileOwnerOfOrganizationOwnedDevice(ActiveAdmin admin) {
         if (!isProfileOwnerOfOrganizationOwnedDevice(admin)) {
             throw new SecurityException(String.format("Provided admin %s is either not a profile "
@@ -8071,21 +8095,12 @@
             return false;
         }
 
-        final int adminUserId = admin.getUserHandle().getIdentifier();
+        return isProfileOwnerOfOrganizationOwnedDevice(
+                admin.info.getComponent(), admin.getUserHandle().getIdentifier());
+    }
 
-        if (!isProfileOwner(admin.info.getComponent(), adminUserId)) {
-            Slog.w(LOG_TAG, String.format("%s is not profile owner of user %d",
-                    admin.info.getComponent(), adminUserId));
-            return false;
-        }
-
-        if (!canProfileOwnerAccessDeviceIds(adminUserId)) {
-            Slog.w(LOG_TAG, String.format("Profile owner of user %d does not own the device.",
-                    adminUserId));
-            return false;
-        }
-
-        return true;
+    private boolean isProfileOwnerOfOrganizationOwnedDevice(ComponentName who, int userId) {
+        return isProfileOwner(who, userId) && canProfileOwnerAccessDeviceIds(userId);
     }
 
     @Override
@@ -11123,8 +11138,11 @@
         if (mInjector.settingsGlobalGetInt(Global.AUTO_TIME_ZONE, 0) == 1) {
             return false;
         }
+        ManualTimeZoneSuggestion manualTimeZoneSuggestion =
+                TimeZoneDetector.createManualTimeZoneSuggestion(
+                        timeZone, "DevicePolicyManagerService: setTimeZone");
         mInjector.binderWithCleanCallingIdentity(() ->
-            mInjector.getAlarmManager().setTimeZone(timeZone));
+                mInjector.getTimeZoneDetector().suggestManualTimeZone(manualTimeZoneSuggestion));
         return true;
     }
 
@@ -12377,7 +12395,7 @@
     @Override
     public String getWifiMacAddress(ComponentName admin) {
         // Make sure caller has DO.
-        enforceDeviceOwner(admin);
+        enforceDeviceOwnerOrProfileOwnerOnOrganizationOwnedDevice(admin);
 
         final long ident = mInjector.binderClearCallingIdentity();
         try {
diff --git a/services/net/java/android/net/ip/IpClientCallbacks.java b/services/net/java/android/net/ip/IpClientCallbacks.java
index db01ae4..61cd88a 100644
--- a/services/net/java/android/net/ip/IpClientCallbacks.java
+++ b/services/net/java/android/net/ip/IpClientCallbacks.java
@@ -17,8 +17,11 @@
 package android.net.ip;
 
 import android.net.DhcpResults;
+import android.net.Layer2PacketParcelable;
 import android.net.LinkProperties;
 
+import java.util.List;
+
 /**
  * Callbacks for handling IpClient events.
  *
@@ -116,4 +119,9 @@
      * whenever 464xlat is being started or stopped.
      */
     public void setNeighborDiscoveryOffload(boolean enable) {}
+
+    /**
+     * Invoked on starting preconnection process.
+     */
+    public void onPreconnectionStart(List<Layer2PacketParcelable> packets) {}
 }
diff --git a/services/net/java/android/net/ip/IpClientManager.java b/services/net/java/android/net/ip/IpClientManager.java
index 1e653cd..4b7ed3c 100644
--- a/services/net/java/android/net/ip/IpClientManager.java
+++ b/services/net/java/android/net/ip/IpClientManager.java
@@ -234,7 +234,7 @@
                     slot, KeepalivePacketDataUtil.toStableParcelable(pkt));
             return true;
         } catch (RemoteException e) {
-            log("Error adding Keepalive Packet Filter ", e);
+            log("Error adding NAT-T Keepalive Packet Filter ", e);
             return false;
         } finally {
             Binder.restoreCallingIdentity(token);
@@ -272,4 +272,22 @@
             Binder.restoreCallingIdentity(token);
         }
     }
+
+    /**
+     * Notify IpClient that preconnection is complete and that the link is ready for use.
+     * The success parameter indicates whether the packets passed in by 'onPreconnectionStart'
+     * were successfully sent to the network or not.
+     */
+    public boolean notifyPreconnectionComplete(boolean success) {
+        final long token = Binder.clearCallingIdentity();
+        try {
+            mIpClient.notifyPreconnectionComplete(success);
+            return true;
+        } catch (RemoteException e) {
+            log("Error notifying IpClient Preconnection completed", e);
+            return false;
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+    }
 }
diff --git a/services/net/java/android/net/ip/IpClientUtil.java b/services/net/java/android/net/ip/IpClientUtil.java
index 714ade1..4d60e62 100644
--- a/services/net/java/android/net/ip/IpClientUtil.java
+++ b/services/net/java/android/net/ip/IpClientUtil.java
@@ -20,12 +20,14 @@
 
 import android.content.Context;
 import android.net.DhcpResultsParcelable;
+import android.net.Layer2PacketParcelable;
 import android.net.LinkProperties;
 import android.net.NetworkStackClient;
 import android.os.ConditionVariable;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.util.List;
 
 
 /**
@@ -176,6 +178,12 @@
             mCb.setNeighborDiscoveryOffload(enable);
         }
 
+        // Invoked on starting preconnection process.
+        @Override
+        public void onPreconnectionStart(List<Layer2PacketParcelable> packets) {
+            mCb.onPreconnectionStart(packets);
+        }
+
         @Override
         public int getInterfaceVersion() {
             return this.VERSION;
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
index 6c09239..8e6114a 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java
@@ -1053,6 +1053,35 @@
 
     @SuppressWarnings("GuardedBy")
     @Test
+    public void testUpdateOomAdj_DoOne_Service_Chain_BoundByFgService_Cycle_2() {
+        ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
+                MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
+        ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID,
+                MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false));
+        bindService(app, client, null, 0, mock(IBinder.class));
+        bindService(client, app, null, 0, mock(IBinder.class));
+        ProcessRecord client2 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID,
+                MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false));
+        bindService(client2, client, null, 0, mock(IBinder.class));
+        client.setHasForegroundServices(true, 0);
+        ArrayList<ProcessRecord> lru = sService.mProcessList.mLruProcesses;
+        lru.clear();
+        lru.add(app);
+        lru.add(client);
+        lru.add(client2);
+        sService.mWakefulness = PowerManagerInternal.WAKEFULNESS_AWAKE;
+        sService.mOomAdjuster.updateOomAdjLocked(app, true, OomAdjuster.OOM_ADJ_REASON_NONE);
+
+        assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
+                SCHED_GROUP_DEFAULT);
+        assertProcStates(client, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
+                SCHED_GROUP_DEFAULT);
+        assertProcStates(client2, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ,
+                SCHED_GROUP_DEFAULT);
+    }
+
+    @SuppressWarnings("GuardedBy")
+    @Test
     public void testUpdateOomAdj_DoOne_Service_Chain_BoundByFgService_Cycle_Branch() {
         ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID,
                 MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false));
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index fb42507..3a07a69 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -40,6 +40,7 @@
     <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
     <uses-permission android:name="android.permission.MANAGE_DEVICE_ADMINS" />
     <uses-permission android:name="android.permission.MODIFY_PHONE_STATE" />
+    <uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE" />
     <uses-permission android:name="android.permission.INTERNET" />
     <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
     <uses-permission android:name="android.permission.PACKET_KEEPALIVE_OFFLOAD" />
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/SystemActionPerformerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/SystemActionPerformerTest.java
index 37f5b87..3352177 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/SystemActionPerformerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/SystemActionPerformerTest.java
@@ -16,18 +16,39 @@
 
 package com.android.server.accessibility;
 
+import static org.hamcrest.Matchers.hasItem;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.not;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
 
 import android.accessibilityservice.AccessibilityService;
+import android.app.PendingIntent;
+import android.app.PendingIntent.CanceledException;
+import android.app.RemoteAction;
 import android.app.StatusBarManager;
+import android.content.BroadcastReceiver;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.graphics.drawable.Icon;
 import android.os.Handler;
+import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction;
+
+import androidx.test.InstrumentationRegistry;
 
 import com.android.internal.util.ScreenshotHelper;
+import com.android.server.LocalServices;
+import com.android.server.statusbar.StatusBarManagerInternal;
 import com.android.server.wm.WindowManagerInternal;
 
 import org.junit.Before;
@@ -35,55 +56,290 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
 /**
  * Tests for SystemActionPerformer
  */
 public class SystemActionPerformerTest {
-    SystemActionPerformer mSystemActionPerformer;
+    private static final int LATCH_TIMEOUT_MS = 500;
+    private static final int LEGACY_SYSTEM_ACTION_COUNT = 9;
+    private static final int NEW_ACTION_ID = 20;
+    private static final String LABEL_1 = "label1";
+    private static final String LABEL_2 = "label2";
+    private static final String INTENT_ACTION1 = "TESTACTION1";
+    private static final String INTENT_ACTION2 = "TESTACTION2";
+    private static final String DESCRIPTION1 = "description1";
+    private static final String DESCRIPTION2 = "description2";
+    private static final PendingIntent TEST_PENDING_INTENT_1 = PendingIntent.getBroadcast(
+            InstrumentationRegistry.getTargetContext(), 0, new Intent(INTENT_ACTION1), 0);
+    private static final RemoteAction NEW_TEST_ACTION_1 = new RemoteAction(
+            Icon.createWithContentUri("content://test"),
+            LABEL_1,
+            DESCRIPTION1,
+            TEST_PENDING_INTENT_1);
+    private static final PendingIntent TEST_PENDING_INTENT_2 = PendingIntent.getBroadcast(
+            InstrumentationRegistry.getTargetContext(), 0, new Intent(INTENT_ACTION2), 0);
+    private static final RemoteAction NEW_TEST_ACTION_2 = new RemoteAction(
+            Icon.createWithContentUri("content://test"),
+            LABEL_2,
+            DESCRIPTION2,
+            TEST_PENDING_INTENT_2);
 
-    @Mock Context mMockContext;
-    @Mock WindowManagerInternal mMockWindowManagerInternal;
-    @Mock StatusBarManager mMockStatusBarManager;
-    @Mock ScreenshotHelper mMockScreenshotHelper;
+    private static final AccessibilityAction NEW_ACCESSIBILITY_ACTION =
+            new AccessibilityAction(NEW_ACTION_ID, LABEL_1);
+    private static final AccessibilityAction LEGACY_NOTIFICATIONS_ACCESSIBILITY_ACTION =
+            new AccessibilityAction(AccessibilityService.GLOBAL_ACTION_NOTIFICATIONS, LABEL_1);
+    private static final AccessibilityAction LEGACY_HOME_ACCESSIBILITY_ACTION =
+            new AccessibilityAction(AccessibilityService.GLOBAL_ACTION_HOME, LABEL_2);
+
+    private SystemActionPerformer mSystemActionPerformer;
+
+    @Mock private Context mMockContext;
+    @Mock private StatusBarManagerInternal mMockStatusBarManagerInternal;
+    @Mock private WindowManagerInternal mMockWindowManagerInternal;
+    @Mock private StatusBarManager mMockStatusBarManager;
+    @Mock private ScreenshotHelper mMockScreenshotHelper;
+    @Mock private SystemActionPerformer.SystemActionsChangedListener mMockListener;
 
     @Before
     public void setup() {
         MockitoAnnotations.initMocks(this);
+        LocalServices.removeServiceForTest(StatusBarManagerInternal.class);
+        LocalServices.addService(StatusBarManagerInternal.class, mMockStatusBarManagerInternal);
+    }
 
-        when(mMockContext.getSystemService(android.app.Service.STATUS_BAR_SERVICE))
-                .thenReturn(mMockStatusBarManager);
+    private void setupWithMockContext() {
+        doReturn(mMockStatusBarManager).when(
+                mMockContext).getSystemService(android.app.Service.STATUS_BAR_SERVICE);
+        doReturn(InstrumentationRegistry.getContext().getResources()).when(
+                mMockContext).getResources();
+        mSystemActionPerformer = new SystemActionPerformer(
+                mMockContext,
+                mMockWindowManagerInternal,
+                () -> mMockScreenshotHelper,
+                mMockListener);
+    }
 
-        mSystemActionPerformer =
-                new SystemActionPerformer(mMockContext, mMockWindowManagerInternal,
-                        () -> mMockScreenshotHelper);
+    private void setupWithRealContext() {
+        mSystemActionPerformer = new SystemActionPerformer(
+                InstrumentationRegistry.getContext(),
+                mMockWindowManagerInternal,
+                () -> mMockScreenshotHelper,
+                mMockListener);
+    }
+
+    // We need below two help functions because AccessbilityAction.equals function only compares
+    // action ids. To verify the test result here, we are also looking at action labels.
+    private void assertHasLegacyAccessibilityAction(
+            List<AccessibilityAction> actions, AccessibilityAction action) {
+        boolean foundAction = false;
+        for (AccessibilityAction a : actions) {
+            if ((a.getId() == action.getId()) && (a.getLabel().equals(action.getLabel()))) {
+                foundAction = true;
+                break;
+            }
+        }
+        assertTrue(foundAction);
+    }
+
+    private void assertHasNoLegacyAccessibilityAction(
+            List<AccessibilityAction> actions, AccessibilityAction action) {
+        boolean foundAction = false;
+        for (AccessibilityAction a : actions) {
+            if ((a.getId() == action.getId()) && (a.getLabel().equals(action.getLabel()))) {
+                foundAction = true;
+                break;
+            }
+        }
+        assertFalse(foundAction);
     }
 
     @Test
-    public void testNotifications_expandsNotificationPanel() {
+    public void testRegisterSystemAction_addedIntoAvailableSystemActions() {
+        setupWithRealContext();
+        // Before any new system action is registered, getSystemActions returns all legacy actions
+        List<AccessibilityAction> actions = mSystemActionPerformer.getSystemActions();
+        assertEquals(LEGACY_SYSTEM_ACTION_COUNT, actions.size());
+        // Register a new system action
+        mSystemActionPerformer.registerSystemAction(NEW_ACTION_ID, NEW_TEST_ACTION_1);
+        actions = mSystemActionPerformer.getSystemActions();
+        assertEquals(LEGACY_SYSTEM_ACTION_COUNT + 1, actions.size());
+        assertThat(actions, hasItem(NEW_ACCESSIBILITY_ACTION));
+    }
+
+    @Test
+    public void testRegisterSystemAction_overrideLegacyAction() {
+        setupWithRealContext();
+        // Before any new system action is registered, getSystemActions returns all legacy actions
+        List<AccessibilityAction> actions = mSystemActionPerformer.getSystemActions();
+        assertEquals(LEGACY_SYSTEM_ACTION_COUNT, actions.size());
+        // Overriding a legacy system action using legacy notification action id
+        mSystemActionPerformer.registerSystemAction(
+                AccessibilityService.GLOBAL_ACTION_NOTIFICATIONS, NEW_TEST_ACTION_1);
+        actions = mSystemActionPerformer.getSystemActions();
+        assertEquals(LEGACY_SYSTEM_ACTION_COUNT, actions.size());
+        assertHasLegacyAccessibilityAction(actions, LEGACY_NOTIFICATIONS_ACCESSIBILITY_ACTION);
+    }
+
+    @Test
+    public void testUnregisterSystemAction_removeFromAvailableSystemActions() {
+        setupWithRealContext();
+        // Before any new system action is registered, getSystemActions returns all legacy actions
+        List<AccessibilityAction> actions = mSystemActionPerformer.getSystemActions();
+        assertEquals(LEGACY_SYSTEM_ACTION_COUNT, actions.size());
+        // Register a new system action
+        mSystemActionPerformer.registerSystemAction(NEW_ACTION_ID, NEW_TEST_ACTION_1);
+        actions = mSystemActionPerformer.getSystemActions();
+        assertEquals(LEGACY_SYSTEM_ACTION_COUNT + 1, actions.size());
+
+        mSystemActionPerformer.unregisterSystemAction(NEW_ACTION_ID);
+        actions = mSystemActionPerformer.getSystemActions();
+        assertEquals(LEGACY_SYSTEM_ACTION_COUNT, actions.size());
+        assertThat(actions, is(not(hasItem(NEW_ACCESSIBILITY_ACTION))));
+    }
+
+    @Test
+    public void testUnregisterSystemAction_removeOverrideForLegacyAction() {
+        setupWithRealContext();
+
+        // Overriding a legacy system action
+        mSystemActionPerformer.registerSystemAction(
+                AccessibilityService.GLOBAL_ACTION_NOTIFICATIONS, NEW_TEST_ACTION_1);
+        List<AccessibilityAction> actions = mSystemActionPerformer.getSystemActions();
+        assertEquals(LEGACY_SYSTEM_ACTION_COUNT, actions.size());
+        assertHasLegacyAccessibilityAction(actions, LEGACY_NOTIFICATIONS_ACCESSIBILITY_ACTION);
+
+        // Remove the overriding action using legacy action id
+        mSystemActionPerformer.unregisterSystemAction(
+                AccessibilityService.GLOBAL_ACTION_NOTIFICATIONS);
+        actions = mSystemActionPerformer.getSystemActions();
+        assertEquals(LEGACY_SYSTEM_ACTION_COUNT, actions.size());
+        assertHasNoLegacyAccessibilityAction(actions, LEGACY_NOTIFICATIONS_ACCESSIBILITY_ACTION);
+    }
+
+    @Test
+    public void testPerformSystemActionNewAction() throws CanceledException {
+        setupWithRealContext();
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        mSystemActionPerformer.registerSystemAction(NEW_ACTION_ID, NEW_TEST_ACTION_1);
+        TestBroadcastReceiver br = new TestBroadcastReceiver(latch);
+        br.register(InstrumentationRegistry.getTargetContext());
+        mSystemActionPerformer.performSystemAction(NEW_ACTION_ID);
+        try {
+            latch.await(LATCH_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+        } catch (InterruptedException e) {
+            fail("RemoteAction should be triggered.");
+        } finally {
+            br.unregister(InstrumentationRegistry.getTargetContext());
+        }
+    }
+
+    @Test
+    public void testPerformSystemActionOverrideLegacyActionUsingLegacyActionId()
+            throws CanceledException {
+        setupWithRealContext();
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        mSystemActionPerformer.registerSystemAction(
+                AccessibilityService.GLOBAL_ACTION_RECENTS, NEW_TEST_ACTION_1);
+        TestBroadcastReceiver br = new TestBroadcastReceiver(latch);
+        br.register(InstrumentationRegistry.getTargetContext());
+        mSystemActionPerformer.performSystemAction(AccessibilityService.GLOBAL_ACTION_RECENTS);
+        try {
+            latch.await(LATCH_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+        } catch (InterruptedException e) {
+            fail("RemoteAction should be triggered.");
+        } finally {
+            br.unregister(InstrumentationRegistry.getTargetContext());
+        }
+        verify(mMockStatusBarManagerInternal, never()).toggleRecentApps();
+        // Now revert to legacy action
+        mSystemActionPerformer.unregisterSystemAction(AccessibilityService.GLOBAL_ACTION_RECENTS);
+        mSystemActionPerformer.performSystemAction(AccessibilityService.GLOBAL_ACTION_RECENTS);
+        verify(mMockStatusBarManagerInternal).toggleRecentApps();
+    }
+
+    @Test
+    public void testNotifications_expandsNotificationPanel_legacy() {
+        setupWithMockContext();
         mSystemActionPerformer
                 .performSystemAction(AccessibilityService.GLOBAL_ACTION_NOTIFICATIONS);
         verify(mMockStatusBarManager).expandNotificationsPanel();
     }
 
     @Test
-    public void testQuickSettings_requestsQuickSettingsPanel() {
+    public void testQuickSettings_requestsQuickSettingsPanel_legacy() {
+        setupWithMockContext();
         mSystemActionPerformer
                 .performSystemAction(AccessibilityService.GLOBAL_ACTION_QUICK_SETTINGS);
         verify(mMockStatusBarManager).expandSettingsPanel();
     }
 
     @Test
-    public void testPowerDialog_requestsFromWindowManager() {
+    public void testRecentApps_legacy() {
+        setupWithRealContext();
+        mSystemActionPerformer.performSystemAction(AccessibilityService.GLOBAL_ACTION_RECENTS);
+        verify(mMockStatusBarManagerInternal).toggleRecentApps();
+    }
+
+    @Test
+    public void testPowerDialog_requestsFromWindowManager_legacy() {
+        setupWithMockContext();
         mSystemActionPerformer.performSystemAction(AccessibilityService.GLOBAL_ACTION_POWER_DIALOG);
         verify(mMockWindowManagerInternal).showGlobalActions();
     }
 
     @Test
-    public void testScreenshot_requestsFromScreenshotHelper() {
+    public void testToggleSplitScreen_legacy() {
+        setupWithRealContext();
+        mSystemActionPerformer.performSystemAction(
+                AccessibilityService.GLOBAL_ACTION_TOGGLE_SPLIT_SCREEN);
+        verify(mMockStatusBarManagerInternal).toggleSplitScreen();
+    }
+
+    @Test
+    public void testScreenshot_requestsFromScreenshotHelper_legacy() {
+        setupWithMockContext();
         mSystemActionPerformer.performSystemAction(
                 AccessibilityService.GLOBAL_ACTION_TAKE_SCREENSHOT);
         verify(mMockScreenshotHelper).takeScreenshot(
                 eq(android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN), anyBoolean(),
                 anyBoolean(), any(Handler.class), any());
     }
+
+    // PendingIntent is a final class and cannot be mocked. So we are using this
+    // Broadcast receiver to verify the registered remote action is called correctly.
+    private static final class TestBroadcastReceiver extends BroadcastReceiver {
+        private CountDownLatch mLatch;
+        private boolean mRegistered;
+        private final IntentFilter mFilter;
+
+        TestBroadcastReceiver(CountDownLatch latch) {
+            mLatch = latch;
+            mRegistered = false;
+            mFilter = new IntentFilter(INTENT_ACTION1);
+        }
+
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            mLatch.countDown();
+        }
+
+        void register(Context context) {
+            if (!mRegistered) {
+                context.registerReceiver(this, mFilter);
+                mRegistered = true;
+            }
+        }
+
+        void unregister(Context context) {
+            if (mRegistered) {
+                context.unregisterReceiver(this);
+            }
+        }
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/appsearch/impl/FakeIcingTest.java b/services/tests/servicestests/src/com/android/server/appsearch/impl/FakeIcingTest.java
index adef02e..6f2de7f 100644
--- a/services/tests/servicestests/src/com/android/server/appsearch/impl/FakeIcingTest.java
+++ b/services/tests/servicestests/src/com/android/server/appsearch/impl/FakeIcingTest.java
@@ -21,6 +21,7 @@
 
 import com.google.android.icing.proto.DocumentProto;
 import com.google.android.icing.proto.PropertyProto;
+import com.google.android.icing.proto.SearchResultProto;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -115,8 +116,9 @@
 
     private static List<String> queryGetUris(FakeIcing icing, String term) {
         List<String> uris = new ArrayList<>();
-        for (DocumentProto result : icing.query(term)) {
-            uris.add(result.getUri());
+        SearchResultProto results = icing.query(term);
+        for (SearchResultProto.ResultProto result : results.getResultsList()) {
+            uris.add(result.getDocument().getUri());
         }
         return uris;
     }
diff --git a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
index f8c87fc..7267976 100644
--- a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
@@ -73,36 +73,36 @@
     @Test
     public void testDisabledChangeDisabled() {
         CompatConfig pc = new CompatConfig();
-        pc.addChange(new CompatChange(1234L, "MY_CHANGE", -1, true));
+        pc.addChange(new CompatChange(1234L, "MY_CHANGE", -1, true, ""));
         assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 1))).isFalse();
     }
 
     @Test
     public void testTargetSdkChangeDisabled() {
         CompatConfig pc = new CompatConfig();
-        pc.addChange(new CompatChange(1234L, "MY_CHANGE", 2, false));
+        pc.addChange(new CompatChange(1234L, "MY_CHANGE", 2, false, null));
         assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 2))).isFalse();
     }
 
     @Test
     public void testTargetSdkChangeEnabled() {
         CompatConfig pc = new CompatConfig();
-        pc.addChange(new CompatChange(1234L, "MY_CHANGE", 2, false));
+        pc.addChange(new CompatChange(1234L, "MY_CHANGE", 2, false, ""));
         assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 3))).isTrue();
     }
 
     @Test
     public void testDisabledOverrideTargetSdkChange() {
         CompatConfig pc = new CompatConfig();
-        pc.addChange(new CompatChange(1234L, "MY_CHANGE", 2, true));
+        pc.addChange(new CompatChange(1234L, "MY_CHANGE", 2, true, null));
         assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 3))).isFalse();
     }
 
     @Test
     public void testGetDisabledChanges() {
         CompatConfig pc = new CompatConfig();
-        pc.addChange(new CompatChange(1234L, "MY_CHANGE", -1, true));
-        pc.addChange(new CompatChange(2345L, "OTHER_CHANGE", -1, false));
+        pc.addChange(new CompatChange(1234L, "MY_CHANGE", -1, true, null));
+        pc.addChange(new CompatChange(2345L, "OTHER_CHANGE", -1, false, null));
         assertThat(pc.getDisabledChanges(
                 makeAppInfo("com.some.package", 2))).asList().containsExactly(1234L);
     }
@@ -110,9 +110,9 @@
     @Test
     public void testGetDisabledChangesSorted() {
         CompatConfig pc = new CompatConfig();
-        pc.addChange(new CompatChange(1234L, "MY_CHANGE", 2, true));
-        pc.addChange(new CompatChange(123L, "OTHER_CHANGE", 2, true));
-        pc.addChange(new CompatChange(12L, "THIRD_CHANGE", 2, true));
+        pc.addChange(new CompatChange(1234L, "MY_CHANGE", 2, true, null));
+        pc.addChange(new CompatChange(123L, "OTHER_CHANGE", 2, true, null));
+        pc.addChange(new CompatChange(12L, "THIRD_CHANGE", 2, true, null));
         assertThat(pc.getDisabledChanges(
                 makeAppInfo("com.some.package", 2))).asList().containsExactly(12L, 123L, 1234L);
     }
@@ -120,7 +120,7 @@
     @Test
     public void testPackageOverrideEnabled() {
         CompatConfig pc = new CompatConfig();
-        pc.addChange(new CompatChange(1234L, "MY_CHANGE", -1, true)); // disabled
+        pc.addChange(new CompatChange(1234L, "MY_CHANGE", -1, true, null)); // disabled
         pc.addOverride(1234L, "com.some.package", true);
         assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 2))).isTrue();
         assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.other.package", 2))).isFalse();
@@ -129,7 +129,7 @@
     @Test
     public void testPackageOverrideDisabled() {
         CompatConfig pc = new CompatConfig();
-        pc.addChange(new CompatChange(1234L, "MY_CHANGE", -1, false));
+        pc.addChange(new CompatChange(1234L, "MY_CHANGE", -1, false, null));
         pc.addOverride(1234L, "com.some.package", false);
         assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 2))).isFalse();
         assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.other.package", 2))).isTrue();
@@ -152,7 +152,7 @@
     @Test
     public void testRemovePackageOverride() {
         CompatConfig pc = new CompatConfig();
-        pc.addChange(new CompatChange(1234L, "MY_CHANGE", -1, false));
+        pc.addChange(new CompatChange(1234L, "MY_CHANGE", -1, false, null));
         pc.addOverride(1234L, "com.some.package", false);
         pc.removeOverride(1234L, "com.some.package");
         assertThat(pc.isChangeEnabled(1234L, makeAppInfo("com.some.package", 2))).isTrue();
@@ -161,8 +161,8 @@
     @Test
     public void testLookupChangeId() {
         CompatConfig pc = new CompatConfig();
-        pc.addChange(new CompatChange(1234L, "MY_CHANGE", -1, false));
-        pc.addChange(new CompatChange(2345L, "ANOTHER_CHANGE", -1, false));
+        pc.addChange(new CompatChange(1234L, "MY_CHANGE", -1, false, null));
+        pc.addChange(new CompatChange(2345L, "ANOTHER_CHANGE", -1, false, null));
         assertThat(pc.lookupChangeId("MY_CHANGE")).isEqualTo(1234L);
     }
 
@@ -174,8 +174,9 @@
 
     @Test
     public void testReadConfig() {
-        Change[] changes = {new Change(1234L, "MY_CHANGE1", false, 2), new Change(1235L,
-                "MY_CHANGE2", true, null), new Change(1236L, "MY_CHANGE3", false, null)};
+        Change[] changes = {new Change(1234L, "MY_CHANGE1", false, 2, null), new Change(1235L,
+                "MY_CHANGE2", true, null, "description"), new Change(1236L, "MY_CHANGE3", false,
+                null, "")};
 
         File dir = createTempDir();
         writeChangesToFile(changes, new File(dir.getPath() + "/platform_compat_config.xml"));
@@ -191,9 +192,9 @@
 
     @Test
     public void testReadConfigMultipleFiles() {
-        Change[] changes1 = {new Change(1234L, "MY_CHANGE1", false, 2)};
-        Change[] changes2 = {new Change(1235L, "MY_CHANGE2", true, null), new Change(1236L,
-                "MY_CHANGE3", false, null)};
+        Change[] changes1 = {new Change(1234L, "MY_CHANGE1", false, 2, null)};
+        Change[] changes2 = {new Change(1235L, "MY_CHANGE2", true, null, ""), new Change(1236L,
+                "MY_CHANGE3", false, null, null)};
 
         File dir = createTempDir();
         writeChangesToFile(changes1,
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
index f86bacf..ac555fd 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java
@@ -23,6 +23,7 @@
 import android.app.PendingIntent;
 import android.app.backup.IBackupManager;
 import android.app.timedetector.TimeDetector;
+import android.app.timezonedetector.TimeZoneDetector;
 import android.app.usage.UsageStatsManagerInternal;
 import android.content.Context;
 import android.content.Intent;
@@ -235,6 +236,11 @@
         }
 
         @Override
+        TimeZoneDetector getTimeZoneDetector() {
+            return services.timeZoneDetector;
+        }
+
+        @Override
         LockPatternUtils newLockPatternUtils() {
             return services.lockPatternUtils;
         }
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 0590020..06b8716 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -64,6 +64,8 @@
 import android.app.admin.DevicePolicyManagerInternal;
 import android.app.admin.PasswordMetrics;
 import android.app.timedetector.ManualTimeSuggestion;
+import android.app.timezonedetector.ManualTimeZoneSuggestion;
+import android.app.timezonedetector.TimeZoneDetector;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
 import android.content.Intent;
@@ -138,6 +140,8 @@
             permission.MANAGE_USERS, permission.INTERACT_ACROSS_USERS_FULL);
     public static final String NOT_DEVICE_OWNER_MSG = "does not own the device";
     public static final String NOT_PROFILE_OWNER_MSG = "does not own the profile";
+    public static final String NOT_ORG_OWNED_PROFILE_OWNER_MSG =
+            "not the profile owner on organization-owned device";
     public static final String ONGOING_CALL_MSG = "ongoing call on the device";
 
     // TODO replace all instances of this with explicit {@link #mServiceContext}.
@@ -2114,12 +2118,14 @@
         assertTrue(dpm.isAdminActive(admin1));
 
         // Test 2. Caller has DA, but not DO.
-        assertExpectException(SecurityException.class, /* messageRegex= */ NOT_DEVICE_OWNER_MSG,
+        assertExpectException(SecurityException.class,
+                /* messageRegex= */ NOT_ORG_OWNED_PROFILE_OWNER_MSG,
                 () -> dpm.getWifiMacAddress(admin1));
 
         // Test 3. Caller has PO, but not DO.
         assertTrue(dpm.setProfileOwner(admin1, null, UserHandle.USER_SYSTEM));
-        assertExpectException(SecurityException.class, /* messageRegex= */ NOT_DEVICE_OWNER_MSG,
+        assertExpectException(SecurityException.class,
+                /* messageRegex= */ NOT_ORG_OWNED_PROFILE_OWNER_MSG,
                 () -> dpm.getWifiMacAddress(admin1));
 
         // Remove PO.
@@ -2141,6 +2147,15 @@
         assertEquals("11:22:33:44:55:66", dpm.getWifiMacAddress(admin1));
     }
 
+    public void testGetMacAddressByOrgOwnedPO() throws Exception {
+        setupProfileOwner();
+        configureProfileOwnerOfOrgOwnedDevice(admin1, DpmMockContext.CALLER_USER_HANDLE);
+
+        final String[] macAddresses = new String[]{"11:22:33:44:55:66"};
+        when(getServices().wifiManager.getFactoryMacAddresses()).thenReturn(macAddresses);
+        assertEquals("11:22:33:44:55:66", dpm.getWifiMacAddress(admin1));
+    }
+
     public void testReboot() throws Exception {
         mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS);
         mContext.callerPermissions.add(permission.MANAGE_PROFILE_AND_DEVICE_OWNERS);
@@ -3694,7 +3709,9 @@
         mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
         setupDeviceOwner();
         dpm.setTimeZone(admin1, "Asia/Shanghai");
-        verify(getServices().alarmManager).setTimeZone("Asia/Shanghai");
+        ManualTimeZoneSuggestion suggestion =
+                TimeZoneDetector.createManualTimeZoneSuggestion("Asia/Shanghai", "Test debug info");
+        verify(getServices().timeZoneDetector).suggestManualTimeZone(suggestion);
     }
 
     public void testSetTimeZoneFailWithPO() throws Exception {
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
index c927364..6a0d926 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java
@@ -32,6 +32,7 @@
 import android.app.NotificationManager;
 import android.app.backup.IBackupManager;
 import android.app.timedetector.TimeDetector;
+import android.app.timezonedetector.TimeZoneDetector;
 import android.app.usage.UsageStatsManagerInternal;
 import android.content.BroadcastReceiver;
 import android.content.ContentValues;
@@ -113,6 +114,7 @@
     public final AccountManager accountManager;
     public final AlarmManager alarmManager;
     public final TimeDetector timeDetector;
+    public final TimeZoneDetector timeZoneDetector;
     public final KeyChain.KeyChainConnection keyChainConnection;
     /** Note this is a partial mock, not a real mock. */
     public final PackageManager packageManager;
@@ -155,6 +157,7 @@
         accountManager = mock(AccountManager.class);
         alarmManager = mock(AlarmManager.class);
         timeDetector = mock(TimeDetector.class);
+        timeZoneDetector = mock(TimeZoneDetector.class);
         keyChainConnection = mock(KeyChain.KeyChainConnection.class, RETURNS_DEEP_STUBS);
 
         // Package manager is huge, so we use a partial mock instead.
diff --git a/services/tests/servicestests/src/com/android/server/integrity/parser/RuleBinaryParserTest.java b/services/tests/servicestests/src/com/android/server/integrity/parser/RuleBinaryParserTest.java
new file mode 100644
index 0000000..88b6d70
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/integrity/parser/RuleBinaryParserTest.java
@@ -0,0 +1,628 @@
+/*
+ * 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.parser;
+
+import static com.android.server.integrity.model.ComponentBitSize.ATOMIC_FORMULA_START;
+import static com.android.server.integrity.model.ComponentBitSize.COMPOUND_FORMULA_END;
+import static com.android.server.integrity.model.ComponentBitSize.COMPOUND_FORMULA_START;
+import static com.android.server.integrity.model.ComponentBitSize.CONNECTOR_BITS;
+import static com.android.server.integrity.model.ComponentBitSize.DEFAULT_FORMAT_VERSION;
+import static com.android.server.integrity.model.ComponentBitSize.EFFECT_BITS;
+import static com.android.server.integrity.model.ComponentBitSize.FORMAT_VERSION_BITS;
+import static com.android.server.integrity.model.ComponentBitSize.KEY_BITS;
+import static com.android.server.integrity.model.ComponentBitSize.OPERATOR_BITS;
+import static com.android.server.integrity.model.ComponentBitSize.SEPARATOR_BITS;
+import static com.android.server.integrity.model.ComponentBitSize.VALUE_SIZE_BITS;
+import static com.android.server.integrity.utils.TestUtils.getBits;
+import static com.android.server.integrity.utils.TestUtils.getBytes;
+import static com.android.server.integrity.utils.TestUtils.getValueBits;
+import static com.android.server.testutils.TestUtils.assertExpectException;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.integrity.AtomicFormula;
+import android.content.integrity.CompoundFormula;
+import android.content.integrity.Rule;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+@RunWith(JUnit4.class)
+public class RuleBinaryParserTest {
+
+    private static final String COMPOUND_FORMULA_START_BITS =
+            getBits(COMPOUND_FORMULA_START, SEPARATOR_BITS);
+    private static final String COMPOUND_FORMULA_END_BITS =
+            getBits(COMPOUND_FORMULA_END, SEPARATOR_BITS);
+    private static final String ATOMIC_FORMULA_START_BITS =
+            getBits(ATOMIC_FORMULA_START, SEPARATOR_BITS);
+    private static final int INVALID_FORMULA_SEPARATOR_VALUE = 3;
+    private static final String INVALID_FORMULA_SEPARATOR_BITS =
+            getBits(INVALID_FORMULA_SEPARATOR_VALUE, SEPARATOR_BITS);
+
+    private static final String NOT = getBits(CompoundFormula.NOT, CONNECTOR_BITS);
+    private static final String AND = getBits(CompoundFormula.AND, CONNECTOR_BITS);
+    private static final String OR = getBits(CompoundFormula.OR, CONNECTOR_BITS);
+    private static final int INVALID_CONNECTOR_VALUE = 3;
+    private static final String INVALID_CONNECTOR =
+            getBits(INVALID_CONNECTOR_VALUE, CONNECTOR_BITS);
+
+    private static final String PACKAGE_NAME = getBits(AtomicFormula.PACKAGE_NAME, KEY_BITS);
+    private static final String APP_CERTIFICATE = getBits(AtomicFormula.APP_CERTIFICATE, KEY_BITS);
+    private static final String VERSION_CODE = getBits(AtomicFormula.VERSION_CODE, KEY_BITS);
+    private static final String PRE_INSTALLED = getBits(AtomicFormula.PRE_INSTALLED, KEY_BITS);
+    private static final int INVALID_KEY_VALUE = 6;
+    private static final String INVALID_KEY = getBits(INVALID_KEY_VALUE, KEY_BITS);
+
+    private static final String EQ = getBits(AtomicFormula.EQ, OPERATOR_BITS);
+    private static final int INVALID_OPERATOR_VALUE = 5;
+    private static final String INVALID_OPERATOR = getBits(INVALID_OPERATOR_VALUE, OPERATOR_BITS);
+
+    private static final String IS_NOT_HASHED = "0";
+
+    private static final String DENY = getBits(Rule.DENY, EFFECT_BITS);
+    private static final int INVALID_EFFECT_VALUE = 5;
+    private static final String INVALID_EFFECT = getBits(INVALID_EFFECT_VALUE, EFFECT_BITS);
+
+    private static final String START_BIT = "1";
+    private static final String END_BIT = "1";
+    private static final String INVALID_MARKER_BIT = "0";
+
+    private static final byte[] DEFAULT_FORMAT_VERSION_BYTES =
+            getBytes(getBits(DEFAULT_FORMAT_VERSION, FORMAT_VERSION_BITS));
+
+    @Test
+    public void testBinaryStream_validCompoundFormula() throws Exception {
+        String packageName = "com.test.app";
+        String ruleBits =
+                START_BIT
+                        + COMPOUND_FORMULA_START_BITS
+                        + NOT
+                        + ATOMIC_FORMULA_START_BITS
+                        + PACKAGE_NAME
+                        + EQ
+                        + IS_NOT_HASHED
+                        + getBits(packageName.length(), VALUE_SIZE_BITS)
+                        + getValueBits(packageName)
+                        + COMPOUND_FORMULA_END_BITS
+                        + DENY
+                        + END_BIT;
+        byte[] ruleBytes = getBytes(ruleBits);
+        ByteBuffer rule =
+                ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length);
+        rule.put(DEFAULT_FORMAT_VERSION_BYTES);
+        rule.put(ruleBytes);
+        RuleParser binaryParser = new RuleBinaryParser();
+        InputStream inputStream = new ByteArrayInputStream(rule.array());
+        Rule expectedRule =
+                new Rule(
+                        new CompoundFormula(
+                                CompoundFormula.NOT,
+                                Collections.singletonList(
+                                        new AtomicFormula.StringAtomicFormula(
+                                                AtomicFormula.PACKAGE_NAME,
+                                                packageName,
+                                                /* isHashedValue= */ false))),
+                        Rule.DENY);
+
+        List<Rule> rules = binaryParser.parse(inputStream);
+
+        assertThat(rules).isEqualTo(Collections.singletonList(expectedRule));
+    }
+
+    @Test
+    public void testBinaryString_validCompoundFormula_notConnector() throws Exception {
+        String packageName = "com.test.app";
+        String ruleBits =
+                START_BIT
+                        + COMPOUND_FORMULA_START_BITS
+                        + NOT
+                        + ATOMIC_FORMULA_START_BITS
+                        + PACKAGE_NAME
+                        + EQ
+                        + IS_NOT_HASHED
+                        + getBits(packageName.length(), VALUE_SIZE_BITS)
+                        + getValueBits(packageName)
+                        + COMPOUND_FORMULA_END_BITS
+                        + DENY
+                        + END_BIT;
+        byte[] ruleBytes = getBytes(ruleBits);
+        ByteBuffer rule =
+                ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length);
+        rule.put(DEFAULT_FORMAT_VERSION_BYTES);
+        rule.put(ruleBytes);
+        RuleParser binaryParser = new RuleBinaryParser();
+        Rule expectedRule =
+                new Rule(
+                        new CompoundFormula(
+                                CompoundFormula.NOT,
+                                Collections.singletonList(
+                                        new AtomicFormula.StringAtomicFormula(
+                                                AtomicFormula.PACKAGE_NAME,
+                                                packageName,
+                                                /* isHashedValue= */ false))),
+                        Rule.DENY);
+
+        List<Rule> rules = binaryParser.parse(rule.array());
+
+        assertThat(rules).isEqualTo(Collections.singletonList(expectedRule));
+    }
+
+    @Test
+    public void testBinaryString_validCompoundFormula_andConnector() throws Exception {
+        String packageName = "com.test.app";
+        String appCertificate = "test_cert";
+        String ruleBits =
+                START_BIT
+                        + COMPOUND_FORMULA_START_BITS
+                        + AND
+                        + ATOMIC_FORMULA_START_BITS
+                        + PACKAGE_NAME
+                        + EQ
+                        + IS_NOT_HASHED
+                        + getBits(packageName.length(), VALUE_SIZE_BITS)
+                        + getValueBits(packageName)
+                        + ATOMIC_FORMULA_START_BITS
+                        + APP_CERTIFICATE
+                        + EQ
+                        + IS_NOT_HASHED
+                        + getBits(appCertificate.length(), VALUE_SIZE_BITS)
+                        + getValueBits(appCertificate)
+                        + COMPOUND_FORMULA_END_BITS
+                        + DENY
+                        + END_BIT;
+        byte[] ruleBytes = getBytes(ruleBits);
+        ByteBuffer rule =
+                ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length);
+        rule.put(DEFAULT_FORMAT_VERSION_BYTES);
+        rule.put(ruleBytes);
+        RuleParser binaryParser = new RuleBinaryParser();
+        Rule expectedRule =
+                new Rule(
+                        new CompoundFormula(
+                                CompoundFormula.AND,
+                                Arrays.asList(
+                                        new AtomicFormula.StringAtomicFormula(
+                                                AtomicFormula.PACKAGE_NAME,
+                                                packageName,
+                                                /* isHashedValue= */ false),
+                                        new AtomicFormula.StringAtomicFormula(
+                                                AtomicFormula.APP_CERTIFICATE,
+                                                appCertificate,
+                                                /* isHashedValue= */ false))),
+                        Rule.DENY);
+        List<Rule> rules = binaryParser.parse(rule.array());
+
+        assertThat(rules).isEqualTo(Collections.singletonList(expectedRule));
+    }
+
+    @Test
+    public void testBinaryString_validCompoundFormula_orConnector() throws Exception {
+        String packageName = "com.test.app";
+        String appCertificate = "test_cert";
+        String ruleBits =
+                START_BIT
+                        + COMPOUND_FORMULA_START_BITS
+                        + OR
+                        + ATOMIC_FORMULA_START_BITS
+                        + PACKAGE_NAME
+                        + EQ
+                        + IS_NOT_HASHED
+                        + getBits(packageName.length(), VALUE_SIZE_BITS)
+                        + getValueBits(packageName)
+                        + ATOMIC_FORMULA_START_BITS
+                        + APP_CERTIFICATE
+                        + EQ
+                        + IS_NOT_HASHED
+                        + getBits(appCertificate.length(), VALUE_SIZE_BITS)
+                        + getValueBits(appCertificate)
+                        + COMPOUND_FORMULA_END_BITS
+                        + DENY
+                        + END_BIT;
+        byte[] ruleBytes = getBytes(ruleBits);
+        ByteBuffer rule =
+                ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length);
+        rule.put(DEFAULT_FORMAT_VERSION_BYTES);
+        rule.put(ruleBytes);
+        RuleParser binaryParser = new RuleBinaryParser();
+        Rule expectedRule =
+                new Rule(
+                        new CompoundFormula(
+                                CompoundFormula.OR,
+                                Arrays.asList(
+                                        new AtomicFormula.StringAtomicFormula(
+                                                AtomicFormula.PACKAGE_NAME,
+                                                packageName,
+                                                /* isHashedValue= */ false),
+                                        new AtomicFormula.StringAtomicFormula(
+                                                AtomicFormula.APP_CERTIFICATE,
+                                                appCertificate,
+                                                /* isHashedValue= */ false))),
+                        Rule.DENY);
+
+        List<Rule> rules = binaryParser.parse(rule.array());
+
+        assertThat(rules).isEqualTo(Collections.singletonList(expectedRule));
+    }
+
+    @Test
+    public void testBinaryString_validAtomicFormula_stringValue() throws Exception {
+        String packageName = "com.test.app";
+        String ruleBits =
+                START_BIT
+                        + ATOMIC_FORMULA_START_BITS
+                        + PACKAGE_NAME
+                        + EQ
+                        + IS_NOT_HASHED
+                        + getBits(packageName.length(), VALUE_SIZE_BITS)
+                        + getValueBits(packageName)
+                        + DENY
+                        + END_BIT;
+        byte[] ruleBytes = getBytes(ruleBits);
+        ByteBuffer rule =
+                ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length);
+        rule.put(DEFAULT_FORMAT_VERSION_BYTES);
+        rule.put(ruleBytes);
+        RuleParser binaryParser = new RuleBinaryParser();
+        Rule expectedRule =
+                new Rule(
+                        new AtomicFormula.StringAtomicFormula(
+                                AtomicFormula.PACKAGE_NAME,
+                                packageName,
+                                /* isHashedValue= */ false),
+                        Rule.DENY);
+
+        List<Rule> rules = binaryParser.parse(rule.array());
+
+        assertThat(rules).isEqualTo(Collections.singletonList(expectedRule));
+    }
+
+    @Test
+    public void testBinaryString_validAtomicFormula_integerValue() throws Exception {
+        String versionCode = "1";
+        String ruleBits =
+                START_BIT
+                        + ATOMIC_FORMULA_START_BITS
+                        + VERSION_CODE
+                        + EQ
+                        + IS_NOT_HASHED
+                        + getBits(versionCode.length(), VALUE_SIZE_BITS)
+                        + getValueBits(versionCode)
+                        + DENY
+                        + END_BIT;
+        byte[] ruleBytes = getBytes(ruleBits);
+        ByteBuffer rule =
+                ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length);
+        rule.put(DEFAULT_FORMAT_VERSION_BYTES);
+        rule.put(ruleBytes);
+        RuleParser binaryParser = new RuleBinaryParser();
+        Rule expectedRule =
+                new Rule(
+                        new AtomicFormula.IntAtomicFormula(
+                                AtomicFormula.VERSION_CODE, AtomicFormula.EQ, 1),
+                        Rule.DENY);
+
+        List<Rule> rules = binaryParser.parse(rule.array());
+
+        assertThat(rules).isEqualTo(Collections.singletonList(expectedRule));
+    }
+
+    @Test
+    public void testBinaryString_validAtomicFormula_booleanValue() throws Exception {
+        String isPreInstalled = "1";
+        String ruleBits =
+                START_BIT
+                        + ATOMIC_FORMULA_START_BITS
+                        + PRE_INSTALLED
+                        + EQ
+                        + IS_NOT_HASHED
+                        + getBits(isPreInstalled.length(), VALUE_SIZE_BITS)
+                        + getValueBits(isPreInstalled)
+                        + DENY
+                        + END_BIT;
+        byte[] ruleBytes = getBytes(ruleBits);
+        ByteBuffer rule =
+                ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length);
+        rule.put(DEFAULT_FORMAT_VERSION_BYTES);
+        rule.put(ruleBytes);
+        RuleParser binaryParser = new RuleBinaryParser();
+        Rule expectedRule =
+                new Rule(
+                        new AtomicFormula.BooleanAtomicFormula(AtomicFormula.PRE_INSTALLED, true),
+                        Rule.DENY);
+
+        List<Rule> rules = binaryParser.parse(rule.array());
+
+        assertThat(rules).isEqualTo(Collections.singletonList(expectedRule));
+    }
+
+    @Test
+    public void testBinaryString_invalidAtomicFormula() throws Exception {
+        String versionCode = "test";
+        String ruleBits =
+                START_BIT
+                        + ATOMIC_FORMULA_START_BITS
+                        + VERSION_CODE
+                        + EQ
+                        + IS_NOT_HASHED
+                        + getBits(versionCode.length(), VALUE_SIZE_BITS)
+                        + getValueBits(versionCode)
+                        + DENY
+                        + END_BIT;
+        byte[] ruleBytes = getBytes(ruleBits);
+        ByteBuffer rule =
+                ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length);
+        rule.put(DEFAULT_FORMAT_VERSION_BYTES);
+        rule.put(ruleBytes);
+        RuleParser binaryParser = new RuleBinaryParser();
+
+        assertExpectException(
+                RuleParseException.class,
+                /* expectedExceptionMessageRegex */ "For input string:",
+                () -> binaryParser.parse(rule.array()));
+    }
+
+    @Test
+    public void testBinaryString_withNoRuleList() throws RuleParseException {
+        ByteBuffer rule = ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length);
+        rule.put(DEFAULT_FORMAT_VERSION_BYTES);
+        RuleParser binaryParser = new RuleBinaryParser();
+
+        List<Rule> rules = binaryParser.parse(rule.array());
+
+        assertThat(rules).isEmpty();
+    }
+
+    @Test
+    public void testBinaryString_withEmptyRule() throws RuleParseException {
+        String ruleBits = START_BIT;
+        byte[] ruleBytes = getBytes(ruleBits);
+        ByteBuffer rule =
+                ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length);
+        rule.put(DEFAULT_FORMAT_VERSION_BYTES);
+        rule.put(ruleBytes);
+        RuleParser binaryParser = new RuleBinaryParser();
+
+        assertExpectException(
+                RuleParseException.class,
+                /* expectedExceptionMessageRegex */ "Invalid byte index",
+                () -> binaryParser.parse(rule.array()));
+    }
+
+    @Test
+    public void testBinaryString_invalidCompoundFormula_invalidNumberOfFormulas() throws Exception {
+        String packageName = "com.test.app";
+        String appCertificate = "test_cert";
+        String ruleBits =
+                START_BIT
+                        + COMPOUND_FORMULA_START_BITS
+                        + NOT
+                        + ATOMIC_FORMULA_START_BITS
+                        + PACKAGE_NAME
+                        + EQ
+                        + IS_NOT_HASHED
+                        + getBits(packageName.length(), VALUE_SIZE_BITS)
+                        + getValueBits(packageName)
+                        + ATOMIC_FORMULA_START_BITS
+                        + APP_CERTIFICATE
+                        + EQ
+                        + IS_NOT_HASHED
+                        + getBits(appCertificate.length(), VALUE_SIZE_BITS)
+                        + getValueBits(appCertificate)
+                        + COMPOUND_FORMULA_END_BITS
+                        + DENY
+                        + END_BIT;
+        byte[] ruleBytes = getBytes(ruleBits);
+        ByteBuffer rule =
+                ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length);
+        rule.put(DEFAULT_FORMAT_VERSION_BYTES);
+        rule.put(ruleBytes);
+        RuleParser binaryParser = new RuleBinaryParser();
+
+        assertExpectException(
+                RuleParseException.class,
+                /* expectedExceptionMessageRegex */ "Connector NOT must have 1 formula only",
+                () -> binaryParser.parse(rule.array()));
+    }
+
+    @Test
+    public void testBinaryString_invalidRule_invalidOperator() throws Exception {
+        String versionCode = "1";
+        String ruleBits =
+                START_BIT
+                        + COMPOUND_FORMULA_START_BITS
+                        + NOT
+                        + ATOMIC_FORMULA_START_BITS
+                        + VERSION_CODE
+                        + INVALID_OPERATOR
+                        + IS_NOT_HASHED
+                        + getBits(versionCode.length(), VALUE_SIZE_BITS)
+                        + getValueBits(versionCode)
+                        + COMPOUND_FORMULA_END_BITS
+                        + DENY
+                        + END_BIT;
+        byte[] ruleBytes = getBytes(ruleBits);
+        ByteBuffer rule =
+                ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length);
+        rule.put(DEFAULT_FORMAT_VERSION_BYTES);
+        rule.put(ruleBytes);
+        RuleParser binaryParser = new RuleBinaryParser();
+
+        assertExpectException(
+                RuleParseException.class,
+                /* expectedExceptionMessageRegex */ String.format(
+                        "Unknown operator: %d", INVALID_OPERATOR_VALUE),
+                () -> binaryParser.parse(rule.array()));
+    }
+
+    @Test
+    public void testBinaryString_invalidRule_invalidEffect() throws Exception {
+        String packageName = "com.test.app";
+        String ruleBits =
+                START_BIT
+                        + COMPOUND_FORMULA_START_BITS
+                        + NOT
+                        + ATOMIC_FORMULA_START_BITS
+                        + PACKAGE_NAME
+                        + EQ
+                        + IS_NOT_HASHED
+                        + getBits(packageName.length(), VALUE_SIZE_BITS)
+                        + getValueBits(packageName)
+                        + COMPOUND_FORMULA_END_BITS
+                        + INVALID_EFFECT
+                        + END_BIT;
+        byte[] ruleBytes = getBytes(ruleBits);
+        ByteBuffer rule =
+                ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length);
+        rule.put(DEFAULT_FORMAT_VERSION_BYTES);
+        rule.put(ruleBytes);
+        RuleParser binaryParser = new RuleBinaryParser();
+
+        assertExpectException(
+                RuleParseException.class,
+                /* expectedExceptionMessageRegex */ String.format(
+                        "Unknown effect: %d", INVALID_EFFECT_VALUE),
+                () -> binaryParser.parse(rule.array()));
+    }
+
+    @Test
+    public void testBinaryString_invalidRule_invalidConnector() throws Exception {
+        String packageName = "com.test.app";
+        String ruleBits =
+                START_BIT
+                        + COMPOUND_FORMULA_START_BITS
+                        + INVALID_CONNECTOR
+                        + ATOMIC_FORMULA_START_BITS
+                        + PACKAGE_NAME
+                        + EQ
+                        + IS_NOT_HASHED
+                        + getBits(packageName.length(), VALUE_SIZE_BITS)
+                        + getValueBits(packageName)
+                        + COMPOUND_FORMULA_END_BITS
+                        + DENY
+                        + END_BIT;
+        byte[] ruleBytes = getBytes(ruleBits);
+        ByteBuffer rule =
+                ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length);
+        rule.put(DEFAULT_FORMAT_VERSION_BYTES);
+        rule.put(ruleBytes);
+        RuleParser binaryParser = new RuleBinaryParser();
+
+        assertExpectException(
+                RuleParseException.class,
+                /* expectedExceptionMessageRegex */ String.format(
+                        "Unknown connector: %d", INVALID_CONNECTOR_VALUE),
+                () -> binaryParser.parse(rule.array()));
+    }
+
+    @Test
+    public void testBinaryString_invalidRule_invalidKey() throws Exception {
+        String packageName = "com.test.app";
+        String ruleBits =
+                START_BIT
+                        + COMPOUND_FORMULA_START_BITS
+                        + NOT
+                        + ATOMIC_FORMULA_START_BITS
+                        + INVALID_KEY
+                        + EQ
+                        + IS_NOT_HASHED
+                        + getBits(packageName.length(), VALUE_SIZE_BITS)
+                        + getValueBits(packageName)
+                        + COMPOUND_FORMULA_END_BITS
+                        + DENY
+                        + END_BIT;
+        byte[] ruleBytes = getBytes(ruleBits);
+        ByteBuffer rule =
+                ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length);
+        rule.put(DEFAULT_FORMAT_VERSION_BYTES);
+        rule.put(ruleBytes);
+        RuleParser binaryParser = new RuleBinaryParser();
+
+        assertExpectException(
+                RuleParseException.class,
+                /* expectedExceptionMessageRegex */ String.format(
+                        "Unknown key: %d", INVALID_KEY_VALUE),
+                () -> binaryParser.parse(rule.array()));
+    }
+
+    @Test
+    public void testBinaryString_invalidRule_invalidSeparator() throws Exception {
+        String packageName = "com.test.app";
+        String ruleBits =
+                START_BIT
+                        + INVALID_FORMULA_SEPARATOR_BITS
+                        + NOT
+                        + ATOMIC_FORMULA_START_BITS
+                        + PACKAGE_NAME
+                        + EQ
+                        + IS_NOT_HASHED
+                        + getBits(packageName.length(), VALUE_SIZE_BITS)
+                        + getValueBits(packageName)
+                        + COMPOUND_FORMULA_END_BITS
+                        + DENY
+                        + END_BIT;
+        byte[] ruleBytes = getBytes(ruleBits);
+        ByteBuffer rule =
+                ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length);
+        rule.put(DEFAULT_FORMAT_VERSION_BYTES);
+        rule.put(ruleBytes);
+        RuleParser binaryParser = new RuleBinaryParser();
+
+        assertExpectException(
+                RuleParseException.class,
+                /* expectedExceptionMessageRegex */ String.format(
+                        "Unknown formula separator: %d", INVALID_FORMULA_SEPARATOR_VALUE),
+                () -> binaryParser.parse(rule.array()));
+    }
+
+    @Test
+    public void testBinaryString_invalidRule_invalidEndMarker() throws Exception {
+        String packageName = "com.test.app";
+        String ruleBits =
+                START_BIT
+                        + COMPOUND_FORMULA_START_BITS
+                        + NOT
+                        + ATOMIC_FORMULA_START_BITS
+                        + PACKAGE_NAME
+                        + EQ
+                        + IS_NOT_HASHED
+                        + getBits(packageName.length(), VALUE_SIZE_BITS)
+                        + getValueBits(packageName)
+                        + COMPOUND_FORMULA_END_BITS
+                        + DENY
+                        + INVALID_MARKER_BIT;
+        byte[] ruleBytes = getBytes(ruleBits);
+        ByteBuffer rule =
+                ByteBuffer.allocate(DEFAULT_FORMAT_VERSION_BYTES.length + ruleBytes.length);
+        rule.put(DEFAULT_FORMAT_VERSION_BYTES);
+        rule.put(ruleBytes);
+        RuleParser binaryParser = new RuleBinaryParser();
+
+        assertExpectException(
+                RuleParseException.class,
+                /* expectedExceptionMessageRegex */ "A rule must end with a '1' bit",
+                () -> binaryParser.parse(rule.array()));
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleBinarySerializerTest.java b/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleBinarySerializerTest.java
new file mode 100644
index 0000000..901277d
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleBinarySerializerTest.java
@@ -0,0 +1,444 @@
+/*
+ * 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.serializer;
+
+import static com.android.server.integrity.model.ComponentBitSize.ATOMIC_FORMULA_START;
+import static com.android.server.integrity.model.ComponentBitSize.COMPOUND_FORMULA_END;
+import static com.android.server.integrity.model.ComponentBitSize.COMPOUND_FORMULA_START;
+import static com.android.server.integrity.model.ComponentBitSize.CONNECTOR_BITS;
+import static com.android.server.integrity.model.ComponentBitSize.DEFAULT_FORMAT_VERSION;
+import static com.android.server.integrity.model.ComponentBitSize.EFFECT_BITS;
+import static com.android.server.integrity.model.ComponentBitSize.FORMAT_VERSION_BITS;
+import static com.android.server.integrity.model.ComponentBitSize.KEY_BITS;
+import static com.android.server.integrity.model.ComponentBitSize.OPERATOR_BITS;
+import static com.android.server.integrity.model.ComponentBitSize.SEPARATOR_BITS;
+import static com.android.server.integrity.model.ComponentBitSize.VALUE_SIZE_BITS;
+import static com.android.server.integrity.utils.TestUtils.getBits;
+import static com.android.server.integrity.utils.TestUtils.getBytes;
+import static com.android.server.integrity.utils.TestUtils.getValueBits;
+import static com.android.server.testutils.TestUtils.assertExpectException;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.integrity.AppInstallMetadata;
+import android.content.integrity.AtomicFormula;
+import android.content.integrity.CompoundFormula;
+import android.content.integrity.Formula;
+import android.content.integrity.Rule;
+
+import androidx.annotation.NonNull;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.io.ByteArrayOutputStream;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Optional;
+
+@RunWith(JUnit4.class)
+public class RuleBinarySerializerTest {
+
+    private static final String COMPOUND_FORMULA_START_BITS =
+            getBits(COMPOUND_FORMULA_START, SEPARATOR_BITS);
+    private static final String COMPOUND_FORMULA_END_BITS =
+            getBits(COMPOUND_FORMULA_END, SEPARATOR_BITS);
+    private static final String ATOMIC_FORMULA_START_BITS =
+            getBits(ATOMIC_FORMULA_START, SEPARATOR_BITS);
+
+    private static final String NOT = getBits(CompoundFormula.NOT, CONNECTOR_BITS);
+    private static final String AND = getBits(CompoundFormula.AND, CONNECTOR_BITS);
+    private static final String OR = getBits(CompoundFormula.OR, CONNECTOR_BITS);
+
+    private static final String PACKAGE_NAME = getBits(AtomicFormula.PACKAGE_NAME, KEY_BITS);
+    private static final String APP_CERTIFICATE = getBits(AtomicFormula.APP_CERTIFICATE, KEY_BITS);
+    private static final String VERSION_CODE = getBits(AtomicFormula.VERSION_CODE, KEY_BITS);
+    private static final String PRE_INSTALLED = getBits(AtomicFormula.PRE_INSTALLED, KEY_BITS);
+
+    private static final String EQ = getBits(AtomicFormula.EQ, OPERATOR_BITS);
+
+    private static final String IS_NOT_HASHED = "0";
+
+    private static final String DENY = getBits(Rule.DENY, EFFECT_BITS);
+
+    private static final String START_BIT = "1";
+    private static final String END_BIT = "1";
+
+    private static final byte[] DEFAULT_FORMAT_VERSION_BYTES =
+            getBytes(getBits(DEFAULT_FORMAT_VERSION, FORMAT_VERSION_BITS));
+
+    @Test
+    public void testBinaryString_serializeEmptyRule() throws Exception {
+        Rule rule = null;
+        RuleSerializer binarySerializer = new RuleBinarySerializer();
+
+        assertExpectException(
+                RuleSerializeException.class,
+                /* expectedExceptionMessageRegex= */ "Null rule can not be serialized",
+                () ->
+                        binarySerializer.serialize(
+                                Collections.singletonList(rule),
+                                /* formatVersion= */ Optional.empty()));
+    }
+
+    @Test
+    public void testBinaryStream_serializeValidCompoundFormula() throws Exception {
+        String packageName = "com.test.app";
+        Rule rule =
+                new Rule(
+                        new CompoundFormula(
+                                CompoundFormula.NOT,
+                                Collections.singletonList(
+                                        new AtomicFormula.StringAtomicFormula(
+                                                AtomicFormula.PACKAGE_NAME,
+                                                packageName,
+                                                /* isHashedValue= */ false))),
+                        Rule.DENY);
+        RuleSerializer binarySerializer = new RuleBinarySerializer();
+        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+        String expectedBits =
+                START_BIT
+                        + COMPOUND_FORMULA_START_BITS
+                        + NOT
+                        + ATOMIC_FORMULA_START_BITS
+                        + PACKAGE_NAME
+                        + EQ
+                        + IS_NOT_HASHED
+                        + getBits(packageName.length(), VALUE_SIZE_BITS)
+                        + getValueBits(packageName)
+                        + COMPOUND_FORMULA_END_BITS
+                        + DENY
+                        + END_BIT;
+        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+        byteArrayOutputStream.write(DEFAULT_FORMAT_VERSION_BYTES);
+        byteArrayOutputStream.write(getBytes(expectedBits));
+        byte[] expectedRules = byteArrayOutputStream.toByteArray();
+
+        binarySerializer.serialize(
+                Collections.singletonList(rule),
+                /* formatVersion= */ Optional.empty(),
+                outputStream);
+
+        byte[] actualRules = outputStream.toByteArray();
+        assertThat(actualRules).isEqualTo(expectedRules);
+    }
+
+    @Test
+    public void testBinaryString_serializeValidCompoundFormula_notConnector() throws Exception {
+        String packageName = "com.test.app";
+        Rule rule =
+                new Rule(
+                        new CompoundFormula(
+                                CompoundFormula.NOT,
+                                Collections.singletonList(
+                                        new AtomicFormula.StringAtomicFormula(
+                                                AtomicFormula.PACKAGE_NAME,
+                                                packageName,
+                                                /* isHashedValue= */ false))),
+                        Rule.DENY);
+        RuleSerializer binarySerializer = new RuleBinarySerializer();
+        String expectedBits =
+                START_BIT
+                        + COMPOUND_FORMULA_START_BITS
+                        + NOT
+                        + ATOMIC_FORMULA_START_BITS
+                        + PACKAGE_NAME
+                        + EQ
+                        + IS_NOT_HASHED
+                        + getBits(packageName.length(), VALUE_SIZE_BITS)
+                        + getValueBits(packageName)
+                        + COMPOUND_FORMULA_END_BITS
+                        + DENY
+                        + END_BIT;
+        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+        byteArrayOutputStream.write(DEFAULT_FORMAT_VERSION_BYTES);
+        byteArrayOutputStream.write(getBytes(expectedBits));
+        byte[] expectedRules = byteArrayOutputStream.toByteArray();
+
+        byte[] actualRules =
+                binarySerializer.serialize(
+                        Collections.singletonList(rule), /* formatVersion= */ Optional.empty());
+
+        assertThat(actualRules).isEqualTo(expectedRules);
+    }
+
+    @Test
+    public void testBinaryString_serializeValidCompoundFormula_andConnector() throws Exception {
+        String packageName = "com.test.app";
+        String appCertificate = "test_cert";
+        Rule rule =
+                new Rule(
+                        new CompoundFormula(
+                                CompoundFormula.AND,
+                                Arrays.asList(
+                                        new AtomicFormula.StringAtomicFormula(
+                                                AtomicFormula.PACKAGE_NAME,
+                                                packageName,
+                                                /* isHashedValue= */ false),
+                                        new AtomicFormula.StringAtomicFormula(
+                                                AtomicFormula.APP_CERTIFICATE,
+                                                appCertificate,
+                                                /* isHashedValue= */ false))),
+                        Rule.DENY);
+        RuleSerializer binarySerializer = new RuleBinarySerializer();
+        String expectedBits =
+                START_BIT
+                        + COMPOUND_FORMULA_START_BITS
+                        + AND
+                        + ATOMIC_FORMULA_START_BITS
+                        + PACKAGE_NAME
+                        + EQ
+                        + IS_NOT_HASHED
+                        + getBits(packageName.length(), VALUE_SIZE_BITS)
+                        + getValueBits(packageName)
+                        + ATOMIC_FORMULA_START_BITS
+                        + APP_CERTIFICATE
+                        + EQ
+                        + IS_NOT_HASHED
+                        + getBits(appCertificate.length(), VALUE_SIZE_BITS)
+                        + getValueBits(appCertificate)
+                        + COMPOUND_FORMULA_END_BITS
+                        + DENY
+                        + END_BIT;
+        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+        byteArrayOutputStream.write(DEFAULT_FORMAT_VERSION_BYTES);
+        byteArrayOutputStream.write(getBytes(expectedBits));
+        byte[] expectedRules = byteArrayOutputStream.toByteArray();
+
+        byte[] actualRules =
+                binarySerializer.serialize(
+                        Collections.singletonList(rule), /* formatVersion= */ Optional.empty());
+
+        assertThat(actualRules).isEqualTo(expectedRules);
+    }
+
+    @Test
+    public void testBinaryString_serializeValidCompoundFormula_orConnector() throws Exception {
+        String packageName = "com.test.app";
+        String appCertificate = "test_cert";
+        Rule rule =
+                new Rule(
+                        new CompoundFormula(
+                                CompoundFormula.OR,
+                                Arrays.asList(
+                                        new AtomicFormula.StringAtomicFormula(
+                                                AtomicFormula.PACKAGE_NAME,
+                                                packageName,
+                                                /* isHashedValue= */ false),
+                                        new AtomicFormula.StringAtomicFormula(
+                                                AtomicFormula.APP_CERTIFICATE,
+                                                appCertificate,
+                                                /* isHashedValue= */ false))),
+                        Rule.DENY);
+        RuleSerializer binarySerializer = new RuleBinarySerializer();
+        String expectedBits =
+                START_BIT
+                        + COMPOUND_FORMULA_START_BITS
+                        + OR
+                        + ATOMIC_FORMULA_START_BITS
+                        + PACKAGE_NAME
+                        + EQ
+                        + IS_NOT_HASHED
+                        + getBits(packageName.length(), VALUE_SIZE_BITS)
+                        + getValueBits(packageName)
+                        + ATOMIC_FORMULA_START_BITS
+                        + APP_CERTIFICATE
+                        + EQ
+                        + IS_NOT_HASHED
+                        + getBits(appCertificate.length(), VALUE_SIZE_BITS)
+                        + getValueBits(appCertificate)
+                        + COMPOUND_FORMULA_END_BITS
+                        + DENY
+                        + END_BIT;
+        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+        byteArrayOutputStream.write(DEFAULT_FORMAT_VERSION_BYTES);
+        byteArrayOutputStream.write(getBytes(expectedBits));
+        byte[] expectedRules = byteArrayOutputStream.toByteArray();
+
+        byte[] actualRules =
+                binarySerializer.serialize(
+                        Collections.singletonList(rule), /* formatVersion= */ Optional.empty());
+
+        assertThat(actualRules).isEqualTo(expectedRules);
+    }
+
+    @Test
+    public void testBinaryString_serializeValidAtomicFormula_stringValue() throws Exception {
+        String packageName = "com.test.app";
+        Rule rule =
+                new Rule(
+                        new AtomicFormula.StringAtomicFormula(
+                                AtomicFormula.PACKAGE_NAME,
+                                packageName,
+                                /* isHashedValue= */ false),
+                        Rule.DENY);
+        RuleSerializer binarySerializer = new RuleBinarySerializer();
+        String expectedBits =
+                START_BIT
+                        + ATOMIC_FORMULA_START_BITS
+                        + PACKAGE_NAME
+                        + EQ
+                        + IS_NOT_HASHED
+                        + getBits(packageName.length(), VALUE_SIZE_BITS)
+                        + getValueBits(packageName)
+                        + DENY
+                        + END_BIT;
+        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+        byteArrayOutputStream.write(DEFAULT_FORMAT_VERSION_BYTES);
+        byteArrayOutputStream.write(getBytes(expectedBits));
+        byte[] expectedRules = byteArrayOutputStream.toByteArray();
+
+        byte[] actualRules =
+                binarySerializer.serialize(
+                        Collections.singletonList(rule), /* formatVersion= */ Optional.empty());
+
+        assertThat(actualRules).isEqualTo(expectedRules);
+    }
+
+    @Test
+    public void testBinaryString_serializeValidAtomicFormula_integerValue() throws Exception {
+        String versionCode = "1";
+        Rule rule =
+                new Rule(
+                        new AtomicFormula.IntAtomicFormula(
+                                AtomicFormula.VERSION_CODE,
+                                AtomicFormula.EQ,
+                                Integer.parseInt(versionCode)),
+                        Rule.DENY);
+        RuleSerializer binarySerializer = new RuleBinarySerializer();
+        String expectedBits =
+                START_BIT
+                        + ATOMIC_FORMULA_START_BITS
+                        + VERSION_CODE
+                        + EQ
+                        + IS_NOT_HASHED
+                        + getBits(versionCode.length(), VALUE_SIZE_BITS)
+                        + getValueBits(versionCode)
+                        + DENY
+                        + END_BIT;
+        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+        byteArrayOutputStream.write(DEFAULT_FORMAT_VERSION_BYTES);
+        byteArrayOutputStream.write(getBytes(expectedBits));
+        byte[] expectedRules = byteArrayOutputStream.toByteArray();
+
+        byte[] actualRules =
+                binarySerializer.serialize(
+                        Collections.singletonList(rule), /* formatVersion= */ Optional.empty());
+
+        assertThat(actualRules).isEqualTo(expectedRules);
+    }
+
+    @Test
+    public void testBinaryString_serializeValidAtomicFormula_booleanValue() throws Exception {
+        String preInstalled = "1";
+        Rule rule =
+                new Rule(
+                        new AtomicFormula.BooleanAtomicFormula(AtomicFormula.PRE_INSTALLED, true),
+                        Rule.DENY);
+        RuleSerializer binarySerializer = new RuleBinarySerializer();
+        String expectedBits =
+                START_BIT
+                        + ATOMIC_FORMULA_START_BITS
+                        + PRE_INSTALLED
+                        + EQ
+                        + IS_NOT_HASHED
+                        + getBits(preInstalled.length(), VALUE_SIZE_BITS)
+                        + getValueBits(preInstalled)
+                        + DENY
+                        + END_BIT;
+        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+        byteArrayOutputStream.write(DEFAULT_FORMAT_VERSION_BYTES);
+        byteArrayOutputStream.write(getBytes(expectedBits));
+        byte[] expectedRules = byteArrayOutputStream.toByteArray();
+
+        byte[] actualRules =
+                binarySerializer.serialize(
+                        Collections.singletonList(rule), /* formatVersion= */ Optional.empty());
+
+        assertThat(actualRules).isEqualTo(expectedRules);
+    }
+
+    @Test
+    public void testBinaryString_serializeInvalidFormulaType() throws Exception {
+        Formula invalidFormula = getInvalidFormula();
+        Rule rule = new Rule(invalidFormula, Rule.DENY);
+        RuleSerializer binarySerializer = new RuleBinarySerializer();
+
+        assertExpectException(
+                RuleSerializeException.class,
+                /* expectedExceptionMessageRegex= */ "Invalid formula type",
+                () ->
+                        binarySerializer.serialize(
+                                Collections.singletonList(rule),
+                                /* formatVersion= */ Optional.empty()));
+    }
+
+    @Test
+    public void testBinaryString_serializeFormatVersion() throws Exception {
+        int formatVersion = 1;
+        RuleSerializer binarySerializer = new RuleBinarySerializer();
+        String expectedBits = getBits(formatVersion, FORMAT_VERSION_BITS);
+        byte[] expectedRules = getBytes(expectedBits);
+
+        byte[] actualRules =
+                binarySerializer.serialize(
+                        Collections.emptyList(), /* formatVersion= */ Optional.of(formatVersion));
+
+        assertThat(actualRules).isEqualTo(expectedRules);
+    }
+
+    private static Formula getInvalidFormula() {
+        return new Formula() {
+            @Override
+            public boolean isSatisfied(AppInstallMetadata appInstallMetadata) {
+                return false;
+            }
+
+            @Override
+            public int getTag() {
+                return 0;
+            }
+
+            @Override
+            public int hashCode() {
+                return super.hashCode();
+            }
+
+            @Override
+            public boolean equals(Object obj) {
+                return super.equals(obj);
+            }
+
+            @NonNull
+            @Override
+            protected Object clone() throws CloneNotSupportedException {
+                return super.clone();
+            }
+
+            @Override
+            public String toString() {
+                return super.toString();
+            }
+
+            @Override
+            protected void finalize() throws Throwable {
+                super.finalize();
+            }
+        };
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleXmlSerializerTest.java b/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleXmlSerializerTest.java
index 5903b5a..ad74901 100644
--- a/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleXmlSerializerTest.java
+++ b/services/tests/servicestests/src/com/android/server/integrity/serializer/RuleXmlSerializerTest.java
@@ -91,7 +91,7 @@
     }
 
     @Test
-    public void testXmlStream_serializeValidOpenFormula() throws Exception {
+    public void testXmlStream_serializeValidCompoundFormula() throws Exception {
         Rule rule =
                 new Rule(
                         new CompoundFormula(
@@ -134,7 +134,7 @@
     }
 
     @Test
-    public void testXmlString_serializeValidOpenFormula_notConnector() throws Exception {
+    public void testXmlString_serializeValidCompoundFormula_notConnector() throws Exception {
         Rule rule =
                 new Rule(
                         new CompoundFormula(
@@ -174,7 +174,7 @@
     }
 
     @Test
-    public void testXmlString_serializeValidOpenFormula_andConnector() throws Exception {
+    public void testXmlString_serializeValidCompoundFormula_andConnector() throws Exception {
         Rule rule =
                 new Rule(
                         new CompoundFormula(
@@ -224,7 +224,7 @@
     }
 
     @Test
-    public void testXmlString_serializeValidOpenFormula_orConnector() throws Exception {
+    public void testXmlString_serializeValidCompoundFormula_orConnector() throws Exception {
         Rule rule =
                 new Rule(
                         new CompoundFormula(
diff --git a/services/tests/servicestests/src/com/android/server/integrity/utils/TestUtils.java b/services/tests/servicestests/src/com/android/server/integrity/utils/TestUtils.java
new file mode 100644
index 0000000..e54410b
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/integrity/utils/TestUtils.java
@@ -0,0 +1,48 @@
+/*
+ * 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.utils;
+
+public class TestUtils {
+
+    public static String getBits(int component, int numOfBits) {
+        return String.format("%" + numOfBits + "s", Integer.toBinaryString(component))
+                .replace(' ', '0');
+    }
+
+    public static String getValueBits(String value) {
+        StringBuilder stringBuilder = new StringBuilder();
+        for (byte valueByte : value.getBytes()) {
+            stringBuilder.append(getBits(valueByte, /* numOfBits= */ 8));
+        }
+        return stringBuilder.toString();
+    }
+
+    public static byte[] getBytes(String bits) {
+        int bitStringSize = bits.length();
+        int numOfBytes = bitStringSize / 8;
+        if (bitStringSize % 8 != 0) {
+            numOfBytes++;
+        }
+        byte[] bytes = new byte[numOfBytes];
+        for (int i = 0; i < bits.length(); i++) {
+            if (bits.charAt(i) == '1') {
+                bytes[i / 8] |= 1 << (7 - (i % 8));
+            }
+        }
+        return bytes;
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
index 313afbb..dee79bb 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -263,6 +263,19 @@
     }
 
     @MediumTest
+    public void testFindExistingGuest_guestExists() throws Exception {
+        UserInfo userInfo1 = createUser("Guest", UserInfo.FLAG_GUEST);
+        UserInfo foundGuest = mUserManager.findCurrentGuestUser();
+        assertNotNull(foundGuest);
+    }
+
+    @SmallTest
+    public void testFindExistingGuest_guestDoesNotExist() throws Exception {
+        UserInfo foundGuest = mUserManager.findCurrentGuestUser();
+        assertNull(foundGuest);
+    }
+
+    @MediumTest
     public void testSetUserAdmin() throws Exception {
         UserInfo userInfo = createUser("SecondaryUser", /*flags=*/ 0);
 
diff --git a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
index 0ca62e2..81fb0ec 100644
--- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -61,7 +61,6 @@
 import android.os.PowerManager;
 import android.os.PowerSaveState;
 import android.os.SystemClock;
-import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.test.mock.MockContentResolver;
@@ -97,10 +96,12 @@
  * Tests for {@link com.android.server.power.PowerManagerService}
  */
 public class PowerManagerServiceTest {
+    private static final String SYSTEM_PROPERTY_QUIESCENT = "ro.boot.quiescent";
+    private static final String SYSTEM_PROPERTY_REBOOT_REASON = "sys.boot.reason";
+
     private static final float PRECISION = 0.001f;
     private static final float BRIGHTNESS_FACTOR = 0.7f;
     private static final boolean BATTERY_SAVER_ENABLED = true;
-    private static final String TEST_LAST_REBOOT_PROPERTY = "test.sys.boot.reason";
 
     @Mock private BatterySaverPolicy mBatterySaverPolicyMock;
     @Mock private LightsManager mLightsManagerMock;
@@ -112,6 +113,7 @@
     @Mock private Notifier mNotifierMock;
     @Mock private WirelessChargerDetector mWirelessChargerDetectorMock;
     @Mock private AmbientDisplayConfiguration mAmbientDisplayConfigurationMock;
+    @Mock private SystemPropertiesWrapper mSystemPropertiesMock;
 
     @Mock
     private InattentiveSleepWarningController mInattentiveSleepWarningControllerMock;
@@ -159,6 +161,7 @@
         when(mBatteryManagerInternalMock.isPowered(anyInt())).thenReturn(false);
         when(mInattentiveSleepWarningControllerMock.isShown()).thenReturn(false);
         when(mDisplayManagerInternalMock.requestPowerState(any(), anyBoolean())).thenReturn(true);
+        when(mSystemPropertiesMock.get(eq(SYSTEM_PROPERTY_QUIESCENT), anyString())).thenReturn("");
 
         mDisplayPowerRequest = new DisplayPowerRequest();
         addLocalServiceMock(LightsManager.class, mLightsManagerMock);
@@ -218,6 +221,11 @@
             InattentiveSleepWarningController createInattentiveSleepWarningController() {
                 return mInattentiveSleepWarningControllerMock;
             }
+
+            @Override
+            public SystemPropertiesWrapper createSystemPropertiesWrapper() {
+                return mSystemPropertiesMock;
+            }
         });
         return mService;
     }
@@ -228,12 +236,6 @@
         LocalServices.removeServiceForTest(DisplayManagerInternal.class);
         LocalServices.removeServiceForTest(BatteryManagerInternal.class);
         LocalServices.removeServiceForTest(ActivityManagerInternal.class);
-
-        Settings.Global.putInt(
-                mContextSpy.getContentResolver(), Settings.Global.THEATER_MODE_ON, 0);
-        setAttentiveTimeout(-1);
-        Settings.Global.putInt(mContextSpy.getContentResolver(),
-                Settings.Global.STAY_ON_WHILE_PLUGGED_IN, 0);
     }
 
     /**
@@ -322,10 +324,10 @@
 
     @Test
     public void testGetLastShutdownReasonInternal() {
+        when(mSystemPropertiesMock.get(eq(SYSTEM_PROPERTY_REBOOT_REASON), any())).thenReturn(
+                "shutdown,thermal");
         createService();
-        SystemProperties.set(TEST_LAST_REBOOT_PROPERTY, "shutdown,thermal");
-        int reason = mService.getLastShutdownReasonInternal(TEST_LAST_REBOOT_PROPERTY);
-        SystemProperties.set(TEST_LAST_REBOOT_PROPERTY, "");
+        int reason = mService.getLastShutdownReasonInternal();
         assertThat(reason).isEqualTo(PowerManager.SHUTDOWN_REASON_THERMAL_SHUTDOWN);
     }
 
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/SimpleTimeDetectorStrategyTest.java b/services/tests/servicestests/src/com/android/server/timedetector/SimpleTimeDetectorStrategyTest.java
index 8a7edf7..7a0a28d 100644
--- a/services/tests/servicestests/src/com/android/server/timedetector/SimpleTimeDetectorStrategyTest.java
+++ b/services/tests/servicestests/src/com/android/server/timedetector/SimpleTimeDetectorStrategyTest.java
@@ -640,9 +640,9 @@
 
     private static PhoneTimeSuggestion createPhoneTimeSuggestion(int phoneId,
             TimestampedValue<Long> utcTime) {
-        PhoneTimeSuggestion timeSuggestion = new PhoneTimeSuggestion(phoneId);
-        timeSuggestion.setUtcTime(utcTime);
-        return timeSuggestion;
+        return new PhoneTimeSuggestion.Builder(phoneId)
+                .setUtcTime(utcTime)
+                .build();
     }
 
     private ManualTimeSuggestion createManualTimeSuggestion(long timeMillis) {
diff --git a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
index 9951e85..84b495f 100644
--- a/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/timedetector/TimeDetectorServiceTest.java
@@ -140,10 +140,10 @@
 
     private static PhoneTimeSuggestion createPhoneTimeSuggestion() {
         int phoneId = 1234;
-        PhoneTimeSuggestion suggestion = new PhoneTimeSuggestion(phoneId);
         TimestampedValue<Long> timeValue = new TimestampedValue<>(100L, 1_000_000L);
-        suggestion.setUtcTime(timeValue);
-        return suggestion;
+        return new PhoneTimeSuggestion.Builder(phoneId)
+                .setUtcTime(timeValue)
+                .build();
     }
 
     private static ManualTimeSuggestion createManualTimeSuggestion() {
diff --git a/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java b/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java
index e32103f..e6bb244 100644
--- a/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java
+++ b/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java
@@ -45,6 +45,7 @@
 import java.io.IOException;
 import java.util.List;
 import java.util.Locale;
+import java.util.Set;
 
 @RunWith(AndroidJUnit4.class)
 @SmallTest
@@ -93,6 +94,8 @@
                 for (File f : usageFiles) {
                     f.delete();
                 }
+            } else {
+                intervalDir.delete();
             }
         }
     }
@@ -587,6 +590,7 @@
         db.readMappingsLocked();
         db.init(1);
         db.putUsageStats(interval, mIntervalStats);
+        db.writeMappingsLocked();
 
         final String removedPackage = "fake.package.name0";
         // invoke handler call directly from test to remove package
@@ -594,19 +598,19 @@
 
         List<IntervalStats> stats = db.queryUsageStats(interval, 0, mEndTime,
                 mIntervalStatsVerifier);
-        for (int i = 0; i < stats.size(); i++) {
-            final IntervalStats stat = stats.get(i);
-            if (stat.packageStats.containsKey(removedPackage)) {
-                fail("Found removed package " + removedPackage + " in package stats.");
+        assertEquals(1, stats.size(),
+                "Only one interval stats object should exist for the given time range.");
+        final IntervalStats stat = stats.get(0);
+        if (stat.packageStats.containsKey(removedPackage)) {
+            fail("Found removed package " + removedPackage + " in package stats.");
+            return;
+        }
+        for (int i = 0; i < stat.events.size(); i++) {
+            final Event event = stat.events.get(i);
+            if (removedPackage.equals(event.mPackage)) {
+                fail("Found an event from removed package " + removedPackage);
                 return;
             }
-            for (int j = 0; j < stat.events.size(); j++) {
-                final Event event = stat.events.get(j);
-                if (removedPackage.equals(event.mPackage)) {
-                    fail("Found an event from removed package " + removedPackage);
-                    return;
-                }
-            }
         }
     }
 
@@ -617,4 +621,90 @@
         verifyPackageNotRetained(UsageStatsManager.INTERVAL_MONTHLY);
         verifyPackageNotRetained(UsageStatsManager.INTERVAL_YEARLY);
     }
+
+    private void verifyPackageDataIsRemoved(UsageStatsDatabase db, int interval,
+            String removedPackage) {
+        List<IntervalStats> stats = db.queryUsageStats(interval, 0, mEndTime,
+                mIntervalStatsVerifier);
+        assertEquals(1, stats.size(),
+                "Only one interval stats object should exist for the given time range.");
+        final IntervalStats stat = stats.get(0);
+        if (stat.packageStats.containsKey(removedPackage)) {
+            fail("Found removed package " + removedPackage + " in package stats.");
+            return;
+        }
+        for (int i = 0; i < stat.events.size(); i++) {
+            final Event event = stat.events.get(i);
+            if (removedPackage.equals(event.mPackage)) {
+                fail("Found an event from removed package " + removedPackage);
+                return;
+            }
+        }
+    }
+
+    private void verifyPackageDataIsNotRemoved(UsageStatsDatabase db, int interval,
+            Set<String> installedPackages) {
+        List<IntervalStats> stats = db.queryUsageStats(interval, 0, mEndTime,
+                mIntervalStatsVerifier);
+        assertEquals(1, stats.size(),
+                "Only one interval stats object should exist for the given time range.");
+        final IntervalStats stat = stats.get(0);
+        if (!stat.packageStats.containsAll(installedPackages)) {
+            fail("Could not find some installed packages in package stats.");
+            return;
+        }
+        // attempt to find an event from each installed package
+        for (String installedPackage : installedPackages) {
+            for (int i = 0; i < stat.events.size(); i++) {
+                if (installedPackage.equals(stat.events.get(i).mPackage)) {
+                    break;
+                }
+                if (i == stat.events.size() - 1) {
+                    fail("Could not find any event for: " + installedPackage);
+                    return;
+                }
+            }
+        }
+    }
+
+    @Test
+    public void testPackageDataIsRemoved() throws IOException {
+        UsageStatsDatabase db = new UsageStatsDatabase(mTestDir);
+        db.readMappingsLocked();
+        db.init(1);
+
+        // write stats to disk for each interval
+        db.putUsageStats(UsageStatsManager.INTERVAL_DAILY, mIntervalStats);
+        db.putUsageStats(UsageStatsManager.INTERVAL_WEEKLY, mIntervalStats);
+        db.putUsageStats(UsageStatsManager.INTERVAL_MONTHLY, mIntervalStats);
+        db.putUsageStats(UsageStatsManager.INTERVAL_YEARLY, mIntervalStats);
+        db.writeMappingsLocked();
+
+        final Set<String> installedPackages = mIntervalStats.packageStats.keySet();
+        final String removedPackage = installedPackages.iterator().next();
+        installedPackages.remove(removedPackage);
+
+        // mimic a package uninstall
+        db.onPackageRemoved(removedPackage, System.currentTimeMillis());
+
+        // mimic the idle prune job being triggered
+        db.pruneUninstalledPackagesData();
+
+        // read data from disk into a new db instance
+        UsageStatsDatabase newDB = new UsageStatsDatabase(mTestDir);
+        newDB.readMappingsLocked();
+        newDB.init(mEndTime);
+
+        // query data for each interval and ensure data for package doesn't exist
+        verifyPackageDataIsRemoved(newDB, UsageStatsManager.INTERVAL_DAILY, removedPackage);
+        verifyPackageDataIsRemoved(newDB, UsageStatsManager.INTERVAL_WEEKLY, removedPackage);
+        verifyPackageDataIsRemoved(newDB, UsageStatsManager.INTERVAL_MONTHLY, removedPackage);
+        verifyPackageDataIsRemoved(newDB, UsageStatsManager.INTERVAL_YEARLY, removedPackage);
+
+        // query data for each interval and ensure some data for installed packages exists
+        verifyPackageDataIsNotRemoved(newDB, UsageStatsManager.INTERVAL_DAILY, installedPackages);
+        verifyPackageDataIsNotRemoved(newDB, UsageStatsManager.INTERVAL_WEEKLY, installedPackages);
+        verifyPackageDataIsNotRemoved(newDB, UsageStatsManager.INTERVAL_MONTHLY, installedPackages);
+        verifyPackageDataIsNotRemoved(newDB, UsageStatsManager.INTERVAL_YEARLY, installedPackages);
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
index 5336811..de2bba2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
@@ -123,7 +123,7 @@
         assertTrue(stack1.isFocusedStackOnDisplay());
 
         // Stack2 should be focused after removing stack1.
-        display.removeChild(stack1);
+        display.removeStack(stack1);
         assertTrue(stack2.isFocusedStackOnDisplay());
     }
 
@@ -224,7 +224,7 @@
         final ActivityRecord activity = new ActivityBuilder(mService).setCreateTask(true)
                 .setStack(alwaysOnTopStack).build();
         alwaysOnTopStack.setAlwaysOnTop(true);
-        display.positionChildAtTop(alwaysOnTopStack, false /* includingParents */);
+        display.positionStackAtTop(alwaysOnTopStack, false /* includingParents */);
         assertTrue(alwaysOnTopStack.isAlwaysOnTop());
         // Ensure always on top state is synced to the children of the stack.
         assertTrue(alwaysOnTopStack.getTopNonFinishingActivity().isAlwaysOnTop());
@@ -238,36 +238,36 @@
         final ActivityStack anotherAlwaysOnTopStack = display.createStack(
                 WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD, true /* onTop */);
         anotherAlwaysOnTopStack.setAlwaysOnTop(true);
-        display.positionChildAtTop(anotherAlwaysOnTopStack, false /* includingParents */);
+        display.positionStackAtTop(anotherAlwaysOnTopStack, false /* includingParents */);
         assertTrue(anotherAlwaysOnTopStack.isAlwaysOnTop());
-        int topPosition = display.getChildCount() - 1;
+        int topPosition = display.getStackCount() - 1;
         // Ensure the new alwaysOnTop stack is put below the pinned stack, but on top of the
         // existing alwaysOnTop stack.
-        assertEquals(anotherAlwaysOnTopStack, display.getChildAt(topPosition - 1));
+        assertEquals(anotherAlwaysOnTopStack, display.getStackAt(topPosition - 1));
 
         final ActivityStack nonAlwaysOnTopStack = display.createStack(
                 WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD, true /* onTop */);
         assertEquals(display, nonAlwaysOnTopStack.getDisplay());
-        topPosition = display.getChildCount() - 1;
+        topPosition = display.getStackCount() - 1;
         // Ensure the non-alwaysOnTop stack is put below the three alwaysOnTop stacks, but above the
         // existing other non-alwaysOnTop stacks.
-        assertEquals(nonAlwaysOnTopStack, display.getChildAt(topPosition - 3));
+        assertEquals(nonAlwaysOnTopStack, display.getStackAt(topPosition - 3));
 
         anotherAlwaysOnTopStack.setAlwaysOnTop(false);
-        display.positionChildAtTop(anotherAlwaysOnTopStack, false /* includingParents */);
+        display.positionStackAtTop(anotherAlwaysOnTopStack, false /* includingParents */);
         assertFalse(anotherAlwaysOnTopStack.isAlwaysOnTop());
         // Ensure, when always on top is turned off for a stack, the stack is put just below all
         // other always on top stacks.
-        assertEquals(anotherAlwaysOnTopStack, display.getChildAt(topPosition - 2));
+        assertEquals(anotherAlwaysOnTopStack, display.getStackAt(topPosition - 2));
         anotherAlwaysOnTopStack.setAlwaysOnTop(true);
 
         // Ensure always on top state changes properly when windowing mode changes.
         anotherAlwaysOnTopStack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
         assertFalse(anotherAlwaysOnTopStack.isAlwaysOnTop());
-        assertEquals(anotherAlwaysOnTopStack, display.getChildAt(topPosition - 2));
+        assertEquals(anotherAlwaysOnTopStack, display.getStackAt(topPosition - 2));
         anotherAlwaysOnTopStack.setWindowingMode(WINDOWING_MODE_FREEFORM);
         assertTrue(anotherAlwaysOnTopStack.isAlwaysOnTop());
-        assertEquals(anotherAlwaysOnTopStack, display.getChildAt(topPosition - 1));
+        assertEquals(anotherAlwaysOnTopStack, display.getStackAt(topPosition - 1));
     }
 
     @Test
@@ -299,14 +299,14 @@
 
         // Reordering stacks while removing stacks.
         doAnswer(invocation -> {
-            display.positionChildAtTop(stack3, false);
+            display.positionStackAtTop(stack3, false);
             return true;
         }).when(mSupervisor).removeTaskByIdLocked(eq(task4.mTaskId), anyBoolean(), anyBoolean(),
                 any());
 
         // Removing stacks from the display while removing stacks.
         doAnswer(invocation -> {
-            display.removeChild(stack2);
+            display.removeStack(stack2);
             return true;
         }).when(mSupervisor).removeTaskByIdLocked(eq(task2.mTaskId), anyBoolean(), anyBoolean(),
                 any());
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index c2b8827..7166829 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -455,7 +455,7 @@
             verify(mService.getLifecycleManager()).scheduleTransaction(
                     eq(mActivity.app.getThread()), eq(mActivity.appToken), eq(expected));
         } finally {
-            stack.getDisplay().removeChild(stack);
+            stack.getDisplay().removeStack(stack);
         }
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
index c8795ce..7806d40 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
@@ -290,7 +290,7 @@
         verify(stack2).positionChildAtBottom(any(), eq(false) /* includingParents */);
 
         // Also move display to back because there is only one stack left.
-        display.removeChild(stack1);
+        display.removeStack(stack1);
         stack2.moveToBack("testMoveStackToBackIncludingParent", stack2.topTask());
         verify(stack2).positionChildAtBottom(any(), eq(true) /* includingParents */);
     }
@@ -576,7 +576,7 @@
 
     @Test
     public void testMoveHomeStackBehindBottomMostVisibleStack_NoMoveHomeBehindFullscreen() {
-        mDefaultDisplay.removeChild(mStack);
+        mDefaultDisplay.removeStack(mStack);
 
         final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
@@ -595,7 +595,7 @@
 
     @Test
     public void testMoveHomeStackBehindBottomMostVisibleStack_NoMoveHomeBehindTranslucent() {
-        mDefaultDisplay.removeChild(mStack);
+        mDefaultDisplay.removeStack(mStack);
 
         final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
@@ -614,7 +614,7 @@
 
     @Test
     public void testMoveHomeStackBehindBottomMostVisibleStack_NoMoveHomeOnTop() {
-        mDefaultDisplay.removeChild(mStack);
+        mDefaultDisplay.removeStack(mStack);
 
         final ActivityStack fullscreenStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
@@ -633,7 +633,7 @@
 
     @Test
     public void testMoveHomeStackBehindBottomMostVisibleStack_MoveHomeBehindFullscreen() {
-        mDefaultDisplay.removeChild(mStack);
+        mDefaultDisplay.removeStack(mStack);
 
         final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
@@ -660,7 +660,7 @@
     @Test
     public void
             testMoveHomeStackBehindBottomMostVisibleStack_MoveHomeBehindFullscreenAndTranslucent() {
-        mDefaultDisplay.removeChild(mStack);
+        mDefaultDisplay.removeStack(mStack);
 
         final ActivityStack homeStack = createStackForShouldBeVisibleTest(mDefaultDisplay,
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, true /* onTop */);
@@ -684,7 +684,7 @@
 
     @Test
     public void testMoveHomeStackBehindStack_BehindHomeStack() {
-        mDefaultDisplay.removeChild(mStack);
+        mDefaultDisplay.removeStack(mStack);
 
         final ActivityStack fullscreenStack1 = createStackForShouldBeVisibleTest(
                 mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
@@ -707,7 +707,7 @@
 
     @Test
     public void testMoveHomeStackBehindStack() {
-        mDefaultDisplay.removeChild(mStack);
+        mDefaultDisplay.removeStack(mStack);
 
         final ActivityStack fullscreenStack1 = createStackForShouldBeVisibleTest(
                 mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
@@ -814,16 +814,16 @@
     }
 
     @SuppressWarnings("TypeParameterUnusedInFormals")
-    private <T extends ActivityStack> T createStackForShouldBeVisibleTest(
+    private ActivityStack createStackForShouldBeVisibleTest(
             ActivityDisplay display, int windowingMode, int activityType, boolean onTop) {
         final ActivityStack stack;
         if (activityType == ACTIVITY_TYPE_HOME) {
             // Home stack and activity are created in ActivityTestsBase#setupActivityManagerService
             stack = mDefaultDisplay.getStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME);
             if (onTop) {
-                mDefaultDisplay.positionChildAtTop(stack, false /* includingParents */);
+                mDefaultDisplay.positionStackAtTop(stack, false /* includingParents */);
             } else {
-                mDefaultDisplay.positionChildAtBottom(stack);
+                mDefaultDisplay.positionStackAtBottom(stack);
             }
         } else {
             stack = new StackBuilder(mRootActivityContainer)
@@ -834,7 +834,7 @@
                     .setCreateActivity(true)
                     .build();
         }
-        return (T) stack;
+        return stack;
     }
 
     @Test
@@ -1051,7 +1051,7 @@
         StackOrderChangedListener listener = new StackOrderChangedListener();
         mDefaultDisplay.registerStackOrderChangedListener(listener);
         try {
-            mDefaultDisplay.removeChild(mStack);
+            mDefaultDisplay.removeStack(mStack);
         } finally {
             mDefaultDisplay.unregisterStackOrderChangedListener(listener);
         }
@@ -1060,12 +1060,12 @@
 
     @Test
     public void testStackOrderChangedOnAddPositionStack() {
-        mDefaultDisplay.removeChild(mStack);
+        mDefaultDisplay.removeStack(mStack);
 
         StackOrderChangedListener listener = new StackOrderChangedListener();
         mDefaultDisplay.registerStackOrderChangedListener(listener);
         try {
-            mDefaultDisplay.addChild(mStack, 0);
+            mDefaultDisplay.addStack(mStack, 0);
         } finally {
             mDefaultDisplay.unregisterStackOrderChangedListener(listener);
         }
@@ -1080,7 +1080,7 @@
                     mDefaultDisplay, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD,
                     true /* onTop */);
             mDefaultDisplay.registerStackOrderChangedListener(listener);
-            mDefaultDisplay.positionChildAtBottom(fullscreenStack1);
+            mDefaultDisplay.positionStackAtBottom(fullscreenStack1);
         } finally {
             mDefaultDisplay.unregisterStackOrderChangedListener(listener);
         }
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
index c712d6d..d5fdf98 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStarterTests.java
@@ -517,8 +517,8 @@
     }
 
     private void assertNoTasks(ActivityDisplay display) {
-        for (int i = display.getChildCount() - 1; i >= 0; --i) {
-            final ActivityStack stack = display.getChildAt(i);
+        for (int i = display.getStackCount() - 1; i >= 0; --i) {
+            final ActivityStack stack = display.getStackAt(i);
             assertThat(stack.getAllTasks()).isEmpty();
         }
     }
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
index 7322ac3..0021cc5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTestsBase.java
@@ -203,6 +203,15 @@
         }
 
         ActivityRecord build() {
+            try {
+                mService.deferWindowLayout();
+                return buildInner();
+            } finally {
+                mService.continueWindowLayout();
+            }
+        }
+
+        ActivityRecord buildInner() {
             if (mComponent == null) {
                 final int id = sCurrentActivityId++;
                 mComponent = ComponentName.createRelative(DEFAULT_COMPONENT_PACKAGE_NAME,
@@ -236,7 +245,7 @@
 
             ActivityOptions options = null;
             if (mLaunchTaskBehind) {
-                options =  ActivityOptions.makeTaskLaunchBehind();
+                options = ActivityOptions.makeTaskLaunchBehind();
             }
 
             final ActivityRecord activity = new ActivityRecord(mService, null /* caller */,
@@ -399,11 +408,18 @@
             return this;
         }
 
+        // TODO(display-merge): Remove
         StackBuilder setDisplay(ActivityDisplay display) {
             mDisplay = display;
             return this;
         }
 
+        StackBuilder setDisplay(DisplayContent display) {
+            // TODO(display-merge): Remove cast
+            mDisplay = (ActivityDisplay) display;
+            return this;
+        }
+
         StackBuilder setOnTop(boolean onTop) {
             mOnTop = onTop;
             return this;
diff --git a/services/tests/wmtests/src/com/android/server/wm/BoundsAnimationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BoundsAnimationControllerTests.java
index beec1a8..0ad0f95 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BoundsAnimationControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BoundsAnimationControllerTests.java
@@ -47,6 +47,7 @@
 
 import org.junit.Before;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 
 /**
  * Test class for {@link BoundsAnimationController} to ensure that it sends the right callbacks
@@ -63,6 +64,7 @@
  */
 @SmallTest
 @Presubmit
+@RunWith(WindowTestRunner.class)
 public class BoundsAnimationControllerTests extends WindowTestsBase {
 
     /**
diff --git a/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java b/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
index 73420a0..0aa6961 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
@@ -36,12 +36,14 @@
 
 import org.junit.Before;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 
 /**
  * Build/Install/Run:
  *  atest FrameworksServicesTests:DimmerTests
  */
 @Presubmit
+@RunWith(WindowTestRunner.class)
 public class DimmerTests extends WindowTestsBase {
 
     private static class TestWindowContainer extends WindowContainer<TestWindowContainer> {
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 3eda980..716e777 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -709,13 +709,13 @@
 
         final ActivityStack stack =
                 new ActivityTestsBase.StackBuilder(mWm.mAtmService.mRootActivityContainer)
-                        .setDisplay(dc.mActivityDisplay)
+                        .setDisplay(dc)
                         .build();
         doReturn(true).when(stack).isVisible();
 
         final ActivityStack freeformStack =
                 new ActivityTestsBase.StackBuilder(mWm.mAtmService.mRootActivityContainer)
-                        .setDisplay(dc.mActivityDisplay)
+                        .setDisplay(dc)
                         .setWindowingMode(WINDOWING_MODE_FREEFORM)
                         .build();
         doReturn(true).when(freeformStack).isVisible();
@@ -743,7 +743,7 @@
 
         final ActivityStack stack =
                 new ActivityTestsBase.StackBuilder(mWm.mAtmService.mRootActivityContainer)
-                        .setDisplay(dc.mActivityDisplay).build();
+                        .setDisplay(dc).build();
         final ActivityRecord activity = stack.topTask().getTopNonFinishingActivity();
 
         activity.setRequestedOrientation(newOrientation);
@@ -765,12 +765,13 @@
 
         final ActivityStack stack =
                 new ActivityTestsBase.StackBuilder(mWm.mAtmService.mRootActivityContainer)
-                        .setDisplay(dc.mActivityDisplay).build();
+                        .setDisplay(dc).build();
         final ActivityRecord activity = stack.topTask().getTopNonFinishingActivity();
 
         activity.setRequestedOrientation(newOrientation);
 
-        verify(dc.mActivityDisplay, never()).updateDisplayOverrideConfigurationLocked(any(),
+        // TODO(display-merge): Remove cast
+        verify((ActivityDisplay) dc, never()).updateDisplayOverrideConfigurationLocked(any(),
                 eq(activity), anyBoolean(), same(null));
         assertEquals(dc.getDisplayRotation().getUserRotation(), dc.getRotation());
     }
@@ -940,6 +941,7 @@
         final DisplayContent displayContent = createNewDisplay();
         Mockito.doReturn(mockLogger).when(displayContent).getMetricsLogger();
         Mockito.doReturn(oldConfig).doReturn(newConfig).when(displayContent).getConfiguration();
+        doNothing().when(displayContent).preOnConfigurationChanged();
 
         displayContent.onConfigurationChanged(newConfig);
 
@@ -959,12 +961,12 @@
         Mockito.doCallRealMethod().when(dr).updateRotationUnchecked(anyBoolean());
         Mockito.doReturn(ROTATION_90).when(dr).rotationForOrientation(anyInt(), anyInt());
         final boolean[] continued = new boolean[1];
-        spyOn(dc.mActivityDisplay);
+        // TODO(display-merge): Remove cast
         Mockito.doAnswer(
                 invocation -> {
                     continued[0] = true;
                     return true;
-                }).when(dc.mActivityDisplay).updateDisplayOverrideConfigurationLocked();
+                }).when((ActivityDisplay) dc).updateDisplayOverrideConfigurationLocked();
         final boolean[] called = new boolean[1];
         mWm.mDisplayRotationController =
                 new IDisplayWindowRotationController.Stub() {
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyInsetsTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyInsetsTests.java
index 6767465..f754c59 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyInsetsTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyInsetsTests.java
@@ -32,9 +32,11 @@
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ErrorCollector;
+import org.junit.runner.RunWith;
 
 @SmallTest
 @Presubmit
+@RunWith(WindowTestRunner.class)
 public class DisplayPolicyInsetsTests extends DisplayPolicyTestsBase {
 
     @Rule
diff --git a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
index 79ad0c4..9e5d9da 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsPersisterTests.java
@@ -46,6 +46,7 @@
 
 import org.junit.Before;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
 
 import java.io.File;
@@ -63,6 +64,7 @@
  */
 @MediumTest
 @Presubmit
+@RunWith(WindowTestRunner.class)
 public class LaunchParamsPersisterTests extends ActivityTestsBase {
     private static final int TEST_USER_ID = 3;
     private static final int ALTERNATIVE_USER_ID = 0;
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
index 4f2d5d2..9f97c48 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -977,9 +977,9 @@
     private void assertNotRestoreTask(Runnable action) {
         // Verify stack count doesn't change because task with fullscreen mode and standard type
         // would have its own stack.
-        final int orignalStackCount = mDisplay.getChildCount();
+        final int originalStackCount = mDisplay.getStackCount();
         action.run();
-        assertEquals(orignalStackCount, mDisplay.getChildCount());
+        assertEquals(originalStackCount, mDisplay.getStackCount());
     }
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
index 1abd366..2374847 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationControllerTest.java
@@ -138,7 +138,8 @@
     @Test
     public void testIncludedApps_expectTargetAndVisible() {
         mWm.setRecentsAnimationController(mController);
-        final ActivityStack homeStack = mDisplayContent.mActivityDisplay.getOrCreateStack(
+        // TODO(display-merge): Remove cast
+        final ActivityStack homeStack = ((ActivityDisplay) mDisplayContent).getOrCreateStack(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
         final ActivityRecord homeActivity =
                 new ActivityTestsBase.ActivityBuilder(mWm.mAtmService)
@@ -163,7 +164,8 @@
     @Test
     public void testWallpaperIncluded_expectTarget() throws Exception {
         mWm.setRecentsAnimationController(mController);
-        final ActivityStack homeStack = mDisplayContent.mActivityDisplay.getOrCreateStack(
+        // TODO(display-merge): Remove cast
+        final ActivityStack homeStack = ((ActivityDisplay) mDisplayContent).getOrCreateStack(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
         final ActivityRecord homeAppWindow =
                 new ActivityTestsBase.ActivityBuilder(mWm.mAtmService)
@@ -192,7 +194,8 @@
     @Test
     public void testWallpaperAnimatorCanceled_expectAnimationKeepsRunning() throws Exception {
         mWm.setRecentsAnimationController(mController);
-        final ActivityStack homeStack = mDisplayContent.mActivityDisplay.getOrCreateStack(
+        // TODO(display-merge): Remove cast
+        final ActivityStack homeStack = ((ActivityDisplay) mDisplayContent).getOrCreateStack(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
         final ActivityRecord homeActivity =
                 new ActivityTestsBase.ActivityBuilder(mWm.mAtmService)
@@ -223,7 +226,8 @@
     @Test
     public void testFinish_expectTargetAndWallpaperAdaptersRemoved() {
         mWm.setRecentsAnimationController(mController);
-        final ActivityStack homeStack = mDisplayContent.mActivityDisplay.getOrCreateStack(
+        // TODO(display-merge): Remove cast
+        final ActivityStack homeStack = ((ActivityDisplay) mDisplayContent).getOrCreateStack(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME, ON_TOP);
         final ActivityRecord homeActivity =
                 new ActivityTestsBase.ActivityBuilder(mWm.mAtmService)
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
index e67380c..4abab63 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentsAnimationTest.java
@@ -119,7 +119,7 @@
         final ActivityDisplay defaultDisplay = mRootActivityContainer.getDefaultDisplay();
         final ActivityStack homeStack =
                 defaultDisplay.getStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME);
-        defaultDisplay.positionChildAtTop(homeStack, false /* includingParents */);
+        defaultDisplay.positionStackAtTop(homeStack, false /* includingParents */);
         ActivityRecord topRunningHomeActivity = homeStack.topRunningActivityLocked();
         if (topRunningHomeActivity == null) {
             topRunningHomeActivity = new ActivityBuilder(mService)
diff --git a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
index e353903..112479b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
@@ -54,6 +54,7 @@
 
 import org.junit.Before;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
@@ -64,6 +65,7 @@
  */
 @SmallTest
 @Presubmit
+@RunWith(WindowTestRunner.class)
 public class RemoteAnimationControllerTest extends WindowTestsBase {
 
     @Mock SurfaceControl mMockLeash;
@@ -84,7 +86,7 @@
         when(mMockRunner.asBinder()).thenReturn(new Binder());
         mAdapter = new RemoteAnimationAdapter(mMockRunner, 100, 50, true /* changeNeedsSnapshot */);
         mAdapter.setCallingPidUid(123, 456);
-        mWm.mH.runWithScissors(() -> mHandler = new TestHandler(null, mClock), 0);
+        runWithScissors(mWm.mH, () -> mHandler = new TestHandler(null, mClock), 0);
         mController = new RemoteAnimationController(mWm, mAdapter, mHandler);
     }
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
index 4257249..59c7c02 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java
@@ -226,20 +226,20 @@
     @Test
     public void testRemovingStackOnAppCrash() {
         final ActivityDisplay defaultDisplay = mRootActivityContainer.getDefaultDisplay();
-        final int originalStackCount = defaultDisplay.getChildCount();
+        final int originalStackCount = defaultDisplay.getStackCount();
         final ActivityStack stack = mRootActivityContainer.getDefaultDisplay().createStack(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
         final ActivityRecord firstActivity = new ActivityBuilder(mService).setCreateTask(true)
                 .setStack(stack).build();
 
-        assertEquals(originalStackCount + 1, defaultDisplay.getChildCount());
+        assertEquals(originalStackCount + 1, defaultDisplay.getStackCount());
 
         // Let's pretend that the app has crashed.
         firstActivity.app.setThread(null);
         mRootActivityContainer.finishTopCrashedActivities(firstActivity.app, "test");
 
         // Verify that the stack was removed.
-        assertEquals(originalStackCount, defaultDisplay.getChildCount());
+        assertEquals(originalStackCount, defaultDisplay.getStackCount());
     }
 
     @Test
@@ -392,7 +392,7 @@
                 ACTIVITY_TYPE_STANDARD, false /* onTop */));
         final Task task = new TaskBuilder(mSupervisor).setStack(targetStack).build();
         final ActivityRecord activity = new ActivityBuilder(mService).setTask(task).build();
-        display.positionChildAtBottom(targetStack);
+        display.positionStackAtBottom(targetStack);
 
         // Assume the stack is not at the topmost position (e.g. behind always-on-top stacks) but it
         // is the current top focused stack.
@@ -493,7 +493,7 @@
         final Task task = new TaskBuilder(mSupervisor).setStack(targetStack).build();
         final ActivityRecord activity = new ActivityBuilder(mService).setTask(task).build();
         activity.setState(ActivityState.RESUMED, "test");
-        display.positionChildAtBottom(targetStack);
+        display.positionStackAtBottom(targetStack);
 
         // Assume the stack is at the topmost position
         assertFalse(targetStack.isTopStackOnDisplay());
diff --git a/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java
index eba2bc8..5c9ccae 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RunningTasksTest.java
@@ -74,7 +74,7 @@
         final int numTasks = 10;
         int activeTime = 0;
         for (int i = 0; i < numTasks; i++) {
-            createTask(display.getChildAt(i % numStacks), ".Task" + i, i, activeTime++);
+            createTask(display.getStackAt(i % numStacks), ".Task" + i, i, activeTime++);
         }
 
         // Ensure that the latest tasks were returned in order of decreasing last active time,
diff --git a/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java
index c483489..3008a75 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimationRunnerTest.java
@@ -50,6 +50,7 @@
 
 import org.junit.Before;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
@@ -63,6 +64,7 @@
  */
 @SmallTest
 @Presubmit
+@RunWith(WindowTestRunner.class)
 public class SurfaceAnimationRunnerTest extends WindowTestsBase {
 
     @Mock SurfaceControl mMockSurface;
@@ -98,6 +100,7 @@
         verify(mMockTransaction, atLeastOnce()).setMatrix(eq(mMockSurface), eq(m), any());
         verify(mMockTransaction, atLeastOnce()).setAlpha(eq(mMockSurface), eq(1.0f));
 
+        waitHandlerIdle(SurfaceAnimationThread.getHandler());
         mFinishCallbackLatch.await(1, SECONDS);
         assertFinishCallbackCalled();
 
@@ -176,6 +179,7 @@
         assertTrue(mSurfaceAnimationRunner.mRunningAnimations.isEmpty());
         mSurfaceAnimationRunner.continueStartingAnimations();
         waitUntilNextFrame();
+        waitHandlerIdle(SurfaceAnimationThread.getHandler());
         assertFalse(mSurfaceAnimationRunner.mRunningAnimations.isEmpty());
         mFinishCallbackLatch.await(1, SECONDS);
         assertFinishCallbackCalled();
diff --git a/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java
index 979aab6..2894241 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SurfaceAnimatorTest.java
@@ -44,6 +44,7 @@
 import org.junit.After;
 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;
@@ -56,6 +57,7 @@
  */
 @SmallTest
 @Presubmit
+@RunWith(WindowTestRunner.class)
 public class SurfaceAnimatorTest extends WindowTestsBase {
 
     @Mock AnimationAdapter mSpec;
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
index 27ebd5a..9a91efe 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskLaunchParamsModifierTests.java
@@ -53,6 +53,7 @@
 
 import org.junit.Before;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -66,6 +67,7 @@
  */
 @SmallTest
 @Presubmit
+@RunWith(WindowTestRunner.class)
 public class TaskLaunchParamsModifierTests extends ActivityTestsBase {
     private static final Rect DISPLAY_BOUNDS = new Rect(/* left */ 0, /* top */ 0,
             /* right */ 1920, /* bottom */ 1080);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java
index 9275512..8970571 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskPositionerTests.java
@@ -19,7 +19,6 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
 
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
 import static com.android.server.wm.TaskPositioner.MIN_ASPECT;
 import static com.android.server.wm.WindowManagerService.dipToPixel;
@@ -33,7 +32,6 @@
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 
-import android.app.IActivityTaskManager;
 import android.graphics.Rect;
 import android.platform.test.annotations.Presubmit;
 import android.util.DisplayMetrics;
@@ -46,6 +44,7 @@
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 
 /**
  * Tests for the {@link TaskPositioner} class.
@@ -55,6 +54,7 @@
  */
 @SmallTest
 @Presubmit
+@RunWith(WindowTestRunner.class)
 public class TaskPositionerTests extends WindowTestsBase {
 
     private static final boolean DEBUGGING = false;
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotCacheTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotCacheTest.java
index f595e05..0274b7d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotCacheTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotCacheTest.java
@@ -18,21 +18,21 @@
 
 import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
 
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
 import static junit.framework.Assert.assertEquals;
 
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import android.app.ActivityManager.TaskSnapshot;
 import android.platform.test.annotations.Presubmit;
 
 import androidx.test.filters.SmallTest;
 
-import android.app.ActivityManager.TaskSnapshot;
-
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
 import org.junit.Before;
 import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
 
 /**
  * Test class for {@link TaskSnapshotCache}.
@@ -42,6 +42,7 @@
  */
 @SmallTest
 @Presubmit
+@RunWith(WindowTestRunner.class)
 public class TaskSnapshotCacheTest extends TaskSnapshotPersisterTestBase {
 
     private TaskSnapshotCache mCache;
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
index 3b11003..6ebaf29 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotControllerTest.java
@@ -33,6 +33,7 @@
 import com.google.android.collect.Sets;
 
 import org.junit.Test;
+import org.junit.runner.RunWith;
 
 /**
  * Test class for {@link TaskSnapshotController}.
@@ -42,6 +43,7 @@
  */
 @SmallTest
 @Presubmit
+@RunWith(WindowTestRunner.class)
 public class TaskSnapshotControllerTest extends WindowTestsBase {
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java
index b29453a..b5a5790 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotPersisterLoaderTest.java
@@ -38,6 +38,7 @@
 import com.android.server.wm.TaskSnapshotPersister.RemoveObsoleteFilesQueueItem;
 
 import org.junit.Test;
+import org.junit.runner.RunWith;
 
 import java.io.File;
 import java.util.function.Predicate;
@@ -50,6 +51,7 @@
  */
 @MediumTest
 @Presubmit
+@RunWith(WindowTestRunner.class)
 public class TaskSnapshotPersisterLoaderTest extends TaskSnapshotPersisterTestBase {
 
     private static final Rect TEST_INSETS = new Rect(10, 20, 30, 40);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
index 74db820..817344f 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
@@ -47,6 +47,7 @@
 import com.android.server.wm.TaskSnapshotSurface.Window;
 
 import org.junit.Test;
+import org.junit.runner.RunWith;
 
 /**
  * Test class for {@link TaskSnapshotSurface}.
@@ -56,6 +57,7 @@
  */
 @SmallTest
 @Presubmit
+@RunWith(WindowTestRunner.class)
 public class TaskSnapshotSurfaceTest extends WindowTestsBase {
 
     private TaskSnapshotSurface mSurface;
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskStackContainersTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskStackContainersTests.java
index dbf61db..6ad9f74 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskStackContainersTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskStackContainersTests.java
@@ -19,6 +19,9 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
+
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.junit.Assert.assertEquals;
@@ -112,6 +115,10 @@
 
     @Test
     public void testDisplayPositionWithPinnedStack() {
+        // Make sure the display is system owned display which capable to move the stack to top.
+        spyOn(mDisplayContent);
+        doReturn(false).when(mDisplayContent).isUntrustedVirtualDisplay();
+
         // The display contains pinned stack that was added in {@link #setUp}.
         final ActivityStack stack = createTaskStackOnDisplay(mDisplayContent);
         final Task task = createTaskInStack(stack, 0 /* userId */);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowConfigurationTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowConfigurationTests.java
index 64ac547..c359fb1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowConfigurationTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowConfigurationTests.java
@@ -48,6 +48,7 @@
 
 import org.junit.Before;
 import org.junit.Test;
+import org.junit.runner.RunWith;
 
 /**
  * Test class to for {@link android.app.WindowConfiguration}.
@@ -57,6 +58,7 @@
  */
 @SmallTest
 @Presubmit
+@RunWith(WindowTestRunner.class)
 public class WindowConfigurationTests extends WindowTestsBase {
     private Rect mParentBounds;
 
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
index 8140045..13cf22d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
@@ -56,6 +56,7 @@
 import androidx.test.filters.SmallTest;
 
 import org.junit.Test;
+import org.junit.runner.RunWith;
 import org.mockito.Mockito;
 
 import java.util.Comparator;
@@ -68,6 +69,7 @@
  */
 @SmallTest
 @Presubmit
+@RunWith(WindowTestRunner.class)
 public class WindowContainerTests extends WindowTestsBase {
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTraversalTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTraversalTests.java
index 4b666f5..8936aad 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTraversalTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTraversalTests.java
@@ -31,6 +31,7 @@
 import androidx.test.filters.SmallTest;
 
 import org.junit.Test;
+import org.junit.runner.RunWith;
 
 import java.util.function.Consumer;
 
@@ -42,6 +43,7 @@
  */
 @SmallTest
 @Presubmit
+@RunWith(WindowTestRunner.class)
 public class WindowContainerTraversalTests extends WindowTestsBase {
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerSettingsTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerSettingsTests.java
index 1c9eed2..ce6efdf 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerSettingsTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerSettingsTests.java
@@ -32,6 +32,7 @@
 import androidx.test.filters.SmallTest;
 
 import org.junit.Test;
+import org.junit.runner.RunWith;
 
 /**
  * Test for {@link WindowManagerService.SettingsObserver}.
@@ -40,6 +41,7 @@
  *  atest WmTests:WindowManagerSettingsTests
  */
 @SmallTest
+@RunWith(WindowTestRunner.class)
 public class WindowManagerSettingsTests extends WindowTestsBase {
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index eed5ef5..72baedb 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -222,7 +222,8 @@
         final WindowState appWindow = createWindow(null, TYPE_APPLICATION, "appWindow");
         final WindowState imeWindow = createWindow(null, TYPE_INPUT_METHOD, "imeWindow");
 
-        // Setting FLAG_NOT_FOCUSABLE prevents the window from being an IME target.
+        // Setting FLAG_NOT_FOCUSABLE without FLAG_ALT_FOCUSABLE_IM prevents the window from being
+        // an IME target.
         appWindow.mAttrs.flags |= FLAG_NOT_FOCUSABLE;
         imeWindow.mAttrs.flags |= FLAG_NOT_FOCUSABLE;
 
@@ -230,7 +231,7 @@
         appWindow.setHasSurface(true);
         imeWindow.setHasSurface(true);
 
-        // Windows with FLAG_NOT_FOCUSABLE can't be IME targets
+        // Windows without flags (FLAG_NOT_FOCUSABLE|FLAG_ALT_FOCUSABLE_IM) can't be IME targets
         assertFalse(appWindow.canBeImeTarget());
         assertFalse(imeWindow.canBeImeTarget());
 
@@ -238,16 +239,10 @@
         appWindow.mAttrs.flags |= (FLAG_NOT_FOCUSABLE | FLAG_ALT_FOCUSABLE_IM);
         imeWindow.mAttrs.flags |= (FLAG_NOT_FOCUSABLE | FLAG_ALT_FOCUSABLE_IM);
 
-        // Visible app window with flags FLAG_NOT_FOCUSABLE or FLAG_ALT_FOCUSABLE_IM can't be IME
-        // target while an IME window can never be an IME target regardless of its visibility
-        // or flags.
-        assertFalse(appWindow.canBeImeTarget());
-        assertFalse(imeWindow.canBeImeTarget());
-
-        appWindow.mAttrs.flags &= ~FLAG_ALT_FOCUSABLE_IM;
-        assertFalse(appWindow.canBeImeTarget());
-        appWindow.mAttrs.flags &= ~FLAG_NOT_FOCUSABLE;
+        // Visible app window with flags can be IME target while an IME window can never be an IME
+        // target regardless of its visibility or flags.
         assertTrue(appWindow.canBeImeTarget());
+        assertFalse(imeWindow.canBeImeTarget());
 
         // Make windows invisible
         appWindow.hideLw(false /* doAnimation */);
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index 437b84b..22ba90b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -320,7 +320,7 @@
         synchronized (mWm.mGlobalLock) {
             return new ActivityTestsBase.StackBuilder(
                     dc.mWmService.mAtmService.mRootActivityContainer)
-                    .setDisplay(dc.mActivityDisplay)
+                    .setDisplay(dc)
                     .setWindowingMode(windowingMode)
                     .setActivityType(activityType)
                     .setCreateActivity(false)
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
index 4cdbea0..e6aed49 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTokenTests.java
@@ -31,6 +31,7 @@
 import androidx.test.filters.SmallTest;
 
 import org.junit.Test;
+import org.junit.runner.RunWith;
 
 /**
  * Tests for the {@link WindowToken} class.
@@ -40,6 +41,7 @@
  */
 @SmallTest
 @Presubmit
+@RunWith(WindowTestRunner.class)
 public class WindowTokenTests extends WindowTestsBase {
 
     @Test
diff --git a/services/usage/java/com/android/server/usage/IntervalStats.java b/services/usage/java/com/android/server/usage/IntervalStats.java
index 46b261b..8fb283a 100644
--- a/services/usage/java/com/android/server/usage/IntervalStats.java
+++ b/services/usage/java/com/android/server/usage/IntervalStats.java
@@ -448,8 +448,11 @@
     /**
      * Parses all of the tokens to strings in the obfuscated usage stats data. This includes
      * deobfuscating each of the package tokens and chooser actions and categories.
+     *
+     * @return {@code true} if any stats were omitted while deobfuscating, {@code false} otherwise.
      */
-    private void deobfuscateUsageStats(PackagesTokenData packagesTokenData) {
+    private boolean deobfuscateUsageStats(PackagesTokenData packagesTokenData) {
+        boolean dataOmitted = false;
         final int usageStatsSize = packageStatsObfuscated.size();
         for (int statsIndex = 0; statsIndex < usageStatsSize; statsIndex++) {
             final int packageToken = packageStatsObfuscated.keyAt(statsIndex);
@@ -457,6 +460,7 @@
             usageStats.mPackageName = packagesTokenData.getPackageString(packageToken);
             if (usageStats.mPackageName == null) {
                 Slog.e(TAG, "Unable to parse usage stats package " + packageToken);
+                dataOmitted = true;
                 continue;
             }
 
@@ -489,14 +493,18 @@
             }
             packageStats.put(usageStats.mPackageName, usageStats);
         }
+        return dataOmitted;
     }
 
     /**
      * Parses all of the tokens to strings in the obfuscated events data. This includes
      * deobfuscating the package token, along with any class, task root package/class tokens, and
      * shortcut or notification channel tokens.
+     *
+     * @return {@code true} if any events were omitted while deobfuscating, {@code false} otherwise.
      */
-    private void deobfuscateEvents(PackagesTokenData packagesTokenData) {
+    private boolean deobfuscateEvents(PackagesTokenData packagesTokenData) {
+        boolean dataOmitted = false;
         for (int i = this.events.size() - 1; i >= 0; i--) {
             final Event event = this.events.get(i);
             final int packageToken = event.mPackageToken;
@@ -504,6 +512,7 @@
             if (event.mPackage == null) {
                 Slog.e(TAG, "Unable to parse event package " + packageToken);
                 this.events.remove(i);
+                dataOmitted = true;
                 continue;
             }
 
@@ -543,6 +552,7 @@
                         Slog.e(TAG, "Unable to parse shortcut " + event.mShortcutIdToken
                                 + " for package " + packageToken);
                         this.events.remove(i);
+                        dataOmitted = true;
                         continue;
                     }
                     break;
@@ -554,21 +564,25 @@
                                 + event.mNotificationChannelIdToken + " for package "
                                 + packageToken);
                         this.events.remove(i);
+                        dataOmitted = true;
                         continue;
                     }
                     break;
             }
         }
+        return dataOmitted;
     }
 
     /**
      * Parses the obfuscated tokenized data held in this interval stats object.
      *
+     * @return {@code true} if any data was omitted while deobfuscating, {@code false} otherwise.
      * @hide
      */
-    public void deobfuscateData(PackagesTokenData packagesTokenData) {
-        deobfuscateUsageStats(packagesTokenData);
-        deobfuscateEvents(packagesTokenData);
+    public boolean deobfuscateData(PackagesTokenData packagesTokenData) {
+        final boolean statsOmitted = deobfuscateUsageStats(packagesTokenData);
+        final boolean eventsOmitted = deobfuscateEvents(packagesTokenData);
+        return statsOmitted || eventsOmitted;
     }
 
     /**
diff --git a/services/usage/java/com/android/server/usage/PackagesTokenData.java b/services/usage/java/com/android/server/usage/PackagesTokenData.java
index 4bf08a4..f19abbb 100644
--- a/services/usage/java/com/android/server/usage/PackagesTokenData.java
+++ b/services/usage/java/com/android/server/usage/PackagesTokenData.java
@@ -162,15 +162,18 @@
      *
      * @param packageName the package to be removed
      * @param timeRemoved the time stamp of when the package was removed
+     * @return the token mapped to the package removed or {@code PackagesTokenData.UNASSIGNED_TOKEN}
+     *         if not mapped
      */
-    public void removePackage(String packageName, long timeRemoved) {
+    public int removePackage(String packageName, long timeRemoved) {
         removedPackagesMap.put(packageName, timeRemoved);
 
         if (!packagesToTokensMap.containsKey(packageName)) {
-            return;
+            return UNASSIGNED_TOKEN;
         }
         final int packageToken = packagesToTokensMap.get(packageName).get(packageName);
         packagesToTokensMap.remove(packageName);
         tokensToPackagesMap.delete(packageToken);
+        return packageToken;
     }
 }
diff --git a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
index 27d7360..e34824c 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java
@@ -29,6 +29,7 @@
 import android.util.TimeUtils;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.IndentingPrintWriter;
 
 import libcore.io.IoUtils;
@@ -52,6 +53,7 @@
 import java.nio.file.Files;
 import java.nio.file.StandardCopyOption;
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
 
 /**
@@ -122,6 +124,7 @@
     private int mCurrentVersion;
     private boolean mFirstUpdate;
     private boolean mNewUpdate;
+    private boolean mUpgradePerformed;
 
     // The obfuscated packages to tokens mappings file
     private final File mPackageMappingsFile;
@@ -325,6 +328,13 @@
         return mNewUpdate;
     }
 
+    /**
+     * Was an upgrade performed when this database was initialized?
+     */
+    boolean wasUpgradePerformed() {
+        return mUpgradePerformed;
+    }
+
     private void checkVersionAndBuildLocked() {
         int version;
         String buildFingerprint;
@@ -397,6 +407,8 @@
         if (mUpdateBreadcrumb.exists()) {
             // Files should be up to date with current version. Clear the version update breadcrumb
             mUpdateBreadcrumb.delete();
+            // update mUpgradePerformed after breadcrumb is deleted to indicate a successful upgrade
+            mUpgradePerformed = true;
         }
 
         if (mBackupsDir.exists() && !KEEP_BACKUP_DIR) {
@@ -545,12 +557,127 @@
         }
     }
 
-    void onPackageRemoved(String packageName, long timeRemoved) {
+    /**
+     * Returns the token mapped to the package removed or {@code PackagesTokenData.UNASSIGNED_TOKEN}
+     * if not mapped.
+     */
+    int onPackageRemoved(String packageName, long timeRemoved) {
         synchronized (mLock) {
-            mPackagesTokenData.removePackage(packageName, timeRemoved);
+            final int tokenRemoved = mPackagesTokenData.removePackage(packageName, timeRemoved);
+            try {
+                writeMappingsLocked();
+            } catch (Exception e) {
+                Slog.w(TAG, "Unable to update package mappings on disk after removing token "
+                        + tokenRemoved);
+            }
+            return tokenRemoved;
         }
     }
 
+    /**
+     * Reads all the usage stats data on disk and rewrites it with any data related to uninstalled
+     * packages omitted. Returns {@code true} on success, {@code false} otherwise.
+     */
+    boolean pruneUninstalledPackagesData() {
+        synchronized (mLock) {
+            for (int i = 0; i < mIntervalDirs.length; i++) {
+                final File[] files = mIntervalDirs[i].listFiles();
+                if (files == null) {
+                    continue;
+                }
+                for (int j = 0; j < files.length; j++) {
+                    try {
+                        final IntervalStats stats = new IntervalStats();
+                        final AtomicFile atomicFile = new AtomicFile(files[j]);
+                        if (!readLocked(atomicFile, stats, mCurrentVersion, mPackagesTokenData)) {
+                            continue; // no data was omitted when read so no need to rewrite
+                        }
+                        // Any data related to packages that have been removed would have failed
+                        // the deobfuscation step on read so the IntervalStats object here only
+                        // contains data for packages that are currently installed - all we need
+                        // to do here is write the data back to disk.
+                        writeLocked(atomicFile, stats, mCurrentVersion, mPackagesTokenData);
+                    } catch (Exception e) {
+                        Slog.e(TAG, "Failed to prune data from: " + files[j].toString());
+                        return false;
+                    }
+                }
+            }
+
+            try {
+                writeMappingsLocked();
+            } catch (IOException e) {
+                Slog.e(TAG, "Failed to write package mappings after pruning data.");
+                return false;
+            }
+            return true;
+        }
+    }
+
+    /**
+     * Iterates through all the files on disk and prunes any data that belongs to packages that have
+     * been uninstalled (packages that are not in the given list).
+     * Note: this should only be called once, when there has been a database upgrade.
+     *
+     * @param installedPackages map of installed packages (package_name:package_install_time)
+     */
+    void prunePackagesDataOnUpgrade(HashMap<String, Long> installedPackages) {
+        if (ArrayUtils.isEmpty(installedPackages)) {
+            return;
+        }
+        synchronized (mLock) {
+            for (int i = 0; i < mIntervalDirs.length; i++) {
+                final File[] files = mIntervalDirs[i].listFiles();
+                if (files == null) {
+                    continue;
+                }
+                for (int j = 0; j < files.length; j++) {
+                    try {
+                        final IntervalStats stats = new IntervalStats();
+                        final AtomicFile atomicFile = new AtomicFile(files[j]);
+                        readLocked(atomicFile, stats, mCurrentVersion, mPackagesTokenData);
+                        if (!pruneStats(installedPackages, stats)) {
+                            continue; // no stats were pruned so no need to rewrite
+                        }
+                        writeLocked(atomicFile, stats, mCurrentVersion, mPackagesTokenData);
+                    } catch (Exception e) {
+                        Slog.e(TAG, "Failed to prune data from: " + files[j].toString());
+                    }
+                }
+            }
+        }
+    }
+
+    private boolean pruneStats(HashMap<String, Long> installedPackages, IntervalStats stats) {
+        boolean dataPruned = false;
+
+        // prune old package usage stats
+        for (int i = stats.packageStats.size() - 1; i >= 0; i--) {
+            final UsageStats usageStats = stats.packageStats.valueAt(i);
+            final Long timeInstalled = installedPackages.get(usageStats.mPackageName);
+            if (timeInstalled == null || timeInstalled > usageStats.mEndTimeStamp) {
+                stats.packageStats.removeAt(i);
+                dataPruned = true;
+            }
+        }
+        if (dataPruned) {
+            // ensure old stats don't linger around during the obfuscation step on write
+            stats.packageStatsObfuscated.clear();
+        }
+
+        // prune old events
+        for (int i = stats.events.size() - 1; i >= 0; i--) {
+            final UsageEvents.Event event = stats.events.get(i);
+            final Long timeInstalled = installedPackages.get(event.mPackage);
+            if (timeInstalled == null || timeInstalled > event.mTimeStamp) {
+                stats.events.remove(i);
+                dataPruned = true;
+            }
+        }
+
+        return dataPruned;
+    }
+
     public void onTimeChanged(long timeDiffMillis) {
         synchronized (mLock) {
             StringBuilder logBuilder = new StringBuilder();
@@ -645,7 +772,6 @@
         }
 
         // filter out events
-        final int eventsSize = stats.events.size();
         for (int i = stats.events.size() - 1; i >= 0; i--) {
             final UsageEvents.Event event = stats.events.get(i);
             final Long timeRemoved = removedPackagesMap.get(event.mPackage);
@@ -942,13 +1068,17 @@
         readLocked(file, statsOut, mCurrentVersion, mPackagesTokenData);
     }
 
-    private static void readLocked(AtomicFile file, IntervalStats statsOut, int version,
+    /**
+     * Returns {@code true} if any stats were omitted while reading, {@code false} otherwise.
+     */
+    private static boolean readLocked(AtomicFile file, IntervalStats statsOut, int version,
             PackagesTokenData packagesTokenData) throws IOException {
+        boolean dataOmitted = false;
         try {
             FileInputStream in = file.openRead();
             try {
                 statsOut.beginTime = parseBeginTime(file);
-                readLocked(in, statsOut, version, packagesTokenData);
+                dataOmitted = readLocked(in, statsOut, version, packagesTokenData);
                 statsOut.lastTimeSaved = file.getLastModifiedTime();
             } finally {
                 try {
@@ -961,10 +1091,15 @@
             Slog.e(TAG, "UsageStatsDatabase", e);
             throw e;
         }
+        return dataOmitted;
     }
 
-    private static void readLocked(InputStream in, IntervalStats statsOut, int version,
+    /**
+     * Returns {@code true} if any stats were omitted while reading, {@code false} otherwise.
+     */
+    private static boolean readLocked(InputStream in, IntervalStats statsOut, int version,
             PackagesTokenData packagesTokenData) throws IOException {
+        boolean dataOmitted = false;
         switch (version) {
             case 1:
             case 2:
@@ -989,14 +1124,14 @@
                 } catch (IOException e) {
                     Slog.e(TAG, "Unable to read interval stats from proto.", e);
                 }
-                statsOut.deobfuscateData(packagesTokenData);
+                dataOmitted = statsOut.deobfuscateData(packagesTokenData);
                 break;
             default:
                 throw new RuntimeException(
                         "Unhandled UsageStatsDatabase version: " + Integer.toString(version)
                                 + " on read.");
         }
-
+        return dataOmitted;
     }
 
     /**
diff --git a/services/usage/java/com/android/server/usage/UsageStatsIdleService.java b/services/usage/java/com/android/server/usage/UsageStatsIdleService.java
new file mode 100644
index 0000000..4468871
--- /dev/null
+++ b/services/usage/java/com/android/server/usage/UsageStatsIdleService.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.usage;
+
+import android.app.job.JobInfo;
+import android.app.job.JobParameters;
+import android.app.job.JobScheduler;
+import android.app.job.JobService;
+import android.app.usage.UsageStatsManagerInternal;
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.AsyncTask;
+import android.os.PersistableBundle;
+
+import com.android.server.LocalServices;
+
+/**
+ * JobService used to do any work for UsageStats while the device is idle.
+ */
+public class UsageStatsIdleService extends JobService {
+
+    /**
+     * Base job ID for the pruning job - must be unique within the system server uid.
+     */
+    private static final int PRUNE_JOB_ID = 546357475;
+
+    private static final String USER_ID_KEY = "user_id";
+
+    static void scheduleJob(Context context, int userId) {
+        final int userJobId = PRUNE_JOB_ID + userId; // unique job id per user
+        final ComponentName component = new ComponentName(context.getPackageName(),
+                UsageStatsIdleService.class.getName());
+        final PersistableBundle bundle = new PersistableBundle();
+        bundle.putInt(USER_ID_KEY, userId);
+        final JobInfo pruneJob = new JobInfo.Builder(userJobId, component)
+                .setRequiresDeviceIdle(true)
+                .setExtras(bundle)
+                .setPersisted(true)
+                .build();
+
+        final JobScheduler jobScheduler = context.getSystemService(JobScheduler.class);
+        final JobInfo pendingPruneJob = jobScheduler.getPendingJob(userJobId);
+        // only schedule a new prune job if one doesn't exist already for this user
+        if (!pruneJob.equals(pendingPruneJob)) {
+            jobScheduler.cancel(userJobId); // cancel any previously scheduled prune job
+            jobScheduler.schedule(pruneJob);
+        }
+
+    }
+
+    static void cancelJob(Context context, int userId) {
+        final int userJobId = PRUNE_JOB_ID + userId; // unique job id per user
+        final JobScheduler jobScheduler = context.getSystemService(JobScheduler.class);
+        jobScheduler.cancel(userJobId);
+    }
+
+    @Override
+    public boolean onStartJob(JobParameters params) {
+        final PersistableBundle bundle = params.getExtras();
+        final int userId = bundle.getInt(USER_ID_KEY, -1);
+        if (userId == -1) {
+            return false;
+        }
+
+        AsyncTask.execute(() -> {
+            final UsageStatsManagerInternal usageStatsManagerInternal = LocalServices.getService(
+                    UsageStatsManagerInternal.class);
+            final boolean pruned = usageStatsManagerInternal.pruneUninstalledPackagesData(userId);
+            jobFinished(params, !pruned); // reschedule if data was not pruned
+        });
+        return true;
+    }
+
+    @Override
+    public boolean onStopJob(JobParameters params) {
+        // Since the pruning job isn't a heavy job, we don't want to cancel it's execution midway.
+        return false;
+    }
+}
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index ba4a448..0649223 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -52,6 +52,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.ParceledListSlice;
@@ -99,6 +100,7 @@
 import java.nio.file.Files;
 import java.nio.file.StandardCopyOption;
 import java.util.Arrays;
+import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Set;
@@ -318,6 +320,8 @@
     }
 
     private void onUserUnlocked(int userId) {
+        // fetch the installed packages outside the lock so it doesn't block package manager.
+        final HashMap<String, Long> installedPackages = getInstalledPackages(userId);
         synchronized (mLock) {
             // Create a user unlocked event to report
             final Event unlockEvent = new Event(USER_UNLOCKED, SystemClock.elapsedRealtime());
@@ -334,9 +338,10 @@
             }
             boolean needToFlush = !pendingEvents.isEmpty();
 
+            initializeUserUsageStatsServiceLocked(userId, System.currentTimeMillis(),
+                    installedPackages);
             mUserUnlockedStates.put(userId, true);
-            final UserUsageStatsService userService = getUserDataAndInitializeIfNeededLocked(
-                    userId, System.currentTimeMillis());
+            final UserUsageStatsService userService = getUserUsageStatsServiceLocked(userId);
             if (userService == null) {
                 Slog.i(TAG, "Attempted to unlock stopped or removed user " + userId);
                 return;
@@ -361,6 +366,29 @@
         }
     }
 
+    /**
+     * Fetches a map (package_name:install_time) of installed packages for the given user. This
+     * map contains all installed packages, including those packages which have been uninstalled
+     * with the DONT_DELETE_DATA flag.
+     * This is a helper method which should only be called when the given user's usage stats service
+     * is initialized; it performs a heavy query to package manager so do not call it otherwise.
+     * <br/>
+     * Note: DO NOT call this while holding the usage stats lock ({@code mLock}).
+     */
+    private HashMap<String, Long> getInstalledPackages(int userId) {
+        if (mPackageManager == null) {
+            return null;
+        }
+        final List<PackageInfo> installedPackages = mPackageManager.getInstalledPackagesAsUser(
+                PackageManager.MATCH_UNINSTALLED_PACKAGES, userId);
+        final HashMap<String, Long> packagesMap = new HashMap<>();
+        for (int i = installedPackages.size() - 1; i >= 0; i--) {
+            final PackageInfo packageInfo = installedPackages.get(i);
+            packagesMap.put(packageInfo.packageName, packageInfo.firstInstallTime);
+        }
+        return packagesMap;
+    }
+
     private DevicePolicyManagerInternal getDpmInternal() {
         if (mDpmInternal == null) {
             mDpmInternal = LocalServices.getService(DevicePolicyManagerInternal.class);
@@ -450,31 +478,42 @@
         }
     }
 
-    private UserUsageStatsService getUserDataAndInitializeIfNeededLocked(int userId,
-            long currentTimeMillis) {
-        UserUsageStatsService service = mUserState.get(userId);
+    /**
+     * This should the be only way to fetch the usage stats service for a specific user.
+     */
+    private UserUsageStatsService getUserUsageStatsServiceLocked(int userId) {
+        final UserUsageStatsService service = mUserState.get(userId);
         if (service == null) {
-            final File usageStatsDir = new File(Environment.getDataSystemCeDirectory(userId),
-                    "usagestats");
-            service = new UserUsageStatsService(getContext(), userId, usageStatsDir, this);
-            if (mUserUnlockedStates.get(userId)) {
-                try {
-                    service.init(currentTimeMillis);
-                    mUserState.put(userId, service);
-                } catch (Exception e) {
-                    if (mUserManager.isUserUnlocked(userId)) {
-                        throw e; // rethrow exception - user is unlocked
-                    } else {
-                        Slog.w(TAG, "Attempted to initialize service for "
-                                + "stopped or removed user " + userId);
-                        return null;
-                    }
-                }
-            }
+            Slog.wtf(TAG, "Failed to fetch usage stats service for user " + userId + ". "
+                    + "The user might not have been initialized yet.");
         }
         return service;
     }
 
+    /**
+     * Initializes the given user's usage stats service - this should ideally only be called once,
+     * when the user is initially unlocked.
+     */
+    private void initializeUserUsageStatsServiceLocked(int userId,
+            long currentTimeMillis, HashMap<String, Long> installedPackages) {
+        final File usageStatsDir = new File(Environment.getDataSystemCeDirectory(userId),
+                "usagestats");
+        final UserUsageStatsService service = new UserUsageStatsService(getContext(), userId,
+                usageStatsDir, this);
+        try {
+            service.init(currentTimeMillis, installedPackages);
+            mUserState.put(userId, service);
+        } catch (Exception e) {
+            if (mUserManager.isUserUnlocked(userId)) {
+                Slog.w(TAG, "Failed to initialized unlocked user " + userId);
+                throw e; // rethrow the exception - user is unlocked
+            } else {
+                Slog.w(TAG, "Attempted to initialize service for stopped or removed user "
+                        + userId);
+            }
+        }
+    }
+
     private void migrateStatsToSystemCeIfNeededLocked(int userId) {
         final File usageStatsDir = new File(Environment.getDataSystemCeDirectory(userId),
                 "usagestats");
@@ -694,7 +733,6 @@
                 return;
             }
 
-            final long timeNow = System.currentTimeMillis();
             final long elapsedRealtime = SystemClock.elapsedRealtime();
 
             if (event.mPackage != null
@@ -789,8 +827,7 @@
                     break;
             }
 
-            final UserUsageStatsService service =
-                    getUserDataAndInitializeIfNeededLocked(userId, timeNow);
+            final UserUsageStatsService service = getUserUsageStatsServiceLocked(userId);
             if (service == null) {
                 return; // user was stopped or removed
             }
@@ -841,15 +878,18 @@
             mAppStandby.onUserRemoved(userId);
             mAppTimeLimit.onUserRemoved(userId);
         }
+        // Cancel any scheduled jobs for this user since the user is being removed.
+        UsageStatsIdleService.cancelJob(getContext(), userId);
     }
 
     /**
      * Called by the Handler for message MSG_PACKAGE_REMOVED.
      */
     private void onPackageRemoved(int userId, String packageName) {
+        final int tokenRemoved;
         synchronized (mLock) {
             final long timeRemoved = System.currentTimeMillis();
-            if (!mUserUnlockedStates.get(userId, false)) {
+            if (!mUserUnlockedStates.get(userId)) {
                 // If user is not unlocked and a package is removed for them, we will handle it
                 // when the user service is initialized and package manager is queried.
                 return;
@@ -859,7 +899,30 @@
                 return;
             }
 
-            userService.onPackageRemoved(packageName, timeRemoved);
+            tokenRemoved = userService.onPackageRemoved(packageName, timeRemoved);
+        }
+
+        // Schedule a job to prune any data related to this package.
+        if (tokenRemoved != PackagesTokenData.UNASSIGNED_TOKEN) {
+            UsageStatsIdleService.scheduleJob(getContext(), userId);
+        }
+    }
+
+    /**
+     * Called by the Binder stub.
+     */
+    private boolean pruneUninstalledPackagesData(int userId) {
+        synchronized (mLock) {
+            if (!mUserUnlockedStates.get(userId)) {
+                return false; // user is no longer unlocked
+            }
+
+            final UserUsageStatsService userService = mUserState.get(userId);
+            if (userService == null) {
+                return false; // user was stopped or removed
+            }
+
+            return userService.pruneUninstalledPackagesData();
         }
     }
 
@@ -874,8 +937,7 @@
                 return null;
             }
 
-            final UserUsageStatsService service =
-                    getUserDataAndInitializeIfNeededLocked(userId, System.currentTimeMillis());
+            final UserUsageStatsService service = getUserUsageStatsServiceLocked(userId);
             if (service == null) {
                 return null; // user was stopped or removed
             }
@@ -909,8 +971,7 @@
                 return null;
             }
 
-            final UserUsageStatsService service =
-                    getUserDataAndInitializeIfNeededLocked(userId, System.currentTimeMillis());
+            final UserUsageStatsService service = getUserUsageStatsServiceLocked(userId);
             if (service == null) {
                 return null; // user was stopped or removed
             }
@@ -929,8 +990,7 @@
                 return null;
             }
 
-            final UserUsageStatsService service =
-                    getUserDataAndInitializeIfNeededLocked(userId, System.currentTimeMillis());
+            final UserUsageStatsService service = getUserUsageStatsServiceLocked(userId);
             if (service == null) {
                 return null; // user was stopped or removed
             }
@@ -949,8 +1009,7 @@
                 return null;
             }
 
-            final UserUsageStatsService service =
-                    getUserDataAndInitializeIfNeededLocked(userId, System.currentTimeMillis());
+            final UserUsageStatsService service = getUserUsageStatsServiceLocked(userId);
             if (service == null) {
                 return null; // user was stopped or removed
             }
@@ -969,8 +1028,7 @@
                 return null;
             }
 
-            final UserUsageStatsService service =
-                    getUserDataAndInitializeIfNeededLocked(userId, System.currentTimeMillis());
+            final UserUsageStatsService service = getUserUsageStatsServiceLocked(userId);
             if (service == null) {
                 return null; // user was stopped or removed
             }
@@ -2025,8 +2083,7 @@
 
                 // Check to ensure that only user 0's data is b/r for now
                 if (user == UserHandle.USER_SYSTEM) {
-                    final UserUsageStatsService userStats = getUserDataAndInitializeIfNeededLocked(
-                            user, System.currentTimeMillis());
+                    final UserUsageStatsService userStats = getUserUsageStatsServiceLocked(user);
                     if (userStats == null) {
                         return null; // user was stopped or removed
                     }
@@ -2046,8 +2103,7 @@
                 }
 
                 if (user == UserHandle.USER_SYSTEM) {
-                    final UserUsageStatsService userStats = getUserDataAndInitializeIfNeededLocked(
-                            user, System.currentTimeMillis());
+                    final UserUsageStatsService userStats = getUserUsageStatsServiceLocked(user);
                     if (userStats == null) {
                         return; // user was stopped or removed
                     }
@@ -2108,6 +2164,11 @@
         public AppUsageLimitData getAppUsageLimit(String packageName, UserHandle user) {
             return mAppTimeLimit.getAppUsageLimit(packageName, user);
         }
+
+        @Override
+        public boolean pruneUninstalledPackagesData(int userId) {
+            return UsageStatsService.this.pruneUninstalledPackagesData(userId);
+        }
     }
 
     private class MyPackageMonitor extends PackageMonitor {
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index c6a5fcf..179b649 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -34,10 +34,7 @@
 import android.app.usage.UsageStats;
 import android.app.usage.UsageStatsManager;
 import android.content.Context;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManagerInternal;
 import android.content.res.Configuration;
-import android.os.Process;
 import android.os.SystemClock;
 import android.text.format.DateUtils;
 import android.util.ArrayMap;
@@ -46,8 +43,8 @@
 import android.util.Slog;
 import android.util.SparseIntArray;
 
+import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.IndentingPrintWriter;
-import com.android.server.LocalServices;
 import com.android.server.usage.UsageStatsDatabase.StatCombiner;
 
 import java.io.File;
@@ -55,7 +52,7 @@
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Arrays;
-import java.util.HashSet;
+import java.util.HashMap;
 import java.util.List;
 
 /**
@@ -112,9 +109,12 @@
         mSystemTimeSnapshot = System.currentTimeMillis();
     }
 
-    void init(final long currentTimeMillis) {
-        readPackageMappingsLocked();
+    void init(final long currentTimeMillis, HashMap<String, Long> installedPackages) {
+        readPackageMappingsLocked(installedPackages);
         mDatabase.init(currentTimeMillis);
+        if (mDatabase.wasUpgradePerformed()) {
+            mDatabase.prunePackagesDataOnUpgrade(installedPackages);
+        }
 
         int nullCount = 0;
         for (int i = 0; i < mCurrentStats.length; i++) {
@@ -170,52 +170,53 @@
         persistActiveStats();
     }
 
-    void onPackageRemoved(String packageName, long timeRemoved) {
-        mDatabase.onPackageRemoved(packageName, timeRemoved);
+    int onPackageRemoved(String packageName, long timeRemoved) {
+        return mDatabase.onPackageRemoved(packageName, timeRemoved);
     }
 
-    private void readPackageMappingsLocked() {
+    private void readPackageMappingsLocked(HashMap<String, Long> installedPackages) {
         mDatabase.readMappingsLocked();
-        cleanUpPackageMappingsLocked();
+        updatePackageMappingsLocked(installedPackages);
     }
 
     /**
-     * Queries Package Manager for a list of installed packages and removes those packages from
-     * mPackagesTokenData which are not installed any more.
+     * Queries Job Scheduler for any pending data prune jobs and if any exist, it updates the
+     * package mappings in memory by removing those tokens.
      * This will only happen once per device boot, when the user is unlocked for the first time.
+     *
+     * @param installedPackages map of installed packages (package_name:package_install_time)
      */
-    private void cleanUpPackageMappingsLocked() {
-        final long timeNow = System.currentTimeMillis();
-        /*
-         Note (b/142501248): PackageManagerInternal#getInstalledApplications is not lightweight.
-         Once its implementation is updated, or it's replaced with a better alternative, update
-         the call here to use it. For now, using the heavy #getInstalledApplications is okay since
-         this clean-up is only performed once every boot.
-         */
-        final PackageManagerInternal packageManagerInternal =
-                LocalServices.getService(PackageManagerInternal.class);
-        if (packageManagerInternal == null) {
+    private void updatePackageMappingsLocked(HashMap<String, Long> installedPackages) {
+        if (ArrayUtils.isEmpty(installedPackages)) {
             return;
         }
-        final List<ApplicationInfo> installedPackages =
-                packageManagerInternal.getInstalledApplications(0, mUserId, Process.SYSTEM_UID);
-        // convert the package list to a set for easy look-ups
-        final HashSet<String> packagesSet = new HashSet<>(installedPackages.size());
-        for (int i = installedPackages.size() - 1; i >= 0; i--) {
-            packagesSet.add(installedPackages.get(i).packageName);
-        }
-        final List<String> removedPackages = new ArrayList<>();
+
+        final long timeNow = System.currentTimeMillis();
+        final ArrayList<String> removedPackages = new ArrayList<>();
         // populate list of packages that are found in the mappings but not in the installed list
         for (int i = mDatabase.mPackagesTokenData.packagesToTokensMap.size() - 1; i >= 0; i--) {
-            if (!packagesSet.contains(mDatabase.mPackagesTokenData.packagesToTokensMap.keyAt(i))) {
-                removedPackages.add(mDatabase.mPackagesTokenData.packagesToTokensMap.keyAt(i));
+            final String packageName = mDatabase.mPackagesTokenData.packagesToTokensMap.keyAt(i);
+            if (!installedPackages.containsKey(packageName)) {
+                removedPackages.add(packageName);
             }
         }
+        if (removedPackages.isEmpty()) {
+            return;
+        }
 
-        // remove packages in the mappings that are no longer installed
+        // remove packages in the mappings that are no longer installed and persist to disk
         for (int i = removedPackages.size() - 1; i >= 0; i--) {
             mDatabase.mPackagesTokenData.removePackage(removedPackages.get(i), timeNow);
         }
+        try {
+            mDatabase.writeMappingsLocked();
+        } catch (Exception e) {
+            Slog.w(TAG, "Unable to write updated package mappings file on service initialization.");
+        }
+    }
+
+    boolean pruneUninstalledPackagesData() {
+        return mDatabase.pruneUninstalledPackagesData();
     }
 
     private void onTimeChanged(long oldTime, long newTime) {
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index a0739c4..86ad795 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -291,7 +291,6 @@
          */
         public static final int DIRECTION_OUTGOING = 1;
 
-
         /** Call can currently be put on hold or unheld. */
         public static final int CAPABILITY_HOLD = 0x00000001;
 
@@ -571,6 +570,7 @@
         private final Bundle mIntentExtras;
         private final long mCreationTimeMillis;
         private final @CallDirection int mCallDirection;
+        private final @Connection.VerificationStatus int mCallerNumberVerificationStatus;
 
         /**
          * Whether the supplied capabilities  supports the specified capability.
@@ -880,6 +880,15 @@
             return mCallDirection;
         }
 
+        /**
+         * Gets the verification status for the phone number of an incoming call as identified in
+         * ATIS-1000082.
+         * @return the verification status.
+         */
+        public @Connection.VerificationStatus int getCallerNumberVerificationStatus() {
+            return mCallerNumberVerificationStatus;
+        }
+
         @Override
         public boolean equals(Object o) {
             if (o instanceof Details) {
@@ -901,7 +910,9 @@
                         areBundlesEqual(mExtras, d.mExtras) &&
                         areBundlesEqual(mIntentExtras, d.mIntentExtras) &&
                         Objects.equals(mCreationTimeMillis, d.mCreationTimeMillis) &&
-                        Objects.equals(mCallDirection, d.mCallDirection);
+                        Objects.equals(mCallDirection, d.mCallDirection) &&
+                        Objects.equals(mCallerNumberVerificationStatus,
+                                d.mCallerNumberVerificationStatus);
             }
             return false;
         }
@@ -923,7 +934,8 @@
                             mExtras,
                             mIntentExtras,
                             mCreationTimeMillis,
-                            mCallDirection);
+                            mCallDirection,
+                            mCallerNumberVerificationStatus);
         }
 
         /** {@hide} */
@@ -944,7 +956,8 @@
                 Bundle extras,
                 Bundle intentExtras,
                 long creationTimeMillis,
-                int callDirection) {
+                int callDirection,
+                int callerNumberVerificationStatus) {
             mTelecomCallId = telecomCallId;
             mHandle = handle;
             mHandlePresentation = handlePresentation;
@@ -962,6 +975,7 @@
             mIntentExtras = intentExtras;
             mCreationTimeMillis = creationTimeMillis;
             mCallDirection = callDirection;
+            mCallerNumberVerificationStatus = callerNumberVerificationStatus;
         }
 
         /** {@hide} */
@@ -983,7 +997,8 @@
                     parcelableCall.getExtras(),
                     parcelableCall.getIntentExtras(),
                     parcelableCall.getCreationTimeMillis(),
-                    parcelableCall.getCallDirection());
+                    parcelableCall.getCallDirection(),
+                    parcelableCall.getCallerNumberVerificationStatus());
         }
 
         @Override
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 4c22ba9..c063279 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -16,6 +16,7 @@
 
 package android.telecom;
 
+import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
@@ -35,9 +36,7 @@
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
 import android.os.SystemClock;
-import android.telephony.Annotation.RilRadioTechnology;
-import android.telephony.ServiceState;
-import android.telephony.TelephonyManager;
+import android.telephony.ims.ImsStreamMediaProfile;
 import android.util.ArraySet;
 import android.view.Surface;
 
@@ -50,6 +49,8 @@
 import java.io.IOException;
 import java.io.InputStreamReader;
 import java.io.OutputStreamWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.nio.channels.Channels;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -153,6 +154,32 @@
     public static final int STATE_PULLING_CALL = 7;
 
     /**
+     * Indicates that the network could not perform verification.
+     */
+    public static final int VERIFICATION_STATUS_NOT_VERIFIED = 0;
+
+    /**
+     * Indicates that verification by the network passed.  This indicates there is a high likelihood
+     * that the call originated from a valid source.
+     */
+    public static final int VERIFICATION_STATUS_PASSED = 1;
+
+    /**
+     * Indicates that verification by the network failed.  This indicates there is a high likelihood
+     * that the call did not originate from a valid source.
+     */
+    public static final int VERIFICATION_STATUS_FAILED = 2;
+
+    /**@hide*/
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "VERIFICATION_STATUS_", value = {
+            VERIFICATION_STATUS_NOT_VERIFIED,
+            VERIFICATION_STATUS_PASSED,
+            VERIFICATION_STATUS_FAILED
+    })
+    public @interface VerificationStatus {}
+
+    /**
      * Connection can currently be put on hold or unheld. This is distinct from
      * {@link #CAPABILITY_SUPPORT_HOLD} in that although a connection may support 'hold' most times,
      * it does not at the moment support the function. This can be true while the call is in the
@@ -475,6 +502,52 @@
     //**********************************************************************************************
 
     /**
+     * Define IMS Audio Codec
+     */
+    // Current audio codec is NONE
+    public static final int AUDIO_CODEC_NONE = ImsStreamMediaProfile.AUDIO_QUALITY_NONE; // 0
+    // Current audio codec is AMR
+    public static final int AUDIO_CODEC_AMR = ImsStreamMediaProfile.AUDIO_QUALITY_AMR; // 1
+    // Current audio codec is AMR_WB
+    public static final int AUDIO_CODEC_AMR_WB = ImsStreamMediaProfile.AUDIO_QUALITY_AMR_WB; // 2
+    // Current audio codec is QCELP13K
+    public static final int AUDIO_CODEC_QCELP13K = ImsStreamMediaProfile.AUDIO_QUALITY_QCELP13K; //3
+    // Current audio codec is EVRC
+    public static final int AUDIO_CODEC_EVRC = ImsStreamMediaProfile.AUDIO_QUALITY_EVRC; // 4
+    // Current audio codec is EVRC_B
+    public static final int AUDIO_CODEC_EVRC_B = ImsStreamMediaProfile.AUDIO_QUALITY_EVRC_B; // 5
+    // Current audio codec is EVRC_WB
+    public static final int AUDIO_CODEC_EVRC_WB = ImsStreamMediaProfile.AUDIO_QUALITY_EVRC_WB; // 6
+    // Current audio codec is EVRC_NW
+    public static final int AUDIO_CODEC_EVRC_NW = ImsStreamMediaProfile.AUDIO_QUALITY_EVRC_NW; // 7
+    // Current audio codec is GSM_EFR
+    public static final int AUDIO_CODEC_GSM_EFR = ImsStreamMediaProfile.AUDIO_QUALITY_GSM_EFR; // 8
+    // Current audio codec is GSM_FR
+    public static final int AUDIO_CODEC_GSM_FR = ImsStreamMediaProfile.AUDIO_QUALITY_GSM_FR; // 9
+    // Current audio codec is GSM_HR
+    public static final int AUDIO_CODEC_GSM_HR = ImsStreamMediaProfile.AUDIO_QUALITY_GSM_HR; // 10
+    // Current audio codec is G711U
+    public static final int AUDIO_CODEC_G711U = ImsStreamMediaProfile.AUDIO_QUALITY_G711U; // 11
+    // Current audio codec is G723
+    public static final int AUDIO_CODEC_G723 = ImsStreamMediaProfile.AUDIO_QUALITY_G723; // 12
+    // Current audio codec is G711A
+    public static final int AUDIO_CODEC_G711A = ImsStreamMediaProfile.AUDIO_QUALITY_G711A; // 13
+    // Current audio codec is G722
+    public static final int AUDIO_CODEC_G722 = ImsStreamMediaProfile.AUDIO_QUALITY_G722; // 14
+    // Current audio codec is G711AB
+    public static final int AUDIO_CODEC_G711AB = ImsStreamMediaProfile.AUDIO_QUALITY_G711AB; // 15
+    // Current audio codec is G729
+    public static final int AUDIO_CODEC_G729 = ImsStreamMediaProfile.AUDIO_QUALITY_G729; // 16
+    // Current audio codec is EVS_NB
+    public static final int AUDIO_CODEC_EVS_NB = ImsStreamMediaProfile.AUDIO_QUALITY_EVS_NB; // 17
+    // Current audio codec is EVS_WB
+    public static final int AUDIO_CODEC_EVS_WB = ImsStreamMediaProfile.AUDIO_QUALITY_EVS_WB; // 18
+    // Current audio codec is EVS_SWB
+    public static final int AUDIO_CODEC_EVS_SWB = ImsStreamMediaProfile.AUDIO_QUALITY_EVS_SWB; // 19
+    // Current audio codec is EVS_FB
+    public static final int AUDIO_CODEC_EVS_FB = ImsStreamMediaProfile.AUDIO_QUALITY_EVS_FB; // 20
+
+    /**
      * Connection extra key used to store the last forwarded number associated with the current
      * connection.  Used to communicate to the user interface that the connection was forwarded via
      * the specified number.
@@ -567,6 +640,13 @@
             "android.telecom.extra.IS_RTT_AUDIO_PRESENT";
 
     /**
+     * The audio codec in use for the current {@link Connection}, if known. Valid values include
+     * {@link #AUDIO_CODEC_AMR_WB} and {@link #AUDIO_CODEC_EVS_WB}.
+     */
+    public static final String EXTRA_AUDIO_CODEC =
+            "android.telecom.extra.AUDIO_CODEC";
+
+    /**
      * Connection event used to inform Telecom that it should play the on hold tone.  This is used
      * to play a tone when the peer puts the current call on hold.  Sent to Telecom via
      * {@link #sendConnectionEvent(String, Bundle)}.
@@ -1803,6 +1883,12 @@
     private Set<String> mPreviousExtraKeys;
 
     /**
+     * The verification status for an incoming call's phone number.
+     */
+    private @VerificationStatus int mCallerNumberVerificationStatus;
+
+
+    /**
      * Create a new Connection.
      */
     public Connection() {}
@@ -3304,4 +3390,26 @@
     public void setCallDirection(@Call.Details.CallDirection int callDirection) {
         mCallDirection = callDirection;
     }
+
+    /**
+     * Gets the verification status for the phone number of an incoming call as identified in
+     * ATIS-1000082.
+     * @return the verification status.
+     */
+    public @VerificationStatus int getCallerNumberVerificationStatus() {
+        return mCallerNumberVerificationStatus;
+    }
+
+    /**
+     * Sets the verification status for the phone number of an incoming call as identified in
+     * ATIS-1000082.
+     * <p>
+     * This property can only be set at the time of creation of a {@link Connection} being returned
+     * by
+     * {@link ConnectionService#onCreateIncomingConnection(PhoneAccountHandle, ConnectionRequest)}.
+     */
+    public void setCallerNumberVerificationStatus(
+            @VerificationStatus int callerNumberVerificationStatus) {
+        mCallerNumberVerificationStatus = callerNumberVerificationStatus;
+    }
 }
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index 812b805..3a0494e 100644
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -1621,7 +1621,8 @@
                         connection.getStatusHints(),
                         connection.getDisconnectCause(),
                         createIdList(connection.getConferenceables()),
-                        connection.getExtras()));
+                        connection.getExtras(),
+                        connection.getCallerNumberVerificationStatus()));
 
         if (isIncoming && request.shouldShowIncomingCallUi() && isSelfManaged) {
             // Tell ConnectionService to show its incoming call UX.
@@ -2156,7 +2157,8 @@
                     emptyList,
                     connection.getExtras(),
                     conferenceId,
-                    connection.getCallDirection());
+                    connection.getCallDirection(),
+                    Connection.VERIFICATION_STATUS_NOT_VERIFIED);
             mAdapter.addExistingConnection(id, parcelableConnection);
         }
     }
diff --git a/telecomm/java/android/telecom/ParcelableCall.java b/telecomm/java/android/telecom/ParcelableCall.java
index fdc3243..a234bb0 100644
--- a/telecomm/java/android/telecom/ParcelableCall.java
+++ b/telecomm/java/android/telecom/ParcelableCall.java
@@ -16,7 +16,6 @@
 
 package android.telecom;
 
-import android.annotation.Nullable;
 import android.annotation.UnsupportedAppUsage;
 import android.net.Uri;
 import android.os.Build;
@@ -66,6 +65,7 @@
     private final Bundle mExtras;
     private final long mCreationTimeMillis;
     private final int mCallDirection;
+    private final int mCallerNumberVerificationStatus;
 
     public ParcelableCall(
             String id,
@@ -94,7 +94,8 @@
             Bundle intentExtras,
             Bundle extras,
             long creationTimeMillis,
-            int callDirection) {
+            int callDirection,
+            int callerNumberVerificationStatus) {
         mId = id;
         mState = state;
         mDisconnectCause = disconnectCause;
@@ -122,6 +123,7 @@
         mExtras = extras;
         mCreationTimeMillis = creationTimeMillis;
         mCallDirection = callDirection;
+        mCallerNumberVerificationStatus = callerNumberVerificationStatus;
     }
 
     /** The unique ID of the call. */
@@ -322,6 +324,15 @@
         return mCallDirection;
     }
 
+    /**
+     * Gets the verification status for the phone number of an incoming call as identified in
+     * ATIS-1000082.
+     * @return the verification status.
+     */
+    public @Connection.VerificationStatus int getCallerNumberVerificationStatus() {
+        return mCallerNumberVerificationStatus;
+    }
+
     /** Responsible for creating ParcelableCall objects for deserialized Parcels. */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
     public static final @android.annotation.NonNull Parcelable.Creator<ParcelableCall> CREATOR =
@@ -360,6 +371,7 @@
             ParcelableRttCall rttCall = source.readParcelable(classLoader);
             long creationTimeMillis = source.readLong();
             int callDirection = source.readInt();
+            int callerNumberVerificationStatus = source.readInt();
             return new ParcelableCall(
                     id,
                     state,
@@ -387,7 +399,8 @@
                     intentExtras,
                     extras,
                     creationTimeMillis,
-                    callDirection);
+                    callDirection,
+                    callerNumberVerificationStatus);
         }
 
         @Override
@@ -433,6 +446,7 @@
         destination.writeParcelable(mRttCall, 0);
         destination.writeLong(mCreationTimeMillis);
         destination.writeInt(mCallDirection);
+        destination.writeInt(mCallerNumberVerificationStatus);
     }
 
     @Override
diff --git a/telecomm/java/android/telecom/ParcelableConnection.java b/telecomm/java/android/telecom/ParcelableConnection.java
index 4734af6..2b9ce9b 100644
--- a/telecomm/java/android/telecom/ParcelableConnection.java
+++ b/telecomm/java/android/telecom/ParcelableConnection.java
@@ -54,6 +54,7 @@
     private final Bundle mExtras;
     private String mParentCallId;
     private @Call.Details.CallDirection int mCallDirection;
+    private @Connection.VerificationStatus int mCallerNumberVerificationStatus;
 
     /** @hide */
     public ParcelableConnection(
@@ -77,12 +78,13 @@
             List<String> conferenceableConnectionIds,
             Bundle extras,
             String parentCallId,
-            @Call.Details.CallDirection int callDirection) {
+            @Call.Details.CallDirection int callDirection,
+            @Connection.VerificationStatus int callerNumberVerificationStatus) {
         this(phoneAccount, state, capabilities, properties, supportedAudioRoutes, address,
                 addressPresentation, callerDisplayName, callerDisplayNamePresentation,
                 videoProvider, videoState, ringbackRequested, isVoipAudioMode, connectTimeMillis,
                 connectElapsedTimeMillis, statusHints, disconnectCause, conferenceableConnectionIds,
-                extras);
+                extras, callerNumberVerificationStatus);
         mParentCallId = parentCallId;
         mCallDirection = callDirection;
     }
@@ -107,7 +109,8 @@
             StatusHints statusHints,
             DisconnectCause disconnectCause,
             List<String> conferenceableConnectionIds,
-            Bundle extras) {
+            Bundle extras,
+            @Connection.VerificationStatus int callerNumberVerificationStatus) {
         mPhoneAccount = phoneAccount;
         mState = state;
         mConnectionCapabilities = capabilities;
@@ -129,6 +132,7 @@
         mExtras = extras;
         mParentCallId = null;
         mCallDirection = Call.Details.DIRECTION_UNKNOWN;
+        mCallerNumberVerificationStatus = callerNumberVerificationStatus;
     }
 
     public PhoneAccountHandle getPhoneAccount() {
@@ -227,6 +231,10 @@
         return mCallDirection;
     }
 
+    public @Connection.VerificationStatus int getCallerNumberVerificationStatus() {
+        return mCallerNumberVerificationStatus;
+    }
+
     @Override
     public String toString() {
         return new StringBuilder()
@@ -276,6 +284,7 @@
             String parentCallId = source.readString();
             long connectElapsedTimeMillis = source.readLong();
             int callDirection = source.readInt();
+            int callerNumberVerificationStatus = source.readInt();
 
             return new ParcelableConnection(
                     phoneAccount,
@@ -298,7 +307,8 @@
                     conferenceableConnectionIds,
                     extras,
                     parentCallId,
-                    callDirection);
+                    callDirection,
+                    callerNumberVerificationStatus);
         }
 
         @Override
@@ -338,5 +348,6 @@
         destination.writeString(mParentCallId);
         destination.writeLong(mConnectElapsedTimeMillis);
         destination.writeInt(mCallDirection);
+        destination.writeInt(mCallerNumberVerificationStatus);
     }
 }
diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
index de3a816..204c37e 100644
--- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
+++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl
@@ -314,8 +314,6 @@
 
     void addOrRemoveTestCallCompanionApp(String packageName, boolean isAdded);
 
-    void setTestAutoModeApp(String packageName);
-
     /**
      * @see TelecomServiceImpl#setSystemDialer
      */
diff --git a/telephony/java/com/android/internal/telephony/TelephonyPermissions.java b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
similarity index 91%
rename from telephony/java/com/android/internal/telephony/TelephonyPermissions.java
rename to telephony/common/com/android/internal/telephony/TelephonyPermissions.java
index 96ddf22..86630b0 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyPermissions.java
+++ b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
@@ -156,6 +156,27 @@
         return false;
     }
 
+    /**
+     * Check whether the app with the given pid/uid can read phone state.
+     *
+     * <p>This method behaves in one of the following ways:
+     * <ul>
+     *   <li>return true: if the caller has the READ_PRIVILEGED_PHONE_STATE permission, the
+     *       READ_PHONE_STATE runtime permission, or carrier privileges on the given subId.
+     *   <li>throw SecurityException: if the caller didn't declare any of these permissions, or, for
+     *       apps which support runtime permissions, if the caller does not currently have any of
+     *       these permissions.
+     *   <li>return false: if the caller lacks all of these permissions and doesn't support runtime
+     *       permissions. This implies that the user revoked the ability to read phone state
+     *       manually (via AppOps). In this case we can't throw as it would break app compatibility,
+     *       so we return false to indicate that the calling function should return dummy data.
+     * </ul>
+     *
+     * <p>Note: for simplicity, this method always returns false for callers using legacy
+     * permissions and who have had READ_PHONE_STATE revoked, even if they are carrier-privileged.
+     * Such apps should migrate to runtime permissions or stop requiring READ_PHONE_STATE on P+
+     * devices.
+     */
     @VisibleForTesting
     public static boolean checkReadPhoneState(
             Context context, Supplier<ITelephony> telephonySupplier, int subId, int pid, int uid,
@@ -208,6 +229,20 @@
                 callingPackage, callingFeatureId, message);
     }
 
+    /**
+     * Check whether the app with the given pid/uid can read phone state, or has carrier
+     * privileges on any active subscription.
+     *
+     * <p>If the app does not have carrier privilege, this method will return {@code false} instead
+     * of throwing a SecurityException. Therefore, the callers cannot tell the difference
+     * between M+ apps which declare the runtime permission but do not have it, and pre-M apps
+     * which declare the static permission but had access revoked via AppOps. Apps in the former
+     * category expect SecurityExceptions; apps in the latter don't. So this method is suitable for
+     * use only if the behavior in both scenarios is meant to be identical.
+     *
+     * @return {@code true} if the app can read phone state or has carrier privilege;
+     *         {@code false} otherwise.
+     */
     @VisibleForTesting
     public static boolean checkReadPhoneStateOnAnyActiveSub(
             Context context, Supplier<ITelephony> telephonySupplier, int pid, int uid,
@@ -453,6 +488,11 @@
                 context, TELEPHONY_SUPPLIER, subId, pid, uid, callingPackage, callingPackageName);
     }
 
+    /**
+     * Check whether the app with the given pid/uid can read the call log.
+     * @return {@code true} if the specified app has the read call log permission and AppOpp granted
+     *      to it, {@code false} otherwise.
+     */
     @VisibleForTesting
     public static boolean checkReadCallLog(
             Context context, Supplier<ITelephony> telephonySupplier, int subId, int pid, int uid,
@@ -490,6 +530,12 @@
                 callingPackage, callingFeatureId, message);
     }
 
+    /**
+     * Returns whether the caller can read phone numbers.
+     *
+     * <p>Besides apps with the ability to read phone state per {@link #checkReadPhoneState}, the
+     * default SMS app and apps with READ_SMS or READ_PHONE_NUMBERS can also read phone numbers.
+     */
     @VisibleForTesting
     public static boolean checkReadPhoneNumber(
             Context context, Supplier<ITelephony> telephonySupplier, int subId, int pid, int uid,
@@ -529,10 +575,10 @@
         } catch (SecurityException readPhoneNumberSecurityException) {
         }
 
-        throw new SecurityException(message + ": Neither user " + uid +
-                " nor current process has " + android.Manifest.permission.READ_PHONE_STATE +
-                ", " + android.Manifest.permission.READ_SMS + ", or " +
-                android.Manifest.permission.READ_PHONE_NUMBERS);
+        throw new SecurityException(message + ": Neither user " + uid
+                + " nor current process has " + android.Manifest.permission.READ_PHONE_STATE
+                + ", " + android.Manifest.permission.READ_SMS + ", or "
+                + android.Manifest.permission.READ_PHONE_NUMBERS);
     }
 
     /**
@@ -543,8 +589,8 @@
      */
     public static void enforceCallingOrSelfModifyPermissionOrCarrierPrivilege(
             Context context, int subId, String message) {
-        if (context.checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE) ==
-                PERMISSION_GRANTED) {
+        if (context.checkCallingOrSelfPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+                == PERMISSION_GRANTED) {
             return;
         }
 
@@ -586,8 +632,8 @@
         }
 
         if (DBG) {
-            Rlog.d(LOG_TAG, "No READ_PRIVILEDED_PHONE_STATE permission, " +
-                    "check carrier privilege next.");
+            Rlog.d(LOG_TAG, "No READ_PRIVILEDED_PHONE_STATE permission, "
+                    + "check carrier privilege next.");
         }
 
         enforceCallingOrSelfCarrierPrivilege(subId, message);
@@ -612,8 +658,8 @@
 
     private static void enforceCarrierPrivilege(
             Supplier<ITelephony> telephonySupplier, int subId, int uid, String message) {
-        if (getCarrierPrivilegeStatus(telephonySupplier, subId, uid) !=
-                TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
+        if (getCarrierPrivilegeStatus(telephonySupplier, subId, uid)
+                != TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
             if (DBG) Rlog.e(LOG_TAG, "No Carrier Privilege.");
             throw new SecurityException(message);
         }
diff --git a/telephony/java/android/telephony/Annotation.java b/telephony/java/android/telephony/Annotation.java
index 72f758e..f89bbc7 100644
--- a/telephony/java/android/telephony/Annotation.java
+++ b/telephony/java/android/telephony/Annotation.java
@@ -1,6 +1,7 @@
 package android.telephony;
 
 import android.annotation.IntDef;
+import android.telecom.Connection;
 import android.telephony.data.ApnSetting;
 
 import java.lang.annotation.Retention;
@@ -509,4 +510,30 @@
             ServiceState.RIL_RADIO_TECHNOLOGY_LTE_CA,
             ServiceState.RIL_RADIO_TECHNOLOGY_NR})
     public @interface RilRadioTechnology {}
+
+    @IntDef({
+            Connection.AUDIO_CODEC_NONE,
+            Connection.AUDIO_CODEC_AMR,
+            Connection.AUDIO_CODEC_AMR_WB,
+            Connection.AUDIO_CODEC_QCELP13K,
+            Connection.AUDIO_CODEC_EVRC,
+            Connection.AUDIO_CODEC_EVRC_B,
+            Connection.AUDIO_CODEC_EVRC_WB,
+            Connection.AUDIO_CODEC_EVRC_NW,
+            Connection.AUDIO_CODEC_GSM_EFR,
+            Connection.AUDIO_CODEC_GSM_FR,
+            Connection.AUDIO_CODEC_G711U,
+            Connection.AUDIO_CODEC_G723,
+            Connection.AUDIO_CODEC_G711A,
+            Connection.AUDIO_CODEC_G722,
+            Connection.AUDIO_CODEC_G711AB,
+            Connection.AUDIO_CODEC_G729,
+            Connection.AUDIO_CODEC_EVS_NB,
+            Connection.AUDIO_CODEC_EVS_WB,
+            Connection.AUDIO_CODEC_EVS_SWB,
+            Connection.AUDIO_CODEC_EVS_FB
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ImsAudioCodec {
+    }
 }
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 98d7661..1b86c09 100755
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -100,6 +100,16 @@
             KEY_CARRIER_VOLTE_PROVISIONED_BOOL = "carrier_volte_provisioned_bool";
 
     /**
+     * Boolean indicating the Supplementary Services(SS) is disable when airplane mode on in the
+     * Call Settings menu.
+     * {@code true}: SS is disable when airplane mode on.
+     * {@code false}: SS is enable when airplane mode on.
+     * The default value for this key is {@code false}
+     */
+    public static final String KEY_DISABLE_SUPPLEMENTARY_SERVICES_IN_AIRPLANE_MODE_BOOL =
+            "disable_supplementary_services_in_airplane_mode_bool";
+
+    /**
      * Boolean indicating if the "Call forwarding" item is visible in the Call Settings menu.
      * true means visible. false means gone.
      * @hide
@@ -3439,6 +3449,7 @@
         sDefaults.putBoolean(KEY_CALL_FORWARDING_WHEN_UNREACHABLE_SUPPORTED_BOOL, true);
         sDefaults.putBoolean(KEY_ADDITIONAL_SETTINGS_CALLER_ID_VISIBILITY_BOOL, true);
         sDefaults.putBoolean(KEY_ADDITIONAL_SETTINGS_CALL_WAITING_VISIBILITY_BOOL, true);
+        sDefaults.putBoolean(KEY_DISABLE_SUPPLEMENTARY_SERVICES_IN_AIRPLANE_MODE_BOOL, false);
         sDefaults.putBoolean(KEY_IGNORE_SIM_NETWORK_LOCKED_EVENTS_BOOL, false);
         sDefaults.putBoolean(KEY_MDN_IS_ADDITIONAL_VOICEMAIL_NUMBER_BOOL, false);
         sDefaults.putBoolean(KEY_OPERATOR_SELECTION_EXPAND_BOOL, true);
diff --git a/telephony/java/android/telephony/PhoneCapability.java b/telephony/java/android/telephony/PhoneCapability.java
index 17b7963f..8a75831 100644
--- a/telephony/java/android/telephony/PhoneCapability.java
+++ b/telephony/java/android/telephony/PhoneCapability.java
@@ -16,6 +16,8 @@
 
 package android.telephony;
 
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -27,12 +29,13 @@
 /**
  * Define capability of a modem group. That is, the capabilities
  * are shared between those modems defined by list of modem IDs.
- * @hide
  */
-public class PhoneCapability implements Parcelable {
+public final class PhoneCapability implements Parcelable {
     // Hardcoded default DSDS capability.
+    /** @hide */
     public static final PhoneCapability DEFAULT_DSDS_CAPABILITY;
     // Hardcoded default Single SIM single standby capability.
+    /** @hide */
     public static final PhoneCapability DEFAULT_SSSS_CAPABILITY;
 
     static {
@@ -48,13 +51,18 @@
         logicalModemList.add(modemInfo1);
         DEFAULT_SSSS_CAPABILITY = new PhoneCapability(1, 1, 0, logicalModemList, false);
     }
-
+    /** @hide */
     public final int maxActiveVoiceCalls;
+    /** @hide */
     public final int maxActiveData;
+    /** @hide */
     public final int max5G;
+    /** @hide */
     public final boolean validationBeforeSwitchSupported;
+    /** @hide */
     public final List<ModemInfo> logicalModemList;
 
+    /** @hide */
     public PhoneCapability(int maxActiveVoiceCalls, int maxActiveData, int max5G,
             List<ModemInfo> logicalModemList, boolean validationBeforeSwitchSupported) {
         this.maxActiveVoiceCalls = maxActiveVoiceCalls;
@@ -116,7 +124,7 @@
     /**
      * {@link Parcelable#writeToParcel}
      */
-    public void writeToParcel(Parcel dest, @Parcelable.WriteFlags int flags) {
+    public void writeToParcel(@NonNull Parcel dest, @Parcelable.WriteFlags int flags) {
         dest.writeInt(maxActiveVoiceCalls);
         dest.writeInt(maxActiveData);
         dest.writeInt(max5G);
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index 9ace86c..d2c8517 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -278,12 +278,9 @@
      */
     public static final int UNKNOWN_ID = -1;
 
-    private String mVoiceOperatorAlphaLong;
-    private String mVoiceOperatorAlphaShort;
-    private String mVoiceOperatorNumeric;
-    private String mDataOperatorAlphaLong;
-    private String mDataOperatorAlphaShort;
-    private String mDataOperatorNumeric;
+    private String mOperatorAlphaLong;
+    private String mOperatorAlphaShort;
+    private String mOperatorNumeric;
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     private boolean mIsManualNetworkSelection;
 
@@ -379,12 +376,9 @@
     protected void copyFrom(ServiceState s) {
         mVoiceRegState = s.mVoiceRegState;
         mDataRegState = s.mDataRegState;
-        mVoiceOperatorAlphaLong = s.mVoiceOperatorAlphaLong;
-        mVoiceOperatorAlphaShort = s.mVoiceOperatorAlphaShort;
-        mVoiceOperatorNumeric = s.mVoiceOperatorNumeric;
-        mDataOperatorAlphaLong = s.mDataOperatorAlphaLong;
-        mDataOperatorAlphaShort = s.mDataOperatorAlphaShort;
-        mDataOperatorNumeric = s.mDataOperatorNumeric;
+        mOperatorAlphaLong = s.mOperatorAlphaLong;
+        mOperatorAlphaShort = s.mOperatorAlphaShort;
+        mOperatorNumeric = s.mOperatorNumeric;
         mIsManualNetworkSelection = s.mIsManualNetworkSelection;
         mCssIndicator = s.mCssIndicator;
         mNetworkId = s.mNetworkId;
@@ -418,12 +412,9 @@
     public ServiceState(Parcel in) {
         mVoiceRegState = in.readInt();
         mDataRegState = in.readInt();
-        mVoiceOperatorAlphaLong = in.readString();
-        mVoiceOperatorAlphaShort = in.readString();
-        mVoiceOperatorNumeric = in.readString();
-        mDataOperatorAlphaLong = in.readString();
-        mDataOperatorAlphaShort = in.readString();
-        mDataOperatorNumeric = in.readString();
+        mOperatorAlphaLong = in.readString();
+        mOperatorAlphaShort = in.readString();
+        mOperatorNumeric = in.readString();
         mIsManualNetworkSelection = in.readInt() != 0;
         mCssIndicator = (in.readInt() != 0);
         mNetworkId = in.readInt();
@@ -448,12 +439,9 @@
     public void writeToParcel(Parcel out, int flags) {
         out.writeInt(mVoiceRegState);
         out.writeInt(mDataRegState);
-        out.writeString(mVoiceOperatorAlphaLong);
-        out.writeString(mVoiceOperatorAlphaShort);
-        out.writeString(mVoiceOperatorNumeric);
-        out.writeString(mDataOperatorAlphaLong);
-        out.writeString(mDataOperatorAlphaShort);
-        out.writeString(mDataOperatorNumeric);
+        out.writeString(mOperatorAlphaLong);
+        out.writeString(mOperatorAlphaShort);
+        out.writeString(mOperatorNumeric);
         out.writeInt(mIsManualNetworkSelection ? 1 : 0);
         out.writeInt(mCssIndicator ? 1 : 0);
         out.writeInt(mNetworkId);
@@ -691,7 +679,7 @@
      * @return long name of operator, null if unregistered or unknown
      */
     public String getOperatorAlphaLong() {
-        return mVoiceOperatorAlphaLong;
+        return mOperatorAlphaLong;
     }
 
     /**
@@ -699,18 +687,10 @@
      * @return long name of operator
      * @hide
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q,
+            publicAlternatives = "Use {@link #getOperatorAlphaLong} instead.")
     public String getVoiceOperatorAlphaLong() {
-        return mVoiceOperatorAlphaLong;
-    }
-
-    /**
-     * Get current registered data network operator name in long alphanumeric format.
-     * @return long name of voice operator
-     * @hide
-     */
-    public String getDataOperatorAlphaLong() {
-        return mDataOperatorAlphaLong;
+        return mOperatorAlphaLong;
     }
 
     /**
@@ -721,7 +701,7 @@
      * @return short name of operator, null if unregistered or unknown
      */
     public String getOperatorAlphaShort() {
-        return mVoiceOperatorAlphaShort;
+        return mOperatorAlphaShort;
     }
 
     /**
@@ -729,9 +709,10 @@
      * @return short name of operator, null if unregistered or unknown
      * @hide
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q,
+            publicAlternatives = "Use {@link #getOperatorAlphaShort} instead.")
     public String getVoiceOperatorAlphaShort() {
-        return mVoiceOperatorAlphaShort;
+        return mOperatorAlphaShort;
     }
 
     /**
@@ -739,9 +720,10 @@
      * @return short name of operator, null if unregistered or unknown
      * @hide
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q,
+            publicAlternatives = "Use {@link #getOperatorAlphaShort} instead.")
     public String getDataOperatorAlphaShort() {
-        return mDataOperatorAlphaShort;
+        return mOperatorAlphaShort;
     }
 
     /**
@@ -755,11 +737,11 @@
      * @hide
      */
     public String getOperatorAlpha() {
-        if (TextUtils.isEmpty(mVoiceOperatorAlphaLong)) {
-            return mVoiceOperatorAlphaShort;
+        if (TextUtils.isEmpty(mOperatorAlphaLong)) {
+            return mOperatorAlphaShort;
         }
 
-        return mVoiceOperatorAlphaLong;
+        return mOperatorAlphaLong;
     }
 
     /**
@@ -775,7 +757,7 @@
      * {@link com.android.internal.telephony.MccTable#countryCodeForMcc(int)}.
      */
     public String getOperatorNumeric() {
-        return mVoiceOperatorNumeric;
+        return mOperatorNumeric;
     }
 
     /**
@@ -785,7 +767,7 @@
      */
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
     public String getVoiceOperatorNumeric() {
-        return mVoiceOperatorNumeric;
+        return mOperatorNumeric;
     }
 
     /**
@@ -793,9 +775,10 @@
      * @return numeric format of operator, null if unregistered or unknown
      * @hide
      */
-    @UnsupportedAppUsage
+    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q,
+            publicAlternatives = "Use {@link #getOperatorNumeric} instead.")
     public String getDataOperatorNumeric() {
-        return mDataOperatorNumeric;
+        return mOperatorNumeric;
     }
 
     /**
@@ -815,12 +798,9 @@
                     mDataRegState,
                     mChannelNumber,
                     Arrays.hashCode(mCellBandwidths),
-                    mVoiceOperatorAlphaLong,
-                    mVoiceOperatorAlphaShort,
-                    mVoiceOperatorNumeric,
-                    mDataOperatorAlphaLong,
-                    mDataOperatorAlphaShort,
-                    mDataOperatorNumeric,
+                    mOperatorAlphaLong,
+                    mOperatorAlphaShort,
+                    mOperatorNumeric,
                     mIsManualNetworkSelection,
                     mCssIndicator,
                     mNetworkId,
@@ -850,12 +830,9 @@
                     && mIsManualNetworkSelection == s.mIsManualNetworkSelection
                     && mChannelNumber == s.mChannelNumber
                     && Arrays.equals(mCellBandwidths, s.mCellBandwidths)
-                    && equalsHandlesNulls(mVoiceOperatorAlphaLong, s.mVoiceOperatorAlphaLong)
-                    && equalsHandlesNulls(mVoiceOperatorAlphaShort, s.mVoiceOperatorAlphaShort)
-                    && equalsHandlesNulls(mVoiceOperatorNumeric, s.mVoiceOperatorNumeric)
-                    && equalsHandlesNulls(mDataOperatorAlphaLong, s.mDataOperatorAlphaLong)
-                    && equalsHandlesNulls(mDataOperatorAlphaShort, s.mDataOperatorAlphaShort)
-                    && equalsHandlesNulls(mDataOperatorNumeric, s.mDataOperatorNumeric)
+                    && equalsHandlesNulls(mOperatorAlphaLong, s.mOperatorAlphaLong)
+                    && equalsHandlesNulls(mOperatorAlphaShort, s.mOperatorAlphaShort)
+                    && equalsHandlesNulls(mOperatorNumeric, s.mOperatorNumeric)
                     && equalsHandlesNulls(mCssIndicator, s.mCssIndicator)
                     && equalsHandlesNulls(mNetworkId, s.mNetworkId)
                     && equalsHandlesNulls(mSystemId, s.mSystemId)
@@ -1007,10 +984,8 @@
                     .append(", mChannelNumber=").append(mChannelNumber)
                     .append(", duplexMode()=").append(getDuplexMode())
                     .append(", mCellBandwidths=").append(Arrays.toString(mCellBandwidths))
-                    .append(", mVoiceOperatorAlphaLong=").append(mVoiceOperatorAlphaLong)
-                    .append(", mVoiceOperatorAlphaShort=").append(mVoiceOperatorAlphaShort)
-                    .append(", mDataOperatorAlphaLong=").append(mDataOperatorAlphaLong)
-                    .append(", mDataOperatorAlphaShort=").append(mDataOperatorAlphaShort)
+                    .append(", mOperatorAlphaLong=").append(mOperatorAlphaLong)
+                    .append(", mOperatorAlphaShort=").append(mOperatorAlphaShort)
                     .append(", isManualNetworkSelection=").append(mIsManualNetworkSelection)
                     .append(mIsManualNetworkSelection ? "(manual)" : "(automatic)")
                     .append(", getRilVoiceRadioTechnology=").append(getRilVoiceRadioTechnology())
@@ -1040,12 +1015,9 @@
         mDataRegState = STATE_OUT_OF_SERVICE;
         mChannelNumber = -1;
         mCellBandwidths = new int[0];
-        mVoiceOperatorAlphaLong = null;
-        mVoiceOperatorAlphaShort = null;
-        mVoiceOperatorNumeric = null;
-        mDataOperatorAlphaLong = null;
-        mDataOperatorAlphaShort = null;
-        mDataOperatorNumeric = null;
+        mOperatorAlphaLong = null;
+        mOperatorAlphaShort = null;
+        mOperatorNumeric = null;
         mIsManualNetworkSelection = false;
         mCssIndicator = false;
         mNetworkId = -1;
@@ -1204,26 +1176,9 @@
     }
 
     public void setOperatorName(String longName, String shortName, String numeric) {
-        mVoiceOperatorAlphaLong = longName;
-        mVoiceOperatorAlphaShort = shortName;
-        mVoiceOperatorNumeric = numeric;
-        mDataOperatorAlphaLong = longName;
-        mDataOperatorAlphaShort = shortName;
-        mDataOperatorNumeric = numeric;
-    }
-
-    /** @hide */
-    public void setVoiceOperatorName(String longName, String shortName, String numeric) {
-        mVoiceOperatorAlphaLong = longName;
-        mVoiceOperatorAlphaShort = shortName;
-        mVoiceOperatorNumeric = numeric;
-    }
-
-    /** @hide */
-    public void setDataOperatorName(String longName, String shortName, String numeric) {
-        mDataOperatorAlphaLong = longName;
-        mDataOperatorAlphaShort = shortName;
-        mDataOperatorNumeric = numeric;
+        mOperatorAlphaLong = longName;
+        mOperatorAlphaShort = shortName;
+        mOperatorNumeric = numeric;
     }
 
     /**
@@ -1233,19 +1188,8 @@
      * @hide
      */
     @UnsupportedAppUsage
-    public void setOperatorAlphaLong(String longName) {
-        mVoiceOperatorAlphaLong = longName;
-        mDataOperatorAlphaLong = longName;
-    }
-
-    /** @hide */
-    public void setVoiceOperatorAlphaLong(String longName) {
-        mVoiceOperatorAlphaLong = longName;
-    }
-
-    /** @hide */
-    public void setDataOperatorAlphaLong(String longName) {
-        mDataOperatorAlphaLong = longName;
+    public void setOperatorAlphaLong(@Nullable String longName) {
+        mOperatorAlphaLong = longName;
     }
 
     public void setIsManualSelection(boolean isManual) {
@@ -1293,12 +1237,12 @@
         m.putInt("dataRegState", mDataRegState);
         m.putInt("dataRoamingType", getDataRoamingType());
         m.putInt("voiceRoamingType", getVoiceRoamingType());
-        m.putString("operator-alpha-long", mVoiceOperatorAlphaLong);
-        m.putString("operator-alpha-short", mVoiceOperatorAlphaShort);
-        m.putString("operator-numeric", mVoiceOperatorNumeric);
-        m.putString("data-operator-alpha-long", mDataOperatorAlphaLong);
-        m.putString("data-operator-alpha-short", mDataOperatorAlphaShort);
-        m.putString("data-operator-numeric", mDataOperatorNumeric);
+        m.putString("operator-alpha-long", mOperatorAlphaLong);
+        m.putString("operator-alpha-short", mOperatorAlphaShort);
+        m.putString("operator-numeric", mOperatorNumeric);
+        m.putString("data-operator-alpha-long", mOperatorAlphaLong);
+        m.putString("data-operator-alpha-short", mOperatorAlphaShort);
+        m.putString("data-operator-numeric", mOperatorNumeric);
         m.putBoolean("manual", mIsManualNetworkSelection);
         m.putInt("radioTechnology", getRilVoiceRadioTechnology());
         m.putInt("dataRadioTechnology", getRadioTechnology());
@@ -1933,12 +1877,9 @@
         }
         if (!removeCoarseLocation) return state;
 
-        state.mDataOperatorAlphaLong = null;
-        state.mDataOperatorAlphaShort = null;
-        state.mDataOperatorNumeric = null;
-        state.mVoiceOperatorAlphaLong = null;
-        state.mVoiceOperatorAlphaShort = null;
-        state.mVoiceOperatorNumeric = null;
+        state.mOperatorAlphaLong = null;
+        state.mOperatorAlphaShort = null;
+        state.mOperatorNumeric = null;
 
         return state;
     }
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index fc26122..e3981f6 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -4723,17 +4723,6 @@
     }
 
     /**
-     * Sim activation type: voice
-     * @hide
-     */
-    public static final int SIM_ACTIVATION_TYPE_VOICE = 0;
-    /**
-     * Sim activation type: data
-     * @hide
-     */
-    public static final int SIM_ACTIVATION_TYPE_DATA = 1;
-
-    /**
      * Initial SIM activation state, unknown. Not set by any carrier apps.
      * @hide
      */
@@ -8172,17 +8161,25 @@
         return Collections.EMPTY_LIST;
     }
 
-    /** @hide */
-    public List<String> getPackagesWithCarrierPrivilegesForAllPhones() {
+    /**
+     * Get the names of packages with carrier privileges for all the active subscriptions.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    @NonNull
+    public List<String> getCarrierPrivilegedPackagesForAllActiveSubscriptions() {
         try {
             ITelephony telephony = getITelephony();
             if (telephony != null) {
                 return telephony.getPackagesWithCarrierPrivilegesForAllPhones();
             }
         } catch (RemoteException ex) {
-            Rlog.e(TAG, "getPackagesWithCarrierPrivilegesForAllPhones RemoteException", ex);
+            Rlog.e(TAG, "getCarrierPrivilegedPackagesForAllActiveSubscriptions RemoteException",
+                    ex);
         } catch (NullPointerException ex) {
-            Rlog.e(TAG, "getPackagesWithCarrierPrivilegesForAllPhones NPE", ex);
+            Rlog.e(TAG, "getCarrierPrivilegedPackagesForAllActiveSubscriptions NPE", ex);
         }
         return Collections.EMPTY_LIST;
     }
diff --git a/tools/processors/unsupportedappusage/test/Android.bp b/tests/ManagedProfileLifecycleStressTest/Android.bp
similarity index 73%
rename from tools/processors/unsupportedappusage/test/Android.bp
rename to tests/ManagedProfileLifecycleStressTest/Android.bp
index 49ea3d4..639ce3c 100644
--- a/tools/processors/unsupportedappusage/test/Android.bp
+++ b/tests/ManagedProfileLifecycleStressTest/Android.bp
@@ -13,16 +13,11 @@
 // limitations under the License.
 
 java_test_host {
-    name: "unsupportedappusage-processor-test",
-
+    name: "ManagedProfileLifecycleStressTest",
     srcs: ["src/**/*.java"],
-
-    static_libs: [
-        "libjavac",
-        "unsupportedappusage-annotation-processor-lib",
-        "truth-host-prebuilt",
-        "mockito-host",
-        "junit-host",
-        "objenesis",
+    libs: ["tradefed"],
+    test_suites: ["device-tests"],
+    target_required: [
+        "DummyDPC",
     ],
 }
diff --git a/tests/ManagedProfileLifecycleStressTest/AndroidTest.xml b/tests/ManagedProfileLifecycleStressTest/AndroidTest.xml
new file mode 100644
index 0000000..e7dbc51
--- /dev/null
+++ b/tests/ManagedProfileLifecycleStressTest/AndroidTest.xml
@@ -0,0 +1,20 @@
+<?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.
+-->
+<configuration description="Stress test for managed profile lifecycle">
+  <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" >
+    <option name="jar" value="ManagedProfileLifecycleStressTest.jar" />
+  </test>
+</configuration>
diff --git a/tools/processors/unsupportedappusage/test/Android.bp b/tests/ManagedProfileLifecycleStressTest/app/DummyDPC/Android.bp
similarity index 70%
copy from tools/processors/unsupportedappusage/test/Android.bp
copy to tests/ManagedProfileLifecycleStressTest/app/DummyDPC/Android.bp
index 49ea3d4..d95af34 100644
--- a/tools/processors/unsupportedappusage/test/Android.bp
+++ b/tests/ManagedProfileLifecycleStressTest/app/DummyDPC/Android.bp
@@ -12,17 +12,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-java_test_host {
-    name: "unsupportedappusage-processor-test",
-
+android_test {
+    name: "DummyDPC",
+    defaults: ["cts_defaults"],
     srcs: ["src/**/*.java"],
-
-    static_libs: [
-        "libjavac",
-        "unsupportedappusage-annotation-processor-lib",
-        "truth-host-prebuilt",
-        "mockito-host",
-        "junit-host",
-        "objenesis",
-    ],
+    sdk_version: "current",
 }
diff --git a/tests/ManagedProfileLifecycleStressTest/app/DummyDPC/AndroidManifest.xml b/tests/ManagedProfileLifecycleStressTest/app/DummyDPC/AndroidManifest.xml
new file mode 100644
index 0000000..860940d
--- /dev/null
+++ b/tests/ManagedProfileLifecycleStressTest/app/DummyDPC/AndroidManifest.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.dummydpc">
+
+    <application
+        android:testOnly="true">
+        <receiver
+            android:name="com.android.dummydpc.DummyDeviceAdminReceiver"
+            android:permission="android.permission.BIND_DEVICE_ADMIN">
+            <meta-data android:name="android.app.device_admin"
+                       android:resource="@xml/device_admin" />
+            <intent-filter>
+                <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
+            </intent-filter>
+        </receiver>
+    </application>
+</manifest>
diff --git a/tests/ManagedProfileLifecycleStressTest/app/DummyDPC/res/xml/device_admin.xml b/tests/ManagedProfileLifecycleStressTest/app/DummyDPC/res/xml/device_admin.xml
new file mode 100644
index 0000000..4b3581e
--- /dev/null
+++ b/tests/ManagedProfileLifecycleStressTest/app/DummyDPC/res/xml/device_admin.xml
@@ -0,0 +1,4 @@
+<device-admin>
+    <uses-policies>
+    </uses-policies>
+</device-admin>
diff --git a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl b/tests/ManagedProfileLifecycleStressTest/app/DummyDPC/src/com/android/dummydpc/DummyDeviceAdminReceiver.java
similarity index 62%
copy from wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl
copy to tests/ManagedProfileLifecycleStressTest/app/DummyDPC/src/com/android/dummydpc/DummyDeviceAdminReceiver.java
index 007ec94..92190b7 100644
--- a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.aidl
+++ b/tests/ManagedProfileLifecycleStressTest/app/DummyDPC/src/com/android/dummydpc/DummyDeviceAdminReceiver.java
@@ -1,11 +1,11 @@
-/**
- * Copyright (c) 2014, The Android Open Source Project
+/*
+ * Copyright (C) 2019 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  *
- *     http://www.apache.org/licenses/LICENSE-2.0
+ *      http://www.apache.org/licenses/LICENSE-2.0
  *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
@@ -13,7 +13,13 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+package com.android.dummydpc;
 
-package android.net.wifi;
+import android.app.admin.DeviceAdminReceiver;
 
-parcelable WifiActivityEnergyInfo;
+/**
+ * Empty admin to use as a managed profile owner.
+ */
+public class DummyDeviceAdminReceiver extends DeviceAdminReceiver {
+}
+
diff --git a/tests/ManagedProfileLifecycleStressTest/src/com/android/test/stress/ManagedProfileLifecycleStressTest.java b/tests/ManagedProfileLifecycleStressTest/src/com/android/test/stress/ManagedProfileLifecycleStressTest.java
new file mode 100644
index 0000000..e323592
--- /dev/null
+++ b/tests/ManagedProfileLifecycleStressTest/src/com/android/test/stress/ManagedProfileLifecycleStressTest.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.stress;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.android.tradefed.log.LogUtil.CLog;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.TimeUnit;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * A test to exercise Android Framework parts related to creating, starting, stopping, and deleting
+ * a managed profile as much as possible. The aim is to catch any issues in this code before it
+ * affects managed profile CTS tests.
+ */
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class ManagedProfileLifecycleStressTest extends BaseHostJUnit4Test {
+    // Stop the test once this time limit has been reached. 25 minutes used as a limit to make total
+    // test time less than 30 minutes, so that it can be put into presubmit.
+    private static final int TIME_LIMIT_MINUTES = 25;
+
+    private static final String DUMMY_DPC_APK = "DummyDPC.apk";
+    private static final String DUMMY_DPC_COMPONENT =
+            "com.android.dummydpc/com.android.dummydpc.DummyDeviceAdminReceiver";
+    private static final Pattern CREATE_USER_OUTPUT_REGEX =
+            Pattern.compile("Success: created user id (\\d+)");
+
+    /**
+     * Create, start, and kill managed profiles in a loop.
+     */
+    @Test
+    public void testCreateStartDelete() throws Exception {
+        int iteration = 0;
+        final long deadline = System.nanoTime() + TimeUnit.MINUTES.toNanos(TIME_LIMIT_MINUTES);
+        while (System.nanoTime() < deadline) {
+            iteration++;
+            CLog.w("Iteration N" + iteration);
+            final int userId = createManagedProfile();
+            startUser(userId);
+            installPackageAsUser(DUMMY_DPC_APK, true /* grantPermissions */, userId, "-t");
+            setProfileOwner(DUMMY_DPC_COMPONENT, userId);
+            removeUser(userId);
+        }
+        CLog.w("Completed " + iteration + " iterations.");
+    }
+
+    private int createManagedProfile() throws Exception {
+        final String output = getDevice().executeShellCommand(
+                "pm create-user --profileOf 0 --managed TestProfile");
+        final Matcher matcher = CREATE_USER_OUTPUT_REGEX.matcher(output.trim());
+        if (!matcher.matches() || matcher.groupCount() != 1) {
+            fail("user creation failed, output: " + output);
+        }
+        return Integer.parseInt(matcher.group(1));
+    }
+
+    private void setProfileOwner(String componentName, int userId) throws Exception {
+        String command = "dpm set-profile-owner --user " + userId + " '" + componentName + "'";
+        String commandOutput = getDevice().executeShellCommand(command);
+        assertTrue("Unexpected dpm output: " + commandOutput, commandOutput.startsWith("Success:"));
+    }
+
+    private void removeUser(int userId) throws Exception {
+        final String output = getDevice().executeShellCommand("pm remove-user " + userId).trim();
+        assertEquals("Unexpected pm output: " + output, "Success: removed user", output);
+    }
+
+    private void startUser(int userId) throws Exception {
+        final String output = getDevice().executeShellCommand("am start-user -w " + userId).trim();
+        assertEquals("Unexpected am output: " + output, "Success: user started", output);
+    }
+}
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index fd3ed7d..a24426b 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -3134,14 +3134,11 @@
                 .addTransportType(TRANSPORT_CELLULAR).build();
         final TestNetworkCallback cellCallback = new TestNetworkCallback();
         mCm.requestNetwork(cellRequest, cellCallback);
-        // NOTE: This request causes the network's capabilities to change. This
-        // is currently delivered before the onAvailable() callbacks.
-        // TODO: Fix this.
-        cellCallback.expectCapabilitiesWith(NET_CAPABILITY_FOREGROUND, mCellNetworkAgent);
         cellCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
         fgCallback.expectAvailableCallbacksValidated(mCellNetworkAgent);
         // Expect a network capabilities update with FOREGROUND, because the most recent
         // request causes its state to change.
+        cellCallback.expectCapabilitiesWith(NET_CAPABILITY_FOREGROUND, mCellNetworkAgent);
         callback.expectCapabilitiesWith(NET_CAPABILITY_FOREGROUND, mCellNetworkAgent);
         assertTrue(isForegroundNetwork(mCellNetworkAgent));
         assertTrue(isForegroundNetwork(mWiFiNetworkAgent));
diff --git a/tools/processors/unsupportedappusage/Android.bp b/tools/processors/unsupportedappusage/Android.bp
deleted file mode 100644
index 1e96234..0000000
--- a/tools/processors/unsupportedappusage/Android.bp
+++ /dev/null
@@ -1,34 +0,0 @@
-
-java_library_host {
-    name: "unsupportedappusage-annotation-processor-lib",
-    srcs: [
-        "src/**/*.java",
-    ],
-    static_libs: [
-        "guava",
-        "unsupportedappusage-annotation"
-    ],
-    openjdk9: {
-        javacflags: [
-            "--add-modules=jdk.compiler",
-            "--add-exports jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED",
-            "--add-exports jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED",
-            "--add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED",
-            "--add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED",
-        ],
-    },
-}
-
-java_plugin {
-    name: "unsupportedappusage-annotation-processor",
-    processor_class: "android.processor.unsupportedappusage.UnsupportedAppUsageProcessor",
-
-    java_resources: [
-        "META-INF/**/*",
-    ],
-    static_libs: [
-        "unsupportedappusage-annotation-processor-lib"
-    ],
-
-    use_tools_jar: true,
-}
diff --git a/tools/processors/unsupportedappusage/META-INF/services/javax.annotation.processing.Processor b/tools/processors/unsupportedappusage/META-INF/services/javax.annotation.processing.Processor
deleted file mode 100644
index 4a969d3..0000000
--- a/tools/processors/unsupportedappusage/META-INF/services/javax.annotation.processing.Processor
+++ /dev/null
@@ -1 +0,0 @@
-android.processor.unsupportedappusage.UnsupportedAppUsageProcessor
diff --git a/tools/processors/unsupportedappusage/src/android/processor/unsupportedappusage/SignatureBuilder.java b/tools/processors/unsupportedappusage/src/android/processor/unsupportedappusage/SignatureBuilder.java
deleted file mode 100644
index 65fc733..0000000
--- a/tools/processors/unsupportedappusage/src/android/processor/unsupportedappusage/SignatureBuilder.java
+++ /dev/null
@@ -1,258 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.processor.unsupportedappusage;
-
-import static javax.lang.model.element.ElementKind.PACKAGE;
-import static javax.tools.Diagnostic.Kind.ERROR;
-import static javax.tools.Diagnostic.Kind.WARNING;
-
-import com.google.common.base.Strings;
-import com.google.common.collect.ImmutableMap;
-import com.sun.tools.javac.code.Type;
-
-import java.lang.annotation.Annotation;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-
-import javax.annotation.processing.Messager;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.PackageElement;
-import javax.lang.model.element.TypeElement;
-import javax.lang.model.element.VariableElement;
-import javax.lang.model.type.ArrayType;
-import javax.lang.model.type.DeclaredType;
-import javax.lang.model.type.TypeKind;
-import javax.lang.model.type.TypeMirror;
-
-/**
- * Builds a dex signature for a given method or field.
- */
-public class SignatureBuilder {
-
-    private static final Map<TypeKind, String> TYPE_MAP = ImmutableMap.<TypeKind, String>builder()
-            .put(TypeKind.BOOLEAN, "Z")
-            .put(TypeKind.BYTE, "B")
-            .put(TypeKind.CHAR, "C")
-            .put(TypeKind.DOUBLE, "D")
-            .put(TypeKind.FLOAT, "F")
-            .put(TypeKind.INT, "I")
-            .put(TypeKind.LONG, "J")
-            .put(TypeKind.SHORT, "S")
-            .put(TypeKind.VOID, "V")
-            .build();
-
-    private final Messager mMessager;
-
-    /**
-     * Exception used internally when we can't build a signature. Whenever this is thrown, an error
-     * will also be written to the Messager.
-     */
-    private class SignatureBuilderException extends Exception {
-        public SignatureBuilderException(String message) {
-            super(message);
-        }
-
-        public void report(Element offendingElement) {
-            mMessager.printMessage(ERROR, getMessage(), offendingElement);
-        }
-    }
-
-    public SignatureBuilder(Messager messager) {
-        mMessager = messager;
-    }
-
-    /**
-     * Returns a list of enclosing elements for the given element, with the package first, and
-     * excluding the element itself.
-     */
-    private List<Element> getEnclosingElements(Element e) {
-        List<Element> enclosing = new ArrayList<>();
-        e = e.getEnclosingElement(); // don't include the element itself.
-        while (e != null) {
-            enclosing.add(e);
-            e = e.getEnclosingElement();
-        }
-        Collections.reverse(enclosing);
-        return enclosing;
-    }
-
-    /**
-     * Get the dex signature for a clazz, in format "Lpackage/name/Outer$Inner;"
-     */
-    private String getClassSignature(TypeElement clazz) {
-        StringBuilder sb = new StringBuilder("L");
-        for (Element enclosing : getEnclosingElements(clazz)) {
-            switch (enclosing.getKind()) {
-                case MODULE:
-                    // ignore this.
-                    break;
-                case PACKAGE:
-                    sb.append(((PackageElement) enclosing)
-                            .getQualifiedName()
-                            .toString()
-                            .replace('.', '/'));
-                    sb.append('/');
-                    break;
-                default:
-                    sb.append(enclosing.getSimpleName()).append('$');
-                    break;
-            }
-
-        }
-        return sb
-                .append(clazz.getSimpleName())
-                .append(";")
-                .toString();
-    }
-
-    /**
-     * Returns the type signature for a given type. For primitive types, a single character.
-     * For classes, the class signature. For arrays, a "[" preceeding the component type.
-     */
-    private String getTypeSignature(TypeMirror type) throws SignatureBuilderException {
-        String sig = TYPE_MAP.get(type.getKind());
-        if (sig != null) {
-            return sig;
-        }
-        switch (type.getKind()) {
-            case ARRAY:
-                return "[" + getTypeSignature(((ArrayType) type).getComponentType());
-            case DECLARED:
-                Element declaring = ((DeclaredType) type).asElement();
-                if (!(declaring instanceof TypeElement)) {
-                    throw new SignatureBuilderException(
-                            "Can't handle declared type of kind " + declaring.getKind());
-                }
-                return getClassSignature((TypeElement) declaring);
-            case TYPEVAR:
-                Type.TypeVar typeVar = (Type.TypeVar) type;
-                if (typeVar.getLowerBound().getKind() != TypeKind.NULL) {
-                    return getTypeSignature(typeVar.getLowerBound());
-                } else if (typeVar.getUpperBound().getKind() != TypeKind.NULL) {
-                    return getTypeSignature(typeVar.getUpperBound());
-                } else {
-                    throw new SignatureBuilderException("Can't handle typevar with no bound");
-                }
-
-            default:
-                throw new SignatureBuilderException("Can't handle type of kind " + type.getKind());
-        }
-    }
-
-    /**
-     * Get the signature for an executable, either a method or a constructor.
-     *
-     * @param name   "<init>" for  constructor, else the method name
-     * @param method The executable element in question.
-     */
-    private String getExecutableSignature(CharSequence name, ExecutableElement method)
-            throws SignatureBuilderException {
-        StringBuilder sig = new StringBuilder();
-        sig.append(getClassSignature((TypeElement) method.getEnclosingElement()))
-                .append("->")
-                .append(name)
-                .append("(");
-        for (VariableElement param : method.getParameters()) {
-            sig.append(getTypeSignature(param.asType()));
-        }
-        sig.append(")")
-                .append(getTypeSignature(method.getReturnType()));
-        return sig.toString();
-    }
-
-    private String buildMethodSignature(ExecutableElement method) throws SignatureBuilderException {
-        return getExecutableSignature(method.getSimpleName(), method);
-    }
-
-    private String buildConstructorSignature(ExecutableElement cons)
-            throws SignatureBuilderException {
-        return getExecutableSignature("<init>", cons);
-    }
-
-    private String buildFieldSignature(VariableElement field) throws SignatureBuilderException {
-        StringBuilder sig = new StringBuilder();
-        sig.append(getClassSignature((TypeElement) field.getEnclosingElement()))
-                .append("->")
-                .append(field.getSimpleName())
-                .append(":")
-                .append(getTypeSignature(field.asType()))
-        ;
-        return sig.toString();
-    }
-
-    /**
-     * Creates the signature for an annotated element.
-     *
-     * @param annotationType type of annotation being processed.
-     * @param element        element for which we want to create a signature.
-     */
-    public String buildSignature(Class<? extends Annotation> annotationType, Element element) {
-        try {
-            String signature;
-            switch (element.getKind()) {
-                case METHOD:
-                    signature = buildMethodSignature((ExecutableElement) element);
-                    break;
-                case CONSTRUCTOR:
-                    signature = buildConstructorSignature((ExecutableElement) element);
-                    break;
-                case FIELD:
-                    signature = buildFieldSignature((VariableElement) element);
-                    break;
-                default:
-                    return null;
-            }
-            // Obtain annotation objects
-            Annotation annotation = element.getAnnotation(annotationType);
-            if (annotation == null) {
-                throw new IllegalStateException(
-                        "Element doesn't have any UnsupportedAppUsage annotation");
-            }
-            try {
-                Method expectedSignatureMethod = annotationType.getMethod("expectedSignature");
-                // If we have an expected signature on the annotation, warn if it doesn't match.
-                String expectedSignature = expectedSignatureMethod.invoke(annotation).toString();
-                if (!Strings.isNullOrEmpty(expectedSignature)) {
-                    if (!signature.equals(expectedSignature)) {
-                        mMessager.printMessage(
-                                WARNING,
-                                String.format(
-                                        "Expected signature doesn't match generated signature.\n"
-                                                + " Expected:  %s\n Generated: %s",
-                                        expectedSignature, signature),
-                                element);
-                    }
-                }
-                return signature;
-            } catch (NoSuchMethodException e) {
-                throw new IllegalStateException(
-                        "Annotation type does not have expectedSignature parameter", e);
-            } catch (IllegalAccessException | InvocationTargetException e) {
-                throw new IllegalStateException(
-                        "Could not get expectedSignature parameter for annotation", e);
-            }
-        } catch (SignatureBuilderException problem) {
-            problem.report(element);
-            return null;
-        }
-    }
-}
diff --git a/tools/processors/unsupportedappusage/src/android/processor/unsupportedappusage/UnsupportedAppUsageProcessor.java b/tools/processors/unsupportedappusage/src/android/processor/unsupportedappusage/UnsupportedAppUsageProcessor.java
deleted file mode 100644
index 5bb956a..0000000
--- a/tools/processors/unsupportedappusage/src/android/processor/unsupportedappusage/UnsupportedAppUsageProcessor.java
+++ /dev/null
@@ -1,204 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.processor.unsupportedappusage;
-
-import static javax.tools.StandardLocation.CLASS_OUTPUT;
-
-import com.google.common.base.Joiner;
-import com.google.common.collect.ImmutableSet;
-import com.sun.tools.javac.model.JavacElements;
-import com.sun.tools.javac.tree.JCTree;
-import com.sun.tools.javac.util.Pair;
-import com.sun.tools.javac.util.Position;
-
-import java.io.IOException;
-import java.io.PrintStream;
-import java.lang.annotation.Annotation;
-import java.net.URLEncoder;
-import java.util.Map;
-import java.util.Set;
-import java.util.TreeMap;
-import java.util.stream.Stream;
-
-import javax.annotation.processing.AbstractProcessor;
-import javax.annotation.processing.RoundEnvironment;
-import javax.annotation.processing.SupportedAnnotationTypes;
-import javax.lang.model.SourceVersion;
-import javax.lang.model.element.AnnotationMirror;
-import javax.lang.model.element.AnnotationValue;
-import javax.lang.model.element.Element;
-import javax.lang.model.element.ExecutableElement;
-import javax.lang.model.element.TypeElement;
-
-/**
- * Annotation processor for {@link UnsupportedAppUsage} annotations.
- *
- * This processor currently outputs a CSV file with a mapping of dex signatures to corresponding
- * source positions.
- *
- * This is used for automating updates to the annotations themselves.
- */
-@SupportedAnnotationTypes({"android.annotation.UnsupportedAppUsage",
-        "dalvik.annotation.compat.UnsupportedAppUsage"
-})
-public class UnsupportedAppUsageProcessor extends AbstractProcessor {
-
-    // Package name for writing output. Output will be written to the "class output" location within
-    // this package.
-    private static final String PACKAGE = "unsupportedappusage";
-    private static final String INDEX_CSV = "unsupportedappusage_index.csv";
-
-    private static final ImmutableSet<Class<? extends Annotation>> SUPPORTED_ANNOTATIONS =
-            ImmutableSet.of(android.annotation.UnsupportedAppUsage.class,
-                    dalvik.annotation.compat.UnsupportedAppUsage.class);
-    private static final ImmutableSet<String> SUPPORTED_ANNOTATION_NAMES =
-            SUPPORTED_ANNOTATIONS.stream().map(annotation -> annotation.getCanonicalName()).collect(
-                    ImmutableSet.toImmutableSet());
-
-    @Override
-    public SourceVersion getSupportedSourceVersion() {
-        return SourceVersion.latest();
-    }
-
-    /**
-     * Write the contents of a stream to a text file, with one line per item.
-     */
-    private void writeToFile(String name,
-            String headerLine,
-            Stream<?> contents) throws IOException {
-        PrintStream out = new PrintStream(processingEnv.getFiler().createResource(
-                CLASS_OUTPUT,
-                PACKAGE,
-                name)
-                .openOutputStream());
-        out.println(headerLine);
-        contents.forEach(o -> out.println(o));
-        if (out.checkError()) {
-            throw new IOException("Error when writing to " + name);
-        }
-        out.close();
-    }
-
-    /**
-     * Find the annotation mirror for the @UnsupportedAppUsage annotation on the given element.
-     */
-    private AnnotationMirror getUnsupportedAppUsageAnnotationMirror(Element e) {
-        for (AnnotationMirror m : e.getAnnotationMirrors()) {
-            TypeElement type = (TypeElement) m.getAnnotationType().asElement();
-            if (SUPPORTED_ANNOTATION_NAMES.contains(type.getQualifiedName().toString())) {
-                return m;
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Returns a CSV header line for the columns returned by
-     * {@link #getAnnotationIndex(String, Element)}.
-     */
-    private String getCsvHeaders() {
-        return Joiner.on(',').join(
-                "signature",
-                "file",
-                "startline",
-                "startcol",
-                "endline",
-                "endcol",
-                "properties"
-        );
-    }
-
-    private String encodeAnnotationProperties(AnnotationMirror annotation) {
-        StringBuilder sb = new StringBuilder();
-        for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> e
-                : annotation.getElementValues().entrySet()) {
-            if (sb.length() > 0) {
-                sb.append("&");
-            }
-            sb.append(e.getKey().getSimpleName())
-                    .append("=")
-                    .append(URLEncoder.encode(e.getValue().toString()));
-        }
-        return sb.toString();
-    }
-
-    /**
-     * Maps an annotated element to the source position of the @UnsupportedAppUsage annotation
-     * attached to it. It returns CSV in the format:
-     * dex-signature,filename,start-line,start-col,end-line,end-col
-     *
-     * The positions refer to the annotation itself, *not* the annotated member. This can therefore
-     * be used to read just the annotation from the file, and to perform in-place edits on it.
-     *
-     * @param signature        the dex signature for the element.
-     * @param annotatedElement The annotated element
-     * @return A single line of CSV text
-     */
-    private String getAnnotationIndex(String signature, Element annotatedElement) {
-        JavacElements javacElem = (JavacElements) processingEnv.getElementUtils();
-        AnnotationMirror unsupportedAppUsage =
-                getUnsupportedAppUsageAnnotationMirror(annotatedElement);
-        Pair<JCTree, JCTree.JCCompilationUnit> pair =
-                javacElem.getTreeAndTopLevel(annotatedElement, unsupportedAppUsage, null);
-        Position.LineMap lines = pair.snd.lineMap;
-        return Joiner.on(",").join(
-                signature,
-                pair.snd.getSourceFile().getName(),
-                lines.getLineNumber(pair.fst.pos().getStartPosition()),
-                lines.getColumnNumber(pair.fst.pos().getStartPosition()),
-                lines.getLineNumber(pair.fst.pos().getEndPosition(pair.snd.endPositions)),
-                lines.getColumnNumber(pair.fst.pos().getEndPosition(pair.snd.endPositions)),
-                encodeAnnotationProperties(unsupportedAppUsage));
-    }
-
-    /**
-     * This is the main entry point in the processor, called by the compiler.
-     */
-    @Override
-    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
-        Map<String, Element> signatureMap = new TreeMap<>();
-        SignatureBuilder sb = new SignatureBuilder(processingEnv.getMessager());
-        for (Class<? extends Annotation> supportedAnnotation : SUPPORTED_ANNOTATIONS) {
-            Set<? extends Element> annotated = roundEnv.getElementsAnnotatedWith(
-                    supportedAnnotation);
-            if (annotated.size() == 0) {
-                continue;
-            }
-            // Build signatures for each annotated member and put them in a map from signature to
-            // member.
-            for (Element e : annotated) {
-                String sig = sb.buildSignature(supportedAnnotation, e);
-                if (sig != null) {
-                    signatureMap.put(sig, e);
-                }
-            }
-        }
-
-        if (!signatureMap.isEmpty()) {
-            try {
-                writeToFile(INDEX_CSV,
-                        getCsvHeaders(),
-                        signatureMap.entrySet()
-                                .stream()
-                                .map(e -> getAnnotationIndex(e.getKey(), e.getValue())));
-            } catch (IOException e) {
-                throw new RuntimeException("Failed to write output", e);
-            }
-        }
-        return true;
-    }
-}
diff --git a/tools/processors/unsupportedappusage/test/src/android/processor/unsupportedappusage/CsvReader.java b/tools/processors/unsupportedappusage/test/src/android/processor/unsupportedappusage/CsvReader.java
deleted file mode 100644
index 23db99e..0000000
--- a/tools/processors/unsupportedappusage/test/src/android/processor/unsupportedappusage/CsvReader.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.processor.unsupportedappusage;
-
-import com.google.common.base.Splitter;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-public class CsvReader {
-
-    private final Splitter mSplitter;
-    private final List<String> mColumns;
-    private final List<Map<String, String>> mContents;
-
-    public CsvReader(InputStream in) throws IOException {
-        mSplitter = Splitter.on(",");
-        BufferedReader br = new BufferedReader(new InputStreamReader(in));
-        mColumns = mSplitter.splitToList(br.readLine());
-        mContents = new ArrayList<>();
-        String line = br.readLine();
-        while (line != null) {
-            List<String> contents = mSplitter.splitToList(line);
-            Map<String, String> contentMap = new HashMap<>();
-            for (int i = 0; i < Math.min(contents.size(), mColumns.size()); ++i) {
-                contentMap.put(mColumns.get(i), contents.get(i));
-            }
-            mContents.add(contentMap);
-            line = br.readLine();
-        }
-        br.close();
-    }
-
-    public List<String> getColumns() {
-        return mColumns;
-    }
-
-    public List<Map<String, String>> getContents() {
-        return mContents;
-    }
-}
diff --git a/tools/processors/unsupportedappusage/test/src/android/processor/unsupportedappusage/UnsupportedAppUsageProcessorTest.java b/tools/processors/unsupportedappusage/test/src/android/processor/unsupportedappusage/UnsupportedAppUsageProcessorTest.java
deleted file mode 100644
index 012e88f..0000000
--- a/tools/processors/unsupportedappusage/test/src/android/processor/unsupportedappusage/UnsupportedAppUsageProcessorTest.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.processor.unsupportedappusage;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import com.android.javac.Javac;
-
-import com.google.common.base.Joiner;
-
-import org.junit.Before;
-import org.junit.Test;
-
-import java.io.IOException;
-import java.util.Map;
-
-public class UnsupportedAppUsageProcessorTest {
-
-    private Javac mJavac;
-
-    @Before
-    public void setup() throws IOException {
-        mJavac = new Javac();
-        mJavac.addSource("dalvik.annotation.compat.UnsupportedAppUsage", Joiner.on('\n').join(
-                "package dalvik.annotation.compat;",
-                "public @interface UnsupportedAppUsage {",
-                "    String expectedSignature() default \"\";\n",
-                "    String someProperty() default \"\";",
-                "}"));
-    }
-
-    private CsvReader compileAndReadCsv() throws IOException {
-        mJavac.compileWithAnnotationProcessor(new UnsupportedAppUsageProcessor());
-        return new CsvReader(
-                mJavac.getOutputFile("unsupportedappusage/unsupportedappusage_index.csv"));
-    }
-
-    @Test
-    public void testSignatureFormat() throws Exception {
-        mJavac.addSource("a.b.Class", Joiner.on('\n').join(
-                "package a.b;",
-                "import dalvik.annotation.compat.UnsupportedAppUsage;",
-                "public class Class {",
-                "  @UnsupportedAppUsage",
-                "  public void method() {}",
-                "}"));
-        assertThat(compileAndReadCsv().getContents().get(0)).containsEntry(
-                "signature", "La/b/Class;->method()V"
-        );
-    }
-
-    @Test
-    public void testSourcePosition() throws Exception {
-        mJavac.addSource("a.b.Class", Joiner.on('\n').join(
-                "package a.b;", // 1
-                "import dalvik.annotation.compat.UnsupportedAppUsage;", // 2
-                "public class Class {", // 3
-                "  @UnsupportedAppUsage", // 4
-                "  public void method() {}", // 5
-                "}"));
-        Map<String, String> row = compileAndReadCsv().getContents().get(0);
-        assertThat(row).containsEntry("startline", "4");
-        assertThat(row).containsEntry("startcol", "3");
-        assertThat(row).containsEntry("endline", "4");
-        assertThat(row).containsEntry("endcol", "23");
-    }
-
-    @Test
-    public void testAnnotationProperties() throws Exception {
-        mJavac.addSource("a.b.Class", Joiner.on('\n').join(
-                "package a.b;", // 1
-                "import dalvik.annotation.compat.UnsupportedAppUsage;", // 2
-                "public class Class {", // 3
-                "  @UnsupportedAppUsage(someProperty=\"value\")", // 4
-                "  public void method() {}", // 5
-                "}"));
-        assertThat(compileAndReadCsv().getContents().get(0)).containsEntry(
-                "properties", "someProperty=%22value%22");
-    }
-
-
-}
diff --git a/wifi/java/android/net/wifi/IOnWifiActivityEnergyInfoListener.aidl b/wifi/java/android/net/wifi/IOnWifiActivityEnergyInfoListener.aidl
new file mode 100644
index 0000000..7e25fd8
--- /dev/null
+++ b/wifi/java/android/net/wifi/IOnWifiActivityEnergyInfoListener.aidl
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net.wifi;
+
+import android.os.connectivity.WifiActivityEnergyInfo;
+
+/**
+ * Interface for Wi-Fi activity energy info listener.
+ *
+ * @hide
+ */
+oneway interface IOnWifiActivityEnergyInfoListener
+{
+    /**
+     * Service to manager callback providing current Wi-Fi activity energy info.
+     * @param info the Wi-Fi activity energy info
+     */
+    void onWifiActivityEnergyInfo(in WifiActivityEnergyInfo info);
+}
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 032f66a..fccbcf7 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -28,15 +28,15 @@
 import android.net.wifi.IDppCallback;
 import android.net.wifi.ILocalOnlyHotspotCallback;
 import android.net.wifi.INetworkRequestMatchCallback;
+import android.net.wifi.IOnWifiActivityEnergyInfoListener;
+import android.net.wifi.IOnWifiUsabilityStatsListener;
 import android.net.wifi.IScanResultsCallback;
 import android.net.wifi.ISoftApCallback;
 import android.net.wifi.ISuggestionConnectionStatusListener;
 import android.net.wifi.ITrafficStateCallback;
 import android.net.wifi.ITxPacketCountListener;
-import android.net.wifi.IOnWifiUsabilityStatsListener;
 import android.net.wifi.ScanResult;
 import android.net.wifi.SoftApConfiguration;
-import android.net.wifi.WifiActivityEnergyInfo;
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiInfo;
 import android.net.wifi.WifiNetworkSuggestion;
@@ -44,6 +44,7 @@
 import android.os.Messenger;
 import android.os.ResultReceiver;
 import android.os.WorkSource;
+import android.os.connectivity.WifiActivityEnergyInfo;
 
 /**
  * Interface that allows controlling and querying Wi-Fi connectivity.
@@ -56,13 +57,7 @@
 
     WifiActivityEnergyInfo reportActivityInfo();
 
-    /**
-     * Requests the controller activity info asynchronously.
-     * The implementor is expected to reply with the
-     * {@link android.net.wifi.WifiActivityEnergyInfo} object placed into the Bundle with the key
-     * {@link android.os.BatteryStats#RESULT_RECEIVER_CONTROLLER_KEY}. The result code is ignored.
-     */
-    oneway void requestActivityInfo(in ResultReceiver result);
+    oneway void getWifiActivityEnergyInfoAsync(in IOnWifiActivityEnergyInfoListener listener);
 
     ParceledListSlice getConfiguredNetworks(String packageName, String featureId);
 
diff --git a/wifi/java/android/net/wifi/ScanResult.java b/wifi/java/android/net/wifi/ScanResult.java
index 9956901..729ef61 100644
--- a/wifi/java/android/net/wifi/ScanResult.java
+++ b/wifi/java/android/net/wifi/ScanResult.java
@@ -499,6 +499,13 @@
 
     /**
      * @hide
+     */
+    public boolean is6GHz() {
+        return ScanResult.is6GHz(frequency);
+    }
+
+    /**
+     * @hide
      * TODO: makes real freq boundaries
      */
     public static boolean is5GHz(int freq) {
@@ -506,6 +513,13 @@
     }
 
     /**
+     * @hide
+     */
+    public static boolean is6GHz(int freq) {
+        return freq > 5925 && freq < 7125;
+    }
+
+    /**
      *  @hide
      * anqp lines from supplicant BSS response
      */
diff --git a/wifi/java/android/net/wifi/SoftApConfiguration.java b/wifi/java/android/net/wifi/SoftApConfiguration.java
index 8030bd6..d755053 100644
--- a/wifi/java/android/net/wifi/SoftApConfiguration.java
+++ b/wifi/java/android/net/wifi/SoftApConfiguration.java
@@ -388,7 +388,7 @@
         @NonNull
         public Builder setBssid(@Nullable MacAddress bssid) {
             if (bssid != null) {
-                Preconditions.checkArgument(!bssid.equals(MacAddress.ALL_ZEROS_ADDRESS));
+                Preconditions.checkArgument(!bssid.equals(WifiManager.ALL_ZEROS_MAC_ADDRESS));
                 Preconditions.checkArgument(!bssid.equals(MacAddress.BROADCAST_ADDRESS));
             }
             mBssid = bssid;
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 52f5eee..0108d5a 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -35,6 +35,7 @@
 import android.content.pm.ParceledListSlice;
 import android.net.ConnectivityManager;
 import android.net.DhcpInfo;
+import android.net.MacAddress;
 import android.net.Network;
 import android.net.NetworkStack;
 import android.net.wifi.hotspot2.IProvisioningCallback;
@@ -48,6 +49,7 @@
 import android.os.Looper;
 import android.os.RemoteException;
 import android.os.WorkSource;
+import android.os.connectivity.WifiActivityEnergyInfo;
 import android.text.TextUtils;
 import android.util.Log;
 import android.util.Pair;
@@ -1186,6 +1188,10 @@
     /** Indicates an invalid SSID. */
     public static final String UNKNOWN_SSID = "<unknown ssid>";
 
+    /** @hide */
+    public static final MacAddress ALL_ZEROS_MAC_ADDRESS =
+            MacAddress.fromString("00:00:00:00:00:00");
+
     /* Number of currently active WifiLocks and MulticastLocks */
     @UnsupportedAppUsage
     private int mActiveLockCount;
@@ -1241,6 +1247,7 @@
      * <li>allowedAuthAlgorithms</li>
      * <li>allowedPairwiseCiphers</li>
      * <li>allowedGroupCiphers</li>
+     * <li>status</li>
      * </ul>
      * @return a list of network configurations in the form of a list
      * of {@link WifiConfiguration} objects.
@@ -2391,6 +2398,82 @@
     }
 
     /**
+     * Interface for Wi-Fi activity energy info listener. Should be implemented by applications and
+     * set when calling {@link WifiManager#getWifiActivityEnergyInfoAsync}.
+     *
+     * @hide
+     */
+    @SystemApi
+    public interface OnWifiActivityEnergyInfoListener {
+        /**
+         * Called when Wi-Fi activity energy info is available.
+         * Note: this listener is triggered at most once for each call to
+         * {@link #getWifiActivityEnergyInfoAsync}.
+         *
+         * @param info the latest {@link WifiActivityEnergyInfo}, or null if unavailable.
+         */
+        void onWifiActivityEnergyInfo(@Nullable WifiActivityEnergyInfo info);
+    }
+
+    private static class OnWifiActivityEnergyInfoProxy
+            extends IOnWifiActivityEnergyInfoListener.Stub {
+        private final Object mLock = new Object();
+        @Nullable @GuardedBy("mLock") private Executor mExecutor;
+        @Nullable @GuardedBy("mLock") private OnWifiActivityEnergyInfoListener mListener;
+
+        OnWifiActivityEnergyInfoProxy(Executor executor,
+                OnWifiActivityEnergyInfoListener listener) {
+            mExecutor = executor;
+            mListener = listener;
+        }
+
+        @Override
+        public void onWifiActivityEnergyInfo(WifiActivityEnergyInfo info) {
+            Executor executor;
+            OnWifiActivityEnergyInfoListener listener;
+            synchronized (mLock) {
+                if (mExecutor == null || mListener == null) {
+                    return;
+                }
+                executor = mExecutor;
+                listener = mListener;
+                // null out to allow garbage collection, prevent triggering listener more than once
+                mExecutor = null;
+                mListener = null;
+            }
+            Binder.clearCallingIdentity();
+            executor.execute(() -> listener.onWifiActivityEnergyInfo(info));
+        }
+    }
+
+    /**
+     * Request to get the current {@link WifiActivityEnergyInfo} asynchronously.
+     * Note: This method will return null if {@link #isEnhancedPowerReportingSupported()} returns
+     * false.
+     *
+     * @param executor the executor that the listener will be invoked on
+     * @param listener the listener that will receive the {@link WifiActivityEnergyInfo} object
+     *                 when it becomes available. The listener will be triggered at most once for
+     *                 each call to this method.
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(ACCESS_WIFI_STATE)
+    public void getWifiActivityEnergyInfoAsync(
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull OnWifiActivityEnergyInfoListener listener) {
+        if (executor == null) throw new IllegalArgumentException("executor cannot be null");
+        if (listener == null) throw new IllegalArgumentException("listener cannot be null");
+        try {
+            mService.getWifiActivityEnergyInfoAsync(
+                    new OnWifiActivityEnergyInfoProxy(executor, listener));
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Request a scan for access points. Returns immediately. The availability
      * of the results is made known later by means of an asynchronous event sent
      * on completion of the scan.
diff --git a/wifi/java/android/net/wifi/WifiNetworkScoreCache.java b/wifi/java/android/net/wifi/WifiNetworkScoreCache.java
index 5a212a8..be37c22 100755
--- a/wifi/java/android/net/wifi/WifiNetworkScoreCache.java
+++ b/wifi/java/android/net/wifi/WifiNetworkScoreCache.java
@@ -22,7 +22,6 @@
 import android.content.Context;
 import android.net.INetworkScoreCache;
 import android.net.NetworkKey;
-import android.net.NetworkScoreManager;
 import android.net.ScoredNetwork;
 import android.os.Handler;
 import android.os.Process;
@@ -39,10 +38,10 @@
 /**
  * {@link INetworkScoreCache} implementation for Wifi Networks.
  *
+ * TODO: This should not be part of wifi mainline module.
  * @hide
  */
-public class WifiNetworkScoreCache extends INetworkScoreCache.Stub
-        implements NetworkScoreManager.NetworkScoreCallback {
+public class WifiNetworkScoreCache extends INetworkScoreCache.Stub {
     private static final String TAG = "WifiNetworkScoreCache";
     private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
 
@@ -248,17 +247,6 @@
     }
 
     @Override protected final void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
-        WifiManager wifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
-        dumpWithLatestScanResults(fd, writer, args, wifiManager.getScanResults());
-    }
-
-    /**
-     * This is directly invoked from within Wifi-Service (on it's instance of this class), hence
-     * avoid making the WifiManager.getScanResults() call to avoid a deadlock.
-     */
-    public final void dumpWithLatestScanResults(
-            FileDescriptor fd, PrintWriter writer, String[] args,
-            List<ScanResult> latestScanResults) {
         mContext.enforceCallingOrSelfPermission(permission.DUMP, TAG);
         String header = String.format("WifiNetworkScoreCache (%s/%d)",
                 mContext.getPackageName(), Process.myUid());
@@ -269,7 +257,8 @@
                 writer.println("    " + score);
             }
             writer.println("  Network scores for latest ScanResults:");
-            for (ScanResult scanResult : latestScanResults) {
+            WifiManager wifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
+            for (ScanResult scanResult : wifiManager.getScanResults()) {
                 writer.println(
                         "    " + buildNetworkKey(scanResult) + ": " + getNetworkScore(scanResult));
             }
diff --git a/wifi/java/android/net/wifi/WifiNetworkSpecifier.java b/wifi/java/android/net/wifi/WifiNetworkSpecifier.java
index ba9dd37..07afd7f 100644
--- a/wifi/java/android/net/wifi/WifiNetworkSpecifier.java
+++ b/wifi/java/android/net/wifi/WifiNetworkSpecifier.java
@@ -49,11 +49,11 @@
         private static final String MATCH_ALL_SSID_PATTERN_PATH = ".*";
         private static final String MATCH_EMPTY_SSID_PATTERN_PATH = "";
         private static final Pair<MacAddress, MacAddress> MATCH_NO_BSSID_PATTERN1 =
-                new Pair(MacAddress.BROADCAST_ADDRESS, MacAddress.BROADCAST_ADDRESS);
+                new Pair<>(MacAddress.BROADCAST_ADDRESS, MacAddress.BROADCAST_ADDRESS);
         private static final Pair<MacAddress, MacAddress> MATCH_NO_BSSID_PATTERN2 =
-                new Pair(MacAddress.ALL_ZEROS_ADDRESS, MacAddress.BROADCAST_ADDRESS);
+                new Pair<>(WifiManager.ALL_ZEROS_MAC_ADDRESS, MacAddress.BROADCAST_ADDRESS);
         private static final Pair<MacAddress, MacAddress> MATCH_ALL_BSSID_PATTERN =
-                new Pair(MacAddress.ALL_ZEROS_ADDRESS, MacAddress.ALL_ZEROS_ADDRESS);
+                new Pair<>(WifiManager.ALL_ZEROS_MAC_ADDRESS, WifiManager.ALL_ZEROS_MAC_ADDRESS);
         private static final MacAddress MATCH_EXACT_BSSID_PATTERN_MASK =
                 MacAddress.BROADCAST_ADDRESS;
 
diff --git a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
index a5ca82c..e78104d 100644
--- a/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
+++ b/wifi/java/android/net/wifi/WifiNetworkSuggestion.java
@@ -545,7 +545,7 @@
                 }
                 if (mBssid != null
                         && (mBssid.equals(MacAddress.BROADCAST_ADDRESS)
-                        || mBssid.equals(MacAddress.ALL_ZEROS_ADDRESS))) {
+                        || mBssid.equals(WifiManager.ALL_ZEROS_MAC_ADDRESS))) {
                     throw new IllegalStateException("invalid bssid for suggestion");
                 }
                 wifiConfiguration = buildWifiConfiguration();
diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java
index b293077..760497b 100644
--- a/wifi/java/android/net/wifi/WifiScanner.java
+++ b/wifi/java/android/net/wifi/WifiScanner.java
@@ -65,14 +65,17 @@
     /** @hide */
     public static final int WIFI_BAND_INDEX_5_GHZ_DFS_ONLY = 2;
     /** @hide */
-    public static final int WIFI_BAND_COUNT = 3;
+    public static final int WIFI_BAND_INDEX_6_GHZ = 3;
+    /** @hide */
+    public static final int WIFI_BAND_COUNT = 4;
 
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
     @IntDef(prefix = {"WIFI_BAND_INDEX_"}, value = {
             WIFI_BAND_INDEX_24_GHZ,
             WIFI_BAND_INDEX_5_GHZ,
-            WIFI_BAND_INDEX_5_GHZ_DFS_ONLY})
+            WIFI_BAND_INDEX_5_GHZ_DFS_ONLY,
+            WIFI_BAND_INDEX_6_GHZ})
     public @interface WifiBandIndex {}
 
     /** no band specified; use channel list instead */
@@ -83,6 +86,8 @@
     public static final int WIFI_BAND_5_GHZ = 1 << WIFI_BAND_INDEX_5_GHZ;
     /** DFS channels from 5 GHz band only */
     public static final int WIFI_BAND_5_GHZ_DFS_ONLY  = 1 << WIFI_BAND_INDEX_5_GHZ_DFS_ONLY;
+    /** 6 GHz band */
+    public static final int WIFI_BAND_6_GHZ = 1 << WIFI_BAND_INDEX_6_GHZ;
 
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
@@ -90,7 +95,8 @@
             WIFI_BAND_UNSPECIFIED,
             WIFI_BAND_24_GHZ,
             WIFI_BAND_5_GHZ,
-            WIFI_BAND_5_GHZ_DFS_ONLY})
+            WIFI_BAND_5_GHZ_DFS_ONLY,
+            WIFI_BAND_6_GHZ})
     public @interface WifiBandBasic {}
 
     /**
@@ -111,6 +117,11 @@
     /** Both 2.4 GHz band and 5 GHz band; with DFS channels */
     public static final int WIFI_BAND_BOTH_WITH_DFS =
             WIFI_BAND_24_GHZ | WIFI_BAND_5_GHZ | WIFI_BAND_5_GHZ_DFS_ONLY;
+    /** 2.4 GHz band and 5 GHz band (no DFS channels) and 6 GHz */
+    public static final int WIFI_BAND_24_5_6_GHZ = WIFI_BAND_BOTH | WIFI_BAND_6_GHZ;
+    /** 2.4 GHz band and 5 GHz band; with DFS channels and 6 GHz */
+    public static final int WIFI_BAND_24_5_WITH_DFS_6_GHZ =
+            WIFI_BAND_BOTH_WITH_DFS | WIFI_BAND_6_GHZ;
 
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
@@ -122,14 +133,17 @@
             WIFI_BAND_5_GHZ_DFS_ONLY,
             WIFI_BAND_24_GHZ_WITH_5GHZ_DFS,
             WIFI_BAND_5_GHZ_WITH_DFS,
-            WIFI_BAND_BOTH_WITH_DFS})
+            WIFI_BAND_BOTH_WITH_DFS,
+            WIFI_BAND_6_GHZ,
+            WIFI_BAND_24_5_6_GHZ,
+            WIFI_BAND_24_5_WITH_DFS_6_GHZ})
     public @interface WifiBand {}
 
     /**
      * Max band value
      * @hide
      */
-    public static final int WIFI_BAND_MAX = 8;
+    public static final int WIFI_BAND_MAX = 0x10;
 
     /** Minimum supported scanning period */
     public static final int MIN_SCAN_PERIOD_MS = 1000;
@@ -174,7 +188,7 @@
     @SystemApi
     @NonNull
     @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
-    public List<Integer> getAvailableChannels(@WifiBand int band) {
+    public List<Integer> getAvailableChannels(int band) {
         try {
             Bundle bundle = mService.getAvailableChannels(band, mContext.getOpPackageName(),
                     mContext.getFeatureId());
@@ -726,6 +740,8 @@
         public int min5GHzRssi;
         /** Minimum 2.4GHz RSSI for a BSSID to be considered */
         public int min24GHzRssi;
+        /** Minimum 6GHz RSSI for a BSSID to be considered */
+        public int min6GHzRssi;
         /** Maximum score that a network can have before bonuses */
         public int initialScoreMax;
         /**
@@ -739,6 +755,8 @@
         public int secureBonus;
         /** 5GHz RSSI score bonus (applied to all 5GHz networks) */
         public int band5GHzBonus;
+        /** 6GHz RSSI score bonus (applied to all 5GHz networks) */
+        public int band6GHzBonus;
         /** Pno Network filter list */
         public PnoNetwork[] networkList;
 
@@ -752,11 +770,13 @@
             dest.writeInt(isConnected ? 1 : 0);
             dest.writeInt(min5GHzRssi);
             dest.writeInt(min24GHzRssi);
+            dest.writeInt(min6GHzRssi);
             dest.writeInt(initialScoreMax);
             dest.writeInt(currentConnectionBonus);
             dest.writeInt(sameNetworkBonus);
             dest.writeInt(secureBonus);
             dest.writeInt(band5GHzBonus);
+            dest.writeInt(band6GHzBonus);
             if (networkList != null) {
                 dest.writeInt(networkList.length);
                 for (int i = 0; i < networkList.length; i++) {
@@ -778,11 +798,13 @@
                         settings.isConnected = in.readInt() == 1;
                         settings.min5GHzRssi = in.readInt();
                         settings.min24GHzRssi = in.readInt();
+                        settings.min6GHzRssi = in.readInt();
                         settings.initialScoreMax = in.readInt();
                         settings.currentConnectionBonus = in.readInt();
                         settings.sameNetworkBonus = in.readInt();
                         settings.secureBonus = in.readInt();
                         settings.band5GHzBonus = in.readInt();
+                        settings.band6GHzBonus = in.readInt();
                         int numNetworks = in.readInt();
                         settings.networkList = new PnoNetwork[numNetworks];
                         for (int i = 0; i < numNetworks; i++) {
diff --git a/wifi/java/com/android/server/wifi/BaseWifiService.java b/wifi/java/com/android/server/wifi/BaseWifiService.java
index bc86839..367cfa0 100644
--- a/wifi/java/com/android/server/wifi/BaseWifiService.java
+++ b/wifi/java/com/android/server/wifi/BaseWifiService.java
@@ -25,6 +25,7 @@
 import android.net.wifi.IDppCallback;
 import android.net.wifi.ILocalOnlyHotspotCallback;
 import android.net.wifi.INetworkRequestMatchCallback;
+import android.net.wifi.IOnWifiActivityEnergyInfoListener;
 import android.net.wifi.IOnWifiUsabilityStatsListener;
 import android.net.wifi.IScanResultsCallback;
 import android.net.wifi.IScanResultsListener;
@@ -35,7 +36,6 @@
 import android.net.wifi.IWifiManager;
 import android.net.wifi.ScanResult;
 import android.net.wifi.SoftApConfiguration;
-import android.net.wifi.WifiActivityEnergyInfo;
 import android.net.wifi.WifiConfiguration;
 import android.net.wifi.WifiInfo;
 import android.net.wifi.WifiNetworkSuggestion;
@@ -46,6 +46,7 @@
 import android.os.RemoteException;
 import android.os.ResultReceiver;
 import android.os.WorkSource;
+import android.os.connectivity.WifiActivityEnergyInfo;
 
 import java.util.List;
 import java.util.Map;
@@ -80,12 +81,16 @@
         throw new UnsupportedOperationException();
     }
 
-    @Override
     public void requestActivityInfo(ResultReceiver result) {
         throw new UnsupportedOperationException();
     }
 
     @Override
+    public void getWifiActivityEnergyInfoAsync(IOnWifiActivityEnergyInfoListener listener) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
     public ParceledListSlice getConfiguredNetworks(String packageName, String featureId) {
         throw new UnsupportedOperationException();
     }
diff --git a/wifi/tests/src/android/net/wifi/WifiManagerTest.java b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
index b3a73bc..de45149 100644
--- a/wifi/tests/src/android/net/wifi/WifiManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiManagerTest.java
@@ -16,15 +16,31 @@
 
 package android.net.wifi;
 
+import static android.net.wifi.WifiManager.ActionListener;
+import static android.net.wifi.WifiManager.BUSY;
+import static android.net.wifi.WifiManager.ERROR;
 import static android.net.wifi.WifiManager.LocalOnlyHotspotCallback.ERROR_GENERIC;
 import static android.net.wifi.WifiManager.LocalOnlyHotspotCallback.ERROR_INCOMPATIBLE_MODE;
 import static android.net.wifi.WifiManager.LocalOnlyHotspotCallback.ERROR_NO_CHANNEL;
 import static android.net.wifi.WifiManager.LocalOnlyHotspotCallback.ERROR_TETHERING_DISALLOWED;
 import static android.net.wifi.WifiManager.LocalOnlyHotspotCallback.REQUEST_REGISTERED;
+import static android.net.wifi.WifiManager.NOT_AUTHORIZED;
+import static android.net.wifi.WifiManager.OnWifiActivityEnergyInfoListener;
 import static android.net.wifi.WifiManager.SAP_START_FAILURE_GENERAL;
+import static android.net.wifi.WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS;
+import static android.net.wifi.WifiManager.STATUS_SUGGESTION_CONNECTION_FAILURE_AUTHENTICATION;
+import static android.net.wifi.WifiManager.TxPacketCountListener;
 import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED;
 import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLING;
 import static android.net.wifi.WifiManager.WIFI_AP_STATE_FAILED;
+import static android.net.wifi.WifiManager.WIFI_FEATURE_DPP;
+import static android.net.wifi.WifiManager.WIFI_FEATURE_OWE;
+import static android.net.wifi.WifiManager.WIFI_FEATURE_P2P;
+import static android.net.wifi.WifiManager.WIFI_FEATURE_PASSPOINT;
+import static android.net.wifi.WifiManager.WIFI_FEATURE_SCANNER;
+import static android.net.wifi.WifiManager.WIFI_FEATURE_WPA3_SAE;
+import static android.net.wifi.WifiManager.WIFI_FEATURE_WPA3_SUITE_B;
+import static android.net.wifi.WifiManager.WpsCallback;
 
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
@@ -71,6 +87,7 @@
 import android.os.HandlerExecutor;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.os.connectivity.WifiActivityEnergyInfo;
 import android.os.test.TestLooper;
 
 import androidx.test.filters.SmallTest;
@@ -113,6 +130,7 @@
     @Mock TrafficStateCallback mTrafficStateCallback;
     @Mock NetworkRequestMatchCallback mNetworkRequestMatchCallback;
     @Mock OnWifiUsabilityStatsListener mOnWifiUsabilityStatsListener;
+    @Mock OnWifiActivityEnergyInfoListener mOnWifiActivityEnergyInfoListener;
     @Mock SuggestionConnectionStatusListener mListener;
     @Mock Runnable mRunnable;
     @Mock Executor mExecutor;
@@ -124,6 +142,7 @@
     private WifiManager mWifiManager;
     private WifiNetworkSuggestion mWifiNetworkSuggestion;
     private ScanResultsCallback mScanResultsCallback;
+    private WifiActivityEnergyInfo mWifiActivityEnergyInfo;
 
     @Before
     public void setUp() throws Exception {
@@ -142,6 +161,7 @@
                 mRunnable.run();
             }
         };
+        mWifiActivityEnergyInfo = new WifiActivityEnergyInfo(0, 0, 0, 0, 0, 0, 0);
     }
 
     /**
@@ -1069,7 +1089,7 @@
     }
 
 
-    class WpsCallbackTester extends WifiManager.WpsCallback {
+    class WpsCallbackTester extends WpsCallback {
         public boolean mStarted = false;
         public boolean mSucceeded = false;
         public boolean mFailed = false;
@@ -1101,7 +1121,7 @@
         WpsCallbackTester wpsCallback = new WpsCallbackTester();
         mWifiManager.startWps(null, wpsCallback);
         assertTrue(wpsCallback.mFailed);
-        assertEquals(WifiManager.ERROR, wpsCallback.mFailureCode);
+        assertEquals(ERROR, wpsCallback.mFailureCode);
         assertFalse(wpsCallback.mStarted);
         assertFalse(wpsCallback.mSucceeded);
         verifyNoMoreInteractions(mWifiService);
@@ -1124,7 +1144,7 @@
         WpsCallbackTester wpsCallback = new WpsCallbackTester();
         mWifiManager.cancelWps(wpsCallback);
         assertTrue(wpsCallback.mFailed);
-        assertEquals(WifiManager.ERROR, wpsCallback.mFailureCode);
+        assertEquals(ERROR, wpsCallback.mFailureCode);
         assertFalse(wpsCallback.mStarted);
         assertFalse(wpsCallback.mSucceeded);
         verifyNoMoreInteractions(mWifiService);
@@ -1437,13 +1457,13 @@
     public void addGetRemoveNetworkSuggestions() throws Exception {
         List<WifiNetworkSuggestion> testList = new ArrayList<>();
         when(mWifiService.addNetworkSuggestions(any(List.class), anyString(),
-                nullable(String.class))).thenReturn(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS);
+                nullable(String.class))).thenReturn(STATUS_NETWORK_SUGGESTIONS_SUCCESS);
         when(mWifiService.removeNetworkSuggestions(any(List.class), anyString())).thenReturn(
-                WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS);
+                STATUS_NETWORK_SUGGESTIONS_SUCCESS);
         when(mWifiService.getNetworkSuggestions(anyString()))
                 .thenReturn(testList);
 
-        assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
+        assertEquals(STATUS_NETWORK_SUGGESTIONS_SUCCESS,
                 mWifiManager.addNetworkSuggestions(testList));
         verify(mWifiService).addNetworkSuggestions(anyList(), eq(TEST_PACKAGE_NAME),
                 nullable(String.class));
@@ -1451,7 +1471,7 @@
         assertEquals(testList, mWifiManager.getNetworkSuggestions());
         verify(mWifiService).getNetworkSuggestions(eq(TEST_PACKAGE_NAME));
 
-        assertEquals(WifiManager.STATUS_NETWORK_SUGGESTIONS_SUCCESS,
+        assertEquals(STATUS_NETWORK_SUGGESTIONS_SUCCESS,
                 mWifiManager.removeNetworkSuggestions(new ArrayList<>()));
         verify(mWifiService).removeNetworkSuggestions(anyList(), eq(TEST_PACKAGE_NAME));
     }
@@ -1515,10 +1535,10 @@
     @Test
     public void testIsEnhancedOpenSupported() throws Exception {
         when(mWifiService.getSupportedFeatures())
-                .thenReturn(new Long(WifiManager.WIFI_FEATURE_OWE));
+                .thenReturn(new Long(WIFI_FEATURE_OWE));
         assertTrue(mWifiManager.isEnhancedOpenSupported());
         when(mWifiService.getSupportedFeatures())
-                .thenReturn(new Long(~WifiManager.WIFI_FEATURE_OWE));
+                .thenReturn(new Long(~WIFI_FEATURE_OWE));
         assertFalse(mWifiManager.isEnhancedOpenSupported());
     }
 
@@ -1528,10 +1548,10 @@
     @Test
     public void testIsWpa3SaeSupported() throws Exception {
         when(mWifiService.getSupportedFeatures())
-                .thenReturn(new Long(WifiManager.WIFI_FEATURE_WPA3_SAE));
+                .thenReturn(new Long(WIFI_FEATURE_WPA3_SAE));
         assertTrue(mWifiManager.isWpa3SaeSupported());
         when(mWifiService.getSupportedFeatures())
-                .thenReturn(new Long(~WifiManager.WIFI_FEATURE_WPA3_SAE));
+                .thenReturn(new Long(~WIFI_FEATURE_WPA3_SAE));
         assertFalse(mWifiManager.isWpa3SaeSupported());
     }
 
@@ -1541,10 +1561,10 @@
     @Test
     public void testIsWpa3SuiteBSupported() throws Exception {
         when(mWifiService.getSupportedFeatures())
-                .thenReturn(new Long(WifiManager.WIFI_FEATURE_WPA3_SUITE_B));
+                .thenReturn(new Long(WIFI_FEATURE_WPA3_SUITE_B));
         assertTrue(mWifiManager.isWpa3SuiteBSupported());
         when(mWifiService.getSupportedFeatures())
-                .thenReturn(new Long(~WifiManager.WIFI_FEATURE_WPA3_SUITE_B));
+                .thenReturn(new Long(~WIFI_FEATURE_WPA3_SUITE_B));
         assertFalse(mWifiManager.isWpa3SuiteBSupported());
     }
 
@@ -1554,10 +1574,10 @@
     @Test
     public void testIsEasyConnectSupported() throws Exception {
         when(mWifiService.getSupportedFeatures())
-                .thenReturn(new Long(WifiManager.WIFI_FEATURE_DPP));
+                .thenReturn(new Long(WIFI_FEATURE_DPP));
         assertTrue(mWifiManager.isEasyConnectSupported());
         when(mWifiService.getSupportedFeatures())
-                .thenReturn(new Long(~WifiManager.WIFI_FEATURE_DPP));
+                .thenReturn(new Long(~WIFI_FEATURE_DPP));
         assertFalse(mWifiManager.isEasyConnectSupported());
     }
 
@@ -1666,9 +1686,9 @@
     @Test
     public void testGetSupportedFeatures() throws Exception {
         long supportedFeatures =
-                WifiManager.WIFI_FEATURE_SCANNER
-                        | WifiManager.WIFI_FEATURE_PASSPOINT
-                        | WifiManager.WIFI_FEATURE_P2P;
+                WIFI_FEATURE_SCANNER
+                        | WIFI_FEATURE_PASSPOINT
+                        | WIFI_FEATURE_P2P;
         when(mWifiService.getSupportedFeatures())
                 .thenReturn(Long.valueOf(supportedFeatures));
 
@@ -1700,6 +1720,58 @@
     }
 
     /**
+     * Tests that passing a null Executor to {@link WifiManager#getWifiActivityEnergyInfoAsync}
+     * throws an exception.
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testGetWifiActivityInfoNullExecutor() throws Exception {
+        mWifiManager.getWifiActivityEnergyInfoAsync(null, mOnWifiActivityEnergyInfoListener);
+    }
+
+    /**
+     * Tests that passing a null listener to {@link WifiManager#getWifiActivityEnergyInfoAsync}
+     * throws an exception.
+     */
+    @Test(expected = IllegalArgumentException.class)
+    public void testGetWifiActivityInfoNullListener() throws Exception {
+        mWifiManager.getWifiActivityEnergyInfoAsync(mExecutor, null);
+    }
+
+    /** Tests that the listener runs on the correct Executor. */
+    @Test
+    public void testGetWifiActivityInfoRunsOnCorrectExecutor() throws Exception {
+        mWifiManager.getWifiActivityEnergyInfoAsync(mExecutor, mOnWifiActivityEnergyInfoListener);
+        ArgumentCaptor<IOnWifiActivityEnergyInfoListener> listenerCaptor =
+                ArgumentCaptor.forClass(IOnWifiActivityEnergyInfoListener.class);
+        verify(mWifiService).getWifiActivityEnergyInfoAsync(listenerCaptor.capture());
+        IOnWifiActivityEnergyInfoListener listener = listenerCaptor.getValue();
+        listener.onWifiActivityEnergyInfo(mWifiActivityEnergyInfo);
+        verify(mExecutor).execute(any());
+
+        // ensure that the executor is only triggered once
+        listener.onWifiActivityEnergyInfo(mWifiActivityEnergyInfo);
+        verify(mExecutor).execute(any());
+    }
+
+    /** Tests that the correct listener runs. */
+    @Test
+    public void testGetWifiActivityInfoRunsCorrectListener() throws Exception {
+        int[] flag = {0};
+        mWifiManager.getWifiActivityEnergyInfoAsync(
+                new SynchronousExecutor(), info -> flag[0]++);
+        ArgumentCaptor<IOnWifiActivityEnergyInfoListener> listenerCaptor =
+                ArgumentCaptor.forClass(IOnWifiActivityEnergyInfoListener.class);
+        verify(mWifiService).getWifiActivityEnergyInfoAsync(listenerCaptor.capture());
+        IOnWifiActivityEnergyInfoListener listener = listenerCaptor.getValue();
+        listener.onWifiActivityEnergyInfo(mWifiActivityEnergyInfo);
+        assertEquals(1, flag[0]);
+
+        // ensure that the listener is only triggered once
+        listener.onWifiActivityEnergyInfo(mWifiActivityEnergyInfo);
+        assertEquals(1, flag[0]);
+    }
+
+    /**
      * Test behavior of {@link WifiManager#getConnectionInfo()}
      */
     @Test
@@ -1756,11 +1828,11 @@
     }
 
     /**
-     * Test behavior of {@link WifiManager#connect(int, WifiManager.ActionListener)}
+     * Test behavior of {@link WifiManager#connect(int, ActionListener)}
      */
     @Test
     public void testConnectWithListener() throws Exception {
-        WifiManager.ActionListener externalListener = mock(WifiManager.ActionListener.class);
+        ActionListener externalListener = mock(ActionListener.class);
         mWifiManager.connect(TEST_NETWORK_ID, externalListener);
 
         ArgumentCaptor<IActionListener> binderListenerCaptor =
@@ -1775,43 +1847,43 @@
         verify(externalListener).onSuccess();
 
         // Trigger on failure.
-        binderListenerCaptor.getValue().onFailure(WifiManager.BUSY);
+        binderListenerCaptor.getValue().onFailure(BUSY);
         mLooper.dispatchAll();
-        verify(externalListener).onFailure(WifiManager.BUSY);
+        verify(externalListener).onFailure(BUSY);
     }
 
     /**
-     * Test behavior of {@link WifiManager#connect(int, WifiManager.ActionListener)}
+     * Test behavior of {@link WifiManager#connect(int, ActionListener)}
      */
     @Test
     public void testConnectWithListenerHandleSecurityException() throws Exception {
         doThrow(new SecurityException()).when(mWifiService)
                 .connect(eq(null), anyInt(), any(IBinder.class),
                         any(IActionListener.class), anyInt());
-        WifiManager.ActionListener externalListener = mock(WifiManager.ActionListener.class);
+        ActionListener externalListener = mock(ActionListener.class);
         mWifiManager.connect(TEST_NETWORK_ID, externalListener);
 
         mLooper.dispatchAll();
-        verify(externalListener).onFailure(WifiManager.NOT_AUTHORIZED);
+        verify(externalListener).onFailure(NOT_AUTHORIZED);
     }
 
     /**
-     * Test behavior of {@link WifiManager#connect(int, WifiManager.ActionListener)}
+     * Test behavior of {@link WifiManager#connect(int, ActionListener)}
      */
     @Test
     public void testConnectWithListenerHandleRemoteException() throws Exception {
         doThrow(new RemoteException()).when(mWifiService)
                 .connect(eq(null), anyInt(), any(IBinder.class),
                         any(IActionListener.class), anyInt());
-        WifiManager.ActionListener externalListener = mock(WifiManager.ActionListener.class);
+        ActionListener externalListener = mock(ActionListener.class);
         mWifiManager.connect(TEST_NETWORK_ID, externalListener);
 
         mLooper.dispatchAll();
-        verify(externalListener).onFailure(WifiManager.ERROR);
+        verify(externalListener).onFailure(ERROR);
     }
 
     /**
-     * Test behavior of {@link WifiManager#connect(int, WifiManager.ActionListener)}
+     * Test behavior of {@link WifiManager#connect(int, ActionListener)}
      */
     @Test
     public void testConnectWithoutListener() throws Exception {
@@ -1823,12 +1895,12 @@
     }
 
     /**
-     * Test behavior of {@link WifiManager#getTxPacketCount(WifiManager.TxPacketCountListener)}
+     * Test behavior of {@link WifiManager#getTxPacketCount(TxPacketCountListener)}
      */
     @Test
     public void testGetTxPacketCount() throws Exception {
-        WifiManager.TxPacketCountListener externalListener =
-                mock(WifiManager.TxPacketCountListener.class);
+        TxPacketCountListener externalListener =
+                mock(TxPacketCountListener.class);
         mWifiManager.getTxPacketCount(externalListener);
 
         ArgumentCaptor<ITxPacketCountListener> binderListenerCaptor =
@@ -1843,9 +1915,9 @@
         verify(externalListener).onSuccess(6);
 
         // Trigger on failure.
-        binderListenerCaptor.getValue().onFailure(WifiManager.BUSY);
+        binderListenerCaptor.getValue().onFailure(BUSY);
         mLooper.dispatchAll();
-        verify(externalListener).onFailure(WifiManager.BUSY);
+        verify(externalListener).onFailure(BUSY);
     }
 
     /**
@@ -1947,7 +2019,7 @@
      */
     @Test
     public void testAddSuggestionConnectionStatusListenerAndReceiveEvent() throws Exception {
-        int errorCode = WifiManager.STATUS_SUGGESTION_CONNECTION_FAILURE_AUTHENTICATION;
+        int errorCode = STATUS_SUGGESTION_CONNECTION_FAILURE_AUTHENTICATION;
         ArgumentCaptor<ISuggestionConnectionStatusListener.Stub> callbackCaptor =
                 ArgumentCaptor.forClass(ISuggestionConnectionStatusListener.Stub.class);
         Executor executor = new SynchronousExecutor();
@@ -1963,7 +2035,7 @@
      */
     @Test
     public void testAddSuggestionConnectionStatusListenerWithTheTargetExecutor() throws Exception {
-        int errorCode = WifiManager.STATUS_SUGGESTION_CONNECTION_FAILURE_AUTHENTICATION;
+        int errorCode = STATUS_SUGGESTION_CONNECTION_FAILURE_AUTHENTICATION;
         ArgumentCaptor<ISuggestionConnectionStatusListener.Stub> callbackCaptor =
                 ArgumentCaptor.forClass(ISuggestionConnectionStatusListener.Stub.class);
         mWifiManager.addSuggestionConnectionStatusListener(mExecutor, mListener);
diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkAgentSpecifierTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkAgentSpecifierTest.java
index e6eece8..adc41f0 100644
--- a/wifi/tests/src/android/net/wifi/WifiNetworkAgentSpecifierTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiNetworkAgentSpecifierTest.java
@@ -208,7 +208,7 @@
         PatternMatcher ssidPattern =
                 new PatternMatcher(TEST_SSID_PATTERN, PatternMatcher.PATTERN_PREFIX);
         Pair<MacAddress, MacAddress> bssidPattern =
-                Pair.create(MacAddress.ALL_ZEROS_ADDRESS, MacAddress.ALL_ZEROS_ADDRESS);
+                Pair.create(WifiManager.ALL_ZEROS_MAC_ADDRESS, WifiManager.ALL_ZEROS_MAC_ADDRESS);
         WifiConfiguration wificonfigurationNetworkSpecifier = new WifiConfiguration();
         wificonfigurationNetworkSpecifier.allowedKeyManagement
                 .set(WifiConfiguration.KeyMgmt.WPA_PSK);
@@ -299,7 +299,7 @@
         PatternMatcher ssidPattern =
                 new PatternMatcher(TEST_SSID_PATTERN, PatternMatcher.PATTERN_PREFIX);
         Pair<MacAddress, MacAddress> bssidPattern =
-                Pair.create(MacAddress.ALL_ZEROS_ADDRESS, MacAddress.ALL_ZEROS_ADDRESS);
+                Pair.create(WifiManager.ALL_ZEROS_MAC_ADDRESS, WifiManager.ALL_ZEROS_MAC_ADDRESS);
         WifiConfiguration wificonfigurationNetworkSpecifier = new WifiConfiguration();
         wificonfigurationNetworkSpecifier.allowedKeyManagement
                 .set(WifiConfiguration.KeyMgmt.WPA_PSK);
diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkSpecifierTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkSpecifierTest.java
index edb43d8..1619744 100644
--- a/wifi/tests/src/android/net/wifi/WifiNetworkSpecifierTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiNetworkSpecifierTest.java
@@ -65,8 +65,10 @@
         assertEquals(Process.myUid(), wifiNetworkSpecifier.requestorUid);
         assertEquals(TEST_SSID, wifiNetworkSpecifier.ssidPatternMatcher.getPath());
         assertEquals(PATTERN_PREFIX, wifiNetworkSpecifier.ssidPatternMatcher.getType());
-        assertEquals(MacAddress.ALL_ZEROS_ADDRESS, wifiNetworkSpecifier.bssidPatternMatcher.first);
-        assertEquals(MacAddress.ALL_ZEROS_ADDRESS, wifiNetworkSpecifier.bssidPatternMatcher.second);
+        assertEquals(WifiManager.ALL_ZEROS_MAC_ADDRESS,
+                wifiNetworkSpecifier.bssidPatternMatcher.first);
+        assertEquals(WifiManager.ALL_ZEROS_MAC_ADDRESS,
+                wifiNetworkSpecifier.bssidPatternMatcher.second);
         assertTrue(wifiNetworkSpecifier.wifiConfiguration.allowedKeyManagement
                 .get(WifiConfiguration.KeyMgmt.NONE));
     }
@@ -210,7 +212,8 @@
     @Test(expected = IllegalStateException.class)
     public void testWifiNetworkSpecifierBuilderWithMatchAllBssidPattern() {
         new WifiNetworkSpecifier.Builder()
-                .setBssidPattern(MacAddress.ALL_ZEROS_ADDRESS, MacAddress.ALL_ZEROS_ADDRESS)
+                .setBssidPattern(WifiManager.ALL_ZEROS_MAC_ADDRESS,
+                        WifiManager.ALL_ZEROS_MAC_ADDRESS)
                 .build();
     }
 
@@ -265,7 +268,7 @@
     @Test(expected = IllegalStateException.class)
     public void testWifiNetworkSpecifierBuilderWithMatchNoneBssidPattern3() {
         new WifiNetworkSpecifier.Builder()
-                .setBssid(MacAddress.ALL_ZEROS_ADDRESS)
+                .setBssid(WifiManager.ALL_ZEROS_MAC_ADDRESS)
                 .build();
     }
 
@@ -513,7 +516,8 @@
 
         WifiNetworkSpecifier specifier2 =
                 new WifiNetworkSpecifier(new PatternMatcher(TEST_SSID, PATTERN_LITERAL),
-                        Pair.create(MacAddress.ALL_ZEROS_ADDRESS, MacAddress.ALL_ZEROS_ADDRESS),
+                        Pair.create(WifiManager.ALL_ZEROS_MAC_ADDRESS,
+                                WifiManager.ALL_ZEROS_MAC_ADDRESS),
                         wifiConfiguration,
                         TEST_UID, TEST_PACKAGE_NAME);
 
diff --git a/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java b/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
index ce085f5..8a5a0fd 100644
--- a/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiNetworkSuggestionTest.java
@@ -294,7 +294,7 @@
     public void testWifiNetworkSuggestionBuilderWithInvalidAllZeroBssid() {
         new WifiNetworkSuggestion.Builder()
                 .setSsid(TEST_SSID)
-                .setBssid(MacAddress.ALL_ZEROS_ADDRESS)
+                .setBssid(WifiManager.ALL_ZEROS_MAC_ADDRESS)
                 .build();
     }
 
diff --git a/wifi/tests/src/android/net/wifi/WifiScannerTest.java b/wifi/tests/src/android/net/wifi/WifiScannerTest.java
index b1436c90..fa4f711 100644
--- a/wifi/tests/src/android/net/wifi/WifiScannerTest.java
+++ b/wifi/tests/src/android/net/wifi/WifiScannerTest.java
@@ -79,15 +79,17 @@
     private static final boolean TEST_PNOSETTINGS_IS_CONNECTED = false;
     private static final int TEST_PNOSETTINGS_MIN_5GHZ_RSSI = -60;
     private static final int TEST_PNOSETTINGS_MIN_2GHZ_RSSI = -70;
+    private static final int TEST_PNOSETTINGS_MIN_6GHZ_RSSI = -55;
     private static final int TEST_PNOSETTINGS_INITIAL_SCORE_MAX = 50;
     private static final int TEST_PNOSETTINGS_CURRENT_CONNECTION_BONUS = 10;
     private static final int TEST_PNOSETTINGS_SAME_NETWORK_BONUS = 11;
     private static final int TEST_PNOSETTINGS_SECURE_BONUS = 12;
     private static final int TEST_PNOSETTINGS_BAND_5GHZ_BONUS = 13;
+    private static final int TEST_PNOSETTINGS_BAND_6GHZ_BONUS = 15;
     private static final String TEST_SSID_1 = "TEST1";
     private static final String TEST_SSID_2 = "TEST2";
     private static final int[] TEST_FREQUENCIES_1 = {};
-    private static final int[] TEST_FREQUENCIES_2 = {2500, 5124};
+    private static final int[] TEST_FREQUENCIES_2 = {2500, 5124, 6245};
     private static final String DESCRIPTION_NOT_AUTHORIZED = "Not authorized";
 
     private WifiScanner mWifiScanner;
@@ -183,11 +185,13 @@
         pnoSettings.isConnected = TEST_PNOSETTINGS_IS_CONNECTED;
         pnoSettings.min5GHzRssi = TEST_PNOSETTINGS_MIN_5GHZ_RSSI;
         pnoSettings.min24GHzRssi = TEST_PNOSETTINGS_MIN_2GHZ_RSSI;
+        pnoSettings.min6GHzRssi = TEST_PNOSETTINGS_MIN_6GHZ_RSSI;
         pnoSettings.initialScoreMax = TEST_PNOSETTINGS_INITIAL_SCORE_MAX;
         pnoSettings.currentConnectionBonus = TEST_PNOSETTINGS_CURRENT_CONNECTION_BONUS;
         pnoSettings.sameNetworkBonus = TEST_PNOSETTINGS_SAME_NETWORK_BONUS;
         pnoSettings.secureBonus = TEST_PNOSETTINGS_SECURE_BONUS;
         pnoSettings.band5GHzBonus = TEST_PNOSETTINGS_BAND_5GHZ_BONUS;
+        pnoSettings.band6GHzBonus = TEST_PNOSETTINGS_BAND_6GHZ_BONUS;
 
         Parcel parcel = Parcel.obtain();
         pnoSettings.writeToParcel(parcel, 0);
@@ -200,6 +204,7 @@
         assertEquals(TEST_PNOSETTINGS_IS_CONNECTED, pnoSettingsDeserialized.isConnected);
         assertEquals(TEST_PNOSETTINGS_MIN_5GHZ_RSSI, pnoSettingsDeserialized.min5GHzRssi);
         assertEquals(TEST_PNOSETTINGS_MIN_2GHZ_RSSI, pnoSettingsDeserialized.min24GHzRssi);
+        assertEquals(TEST_PNOSETTINGS_MIN_6GHZ_RSSI, pnoSettingsDeserialized.min6GHzRssi);
         assertEquals(TEST_PNOSETTINGS_INITIAL_SCORE_MAX, pnoSettingsDeserialized.initialScoreMax);
         assertEquals(TEST_PNOSETTINGS_CURRENT_CONNECTION_BONUS,
                 pnoSettingsDeserialized.currentConnectionBonus);
@@ -207,6 +212,7 @@
                 pnoSettingsDeserialized.sameNetworkBonus);
         assertEquals(TEST_PNOSETTINGS_SECURE_BONUS, pnoSettingsDeserialized.secureBonus);
         assertEquals(TEST_PNOSETTINGS_BAND_5GHZ_BONUS, pnoSettingsDeserialized.band5GHzBonus);
+        assertEquals(TEST_PNOSETTINGS_BAND_6GHZ_BONUS, pnoSettingsDeserialized.band6GHzBonus);
 
         // Test parsing of PnoNetwork
         assertEquals(pnoSettings.networkList.length, pnoSettingsDeserialized.networkList.length);