Merge changes I17f77bd9,Iff991ac6

* changes:
  Migrate SubsystemSleepStatePuller to jni
  Move SubsystemSleepStatePuller
diff --git a/Android.bp b/Android.bp
index ffb4d3a..2318f7b 100644
--- a/Android.bp
+++ b/Android.bp
@@ -734,6 +734,7 @@
         "core/java/android/annotation/RequiresPermission.java",
         "core/java/android/annotation/SystemApi.java",
         "core/java/android/annotation/TestApi.java",
+        "core/java/com/android/internal/annotations/GuardedBy.java",
     ],
 }
 // Build ext.jar
diff --git a/apex/blobstore/framework/java/android/app/blob/BlobHandle.java b/apex/blobstore/framework/java/android/app/blob/BlobHandle.java
index f7e6a98..ee0ee98 100644
--- a/apex/blobstore/framework/java/android/app/blob/BlobHandle.java
+++ b/apex/blobstore/framework/java/android/app/blob/BlobHandle.java
@@ -45,6 +45,10 @@
 public final class BlobHandle implements Parcelable {
     private static final String ALGO_SHA_256 = "SHA-256";
 
+    private static final String[] SUPPORTED_ALGOS = {
+            ALGO_SHA_256
+    };
+
     private static final int LIMIT_BLOB_TAG_LENGTH = 128; // characters
 
     /**
@@ -104,14 +108,9 @@
     public static @NonNull BlobHandle create(@NonNull String algorithm, @NonNull byte[] digest,
             @NonNull CharSequence label, @CurrentTimeMillisLong long expiryTimeMillis,
             @NonNull String tag) {
-        Preconditions.checkNotNull(algorithm, "algorithm must not be null");
-        Preconditions.checkNotNull(digest, "digest must not be null");
-        Preconditions.checkNotNull(label, "label must not be null");
-        Preconditions.checkArgumentNonnegative(expiryTimeMillis,
-                "expiryTimeMillis must not be negative");
-        Preconditions.checkNotNull(tag, "tag must not be null");
-        Preconditions.checkArgument(tag.length() <= LIMIT_BLOB_TAG_LENGTH, "tag too long");
-        return new BlobHandle(algorithm, digest, label, expiryTimeMillis, tag);
+        final BlobHandle handle = new BlobHandle(algorithm, digest, label, expiryTimeMillis, tag);
+        handle.assertIsValid();
+        return handle;
     }
 
     /**
@@ -223,6 +222,17 @@
         fout.println("tag: " + tag);
     }
 
+    /** @hide */
+    public void assertIsValid() {
+        Preconditions.checkArgumentIsSupported(SUPPORTED_ALGOS, algorithm);
+        Preconditions.checkByteArrayNotEmpty(digest, "digest");
+        Preconditions.checkStringNotEmpty(label, "label must not be null");
+        Preconditions.checkArgumentNonnegative(expiryTimeMillis,
+                "expiryTimeMillis must not be negative");
+        Preconditions.checkStringNotEmpty(tag, "tag must not be null");
+        Preconditions.checkArgument(tag.length() <= LIMIT_BLOB_TAG_LENGTH, "tag too long");
+    }
+
     public static final @NonNull Creator<BlobHandle> CREATOR = new Creator<BlobHandle>() {
         @Override
         public @NonNull BlobHandle createFromParcel(@NonNull Parcel source) {
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
index fcc30e30..dfe4410 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
@@ -49,6 +49,7 @@
 import android.content.IntentFilter;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManagerInternal;
+import android.content.res.ResourceId;
 import android.os.Binder;
 import android.os.Handler;
 import android.os.HandlerThread;
@@ -91,6 +92,7 @@
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Objects;
 
 /**
  * Service responsible for maintaining and facilitating access to data blobs published by apps.
@@ -658,10 +660,9 @@
         @IntRange(from = 1)
         public long createSession(@NonNull BlobHandle blobHandle,
                 @NonNull String packageName) {
-            Preconditions.checkNotNull(blobHandle, "blobHandle must not be null");
-            Preconditions.checkNotNull(packageName, "packageName must not be null");
-            // TODO: verify blobHandle.algorithm is sha-256
-            // TODO: assert blobHandle is valid.
+            Objects.requireNonNull(blobHandle, "blobHandle must not be null");
+            blobHandle.assertIsValid();
+            Objects.requireNonNull(packageName, "packageName must not be null");
 
             final int callingUid = Binder.getCallingUid();
             verifyCallingPackage(callingUid, packageName);
@@ -682,7 +683,7 @@
                 @NonNull String packageName) {
             Preconditions.checkArgumentPositive(sessionId,
                     "sessionId must be positive: " + sessionId);
-            Preconditions.checkNotNull(packageName, "packageName must not be null");
+            Objects.requireNonNull(packageName, "packageName must not be null");
 
             final int callingUid = Binder.getCallingUid();
             verifyCallingPackage(callingUid, packageName);
@@ -695,7 +696,7 @@
                 @NonNull String packageName) {
             Preconditions.checkArgumentPositive(sessionId,
                     "sessionId must be positive: " + sessionId);
-            Preconditions.checkNotNull(packageName, "packageName must not be null");
+            Objects.requireNonNull(packageName, "packageName must not be null");
 
             final int callingUid = Binder.getCallingUid();
             verifyCallingPackage(callingUid, packageName);
@@ -706,8 +707,9 @@
         @Override
         public ParcelFileDescriptor openBlob(@NonNull BlobHandle blobHandle,
                 @NonNull String packageName) {
-            Preconditions.checkNotNull(blobHandle, "blobHandle must not be null");
-            Preconditions.checkNotNull(packageName, "packageName must not be null");
+            Objects.requireNonNull(blobHandle, "blobHandle must not be null");
+            blobHandle.assertIsValid();
+            Objects.requireNonNull(packageName, "packageName must not be null");
 
             final int callingUid = Binder.getCallingUid();
             verifyCallingPackage(callingUid, packageName);
@@ -727,24 +729,27 @@
 
         @Override
         public void acquireLease(@NonNull BlobHandle blobHandle, @IdRes int descriptionResId,
-                @CurrentTimeSecondsLong long leaseTimeoutSecs, @NonNull String packageName) {
-            Preconditions.checkNotNull(blobHandle, "blobHandle must not be null");
-            Preconditions.checkNotNull(packageName, "packageName must not be null");
-            Preconditions.checkArgumentPositive(descriptionResId,
-                    "descriptionResId must be positive; value=" + descriptionResId);
+                @CurrentTimeSecondsLong long leaseExpiryTimeMillis, @NonNull String packageName) {
+            Objects.requireNonNull(blobHandle, "blobHandle must not be null");
+            blobHandle.assertIsValid();
+            Preconditions.checkArgument(ResourceId.isValid(descriptionResId),
+                    "descriptionResId is not valid");
+            Preconditions.checkArgumentNonnegative(leaseExpiryTimeMillis,
+                    "leaseExpiryTimeMillis must not be negative");
+            Objects.requireNonNull(packageName, "packageName must not be null");
 
             final int callingUid = Binder.getCallingUid();
             verifyCallingPackage(callingUid, packageName);
 
-            acquireLeaseInternal(blobHandle, descriptionResId, leaseTimeoutSecs,
+            acquireLeaseInternal(blobHandle, descriptionResId, leaseExpiryTimeMillis,
                     callingUid, packageName);
         }
 
         @Override
         public void releaseLease(@NonNull BlobHandle blobHandle, @NonNull String packageName) {
-            Preconditions.checkNotNull(blobHandle, "blobHandle must not be null");
-            Preconditions.checkNotNull(packageName, "packageName must not be null");
-
+            Objects.requireNonNull(blobHandle, "blobHandle must not be null");
+            blobHandle.assertIsValid();
+            Objects.requireNonNull(packageName, "packageName must not be null");
 
             final int callingUid = Binder.getCallingUid();
             verifyCallingPackage(callingUid, packageName);
@@ -754,7 +759,7 @@
 
         @Override
         public void waitForIdle(@NonNull RemoteCallback remoteCallback) {
-            Preconditions.checkNotNull(remoteCallback, "remoteCallback must not be null");
+            Objects.requireNonNull(remoteCallback, "remoteCallback must not be null");
 
             mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP,
                     "Caller is not allowed to call this; caller=" + Binder.getCallingUid());
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java
index 7d1c166..40f9f58 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java
@@ -62,6 +62,7 @@
 import java.security.NoSuchAlgorithmException;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Objects;
 
 /** TODO: add doc */
 public class BlobStoreSession extends IBlobStoreSession.Stub {
@@ -155,6 +156,8 @@
     @NonNull
     public ParcelFileDescriptor openWrite(@BytesLong long offsetBytes,
             @BytesLong long lengthBytes) {
+        Preconditions.checkArgumentNonnegative(offsetBytes, "offsetBytes must not be negative");
+
         assertCallerIsOwner();
         synchronized (mSessionLock) {
             if (mState != STATE_OPENED) {
@@ -242,7 +245,7 @@
     public void allowPackageAccess(@NonNull String packageName,
             @NonNull byte[] certificate) {
         assertCallerIsOwner();
-        Preconditions.checkNotNull(packageName, "packageName must not be null");
+        Objects.requireNonNull(packageName, "packageName must not be null");
         synchronized (mSessionLock) {
             if (mState != STATE_OPENED) {
                 throw new IllegalStateException("Not allowed to change access type in state: "
@@ -280,7 +283,9 @@
     public boolean isPackageAccessAllowed(@NonNull String packageName,
             @NonNull byte[] certificate) {
         assertCallerIsOwner();
-        Preconditions.checkNotNull(packageName, "packageName must not be null");
+        Objects.requireNonNull(packageName, "packageName must not be null");
+        Preconditions.checkByteArrayNotEmpty(certificate, "certificate");
+
         synchronized (mSessionLock) {
             if (mState != STATE_OPENED) {
                 throw new IllegalStateException("Not allowed to get access type in state: "
diff --git a/apex/statsd/Android.bp b/apex/statsd/Android.bp
index 09ca1d2..1f9f18c 100644
--- a/apex/statsd/Android.bp
+++ b/apex/statsd/Android.bp
@@ -19,8 +19,9 @@
 }
 
 apex_defaults {
-    // libc.so and libcutils.so are included in the apex
-    // native_shared_libs: ["libc", "libcutils"],
+    native_shared_libs: [
+        "libstats_jni",
+    ],
     // binaries: ["vold"],
     java_libs: [
         "framework-statsd",
@@ -44,3 +45,33 @@
     // com.android.os.statsd.pk8 (the private key)
     certificate: "com.android.os.statsd",
 }
+
+
+// JNI library for StatsLog.write
+cc_library_shared {
+    name: "libstats_jni",
+    srcs: ["jni/**/*.cpp"],
+    shared_libs: [
+        "libnativehelper", // Has stable abi - should not be copied into apex.
+        "liblog",  // Has a stable abi - should not be copied into apex.
+    ],
+    static_libs: [
+        //TODO: make shared - need libstatssocket to also live in the apex.
+        "libstatssocket",
+        "libcutils", // TODO: remove - needed by libstatssocket
+    ],
+    //TODO: is libc++_static correct?
+    stl: "libc++_static",
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wextra",
+        "-Wno-unused-parameter",
+    ],
+    apex_available: [
+        "com.android.os.statsd",
+        "test_com.android.os.statsd",
+        //TODO (b/148620413): remove platform.
+         "//apex_available:platform",
+    ],
+}
\ No newline at end of file
diff --git a/apex/statsd/aidl/android/os/IStatsCompanionService.aidl b/apex/statsd/aidl/android/os/IStatsCompanionService.aidl
index bdd1da7..b94928f 100644
--- a/apex/statsd/aidl/android/os/IStatsCompanionService.aidl
+++ b/apex/statsd/aidl/android/os/IStatsCompanionService.aidl
@@ -61,4 +61,11 @@
 
     /** Tells StatsCompaionService to grab the uid map snapshot and send it to statsd. */
     oneway void triggerUidSnapshot();
+
+    /**
+     * Ask StatsCompanionService if the given permission is allowed for a particular process
+     * and user ID. statsd is incapable of doing this check itself because checkCallingPermission
+     * is not currently supported by libbinder_ndk.
+     */
+    boolean checkPermission(String permission, int pid, int uid);
 }
diff --git a/core/jni/android_util_StatsLog.cpp b/apex/statsd/jni/android_util_StatsLog.cpp
similarity index 71%
rename from core/jni/android_util_StatsLog.cpp
rename to apex/statsd/jni/android_util_StatsLog.cpp
index 9225fc2..9d410eb 100644
--- a/core/jni/android_util_StatsLog.cpp
+++ b/apex/statsd/jni/android_util_StatsLog.cpp
@@ -17,12 +17,9 @@
 #define LOG_NAMESPACE "StatsLog.tag."
 #define LOG_TAG "StatsLog_println"
 
-#include <assert.h>
-
 #include "jni.h"
+#include <log/log.h>
 #include <nativehelper/JNIHelp.h>
-#include "utils/misc.h"
-#include "core_jni_helpers.h"
 #include "stats_buffer_writer.h"
 
 namespace android {
@@ -57,7 +54,27 @@
 
 int register_android_util_StatsLog(JNIEnv* env)
 {
-    return RegisterMethodsOrDie(env, "android/util/StatsLog", gMethods, NELEM(gMethods));
+    return jniRegisterNativeMethods(env, "android/util/StatsLog", gMethods, NELEM(gMethods));
 }
-
 }; // namespace android
+
+/*
+ * JNI Initialization
+ */
+jint JNI_OnLoad(JavaVM* jvm, void* reserved) {
+    JNIEnv* e;
+    int status;
+
+    ALOGV("statsd : loading JNI\n");
+    // Check JNI version
+    if (jvm->GetEnv((void**)&e, JNI_VERSION_1_4)) {
+        ALOGE("JNI version mismatch error");
+        return JNI_ERR;
+    }
+    status = android::register_android_util_StatsLog(e);
+    if (status < 0) {
+        ALOGE("jni statsd registration failure, status: %d", status);
+        return JNI_ERR;
+    }
+    return JNI_VERSION_1_4;
+}
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 1e92826..3e9a488 100644
--- a/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
+++ b/apex/statsd/service/java/com/android/server/stats/StatsCompanionService.java
@@ -516,6 +516,11 @@
         }
     }
 
+    @Override // Binder call
+    public boolean checkPermission(String permission, int pid, int uid) {
+        StatsCompanion.enforceStatsCompanionPermission(mContext);
+        return mContext.checkPermission(permission, pid, uid) == PackageManager.PERMISSION_GRANTED;
+    }
 
     // Statsd related code
 
diff --git a/api/current.txt b/api/current.txt
index 1306bfa..a50a4c2 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -31129,11 +31129,6 @@
     field @Deprecated public static final String[] strings;
   }
 
-  @Deprecated public static class WifiConfiguration.SuiteBCipher {
-    field @Deprecated public static final int ECDHE_ECDSA = 0; // 0x0
-    field @Deprecated public static final int ECDHE_RSA = 1; // 0x1
-  }
-
   public class WifiEnterpriseConfig implements android.os.Parcelable {
     ctor public WifiEnterpriseConfig();
     ctor public WifiEnterpriseConfig(android.net.wifi.WifiEnterpriseConfig);
@@ -31616,6 +31611,7 @@
     method public android.net.wifi.hotspot2.pps.Credential getCredential();
     method public android.net.wifi.hotspot2.pps.HomeSp getHomeSp();
     method public long getSubscriptionExpirationTimeInMillis();
+    method @NonNull public String getUniqueId() throws java.lang.IllegalStateException;
     method public boolean isOsuProvisioned();
     method public void setCredential(android.net.wifi.hotspot2.pps.Credential);
     method public void setHomeSp(android.net.wifi.hotspot2.pps.HomeSp);
@@ -31796,8 +31792,8 @@
     method public boolean isGroupOwner();
     method public void writeToParcel(android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.net.wifi.p2p.WifiP2pGroup> CREATOR;
-    field public static final int PERSISTENT_NET_ID = -2; // 0xfffffffe
-    field public static final int TEMPORARY_NET_ID = -1; // 0xffffffff
+    field public static final int NETWORK_ID_PERSISTENT = -2; // 0xfffffffe
+    field public static final int NETWORK_ID_TEMPORARY = -1; // 0xffffffff
   }
 
   public class WifiP2pInfo implements android.os.Parcelable {
@@ -36944,7 +36940,7 @@
     ctor public VibrationAttributes.Builder();
     ctor public VibrationAttributes.Builder(@Nullable android.os.VibrationAttributes);
     method @NonNull public android.os.VibrationAttributes build();
-    method @NonNull public android.os.VibrationAttributes.Builder replaceFlags(int);
+    method @NonNull public android.os.VibrationAttributes.Builder setFlags(int, int);
     method @NonNull public android.os.VibrationAttributes.Builder setUsage(int);
   }
 
@@ -46381,7 +46377,6 @@
   public final class BarringInfo implements android.os.Parcelable {
     method public int describeContents();
     method @NonNull public android.telephony.BarringInfo.BarringServiceInfo getBarringServiceInfo(int);
-    method public boolean isServiceBarred(int);
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field public static final int BARRING_SERVICE_TYPE_CS_FALLBACK = 5; // 0x5
     field public static final int BARRING_SERVICE_TYPE_CS_SERVICE = 0; // 0x0
diff --git a/api/system-current.txt b/api/system-current.txt
index 0930f68..83889d4 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1604,7 +1604,7 @@
   }
 
   public final class BluetoothHidDevice implements android.bluetooth.BluetoothProfile {
-    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int);
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int);
   }
 
   public final class BluetoothHidHost implements android.bluetooth.BluetoothProfile {
@@ -6233,7 +6233,7 @@
   }
 
   public abstract class NetworkAgent {
-    ctor public NetworkAgent(@NonNull android.content.Context, @NonNull android.os.Looper, @NonNull String, @NonNull android.net.NetworkCapabilities, @NonNull android.net.LinkProperties, int, @NonNull android.net.NetworkAgentConfig, @Nullable android.net.NetworkProvider);
+    ctor public NetworkAgent(@NonNull android.content.Context, @NonNull android.os.Looper, @NonNull String, @NonNull android.net.NetworkCapabilities, @NonNull android.net.LinkProperties, @NonNull android.net.NetworkScore, @NonNull android.net.NetworkAgentConfig, @Nullable android.net.NetworkProvider);
     method @Nullable public android.net.Network getNetwork();
     method public void onAddKeepalivePacketFilter(int, @NonNull android.net.KeepalivePacketData);
     method public void onAutomaticReconnectDisabled();
@@ -6248,7 +6248,7 @@
     method @NonNull public android.net.Network register();
     method public void sendLinkProperties(@NonNull android.net.LinkProperties);
     method public void sendNetworkCapabilities(@NonNull android.net.NetworkCapabilities);
-    method public void sendNetworkScore(int);
+    method public void sendNetworkScore(@NonNull android.net.NetworkScore);
     method public void sendSocketKeepaliveEvent(int, int);
     method public void setConnected();
     method @Deprecated public void setLegacyExtraInfo(@Nullable String);
@@ -6351,6 +6351,55 @@
     method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_SIGNAL_STRENGTH_WAKEUP) public android.net.NetworkRequest.Builder setSignalStrength(int);
   }
 
+  public final class NetworkScore implements android.os.Parcelable {
+    method public int describeContents();
+    method @NonNull public android.net.NetworkScore.Metrics getEndToEndMetrics();
+    method @NonNull public android.net.NetworkScore.Metrics getLinkLayerMetrics();
+    method public int getRange();
+    method @IntRange(from=android.net.NetworkScore.UNKNOWN_SIGNAL_STRENGTH, to=android.net.NetworkScore.MAX_SIGNAL_STRENGTH) public int getSignalStrength();
+    method public boolean hasPolicy(int);
+    method public boolean isExiting();
+    method @NonNull public android.net.NetworkScore withExiting(boolean);
+    method @NonNull public android.net.NetworkScore withSignalStrength(@IntRange(from=android.net.NetworkScore.UNKNOWN_SIGNAL_STRENGTH) int);
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkScore> CREATOR;
+    field public static final int MAX_SIGNAL_STRENGTH = 1000; // 0x3e8
+    field public static final int MIN_SIGNAL_STRENGTH = 0; // 0x0
+    field public static final int POLICY_DEFAULT_SUBSCRIPTION = 8; // 0x8
+    field public static final int POLICY_IGNORE_ON_WIFI = 4; // 0x4
+    field public static final int POLICY_LOCKDOWN_VPN = 1; // 0x1
+    field public static final int POLICY_VPN = 2; // 0x2
+    field public static final int RANGE_CLOSE = 1; // 0x1
+    field public static final int RANGE_LONG = 4; // 0x4
+    field public static final int RANGE_MEDIUM = 3; // 0x3
+    field public static final int RANGE_SHORT = 2; // 0x2
+    field public static final int RANGE_UNKNOWN = 0; // 0x0
+    field public static final int UNKNOWN_SIGNAL_STRENGTH = -1; // 0xffffffff
+  }
+
+  public static class NetworkScore.Builder {
+    ctor public NetworkScore.Builder();
+    method @NonNull public android.net.NetworkScore.Builder addPolicy(int);
+    method @NonNull public android.net.NetworkScore build();
+    method @NonNull public android.net.NetworkScore.Builder clearPolicy(int);
+    method @NonNull public android.net.NetworkScore.Builder setEndToEndMetrics(@NonNull android.net.NetworkScore.Metrics);
+    method @NonNull public android.net.NetworkScore.Builder setExiting(boolean);
+    method @NonNull public android.net.NetworkScore.Builder setLegacyScore(int);
+    method @NonNull public android.net.NetworkScore.Builder setLinkLayerMetrics(@NonNull android.net.NetworkScore.Metrics);
+    method @NonNull public android.net.NetworkScore.Builder setRange(int);
+    method @NonNull public android.net.NetworkScore.Builder setSignalStrength(@IntRange(from=android.net.NetworkScore.UNKNOWN_SIGNAL_STRENGTH, to=android.net.NetworkScore.MAX_SIGNAL_STRENGTH) int);
+  }
+
+  public static class NetworkScore.Metrics {
+    ctor public NetworkScore.Metrics(@IntRange(from=android.net.NetworkScore.Metrics.LATENCY_UNKNOWN) int, @IntRange(from=android.net.NetworkScore.Metrics.BANDWIDTH_UNKNOWN) int, @IntRange(from=android.net.NetworkScore.Metrics.BANDWIDTH_UNKNOWN) int);
+    field public static final int BANDWIDTH_UNKNOWN = -1; // 0xffffffff
+    field @NonNull public static final android.net.NetworkScore.Metrics EMPTY;
+    field public static final int LATENCY_UNKNOWN = -1; // 0xffffffff
+    field @IntRange(from=android.net.NetworkScore.Metrics.BANDWIDTH_UNKNOWN) public final int downlinkBandwidthKBps;
+    field @IntRange(from=android.net.NetworkScore.Metrics.LATENCY_UNKNOWN) public final int latencyMs;
+    field @IntRange(from=android.net.NetworkScore.Metrics.BANDWIDTH_UNKNOWN) public final int uplinkBandwidthKBps;
+  }
+
   public class NetworkScoreManager {
     method @RequiresPermission(anyOf={android.Manifest.permission.SCORE_NETWORKS, android.Manifest.permission.REQUEST_NETWORK_SCORES}) public boolean clearScores() throws java.lang.SecurityException;
     method @RequiresPermission(anyOf={android.Manifest.permission.SCORE_NETWORKS, android.Manifest.permission.REQUEST_NETWORK_SCORES}) public void disableScoring() throws java.lang.SecurityException;
@@ -7465,6 +7514,7 @@
     method @Deprecated @NonNull public android.net.wifi.WifiConfiguration.NetworkSelectionStatus getNetworkSelectionStatus();
     method @Deprecated @NonNull public String getPrintableSsid();
     method @Deprecated @NonNull public android.net.IpConfiguration.ProxySettings getProxySettings();
+    method @Deprecated public int getRecentFailureReason();
     method @Deprecated @Nullable public android.net.StaticIpConfiguration getStaticIpConfiguration();
     method @Deprecated public boolean hasNoInternetAccess();
     method @Deprecated public boolean isEphemeral();
@@ -7482,6 +7532,8 @@
     field @Deprecated public static final int METERED_OVERRIDE_NOT_METERED = 2; // 0x2
     field @Deprecated public static final int RANDOMIZATION_NONE = 0; // 0x0
     field @Deprecated public static final int RANDOMIZATION_PERSISTENT = 1; // 0x1
+    field @Deprecated public static final int RECENT_FAILURE_AP_UNABLE_TO_HANDLE_NEW_STA = 17; // 0x11
+    field @Deprecated public static final int RECENT_FAILURE_NONE = 0; // 0x0
     field @Deprecated public boolean allowAutojoin;
     field @Deprecated public int apBand;
     field @Deprecated public int carrierId;
@@ -7497,7 +7549,6 @@
     field @Deprecated public int numAssociation;
     field @Deprecated public int numScorerOverride;
     field @Deprecated public int numScorerOverrideAndSwitchedNetwork;
-    field @Deprecated @NonNull public final android.net.wifi.WifiConfiguration.RecentFailure recentFailure;
     field @Deprecated public boolean requirePMF;
     field @Deprecated @Nullable public String saePasswordId;
     field @Deprecated public boolean shared;
@@ -7543,12 +7594,6 @@
     method @Deprecated @NonNull public android.net.wifi.WifiConfiguration.NetworkSelectionStatus.Builder setNetworkSelectionStatus(int);
   }
 
-  @Deprecated public static class WifiConfiguration.RecentFailure {
-    method @Deprecated public int getAssociationStatus();
-    field @Deprecated public static final int NONE = 0; // 0x0
-    field @Deprecated public static final int STATUS_AP_UNABLE_TO_HANDLE_NEW_STA = 17; // 0x11
-  }
-
   public class WifiEnterpriseConfig implements android.os.Parcelable {
     method @Nullable public String[] getCaCertificateAliases();
     method @NonNull public String getCaPath();
diff --git a/api/test-current.txt b/api/test-current.txt
index e0b5b34..4fd33b8 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -1387,6 +1387,10 @@
     method public int getMaxMacroBlocks();
   }
 
+  public final class MediaRoute2Info implements android.os.Parcelable {
+    method @NonNull public String getOriginalId();
+  }
+
   public final class PlaybackParams implements android.os.Parcelable {
     method public int getAudioStretchMode();
     method public android.media.PlaybackParams setAudioStretchMode(int);
@@ -1688,6 +1692,55 @@
     field public static final int TRANSPORT_TEST = 7; // 0x7
   }
 
+  public final class NetworkScore implements android.os.Parcelable {
+    method public int describeContents();
+    method @NonNull public android.net.NetworkScore.Metrics getEndToEndMetrics();
+    method @NonNull public android.net.NetworkScore.Metrics getLinkLayerMetrics();
+    method public int getRange();
+    method @IntRange(from=android.net.NetworkScore.UNKNOWN_SIGNAL_STRENGTH, to=android.net.NetworkScore.MAX_SIGNAL_STRENGTH) public int getSignalStrength();
+    method public boolean hasPolicy(int);
+    method public boolean isExiting();
+    method @NonNull public android.net.NetworkScore withExiting(boolean);
+    method @NonNull public android.net.NetworkScore withSignalStrength(@IntRange(from=android.net.NetworkScore.UNKNOWN_SIGNAL_STRENGTH) int);
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkScore> CREATOR;
+    field public static final int MAX_SIGNAL_STRENGTH = 1000; // 0x3e8
+    field public static final int MIN_SIGNAL_STRENGTH = 0; // 0x0
+    field public static final int POLICY_DEFAULT_SUBSCRIPTION = 8; // 0x8
+    field public static final int POLICY_IGNORE_ON_WIFI = 4; // 0x4
+    field public static final int POLICY_LOCKDOWN_VPN = 1; // 0x1
+    field public static final int POLICY_VPN = 2; // 0x2
+    field public static final int RANGE_CLOSE = 1; // 0x1
+    field public static final int RANGE_LONG = 4; // 0x4
+    field public static final int RANGE_MEDIUM = 3; // 0x3
+    field public static final int RANGE_SHORT = 2; // 0x2
+    field public static final int RANGE_UNKNOWN = 0; // 0x0
+    field public static final int UNKNOWN_SIGNAL_STRENGTH = -1; // 0xffffffff
+  }
+
+  public static class NetworkScore.Builder {
+    ctor public NetworkScore.Builder();
+    method @NonNull public android.net.NetworkScore.Builder addPolicy(int);
+    method @NonNull public android.net.NetworkScore build();
+    method @NonNull public android.net.NetworkScore.Builder clearPolicy(int);
+    method @NonNull public android.net.NetworkScore.Builder setEndToEndMetrics(@NonNull android.net.NetworkScore.Metrics);
+    method @NonNull public android.net.NetworkScore.Builder setExiting(boolean);
+    method @NonNull public android.net.NetworkScore.Builder setLegacyScore(int);
+    method @NonNull public android.net.NetworkScore.Builder setLinkLayerMetrics(@NonNull android.net.NetworkScore.Metrics);
+    method @NonNull public android.net.NetworkScore.Builder setRange(int);
+    method @NonNull public android.net.NetworkScore.Builder setSignalStrength(@IntRange(from=android.net.NetworkScore.UNKNOWN_SIGNAL_STRENGTH, to=android.net.NetworkScore.MAX_SIGNAL_STRENGTH) int);
+  }
+
+  public static class NetworkScore.Metrics {
+    ctor public NetworkScore.Metrics(@IntRange(from=android.net.NetworkScore.Metrics.LATENCY_UNKNOWN) int, @IntRange(from=android.net.NetworkScore.Metrics.BANDWIDTH_UNKNOWN) int, @IntRange(from=android.net.NetworkScore.Metrics.BANDWIDTH_UNKNOWN) int);
+    field public static final int BANDWIDTH_UNKNOWN = -1; // 0xffffffff
+    field @NonNull public static final android.net.NetworkScore.Metrics EMPTY;
+    field public static final int LATENCY_UNKNOWN = -1; // 0xffffffff
+    field @IntRange(from=android.net.NetworkScore.Metrics.BANDWIDTH_UNKNOWN) public final int downlinkBandwidthKBps;
+    field @IntRange(from=android.net.NetworkScore.Metrics.LATENCY_UNKNOWN) public final int latencyMs;
+    field @IntRange(from=android.net.NetworkScore.Metrics.BANDWIDTH_UNKNOWN) public final int uplinkBandwidthKBps;
+  }
+
   public class NetworkStack {
     field public static final String PERMISSION_MAINLINE_NETWORK_STACK = "android.permission.MAINLINE_NETWORK_STACK";
   }
diff --git a/cmds/statsd/benchmark/log_event_benchmark.cpp b/cmds/statsd/benchmark/log_event_benchmark.cpp
index bdfdb2e..30dfe32 100644
--- a/cmds/statsd/benchmark/log_event_benchmark.cpp
+++ b/cmds/statsd/benchmark/log_event_benchmark.cpp
@@ -39,7 +39,7 @@
     uint8_t msg[LOGGER_ENTRY_MAX_PAYLOAD];
     size_t size = createAndParseStatsEvent(msg);
     while (state.KeepRunning()) {
-        benchmark::DoNotOptimize(LogEvent(msg, size, /*uid=*/ 1000));
+        benchmark::DoNotOptimize(LogEvent(msg, size, /*uid=*/ 1000, /*pid=*/ 1001));
     }
 }
 BENCHMARK(BM_LogEventCreation);
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index c1a8d69..05281f7 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -30,7 +30,6 @@
 #include <android-base/stringprintf.h>
 #include <android-base/strings.h>
 #include <binder/IPCThreadState.h>
-#include <binder/IServiceManager.h>
 #include <binder/PermissionController.h>
 #include <cutils/multiuser.h>
 #include <dirent.h>
@@ -77,6 +76,25 @@
     return binder::Status::fromExceptionCode(code, String8(msg.c_str()));
 }
 
+
+static bool checkPermission(const char* permission) {
+    sp<IStatsCompanionService> scs = getStatsCompanionService();
+    if (scs == nullptr) {
+        return false;
+    }
+
+    bool success;
+    pid_t pid = IPCThreadState::self()->getCallingPid();
+    uid_t uid = IPCThreadState::self()->getCallingUid();
+
+    binder::Status status = scs->checkPermission(String16(permission), pid, uid, &success);
+    if (!status.isOk()) {
+        return false;
+    }
+    return success;
+}
+
+
 binder::Status checkUid(uid_t expectedUid) {
     uid_t uid = IPCThreadState::self()->getCallingUid();
     if (uid == expectedUid || uid == AID_ROOT) {
@@ -97,11 +115,11 @@
     }
 
     // Caller must be granted these permissions
-    if (!checkCallingPermission(String16(kPermissionDump))) {
+    if (!checkPermission(kPermissionDump)) {
         return exception(binder::Status::EX_SECURITY,
                 StringPrintf("UID %d / PID %d lacks permission %s", uid, pid, kPermissionDump));
     }
-    if (!checkCallingPermission(String16(kPermissionUsage))) {
+    if (!checkPermission(kPermissionUsage)) {
         return exception(binder::Status::EX_SECURITY,
                 StringPrintf("UID %d / PID %d lacks permission %s", uid, pid, kPermissionUsage));
     }
@@ -285,7 +303,7 @@
  * TODO: Come up with a more robust method of enacting <serviceutils/PriorityDumper.h>.
  */
 status_t StatsService::dump(int fd, const Vector<String16>& args) {
-    if (!checkCallingPermission(String16(kPermissionDump))) {
+    if (!checkPermission(kPermissionDump)) {
         return PERMISSION_DENIED;
     }
     int lastArg = args.size() - 1;
@@ -914,7 +932,7 @@
     IPCThreadState* ipc = IPCThreadState::self();
     VLOG("StatsService::cmd_clear_puller_cache with Pid %i, Uid %i",
             ipc->getCallingPid(), ipc->getCallingUid());
-    if (checkCallingPermission(String16(kPermissionDump))) {
+    if (checkPermission(kPermissionDump)) {
         int cleared = mPullerManager->ForceClearPullerCache();
         dprintf(out, "Puller removed %d cached data!\n", cleared);
         return NO_ERROR;
@@ -927,7 +945,7 @@
     IPCThreadState* ipc = IPCThreadState::self();
     VLOG("StatsService::cmd_print_logs with Pid %i, Uid %i", ipc->getCallingPid(),
          ipc->getCallingUid());
-    if (checkCallingPermission(String16(kPermissionDump))) {
+    if (checkPermission(kPermissionDump)) {
         bool enabled = true;
         if (args.size() >= 2) {
             enabled = atoi(args[1].c_str()) != 0;
@@ -1314,12 +1332,12 @@
     // Root, system, and shell always have access
     if (uid != AID_ROOT && uid != AID_SYSTEM && uid != AID_SHELL) {
         // Caller must be granted these permissions
-        if (!checkCallingPermission(String16(kPermissionDump))) {
+        if (!checkPermission(kPermissionDump)) {
             return exception(binder::Status::EX_SECURITY,
                              StringPrintf("UID %d / PID %d lacks permission %s", uid, pid,
                                           kPermissionDump));
         }
-        if (!checkCallingPermission(String16(kPermissionUsage))) {
+        if (!checkPermission(kPermissionUsage)) {
             return exception(binder::Status::EX_SECURITY,
                              StringPrintf("UID %d / PID %d lacks permission %s", uid, pid,
                                           kPermissionUsage));
@@ -1410,12 +1428,12 @@
     // Root, system, and shell always have access
     if (uid != AID_ROOT && uid != AID_SYSTEM && uid != AID_SHELL) {
         // Caller must be granted these permissions
-        if (!checkCallingPermission(String16(kPermissionDump))) {
+        if (!checkPermission(kPermissionDump)) {
             return exception(binder::Status::EX_SECURITY,
                              StringPrintf("UID %d / PID %d lacks permission %s", uid, pid,
                                           kPermissionDump));
         }
-        if (!checkCallingPermission(String16(kPermissionUsage))) {
+        if (!checkPermission(kPermissionUsage)) {
             return exception(binder::Status::EX_SECURITY,
                              StringPrintf("UID %d / PID %d lacks permission %s", uid, pid,
                                           kPermissionUsage));
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index d96bb46..12058ca 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -232,8 +232,8 @@
         NfcCardemulationOccurred nfc_cardemulation_occurred = 137 [(module) = "nfc"];
         NfcTagOccurred nfc_tag_occurred = 138 [(module) = "nfc"];
         NfcHceTransactionOccurred nfc_hce_transaction_occurred = 139 [(module) = "nfc"];
-        SeStateChanged se_state_changed = 140;
-        SeOmapiReported se_omapi_reported = 141;
+        SeStateChanged se_state_changed = 140 [(module) = "secure_element"];
+        SeOmapiReported se_omapi_reported = 141 [(module) = "secure_element"];
         BroadcastDispatchLatencyReported broadcast_dispatch_latency_reported =
                 142 [(module) = "framework"];
         AttentionManagerServiceResultReported attention_manager_service_result_reported =
diff --git a/cmds/statsd/src/external/StatsCallbackPuller.cpp b/cmds/statsd/src/external/StatsCallbackPuller.cpp
index 6257771..1a11f0e 100644
--- a/cmds/statsd/src/external/StatsCallbackPuller.cpp
+++ b/cmds/statsd/src/external/StatsCallbackPuller.cpp
@@ -68,7 +68,7 @@
                     for (const StatsEventParcel& parcel: output) {
                         shared_ptr<LogEvent> event = make_shared<LogEvent>(
                                 const_cast<uint8_t*>(parcel.buffer.data()), parcel.buffer.size(),
-                                /*uid=*/-1, /*useNewSchema=*/true);
+                                /*uid=*/-1, /*pid=*/-1, /*useNewSchema=*/true);
                         sharedData->push_back(event);
                     }
                     *pullSuccess = success;
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index 3827b9e..9a0693a 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -37,11 +37,12 @@
 
 // Msg is expected to begin at the start of the serialized atom -- it should not
 // include the android_log_header_t or the StatsEventTag.
-LogEvent::LogEvent(uint8_t* msg, uint32_t len, uint32_t uid)
+LogEvent::LogEvent(uint8_t* msg, uint32_t len, int32_t uid, int32_t pid)
     : mBuf(msg),
       mRemainingLen(len),
       mLogdTimestampNs(time(nullptr)),
-      mLogUid(uid)
+      mLogUid(uid),
+      mLogPid(pid)
 {
 #ifdef NEW_ENCODING_SCHEME
     initNew();
@@ -52,8 +53,13 @@
 #endif
 }
 
-LogEvent::LogEvent(uint8_t* msg, uint32_t len, uint32_t uid, bool useNewSchema)
-    : mBuf(msg), mRemainingLen(len), mLogdTimestampNs(time(nullptr)), mLogUid(uid) {
+LogEvent::LogEvent(uint8_t* msg, uint32_t len, int32_t uid, int32_t pid, bool useNewSchema)
+    : mBuf(msg),
+      mRemainingLen(len),
+      mLogdTimestampNs(time(nullptr)),
+      mLogUid(uid),
+      mLogPid(pid)
+{
     if (useNewSchema) {
         initNew();
     } else {
@@ -66,6 +72,7 @@
 LogEvent::LogEvent(const LogEvent& event) {
     mTagId = event.mTagId;
     mLogUid = event.mLogUid;
+    mLogPid = event.mLogPid;
     mElapsedTimestampNs = event.mElapsedTimestampNs;
     mLogdTimestampNs = event.mLogdTimestampNs;
     mValues = event.mValues;
@@ -146,6 +153,7 @@
     mElapsedTimestampNs = getElapsedRealtimeNs();
     mTagId = android::util::BINARY_PUSH_STATE_CHANGED;
     mLogUid = android::IPCThreadState::self()->getCallingUid();
+    mLogPid = android::IPCThreadState::self()->getCallingPid();
 
     mValues.push_back(FieldValue(Field(mTagId, getSimpleField(1)), Value(trainName)));
     mValues.push_back(FieldValue(Field(mTagId, getSimpleField(2)), Value(trainVersionCode)));
diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h
index 463a1b6..583dae2 100644
--- a/cmds/statsd/src/logd/LogEvent.h
+++ b/cmds/statsd/src/logd/LogEvent.h
@@ -71,12 +71,12 @@
     /**
      * Read a LogEvent from the socket
      */
-    explicit LogEvent(uint8_t* msg, uint32_t len, uint32_t uid);
+    explicit LogEvent(uint8_t* msg, uint32_t len, int32_t uid, int32_t pid);
 
     /**
      * Temp constructor to use for pulled atoms until we flip the socket schema.
      */
-    explicit LogEvent(uint8_t* msg, uint32_t len, uint32_t uid, bool useNewSchema);
+    explicit LogEvent(uint8_t* msg, uint32_t len, int32_t uid, int32_t pid, bool useNewSchema);
 
     /**
      * Constructs a LogEvent with synthetic data for testing. Must call init() before reading.
@@ -123,9 +123,17 @@
      */
     inline int GetTagId() const { return mTagId; }
 
-    inline uint32_t GetUid() const {
-        return mLogUid;
-    }
+    /**
+     * Get the uid of the logging client.
+     * Returns -1 if the uid is unknown/has not been set.
+     */
+    inline int32_t GetUid() const { return mLogUid; }
+
+    /**
+     * Get the pid of the logging client.
+     * Returns -1 if the pid is unknown/has not been set.
+     */
+    inline int32_t GetPid() const { return mLogPid; }
 
     /**
      * Get the nth value, starting at 1.
@@ -305,9 +313,14 @@
     // The elapsed timestamp set by statsd log writer.
     int64_t mElapsedTimestampNs;
 
+    // The atom tag of the event.
     int mTagId;
 
-    uint32_t mLogUid;
+    // The uid of the logging client (defaults to -1).
+    int32_t mLogUid = -1;
+
+    // The pid of the logging client (defaults to -1).
+    int32_t mLogPid = -1;
 };
 
 void writeExperimentIdsToProto(const std::vector<int64_t>& experimentIds, std::vector<uint8_t>* protoOut);
diff --git a/cmds/statsd/src/socket/StatsSocketListener.cpp b/cmds/statsd/src/socket/StatsSocketListener.cpp
index 4308a110..cdb4d3a 100755
--- a/cmds/statsd/src/socket/StatsSocketListener.cpp
+++ b/cmds/statsd/src/socket/StatsSocketListener.cpp
@@ -126,9 +126,10 @@
     uint8_t* msg = ptr + sizeof(uint32_t);
     uint32_t len = n - sizeof(uint32_t);
     uint32_t uid = cred->uid;
+    uint32_t pid = cred->pid;
 
     int64_t oldestTimestamp;
-    if (!mQueue->push(std::make_unique<LogEvent>(msg, len, uid), &oldestTimestamp)) {
+    if (!mQueue->push(std::make_unique<LogEvent>(msg, len, uid, pid), &oldestTimestamp)) {
         StatsdStats::getInstance().noteEventQueueOverflow(oldestTimestamp);
     }
 
diff --git a/cmds/statsd/tests/LogEvent_test.cpp b/cmds/statsd/tests/LogEvent_test.cpp
index 1cf9fb6..35b0396 100644
--- a/cmds/statsd/tests/LogEvent_test.cpp
+++ b/cmds/statsd/tests/LogEvent_test.cpp
@@ -57,9 +57,11 @@
     size_t size;
     uint8_t* buf = stats_event_get_buffer(event, &size);
 
-    LogEvent logEvent(buf, size, /*uid=*/ 1000);
+    LogEvent logEvent(buf, size, /*uid=*/ 1000, /*pid=*/ 1001);
     EXPECT_TRUE(logEvent.isValid());
     EXPECT_EQ(100, logEvent.GetTagId());
+    EXPECT_EQ(1000, logEvent.GetUid());
+    EXPECT_EQ(1001, logEvent.GetPid());
 
     const vector<FieldValue>& values = logEvent.getValues();
     EXPECT_EQ(4, values.size());
@@ -103,9 +105,11 @@
     size_t size;
     uint8_t* buf = stats_event_get_buffer(event, &size);
 
-    LogEvent logEvent(buf, size, /*uid=*/ 1000);
+    LogEvent logEvent(buf, size, /*uid=*/ 1000, /*pid=*/ 1001);
     EXPECT_TRUE(logEvent.isValid());
     EXPECT_EQ(100, logEvent.GetTagId());
+    EXPECT_EQ(1000, logEvent.GetUid());
+    EXPECT_EQ(1001, logEvent.GetPid());
 
     const vector<FieldValue>& values = logEvent.getValues();
     EXPECT_EQ(2, values.size());
@@ -136,9 +140,11 @@
     size_t size;
     uint8_t* buf = stats_event_get_buffer(event, &size);
 
-    LogEvent logEvent(buf, size, /*uid=*/ 1000);
+    LogEvent logEvent(buf, size, /*uid=*/ 1000, /*pid=*/ 1001);
     EXPECT_TRUE(logEvent.isValid());
     EXPECT_EQ(100, logEvent.GetTagId());
+    EXPECT_EQ(1000, logEvent.GetUid());
+    EXPECT_EQ(1001, logEvent.GetPid());
 
     const vector<FieldValue>& values = logEvent.getValues();
     EXPECT_EQ(1, values.size());
@@ -162,9 +168,11 @@
     size_t size;
     uint8_t* buf = stats_event_get_buffer(event, &size);
 
-    LogEvent logEvent(buf, size, /*uid=*/ 1000);
+    LogEvent logEvent(buf, size, /*uid=*/ 1000, /*pid=*/ 1001);
     EXPECT_TRUE(logEvent.isValid());
     EXPECT_EQ(100, logEvent.GetTagId());
+    EXPECT_EQ(1000, logEvent.GetUid());
+    EXPECT_EQ(1001, logEvent.GetPid());
 
     const vector<FieldValue>& values = logEvent.getValues();
     EXPECT_EQ(1, values.size());
@@ -196,9 +204,11 @@
     size_t size;
     uint8_t* buf = stats_event_get_buffer(event, &size);
 
-    LogEvent logEvent(buf, size, /*uid=*/ 1000);
+    LogEvent logEvent(buf, size, /*uid=*/ 1000, /*pid=*/ 1001);
     EXPECT_TRUE(logEvent.isValid());
     EXPECT_EQ(100, logEvent.GetTagId());
+    EXPECT_EQ(1000, logEvent.GetUid());
+    EXPECT_EQ(1001, logEvent.GetPid());
 
     const vector<FieldValue>& values = logEvent.getValues();
     EXPECT_EQ(8, values.size()); // 2 FieldValues per key-value pair
@@ -260,9 +270,11 @@
     size_t size;
     uint8_t* buf = stats_event_get_buffer(event, &size);
 
-    LogEvent logEvent(buf, size, /*uid=*/ 1000);
+    LogEvent logEvent(buf, size, /*uid=*/ 1000, /*pid=*/ 1001);
     EXPECT_TRUE(logEvent.isValid());
     EXPECT_EQ(100, logEvent.GetTagId());
+    EXPECT_EQ(1000, logEvent.GetUid());
+    EXPECT_EQ(1001, logEvent.GetPid());
 
     const vector<FieldValue>& values = logEvent.getValues();
     EXPECT_EQ(4, values.size()); // 2 per attribution node
diff --git a/core/java/android/app/ActivityTaskManager.java b/core/java/android/app/ActivityTaskManager.java
index dd9a2bc..58bff7f 100644
--- a/core/java/android/app/ActivityTaskManager.java
+++ b/core/java/android/app/ActivityTaskManager.java
@@ -158,6 +158,24 @@
                 }
             };
 
+    /** @hide */
+    @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS)
+    public static ITaskOrganizerController getTaskOrganizerController() {
+        return ITaskOrganizerControllerSingleton.get();
+    }
+
+    private static final Singleton<ITaskOrganizerController> ITaskOrganizerControllerSingleton =
+            new Singleton<ITaskOrganizerController>() {
+                @Override
+                protected ITaskOrganizerController create() {
+                    try {
+                        return getService().getTaskOrganizerController();
+                    } catch (RemoteException e) {
+                        return null;
+                    }
+                }
+            };
+
     /**
      * Sets the windowing mode for a specific task. Only works on tasks of type
      * {@link WindowConfiguration#ACTIVITY_TYPE_STANDARD}
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index 85fa7c1..503f5c5 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -29,6 +29,7 @@
 import android.app.IRequestFinishCallback;
 import android.app.IServiceConnection;
 import android.app.IStopUserCallback;
+import android.app.ITaskOrganizerController;
 import android.app.ITaskStackListener;
 import android.app.IUiAutomationConnection;
 import android.app.IUidObserver;
@@ -71,7 +72,6 @@
 import android.view.ITaskOrganizer;
 import android.view.RemoteAnimationDefinition;
 import android.view.RemoteAnimationAdapter;
-import android.view.WindowContainerTransaction;
 import com.android.internal.app.IVoiceInteractor;
 import com.android.internal.os.IResultReceiver;
 import com.android.internal.policy.IKeyguardDismissCallback;
@@ -123,8 +123,6 @@
             int requestCode, int flags, in ProfilerInfo profilerInfo, in Bundle options,
             IBinder permissionToken, boolean ignoreTargetSecurity, int userId);
 
-    void registerTaskOrganizer(in ITaskOrganizer organizer, int windowingMode);
-
     boolean isActivityStartAllowedOnDisplay(int displayId, in Intent intent, in String resolvedType,
             int userId);
 
@@ -224,7 +222,6 @@
     void setTaskResizeable(int taskId, int resizeableMode);
     void toggleFreeformWindowingMode(in IBinder token);
     void resizeTask(int taskId, in Rect bounds, int resizeMode);
-    void applyContainerTransaction(in WindowContainerTransaction t);
     void moveStackToDisplay(int stackId, int displayId);
     void removeStack(int stackId);
 
@@ -364,6 +361,11 @@
             in Rect tempOtherTaskBounds, in Rect tempOtherTaskInsetBounds);
 
     /**
+     * Returns an interface enabling the management of task organizers.
+     */
+    ITaskOrganizerController getTaskOrganizerController();
+
+    /**
      * Sets whether we are currently in an interactive split screen resize operation where we
      * are changing the docked stack size.
      */
diff --git a/core/java/android/app/ITaskOrganizerController.aidl b/core/java/android/app/ITaskOrganizerController.aidl
new file mode 100644
index 0000000..168f782
--- /dev/null
+++ b/core/java/android/app/ITaskOrganizerController.aidl
@@ -0,0 +1,51 @@
+/**
+ * Copyright (c) 2020, 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;
+
+import android.app.ActivityManager;
+import android.view.ITaskOrganizer;
+import android.view.IWindowContainer;
+import android.view.WindowContainerTransaction;
+
+/** @hide */
+interface ITaskOrganizerController {
+
+    /**
+     * Register a TaskOrganizer to manage tasks as they enter the given windowing mode.
+     * If there was already a TaskOrganizer for this windowing mode it will be evicted
+     * and receive taskVanished callbacks in the process.
+     */
+    void registerTaskOrganizer(ITaskOrganizer organizer, int windowingMode);
+
+    /** Apply multiple WindowContainer operations at once. */
+    void applyContainerTransaction(in WindowContainerTransaction t);
+
+    /** Creates a persistent root task in WM for a particular windowing-mode. */
+    ActivityManager.RunningTaskInfo createRootTask(int displayId, int windowingMode);
+
+    /** Deletes a persistent root task in WM */
+    boolean deleteRootTask(IWindowContainer task);
+
+    /** Get the root task which contains the current ime target */
+    IWindowContainer getImeTarget(int display);
+
+    /**
+     * Set's the root task to launch new tasks into on a display. {@code null} means no launch root
+     * and thus new tasks just end up directly on the display.
+     */
+    void setLaunchRoot(int displayId, in IWindowContainer root);
+}
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 59c17192..65a5f6b 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -365,11 +365,9 @@
         registerService(Context.TETHERING_SERVICE, TetheringManager.class,
                 new CachedServiceFetcher<TetheringManager>() {
             @Override
-            public TetheringManager createService(ContextImpl ctx) throws ServiceNotFoundException {
-                IBinder b = ServiceManager.getService(Context.TETHERING_SERVICE);
-                if (b == null) return null;
-
-                return new TetheringManager(ctx, b);
+            public TetheringManager createService(ContextImpl ctx) {
+                return new TetheringManager(
+                        ctx, () -> ServiceManager.getService(Context.TETHERING_SERVICE));
             }});
 
 
diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java
index fe9c640..662ca6e 100644
--- a/core/java/android/app/TaskInfo.java
+++ b/core/java/android/app/TaskInfo.java
@@ -16,6 +16,8 @@
 
 package android.app;
 
+import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.compat.annotation.UnsupportedAppUsage;
@@ -25,6 +27,7 @@
 import android.os.Parcel;
 import android.os.RemoteException;
 import android.util.Log;
+import android.view.IWindowContainer;
 
 /**
  * Stores information about a particular Task.
@@ -138,6 +141,19 @@
     @UnsupportedAppUsage
     public final Configuration configuration = new Configuration();
 
+    /**
+     * Used as an opaque identifier for this task.
+     * @hide
+     */
+    @NonNull
+    public IWindowContainer token;
+
+    /**
+     * The activity type of the top activity in this task.
+     * @hide
+     */
+    public @WindowConfiguration.ActivityType int topActivityType;
+
     TaskInfo() {
         // Do nothing
     }
@@ -160,6 +176,11 @@
         }
     }
 
+    /** @hide */
+    public boolean isResizable() {
+        return resizeMode != RESIZE_MODE_UNRESIZEABLE;
+    }
+
     /**
      * Reads the TaskInfo from a parcel.
      */
@@ -186,6 +207,8 @@
         supportsSplitScreenMultiWindow = source.readBoolean();
         resizeMode = source.readInt();
         configuration.readFromParcel(source);
+        token = IWindowContainer.Stub.asInterface(source.readStrongBinder());
+        topActivityType = source.readInt();
     }
 
     /**
@@ -221,6 +244,8 @@
         dest.writeBoolean(supportsSplitScreenMultiWindow);
         dest.writeInt(resizeMode);
         configuration.writeToParcel(dest, flags);
+        dest.writeStrongInterface(token);
+        dest.writeInt(topActivityType);
     }
 
     @Override
@@ -234,6 +259,8 @@
                 + " numActivities=" + numActivities
                 + " lastActiveTime=" + lastActiveTime
                 + " supportsSplitScreenMultiWindow=" + supportsSplitScreenMultiWindow
-                + " resizeMode=" + resizeMode;
+                + " resizeMode=" + resizeMode
+                + " token=" + token
+                + " topActivityType=" + topActivityType;
     }
 }
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index e24b040..325fd94 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -2162,6 +2162,33 @@
     }
 
     /**
+     * Fetches a list of the most recently connected bluetooth devices ordered by how recently they
+     * were connected with most recently first and least recently last
+     *
+     * @return {@link List} of bonded {@link BluetoothDevice} ordered by how recently they were
+     * connected
+     *
+     * @hide
+     */
+    @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+    public @NonNull List<BluetoothDevice> getMostRecentlyConnectedDevices() {
+        if (getState() != STATE_ON) {
+            return new ArrayList<>();
+        }
+        try {
+            mServiceLock.readLock().lock();
+            if (mService != null) {
+                return mService.getMostRecentlyConnectedDevices();
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "", e);
+        } finally {
+            mServiceLock.readLock().unlock();
+        }
+        return new ArrayList<>();
+    }
+
+    /**
      * Return the set of {@link BluetoothDevice} objects that are bonded
      * (paired) to the local adapter.
      * <p>If Bluetooth state is not {@link #STATE_ON}, this API
diff --git a/core/java/android/bluetooth/BluetoothHidDevice.java b/core/java/android/bluetooth/BluetoothHidDevice.java
index a923be6..b5959c0 100644
--- a/core/java/android/bluetooth/BluetoothHidDevice.java
+++ b/core/java/android/bluetooth/BluetoothHidDevice.java
@@ -706,7 +706,7 @@
      * @hide
      */
     @SystemApi
-    @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
     public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
             @ConnectionPolicy int connectionPolicy) {
         log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index ce9693d..fa12c08 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -3322,15 +3322,19 @@
     // of dependent changes that would conflict throughout the automerger graph. Having this
     // temporarily helps with the process of going through with all these dependent changes across
     // the entire tree.
+    // STOPSHIP (b/148055573) : remove this before R is released.
     /**
      * @hide
      * Register a NetworkAgent with ConnectivityService.
      * @return Network corresponding to NetworkAgent.
+     * @deprecated use the version that takes a NetworkScore and a provider ID.
      */
     @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
+    @Deprecated
     public Network registerNetworkAgent(Messenger messenger, NetworkInfo ni, LinkProperties lp,
             NetworkCapabilities nc, int score, NetworkAgentConfig config) {
-        return registerNetworkAgent(messenger, ni, lp, nc, score, config, NetworkProvider.ID_NONE);
+        final NetworkScore ns = new NetworkScore.Builder().setLegacyScore(score).build();
+        return registerNetworkAgent(messenger, ni, lp, nc, ns, config, NetworkProvider.ID_NONE);
     }
 
     /**
@@ -3340,7 +3344,7 @@
      */
     @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
     public Network registerNetworkAgent(Messenger messenger, NetworkInfo ni, LinkProperties lp,
-            NetworkCapabilities nc, int score, NetworkAgentConfig config, int providerId) {
+            NetworkCapabilities nc, NetworkScore score, NetworkAgentConfig config, int providerId) {
         try {
             return mService.registerNetworkAgent(messenger, ni, lp, nc, score, config, providerId);
         } catch (RemoteException e) {
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 3e9e7fa..0dc66b5 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -26,6 +26,7 @@
 import android.net.NetworkInfo;
 import android.net.NetworkQuotaInfo;
 import android.net.NetworkRequest;
+import android.net.NetworkScore;
 import android.net.NetworkState;
 import android.net.ISocketKeepaliveCallback;
 import android.net.ProxyInfo;
@@ -154,7 +155,7 @@
     void declareNetworkRequestUnfulfillable(in NetworkRequest request);
 
     Network registerNetworkAgent(in Messenger messenger, in NetworkInfo ni, in LinkProperties lp,
-            in NetworkCapabilities nc, int score, in NetworkAgentConfig config,
+            in NetworkCapabilities nc, in NetworkScore score, in NetworkAgentConfig config,
             in int factorySerialNumber);
 
     NetworkRequest requestNetwork(in NetworkCapabilities networkCapabilities,
diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java
index 7cc569a..ddf8dbb 100644
--- a/core/java/android/net/NetworkAgent.java
+++ b/core/java/android/net/NetworkAgent.java
@@ -117,13 +117,6 @@
     public static final int EVENT_NETWORK_PROPERTIES_CHANGED = BASE + 3;
 
     /**
-     * Centralize the place where base network score, and network score scaling, will be
-     * stored, so as we can consistently compare apple and oranges, or wifi, ethernet and LTE
-     * @hide
-     */
-    public static final int WIFI_BASE_SCORE = 60;
-
-    /**
      * Sent by the NetworkAgent to ConnectivityService to pass the current
      * network score.
      * obj = network score Integer
@@ -272,7 +265,13 @@
      */
     public static final int CMD_REMOVE_KEEPALIVE_PACKET_FILTER = BASE + 17;
 
+    // STOPSHIP (b/148055573) : remove this before R is released.
+    private static NetworkScore makeNetworkScore(int score) {
+        return new NetworkScore.Builder().setLegacyScore(score).build();
+    }
+
     /** @hide TODO: remove and replace usage with the public constructor. */
+    // STOPSHIP (b/148055573) : remove this before R is released.
     public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni,
             NetworkCapabilities nc, LinkProperties lp, int score) {
         this(looper, context, logTag, ni, nc, lp, score, null, NetworkProvider.ID_NONE);
@@ -280,6 +279,7 @@
     }
 
     /** @hide TODO: remove and replace usage with the public constructor. */
+    // STOPSHIP (b/148055573) : remove this before R is released.
     public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni,
             NetworkCapabilities nc, LinkProperties lp, int score, NetworkAgentConfig config) {
         this(looper, context, logTag, ni, nc, lp, score, config, NetworkProvider.ID_NONE);
@@ -287,6 +287,7 @@
     }
 
     /** @hide TODO: remove and replace usage with the public constructor. */
+    // STOPSHIP (b/148055573) : remove this before R is released.
     public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni,
             NetworkCapabilities nc, LinkProperties lp, int score, int providerId) {
         this(looper, context, logTag, ni, nc, lp, score, null, providerId);
@@ -294,10 +295,12 @@
     }
 
     /** @hide TODO: remove and replace usage with the public constructor. */
+    // STOPSHIP (b/148055573) : remove this before R is released.
     public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni,
             NetworkCapabilities nc, LinkProperties lp, int score, NetworkAgentConfig config,
             int providerId) {
-        this(looper, context, logTag, nc, lp, score, config, providerId, ni, true /* legacy */);
+        this(looper, context, logTag, nc, lp, makeNetworkScore(score), config, providerId, ni,
+                true /* legacy */);
         register();
     }
 
@@ -323,8 +326,9 @@
      * @param provider the {@link NetworkProvider} managing this agent.
      */
     public NetworkAgent(@NonNull Context context, @NonNull Looper looper, @NonNull String logTag,
-            @NonNull NetworkCapabilities nc, @NonNull LinkProperties lp, int score,
-            @NonNull NetworkAgentConfig config, @Nullable NetworkProvider provider) {
+            @NonNull NetworkCapabilities nc, @NonNull LinkProperties lp,
+            @NonNull NetworkScore score, @NonNull NetworkAgentConfig config,
+            @Nullable NetworkProvider provider) {
         this(looper, context, logTag, nc, lp, score, config,
                 provider == null ? NetworkProvider.ID_NONE : provider.getProviderId(),
                 getLegacyNetworkInfo(config), false /* legacy */);
@@ -334,12 +338,12 @@
         public final Context context;
         public final NetworkCapabilities capabilities;
         public final LinkProperties properties;
-        public final int score;
+        public final NetworkScore score;
         public final NetworkAgentConfig config;
         public final NetworkInfo info;
         InitialConfiguration(@NonNull Context context, @NonNull NetworkCapabilities capabilities,
-                @NonNull LinkProperties properties, int score, @NonNull NetworkAgentConfig config,
-                @NonNull NetworkInfo info) {
+                @NonNull LinkProperties properties, @NonNull NetworkScore score,
+                @NonNull NetworkAgentConfig config, @NonNull NetworkInfo info) {
             this.context = context;
             this.capabilities = capabilities;
             this.properties = properties;
@@ -351,7 +355,7 @@
     private volatile InitialConfiguration mInitialConfiguration;
 
     private NetworkAgent(@NonNull Looper looper, @NonNull Context context, @NonNull String logTag,
-            @NonNull NetworkCapabilities nc, @NonNull LinkProperties lp, int score,
+            @NonNull NetworkCapabilities nc, @NonNull LinkProperties lp, NetworkScore score,
             @NonNull NetworkAgentConfig config, int providerId, @NonNull NetworkInfo ni,
             boolean legacy) {
         mHandler = new NetworkAgentHandler(looper);
@@ -646,22 +650,8 @@
      * Must be called by the agent to update the score of this network.
      * @param score the new score.
      */
-    public void sendNetworkScore(int score) {
-        if (score < 0) {
-            throw new IllegalArgumentException("Score must be >= 0");
-        }
-        final NetworkScore ns = new NetworkScore();
-        ns.putIntExtension(NetworkScore.LEGACY_SCORE, score);
-        updateScore(ns);
-    }
-
-    /**
-     * Must be called by the agent when it has a new {@link NetworkScore} for this network.
-     * @param ns the new score.
-     * @hide TODO: unhide the NetworkScore class, and rename to sendNetworkScore.
-     */
-    public void updateScore(@NonNull NetworkScore ns) {
-        queueOrSendMessage(EVENT_NETWORK_SCORE_CHANGED, new NetworkScore(ns));
+    public void sendNetworkScore(@NonNull final NetworkScore score) {
+        queueOrSendMessage(EVENT_NETWORK_SCORE_CHANGED, score);
     }
 
     /**
diff --git a/core/java/android/net/NetworkScore.aidl b/core/java/android/net/NetworkScore.aidl
new file mode 100644
index 0000000..be9a98b
--- /dev/null
+++ b/core/java/android/net/NetworkScore.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2020, 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;
+
+parcelable NetworkScore;
diff --git a/core/java/android/net/NetworkScore.java b/core/java/android/net/NetworkScore.java
index 13f2994..ae17378 100644
--- a/core/java/android/net/NetworkScore.java
+++ b/core/java/android/net/NetworkScore.java
@@ -15,12 +15,18 @@
  */
 package android.net;
 
+import android.annotation.IntDef;
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.Objects;
 
 /**
@@ -28,57 +34,392 @@
  *
  * A NetworkScore object represents the characteristics of a network that affects how good the
  * network is considered for a particular use.
+ *
+ * This class is not thread-safe.
  * @hide
  */
+@TestApi
+@SystemApi
 public final class NetworkScore implements Parcelable {
+    /** An object containing scoring-relevant metrics for a network. */
+    public static class Metrics {
+        /** Value meaning the latency is unknown. */
+        public static final int LATENCY_UNKNOWN = -1;
 
-    // The key of bundle which is used to get the legacy network score of NetworkAgentInfo.
-    // TODO: Remove this when the transition to NetworkScore is over.
-    public static final String LEGACY_SCORE = "LEGACY_SCORE";
+        /** Value meaning the bandwidth is unknown. */
+        public static final int BANDWIDTH_UNKNOWN = -1;
+
+        /**
+         * Round-trip delay in milliseconds to the relevant destination for this Metrics object.
+         *
+         * LATENCY_UNKNOWN if unknown.
+         */
+        @IntRange(from = LATENCY_UNKNOWN)
+        public final int latencyMs;
+
+        /**
+         * Downlink in kB/s with the relevant destination for this Metrics object.
+         *
+         * BANDWIDTH_UNKNOWN if unknown. If directional bandwidth is unknown, up and downlink
+         * bandwidth can have the same value.
+         */
+        @IntRange(from = BANDWIDTH_UNKNOWN)
+        public final int downlinkBandwidthKBps;
+
+        /**
+         * Uplink in kB/s with the relevant destination for this Metrics object.
+         *
+         * BANDWIDTH_UNKNOWN if unknown. If directional bandwidth is unknown, up and downlink
+         * bandwidth can have the same value.
+         */
+        @IntRange(from = BANDWIDTH_UNKNOWN)
+        public final int uplinkBandwidthKBps;
+
+        /** Constructor */
+        public Metrics(@IntRange(from = LATENCY_UNKNOWN) final int latency,
+                @IntRange(from = BANDWIDTH_UNKNOWN) final int downlinkBandwidth,
+                @IntRange(from = BANDWIDTH_UNKNOWN) final int uplinkBandwidth) {
+            latencyMs = latency;
+            downlinkBandwidthKBps = downlinkBandwidth;
+            uplinkBandwidthKBps = uplinkBandwidth;
+        }
+
+        /** toString */
+        public String toString() {
+            return "latency = " + latencyMs + " downlinkBandwidth = " + downlinkBandwidthKBps
+                    + "uplinkBandwidth = " + uplinkBandwidthKBps;
+        }
+
+        @NonNull
+        public static final Metrics EMPTY =
+                new Metrics(LATENCY_UNKNOWN, BANDWIDTH_UNKNOWN, BANDWIDTH_UNKNOWN);
+    }
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(flag = true, prefix = "POLICY_", value = {
+            POLICY_LOCKDOWN_VPN,
+            POLICY_VPN,
+            POLICY_IGNORE_ON_WIFI,
+            POLICY_DEFAULT_SUBSCRIPTION
+    })
+    public @interface Policy {
+    }
+
+    /**
+     * This network is a VPN with lockdown enabled.
+     *
+     * If a network with this bit is present in the list of candidates and not connected,
+     * no network can satisfy the request.
+     */
+    public static final int POLICY_LOCKDOWN_VPN = 1 << 0;
+
+    /**
+     * This network is a VPN.
+     *
+     * If a network with this bit is present and the request UID is included in the UID ranges
+     * of this network, it outscores all other networks without this bit.
+     */
+    public static final int POLICY_VPN = 1 << 1;
+
+    /**
+     * This network should not be used if a previously validated WiFi network is available.
+     *
+     * If a network with this bit is present and a previously validated WiFi is present too, the
+     * network with this bit is outscored by it. This stays true if the WiFi network
+     * becomes unvalidated : this network will not be considered.
+     */
+    public static final int POLICY_IGNORE_ON_WIFI = 1 << 2;
+
+    /**
+     * This network is the default subscription for this transport.
+     *
+     * If a network with this bit is present, other networks of the same transport without this
+     * bit are outscored by it. A device with two active subscriptions and a setting
+     * to decide the default one would have this policy bit on the network for the default
+     * subscription so that when both are active at the same time, the device chooses the
+     * network associated with the default subscription rather than the one with the best link
+     * quality (which will happen if policy doesn't dictate otherwise).
+     */
+    public static final int POLICY_DEFAULT_SUBSCRIPTION = 1 << 3;
+
+    /**
+     * Policy bits for this network. Filled in by the NetworkAgent.
+     */
+    private final int mPolicy;
+
+    /**
+     * Predicted metrics to the gateway (it contains latency and bandwidth to the gateway).
+     * This is filled by the NetworkAgent with the theoretical values of the link if available,
+     * although they may fill it with predictions from historical data if available.
+     * Note that while this member cannot be null, any and all of its members can be unknown.
+     */
     @NonNull
-    private final Bundle mExtensions;
+    private final Metrics mLinkLayerMetrics;
 
-    public NetworkScore() {
-        mExtensions = new Bundle();
-    }
+    /**
+     * Predicted metrics to representative servers.
+     * This is filled by connectivity with (if available) a best-effort estimate of the performance
+     * information to servers the user connects to in similar circumstances, and predicted from
+     * actual measurements if possible.
+     * Note that while this member cannot be null, any and all of its members can be unknown.
+     */
+    @NonNull
+    private final Metrics mEndToEndMetrics;
 
-    public NetworkScore(@NonNull NetworkScore source) {
-        mExtensions = new Bundle(source.mExtensions);
+    /** Value meaning the signal strength is unknown. */
+    public static final int UNKNOWN_SIGNAL_STRENGTH = -1;
+
+    /** The smallest meaningful signal strength. */
+    public static final int MIN_SIGNAL_STRENGTH = 0;
+
+    /** The largest meaningful signal strength. */
+    public static final int MAX_SIGNAL_STRENGTH = 1000;
+
+    /**
+     * User-visible measure of the strength of the signal normalized 1~1000.
+     * This represents a measure of the signal strength for this network.
+     * If unknown, this has value UNKNOWN_SIGNAL_STRENGTH.
+     */
+    // A good way to populate this value is to fill it with the number of bars displayed in
+    // the system UI, scaled 0 to 1000. This is what the user sees and it makes sense to them.
+    // Cellular for example could quantize the ASU value (see SignalStrength#getAsuValue) into
+    // this, while WiFi could scale the RSSI (see WifiManager#calculateSignalLevel).
+    @IntRange(from = UNKNOWN_SIGNAL_STRENGTH, to = MAX_SIGNAL_STRENGTH)
+    private final int mSignalStrength;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(flag = true, prefix = "RANGE_", value = {
+            RANGE_UNKNOWN, RANGE_CLOSE, RANGE_SHORT, RANGE_MEDIUM, RANGE_LONG
+    })
+    public @interface Range {
     }
 
     /**
-     * Put the value of parcelable inside the bundle by key.
+     * The range of this network is not known.
+     * This can be used by VPN the range of which depends on the range of the underlying network.
      */
-    public void putExtension(@Nullable String key, @Nullable Parcelable value) {
-        mExtensions.putParcelable(key, value);
+    public static final int RANGE_UNKNOWN = 0;
+
+    /**
+     * This network typically only operates at close range, like an NFC beacon.
+     */
+    public static final int RANGE_CLOSE = 1;
+
+    /**
+     * This network typically operates at a range of a few meters to a few dozen meters, like WiFi.
+     */
+    public static final int RANGE_SHORT = 2;
+
+    /**
+     * This network typically operates at a range of a few dozen to a few hundred meters, like CBRS.
+     */
+    public static final int RANGE_MEDIUM = 3;
+
+    /**
+     * This network typically offers continuous connectivity up to many kilometers away, like LTE.
+     */
+    public static final int RANGE_LONG = 4;
+
+    /**
+     * The typical range of this networking technology.
+     *
+     * This is one of the RANGE_* constants and is filled by the NetworkAgent.
+     * This may be useful when evaluating how desirable a network is, because for two networks that
+     * have equivalent performance and cost, the one that won't lose IP continuity when the user
+     * moves is probably preferable.
+     * Agents should fill this with the largest typical range this technology provides. See the
+     * descriptions of the individual constants for guidance.
+     *
+     * If unknown, this is set to RANGE_UNKNOWN.
+     */
+    @Range private final int mRange;
+
+    /**
+     * A prediction of whether this network is likely to be unusable in a few seconds.
+     *
+     * NetworkAgents set this to true to mean they are aware that usability is limited due to
+     * low signal strength, congestion, or other reasons, and indicates that the system should
+     * only use this network as a last resort. An example would be a WiFi network when the device
+     * is about to move outside of range.
+     *
+     * This is filled by the NetworkAgent. Agents that don't know whether this network is likely
+     * to be unusable soon should set this to false.
+     */
+    private final boolean mExiting;
+
+    /**
+     * The legacy score, as a migration strategy from Q to R.
+     * STOPSHIP : remove this before R ships.
+     */
+    private final int mLegacyScore;
+
+    /**
+     * Create a new NetworkScore object.
+     */
+    private NetworkScore(@Policy final int policy, @Nullable final Metrics l2Perf,
+            @Nullable final Metrics e2ePerf,
+            @IntRange(from = UNKNOWN_SIGNAL_STRENGTH, to = MAX_SIGNAL_STRENGTH)
+            final int signalStrength,
+            @Range final int range, final boolean exiting, final int legacyScore) {
+        mPolicy = policy;
+        mLinkLayerMetrics = null != l2Perf ? l2Perf : Metrics.EMPTY;
+        mEndToEndMetrics = null != e2ePerf ? e2ePerf : Metrics.EMPTY;
+        mSignalStrength = signalStrength;
+        mRange = range;
+        mExiting = exiting;
+        mLegacyScore = legacyScore;
     }
 
     /**
-     * Put the value of int inside the bundle by key.
+     * Utility function to return a copy of this with a different exiting value.
      */
-    public void putIntExtension(@Nullable String key, int value) {
-        mExtensions.putInt(key, value);
+    @NonNull public NetworkScore withExiting(final boolean exiting) {
+        return new NetworkScore(mPolicy, mLinkLayerMetrics, mEndToEndMetrics,
+                mSignalStrength, mRange, exiting, mLegacyScore);
     }
 
     /**
-     * Get the value of non primitive type by key.
+     * Utility function to return a copy of this with a different signal strength.
      */
-    public <T extends Parcelable> T getExtension(@Nullable String key) {
-        return mExtensions.getParcelable(key);
+    @NonNull public NetworkScore withSignalStrength(
+            @IntRange(from = UNKNOWN_SIGNAL_STRENGTH) final int signalStrength) {
+        return new NetworkScore(mPolicy, mLinkLayerMetrics, mEndToEndMetrics,
+                signalStrength, mRange, mExiting, mLegacyScore);
     }
 
     /**
-     * Get the value of int by key.
+     * Returns whether this network has a particular policy flag.
+     * @param policy the policy, as one of the POLICY_* constants.
      */
-    public int getIntExtension(@Nullable String key) {
-        return mExtensions.getInt(key);
+    public boolean hasPolicy(@Policy final int policy) {
+        return 0 != (mPolicy & policy);
     }
 
     /**
-     * Remove the entry by given key.
+     * Returns the Metrics representing the performance of the link layer.
+     *
+     * This contains the theoretical performance of the link, if available.
+     * Note that while this function cannot return null, any and/or all of the members of the
+     * returned object can be null if unknown.
      */
-    public void removeExtension(@Nullable String key) {
-        mExtensions.remove(key);
+    @NonNull public Metrics getLinkLayerMetrics() {
+        return mLinkLayerMetrics;
+    }
+
+    /**
+     * Returns the Metrics representing the end-to-end performance of the network.
+     *
+     * This contains the end-to-end performance of the link, if available.
+     * Note that while this function cannot return null, any and/or all of the members of the
+     * returned object can be null if unknown.
+     */
+    @NonNull public Metrics getEndToEndMetrics() {
+        return mEndToEndMetrics;
+    }
+
+    /**
+     * Returns the signal strength of this network normalized 0~1000, or UNKNOWN_SIGNAL_STRENGTH.
+     */
+    @IntRange(from = UNKNOWN_SIGNAL_STRENGTH, to = MAX_SIGNAL_STRENGTH)
+    public int getSignalStrength() {
+        return mSignalStrength;
+    }
+
+    /**
+     * Returns the typical range of this network technology as one of the RANGE_* constants.
+     */
+    @Range public int getRange() {
+        return mRange;
+    }
+
+    /** Returns a prediction of whether this network is likely to be unusable in a few seconds. */
+    public boolean isExiting() {
+        return mExiting;
+    }
+
+    /**
+     * Get the legacy score.
+     * @hide
+     */
+    public int getLegacyScore() {
+        return mLegacyScore;
+    }
+
+    /** Builder for NetworkScore. */
+    public static class Builder {
+        private int mPolicy = 0;
+        @NonNull
+        private Metrics mLinkLayerMetrics = new Metrics(Metrics.LATENCY_UNKNOWN,
+                Metrics.BANDWIDTH_UNKNOWN, Metrics.BANDWIDTH_UNKNOWN);
+        @NonNull
+        private Metrics mEndToMetrics = new Metrics(Metrics.LATENCY_UNKNOWN,
+                Metrics.BANDWIDTH_UNKNOWN, Metrics.BANDWIDTH_UNKNOWN);
+        private int mSignalStrength = UNKNOWN_SIGNAL_STRENGTH;
+        private int mRange = RANGE_UNKNOWN;
+        private boolean mExiting = false;
+        private int mLegacyScore = 0;
+        @NonNull private Bundle mExtensions = new Bundle();
+
+        /** Create a new builder. */
+        public Builder() { }
+
+        /** Add a policy flag. */
+        @NonNull public Builder addPolicy(@Policy final int policy) {
+            mPolicy |= policy;
+            return this;
+        }
+
+        /** Clear a policy flag */
+        @NonNull public Builder clearPolicy(@Policy final int policy) {
+            mPolicy &= ~policy;
+            return this;
+        }
+
+        /** Set the link layer metrics. */
+        @NonNull public Builder setLinkLayerMetrics(@NonNull final Metrics linkLayerMetrics) {
+            mLinkLayerMetrics = linkLayerMetrics;
+            return this;
+        }
+
+        /** Set the end-to-end metrics. */
+        @NonNull public Builder setEndToEndMetrics(@NonNull final Metrics endToEndMetrics) {
+            mEndToMetrics = endToEndMetrics;
+            return this;
+        }
+
+        /** Set the signal strength. */
+        @NonNull public Builder setSignalStrength(
+                @IntRange(from = UNKNOWN_SIGNAL_STRENGTH, to = MAX_SIGNAL_STRENGTH)
+                        final int signalStrength) {
+            mSignalStrength = signalStrength;
+            return this;
+        }
+
+        /** Set the range. */
+        @NonNull public Builder setRange(@Range final int range) {
+            mRange = range;
+            return this;
+        }
+
+        /** Set whether this network is exiting. */
+        @NonNull public Builder setExiting(final boolean exiting) {
+            mExiting = exiting;
+            return this;
+        }
+
+        /** Add a parcelable extension. */
+        @NonNull public Builder setLegacyScore(final int legacyScore) {
+            mLegacyScore = legacyScore;
+            return this;
+        }
+
+        /** Build the NetworkScore object represented by this builder. */
+        @NonNull public NetworkScore build() {
+            return new NetworkScore(mPolicy, mLinkLayerMetrics, mEndToMetrics,
+                    mSignalStrength, mRange, mExiting, mLegacyScore);
+        }
     }
 
     @Override
@@ -88,9 +429,17 @@
 
     @Override
     public void writeToParcel(@NonNull Parcel dest, int flags) {
-        synchronized (this) {
-            dest.writeBundle(mExtensions);
-        }
+        dest.writeInt(mPolicy);
+        dest.writeInt(mLinkLayerMetrics.latencyMs);
+        dest.writeInt(mLinkLayerMetrics.downlinkBandwidthKBps);
+        dest.writeInt(mLinkLayerMetrics.uplinkBandwidthKBps);
+        dest.writeInt(mEndToEndMetrics.latencyMs);
+        dest.writeInt(mEndToEndMetrics.downlinkBandwidthKBps);
+        dest.writeInt(mEndToEndMetrics.uplinkBandwidthKBps);
+        dest.writeInt(mSignalStrength);
+        dest.writeInt(mRange);
+        dest.writeBoolean(mExiting);
+        dest.writeInt(mLegacyScore);
     }
 
     public static final @NonNull Creator<NetworkScore> CREATOR = new Creator<NetworkScore>() {
@@ -106,57 +455,52 @@
     };
 
     private NetworkScore(@NonNull Parcel in) {
-        mExtensions = in.readBundle();
+        mPolicy = in.readInt();
+        mLinkLayerMetrics = new Metrics(in.readInt(), in.readInt(), in.readInt());
+        mEndToEndMetrics = new Metrics(in.readInt(), in.readInt(), in.readInt());
+        mSignalStrength = in.readInt();
+        mRange = in.readInt();
+        mExiting = in.readBoolean();
+        mLegacyScore = in.readInt();
     }
 
-    // TODO: Modify this method once new fields are added into this class.
     @Override
     public boolean equals(@Nullable Object obj) {
         if (!(obj instanceof NetworkScore)) {
             return false;
         }
         final NetworkScore other = (NetworkScore) obj;
-        return bundlesEqual(mExtensions, other.mExtensions);
+        return mPolicy == other.mPolicy
+                && mLinkLayerMetrics.latencyMs == other.mLinkLayerMetrics.latencyMs
+                && mLinkLayerMetrics.uplinkBandwidthKBps
+                == other.mLinkLayerMetrics.uplinkBandwidthKBps
+                && mEndToEndMetrics.latencyMs == other.mEndToEndMetrics.latencyMs
+                && mEndToEndMetrics.uplinkBandwidthKBps
+                == other.mEndToEndMetrics.uplinkBandwidthKBps
+                && mSignalStrength == other.mSignalStrength
+                && mRange == other.mRange
+                && mExiting == other.mExiting
+                && mLegacyScore == other.mLegacyScore;
     }
 
     @Override
     public int hashCode() {
-        int result = 29;
-        for (String key : mExtensions.keySet()) {
-            final Object value = mExtensions.get(key);
-            // The key may be null, so call Objects.hash() is safer.
-            result += 31 * value.hashCode() + 37 * Objects.hash(key);
-        }
-        return result;
-    }
-
-    // mExtensions won't be null since the constructor will create it.
-    private boolean bundlesEqual(@NonNull Bundle bundle1, @NonNull Bundle bundle2) {
-        if (bundle1 == bundle2) {
-            return true;
-        }
-
-        // This is unlikely but it's fine to add this clause here.
-        if (null == bundle1 || null == bundle2) {
-            return false;
-        }
-
-        if (bundle1.size() != bundle2.size()) {
-            return false;
-        }
-
-        for (String key : bundle1.keySet()) {
-            final Object value1 = bundle1.get(key);
-            final Object value2 = bundle2.get(key);
-            if (!Objects.equals(value1, value2)) {
-                return false;
-            }
-        }
-        return true;
+        return Objects.hash(mPolicy,
+                mLinkLayerMetrics.latencyMs, mLinkLayerMetrics.uplinkBandwidthKBps,
+                mEndToEndMetrics.latencyMs, mEndToEndMetrics.uplinkBandwidthKBps,
+                mSignalStrength, mRange, mExiting, mLegacyScore);
     }
 
     /** Convert to a string */
     public String toString() {
-        return "NetworkScore[" + mExtensions.toString() + "]";
+        return "NetworkScore ["
+                + "Policy = " + mPolicy
+                + " LinkLayerMetrics = " + mLinkLayerMetrics
+                + " EndToEndMetrics = " + mEndToEndMetrics
+                + " SignalStrength = " + mSignalStrength
+                + " Range = " + mRange
+                + " Exiting = " + mExiting
+                + " LegacyScore = " + mLegacyScore
+                + "]";
     }
 }
diff --git a/core/java/android/os/VibrationAttributes.java b/core/java/android/os/VibrationAttributes.java
index 3e16640..27782fa 100644
--- a/core/java/android/os/VibrationAttributes.java
+++ b/core/java/android/os/VibrationAttributes.java
@@ -391,11 +391,23 @@
          * Replaces flags
          * @param flags any combination of flags.
          * @return the same Builder instance.
+         * @hide
          */
         public @NonNull Builder replaceFlags(int flags) {
             mFlags = flags;
             return this;
         }
+
+        /**
+         * Set flags
+         * @param flags combination of flags to be set.
+         * @param mask Bit range that should be changed.
+         * @return the same Builder instance.
+         */
+        public @NonNull Builder setFlags(int flags, int mask) {
+            mFlags = (mFlags & ~mask) | (flags & mask);
+            return this;
+        }
     }
 }
 
diff --git a/core/java/android/telephony/PhoneStateListener.java b/core/java/android/telephony/PhoneStateListener.java
index 023f000..531ade0 100644
--- a/core/java/android/telephony/PhoneStateListener.java
+++ b/core/java/android/telephony/PhoneStateListener.java
@@ -168,9 +168,11 @@
     public static final int LISTEN_SIGNAL_STRENGTHS                         = 0x00000100;
 
     /**
-     * Listen for always reported changes of the network signal strengths (cellular),
+     * Listen for changes of the network signal strengths (cellular) always reported from modem,
      * even in some situations such as the screen of the device is off.
      *
+     * @see #onSignalStrengthsChanged
+     *
      * @hide
      */
     @RequiresPermission(android.Manifest.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH)
diff --git a/core/java/android/timezone/CountryTimeZones.java b/core/java/android/timezone/CountryTimeZones.java
index e5bbdf4..5875761 100644
--- a/core/java/android/timezone/CountryTimeZones.java
+++ b/core/java/android/timezone/CountryTimeZones.java
@@ -56,7 +56,7 @@
          */
         @NonNull
         public String getTimeZoneId() {
-            return mDelegate.timeZoneId;
+            return mDelegate.getTimeZoneId();
         }
 
         /**
@@ -187,7 +187,7 @@
      * would be a good choice <em>generally</em> when there's no other information available.
      */
     public boolean isDefaultTimeZoneBoosted() {
-        return mDelegate.getDefaultTimeZoneBoost();
+        return mDelegate.isDefaultTimeZoneBoosted();
     }
 
     /**
@@ -220,7 +220,8 @@
                 mDelegate.lookupByOffsetWithBias(
                         totalOffsetMillis, isDst, dstOffsetMillis, whenMillis, bias);
         return delegateOffsetResult == null ? null :
-                new OffsetResult(delegateOffsetResult.mTimeZone, delegateOffsetResult.mOneMatch);
+                new OffsetResult(
+                        delegateOffsetResult.getTimeZone(), delegateOffsetResult.isOnlyMatch());
     }
 
     /**
diff --git a/core/java/android/timezone/TzDataSetVersion.java b/core/java/android/timezone/TzDataSetVersion.java
index 605155e..efe50a0 100644
--- a/core/java/android/timezone/TzDataSetVersion.java
+++ b/core/java/android/timezone/TzDataSetVersion.java
@@ -111,23 +111,23 @@
 
     /** Returns the major version number. See {@link TzDataSetVersion}. */
     public int getFormatMajorVersion() {
-        return mDelegate.formatMajorVersion;
+        return mDelegate.getFormatMajorVersion();
     }
 
     /** Returns the minor version number. See {@link TzDataSetVersion}. */
     public int getFormatMinorVersion() {
-        return mDelegate.formatMinorVersion;
+        return mDelegate.getFormatMinorVersion();
     }
 
     /** Returns the tzdb version string. See {@link TzDataSetVersion}. */
     @NonNull
     public String getRulesVersion() {
-        return mDelegate.rulesVersion;
+        return mDelegate.getRulesVersion();
     }
 
     /** Returns the Android revision. See {@link TzDataSetVersion}. */
     public int getRevision() {
-        return mDelegate.revision;
+        return mDelegate.getRevision();
     }
 
     @Override
diff --git a/core/java/android/util/TimeUtils.java b/core/java/android/util/TimeUtils.java
index c325874..36f4e53 100644
--- a/core/java/android/util/TimeUtils.java
+++ b/core/java/android/util/TimeUtils.java
@@ -81,7 +81,7 @@
         }
         CountryTimeZones.OffsetResult offsetResult = countryTimeZones.lookupByOffsetWithBias(
                 offsetMillis, isDst, null /* dstOffsetMillis */, whenMillis, bias);
-        return offsetResult != null ? offsetResult.mTimeZone : null;
+        return offsetResult != null ? offsetResult.getTimeZone() : null;
     }
 
     /**
@@ -109,8 +109,8 @@
 
         List<String> timeZoneIds = new ArrayList<>();
         for (TimeZoneMapping timeZoneMapping : countryTimeZones.getTimeZoneMappings()) {
-            if (timeZoneMapping.showInPicker) {
-                timeZoneIds.add(timeZoneMapping.timeZoneId);
+            if (timeZoneMapping.isShownInPicker()) {
+                timeZoneIds.add(timeZoneMapping.getTimeZoneId());
             }
         }
         return Collections.unmodifiableList(timeZoneIds);
diff --git a/core/java/android/view/ITaskOrganizer.aidl b/core/java/android/view/ITaskOrganizer.aidl
index e92aafe..5ccdd30 100644
--- a/core/java/android/view/ITaskOrganizer.aidl
+++ b/core/java/android/view/ITaskOrganizer.aidl
@@ -26,8 +26,7 @@
  * {@hide}
  */
 oneway interface ITaskOrganizer {
-    void taskAppeared(in IWindowContainer container,
-        in ActivityManager.RunningTaskInfo taskInfo);
+    void taskAppeared(in ActivityManager.RunningTaskInfo taskInfo);
     void taskVanished(in IWindowContainer container);
 
     /**
@@ -35,4 +34,19 @@
      * ActivityTaskManagerService#applyTaskOrganizerTransaction
      */
     void transactionReady(int id, in SurfaceControl.Transaction t);
+
+    /**
+     * Will fire when core attributes of a Task's info change. Relevant properties include the
+     * {@link WindowConfiguration.ActivityType} and whether it is resizable.
+     *
+     * This is used, for example, during split-screen. The flow for starting is: Something sends an
+     * Intent with windowingmode. Then WM finds a matching root task and launches the new task into
+     * it. This causes the root task's info to change because now it has a task when it didn't
+     * before. The default Divider implementation interprets this as a request to enter
+     * split-screen mode and will move all other Tasks into the secondary root task. When WM
+     * applies this change, it triggers an info change in the secondary root task because it now
+     * has children. The Divider impl looks at the info and can see that the secondary root task
+     * has adopted an ActivityType of HOME and proceeds to show the minimized dock UX.
+     */
+    void onTaskInfoChanged(in ActivityManager.RunningTaskInfo info);
 }
\ No newline at end of file
diff --git a/core/java/android/view/InsetsSource.java b/core/java/android/view/InsetsSource.java
index 37034ee..77bc25b 100644
--- a/core/java/android/view/InsetsSource.java
+++ b/core/java/android/view/InsetsSource.java
@@ -53,6 +53,9 @@
         mType = other.mType;
         mFrame = new Rect(other.mFrame);
         mVisible = other.mVisible;
+        mVisibleFrame = other.mVisibleFrame != null
+                ? new Rect(other.mVisibleFrame)
+                : null;
     }
 
     public void setFrame(Rect frame) {
@@ -165,6 +168,7 @@
     public int hashCode() {
         int result = mType;
         result = 31 * result + mFrame.hashCode();
+        result = 31 * result + (mVisibleFrame != null ? mVisibleFrame.hashCode() : 0);
         result = 31 * result + (mVisible ? 1 : 0);
         return result;
     }
diff --git a/core/java/android/view/WindowContainerTransaction.java b/core/java/android/view/WindowContainerTransaction.java
index c55caa3..33f21f2 100644
--- a/core/java/android/view/WindowContainerTransaction.java
+++ b/core/java/android/view/WindowContainerTransaction.java
@@ -86,6 +86,17 @@
         return this;
     }
 
+    /**
+     * Set the smallestScreenWidth of a container.
+     */
+    public WindowContainerTransaction setSmallestScreenWidthDp(IWindowContainer container,
+            int widthDp) {
+        Change cfg = getOrCreateChange(container.asBinder());
+        cfg.mConfiguration.smallestScreenWidthDp = widthDp;
+        cfg.mConfigSetMask |= ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE;
+        return this;
+    }
+
     public Map<IBinder, Change> getChanges() {
         return mChanges;
     }
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index f13a638..863e8a2 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -2125,8 +2125,9 @@
                 return new Pair<>(Insets.NONE, insets);
             }
 
-            boolean includeIme = (getAttributes().softInputMode & SOFT_INPUT_MASK_ADJUST)
-                    == SOFT_INPUT_ADJUST_RESIZE;
+            boolean includeIme =
+                    (getViewRootImpl().mWindowAttributes.softInputMode & SOFT_INPUT_MASK_ADJUST)
+                            == SOFT_INPUT_ADJUST_RESIZE;
             Insets insetsToApply;
             if (ViewRootImpl.sNewInsetsMode == 0) {
                 insetsToApply = insets.getSystemWindowInsets();
diff --git a/core/java/com/android/internal/util/Preconditions.java b/core/java/com/android/internal/util/Preconditions.java
index 408a7a8..a87e080 100644
--- a/core/java/com/android/internal/util/Preconditions.java
+++ b/core/java/com/android/internal/util/Preconditions.java
@@ -21,7 +21,9 @@
 import android.compat.annotation.UnsupportedAppUsage;
 import android.text.TextUtils;
 
+import java.util.Arrays;
 import java.util.Collection;
+import java.util.Objects;
 
 /**
  * Simple static methods to be called at the start of your own methods to verify
@@ -497,6 +499,64 @@
     }
 
     /**
+     * Ensures that the given byte array is not {@code null}, and contains at least one element.
+     *
+     * @param value an array of elements.
+     * @param valueName the name of the argument to use if the check fails.
+
+     * @return the validated array
+     *
+     * @throws NullPointerException if the {@code value} was {@code null}
+     * @throws IllegalArgumentException if the {@code value} was empty
+     */
+    @NonNull
+    public static byte[] checkByteArrayNotEmpty(final byte[] value, final String valueName) {
+        if (value == null) {
+            throw new NullPointerException(valueName + " must not be null");
+        }
+        if (value.length == 0) {
+            throw new IllegalArgumentException(valueName + " is empty");
+        }
+        return value;
+    }
+
+    /**
+     * Ensures that argument {@code value} is one of {@code supportedValues}.
+     *
+     * @param supportedValues an array of string values
+     * @param value a string value
+     *
+     * @return the validated value
+     *
+     * @throws NullPointerException if either {@code value} or {@code supportedValues} is null
+     * @throws IllegalArgumentException if the {@code value} is not in {@code supportedValues}
+     */
+    @NonNull
+    public static String checkArgumentIsSupported(final String[] supportedValues,
+            final String value) {
+        checkNotNull(value);
+        checkNotNull(supportedValues);
+
+        if (!contains(supportedValues, value)) {
+            throw new IllegalArgumentException(value + "is not supported "
+                    + Arrays.toString(supportedValues));
+        }
+        return value;
+    }
+
+    private static boolean contains(String[] values, String value) {
+        if (values == null) {
+            return false;
+        }
+        for (int i = 0; i < values.length; ++i) {
+            if (Objects.equals(value, values[i])) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
      * Ensures that all elements in the argument floating point array are within the inclusive range
      *
      * <p>While this can be used to range check against +/- infinity, note that all NaN numbers
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index a2f514a..c04efa3 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -147,7 +147,6 @@
                 "android_service_DataLoaderService.cpp",
                 "android_util_AssetManager.cpp",
                 "android_util_Binder.cpp",
-                "android_util_StatsLog.cpp",
                 "android_util_MemoryIntArray.cpp",
                 "android_util_Process.cpp",
                 "android_util_StringBlock.cpp",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 9783b65..924cd81 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -108,7 +108,6 @@
 extern int register_android_app_admin_SecurityLog(JNIEnv* env);
 extern int register_android_content_AssetManager(JNIEnv* env);
 extern int register_android_util_EventLog(JNIEnv* env);
-extern int register_android_util_StatsLog(JNIEnv* env);
 extern int register_android_util_StatsLogInternal(JNIEnv* env);
 extern int register_android_util_Log(JNIEnv* env);
 extern int register_android_util_MemoryIntArray(JNIEnv* env);
@@ -622,6 +621,8 @@
     char jitprithreadweightOptBuf[sizeof("-Xjitprithreadweight:")-1 + PROPERTY_VALUE_MAX];
     char jittransitionweightOptBuf[sizeof("-Xjittransitionweight:")-1 + PROPERTY_VALUE_MAX];
     char hotstartupsamplesOptsBuf[sizeof("-Xps-hot-startup-method-samples:")-1 + PROPERTY_VALUE_MAX];
+    char saveResolvedClassesDelayMsOptsBuf[
+            sizeof("-Xps-save-resolved-classes-delay-ms:")-1 + PROPERTY_VALUE_MAX];
     char madviseRandomOptsBuf[sizeof("-XX:MadviseRandomAccess:")-1 + PROPERTY_VALUE_MAX];
     char gctypeOptsBuf[sizeof("-Xgc:")-1 + PROPERTY_VALUE_MAX];
     char backgroundgcOptsBuf[sizeof("-XX:BackgroundGC=")-1 + PROPERTY_VALUE_MAX];
@@ -817,6 +818,9 @@
     parseRuntimeOption("dalvik.vm.hot-startup-method-samples", hotstartupsamplesOptsBuf,
             "-Xps-hot-startup-method-samples:");
 
+    parseRuntimeOption("dalvik.vm.ps-resolved-classes-delay-ms", saveResolvedClassesDelayMsOptsBuf,
+            "-Xps-save-resolved-classes-delay-ms:");
+
     property_get("ro.config.low_ram", propBuf, "");
     if (strcmp(propBuf, "true") == 0) {
       addOption("-XX:LowMemoryMode");
@@ -1441,7 +1445,6 @@
     REG_JNI(register_android_util_EventLog),
     REG_JNI(register_android_util_Log),
     REG_JNI(register_android_util_MemoryIntArray),
-    REG_JNI(register_android_util_StatsLog),
     REG_JNI(register_android_util_StatsLogInternal),
     REG_JNI(register_android_app_admin_SecurityLog),
     REG_JNI(register_android_content_AssetManager),
diff --git a/core/jni/fd_utils.cpp b/core/jni/fd_utils.cpp
index 1fcc8ac..cfd3c09 100644
--- a/core/jni/fd_utils.cpp
+++ b/core/jni/fd_utils.cpp
@@ -51,6 +51,7 @@
         "/dev/socket/webview_zygote",
         "/dev/socket/heapprofd",
         "/sys/kernel/debug/tracing/trace_marker",
+        "/sys/kernel/tracing/trace_marker",
         "/system/framework/framework-res.apk",
         "/dev/urandom",
         "/dev/ion",
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 181a32d..3a1b63d 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -4858,19 +4858,6 @@
     <permission android:name="android.permission.ACCESS_SHARED_LIBRARIES"
                 android:protectionLevel="signature|installer" />
 
-    <!-- Allows an app to log compat change usage.
-         @hide  <p>Not for use by third-party applications.</p> -->
-    <permission android:name="android.permission.LOG_COMPAT_CHANGE"
-                android:protectionLevel="signature" />
-    <!-- Allows an app to read compat change config.
-         @hide  <p>Not for use by third-party applications.</p> -->
-    <permission android:name="android.permission.READ_COMPAT_CHANGE_CONFIG"
-                android:protectionLevel="signature" />
-    <!-- Allows an app to override compat change config.
-         @hide  <p>Not for use by third-party applications.</p> -->
-    <permission android:name="android.permission.OVERRIDE_COMPAT_CHANGE_CONFIG"
-                android:protectionLevel="signature" />
-
     <!-- Allows input events to be monitored. Very dangerous!  @hide -->
     <permission android:name="android.permission.MONITOR_INPUT"
                 android:protectionLevel="signature" />
diff --git a/core/tests/coretests/src/android/os/PowerManagerTest.java b/core/tests/coretests/src/android/os/PowerManagerTest.java
index 5cb7852..ea0a0fd 100644
--- a/core/tests/coretests/src/android/os/PowerManagerTest.java
+++ b/core/tests/coretests/src/android/os/PowerManagerTest.java
@@ -214,6 +214,9 @@
         }
         // Add listener2 on main thread.
         mPm.addThermalStatusListener(mListener2);
+        verify(mListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC)
+            .times(1)).onThermalStatusChanged(status);
+        reset(mListener2);
         status = PowerManager.THERMAL_STATUS_MODERATE;
         mUiDevice.executeShellCommand("cmd thermalservice override-status "
                 + Integer.toString(status));
diff --git a/core/tests/coretests/src/android/view/InsetsStateTest.java b/core/tests/coretests/src/android/view/InsetsStateTest.java
index 20be8c2..79ac2b7 100644
--- a/core/tests/coretests/src/android/view/InsetsStateTest.java
+++ b/core/tests/coretests/src/android/view/InsetsStateTest.java
@@ -213,6 +213,7 @@
     @Test
     public void testParcelUnparcel() {
         mState.getSource(ITYPE_IME).setFrame(new Rect(0, 0, 100, 100));
+        mState.getSource(ITYPE_IME).setVisibleFrame(new Rect(0, 0, 50, 10));
         mState.getSource(ITYPE_IME).setVisible(true);
         mState.getSource(ITYPE_STATUS_BAR).setFrame(new Rect(0, 0, 100, 100));
         Parcel p = Parcel.obtain();
@@ -224,6 +225,16 @@
     }
 
     @Test
+    public void testCopy() {
+        mState.getSource(ITYPE_IME).setFrame(new Rect(0, 0, 100, 100));
+        mState.getSource(ITYPE_IME).setVisibleFrame(new Rect(0, 0, 50, 10));
+        mState.getSource(ITYPE_IME).setVisible(true);
+        mState.getSource(ITYPE_STATUS_BAR).setFrame(new Rect(0, 0, 100, 100));
+        mState2.set(mState, true);
+        assertEquals(mState, mState2);
+    }
+
+    @Test
     public void testGetDefaultVisibility() {
         assertTrue(InsetsState.getDefaultVisibility(ITYPE_STATUS_BAR));
         assertTrue(InsetsState.getDefaultVisibility(ITYPE_NAVIGATION_BAR));
diff --git a/data/etc/car/com.android.car.secondaryhome.xml b/data/etc/car/com.android.car.secondaryhome.xml
new file mode 100644
index 0000000..c74b86e
--- /dev/null
+++ b/data/etc/car/com.android.car.secondaryhome.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2020 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
+  -->
+<permissions>
+    <privapp-permissions package="com.android.car.secondaryhome">
+        <!-- Required to launch app in ActivityView -->
+        <permission name="android.permission.ACTIVITY_EMBEDDING"/>
+        <!-- Required to send notification to current user-->
+        <permission name="android.permission.MANAGE_USERS"/>
+    </privapp-permissions>
+</permissions>
diff --git a/libs/hwui/pipeline/skia/ATraceMemoryDump.cpp b/libs/hwui/pipeline/skia/ATraceMemoryDump.cpp
index 551bdc6..234f42d 100644
--- a/libs/hwui/pipeline/skia/ATraceMemoryDump.cpp
+++ b/libs/hwui/pipeline/skia/ATraceMemoryDump.cpp
@@ -39,14 +39,13 @@
  * ATraceMemoryDump calculates memory category first by looking at the "type" string passed to
  * dumpStringValue and then by looking at "backingType" passed to setMemoryBacking.
  * Only GPU Texture memory is tracked separately and everything else is grouped as one
- * "GPU Memory" category.
+ * "Misc Memory" category.
  */
 static std::unordered_map<const char*, const char*> sResourceMap = {
         {"malloc", "HWUI CPU Memory"},          // taken from setMemoryBacking(backingType)
         {"gl_texture", "HWUI Texture Memory"},  // taken from setMemoryBacking(backingType)
-        {"Texture",
-         "HWUI Texture Memory"},  // taken from dumpStringValue(value, valueName="type")
-        // Uncomment categories below to split "GPU Memory" into more brackets for debugging.
+        {"Texture", "HWUI Texture Memory"},  // taken from dumpStringValue(value, valueName="type")
+        // Uncomment categories below to split "Misc Memory" into more brackets for debugging.
         /*{"vk_buffer", "vk_buffer"},
         {"gl_renderbuffer", "gl_renderbuffer"},
         {"gl_buffer", "gl_buffer"},
@@ -169,8 +168,8 @@
     mLastDumpValue = 0;
     mLastPurgeableDumpValue = INVALID_MEMORY_SIZE;
     mLastDumpName = dumpName;
-    // Categories not listed in sResourceMap are reported as "GPU memory"
-    mCategory = "HWUI GPU Memory";
+    // Categories not listed in sResourceMap are reported as "Misc Memory"
+    mCategory = "HWUI Misc Memory";
 }
 
 } /* namespace skiapipeline */
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index 5993e17..699b96a 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -143,10 +143,11 @@
     ATRACE_CALL();
 
     if (surface) {
-        mNativeSurface = new ReliableSurface{std::move(surface)};
+        mNativeSurface = std::make_unique<ReliableSurface>(std::move(surface));
+        mNativeSurface->init();
         if (enableTimeout) {
             // TODO: Fix error handling & re-shorten timeout
-            ANativeWindow_setDequeueTimeout(mNativeSurface.get(), 4000_ms);
+            ANativeWindow_setDequeueTimeout(mNativeSurface->getNativeWindow(), 4000_ms);
         }
     } else {
         mNativeSurface = nullptr;
@@ -161,8 +162,9 @@
     }
 
     ColorMode colorMode = mWideColorGamut ? ColorMode::WideColorGamut : ColorMode::SRGB;
-    bool hasSurface = mRenderPipeline->setSurface(mNativeSurface.get(), mSwapBehavior, colorMode,
-                                                  mRenderAheadCapacity);
+    bool hasSurface = mRenderPipeline->setSurface(
+            mNativeSurface ? mNativeSurface->getNativeWindow() : nullptr, mSwapBehavior, colorMode,
+            mRenderAheadCapacity);
 
     mFrameNumber = -1;
 
@@ -203,7 +205,8 @@
 
 void CanvasContext::allocateBuffers() {
     if (mNativeSurface) {
-        mNativeSurface->allocateBuffers();
+        ANativeWindow* anw = mNativeSurface->getNativeWindow();
+        ANativeWindow_allocateBuffers(anw);
     }
 }
 
@@ -428,7 +431,7 @@
         presentTime = mCurrentFrameInfo->get(FrameInfoIndex::Vsync) +
                 (frameIntervalNanos * (renderAhead + 1));
     }
-    native_window_set_buffers_timestamp(mNativeSurface.get(), presentTime);
+    native_window_set_buffers_timestamp(mNativeSurface->getNativeWindow(), presentTime);
 }
 
 void CanvasContext::draw() {
@@ -489,16 +492,18 @@
         swap.swapCompletedTime = systemTime(SYSTEM_TIME_MONOTONIC);
         swap.vsyncTime = mRenderThread.timeLord().latestVsync();
         if (didDraw) {
-            nsecs_t dequeueStart = ANativeWindow_getLastDequeueStartTime(mNativeSurface.get());
+            nsecs_t dequeueStart =
+                    ANativeWindow_getLastDequeueStartTime(mNativeSurface->getNativeWindow());
             if (dequeueStart < mCurrentFrameInfo->get(FrameInfoIndex::SyncStart)) {
                 // Ignoring dequeue duration as it happened prior to frame render start
                 // and thus is not part of the frame.
                 swap.dequeueDuration = 0;
             } else {
                 swap.dequeueDuration =
-                        ANativeWindow_getLastDequeueDuration(mNativeSurface.get());
+                        ANativeWindow_getLastDequeueDuration(mNativeSurface->getNativeWindow());
             }
-            swap.queueDuration = ANativeWindow_getLastQueueDuration(mNativeSurface.get());
+            swap.queueDuration =
+                    ANativeWindow_getLastQueueDuration(mNativeSurface->getNativeWindow());
         } else {
             swap.dequeueDuration = 0;
             swap.queueDuration = 0;
@@ -567,14 +572,16 @@
 }
 
 SkISize CanvasContext::getNextFrameSize() const {
-    ReliableSurface* surface = mNativeSurface.get();
-    if (surface) {
-        SkISize size;
-        size.fWidth = ANativeWindow_getWidth(surface);
-        size.fHeight = ANativeWindow_getHeight(surface);
-        return size;
+    static constexpr SkISize defaultFrameSize = {INT32_MAX, INT32_MAX};
+    if (mNativeSurface == nullptr) {
+        return defaultFrameSize;
     }
-    return {INT32_MAX, INT32_MAX};
+    ANativeWindow* anw = mNativeSurface->getNativeWindow();
+
+    SkISize size;
+    size.fWidth = ANativeWindow_getWidth(anw);
+    size.fHeight = ANativeWindow_getHeight(anw);
+    return size;
 }
 
 void CanvasContext::prepareAndDraw(RenderNode* node) {
@@ -702,11 +709,9 @@
     if (!mNativeSurface) return false;
     if (mHaveNewSurface) return true;
 
-    int width = -1;
-    int height = -1;
-    ReliableSurface* surface = mNativeSurface.get();
-    surface->query(NATIVE_WINDOW_WIDTH, &width);
-    surface->query(NATIVE_WINDOW_HEIGHT, &height);
+    ANativeWindow* anw = mNativeSurface->getNativeWindow();
+    const int width = ANativeWindow_getWidth(anw);
+    const int height = ANativeWindow_getHeight(anw);
 
     return width != mLastFrameWidth || height != mLastFrameHeight;
 }
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index 4490f80..0967b20 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -220,7 +220,7 @@
     int32_t mLastFrameHeight = 0;
 
     RenderThread& mRenderThread;
-    sp<ReliableSurface> mNativeSurface;
+    std::unique_ptr<ReliableSurface> mNativeSurface;
     // stopped indicates the CanvasContext will reject actual redraw operations,
     // and defer repaint until it is un-stopped
     bool mStopped = false;
diff --git a/libs/hwui/renderthread/ReliableSurface.cpp b/libs/hwui/renderthread/ReliableSurface.cpp
index 864780f..e92500f 100644
--- a/libs/hwui/renderthread/ReliableSurface.cpp
+++ b/libs/hwui/renderthread/ReliableSurface.cpp
@@ -26,64 +26,38 @@
 // to propagate this error back to the caller
 constexpr bool DISABLE_BUFFER_PREFETCH = true;
 
-// TODO: Make surface less protected
-// This exists because perform is a varargs, and ANativeWindow has no va_list perform.
-// So wrapping/chaining that is hard. Telling the compiler to ignore protected is easy, so we do
-// that instead
-struct SurfaceExposer : Surface {
-    // Make warnings happy
-    SurfaceExposer() = delete;
-
-    using Surface::cancelBuffer;
-    using Surface::dequeueBuffer;
-    using Surface::lockBuffer_DEPRECATED;
-    using Surface::perform;
-    using Surface::queueBuffer;
-    using Surface::setBufferCount;
-    using Surface::setSwapInterval;
-};
-
-#define callProtected(surface, func, ...) ((*surface).*&SurfaceExposer::func)(__VA_ARGS__)
-
 ReliableSurface::ReliableSurface(sp<Surface>&& surface) : mSurface(std::move(surface)) {
     LOG_ALWAYS_FATAL_IF(!mSurface, "Error, unable to wrap a nullptr");
-
-    ANativeWindow::setSwapInterval = hook_setSwapInterval;
-    ANativeWindow::dequeueBuffer = hook_dequeueBuffer;
-    ANativeWindow::cancelBuffer = hook_cancelBuffer;
-    ANativeWindow::queueBuffer = hook_queueBuffer;
-    ANativeWindow::query = hook_query;
-    ANativeWindow::perform = hook_perform;
-
-    ANativeWindow::dequeueBuffer_DEPRECATED = hook_dequeueBuffer_DEPRECATED;
-    ANativeWindow::cancelBuffer_DEPRECATED = hook_cancelBuffer_DEPRECATED;
-    ANativeWindow::lockBuffer_DEPRECATED = hook_lockBuffer_DEPRECATED;
-    ANativeWindow::queueBuffer_DEPRECATED = hook_queueBuffer_DEPRECATED;
 }
 
 ReliableSurface::~ReliableSurface() {
     clearReservedBuffer();
+    // Clear out the interceptors for proper hygiene.
+    // As a concrete example, if the underlying ANativeWindow is associated with
+    // an EGLSurface that is still in use, then if we don't clear out the
+    // interceptors then we walk into undefined behavior.
+    ANativeWindow_setCancelBufferInterceptor(mSurface.get(), nullptr, nullptr);
+    ANativeWindow_setDequeueBufferInterceptor(mSurface.get(), nullptr, nullptr);
+    ANativeWindow_setQueueBufferInterceptor(mSurface.get(), nullptr, nullptr);
+    ANativeWindow_setPerformInterceptor(mSurface.get(), nullptr, nullptr);
 }
 
-void ReliableSurface::perform(int operation, va_list args) {
-    std::lock_guard _lock{mMutex};
+void ReliableSurface::init() {
+    int result = ANativeWindow_setCancelBufferInterceptor(mSurface.get(), hook_cancelBuffer, this);
+    LOG_ALWAYS_FATAL_IF(result != NO_ERROR, "Failed to set cancelBuffer interceptor: error = %d",
+                        result);
 
-    switch (operation) {
-        case NATIVE_WINDOW_SET_USAGE:
-            mUsage = va_arg(args, uint32_t);
-            break;
-        case NATIVE_WINDOW_SET_USAGE64:
-            mUsage = va_arg(args, uint64_t);
-            break;
-        case NATIVE_WINDOW_SET_BUFFERS_GEOMETRY:
-            /* width */ va_arg(args, uint32_t);
-            /* height */ va_arg(args, uint32_t);
-            mFormat = va_arg(args, PixelFormat);
-            break;
-        case NATIVE_WINDOW_SET_BUFFERS_FORMAT:
-            mFormat = va_arg(args, PixelFormat);
-            break;
-    }
+    result = ANativeWindow_setDequeueBufferInterceptor(mSurface.get(), hook_dequeueBuffer, this);
+    LOG_ALWAYS_FATAL_IF(result != NO_ERROR, "Failed to set dequeueBuffer interceptor: error = %d",
+                        result);
+
+    result = ANativeWindow_setQueueBufferInterceptor(mSurface.get(), hook_queueBuffer, this);
+    LOG_ALWAYS_FATAL_IF(result != NO_ERROR, "Failed to set queueBuffer interceptor: error = %d",
+                        result);
+
+    result = ANativeWindow_setPerformInterceptor(mSurface.get(), hook_perform, this);
+    LOG_ALWAYS_FATAL_IF(result != NO_ERROR, "Failed to set perform interceptor: error = %d",
+                        result);
 }
 
 int ReliableSurface::reserveNext() {
@@ -111,7 +85,9 @@
 
     int fenceFd = -1;
     ANativeWindowBuffer* buffer = nullptr;
-    int result = callProtected(mSurface, dequeueBuffer, &buffer, &fenceFd);
+
+    // Note that this calls back into our own hooked method.
+    int result = ANativeWindow_dequeueBuffer(mSurface.get(), &buffer, &fenceFd);
 
     {
         std::lock_guard _lock{mMutex};
@@ -138,61 +114,13 @@
         mHasDequeuedBuffer = false;
     }
     if (buffer) {
-        callProtected(mSurface, cancelBuffer, buffer, releaseFd);
+        // Note that clearReservedBuffer may be reentrant here, so
+        // mReservedBuffer must be cleared once we reach here to avoid recursing
+        // forever.
+        ANativeWindow_cancelBuffer(mSurface.get(), buffer, releaseFd);
     }
 }
 
-int ReliableSurface::cancelBuffer(ANativeWindowBuffer* buffer, int fenceFd) {
-    clearReservedBuffer();
-    if (isFallbackBuffer(buffer)) {
-        if (fenceFd > 0) {
-            close(fenceFd);
-        }
-        return OK;
-    }
-    int result = callProtected(mSurface, cancelBuffer, buffer, fenceFd);
-    return result;
-}
-
-int ReliableSurface::dequeueBuffer(ANativeWindowBuffer** buffer, int* fenceFd) {
-    {
-        std::lock_guard _lock{mMutex};
-        if (mReservedBuffer) {
-            *buffer = mReservedBuffer;
-            *fenceFd = mReservedFenceFd.release();
-            mReservedBuffer = nullptr;
-            return OK;
-        }
-    }
-
-
-    int result = callProtected(mSurface, dequeueBuffer, buffer, fenceFd);
-    if (result != OK) {
-        ALOGW("dequeueBuffer failed, error = %d; switching to fallback", result);
-        *buffer = acquireFallbackBuffer(result);
-        *fenceFd = -1;
-        return *buffer ? OK : INVALID_OPERATION;
-    } else {
-        std::lock_guard _lock{mMutex};
-        mHasDequeuedBuffer = true;
-    }
-    return OK;
-}
-
-int ReliableSurface::queueBuffer(ANativeWindowBuffer* buffer, int fenceFd) {
-    clearReservedBuffer();
-
-    if (isFallbackBuffer(buffer)) {
-        if (fenceFd > 0) {
-            close(fenceFd);
-        }
-        return OK;
-    }
-
-    int result = callProtected(mSurface, queueBuffer, buffer, fenceFd);
-    return result;
-}
-
 bool ReliableSurface::isFallbackBuffer(const ANativeWindowBuffer* windowBuffer) const {
     if (!mScratchBuffer || !windowBuffer) {
         return false;
@@ -229,82 +157,95 @@
     return AHardwareBuffer_to_ANativeWindowBuffer(newBuffer);
 }
 
-Surface* ReliableSurface::getWrapped(const ANativeWindow* window) {
-    return getSelf(window)->mSurface.get();
-}
+int ReliableSurface::hook_dequeueBuffer(ANativeWindow* window,
+                                        ANativeWindow_dequeueBufferFn dequeueBuffer, void* data,
+                                        ANativeWindowBuffer** buffer, int* fenceFd) {
+    ReliableSurface* rs = reinterpret_cast<ReliableSurface*>(data);
+    {
+        std::lock_guard _lock{rs->mMutex};
+        if (rs->mReservedBuffer) {
+            *buffer = rs->mReservedBuffer;
+            *fenceFd = rs->mReservedFenceFd.release();
+            rs->mReservedBuffer = nullptr;
+            return OK;
+        }
+    }
 
-int ReliableSurface::hook_setSwapInterval(ANativeWindow* window, int interval) {
-    return callProtected(getWrapped(window), setSwapInterval, interval);
-}
-
-int ReliableSurface::hook_dequeueBuffer(ANativeWindow* window, ANativeWindowBuffer** buffer,
-                                        int* fenceFd) {
-    return getSelf(window)->dequeueBuffer(buffer, fenceFd);
-}
-
-int ReliableSurface::hook_cancelBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer,
-                                       int fenceFd) {
-    return getSelf(window)->cancelBuffer(buffer, fenceFd);
-}
-
-int ReliableSurface::hook_queueBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer,
-                                      int fenceFd) {
-    return getSelf(window)->queueBuffer(buffer, fenceFd);
-}
-
-int ReliableSurface::hook_dequeueBuffer_DEPRECATED(ANativeWindow* window,
-                                                   ANativeWindowBuffer** buffer) {
-    ANativeWindowBuffer* buf;
-    int fenceFd = -1;
-    int result = window->dequeueBuffer(window, &buf, &fenceFd);
+    int result = dequeueBuffer(window, buffer, fenceFd);
     if (result != OK) {
-        return result;
+        ALOGW("dequeueBuffer failed, error = %d; switching to fallback", result);
+        *buffer = rs->acquireFallbackBuffer(result);
+        *fenceFd = -1;
+        return *buffer ? OK : INVALID_OPERATION;
+    } else {
+        std::lock_guard _lock{rs->mMutex};
+        rs->mHasDequeuedBuffer = true;
     }
-    sp<Fence> fence(new Fence(fenceFd));
-    int waitResult = fence->waitForever("dequeueBuffer_DEPRECATED");
-    if (waitResult != OK) {
-        ALOGE("dequeueBuffer_DEPRECATED: Fence::wait returned an error: %d", waitResult);
-        window->cancelBuffer(window, buf, -1);
-        return waitResult;
-    }
-    *buffer = buf;
-    return result;
-}
-
-int ReliableSurface::hook_cancelBuffer_DEPRECATED(ANativeWindow* window,
-                                                  ANativeWindowBuffer* buffer) {
-    return window->cancelBuffer(window, buffer, -1);
-}
-
-int ReliableSurface::hook_lockBuffer_DEPRECATED(ANativeWindow* window,
-                                                ANativeWindowBuffer* buffer) {
-    // This method is a no-op in Surface as well
     return OK;
 }
 
-int ReliableSurface::hook_queueBuffer_DEPRECATED(ANativeWindow* window,
-                                                 ANativeWindowBuffer* buffer) {
-    return window->queueBuffer(window, buffer, -1);
+int ReliableSurface::hook_cancelBuffer(ANativeWindow* window,
+                                       ANativeWindow_cancelBufferFn cancelBuffer, void* data,
+                                       ANativeWindowBuffer* buffer, int fenceFd) {
+    ReliableSurface* rs = reinterpret_cast<ReliableSurface*>(data);
+    rs->clearReservedBuffer();
+    if (rs->isFallbackBuffer(buffer)) {
+        if (fenceFd > 0) {
+            close(fenceFd);
+        }
+        return OK;
+    }
+    return cancelBuffer(window, buffer, fenceFd);
 }
 
-int ReliableSurface::hook_query(const ANativeWindow* window, int what, int* value) {
-    return getWrapped(window)->query(what, value);
+int ReliableSurface::hook_queueBuffer(ANativeWindow* window,
+                                      ANativeWindow_queueBufferFn queueBuffer, void* data,
+                                      ANativeWindowBuffer* buffer, int fenceFd) {
+    ReliableSurface* rs = reinterpret_cast<ReliableSurface*>(data);
+    rs->clearReservedBuffer();
+
+    if (rs->isFallbackBuffer(buffer)) {
+        if (fenceFd > 0) {
+            close(fenceFd);
+        }
+        return OK;
+    }
+
+    return queueBuffer(window, buffer, fenceFd);
 }
 
-int ReliableSurface::hook_perform(ANativeWindow* window, int operation, ...) {
+int ReliableSurface::hook_perform(ANativeWindow* window, ANativeWindow_performFn perform,
+                                  void* data, int operation, va_list args) {
     // Drop the reserved buffer if there is one since this (probably) mutated buffer dimensions
     // TODO: Filter to things that only affect the reserved buffer
     // TODO: Can we mutate the reserved buffer in some cases?
-    getSelf(window)->clearReservedBuffer();
-    va_list args;
-    va_start(args, operation);
-    int result = callProtected(getWrapped(window), perform, operation, args);
-    va_end(args);
+    ReliableSurface* rs = reinterpret_cast<ReliableSurface*>(data);
+    rs->clearReservedBuffer();
 
-    va_start(args, operation);
-    getSelf(window)->perform(operation, args);
-    va_end(args);
+    va_list argsCopy;
+    va_copy(argsCopy, args);
+    int result = perform(window, operation, argsCopy);
 
+    {
+        std::lock_guard _lock{rs->mMutex};
+
+        switch (operation) {
+            case ANATIVEWINDOW_PERFORM_SET_USAGE:
+                rs->mUsage = va_arg(args, uint32_t);
+                break;
+            case ANATIVEWINDOW_PERFORM_SET_USAGE64:
+                rs->mUsage = va_arg(args, uint64_t);
+                break;
+            case ANATIVEWINDOW_PERFORM_SET_BUFFERS_GEOMETRY:
+                /* width */ va_arg(args, uint32_t);
+                /* height */ va_arg(args, uint32_t);
+                rs->mFormat = va_arg(args, PixelFormat);
+                break;
+            case ANATIVEWINDOW_PERFORM_SET_BUFFERS_FORMAT:
+                rs->mFormat = va_arg(args, PixelFormat);
+                break;
+        }
+    }
     return result;
 }
 
diff --git a/libs/hwui/renderthread/ReliableSurface.h b/libs/hwui/renderthread/ReliableSurface.h
index f768df3..3247253 100644
--- a/libs/hwui/renderthread/ReliableSurface.h
+++ b/libs/hwui/renderthread/ReliableSurface.h
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include <apex/window.h>
 #include <gui/Surface.h>
 #include <utils/Macros.h>
 #include <utils/StrongPointer.h>
@@ -24,16 +25,21 @@
 
 namespace android::uirenderer::renderthread {
 
-class ReliableSurface : public ANativeObjectBase<ANativeWindow, ReliableSurface, RefBase> {
+class ReliableSurface {
     PREVENT_COPY_AND_ASSIGN(ReliableSurface);
 
 public:
     ReliableSurface(sp<Surface>&& surface);
     ~ReliableSurface();
 
-    int reserveNext();
+    // Performs initialization that is not safe to do in the constructor.
+    // For instance, registering ANativeWindow interceptors with ReliableSurface
+    // passed as the data pointer is not safe.
+    void init();
 
-    void allocateBuffers() { mSurface->allocateBuffers(); }
+    ANativeWindow* getNativeWindow() { return mSurface.get(); }
+
+    int reserveNext();
 
     int query(int what, int* value) const { return mSurface->query(what, value); }
 
@@ -61,7 +67,7 @@
     }
 
 private:
-    const sp<Surface> mSurface;
+    sp<Surface> mSurface;
 
     mutable std::mutex mMutex;
 
@@ -78,27 +84,20 @@
     ANativeWindowBuffer* acquireFallbackBuffer(int error);
     void clearReservedBuffer();
 
-    void perform(int operation, va_list args);
-    int cancelBuffer(ANativeWindowBuffer* buffer, int fenceFd);
-    int dequeueBuffer(ANativeWindowBuffer** buffer, int* fenceFd);
-    int queueBuffer(ANativeWindowBuffer* buffer, int fenceFd);
+    // ANativeWindow hooks. When an ANativeWindow_* method is called on the
+    // underlying ANativeWindow, these methods will intercept the original call.
+    // For example, an EGL driver would call into these hooks instead of the
+    // original methods.
+    static int hook_cancelBuffer(ANativeWindow* window, ANativeWindow_cancelBufferFn cancelBuffer,
+                                 void* data, ANativeWindowBuffer* buffer, int fenceFd);
+    static int hook_dequeueBuffer(ANativeWindow* window,
+                                  ANativeWindow_dequeueBufferFn dequeueBuffer, void* data,
+                                  ANativeWindowBuffer** buffer, int* fenceFd);
+    static int hook_queueBuffer(ANativeWindow* window, ANativeWindow_queueBufferFn queueBuffer,
+                                void* data, ANativeWindowBuffer* buffer, int fenceFd);
 
-    static Surface* getWrapped(const ANativeWindow*);
-
-    // ANativeWindow hooks
-    static int hook_cancelBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer, int fenceFd);
-    static int hook_dequeueBuffer(ANativeWindow* window, ANativeWindowBuffer** buffer,
-                                  int* fenceFd);
-    static int hook_queueBuffer(ANativeWindow* window, ANativeWindowBuffer* buffer, int fenceFd);
-
-    static int hook_perform(ANativeWindow* window, int operation, ...);
-    static int hook_query(const ANativeWindow* window, int what, int* value);
-    static int hook_setSwapInterval(ANativeWindow* window, int interval);
-
-    static int hook_cancelBuffer_DEPRECATED(ANativeWindow* window, ANativeWindowBuffer* buffer);
-    static int hook_dequeueBuffer_DEPRECATED(ANativeWindow* window, ANativeWindowBuffer** buffer);
-    static int hook_lockBuffer_DEPRECATED(ANativeWindow* window, ANativeWindowBuffer* buffer);
-    static int hook_queueBuffer_DEPRECATED(ANativeWindow* window, ANativeWindowBuffer* buffer);
+    static int hook_perform(ANativeWindow* window, ANativeWindow_performFn perform, void* data,
+                            int operation, va_list args);
 };
 
 };  // namespace android::uirenderer::renderthread
diff --git a/media/java/android/media/MediaRoute2Info.java b/media/java/android/media/MediaRoute2Info.java
index dd01243..dace091 100644
--- a/media/java/android/media/MediaRoute2Info.java
+++ b/media/java/android/media/MediaRoute2Info.java
@@ -21,6 +21,7 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.TestApi;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.Parcel;
@@ -360,6 +361,7 @@
      * @hide
      */
     @NonNull
+    @TestApi
     public String getOriginalId() {
         return mId;
     }
diff --git a/media/java/android/media/audiopolicy/AudioVolumeGroupChangeHandler.java b/media/java/android/media/audiopolicy/AudioVolumeGroupChangeHandler.java
index 074188e..adf4d3d 100644
--- a/media/java/android/media/audiopolicy/AudioVolumeGroupChangeHandler.java
+++ b/media/java/android/media/audiopolicy/AudioVolumeGroupChangeHandler.java
@@ -157,9 +157,7 @@
             Handler handler = eventHandler.handler();
             if (handler != null) {
                 Message m = handler.obtainMessage(what, arg1, arg2, obj);
-                if (what != AUDIOVOLUMEGROUP_EVENT_NEW_LISTENER) {
-                    handler.removeMessages(what);
-                }
+                // Do not remove previous messages, as we would lose notification of group changes
                 handler.sendMessage(m);
             }
         }
diff --git a/native/android/Android.bp b/native/android/Android.bp
index 0c6f507..34ab7a0 100644
--- a/native/android/Android.bp
+++ b/native/android/Android.bp
@@ -64,6 +64,7 @@
         "libgui",
         "libharfbuzz_ng",  // Only for including hb.h via minikin
         "libsensor",
+        "libstats_jni",
         "libandroid_runtime",
         "libminikin",
         "libnetd_client",
diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt
index 97b861b..a8f1d2c 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -23,6 +23,8 @@
     AChoreographer_postFrameCallbackDelayed; # introduced=24
     AChoreographer_postFrameCallback64; # introduced=29
     AChoreographer_postFrameCallbackDelayed64; # introduced=29
+    AChoreographer_registerRefreshRateCallback; # introduced=30
+    AChoreographer_unregisterRefreshRateCallback; # introduced=30
     AConfiguration_copy;
     AConfiguration_delete;
     AConfiguration_diff;
diff --git a/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java b/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java
index aac7fc3..d48aa24 100644
--- a/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java
+++ b/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java
@@ -402,7 +402,7 @@
         private static List<String> extractTimeZoneIds(List<TimeZoneMapping> timeZoneMappings) {
             final List<String> zoneIds = new ArrayList<>(timeZoneMappings.size());
             for (TimeZoneMapping timeZoneMapping : timeZoneMappings) {
-                zoneIds.add(timeZoneMapping.timeZoneId);
+                zoneIds.add(timeZoneMapping.getTimeZoneId());
             }
             return Collections.unmodifiableList(zoneIds);
         }
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index 84dde05..44e70f4 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -654,7 +654,7 @@
                 }
             }
         }
-        return oldMetering == mIsScoredNetworkMetered;
+        return oldMetering != mIsScoredNetworkMetered;
     }
 
     /**
@@ -1170,8 +1170,8 @@
             } else { // In range, not disabled.
                 if (mConfig != null) { // Is saved network
                     // Last attempt to connect to this failed. Show reason why
-                    switch (mConfig.recentFailure.getAssociationStatus()) {
-                        case WifiConfiguration.RecentFailure.STATUS_AP_UNABLE_TO_HANDLE_NEW_STA:
+                    switch (mConfig.getRecentFailureReason()) {
+                        case WifiConfiguration.RECENT_FAILURE_AP_UNABLE_TO_HANDLE_NEW_STA:
                             summary.append(mContext.getString(
                                     R.string.wifi_ap_unable_to_handle_new_sta));
                             break;
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index db18213a..9934e59 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
@@ -41,13 +41,13 @@
 import android.util.Base64;
 import android.util.Slog;
 import android.util.SparseIntArray;
-import android.util.StatsLog;
 import android.util.TimeUtils;
 import android.util.Xml;
 import android.util.proto.ProtoOutputStream;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.FrameworkStatsLog;
 
 import libcore.io.IoUtils;
 
@@ -429,8 +429,9 @@
             mSettings.put(name, newState);
         }
 
-        StatsLog.write(StatsLog.SETTING_CHANGED, name, value, newState.value, oldValue, tag,
-            makeDefault, getUserIdFromKey(mKey), StatsLog.SETTING_CHANGED__REASON__UPDATED);
+        FrameworkStatsLog.write(FrameworkStatsLog.SETTING_CHANGED, name, value, newState.value,
+                oldValue, tag, makeDefault, getUserIdFromKey(mKey),
+                FrameworkStatsLog.SETTING_CHANGED__REASON__UPDATED);
 
         addHistoricalOperationLocked(HISTORICAL_OPERATION_UPDATE, newState);
 
@@ -489,9 +490,9 @@
             if (key.startsWith(prefix) && !keyValues.containsKey(key)) {
                 Setting oldState = mSettings.remove(key);
 
-                StatsLog.write(StatsLog.SETTING_CHANGED, key, /* value= */ "", /* newValue= */ "",
-                        oldState.value, /* tag */ "", false, getUserIdFromKey(mKey),
-                        StatsLog.SETTING_CHANGED__REASON__DELETED);
+                FrameworkStatsLog.write(FrameworkStatsLog.SETTING_CHANGED, key,
+                        /* value= */ "", /* newValue= */ "", oldState.value, /* tag */ "", false,
+                        getUserIdFromKey(mKey), FrameworkStatsLog.SETTING_CHANGED__REASON__DELETED);
                 addHistoricalOperationLocked(HISTORICAL_OPERATION_DELETE, oldState);
                 changedKeys.add(key); // key was removed
             }
@@ -516,9 +517,9 @@
                 continue;
             }
 
-            StatsLog.write(StatsLog.SETTING_CHANGED, key, value, state.value, oldValue,
-                    /* tag */ null, /* make default */ false,
-                    getUserIdFromKey(mKey), StatsLog.SETTING_CHANGED__REASON__UPDATED);
+            FrameworkStatsLog.write(FrameworkStatsLog.SETTING_CHANGED, key, value, state.value,
+                    oldValue, /* tag */ null, /* make default */ false,
+                    getUserIdFromKey(mKey), FrameworkStatsLog.SETTING_CHANGED__REASON__UPDATED);
             addHistoricalOperationLocked(HISTORICAL_OPERATION_UPDATE, state);
         }
 
@@ -544,9 +545,9 @@
 
         Setting oldState = mSettings.remove(name);
 
-        StatsLog.write(StatsLog.SETTING_CHANGED, name, /* value= */ "", /* newValue= */ "",
-            oldState.value, /* tag */ "", false, getUserIdFromKey(mKey),
-            StatsLog.SETTING_CHANGED__REASON__DELETED);
+        FrameworkStatsLog.write(FrameworkStatsLog.SETTING_CHANGED, name, /* value= */ "",
+                /* newValue= */ "", oldState.value, /* tag */ "", false, getUserIdFromKey(mKey),
+                FrameworkStatsLog.SETTING_CHANGED__REASON__DELETED);
 
         updateMemoryUsagePerPackageLocked(oldState.packageName, oldState.value,
                 null, oldState.defaultValue, null);
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 7d2b85d..84d9bb6 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -219,6 +219,9 @@
     <!-- Permission required for CTS test - CrossProfileAppsHostSideTest -->
     <uses-permission android:name="android.permission.INTERACT_ACROSS_PROFILES"/>
 
+    <!-- permissions required for CTS test - PhoneStateListenerTest -->
+    <uses-permission android:name="android.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH" />
+
     <!-- Permission required for CTS test - UiModeManagerTest -->
     <uses-permission android:name="android.permission.ENTER_CAR_MODE_PRIORITIZED"/>
 
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
index dd1856a..c9c38d3 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsImpl.java
@@ -30,17 +30,17 @@
 import android.widget.TextView;
 
 import com.android.internal.R;
-import com.android.internal.colorextraction.ColorExtractor.GradientColors;
 import com.android.internal.colorextraction.drawable.ScrimDrawable;
 import com.android.keyguard.KeyguardUpdateMonitor;
 import com.android.settingslib.Utils;
 import com.android.systemui.Dependency;
-import com.android.systemui.colorextraction.SysuiColorExtractor;
 import com.android.systemui.plugins.GlobalActions;
 import com.android.systemui.plugins.GlobalActionsPanelPlugin;
 import com.android.systemui.plugins.PluginListener;
 import com.android.systemui.shared.plugins.PluginManager;
+import com.android.systemui.statusbar.BlurUtils;
 import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.phone.ScrimController;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.statusbar.policy.ExtensionController;
 import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -59,6 +59,7 @@
     private final KeyguardStateController mKeyguardStateController;
     private final DeviceProvisionedController mDeviceProvisionedController;
     private final ExtensionController.Extension<GlobalActionsPanelPlugin> mPanelExtension;
+    private final BlurUtils mBlurUtils;
     private GlobalActionsPanelPlugin mPlugin;
     private final CommandQueue mCommandQueue;
     private GlobalActionsDialog mGlobalActionsDialog;
@@ -68,13 +69,14 @@
 
     @Inject
     public GlobalActionsImpl(Context context, CommandQueue commandQueue,
-            Lazy<GlobalActionsDialog> globalActionsDialogLazy) {
+            Lazy<GlobalActionsDialog> globalActionsDialogLazy, BlurUtils blurUtils) {
         mContext = context;
         mGlobalActionsDialogLazy = globalActionsDialogLazy;
         mKeyguardStateController = Dependency.get(KeyguardStateController.class);
         mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class);
         mPluginManager = Dependency.get(PluginManager.class);
         mCommandQueue = commandQueue;
+        mBlurUtils = blurUtils;
         mCommandQueue.addCallback(this);
         mPanelExtension = Dependency.get(ExtensionController.class)
                 .newExtension(GlobalActionsPanelPlugin.class)
@@ -110,7 +112,6 @@
     @Override
     public void showShutdownUi(boolean isReboot, String reason) {
         ScrimDrawable background = new ScrimDrawable();
-        background.setAlpha((int) (SHUTDOWN_SCRIM_ALPHA * 255));
 
         Dialog d = new Dialog(mContext,
                 com.android.systemui.R.style.Theme_SystemUI_Dialog_GlobalActions);
@@ -160,8 +161,13 @@
             reasonView.setText(rebootReasonMessage);
         }
 
-        GradientColors colors = Dependency.get(SysuiColorExtractor.class).getNeutralColors();
-        background.setColor(colors.getMainColor(), false);
+        if (mBlurUtils.supportsBlursOnWindows()) {
+            background.setAlpha((int) (ScrimController.GRADIENT_SCRIM_ALPHA_BUSY * 255));
+            mBlurUtils.applyBlur(d.getWindow().getDecorView().getViewRootImpl(),
+                        mBlurUtils.radiusForRatio(1));
+        } else {
+            background.setAlpha((int) (SHUTDOWN_SCRIM_ALPHA * 255));
+        }
 
         d.show();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
index e6082dd..e7e1ba8 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
@@ -106,7 +106,7 @@
 
         // Initialize screenshot notification smart actions provider.
         mSmartActionsEnabled = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
-                SystemUiDeviceConfigFlags.ENABLE_SCREENSHOT_NOTIFICATION_SMART_ACTIONS, false);
+                SystemUiDeviceConfigFlags.ENABLE_SCREENSHOT_NOTIFICATION_SMART_ACTIONS, true);
         if (mSmartActionsEnabled) {
             mSmartActionsProvider =
                     SystemUIFactory.getInstance()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
index 5cfb4b3..333b4a7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
@@ -274,7 +274,7 @@
 
     @Test
     public void testRemoveBubble_withDismissedNotif() {
-        mEntryListener.onNotificationAdded(mRow.getEntry());
+        mEntryListener.onPendingEntryAdded(mRow.getEntry());
         mBubbleController.updateBubble(mRow.getEntry());
 
         assertTrue(mBubbleController.hasBubbles());
@@ -317,7 +317,7 @@
         assertFalse(mBubbleController.isStackExpanded());
 
         // Mark it as a bubble and add it explicitly
-        mEntryListener.onNotificationAdded(mRow.getEntry());
+        mEntryListener.onPendingEntryAdded(mRow.getEntry());
         mBubbleController.updateBubble(mRow.getEntry());
 
         // We should have bubbles & their notifs should not be suppressed
@@ -347,8 +347,8 @@
     @Test
     public void testCollapseAfterChangingExpandedBubble() {
         // Mark it as a bubble and add it explicitly
-        mEntryListener.onNotificationAdded(mRow.getEntry());
-        mEntryListener.onNotificationAdded(mRow2.getEntry());
+        mEntryListener.onPendingEntryAdded(mRow.getEntry());
+        mEntryListener.onPendingEntryAdded(mRow2.getEntry());
         mBubbleController.updateBubble(mRow.getEntry());
         mBubbleController.updateBubble(mRow2.getEntry());
 
@@ -390,7 +390,7 @@
     @Test
     public void testExpansionRemovesShowInShadeAndDot() {
         // Mark it as a bubble and add it explicitly
-        mEntryListener.onNotificationAdded(mRow.getEntry());
+        mEntryListener.onPendingEntryAdded(mRow.getEntry());
         mBubbleController.updateBubble(mRow.getEntry());
 
         // We should have bubbles & their notifs should not be suppressed
@@ -416,7 +416,7 @@
     @Test
     public void testUpdateWhileExpanded_DoesntChangeShowInShadeAndDot() {
         // Mark it as a bubble and add it explicitly
-        mEntryListener.onNotificationAdded(mRow.getEntry());
+        mEntryListener.onPendingEntryAdded(mRow.getEntry());
         mBubbleController.updateBubble(mRow.getEntry());
 
         // We should have bubbles & their notifs should not be suppressed
@@ -452,8 +452,8 @@
     @Test
     public void testRemoveLastExpandedCollapses() {
         // Mark it as a bubble and add it explicitly
-        mEntryListener.onNotificationAdded(mRow.getEntry());
-        mEntryListener.onNotificationAdded(mRow2.getEntry());
+        mEntryListener.onPendingEntryAdded(mRow.getEntry());
+        mEntryListener.onPendingEntryAdded(mRow2.getEntry());
         mBubbleController.updateBubble(mRow.getEntry());
         mBubbleController.updateBubble(mRow2.getEntry());
         verify(mBubbleStateChangeListener).onHasBubblesChanged(true);
@@ -496,7 +496,7 @@
                 Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE, false /* enableFlag */);
 
         // Add the auto expand bubble
-        mEntryListener.onNotificationAdded(mRow.getEntry());
+        mEntryListener.onPendingEntryAdded(mRow.getEntry());
         mBubbleController.updateBubble(mRow.getEntry());
 
         // Expansion shouldn't change
@@ -514,7 +514,7 @@
                 Notification.BubbleMetadata.FLAG_AUTO_EXPAND_BUBBLE, true /* enableFlag */);
 
         // Add the auto expand bubble
-        mEntryListener.onNotificationAdded(mRow.getEntry());
+        mEntryListener.onPendingEntryAdded(mRow.getEntry());
         mBubbleController.updateBubble(mRow.getEntry());
 
         // Expansion should change
@@ -532,7 +532,7 @@
                 Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION, true /* enableFlag */);
 
         // Add the suppress notif bubble
-        mEntryListener.onNotificationAdded(mRow.getEntry());
+        mEntryListener.onPendingEntryAdded(mRow.getEntry());
         mBubbleController.updateBubble(mRow.getEntry());
 
         // Notif should be suppressed because we were foreground
@@ -576,7 +576,7 @@
     public void testExpandStackAndSelectBubble_removedFirst() {
         final String key = mRow.getEntry().getKey();
 
-        mEntryListener.onNotificationAdded(mRow.getEntry());
+        mEntryListener.onPendingEntryAdded(mRow.getEntry());
         mBubbleController.updateBubble(mRow.getEntry());
 
         // Simulate notification cancellation.
@@ -588,7 +588,7 @@
 
     @Test
     public void testMarkNewNotificationAsShowInShade() {
-        mEntryListener.onNotificationAdded(mRow.getEntry());
+        mEntryListener.onPendingEntryAdded(mRow.getEntry());
         assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
                 mRow.getEntry()));
 
@@ -598,7 +598,7 @@
 
     @Test
     public void testAddNotif_notBubble() {
-        mEntryListener.onNotificationAdded(mNonBubbleNotifRow.getEntry());
+        mEntryListener.onPendingEntryAdded(mNonBubbleNotifRow.getEntry());
         mEntryListener.onPreEntryUpdated(mNonBubbleNotifRow.getEntry());
 
         verify(mBubbleStateChangeListener, never()).onHasBubblesChanged(anyBoolean());
@@ -643,7 +643,7 @@
 
     @Test
     public void testRemoveBubble_succeeds_appCancel() {
-        mEntryListener.onNotificationAdded(mRow.getEntry());
+        mEntryListener.onPendingEntryAdded(mRow.getEntry());
         mBubbleController.updateBubble(mRow.getEntry());
 
         assertTrue(mBubbleController.hasBubbles());
@@ -658,7 +658,7 @@
 
     @Test
     public void removeBubble_fails_clearAll()  {
-        mEntryListener.onNotificationAdded(mRow.getEntry());
+        mEntryListener.onPendingEntryAdded(mRow.getEntry());
         mBubbleController.updateBubble(mRow.getEntry());
 
         assertTrue(mBubbleController.hasBubbles());
@@ -681,7 +681,7 @@
 
     @Test
     public void removeBubble_fails_userDismissNotif() {
-        mEntryListener.onNotificationAdded(mRow.getEntry());
+        mEntryListener.onPendingEntryAdded(mRow.getEntry());
         mBubbleController.updateBubble(mRow.getEntry());
 
         assertTrue(mBubbleController.hasBubbles());
@@ -704,7 +704,7 @@
 
     @Test
     public void removeBubble_succeeds_userDismissBubble_userDimissNotif() {
-        mEntryListener.onNotificationAdded(mRow.getEntry());
+        mEntryListener.onPendingEntryAdded(mRow.getEntry());
         mBubbleController.updateBubble(mRow.getEntry());
 
         assertTrue(mBubbleController.hasBubbles());
@@ -730,7 +730,7 @@
                 mock(BubbleController.NotificationSuppressionChangedListener.class);
         mBubbleData.setSuppressionChangedListener(listener);
 
-        mEntryListener.onNotificationAdded(mRow.getEntry());
+        mEntryListener.onPendingEntryAdded(mRow.getEntry());
 
         assertTrue(mBubbleController.hasBubbles());
         assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
@@ -753,7 +753,7 @@
                 mock(BubbleController.NotificationSuppressionChangedListener.class);
         mBubbleData.setSuppressionChangedListener(listener);
 
-        mEntryListener.onNotificationAdded(mRow.getEntry());
+        mEntryListener.onPendingEntryAdded(mRow.getEntry());
 
         assertTrue(mBubbleController.hasBubbles());
         assertFalse(mBubbleController.isBubbleNotificationSuppressedFromShade(
diff --git a/packages/Tethering/common/TetheringLib/jarjar-rules.txt b/packages/Tethering/common/TetheringLib/jarjar-rules.txt
index 35e0f88..1403bba 100644
--- a/packages/Tethering/common/TetheringLib/jarjar-rules.txt
+++ b/packages/Tethering/common/TetheringLib/jarjar-rules.txt
@@ -1 +1,2 @@
 rule android.annotation.** com.android.networkstack.tethering.annotation.@1
+rule com.android.internal.annotations.** com.android.networkstack.tethering.annotation.@1
\ No newline at end of file
diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java b/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
index 37ce1d57..53a358f 100644
--- a/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
+++ b/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
@@ -30,6 +30,9 @@
 import android.util.ArrayMap;
 import android.util.Log;
 
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
@@ -37,6 +40,7 @@
 import java.util.List;
 import java.util.Objects;
 import java.util.concurrent.Executor;
+import java.util.function.Supplier;
 
 /**
  * This class provides the APIs to control the tethering service.
@@ -50,17 +54,23 @@
 public class TetheringManager {
     private static final String TAG = TetheringManager.class.getSimpleName();
     private static final int DEFAULT_TIMEOUT_MS = 60_000;
+    private static final long CONNECTOR_POLL_INTERVAL_MILLIS = 200L;
 
-    private static TetheringManager sInstance;
+    @GuardedBy("mConnectorWaitQueue")
+    @Nullable
+    private ITetheringConnector mConnector;
+    @GuardedBy("mConnectorWaitQueue")
+    @NonNull
+    private final List<ConnectorConsumer> mConnectorWaitQueue = new ArrayList<>();
+    private final Supplier<IBinder> mConnectorSupplier;
 
-    private final ITetheringConnector mConnector;
     private final TetheringCallbackInternal mCallback;
     private final Context mContext;
     private final ArrayMap<TetheringEventCallback, ITetheringEventCallback>
             mTetheringEventCallbacks = new ArrayMap<>();
 
-    private TetheringConfigurationParcel mTetheringConfiguration;
-    private TetherStatesParcel mTetherStatesParcel;
+    private volatile TetheringConfigurationParcel mTetheringConfiguration;
+    private volatile TetherStatesParcel mTetherStatesParcel;
 
     /**
      * Broadcast Action: A tetherable connection has come or gone.
@@ -162,29 +172,139 @@
     /**
      * Create a TetheringManager object for interacting with the tethering service.
      *
+     * @param context Context for the manager.
+     * @param connectorSupplier Supplier for the manager connector; may return null while the
+     *                          service is not connected.
      * {@hide}
      */
-    public TetheringManager(@NonNull final Context context, @NonNull final IBinder service) {
+    public TetheringManager(@NonNull final Context context,
+            @NonNull Supplier<IBinder> connectorSupplier) {
         mContext = context;
-        mConnector = ITetheringConnector.Stub.asInterface(service);
         mCallback = new TetheringCallbackInternal();
+        mConnectorSupplier = connectorSupplier;
 
         final String pkgName = mContext.getOpPackageName();
+
+        final IBinder connector = mConnectorSupplier.get();
+        // If the connector is available on start, do not start a polling thread. This introduces
+        // differences in the thread that sends the oneway binder calls to the service between the
+        // first few seconds after boot and later, but it avoids always having differences between
+        // the first usage of TetheringManager from a process and subsequent usages (so the
+        // difference is only on boot). On boot binder calls may be queued until the service comes
+        // up and be sent from a worker thread; later, they are always sent from the caller thread.
+        // Considering that it's just oneway binder calls, and ordering is preserved, this seems
+        // better than inconsistent behavior persisting after boot.
+        if (connector != null) {
+            mConnector = ITetheringConnector.Stub.asInterface(connector);
+        } else {
+            startPollingForConnector();
+        }
+
         Log.i(TAG, "registerTetheringEventCallback:" + pkgName);
+        getConnector(c -> c.registerTetheringEventCallback(mCallback, pkgName));
+    }
+
+    private void startPollingForConnector() {
+        new Thread(() -> {
+            while (true) {
+                try {
+                    Thread.sleep(200);
+                } catch (InterruptedException e) {
+                    // Not much to do here, the system needs to wait for the connector
+                }
+
+                final IBinder connector = mConnectorSupplier.get();
+                if (connector != null) {
+                    onTetheringConnected(ITetheringConnector.Stub.asInterface(connector));
+                    return;
+                }
+            }
+        }).start();
+    }
+
+    private interface ConnectorConsumer {
+        void onConnectorAvailable(ITetheringConnector connector) throws RemoteException;
+    }
+
+    private void onTetheringConnected(ITetheringConnector connector) {
+        // Process the connector wait queue in order, including any items that are added
+        // while processing.
+        //
+        // 1. Copy the queue to a local variable under lock.
+        // 2. Drain the local queue with the lock released (otherwise, enqueuing future commands
+        //    would block on the lock).
+        // 3. Acquire the lock again. If any new tasks were queued during step 2, goto 1.
+        //    If not, set mConnector to non-null so future tasks are run immediately, not queued.
+        //
+        // For this to work, all calls to the tethering service must use getConnector(), which
+        // ensures that tasks are added to the queue with the lock held.
+        //
+        // Once mConnector is set to non-null, it will never be null again. If the network stack
+        // process crashes, no recovery is possible.
+        // TODO: evaluate whether it is possible to recover from network stack process crashes
+        // (though in most cases the system will have crashed when the network stack process
+        // crashes).
+        do {
+            final List<ConnectorConsumer> localWaitQueue;
+            synchronized (mConnectorWaitQueue) {
+                localWaitQueue = new ArrayList<>(mConnectorWaitQueue);
+                mConnectorWaitQueue.clear();
+            }
+
+            // Allow more tasks to be added at the end without blocking while draining the queue.
+            for (ConnectorConsumer task : localWaitQueue) {
+                try {
+                    task.onConnectorAvailable(connector);
+                } catch (RemoteException e) {
+                    // Most likely the network stack process crashed, which is likely to crash the
+                    // system. Keep processing other requests but report the error loudly.
+                    Log.wtf(TAG, "Error processing request for the tethering connector", e);
+                }
+            }
+
+            synchronized (mConnectorWaitQueue) {
+                if (mConnectorWaitQueue.size() == 0) {
+                    mConnector = connector;
+                    return;
+                }
+            }
+        } while (true);
+    }
+
+    /**
+     * Asynchronously get the ITetheringConnector to execute some operation.
+     *
+     * <p>If the connector is already available, the operation will be executed on the caller's
+     * thread. Otherwise it will be queued and executed on a worker thread. The operation should be
+     * limited to performing oneway binder calls to minimize differences due to threading.
+     */
+    private void getConnector(ConnectorConsumer consumer) {
+        final ITetheringConnector connector;
+        synchronized (mConnectorWaitQueue) {
+            connector = mConnector;
+            if (connector == null) {
+                mConnectorWaitQueue.add(consumer);
+                return;
+            }
+        }
+
         try {
-            mConnector.registerTetheringEventCallback(mCallback, pkgName);
+            consumer.onConnectorAvailable(connector);
         } catch (RemoteException e) {
             throw new IllegalStateException(e);
         }
     }
 
     private interface RequestHelper {
-        void runRequest(IIntResultListener listener);
+        void runRequest(ITetheringConnector connector, IIntResultListener listener);
     }
 
+    // Used to dispatch legacy ConnectivityManager methods that expect tethering to be able to
+    // return results and perform operations synchronously.
+    // TODO: remove once there are no callers of these legacy methods.
     private class RequestDispatcher {
         private final ConditionVariable mWaiting;
-        public int mRemoteResult;
+        public volatile int mRemoteResult;
 
         private final IIntResultListener mListener = new IIntResultListener.Stub() {
                 @Override
@@ -199,7 +319,7 @@
         }
 
         int waitForResult(final RequestHelper request) {
-            request.runRequest(mListener);
+            getConnector(c -> request.runRequest(c, mListener));
             if (!mWaiting.block(DEFAULT_TIMEOUT_MS)) {
                 throw new IllegalStateException("Callback timeout");
             }
@@ -222,7 +342,7 @@
     }
 
     private class TetheringCallbackInternal extends ITetheringEventCallback.Stub {
-        private int mError = TETHER_ERROR_NO_ERROR;
+        private volatile int mError = TETHER_ERROR_NO_ERROR;
         private final ConditionVariable mWaitForCallback = new ConditionVariable();
 
         @Override
@@ -280,9 +400,9 @@
         Log.i(TAG, "tether caller:" + callerPkg);
         final RequestDispatcher dispatcher = new RequestDispatcher();
 
-        return dispatcher.waitForResult(listener -> {
+        return dispatcher.waitForResult((connector, listener) -> {
             try {
-                mConnector.tether(iface, callerPkg, listener);
+                connector.tether(iface, callerPkg, listener);
             } catch (RemoteException e) {
                 throw new IllegalStateException(e);
             }
@@ -304,9 +424,9 @@
 
         final RequestDispatcher dispatcher = new RequestDispatcher();
 
-        return dispatcher.waitForResult(listener -> {
+        return dispatcher.waitForResult((connector, listener) -> {
             try {
-                mConnector.untether(iface, callerPkg, listener);
+                connector.untether(iface, callerPkg, listener);
             } catch (RemoteException e) {
                 throw new IllegalStateException(e);
             }
@@ -330,9 +450,9 @@
 
         final RequestDispatcher dispatcher = new RequestDispatcher();
 
-        return dispatcher.waitForResult(listener -> {
+        return dispatcher.waitForResult((connector, listener) -> {
             try {
-                mConnector.setUsbTethering(enable, callerPkg, listener);
+                connector.setUsbTethering(enable, callerPkg, listener);
             } catch (RemoteException e) {
                 throw new IllegalStateException(e);
             }
@@ -467,11 +587,7 @@
                 });
             }
         };
-        try {
-            mConnector.startTethering(request.getParcel(), callerPkg, listener);
-        } catch (RemoteException e) {
-            throw new IllegalStateException(e);
-        }
+        getConnector(c -> c.startTethering(request.getParcel(), callerPkg, listener));
     }
 
     /**
@@ -509,15 +625,15 @@
         final String callerPkg = mContext.getOpPackageName();
         Log.i(TAG, "stopTethering caller:" + callerPkg);
 
-        final RequestDispatcher dispatcher = new RequestDispatcher();
-
-        dispatcher.waitForResult(listener -> {
-            try {
-                mConnector.stopTethering(type, callerPkg, listener);
-            } catch (RemoteException e) {
-                throw new IllegalStateException(e);
+        getConnector(c -> c.stopTethering(type, callerPkg, new IIntResultListener.Stub() {
+            @Override
+            public void onResult(int resultCode) {
+                // TODO: provide an API to obtain result
+                // This has never been possible as stopTethering has always been void and never
+                // taken a callback object. The only indication that callers have is if the call
+                // results in a TETHER_STATE_CHANGE broadcast.
             }
-        });
+        }));
     }
 
     /**
@@ -591,12 +707,8 @@
         final String callerPkg = mContext.getOpPackageName();
         Log.i(TAG, "getLatestTetheringEntitlementResult caller:" + callerPkg);
 
-        try {
-            mConnector.requestLatestTetheringEntitlementResult(type, receiver, showEntitlementUi,
-                    callerPkg);
-        } catch (RemoteException e) {
-            throw new IllegalStateException(e);
-        }
+        getConnector(c -> c.requestLatestTetheringEntitlementResult(
+                type, receiver, showEntitlementUi, callerPkg));
     }
 
     /**
@@ -832,11 +944,7 @@
                     });
                 }
             };
-            try {
-                mConnector.registerTetheringEventCallback(remoteCallback, callerPkg);
-            } catch (RemoteException e) {
-                throw new IllegalStateException(e);
-            }
+            getConnector(c -> c.registerTetheringEventCallback(remoteCallback, callerPkg));
             mTetheringEventCallbacks.put(callback, remoteCallback);
         }
     }
@@ -860,11 +968,8 @@
             if (remoteCallback == null) {
                 throw new IllegalArgumentException("callback was not registered.");
             }
-            try {
-                mConnector.unregisterTetheringEventCallback(remoteCallback, callerPkg);
-            } catch (RemoteException e) {
-                throw new IllegalStateException(e);
-            }
+
+            getConnector(c -> c.unregisterTetheringEventCallback(remoteCallback, callerPkg));
         }
     }
 
@@ -1002,9 +1107,9 @@
         final String callerPkg = mContext.getOpPackageName();
 
         final RequestDispatcher dispatcher = new RequestDispatcher();
-        final int ret = dispatcher.waitForResult(listener -> {
+        final int ret = dispatcher.waitForResult((connector, listener) -> {
             try {
-                mConnector.isTetheringSupported(callerPkg, listener);
+                connector.isTetheringSupported(callerPkg, listener);
             } catch (RemoteException e) {
                 throw new IllegalStateException(e);
             }
@@ -1027,13 +1132,14 @@
         final String callerPkg = mContext.getOpPackageName();
         Log.i(TAG, "stopAllTethering caller:" + callerPkg);
 
-        final RequestDispatcher dispatcher = new RequestDispatcher();
-        dispatcher.waitForResult(listener -> {
-            try {
-                mConnector.stopAllTethering(callerPkg, listener);
-            } catch (RemoteException e) {
-                throw new IllegalStateException(e);
+        getConnector(c -> c.stopAllTethering(callerPkg, new IIntResultListener.Stub() {
+            @Override
+            public void onResult(int resultCode) {
+                // TODO: add an API parameter to send result to caller.
+                // This has never been possible as stopAllTethering has always been void and never
+                // taken a callback object. The only indication that callers have is if the call
+                // results in a TETHER_STATE_CHANGE broadcast.
             }
-        });
+        }));
     }
 }
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index d933e9d..2982dc9 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -5537,20 +5537,6 @@
         return nri.request.requestId == mDefaultRequest.requestId;
     }
 
-    // TODO : remove this method. It's a stopgap measure to help sheperding a number of dependent
-    // changes that would conflict throughout the automerger graph. Having this method temporarily
-    // helps with the process of going through with all these dependent changes across the entire
-    // tree.
-    /**
-     * Register a new agent. {@see #registerNetworkAgent} below.
-     */
-    public Network registerNetworkAgent(Messenger messenger, NetworkInfo networkInfo,
-            LinkProperties linkProperties, NetworkCapabilities networkCapabilities,
-            int currentScore, NetworkAgentConfig networkAgentConfig) {
-        return registerNetworkAgent(messenger, networkInfo, linkProperties, networkCapabilities,
-                currentScore, networkAgentConfig, NetworkProvider.ID_NONE);
-    }
-
     /**
      * Register a new agent with ConnectivityService to handle a network.
      *
@@ -5569,7 +5555,7 @@
      */
     public Network registerNetworkAgent(Messenger messenger, NetworkInfo networkInfo,
             LinkProperties linkProperties, NetworkCapabilities networkCapabilities,
-            int currentScore, NetworkAgentConfig networkAgentConfig, int providerId) {
+            NetworkScore currentScore, NetworkAgentConfig networkAgentConfig, int providerId) {
         enforceNetworkFactoryPermission();
 
         LinkProperties lp = new LinkProperties(linkProperties);
@@ -5577,12 +5563,10 @@
         // TODO: Instead of passing mDefaultRequest, provide an API to determine whether a Network
         // satisfies mDefaultRequest.
         final NetworkCapabilities nc = new NetworkCapabilities(networkCapabilities);
-        final NetworkScore ns = new NetworkScore();
-        ns.putIntExtension(NetworkScore.LEGACY_SCORE, currentScore);
         final NetworkAgentInfo nai = new NetworkAgentInfo(messenger, new AsyncChannel(),
                 new Network(mNetIdManager.reserveNetId()), new NetworkInfo(networkInfo), lp, nc,
-                ns, mContext, mTrackerHandler, new NetworkAgentConfig(networkAgentConfig), this,
-                mNetd, mDnsResolver, mNMS, providerId);
+                currentScore, mContext, mTrackerHandler, new NetworkAgentConfig(networkAgentConfig),
+                this, mNetd, mDnsResolver, mNMS, providerId);
         // Make sure the network capabilities reflect what the agent info says.
         nai.getAndSetNetworkCapabilities(mixInCapabilities(nai, nc));
         final String extraInfo = networkInfo.getExtraInfo();
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 36dd017..139a871 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -897,6 +897,13 @@
                     if ((events & PhoneStateListener.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH)
                             != 0) {
                         updateReportSignalStrengthDecision(r.subId);
+                        try {
+                            if (mSignalStrength[phoneId] != null) {
+                                r.callback.onSignalStrengthsChanged(mSignalStrength[phoneId]);
+                            }
+                        } catch (RemoteException ex) {
+                            remove(r.binder);
+                        }
                     }
                     if (validateEventsAndUserLocked(r, PhoneStateListener.LISTEN_CELL_INFO)) {
                         try {
@@ -1326,9 +1333,10 @@
                         log("notifySignalStrengthForPhoneId: r=" + r + " subId=" + subId
                                 + " phoneId=" + phoneId + " ss=" + signalStrength);
                     }
-                    if (r.matchPhoneStateListenerEvent(
-                                PhoneStateListener.LISTEN_SIGNAL_STRENGTHS) &&
-                            idMatch(r.subId, subId, phoneId)) {
+                    if ((r.matchPhoneStateListenerEvent(PhoneStateListener.LISTEN_SIGNAL_STRENGTHS)
+                            || r.matchPhoneStateListenerEvent(
+                                    PhoneStateListener.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH))
+                            && idMatch(r.subId, subId, phoneId)) {
                         try {
                             if (DBG) {
                                 log("notifySignalStrengthForPhoneId: callback.onSsS r=" + r
@@ -1341,7 +1349,7 @@
                         }
                     }
                     if (r.matchPhoneStateListenerEvent(PhoneStateListener.LISTEN_SIGNAL_STRENGTH) &&
-                            idMatch(r.subId, subId, phoneId)){
+                            idMatch(r.subId, subId, phoneId)) {
                         try {
                             int gsmSignalStrength = signalStrength.getGsmSignalStrength();
                             int ss = (gsmSignalStrength == 99 ? -1 : gsmSignalStrength);
@@ -2542,6 +2550,11 @@
                     android.Manifest.permission.READ_ACTIVE_EMERGENCY_SESSION, null);
         }
 
+        if ((events & PhoneStateListener.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH) != 0) {
+            mContext.enforceCallingOrSelfPermission(
+                    android.Manifest.permission.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH, null);
+        }
+
         if ((events & READ_PRIVILEGED_PHONE_STATE_PERMISSION_MASK) != 0) {
             mContext.enforceCallingOrSelfPermission(
                     android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, null);
@@ -2657,7 +2670,8 @@
             }
         }
 
-        if ((events & PhoneStateListener.LISTEN_SIGNAL_STRENGTHS) != 0) {
+        if ((events & PhoneStateListener.LISTEN_SIGNAL_STRENGTHS) != 0
+                || (events & PhoneStateListener.LISTEN_ALWAYS_REPORTED_SIGNAL_STRENGTH) != 0) {
             try {
                 if (mSignalStrength[phoneId] != null) {
                     SignalStrength signalStrength = mSignalStrength[phoneId];
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 8cf620d..6560777 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -355,7 +355,6 @@
 import com.android.server.firewall.IntentFirewall;
 import com.android.server.job.JobSchedulerInternal;
 import com.android.server.pm.Installer;
-import com.android.server.pm.Installer.InstallerException;
 import com.android.server.pm.permission.PermissionManagerServiceInternal;
 import com.android.server.uri.GrantUri;
 import com.android.server.uri.UriGrantsManagerInternal;
@@ -5270,26 +5269,6 @@
             mCallFinishBooting = false;
         }
 
-        ArraySet<String> completedIsas = new ArraySet<String>();
-        for (String abi : Build.SUPPORTED_ABIS) {
-            ZYGOTE_PROCESS.establishZygoteConnectionForAbi(abi);
-            final String instructionSet = VMRuntime.getInstructionSet(abi);
-            if (!completedIsas.contains(instructionSet)) {
-                try {
-                    mInstaller.markBootComplete(VMRuntime.getInstructionSet(abi));
-                } catch (InstallerException e) {
-                    if (!VMRuntime.didPruneDalvikCache()) {
-                        // This is technically not the right filter, as different zygotes may
-                        // have made different pruning decisions. But the log is best effort,
-                        // anyways.
-                        Slog.w(TAG, "Unable to mark boot complete for abi: " + abi + " (" +
-                                e.getMessage() +")");
-                    }
-                }
-                completedIsas.add(instructionSet);
-            }
-        }
-
         // Let the ART runtime in zygote and system_server know that the boot completed.
         ZYGOTE_PROCESS.bootCompleted();
         VMRuntime.bootCompleted();
@@ -6876,6 +6855,7 @@
                 }
             }
 
+            ProcessRecord dyingProc = null;
             if (cpr != null && cpr.proc != null) {
                 providerRunning = !cpr.proc.killed;
 
@@ -6885,14 +6865,9 @@
                 // (See the commit message on I2c4ba1e87c2d47f2013befff10c49b3dc337a9a7 to see
                 // how to test this case.)
                 if (cpr.proc.killed && cpr.proc.killedByAm) {
-                    final long iden = Binder.clearCallingIdentity();
-                    try {
-                        mProcessList.killProcAndWaitIfNecessaryLocked(cpr.proc, false,
-                                cpr.uid == cpr.proc.uid || cpr.proc.isolated,
-                                "getContentProviderImpl: %s (killedByAm)", startTime);
-                    } finally {
-                        Binder.restoreCallingIdentity(iden);
-                    }
+                    Slog.wtf(TAG, cpr.proc.toString() + " was killed by AM but isn't really dead");
+                    // Now we are going to wait for the death before starting the new process.
+                    dyingProc = cpr.proc;
                 }
             }
 
@@ -6993,18 +6968,18 @@
                     // has been killed on us.  We need to wait for a new
                     // process to be started, and make sure its death
                     // doesn't kill our process.
-                    Slog.i(TAG, "Existing provider " + cpr.name.flattenToShortString()
+                    Slog.wtf(TAG, "Existing provider " + cpr.name.flattenToShortString()
                             + " is crashing; detaching " + r);
                     boolean lastRef = decProviderCountLocked(conn, cpr, token, stable);
-                    mProcessList.killProcAndWaitIfNecessaryLocked(cpr.proc,
-                            false, true, "getContentProviderImpl: %s", startTime);
                     if (!lastRef) {
                         // This wasn't the last ref our process had on
-                        // the provider...  we have now been killed, bail.
+                        // the provider...  we will be killed during cleaning up, bail.
                         return null;
                     }
+                    // We'll just start a new process to host the content provider
                     providerRunning = false;
                     conn = null;
+                    dyingProc = cpr.proc;
                 } else {
                     cpr.proc.verifiedAdj = cpr.proc.setAdj;
                 }
@@ -7080,7 +7055,7 @@
                 checkTime(startTime, "getContentProviderImpl: before getProviderByClass");
                 cpr = mProviderMap.getProviderByClass(comp, userId);
                 checkTime(startTime, "getContentProviderImpl: after getProviderByClass");
-                final boolean firstClass = cpr == null;
+                boolean firstClass = cpr == null;
                 if (firstClass) {
                     final long ident = Binder.clearCallingIdentity();
 
@@ -7111,6 +7086,13 @@
                     } finally {
                         Binder.restoreCallingIdentity(ident);
                     }
+                } else if (dyingProc == cpr.proc) {
+                    // The old stable connection's client should be killed during proc cleaning up,
+                    // so do not re-use the old ContentProviderRecord, otherwise the new clients
+                    // could get killed unexpectedly.
+                    cpr = new ContentProviderRecord(cpr);
+                    // This is sort of "firstClass"
+                    firstClass = true;
                 }
 
                 checkTime(startTime, "getContentProviderImpl: now have ContentProviderRecord");
@@ -14270,10 +14252,19 @@
                 cpr.launchingApp = null;
                 cpr.notifyAll();
             }
-            mProviderMap.removeProviderByClass(cpr.name, UserHandle.getUserId(cpr.uid));
+            final int userId = UserHandle.getUserId(cpr.uid);
+            // Don't remove from provider map if it doesn't match
+            // could be a new content provider is starting
+            if (mProviderMap.getProviderByClass(cpr.name, userId) == cpr) {
+                mProviderMap.removeProviderByClass(cpr.name, userId);
+            }
             String names[] = cpr.info.authority.split(";");
             for (int j = 0; j < names.length; j++) {
-                mProviderMap.removeProviderByName(names[j], UserHandle.getUserId(cpr.uid));
+                // Don't remove from provider map if it doesn't match
+                // could be a new content provider is starting
+                if (mProviderMap.getProviderByName(names[j], userId) == cpr) {
+                    mProviderMap.removeProviderByName(names[j], userId);
+                }
             }
         }
 
@@ -14368,6 +14359,10 @@
         // Remove published content providers.
         for (int i = app.pubProviders.size() - 1; i >= 0; i--) {
             ContentProviderRecord cpr = app.pubProviders.valueAt(i);
+            if (cpr.proc != app) {
+                // If the hosting process record isn't really us, bail out
+                continue;
+            }
             final boolean alwaysRemove = app.bad || !allowRestart;
             final boolean inLaunching = removeDyingProviderLocked(app, cpr, alwaysRemove);
             if (!alwaysRemove && inLaunching && cpr.hasConnectionOrHandle()) {
@@ -14453,6 +14448,27 @@
         mUiHandler.obtainMessage(DISPATCH_PROCESS_DIED_UI_MSG, app.pid, app.info.uid,
                 null).sendToTarget();
 
+        // If this is a precede instance of another process instance
+        allowRestart = true;
+        synchronized (app) {
+            if (app.mSuccessor != null) {
+                // We don't allow restart with this ProcessRecord now,
+                // because we have created a new one already.
+                allowRestart = false;
+                // If it's persistent, add the successor to mPersistentStartingProcesses
+                if (app.isPersistent() && !app.removed) {
+                    if (mPersistentStartingProcesses.indexOf(app.mSuccessor) < 0) {
+                        mPersistentStartingProcesses.add(app.mSuccessor);
+                    }
+                }
+                // clean up the field so the successor's proc starter could proceed.
+                app.mSuccessor.mPrecedence = null;
+                app.mSuccessor = null;
+                // Notify if anyone is waiting for it.
+                app.notifyAll();
+            }
+        }
+
         // If the caller is restarting this app, then leave it in its
         // current lists and let the caller take care of it.
         if (restarting) {
@@ -14482,7 +14498,7 @@
         mAtmInternal.onCleanUpApplicationRecord(app.getWindowProcessController());
         mProcessList.noteProcessDiedLocked(app);
 
-        if (restart && !app.isolated) {
+        if (restart && allowRestart && !app.isolated) {
             // We have components that still need to be running in the
             // process, so re-launch it.
             if (index < 0) {
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 7c36a7e..b19f2b3 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -1842,26 +1842,9 @@
         if (mService.mConstants.FLAG_PROCESS_START_ASYNC) {
             if (DEBUG_PROCESSES) Slog.i(TAG_PROCESSES,
                     "Posting procStart msg for " + app.toShortString());
-            mService.mProcStartHandler.post(() -> {
-                try {
-                    final Process.ProcessStartResult startResult = startProcess(app.hostingRecord,
-                            entryPoint, app, app.startUid, gids, runtimeFlags, mountExternal,
-                            app.seInfo, requiredAbi, instructionSet, invokeWith, app.startTime);
-                    synchronized (mService) {
-                        handleProcessStartedLocked(app, startResult, startSeq);
-                    }
-                } catch (RuntimeException e) {
-                    synchronized (mService) {
-                        Slog.e(ActivityManagerService.TAG, "Failure starting process "
-                                + app.processName, e);
-                        mPendingStarts.remove(startSeq);
-                        app.pendingStart = false;
-                        mService.forceStopPackageLocked(app.info.packageName,
-                                UserHandle.getAppId(app.uid),
-                                false, false, true, false, false, app.userId, "start failure");
-                    }
-                }
-            });
+            mService.mProcStartHandler.post(() -> handleProcessStart(
+                    app, entryPoint, gids, runtimeFlags, mountExternal, requiredAbi,
+                    instructionSet, invokeWith, startSeq));
             return true;
         } else {
             try {
@@ -1882,6 +1865,66 @@
         }
     }
 
+    /**
+     * Main handler routine to start the given process from the ProcStartHandler.
+     *
+     * <p>Note: this function doesn't hold the global AM lock intentionally.</p>
+     */
+    private void handleProcessStart(final ProcessRecord app, final String entryPoint,
+            final int[] gids, final int runtimeFlags, final int mountExternal,
+            final String requiredAbi, final String instructionSet,
+            final String invokeWith, final long startSeq) {
+        // If there is a precede instance of the process, wait for its death with a timeout.
+        // Use local reference since we are not using locks here
+        final ProcessRecord precedence = app.mPrecedence;
+        if (precedence != null) {
+            final int pid = precedence.pid;
+            long now = System.currentTimeMillis();
+            final long end = now + PROC_KILL_TIMEOUT;
+            try {
+                Process.waitForProcessDeath(pid, PROC_KILL_TIMEOUT);
+                // It's killed successfully, but we'd make sure the cleanup work is done.
+                synchronized (precedence) {
+                    if (app.mPrecedence != null) {
+                        now = System.currentTimeMillis();
+                        if (now < end) {
+                            try {
+                                precedence.wait(end - now);
+                            } catch (InterruptedException e) {
+                            }
+                        }
+                    }
+                    if (app.mPrecedence != null) {
+                        // The cleanup work hasn't be done yet, let's log it and continue.
+                        Slog.w(TAG, precedence + " has died, but its cleanup isn't done");
+                    }
+                }
+            } catch (Exception e) {
+                // It's still alive...
+                Slog.wtf(TAG, precedence.toString() + " refused to die, but we need to launch "
+                        + app);
+            }
+        }
+        try {
+            final Process.ProcessStartResult startResult = startProcess(app.hostingRecord,
+                    entryPoint, app, app.startUid, gids, runtimeFlags, mountExternal,
+                    app.seInfo, requiredAbi, instructionSet, invokeWith, app.startTime);
+            synchronized (mService) {
+                handleProcessStartedLocked(app, startResult, startSeq);
+            }
+        } catch (RuntimeException e) {
+            synchronized (mService) {
+                Slog.e(ActivityManagerService.TAG, "Failure starting process "
+                        + app.processName, e);
+                mPendingStarts.remove(startSeq);
+                app.pendingStart = false;
+                mService.forceStopPackageLocked(app.info.packageName,
+                        UserHandle.getAppId(app.uid),
+                        false, false, true, false, false, app.userId, "start failure");
+            }
+        }
+    }
+
     @GuardedBy("mService")
     public void killAppZygoteIfNeededLocked(AppZygote appZygote, boolean force) {
         final ApplicationInfo appInfo = appZygote.getAppInfo();
@@ -2136,6 +2179,7 @@
                 + " app=" + app + " knownToBeDead=" + knownToBeDead
                 + " thread=" + (app != null ? app.thread : null)
                 + " pid=" + (app != null ? app.pid : -1));
+        ProcessRecord precedence = null;
         if (app != null && app.pid > 0) {
             if ((!knownToBeDead && !app.killed) || app.thread == null) {
                 // We already have the app running, or are waiting for it to
@@ -2150,9 +2194,15 @@
             // An application record is attached to a previous process,
             // clean it up now.
             if (DEBUG_PROCESSES) Slog.v(TAG_PROCESSES, "App died: " + app);
-            // do the killing
-            killProcAndWaitIfNecessaryLocked(app, true, app.uid == info.uid || app.isolated,
-                    "startProcess: bad proc running, killing: %s", startTime);
+            checkSlow(startTime, "startProcess: bad proc running, killing");
+            ProcessList.killProcessGroup(app.uid, app.pid);
+            checkSlow(startTime, "startProcess: done killing old proc");
+
+            Slog.wtf(TAG_PROCESSES, app.toString() + " is attached to a previous process");
+            // We are not going to re-use the ProcessRecord, as we haven't dealt with the cleanup
+            // routine of it yet, but we'd set it as the precedence of the new process.
+            precedence = app;
+            app = null;
         }
 
         if (app == null) {
@@ -2166,6 +2216,10 @@
             app.crashHandler = crashHandler;
             app.isolatedEntryPoint = entryPoint;
             app.isolatedEntryPointArgs = entryPointArgs;
+            if (precedence != null) {
+                app.mPrecedence = precedence;
+                precedence.mSuccessor = app;
+            }
             checkSlow(startTime, "startProcess: done creating new process record");
         } else {
             // If this is a new package in the process, add the package to the list
@@ -2193,44 +2247,6 @@
         return success ? app : null;
     }
 
-    /**
-     * Kill (if asked to) and wait for the given process died if necessary
-     * @param app - The process record to kill
-     * @param doKill - Kill the given process record
-     * @param wait - Wait for the death of the given process
-     * @param formatString - The log message for slow operation
-     * @param startTime - The start timestamp of the operation
-     */
-    @GuardedBy("mService")
-    void killProcAndWaitIfNecessaryLocked(final ProcessRecord app, final boolean doKill,
-            final boolean wait, final String formatString, final long startTime) {
-
-        checkSlow(startTime, String.format(formatString, "before appDied"));
-
-        if (doKill) {
-            // do the killing
-            ProcessList.killProcessGroup(app.uid, app.pid);
-            noteAppKill(app, ApplicationExitInfo.REASON_OTHER,
-                    ApplicationExitInfo.SUBREASON_UNKNOWN,
-                    String.format(formatString, ""));
-        }
-
-        // wait for the death
-        if (wait) {
-            try {
-                Process.waitForProcessDeath(app.pid, PROC_KILL_TIMEOUT);
-            } catch (Exception e) {
-                // Maybe the process goes into zombie, use an expensive API to check again.
-                if (mService.isProcessAliveLocked(app)) {
-                    Slog.w(TAG, String.format(formatString,
-                              "waiting for app killing timed out"));
-                }
-            }
-        }
-
-        checkSlow(startTime, String.format(formatString, "after appDied"));
-    }
-
     @GuardedBy("mService")
     private String isProcStartValidLocked(ProcessRecord app, long expectedStartSeq) {
         StringBuilder sb = null;
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index b0c0aae..fc33c25 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -320,6 +320,14 @@
     // set of disabled compat changes for the process (all others are enabled)
     long[] mDisabledCompatChanges;
 
+    // The precede instance of the process, which would exist when the previous process is killed
+    // but not fully dead yet; in this case, the new instance of the process should be held until
+    // this precede instance is fully dead.
+    volatile ProcessRecord mPrecedence;
+    // The succeeding instance of the process, which is going to be started after this process
+    // is killed successfully.
+    volatile ProcessRecord mSuccessor;
+
     // Cached task info for OomAdjuster
     private static final int VALUE_INVALID = -1;
     private static final int VALUE_FALSE = 0;
diff --git a/services/core/java/com/android/server/compat/CompatConfig.java b/services/core/java/com/android/server/compat/CompatConfig.java
index 8d26176..7f7c9c4 100644
--- a/services/core/java/com/android/server/compat/CompatConfig.java
+++ b/services/core/java/com/android/server/compat/CompatConfig.java
@@ -35,6 +35,7 @@
 import com.android.internal.compat.OverrideAllowedState;
 import com.android.server.compat.config.Change;
 import com.android.server.compat.config.XmlParser;
+import com.android.server.pm.ApexManager;
 
 import org.xmlpull.v1.XmlPullParserException;
 
@@ -45,6 +46,7 @@
 import java.io.InputStream;
 import java.io.PrintWriter;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Set;
 
 import javax.xml.datatype.DatatypeConfigurationException;
@@ -369,12 +371,18 @@
                 Environment.getRootDirectory(), "etc", "compatconfig"));
         config.initConfigFromLib(Environment.buildPath(
                 Environment.getRootDirectory(), "system_ext", "etc", "compatconfig"));
+
+        List<ApexManager.ActiveApexInfo> apexes = ApexManager.getInstance().getActiveApexInfos();
+        for (ApexManager.ActiveApexInfo apex : apexes) {
+            config.initConfigFromLib(Environment.buildPath(
+                    apex.apexDirectory, "etc", "compatconfig"));
+        }
         return config;
     }
 
     void initConfigFromLib(File libraryDir) {
         if (!libraryDir.exists() || !libraryDir.isDirectory()) {
-            Slog.e(TAG, "No directory " + libraryDir + ", skipping");
+            Slog.d(TAG, "No directory " + libraryDir + ", skipping");
             return;
         }
         for (File f : libraryDir.listFiles()) {
diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java
index 4d5af9a..bb8b12e 100644
--- a/services/core/java/com/android/server/compat/PlatformCompat.java
+++ b/services/core/java/com/android/server/compat/PlatformCompat.java
@@ -16,11 +16,6 @@
 
 package com.android.server.compat;
 
-import static android.Manifest.permission.LOG_COMPAT_CHANGE;
-import static android.Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG;
-import static android.Manifest.permission.READ_COMPAT_CHANGE_CONFIG;
-import static android.content.pm.PackageManager.PERMISSION_GRANTED;
-
 import android.app.ActivityManager;
 import android.app.IActivityManager;
 import android.content.Context;
@@ -73,14 +68,12 @@
 
     @Override
     public void reportChange(long changeId, ApplicationInfo appInfo) {
-        checkCompatChangeLogPermission();
         reportChange(changeId, appInfo.uid,
                 StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__LOGGED);
     }
 
     @Override
     public void reportChangeByPackageName(long changeId, String packageName, int userId) {
-        checkCompatChangeLogPermission();
         ApplicationInfo appInfo = getApplicationInfo(packageName, userId);
         if (appInfo == null) {
             return;
@@ -90,13 +83,11 @@
 
     @Override
     public void reportChangeByUid(long changeId, int uid) {
-        checkCompatChangeLogPermission();
         reportChange(changeId, uid, StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__LOGGED);
     }
 
     @Override
     public boolean isChangeEnabled(long changeId, ApplicationInfo appInfo) {
-        checkCompatChangeReadPermission();
         if (mCompatConfig.isChangeEnabled(changeId, appInfo)) {
             reportChange(changeId, appInfo.uid,
                     StatsLog.APP_COMPATIBILITY_CHANGE_REPORTED__STATE__ENABLED);
@@ -109,7 +100,6 @@
 
     @Override
     public boolean isChangeEnabledByPackageName(long changeId, String packageName, int userId) {
-        checkCompatChangeReadPermission();
         ApplicationInfo appInfo = getApplicationInfo(packageName, userId);
         if (appInfo == null) {
             return true;
@@ -119,7 +109,6 @@
 
     @Override
     public boolean isChangeEnabledByUid(long changeId, int uid) {
-        checkCompatChangeReadPermission();
         String[] packages = mContext.getPackageManager().getPackagesForUid(uid);
         if (packages == null || packages.length == 0) {
             return true;
@@ -152,7 +141,6 @@
     @Override
     public void setOverrides(CompatibilityChangeConfig overrides, String packageName)
             throws RemoteException, SecurityException {
-        checkCompatChangeOverridePermission();
         mCompatConfig.addOverrides(overrides, packageName);
         killPackage(packageName);
     }
@@ -160,13 +148,11 @@
     @Override
     public void setOverridesForTest(CompatibilityChangeConfig overrides, String packageName)
             throws RemoteException, SecurityException {
-        checkCompatChangeOverridePermission();
         mCompatConfig.addOverrides(overrides, packageName);
     }
 
     @Override
     public void clearOverrides(String packageName) throws RemoteException, SecurityException {
-        checkCompatChangeOverridePermission();
         mCompatConfig.removePackageOverrides(packageName);
         killPackage(packageName);
     }
@@ -174,14 +160,12 @@
     @Override
     public void clearOverridesForTest(String packageName)
             throws RemoteException, SecurityException {
-        checkCompatChangeOverridePermission();
         mCompatConfig.removePackageOverrides(packageName);
     }
 
     @Override
     public boolean clearOverride(long changeId, String packageName)
             throws RemoteException, SecurityException {
-        checkCompatChangeOverridePermission();
         boolean existed = mCompatConfig.removeOverride(changeId, packageName);
         killPackage(packageName);
         return existed;
@@ -189,13 +173,11 @@
 
     @Override
     public CompatibilityChangeConfig getAppConfig(ApplicationInfo appInfo) {
-        checkCompatChangeReadPermission();
         return mCompatConfig.getAppConfig(appInfo);
     }
 
     @Override
     public CompatibilityChangeInfo[] listAllChanges() {
-        checkCompatChangeReadPermission();
         return mCompatConfig.dumpChanges();
     }
 
@@ -234,7 +216,6 @@
 
     @Override
     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        checkCompatChangeReadPermission();
         if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, "platform_compat", pw)) return;
         mCompatConfig.dumpConfig(pw);
     }
@@ -292,25 +273,4 @@
             Binder.restoreCallingIdentity(identity);
         }
     }
-
-    private void checkCompatChangeLogPermission() throws SecurityException {
-        if (mContext.checkCallingOrSelfPermission(LOG_COMPAT_CHANGE)
-                != PERMISSION_GRANTED) {
-            throw new SecurityException("Cannot log compat change usage");
-        }
-    }
-
-    private void checkCompatChangeReadPermission() throws SecurityException {
-        if (mContext.checkCallingOrSelfPermission(READ_COMPAT_CHANGE_CONFIG)
-                != PERMISSION_GRANTED) {
-            throw new SecurityException("Cannot read compat change");
-        }
-    }
-
-    private void checkCompatChangeOverridePermission() throws SecurityException {
-        if (mContext.checkCallingOrSelfPermission(OVERRIDE_COMPAT_CHANGE_CONFIG)
-                != PERMISSION_GRANTED) {
-            throw new SecurityException("Cannot override compat change");
-        }
-    }
 }
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index d66aec5..3cfe916 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -483,7 +483,7 @@
             return ConnectivityConstants.EXPLICITLY_SELECTED_NETWORK_SCORE;
         }
 
-        int score = mNetworkScore.getIntExtension(NetworkScore.LEGACY_SCORE);
+        int score = mNetworkScore.getLegacyScore();
         if (!lastValidated && !pretendValidated && !ignoreWifiUnvalidationPenalty() && !isVPN()) {
             score -= ConnectivityConstants.UNVALIDATED_SCORE_PENALTY;
         }
diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java
index decb1f9..96532f4 100644
--- a/services/core/java/com/android/server/display/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/DisplayModeDirector.java
@@ -1050,6 +1050,7 @@
 
         public void dumpLocked(PrintWriter pw) {
             pw.println("  BrightnessObserver");
+            pw.println("    mAmbientLux: " + mAmbientLux);
             pw.println("    mRefreshRateInZone: " + mRefreshRateInZone);
 
             for (int d: mDisplayBrightnessThresholds) {
@@ -1059,6 +1060,8 @@
             for (int d: mAmbientBrightnessThresholds) {
                 pw.println("    mAmbientBrightnessThreshold: " + d);
             }
+
+            mLightSensorListener.dumpLocked(pw);
         }
 
         public void onDisplayChanged(int displayId) {
@@ -1222,6 +1225,10 @@
             final private static int INJECT_EVENTS_INTERVAL_MS = LIGHT_SENSOR_RATE_MS;
             private float mLastSensorData;
 
+            public void dumpLocked(PrintWriter pw) {
+                pw.println("    mLastSensorData: " + mLastSensorData);
+            }
+
             @Override
             public void onSensorChanged(SensorEvent event) {
                 mLastSensorData = event.values[0];
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 7104790..68cc014 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -2735,7 +2735,7 @@
                     // TODO(b/144152069): Remove informative toast
                     mUiHandler.post(() -> Toast.makeText(getContext(),
                             "Background custom toast blocked for package " + pkg + ".\n"
-                                    + "See go/r-toast-block.",
+                                    + "See g.co/dev/toast.",
                             Toast.LENGTH_SHORT).show());
                     Slog.w(TAG, "Blocking custom toast from package " + pkg
                             + " due to package not in the foreground");
diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java
index 90afeff..4ff37a2 100644
--- a/services/core/java/com/android/server/pm/ApexManager.java
+++ b/services/core/java/com/android/server/pm/ApexManager.java
@@ -95,6 +95,7 @@
      * Returns an instance of either {@link ApexManagerImpl} or {@link ApexManagerFlattenedApex}
      * depending on whether this device supports APEX, i.e. {@link ApexProperties#updatable()}
      * evaluates to {@code true}.
+     * @hide
      */
     public static ApexManager getInstance() {
         return sApexManagerSingleton.get();
@@ -102,8 +103,9 @@
 
     /**
      * Minimal information about APEX mount points and the original APEX package they refer to.
+     * @hide
      */
-    static class ActiveApexInfo {
+    public static class ActiveApexInfo {
         @Nullable public final String apexModuleName;
         public final File apexDirectory;
         public final File preInstalledApexPath;
@@ -130,8 +132,10 @@
 
     /**
      * Returns {@link ActiveApexInfo} records relative to all active APEX packages.
+     *
+     * @hide
      */
-    abstract List<ActiveApexInfo> getActiveApexInfos();
+    public abstract List<ActiveApexInfo> getActiveApexInfos();
 
     abstract void systemReady(Context context);
 
@@ -362,7 +366,7 @@
         }
 
         @Override
-        List<ActiveApexInfo> getActiveApexInfos() {
+        public List<ActiveApexInfo> getActiveApexInfos() {
             synchronized (mLock) {
                 if (mActiveApexInfosCache == null) {
                     try {
@@ -798,7 +802,7 @@
      */
     private static final class ApexManagerFlattenedApex extends ApexManager {
         @Override
-        List<ActiveApexInfo> getActiveApexInfos() {
+        public List<ActiveApexInfo> getActiveApexInfos() {
             // There is no apexd running in case of flattened apex
             // We look up the /apex directory and identify the active APEX modules from there.
             // As "preinstalled" path, we just report /system since in the case of flattened APEX
diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
index c712431..ba7583f 100644
--- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java
+++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
@@ -36,9 +36,9 @@
 import android.os.storage.StorageManager;
 import android.util.ArraySet;
 import android.util.Log;
-import android.util.StatsLog;
 
 import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.FrameworkStatsLog;
 import com.android.server.LocalServices;
 import com.android.server.PinnerService;
 import com.android.server.pm.dex.DexManager;
@@ -444,7 +444,7 @@
         }
 
         if (dex_opt_performed) {
-            StatsLog.write(StatsLog.APP_DOWNGRADED, pkg, package_size_before,
+            FrameworkStatsLog.write(FrameworkStatsLog.APP_DOWNGRADED, pkg, package_size_before,
                     getPackageSize(pm, pkg), /*aggressive=*/ false);
         }
         return dex_opt_performed;
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index f962eed..40ea6cf 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -450,16 +450,6 @@
         }
     }
 
-    public void markBootComplete(String instructionSet) throws InstallerException {
-        assertValidInstructionSet(instructionSet);
-        if (!checkBeforeRemote()) return;
-        try {
-            mInstalld.markBootComplete(instructionSet);
-        } catch (Exception e) {
-            throw InstallerException.from(e);
-        }
-    }
-
     public void freeCache(String uuid, long targetFreeBytes, long cacheReservedBytes, int flags)
             throws InstallerException {
         if (!checkBeforeRemote()) return;
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 02ec472..93a0506 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -288,7 +288,6 @@
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
 import android.util.SparseIntArray;
-import android.util.StatsLog;
 import android.util.TimingsTraceLog;
 import android.util.Xml;
 import android.util.apk.ApkSignatureVerifier;
@@ -310,6 +309,7 @@
 import com.android.internal.util.ConcurrentUtils;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.FrameworkStatsLog;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.Preconditions;
 import com.android.server.AttributeCache;
@@ -2163,7 +2163,8 @@
                                 getPackageExternalStorageType(volume, isExternal(res.pkg));
                         // If the package was installed externally, log it.
                         if (packageExternalStorageType != StorageEnums.UNKNOWN) {
-                            StatsLog.write(StatsLog.APP_INSTALL_ON_EXTERNAL_STORAGE_REPORTED,
+                            FrameworkStatsLog.write(
+                                    FrameworkStatsLog.APP_INSTALL_ON_EXTERNAL_STORAGE_REPORTED,
                                     packageExternalStorageType, res.pkg.getPackageName());
                         }
                     }
@@ -19706,9 +19707,7 @@
         if (packageName == null) {
             return null;
         }
-        if (getPackageInfo(packageName, MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE
-                        | MATCH_DIRECT_BOOT_UNAWARE | MATCH_DISABLED_COMPONENTS,
-                UserHandle.getCallingUserId()) == null) {
+        if (getPackageInfo(packageName, MATCH_FACTORY_ONLY, UserHandle.USER_SYSTEM) == null) {
             return null;
         }
         return packageName;
@@ -22179,13 +22178,15 @@
 
         if (!isPreviousLocationExternal && isExternal(pkg)) {
             // Move from internal to external storage.
-            StatsLog.write(StatsLog.APP_MOVED_STORAGE_REPORTED, packageExternalStorageType,
-                    StatsLog.APP_MOVED_STORAGE_REPORTED__MOVE_TYPE__TO_EXTERNAL,
+            FrameworkStatsLog.write(FrameworkStatsLog.APP_MOVED_STORAGE_REPORTED,
+                    packageExternalStorageType,
+                    FrameworkStatsLog.APP_MOVED_STORAGE_REPORTED__MOVE_TYPE__TO_EXTERNAL,
                     packageName);
         } else if (isPreviousLocationExternal && !isExternal(pkg)) {
             // Move from external to internal storage.
-            StatsLog.write(StatsLog.APP_MOVED_STORAGE_REPORTED, packageExternalStorageType,
-                    StatsLog.APP_MOVED_STORAGE_REPORTED__MOVE_TYPE__TO_INTERNAL,
+            FrameworkStatsLog.write(FrameworkStatsLog.APP_MOVED_STORAGE_REPORTED,
+                    packageExternalStorageType,
+                    FrameworkStatsLog.APP_MOVED_STORAGE_REPORTED__MOVE_TYPE__TO_INTERNAL,
                     packageName);
         }
     }
diff --git a/services/core/java/com/android/server/timezone/RulesManagerService.java b/services/core/java/com/android/server/timezone/RulesManagerService.java
index cdf8ea3..bbd1ae6 100644
--- a/services/core/java/com/android/server/timezone/RulesManagerService.java
+++ b/services/core/java/com/android/server/timezone/RulesManagerService.java
@@ -198,7 +198,7 @@
                     Slog.w(TAG, "Failed to read staged distro.", e);
                 }
             }
-            return new RulesState(baseVersion.rulesVersion, DISTRO_FORMAT_VERSION_SUPPORTED,
+            return new RulesState(baseVersion.getRulesVersion(), DISTRO_FORMAT_VERSION_SUPPORTED,
                     operationInProgress, stagedOperationStatus, stagedDistroRulesVersion,
                     distroStatus, installedDistroRulesVersion);
         }
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index dda11f9..b05c250 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -3074,7 +3074,11 @@
         mStackSupervisor.getActivityMetricsLogger().notifyActivityRemoved(this);
         waitingToShow = false;
 
-        boolean delayed = isAnimating(TRANSITION | CHILDREN);
+        // Defer removal of this activity when either a child is animating, or app transition is on
+        // going. App transition animation might be applied on the parent stack not on the activity,
+        // but the actual frame buffer is associated with the activity, so we have to keep the
+        // activity while a parent is animating.
+        boolean delayed = isAnimating(TRANSITION | PARENTS | CHILDREN);
         if (getDisplayContent().mClosingApps.contains(this)) {
             delayed = true;
         } else if (getDisplayContent().mAppTransition.isTransitionSet()) {
diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java
index ddf0117..0d72d84 100644
--- a/services/core/java/com/android/server/wm/ActivityStack.java
+++ b/services/core/java/com/android/server/wm/ActivityStack.java
@@ -341,6 +341,9 @@
 
     private static final int TRANSLUCENT_TIMEOUT_MSG = FIRST_ACTIVITY_STACK_MSG + 1;
 
+    // TODO(task-hierarchy): remove when tiles can be actual parents
+    TaskTile mTile = null;
+
     private final Handler mHandler;
 
     private class ActivityStackHandler extends Handler {
@@ -638,11 +641,20 @@
     }
 
     @Override
+    public void resolveOverrideConfiguration(Configuration newParentConfig) {
+        super.resolveOverrideConfiguration(newParentConfig);
+        if (mTile != null) {
+            // If this is a virtual child of a tile, simulate the parent-child relationship
+            mTile.updateResolvedConfig(getResolvedOverrideConfiguration());
+        }
+    }
+
+    @Override
     public void onConfigurationChanged(Configuration newParentConfig) {
         // Calling Task#onConfigurationChanged() for leaf task since the ops in this method are
         // particularly for ActivityStack, like preventing bounds changes when inheriting certain
         // windowing mode.
-        if (!isRootTask()) {
+        if (!isRootTask() || this instanceof TaskTile) {
             super.onConfigurationChanged(newParentConfig);
             return;
         }
@@ -3944,7 +3956,6 @@
                 ? ((WindowContainer) newParent).getDisplayContent() : null;
         final DisplayContent oldDisplay = oldParent != null
                 ? ((WindowContainer) oldParent).getDisplayContent() : null;
-
         super.onParentChanged(newParent, oldParent);
 
         if (display != null && inSplitScreenPrimaryWindowingMode()
@@ -3963,6 +3974,11 @@
         if (oldDisplay != null && oldDisplay.isRemoving()) {
             postReparent();
         }
+        if (mTile != null && getSurfaceControl() != null) {
+            // by now, the TaskStack should already have been reparented, so we can reparent its
+            // surface here
+            reparentSurfaceControl(getPendingTransaction(), mTile.getSurfaceControl());
+        }
     }
 
     void reparent(DisplayContent newParent, boolean onTop) {
@@ -4000,7 +4016,16 @@
 
     @Override
     void getRelativeDisplayedPosition(Point outPos) {
-        super.getRelativeDisplayedPosition(outPos);
+        // check for tile which is "virtually" a parent.
+        if (mTile != null) {
+            final Rect dispBounds = getDisplayedBounds();
+            outPos.set(dispBounds.left, dispBounds.top);
+            final Rect parentBounds = mTile.getBounds();
+            outPos.offset(-parentBounds.left, -parentBounds.top);
+        } else {
+            super.getRelativeDisplayedPosition(outPos);
+        }
+
         final int outset = getStackOutset();
         outPos.x -= outset;
         outPos.y -= outset;
@@ -4010,6 +4035,16 @@
         if (mSurfaceControl == null) {
             return;
         }
+        if (mTile != null) {
+            // Tile controls crop, so the app needs to be able to draw its background outside of
+            // the stack bounds for when the tile crop gets bigger than the stack.
+            if (mLastSurfaceSize.equals(0, 0)) {
+                return;
+            }
+            transaction.setWindowCrop(mSurfaceControl, null);
+            mLastSurfaceSize.set(0, 0);
+            return;
+        }
 
         final Rect stackBounds = getDisplayedBounds();
         int width = stackBounds.width();
@@ -4033,6 +4068,9 @@
 
     @Override
     void onDisplayChanged(DisplayContent dc) {
+        if (mTile != null && dc != mTile.getDisplay()) {
+            mTile.removeChild(this);
+        }
         super.onDisplayChanged(dc);
         if (isRootTask()) {
             updateSurfaceBounds();
@@ -4848,6 +4886,42 @@
         return shouldSleepActivities() || mAtmService.mShuttingDown;
     }
 
+    TaskTile getTile() {
+        return mTile;
+    }
+
+    /**
+     * Don't call this directly. instead use {@link TaskTile#addChild} or
+     * {@link TaskTile#removeChild}.
+     */
+    void setTile(TaskTile tile) {
+        TaskTile origTile = mTile;
+        mTile = tile;
+        final ConfigurationContainer parent = getParent();
+        if (parent != null) {
+            onConfigurationChanged(parent.getConfiguration());
+        }
+
+        // Reparent to tile surface or back to original parent
+        if (getSurfaceControl() == null) {
+            return;
+        }
+        if (mTile != null) {
+            reparentSurfaceControl(getPendingTransaction(), mTile.getSurfaceControl());
+        } else if (mTile == null && origTile != null) {
+            reparentSurfaceControl(getPendingTransaction(), getParentSurfaceControl());
+        }
+    }
+
+    @Override
+    void removeImmediately() {
+        // TODO(task-hierarchy): remove this override when tiles are in hierarchy
+        if (mTile != null) {
+            mTile.removeChild(this);
+        }
+        super.removeImmediately();
+    }
+
     @Override
     public void dumpDebug(ProtoOutputStream proto, long fieldId,
             @WindowTraceLogLevel int logLevel) {
diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
index 362e781..a513ef8 100644
--- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
@@ -1711,6 +1711,7 @@
              * invisible as well and added to the stopping list.  After which we process the
              * stopping list by handling the idle.
              */
+            stack.cancelAnimation();
             stack.mForceHidden = true;
             stack.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);
             stack.mForceHidden = false;
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index f019013..d6e7077 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -144,6 +144,7 @@
 import android.app.IAssistDataReceiver;
 import android.app.INotificationManager;
 import android.app.IRequestFinishCallback;
+import android.app.ITaskOrganizerController;
 import android.app.ITaskStackListener;
 import android.app.Notification;
 import android.app.NotificationManager;
@@ -225,10 +226,8 @@
 import android.util.TimeUtils;
 import android.util.proto.ProtoOutputStream;
 import android.view.IRecentsAnimationRunner;
-import android.view.ITaskOrganizer;
 import android.view.RemoteAnimationAdapter;
 import android.view.RemoteAnimationDefinition;
-import android.view.WindowContainerTransaction;
 import android.view.WindowManager;
 
 import com.android.internal.R;
@@ -292,7 +291,6 @@
 import java.util.Date;
 import java.util.HashMap;
 import java.util.HashSet;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
@@ -344,10 +342,6 @@
     /** This activity is being relaunched due to a free-resize operation. */
     public static final int RELAUNCH_REASON_FREE_RESIZE = 2;
 
-    /** Flag indicating that an applied transaction may have effected lifecycle */
-    private static final int TRANSACT_EFFECTS_CLIENT_CONFIG = 1;
-    private static final int TRANSACT_EFFECTS_LIFECYCLE = 1 << 1;
-
     Context mContext;
 
     /**
@@ -669,8 +663,7 @@
     /**
      * Stores the registration and state of TaskOrganizers in use.
      */
-    TaskOrganizerController mTaskOrganizerController =
-        new TaskOrganizerController(this, mGlobalLock);
+    TaskOrganizerController mTaskOrganizerController = new TaskOrganizerController(this);
 
     private int mDeviceOwnerUid = Process.INVALID_UID;
 
@@ -1286,15 +1279,6 @@
     }
 
     @Override
-    public final void registerTaskOrganizer(ITaskOrganizer organizer, int windowingMode) {
-        enforceCallerIsRecentsOrHasPermission(
-                MANAGE_ACTIVITY_STACKS, "registerTaskOrganizer()");
-        synchronized (mGlobalLock) {
-            mTaskOrganizerController.registerTaskOrganizer(organizer, windowingMode);
-        }
-    }
-
-    @Override
     public IBinder requestStartActivityPermissionToken(IBinder delegatorToken) {
         int callingUid = Binder.getCallingUid();
         if (UserHandle.getAppId(callingUid) != SYSTEM_UID) {
@@ -3304,116 +3288,6 @@
         }
     }
 
-    private int sanitizeAndApplyChange(WindowContainer container,
-            WindowContainerTransaction.Change change) {
-        if (!(container instanceof Task || container instanceof ActivityStack)) {
-            throw new RuntimeException("Invalid token in task transaction");
-        }
-        // The "client"-facing API should prevent bad changes; however, just in case, sanitize
-        // masks here.
-        int configMask = change.getConfigSetMask();
-        int windowMask = change.getWindowSetMask();
-        configMask &= ActivityInfo.CONFIG_WINDOW_CONFIGURATION
-                | ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE;
-        windowMask &= WindowConfiguration.WINDOW_CONFIG_BOUNDS;
-        int effects = 0;
-        if (configMask != 0) {
-            Configuration c = new Configuration(container.getRequestedOverrideConfiguration());
-            c.setTo(change.getConfiguration(), configMask, windowMask);
-            container.onRequestedOverrideConfigurationChanged(c);
-            // TODO(b/145675353): remove the following once we could apply new bounds to the
-            // pinned stack together with its children.
-            resizePinnedStackIfNeeded(container, configMask, windowMask, c);
-            effects |= TRANSACT_EFFECTS_CLIENT_CONFIG;
-        }
-        if ((change.getChangeMask() & WindowContainerTransaction.Change.CHANGE_FOCUSABLE) != 0) {
-            if (container.setFocusable(change.getFocusable())) {
-                effects |= TRANSACT_EFFECTS_LIFECYCLE;
-            }
-        }
-        return effects;
-    }
-
-    private void resizePinnedStackIfNeeded(ConfigurationContainer container, int configMask,
-            int windowMask, Configuration config) {
-        if ((container instanceof ActivityStack)
-                && ((configMask & ActivityInfo.CONFIG_WINDOW_CONFIGURATION) != 0)
-                && ((windowMask & WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0)) {
-            final ActivityStack stack = (ActivityStack) container;
-            if (stack.inPinnedWindowingMode()) {
-                stack.resize(config.windowConfiguration.getBounds(),
-                        null /* tempTaskBounds */, null /* tempTaskInsetBounds */,
-                        PRESERVE_WINDOWS, true /* deferResume */);
-            }
-        }
-    }
-
-    private int applyWindowContainerChange(WindowContainer wc,
-            WindowContainerTransaction.Change c) {
-        int effects = sanitizeAndApplyChange(wc, c);
-
-        Rect enterPipBounds = c.getEnterPipBounds();
-        if (enterPipBounds != null) {
-            Task tr = (Task) wc;
-            mStackSupervisor.updatePictureInPictureMode(tr,
-                    enterPipBounds, true);
-        }
-        return effects;
-    }
-
-    @Override
-    public void applyContainerTransaction(WindowContainerTransaction t) {
-        mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "applyContainerTransaction()");
-        if (t == null) {
-            return;
-        }
-        long ident = Binder.clearCallingIdentity();
-        try {
-            synchronized (mGlobalLock) {
-                int effects = 0;
-                deferWindowLayout();
-                try {
-                    ArraySet<WindowContainer> haveConfigChanges = new ArraySet<>();
-                    Iterator<Map.Entry<IBinder, WindowContainerTransaction.Change>> entries =
-                            t.getChanges().entrySet().iterator();
-                    while (entries.hasNext()) {
-                        final Map.Entry<IBinder, WindowContainerTransaction.Change> entry =
-                                entries.next();
-                        final WindowContainer wc = WindowContainer.RemoteToken.fromBinder(
-                                entry.getKey()).getContainer();
-                        int containerEffect = applyWindowContainerChange(wc, entry.getValue());
-                        effects |= containerEffect;
-                        // Lifecycle changes will trigger ensureConfig for everything.
-                        if ((effects & TRANSACT_EFFECTS_LIFECYCLE) == 0
-                                && (containerEffect & TRANSACT_EFFECTS_CLIENT_CONFIG) != 0) {
-                            haveConfigChanges.add(wc);
-                        }
-                    }
-                    if ((effects & TRANSACT_EFFECTS_LIFECYCLE) != 0) {
-                        // Already calls ensureActivityConfig
-                        mRootWindowContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);
-                    } else if ((effects & TRANSACT_EFFECTS_CLIENT_CONFIG) != 0) {
-                        final PooledConsumer f = PooledLambda.obtainConsumer(
-                                ActivityRecord::ensureActivityConfiguration,
-                                PooledLambda.__(ActivityRecord.class), 0,
-                                false /* preserveWindow */);
-                        try {
-                            for (int i = haveConfigChanges.size() - 1; i >= 0; --i) {
-                                haveConfigChanges.valueAt(i).forAllActivities(f);
-                            }
-                        } finally {
-                            f.recycle();
-                        }
-                    }
-                } finally {
-                    continueWindowLayout();
-                }
-            }
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
-    }
-
     @Override
     public boolean releaseActivityInstance(IBinder token) {
         synchronized (mGlobalLock) {
@@ -4442,6 +4316,13 @@
         }
     }
 
+    @Override
+    public ITaskOrganizerController getTaskOrganizerController() {
+        mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS,
+                "getTaskOrganizerController()");
+        return mTaskOrganizerController;
+    }
+
     /**
      * Check that we have the features required for VR-related API calls, and throw an exception if
      * not.
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index 3927d5f..0798a91 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -431,6 +431,8 @@
         while (!candidates.isEmpty()) {
             final WindowContainer current = candidates.removeFirst();
             final WindowContainer parent = current.getParent();
+            siblings.clear();
+            siblings.add(current);
             boolean canPromote = true;
 
             if (parent == null) {
@@ -468,12 +470,11 @@
                 //
                 // [Task] +- [ActivityRecord1] (visible, in opening apps)
                 //        +- [ActivityRecord2] (visible, not in opening apps)
-                siblings.clear();
                 for (int j = 0; j < parent.getChildCount(); ++j) {
                     final WindowContainer sibling = parent.getChildAt(j);
-                    if (sibling == current || candidates.remove(sibling)) {
+                    if (candidates.remove(sibling)) {
                         siblings.add(sibling);
-                    } else if (sibling.isVisible()) {
+                    } else if (sibling != current && sibling.isVisible()) {
                         canPromote = false;
                     }
                 }
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 6e479b2..9f4cd88 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -207,6 +207,7 @@
 import android.view.Gravity;
 import android.view.IDisplayWindowInsetsController;
 import android.view.ISystemGestureExclusionListener;
+import android.view.ITaskOrganizer;
 import android.view.IWindow;
 import android.view.InputChannel;
 import android.view.InputDevice;
@@ -664,6 +665,9 @@
     private final RootWindowContainer.FindTaskResult
             mTmpFindTaskResult = new RootWindowContainer.FindTaskResult();
 
+    // When non-null, new stacks get put into this tile.
+    TaskTile mLaunchTile = null;
+
     private final Consumer<WindowState> mUpdateWindowsForAnimator = w -> {
         WindowStateAnimator winAnimator = w.mWinAnimator;
         final ActivityRecord activity = w.mActivityRecord;
@@ -4275,8 +4279,15 @@
 
         @VisibleForTesting
         ActivityStack getTopStack() {
-            return mTaskContainers.getChildCount() > 0
-                    ? mTaskContainers.getChildAt(mTaskContainers.getChildCount() - 1) : null;
+            // TODO(task-hierarchy): Just grab index -1 once tiles are in hierarchy.
+            for (int i = mTaskContainers.getChildCount() - 1; i >= 0; --i) {
+                final ActivityStack child = mTaskContainers.getChildAt(i);
+                if (child instanceof TaskTile) {
+                    continue;
+                }
+                return child;
+            }
+            return null;
         }
 
         int getIndexOf(ActivityStack stack) {
@@ -4318,6 +4329,10 @@
         }
 
         private void addStackReferenceIfNeeded(ActivityStack stack) {
+            // TODO(task-hierarchy): Remove when tiles are in hierarchy.
+            if (stack instanceof TaskTile) {
+                return;
+            }
             if (stack.isActivityTypeHome()) {
                 if (mRootHomeTask != null) {
                     if (!stack.isDescendantOf(mRootHomeTask)) {
@@ -4735,6 +4750,17 @@
                 mSplitScreenDividerAnchor = null;
             }
         }
+
+        @Override
+        void onChildPositionChanged(WindowContainer child) {
+            // TODO(task-hierarchy): Move functionality to TaskTile when it's a proper parent.
+            TaskTile tile = ((ActivityStack) child).getTile();
+            if (tile == null) {
+                return;
+            }
+            mAtmService.mTaskOrganizerController.dispatchTaskInfoChanged(
+                    tile, false /* force */);
+        }
     }
 
     private final class AboveAppWindowContainers extends NonAppWindowContainers {
@@ -6271,6 +6297,10 @@
     }
 
     boolean isTopNotPinnedStack(ActivityStack stack) {
+        // TODO(task-hierarchy): Remove when tiles are in hierarchy.
+        if (stack instanceof TaskTile) {
+            return false;
+        }
         for (int i = getStackCount() - 1; i >= 0; --i) {
             final ActivityStack current = getStackAt(i);
             if (!current.inPinnedWindowingMode()) {
@@ -6685,6 +6715,19 @@
         return getHomeActivityForUser(mRootWindowContainer.mCurrentUser);
     }
 
+    // TODO(task-hierarchy): Remove when tiles are in hierarchy.
+    void addTile(TaskTile tile) {
+        mTaskContainers.addChild(tile, POSITION_BOTTOM);
+        ITaskOrganizer organizer = mAtmService.mTaskOrganizerController.getTaskOrganizer(
+                tile.getWindowingMode());
+        tile.setTaskOrganizer(organizer);
+    }
+
+    // TODO(task-hierarchy): Remove when tiles are in hierarchy.
+    void removeTile(TaskTile tile) {
+        mTaskContainers.removeChild(tile);
+    }
+
     @Nullable
     ActivityRecord getHomeActivityForUser(int userId) {
         final ActivityStack homeStack = getRootHomeTask();
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index da77314..efe79b3 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -526,7 +526,7 @@
             mService.mH.removeCallbacks(mDisplayRotationHandlerTimeout);
             mIsWaitingForRemoteRotation = false;
             mDisplayContent.sendNewConfiguration();
-            mService.mAtmService.applyContainerTransaction(t);
+            mService.mAtmService.mTaskOrganizerController.applyContainerTransaction(t);
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index 568966a..289ac4c 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -60,6 +60,8 @@
             w.notifyInsetsChanged();
         }
     };
+    private final InsetsControlTarget mEmptyImeControlTarget = () -> {
+    };
 
     InsetsStateController(DisplayContent displayContent) {
         mDisplayContent = displayContent;
@@ -182,7 +184,10 @@
     }
 
     void onImeControlTargetChanged(@Nullable InsetsControlTarget imeTarget) {
-        onControlChanged(ITYPE_IME, imeTarget);
+
+        // Make sure that we always have a control target for the IME, even if the IME target is
+        // null. Otherwise there is no leash that will hide it and IME becomes "randomly" visible.
+        onControlChanged(ITYPE_IME, imeTarget != null ? imeTarget : mEmptyImeControlTarget);
         notifyPendingInsetsControlChanged();
     }
 
diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java
index 647be0f..9770947 100644
--- a/services/core/java/com/android/server/wm/RecentsAnimation.java
+++ b/services/core/java/com/android/server/wm/RecentsAnimation.java
@@ -388,11 +388,12 @@
                     // surfaces needs to be done immediately.
                     mWindowManager.executeAppTransition();
 
-                    // After reordering the stacks, reset the minimized state. At this point, either
-                    // the target activity is now top-most and we will stay minimized (if in
-                    // split-screen), or we will have returned to the app, and the minimized state
-                    // should be reset
-                    mWindowManager.checkSplitScreenMinimizedChanged(true /* animate */);
+                    if (targetStack.getTile() != null) {
+                        // Client state may have changed during the recents animation, so force
+                        // send task info so the client can synchronize its state.
+                        mService.mTaskOrganizerController.dispatchTaskInfoChanged(
+                                targetStack.mTile, true /* force */);
+                    }
                 } catch (Exception e) {
                     Slog.e(TAG, "Failed to clean up recents activity", e);
                     throw e;
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index e6fd512..a13399b 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -1012,6 +1012,9 @@
 
         mWmService.scheduleAnimationLocked();
 
+        // Send any pending task-info changes that were queued-up during a layout deferment
+        mWmService.mAtmService.mTaskOrganizerController.dispatchPendingTaskInfoChanges();
+
         if (DEBUG_WINDOW_TRACE) Slog.e(TAG,
                 "performSurfacePlacementInner exit: animating="
                         + mWmService.mAnimator.isAnimating());
@@ -2959,6 +2962,12 @@
             case ACTIVITY_TYPE_RECENTS: return r.isActivityTypeRecents();
             case ACTIVITY_TYPE_ASSISTANT: return r.isActivityTypeAssistant();
         }
+        // TODO(task-hierarchy): Find another way to differentiate tile from normal stack once it is
+        //                       part of the hierarchy
+        if (stack instanceof TaskTile) {
+            // Don't launch directly into tiles.
+            return false;
+        }
         // There is a 1-to-1 relationship between stack and task when not in
         // primary split-windowing mode.
         if (stack.getWindowingMode() == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 36cae1f..271f559 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -1257,7 +1257,7 @@
         if (affinityIntent != null) return affinityIntent;
         // Probably a task that contains other tasks, so return the intent for the top task?
         final Task topTask = getTopMostTask();
-        return topTask != null ? topTask.getBaseIntent() : null;
+        return (topTask != this && topTask != null) ? topTask.getBaseIntent() : null;
     }
 
     /** Returns the first non-finishing activity from the bottom. */
@@ -3214,7 +3214,8 @@
         info.taskId = mTaskId;
         info.displayId = getDisplayId();
         info.isRunning = getTopNonFinishingActivity() != null;
-        info.baseIntent = new Intent(getBaseIntent());
+        final Intent baseIntent = getBaseIntent();
+        info.baseIntent = baseIntent == null ? new Intent() : baseIntent;
         info.baseActivity = mReuseActivitiesReport.base != null
                 ? mReuseActivitiesReport.base.intent.getComponent()
                 : null;
@@ -3229,6 +3230,10 @@
         info.supportsSplitScreenMultiWindow = supportsSplitScreenWindowingMode();
         info.resizeMode = mResizeMode;
         info.configuration.setTo(getConfiguration());
+        info.token = mRemoteToken;
+        // Get's the first non-undefined activity type among this and children. Can't use
+        // configuration.windowConfiguration because that would only be this level.
+        info.topActivityType = getActivityType();
     }
 
     /**
@@ -3375,7 +3380,7 @@
         if (affinity != null) {
             sb.append(" A=");
             sb.append(affinity);
-        } else if (intent != null) {
+        } else if (intent != null && intent.getComponent() != null) {
             sb.append(" I=");
             sb.append(intent.getComponent().flattenToShortString());
         } else if (affinityIntent != null && affinityIntent.getComponent() != null) {
@@ -3865,7 +3870,12 @@
 
     boolean isControlledByTaskOrganizer() {
         final Task rootTask = getRootTask();
-        return rootTask == this && rootTask.mTaskOrganizer != null;
+        return rootTask == this && rootTask.mTaskOrganizer != null
+                // TODO(task-hierarchy): Figure out how to control nested tasks.
+                // For now, if this is in a tile let WM drive.
+                && !(rootTask instanceof TaskTile)
+                && !(rootTask instanceof ActivityStack
+                        && ((ActivityStack) rootTask).getTile() != null);
     }
 
     @Override
@@ -3893,6 +3903,9 @@
    }
 
     void setTaskOrganizer(ITaskOrganizer organizer) {
+        if (mTaskOrganizer == organizer) {
+            return;
+        }
         // Let the old organizer know it has lost control.
         if (mTaskOrganizer != null) {
             sendTaskVanished();
@@ -3918,8 +3931,6 @@
     public void updateSurfacePosition() {
         // Avoid fighting with the TaskOrganizer over Surface position.
         if (isControlledByTaskOrganizer()) {
-            getPendingTransaction().setPosition(mSurfaceControl, 0, 0);
-            scheduleAnimation();
             return;
         } else {
             super.updateSurfacePosition();
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index 66c65e2..44a6fc9 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -16,26 +16,51 @@
 
 package com.android.server.wm;
 
-import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.Manifest.permission.MANAGE_ACTIVITY_STACKS;
 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
 
+import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
+
+import android.annotation.Nullable;
+import android.app.ActivityManager.RunningTaskInfo;
+import android.app.ITaskOrganizerController;
+import android.app.WindowConfiguration;
+import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
+import android.graphics.Rect;
+import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.util.ArraySet;
 import android.util.Slog;
 import android.view.ITaskOrganizer;
-import android.view.SurfaceControl;
+import android.view.IWindowContainer;
+import android.view.WindowContainerTransaction;
+
+import com.android.internal.util.function.pooled.PooledConsumer;
+import com.android.internal.util.function.pooled.PooledLambda;
 
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.WeakHashMap;
 
 /**
  * Stores the TaskOrganizers associated with a given windowing mode and
  * their associated state.
  */
-class TaskOrganizerController {
+class TaskOrganizerController extends ITaskOrganizerController.Stub {
     private static final String TAG = "TaskOrganizerController";
 
-    private WindowManagerGlobalLock mGlobalLock;
+    /** Flag indicating that an applied transaction may have effected lifecycle */
+    private static final int TRANSACT_EFFECTS_CLIENT_CONFIG = 1;
+    private static final int TRANSACT_EFFECTS_LIFECYCLE = 1 << 1;
+
+    private final WindowManagerGlobalLock mGlobalLock;
 
     private class DeathRecipient implements IBinder.DeathRecipient {
         int mWindowingMode;
@@ -87,11 +112,20 @@
 
     final HashMap<Integer, ITaskOrganizer> mTaskOrganizersByPendingSyncId = new HashMap();
 
+    private final WeakHashMap<Task, RunningTaskInfo> mLastSentTaskInfos = new WeakHashMap<>();
+    private final ArrayList<Task> mPendingTaskInfoChanges = new ArrayList<>();
+
     final ActivityTaskManagerService mService;
 
-    TaskOrganizerController(ActivityTaskManagerService atm, WindowManagerGlobalLock lock) {
+    RunningTaskInfo mTmpTaskInfo;
+
+    TaskOrganizerController(ActivityTaskManagerService atm) {
         mService = atm;
-        mGlobalLock = lock;
+        mGlobalLock = atm.mGlobalLock;
+    }
+
+    private void enforceStackPermission(String func) {
+        mService.mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, func);
     }
 
     private void clearIfNeeded(int windowingMode) {
@@ -106,26 +140,35 @@
      * If there was already a TaskOrganizer for this windowing mode it will be evicted
      * and receive taskVanished callbacks in the process.
      */
-    void registerTaskOrganizer(ITaskOrganizer organizer, int windowingMode) {
-        if (windowingMode != WINDOWING_MODE_PINNED &&
-            windowingMode != WINDOWING_MODE_MULTI_WINDOW) {
-            throw new UnsupportedOperationException(
-                    "As of now only Pinned and Multiwindow windowing modes are"
-                    + " supported for registerTaskOrganizer");
-
+    @Override
+    public void registerTaskOrganizer(ITaskOrganizer organizer, int windowingMode) {
+        if (windowingMode != WINDOWING_MODE_PINNED
+                && windowingMode != WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
+                && windowingMode != WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
+                && windowingMode != WINDOWING_MODE_MULTI_WINDOW) {
+            throw new UnsupportedOperationException("As of now only Pinned/Split/Multiwindow"
+                    + " windowing modes are supported for registerTaskOrganizer");
         }
-        clearIfNeeded(windowingMode);
-        DeathRecipient dr = new DeathRecipient(organizer, windowingMode);
+        enforceStackPermission("registerTaskOrganizer()");
+        final long origId = Binder.clearCallingIdentity();
         try {
-            organizer.asBinder().linkToDeath(dr, 0);
-        } catch (RemoteException e) {
-            Slog.e(TAG, "TaskOrganizer failed to register death recipient");
+            synchronized (mGlobalLock) {
+                clearIfNeeded(windowingMode);
+                DeathRecipient dr = new DeathRecipient(organizer, windowingMode);
+                try {
+                    organizer.asBinder().linkToDeath(dr, 0);
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "TaskOrganizer failed to register death recipient");
+                }
+
+                final TaskOrganizerState state = new TaskOrganizerState(organizer, dr);
+                mTaskOrganizersForWindowingMode.put(windowingMode, state);
+
+                mTaskOrganizerStates.put(organizer, state);
+            }
+        } finally {
+            Binder.restoreCallingIdentity(origId);
         }
-
-        final TaskOrganizerState state = new TaskOrganizerState(organizer, dr);
-        mTaskOrganizersForWindowingMode.put(windowingMode, state);
-
-        mTaskOrganizerStates.put(organizer, state);
     }
 
     ITaskOrganizer getTaskOrganizer(int windowingMode) {
@@ -138,7 +181,7 @@
 
     private void sendTaskAppeared(ITaskOrganizer organizer, Task task) {
         try {
-            organizer.taskAppeared(task.getRemoteToken(), task.getTaskInfo());
+            organizer.taskAppeared(task.getTaskInfo());
         } catch (Exception e) {
             Slog.e(TAG, "Exception sending taskAppeared callback" + e);
         }
@@ -167,4 +210,254 @@
         // we do this AFTER sending taskVanished.
         state.removeTask(task);
     }
+
+    @Override
+    public RunningTaskInfo createRootTask(int displayId, int windowingMode) {
+        enforceStackPermission("createRootTask()");
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                DisplayContent display = mService.mRootWindowContainer.getDisplayContent(displayId);
+                if (display == null) {
+                    return null;
+                }
+                final int nextId = display.getNextStackId();
+                TaskTile tile = new TaskTile(mService, nextId, windowingMode);
+                display.addTile(tile);
+                RunningTaskInfo out = new RunningTaskInfo();
+                tile.fillTaskInfo(out);
+                mLastSentTaskInfos.put(tile, out);
+                return out;
+            }
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
+
+    @Override
+    public boolean deleteRootTask(IWindowContainer token) {
+        enforceStackPermission("deleteRootTask()");
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                TaskTile tile = TaskTile.forToken(token.asBinder());
+                if (tile == null) {
+                    return false;
+                }
+                tile.removeImmediately();
+                return true;
+            }
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
+
+    void dispatchPendingTaskInfoChanges() {
+        if (mService.mWindowManager.mWindowPlacerLocked.isLayoutDeferred()) {
+            return;
+        }
+        for (int i = 0, n = mPendingTaskInfoChanges.size(); i < n; ++i) {
+            dispatchTaskInfoChanged(mPendingTaskInfoChanges.get(i), false /* force */);
+        }
+        mPendingTaskInfoChanges.clear();
+    }
+
+    void dispatchTaskInfoChanged(Task task, boolean force) {
+        if (!force && mService.mWindowManager.mWindowPlacerLocked.isLayoutDeferred()) {
+            // Defer task info reporting while layout is deferred. This is because layout defer
+            // blocks tend to do lots of re-ordering which can mess up animations in receivers.
+            mPendingTaskInfoChanges.remove(task);
+            mPendingTaskInfoChanges.add(task);
+            return;
+        }
+        RunningTaskInfo lastInfo = mLastSentTaskInfos.get(task);
+        if (mTmpTaskInfo == null) {
+            mTmpTaskInfo = new RunningTaskInfo();
+        }
+        task.fillTaskInfo(mTmpTaskInfo);
+        boolean changed = lastInfo == null
+                || mTmpTaskInfo.topActivityType != lastInfo.topActivityType
+                || mTmpTaskInfo.isResizable() != lastInfo.isResizable();
+        if (!(changed || force)) {
+            return;
+        }
+        final RunningTaskInfo newInfo = mTmpTaskInfo;
+        mLastSentTaskInfos.put(task, mTmpTaskInfo);
+        // Since we've stored this, clean up the reference so a new one will be created next time.
+        // Transferring it this way means we only have to construct new RunningTaskInfos when they
+        // change.
+        mTmpTaskInfo = null;
+
+        if (task.mTaskOrganizer != null) {
+            try {
+                task.mTaskOrganizer.onTaskInfoChanged(newInfo);
+            } catch (RemoteException e) {
+            }
+        }
+    }
+
+    @Override
+    public IWindowContainer getImeTarget(int displayId) {
+        enforceStackPermission("getImeTarget()");
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                DisplayContent dc = mService.mWindowManager.mRoot
+                        .getDisplayContent(displayId);
+                if (dc == null || dc.mInputMethodTarget == null) {
+                    return null;
+                }
+                // Avoid WindowState#getRootTask() so we don't attribute system windows to a task.
+                final Task task = dc.mInputMethodTarget.getTask();
+                if (task == null) {
+                    return null;
+                }
+                ActivityStack rootTask = (ActivityStack) task.getRootTask();
+                final TaskTile tile = rootTask.getTile();
+                if (tile != null) {
+                    rootTask = tile;
+                }
+                return rootTask.mRemoteToken;
+            }
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
+
+    @Override
+    public void setLaunchRoot(int displayId, @Nullable IWindowContainer tile) {
+        enforceStackPermission("setLaunchRoot()");
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                DisplayContent display = mService.mRootWindowContainer.getDisplayContent(displayId);
+                if (display == null) {
+                    return;
+                }
+                TaskTile taskTile = tile == null ? null : TaskTile.forToken(tile.asBinder());
+                if (taskTile == null) {
+                    display.mLaunchTile = null;
+                    return;
+                }
+                if (taskTile.getDisplay() != display) {
+                    throw new RuntimeException("Can't set launch root for display " + displayId
+                            + " to task on display " + taskTile.getDisplay().getDisplayId());
+                }
+                display.mLaunchTile = taskTile;
+            }
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
+
+    private int sanitizeAndApplyChange(WindowContainer container,
+            WindowContainerTransaction.Change change) {
+        if (!(container instanceof Task)) {
+            throw new RuntimeException("Invalid token in task transaction");
+        }
+        // The "client"-facing API should prevent bad changes; however, just in case, sanitize
+        // masks here.
+        int configMask = change.getConfigSetMask();
+        int windowMask = change.getWindowSetMask();
+        configMask &= ActivityInfo.CONFIG_WINDOW_CONFIGURATION
+                | ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE;
+        windowMask &= WindowConfiguration.WINDOW_CONFIG_BOUNDS;
+        int effects = 0;
+        if (configMask != 0) {
+            Configuration c = new Configuration(container.getRequestedOverrideConfiguration());
+            c.setTo(change.getConfiguration(), configMask, windowMask);
+            container.onRequestedOverrideConfigurationChanged(c);
+            // TODO(b/145675353): remove the following once we could apply new bounds to the
+            // pinned stack together with its children.
+            resizePinnedStackIfNeeded(container, configMask, windowMask, c);
+            effects |= TRANSACT_EFFECTS_CLIENT_CONFIG;
+        }
+        if ((change.getChangeMask() & WindowContainerTransaction.Change.CHANGE_FOCUSABLE) != 0) {
+            if (container.setFocusable(change.getFocusable())) {
+                effects |= TRANSACT_EFFECTS_LIFECYCLE;
+            }
+        }
+        return effects;
+    }
+
+    private void resizePinnedStackIfNeeded(ConfigurationContainer container, int configMask,
+            int windowMask, Configuration config) {
+        if ((container instanceof ActivityStack)
+                && ((configMask & ActivityInfo.CONFIG_WINDOW_CONFIGURATION) != 0)
+                && ((windowMask & WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0)) {
+            final ActivityStack stack = (ActivityStack) container;
+            if (stack.inPinnedWindowingMode()) {
+                stack.resize(config.windowConfiguration.getBounds(),
+                        null /* tempTaskBounds */, null /* tempTaskInsetBounds */,
+                        PRESERVE_WINDOWS, true /* deferResume */);
+            }
+        }
+    }
+
+    private int applyWindowContainerChange(WindowContainer wc,
+            WindowContainerTransaction.Change c) {
+        int effects = sanitizeAndApplyChange(wc, c);
+
+        Rect enterPipBounds = c.getEnterPipBounds();
+        if (enterPipBounds != null) {
+            Task tr = (Task) wc;
+            mService.mStackSupervisor.updatePictureInPictureMode(tr,
+                    enterPipBounds, true);
+        }
+        return effects;
+    }
+
+    @Override
+    public void applyContainerTransaction(WindowContainerTransaction t) {
+        enforceStackPermission("applyContainerTransaction()");
+        if (t == null) {
+            return;
+        }
+        long ident = Binder.clearCallingIdentity();
+        try {
+            synchronized (mGlobalLock) {
+                int effects = 0;
+                mService.deferWindowLayout();
+                try {
+                    ArraySet<WindowContainer> haveConfigChanges = new ArraySet<>();
+                    Iterator<Map.Entry<IBinder, WindowContainerTransaction.Change>> entries =
+                            t.getChanges().entrySet().iterator();
+                    while (entries.hasNext()) {
+                        final Map.Entry<IBinder, WindowContainerTransaction.Change> entry =
+                                entries.next();
+                        final WindowContainer wc = WindowContainer.RemoteToken.fromBinder(
+                                entry.getKey()).getContainer();
+                        int containerEffect = applyWindowContainerChange(wc, entry.getValue());
+                        effects |= containerEffect;
+                        // Lifecycle changes will trigger ensureConfig for everything.
+                        if ((effects & TRANSACT_EFFECTS_LIFECYCLE) == 0
+                                && (containerEffect & TRANSACT_EFFECTS_CLIENT_CONFIG) != 0) {
+                            haveConfigChanges.add(wc);
+                        }
+                    }
+                    if ((effects & TRANSACT_EFFECTS_LIFECYCLE) != 0) {
+                        // Already calls ensureActivityConfig
+                        mService.mRootWindowContainer.ensureActivitiesVisible(
+                                null, 0, PRESERVE_WINDOWS);
+                    } else if ((effects & TRANSACT_EFFECTS_CLIENT_CONFIG) != 0) {
+                        final PooledConsumer f = PooledLambda.obtainConsumer(
+                                ActivityRecord::ensureActivityConfiguration,
+                                PooledLambda.__(ActivityRecord.class), 0,
+                                false /* preserveWindow */);
+                        try {
+                            for (int i = haveConfigChanges.size() - 1; i >= 0; --i) {
+                                haveConfigChanges.valueAt(i).forAllActivities(f);
+                            }
+                        } finally {
+                            f.recycle();
+                        }
+                    }
+                } finally {
+                    mService.continueWindowLayout();
+                }
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
index e47eaee..f4e4245 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
@@ -36,7 +36,6 @@
 import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DRAW_BAR_BACKGROUNDS;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
-
 import static com.android.internal.policy.DecorView.NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES;
 import static com.android.internal.policy.DecorView.STATUS_BAR_COLOR_VIEW_ATTRIBUTES;
 import static com.android.internal.policy.DecorView.getColorViewLeftInset;
@@ -188,8 +187,9 @@
                 return null;
             }
             sysUiVis = topFullscreenOpaqueWindow.getSystemUiVisibility();
-            windowFlags = topFullscreenOpaqueWindow.getAttrs().flags;
-            windowPrivateFlags = topFullscreenOpaqueWindow.getAttrs().privateFlags;
+            WindowManager.LayoutParams attrs = topFullscreenOpaqueWindow.mAttrs;
+            windowFlags = attrs.flags;
+            windowPrivateFlags = attrs.privateFlags;
 
             layoutParams.packageName = mainWindow.getAttrs().packageName;
             layoutParams.windowAnimations = mainWindow.getAttrs().windowAnimations;
@@ -204,6 +204,14 @@
             layoutParams.width = LayoutParams.MATCH_PARENT;
             layoutParams.height = LayoutParams.MATCH_PARENT;
             layoutParams.systemUiVisibility = sysUiVis;
+            layoutParams.insetsFlags.behavior
+                    = topFullscreenOpaqueWindow.mAttrs.insetsFlags.behavior;
+            layoutParams.insetsFlags.appearance
+                    = topFullscreenOpaqueWindow.mAttrs.insetsFlags.appearance;
+            layoutParams.setFitInsetsTypes(attrs.getFitInsetsTypes());
+            layoutParams.setFitInsetsSides(attrs.getFitInsetsSides());
+            layoutParams.setFitInsetsIgnoringVisibility(attrs.isFitInsetsIgnoringVisibility());
+
             layoutParams.setTitle(String.format(TITLE_FORMAT, task.mTaskId));
 
             final TaskDescription td = task.getTaskDescription();
diff --git a/services/core/java/com/android/server/wm/TaskTile.java b/services/core/java/com/android/server/wm/TaskTile.java
new file mode 100644
index 0000000..add11d6
--- /dev/null
+++ b/services/core/java/com/android/server/wm/TaskTile.java
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.app.ActivityTaskManager.INVALID_TASK_ID;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
+import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
+
+import android.app.ActivityManager;
+import android.app.TaskInfo;
+import android.app.WindowConfiguration;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.res.Configuration;
+import android.graphics.Rect;
+import android.os.IBinder;
+import android.util.Slog;
+import android.view.SurfaceControl;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+
+/**
+ * A Tile. Right now this acts as a proxy for manipulating non-child stacks. Eventually, this
+ * can become an actual parent.
+ */
+// TODO(task-hierarchy): Remove when tasks can nest >2 or when single tasks can handle their
+//                       own lifecycles.
+public class TaskTile extends ActivityStack {
+    private static final String TAG = "TaskTile";
+    final ArrayList<WindowContainer> mChildren = new ArrayList<>();
+
+    private static ActivityInfo createEmptyActivityInfo() {
+        ActivityInfo info = new ActivityInfo();
+        info.applicationInfo = new ApplicationInfo();
+        return info;
+    }
+
+    TaskTile(ActivityTaskManagerService atmService, int id, int windowingMode) {
+        super(atmService, id, new Intent() /*intent*/,  null /*affinityIntent*/, null /*affinity*/,
+                null /*rootAffinity*/, null /*realActivity*/, null /*origActivity*/,
+                false /*rootWasReset*/, false /*autoRemoveRecents*/, false /*askedCompatMode*/,
+                0 /*userId*/, 0 /*effectiveUid*/, null /*lastDescription*/,
+                System.currentTimeMillis(), true /*neverRelinquishIdentity*/,
+                new ActivityManager.TaskDescription(), id, INVALID_TASK_ID, INVALID_TASK_ID,
+                0 /*taskAffiliationColor*/, 0 /*callingUid*/, "" /*callingPackage*/,
+                RESIZE_MODE_RESIZEABLE, false /*supportsPictureInPicture*/,
+                false /*_realActivitySuspended*/, false /*userSetupComplete*/, INVALID_MIN_SIZE,
+                INVALID_MIN_SIZE, createEmptyActivityInfo(), null /*voiceSession*/,
+                null /*voiceInteractor*/, null /*stack*/);
+        getRequestedOverrideConfiguration().windowConfiguration.setWindowingMode(windowingMode);
+    }
+
+    @Override
+    void onDisplayChanged(DisplayContent dc) {
+        mDisplayContent = null;
+        if (dc != null) {
+            dc.getPendingTransaction().merge(getPendingTransaction());
+        }
+        mDisplayContent = dc;
+        // Virtual parent, so don't notify children.
+    }
+
+    /**
+     * If there is a disconnection, this will clean up any vestigial surfaces left on the tile
+     * leash by moving known children to a new surfacecontrol and then removing the old one.
+     */
+    void cleanupSurfaces() {
+        if (mSurfaceControl == null) {
+            return;
+        }
+        SurfaceControl oldSurface = mSurfaceControl;
+        WindowContainer parentWin = getParent();
+        if (parentWin == null) {
+            return;
+        }
+        mSurfaceControl = parentWin.makeChildSurface(null).setName("TaskTile " + mTaskId + " - "
+                    + getRequestedOverrideWindowingMode()).setContainerLayer().build();
+        SurfaceControl.Transaction t = parentWin.getPendingTransaction();
+        t.show(mSurfaceControl);
+        for (int i = 0; i < mChildren.size(); ++i) {
+            if (mChildren.get(i).getSurfaceControl() == null) {
+                continue;
+            }
+            mChildren.get(i).reparentSurfaceControl(t, mSurfaceControl);
+        }
+        t.remove(oldSurface);
+    }
+
+    @Override
+    protected void addChild(WindowContainer child, Comparator<WindowContainer> comparator) {
+        throw new RuntimeException("Improper use of addChild() on Tile");
+    }
+
+    @Override
+    void addChild(WindowContainer child, int index) {
+        mChildren.add(child);
+        if (child instanceof ActivityStack) {
+            ((ActivityStack) child).setTile(this);
+        }
+        mAtmService.mTaskOrganizerController.dispatchTaskInfoChanged(
+                this, false /* force */);
+    }
+
+    @Override
+    void removeChild(WindowContainer child) {
+        if (child instanceof ActivityStack) {
+            ((ActivityStack) child).setTile(null);
+        }
+        mChildren.remove(child);
+        mAtmService.mTaskOrganizerController.dispatchTaskInfoChanged(
+                this, false /* force */);
+    }
+
+    void removeAllChildren() {
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final WindowContainer child = mChildren.get(i);
+            if (child instanceof ActivityStack) {
+                ((ActivityStack) child).setTile(null);
+            }
+        }
+        mChildren.clear();
+        mAtmService.mTaskOrganizerController.dispatchTaskInfoChanged(
+                this, false /* force */);
+    }
+
+    @Override
+    protected int getChildCount() {
+        // Currently 0 as this isn't a proper hierarchy member yet.
+        return 0;
+    }
+
+    @Override
+    public void setWindowingMode(/*@WindowConfiguration.WindowingMode*/ int windowingMode) {
+        Configuration c = new Configuration(getRequestedOverrideConfiguration());
+        c.windowConfiguration.setWindowingMode(windowingMode);
+        onRequestedOverrideConfigurationChanged(c);
+    }
+
+    @Override
+    public void onConfigurationChanged(Configuration newParentConfig) {
+        super.onConfigurationChanged(newParentConfig);
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final WindowContainer child = mChildren.get(i);
+            child.onConfigurationChanged(child.getParent().getConfiguration());
+        }
+    }
+
+    /**
+     * Until this can be part of the hierarchy, the Stack level can use this utility during
+     * resolveOverrideConfig to simulate inheritance.
+     */
+    void updateResolvedConfig(Configuration inOutResolvedConfig) {
+        Rect resolveBounds = inOutResolvedConfig.windowConfiguration.getBounds();
+        if (resolveBounds == null || resolveBounds.isEmpty()) {
+            resolveBounds.set(getRequestedOverrideBounds());
+        }
+        int stackMode = inOutResolvedConfig.windowConfiguration.getWindowingMode();
+        if (stackMode == WindowConfiguration.WINDOWING_MODE_UNDEFINED
+                || stackMode == WindowConfiguration.WINDOWING_MODE_FULLSCREEN) {
+            // Also replace FULLSCREEN because we interpret FULLSCREEN as "fill parent"
+            inOutResolvedConfig.windowConfiguration.setWindowingMode(
+                    getRequestedOverrideWindowingMode());
+        }
+        if (inOutResolvedConfig.smallestScreenWidthDp
+                == Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED) {
+            inOutResolvedConfig.smallestScreenWidthDp =
+                    getRequestedOverrideConfiguration().smallestScreenWidthDp;
+        }
+    }
+
+    @Override
+    void fillTaskInfo(TaskInfo info) {
+        super.fillTaskInfo(info);
+        WindowContainer top = null;
+        // Check mChildren.isEmpty directly because hasChild() -> getChildCount() always returns 0
+        if (!mChildren.isEmpty()) {
+            // Find the top-most root task which is a virtual child of this Tile. Because this is a
+            // virtual parent, the mChildren order here isn't changed during hierarchy operations.
+            WindowContainer parent = mChildren.get(0).getParent();
+            for (int i = parent.getChildCount() - 1; i >= 0; --i) {
+                if (mChildren.contains(parent.getChildAt(i))) {
+                    top = parent.getChildAt(i);
+                    break;
+                }
+            }
+        }
+        final Task topTask = top == null ? null : top.getTopMostTask();
+        boolean isResizable = topTask == null || topTask.isResizeable();
+        info.resizeMode = isResizable ? RESIZE_MODE_RESIZEABLE : RESIZE_MODE_UNRESIZEABLE;
+        info.topActivityType = top == null ? ACTIVITY_TYPE_UNDEFINED : top.getActivityType();
+        info.configuration.setTo(getRequestedOverrideConfiguration());
+    }
+
+    @Override
+    void removeImmediately() {
+        removeAllChildren();
+        super.removeImmediately();
+    }
+
+    static TaskTile forToken(IBinder token) {
+        try {
+            return (TaskTile) ((TaskToken) token).getContainer();
+        } catch (ClassCastException e) {
+            Slog.w(TAG, "Bad tile token: " + token, e);
+            return null;
+        }
+    }
+}
diff --git a/services/core/jni/com_android_server_SystemServer.cpp b/services/core/jni/com_android_server_SystemServer.cpp
index 78b64ca..67254b8 100644
--- a/services/core/jni/com_android_server_SystemServer.cpp
+++ b/services/core/jni/com_android_server_SystemServer.cpp
@@ -14,6 +14,12 @@
  * limitations under the License.
  */
 
+#include <dlfcn.h>
+#include <pthread.h>
+
+#include <chrono>
+#include <thread>
+
 #include <jni.h>
 #include <nativehelper/JNIHelp.h>
 
@@ -25,12 +31,17 @@
 #include <sensorservicehidl/SensorManager.h>
 
 #include <bionic/malloc.h>
+#include <bionic/reserved_signals.h>
 
+#include <android-base/properties.h>
 #include <cutils/properties.h>
 #include <utils/Log.h>
 #include <utils/misc.h>
 #include <utils/AndroidThreads.h>
 
+using android::base::GetIntProperty;
+using namespace std::chrono_literals;
+
 namespace android {
 
 static void android_server_SystemServer_startSensorService(JNIEnv* /* env */, jobject /* clazz */) {
@@ -68,7 +79,50 @@
 
 static void android_server_SystemServer_initZygoteChildHeapProfiling(JNIEnv* /* env */,
                                                                      jobject /* clazz */) {
-   android_mallopt(M_INIT_ZYGOTE_CHILD_PROFILING, nullptr, 0);
+    android_mallopt(M_INIT_ZYGOTE_CHILD_PROFILING, nullptr, 0);
+}
+
+static int get_current_max_fd() {
+    // Not actually guaranteed to be the max, but close enough for our purposes.
+    int fd = open("/dev/null", O_RDONLY | O_CLOEXEC);
+    LOG_ALWAYS_FATAL_IF(fd == -1, "failed to open /dev/null: %s", strerror(errno));
+    close(fd);
+    return fd;
+}
+
+static const char kFdLeakEnableThresholdProperty[] = "persist.sys.debug.fdtrack_enable_threshold";
+static const char kFdLeakAbortThresholdProperty[] = "persist.sys.debug.fdtrack_abort_threshold";
+static const char kFdLeakCheckIntervalProperty[] = "persist.sys.debug.fdtrack_interval";
+
+static void android_server_SystemServer_spawnFdLeakCheckThread(JNIEnv*, jobject) {
+    std::thread([]() {
+        pthread_setname_np(pthread_self(), "FdLeakCheckThread");
+        bool loaded = false;
+        while (true) {
+            const int enable_threshold = GetIntProperty(kFdLeakEnableThresholdProperty, 1024);
+            const int abort_threshold = GetIntProperty(kFdLeakAbortThresholdProperty, 2048);
+            const int check_interval = GetIntProperty(kFdLeakCheckIntervalProperty, 120);
+            int max_fd = get_current_max_fd();
+            if (max_fd > enable_threshold && !loaded) {
+                loaded = true;
+                ALOGE("fd count above threshold of %d, starting fd backtraces", enable_threshold);
+                if (dlopen("libfdtrack.so", RTLD_GLOBAL) == nullptr) {
+                    ALOGE("failed to load libfdtrack.so: %s", dlerror());
+                }
+            } else if (max_fd > abort_threshold) {
+                raise(BIONIC_SIGNAL_FDTRACK);
+
+                // Wait for a bit to allow fdtrack to dump backtraces to logcat.
+                std::this_thread::sleep_for(5s);
+
+                LOG_ALWAYS_FATAL(
+                    "b/140703823: aborting due to fd leak: check logs for fd "
+                    "backtraces");
+            }
+
+            std::this_thread::sleep_for(std::chrono::seconds(check_interval));
+        }
+    }).detach();
 }
 
 /*
@@ -80,6 +134,9 @@
     { "startHidlServices", "()V", (void*) android_server_SystemServer_startHidlServices },
     { "initZygoteChildHeapProfiling", "()V",
       (void*) android_server_SystemServer_initZygoteChildHeapProfiling },
+    { "spawnFdLeakCheckThread", "()V",
+      (void*) android_server_SystemServer_spawnFdLeakCheckThread },
+
 };
 
 int register_android_server_SystemServer(JNIEnv* env)
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index b7162f9..2a6b029 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -365,6 +365,12 @@
      */
     private static native void initZygoteChildHeapProfiling();
 
+
+    /**
+     * Spawn a thread that monitors for fd leaks.
+     */
+    private static native void spawnFdLeakCheckThread();
+
     /**
      * The main entry point from zygote.
      */
@@ -499,6 +505,11 @@
                 initZygoteChildHeapProfiling();
             }
 
+            // Debug builds - spawn a thread to monitor for fd leaks.
+            if (Build.IS_DEBUGGABLE) {
+                spawnFdLeakCheckThread();
+            }
+
             // Check whether we failed to shut down last time we tried.
             // This call may not return.
             performPendingShutdown();
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index b7c9001..d2ddff3 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -66,8 +66,6 @@
     <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
     <uses-permission android:name="android.permission.SUSPEND_APPS"/>
     <uses-permission android:name="android.permission.CONTROL_KEYGUARD"/>
-    <uses-permission android:name="android.permission.READ_COMPAT_CHANGE_CONFIG"/>
-    <uses-permission android:name="android.permission.LOG_COMPAT_CHANGE"/>
     <uses-permission android:name="android.permission.MANAGE_BIND_INSTANT_SERVICE"/>
     <uses-permission android:name="android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS" />
     <uses-permission android:name="android.permission.READ_DEVICE_CONFIG" />
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
index d22502d..4532400 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskManagerServiceTests.java
@@ -16,9 +16,6 @@
 
 package com.android.server.wm;
 
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
-import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
-
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
@@ -27,17 +24,14 @@
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.when;
 
 import android.app.Activity;
-import android.app.ActivityManager;
 import android.app.PictureInPictureParams;
 import android.app.servertransaction.ClientTransaction;
 import android.app.servertransaction.EnterPipRequestedItem;
@@ -46,7 +40,6 @@
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.view.IDisplayWindowListener;
-import android.view.WindowContainerTransaction;
 
 import androidx.test.filters.MediumTest;
 
@@ -126,47 +119,6 @@
     }
 
     @Test
-    public void testTaskTransaction() {
-        removeGlobalMinSizeRestriction();
-        final ActivityStack stack = new StackBuilder(mRootWindowContainer)
-                .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
-        final Task task = stack.getTopMostTask();
-        WindowContainerTransaction t = new WindowContainerTransaction();
-        Rect newBounds = new Rect(10, 10, 100, 100);
-        t.setBounds(task.mRemoteToken, new Rect(10, 10, 100, 100));
-        mService.applyContainerTransaction(t);
-        assertEquals(newBounds, task.getBounds());
-    }
-
-    @Test
-    public void testStackTransaction() {
-        removeGlobalMinSizeRestriction();
-        final ActivityStack stack = new StackBuilder(mRootWindowContainer)
-                .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
-        ActivityManager.StackInfo info =
-                mService.getStackInfo(WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD);
-        WindowContainerTransaction t = new WindowContainerTransaction();
-        assertEquals(stack.mRemoteToken, info.stackToken);
-        Rect newBounds = new Rect(10, 10, 100, 100);
-        t.setBounds(info.stackToken, new Rect(10, 10, 100, 100));
-        mService.applyContainerTransaction(t);
-        assertEquals(newBounds, stack.getBounds());
-    }
-
-    @Test
-    public void testContainerChanges() {
-        removeGlobalMinSizeRestriction();
-        final ActivityStack stack = new StackBuilder(mRootWindowContainer)
-                .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
-        final Task task = stack.getTopMostTask();
-        WindowContainerTransaction t = new WindowContainerTransaction();
-        assertTrue(task.isFocusable());
-        t.setFocusable(stack.mRemoteToken, false);
-        mService.applyContainerTransaction(t);
-        assertFalse(task.isFocusable());
-    }
-
-    @Test
     public void testDisplayWindowListener() {
         final ArrayList<Integer> added = new ArrayList<>();
         final ArrayList<Integer> changed = new ArrayList<>();
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
index 9e80cf2..078347e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskOrganizerTests.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 The Android Open Source Project
+ * Copyright (C) 2020 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.
@@ -16,45 +16,50 @@
 
 package com.android.server.wm;
 
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
-import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.assertFalse;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
-import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
 import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
 
-import android.graphics.Point;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+
+import android.app.ActivityManager.RunningTaskInfo;
+import android.app.ActivityManager.StackInfo;
+import android.content.res.Configuration;
 import android.graphics.Rect;
-import android.platform.test.annotations.Presubmit;
 import android.os.Binder;
 import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+import android.view.Display;
 import android.view.ITaskOrganizer;
+import android.view.IWindowContainer;
 import android.view.SurfaceControl;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
+import android.view.WindowContainerTransaction;
 
 import androidx.test.filters.SmallTest;
 
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
+import java.util.ArrayList;
+import java.util.List;
+
 /**
- * Test class for {@link TaskOrganizer}.
+ * Test class for {@link ITaskOrganizer} and {@link android.app.ITaskOrganizerController}.
  *
  * Build/Install/Run:
  *  atest WmTests:TaskOrganizerTests
@@ -67,7 +72,8 @@
         final ITaskOrganizer organizer = mock(ITaskOrganizer.class);
         when(organizer.asBinder()).thenReturn(new Binder());
 
-        mWm.mAtmService.registerTaskOrganizer(organizer, windowingMode);
+        mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(
+                organizer, windowingMode);
 
         return organizer;
     }
@@ -83,7 +89,7 @@
         final ITaskOrganizer organizer = registerMockOrganizer();
 
         task.setTaskOrganizer(organizer);
-        verify(organizer).taskAppeared(any(), any());
+        verify(organizer).taskAppeared(any());
 
         task.removeImmediately();
         verify(organizer).taskVanished(any());
@@ -97,10 +103,10 @@
         final ITaskOrganizer organizer2 = registerMockOrganizer(WINDOWING_MODE_PINNED);
 
         task.setTaskOrganizer(organizer);
-        verify(organizer).taskAppeared(any(), any());
+        verify(organizer).taskAppeared(any());
         task.setTaskOrganizer(organizer2);
         verify(organizer).taskVanished(any());
-        verify(organizer2).taskAppeared(any(), any());
+        verify(organizer2).taskAppeared(any());
     }
 
     @Test
@@ -111,10 +117,10 @@
         final ITaskOrganizer organizer2 = registerMockOrganizer(WINDOWING_MODE_PINNED);
  
         stack.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
-        verify(organizer).taskAppeared(any(), any());
+        verify(organizer).taskAppeared(any());
         stack.setWindowingMode(WINDOWING_MODE_PINNED);
         verify(organizer).taskVanished(any());
-        verify(organizer2).taskAppeared(any(), any());
+        verify(organizer2).taskAppeared(any());
     }
 
     @Test
@@ -124,7 +130,7 @@
         final ITaskOrganizer organizer = registerMockOrganizer();
 
         stack.setTaskOrganizer(organizer);
-        verify(organizer).taskAppeared(any(), any());
+        verify(organizer).taskAppeared(any());
         assertTrue(stack.isControlledByTaskOrganizer());
 
         stack.setTaskOrganizer(null);
@@ -140,9 +146,176 @@
         final Task task = createTaskInStack(stack, 0 /* userId */);
         final Task task2 = createTaskInStack(stack, 0 /* userId */);
         stack.setWindowingMode(WINDOWING_MODE_PINNED);
-        verify(organizer, times(1)).taskAppeared(any(), any());
+        verify(organizer, times(1)).taskAppeared(any());
 
         stack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
         verify(organizer, times(1)).taskVanished(any());
     }
+
+    @Test
+    public void testTaskTransaction() {
+        removeGlobalMinSizeRestriction();
+        final ActivityStack stack = new ActivityTestsBase.StackBuilder(mWm.mRoot)
+                .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
+        final Task task = stack.getTopMostTask();
+        WindowContainerTransaction t = new WindowContainerTransaction();
+        Rect newBounds = new Rect(10, 10, 100, 100);
+        t.setBounds(task.mRemoteToken, new Rect(10, 10, 100, 100));
+        mWm.mAtmService.mTaskOrganizerController.applyContainerTransaction(t);
+        assertEquals(newBounds, task.getBounds());
+    }
+
+    @Test
+    public void testStackTransaction() {
+        removeGlobalMinSizeRestriction();
+        final ActivityStack stack = new ActivityTestsBase.StackBuilder(mWm.mRoot)
+                .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
+        StackInfo info =
+                mWm.mAtmService.getStackInfo(WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD);
+        WindowContainerTransaction t = new WindowContainerTransaction();
+        assertEquals(stack.mRemoteToken, info.stackToken);
+        Rect newBounds = new Rect(10, 10, 100, 100);
+        t.setBounds(info.stackToken, new Rect(10, 10, 100, 100));
+        mWm.mAtmService.mTaskOrganizerController.applyContainerTransaction(t);
+        assertEquals(newBounds, stack.getBounds());
+    }
+
+    @Test
+    public void testContainerChanges() {
+        removeGlobalMinSizeRestriction();
+        final ActivityStack stack = new ActivityTestsBase.StackBuilder(mWm.mRoot)
+                .setWindowingMode(WINDOWING_MODE_FREEFORM).build();
+        final Task task = stack.getTopMostTask();
+        WindowContainerTransaction t = new WindowContainerTransaction();
+        assertTrue(task.isFocusable());
+        t.setFocusable(stack.mRemoteToken, false);
+        mWm.mAtmService.mTaskOrganizerController.applyContainerTransaction(t);
+        assertFalse(task.isFocusable());
+    }
+
+    @Test
+    public void testCreateDeleteRootTasks() {
+        RunningTaskInfo info1 = mWm.mAtmService.mTaskOrganizerController.createRootTask(
+                Display.DEFAULT_DISPLAY,
+                WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
+        assertEquals(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY,
+                info1.configuration.windowConfiguration.getWindowingMode());
+        assertEquals(ACTIVITY_TYPE_UNDEFINED, info1.topActivityType);
+
+        RunningTaskInfo info2 = mWm.mAtmService.mTaskOrganizerController.createRootTask(
+                Display.DEFAULT_DISPLAY,
+                WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+        assertEquals(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY,
+                info2.configuration.windowConfiguration.getWindowingMode());
+        assertEquals(ACTIVITY_TYPE_UNDEFINED, info2.topActivityType);
+
+        DisplayContent dc = mWm.mRoot.getDisplayContent(Display.DEFAULT_DISPLAY);
+        List<TaskTile> infos = getTaskTiles(dc);
+        assertEquals(2, infos.size());
+
+        assertTrue(mWm.mAtmService.mTaskOrganizerController.deleteRootTask(info1.token));
+        infos = getTaskTiles(dc);
+        assertEquals(1, infos.size());
+        assertEquals(WINDOWING_MODE_SPLIT_SCREEN_SECONDARY, infos.get(0).getWindowingMode());
+    }
+
+    @Test
+    public void testTileAddRemoveChild() {
+        RunningTaskInfo info1 = mWm.mAtmService.mTaskOrganizerController.createRootTask(
+                mDisplayContent.mDisplayId, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+
+        final ActivityStack stack = createTaskStackOnDisplay(
+                WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, mDisplayContent);
+        assertEquals(mDisplayContent.getWindowingMode(), stack.getWindowingMode());
+        TaskTile tile1 = TaskTile.forToken(info1.token.asBinder());
+        tile1.addChild(stack, 0 /* index */);
+        assertEquals(info1.configuration.windowConfiguration.getWindowingMode(),
+                stack.getWindowingMode());
+
+        // Info should reflect new membership
+        List<TaskTile> tiles = getTaskTiles(mDisplayContent);
+        info1 = new RunningTaskInfo();
+        tiles.get(0).fillTaskInfo(info1);
+        assertEquals(ACTIVITY_TYPE_STANDARD, info1.topActivityType);
+
+        // Children inherit configuration
+        Rect newSize = new Rect(10, 10, 300, 300);
+        Configuration c = new Configuration(tile1.getRequestedOverrideConfiguration());
+        c.windowConfiguration.setBounds(newSize);
+        tile1.onRequestedOverrideConfigurationChanged(c);
+        assertEquals(newSize, stack.getBounds());
+
+        tile1.removeChild(stack);
+        assertEquals(mDisplayContent.getWindowingMode(), stack.getWindowingMode());
+        info1 = new RunningTaskInfo();
+        tiles = getTaskTiles(mDisplayContent);
+        tiles.get(0).fillTaskInfo(info1);
+        assertEquals(ACTIVITY_TYPE_UNDEFINED, info1.topActivityType);
+    }
+
+    @Test
+    public void testTaskInfoCallback() {
+        final ArrayList<RunningTaskInfo> lastReportedTiles = new ArrayList<>();
+        final boolean[] called = {false};
+        ITaskOrganizer listener = new ITaskOrganizer.Stub() {
+            @Override
+            public void taskAppeared(RunningTaskInfo taskInfo) { }
+
+            @Override
+            public void taskVanished(IWindowContainer container) { }
+
+            @Override
+            public void transactionReady(int id, SurfaceControl.Transaction t) { }
+
+            @Override
+            public void onTaskInfoChanged(RunningTaskInfo info) throws RemoteException {
+                lastReportedTiles.add(info);
+                called[0] = true;
+            }
+        };
+        mWm.mAtmService.mTaskOrganizerController.registerTaskOrganizer(listener,
+                WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+        RunningTaskInfo info1 = mWm.mAtmService.mTaskOrganizerController.createRootTask(
+                mDisplayContent.mDisplayId, WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
+        lastReportedTiles.clear();
+        called[0] = false;
+
+        final ActivityStack stack = createTaskStackOnDisplay(
+                WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD, mDisplayContent);
+        TaskTile tile1 = TaskTile.forToken(info1.token.asBinder());
+        tile1.addChild(stack, 0 /* index */);
+        assertTrue(called[0]);
+        assertEquals(ACTIVITY_TYPE_STANDARD, lastReportedTiles.get(0).topActivityType);
+
+        lastReportedTiles.clear();
+        called[0] = false;
+        final ActivityStack stack2 = createTaskStackOnDisplay(
+                WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME, mDisplayContent);
+        tile1.addChild(stack2, 0 /* index */);
+        assertTrue(called[0]);
+        assertEquals(ACTIVITY_TYPE_HOME, lastReportedTiles.get(0).topActivityType);
+
+        lastReportedTiles.clear();
+        called[0] = false;
+        mDisplayContent.positionStackAtTop(stack, false /* includingParents */);
+        assertTrue(called[0]);
+        assertEquals(ACTIVITY_TYPE_STANDARD, lastReportedTiles.get(0).topActivityType);
+
+        lastReportedTiles.clear();
+        called[0] = false;
+        tile1.removeAllChildren();
+        assertTrue(called[0]);
+        assertEquals(ACTIVITY_TYPE_UNDEFINED, lastReportedTiles.get(0).topActivityType);
+    }
+
+    private List<TaskTile> getTaskTiles(DisplayContent dc) {
+        ArrayList<TaskTile> out = new ArrayList<>();
+        for (int i = dc.getStackCount() - 1; i >= 0; --i) {
+            final Task t = dc.getStackAt(i);
+            if (t instanceof TaskTile) {
+                out.add((TaskTile) t);
+            }
+        }
+        return out;
+    }
 }
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 8e362ae..1ca2e318 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -40,6 +40,7 @@
 import static org.mockito.Mockito.mock;
 
 import android.content.Context;
+import android.content.Intent;
 import android.util.Log;
 import android.view.Display;
 import android.view.DisplayInfo;
@@ -340,6 +341,7 @@
                     .setWindowingMode(windowingMode)
                     .setActivityType(activityType)
                     .setCreateActivity(false)
+                    .setIntent(new Intent())
                     .build();
         }
     }
diff --git a/telephony/java/android/telephony/BarringInfo.java b/telephony/java/android/telephony/BarringInfo.java
index 5419c3c..9baa66f 100644
--- a/telephony/java/android/telephony/BarringInfo.java
+++ b/telephony/java/android/telephony/BarringInfo.java
@@ -238,6 +238,12 @@
         }
     }
 
+    private static final BarringServiceInfo BARRING_SERVICE_INFO_UNKNOWN =
+            new BarringServiceInfo(BarringServiceInfo.BARRING_TYPE_UNKNOWN);
+
+    private static final BarringServiceInfo BARRING_SERVICE_INFO_UNBARRED =
+            new BarringServiceInfo(BarringServiceInfo.BARRING_TYPE_NONE);
+
     private CellIdentity mCellIdentity;
 
     // A SparseArray potentially mapping each BarringService type to a BarringServiceInfo config
@@ -298,17 +304,6 @@
     }
 
     /**
-     * Return whether a service is currently barred based on the BarringInfo
-     *
-     * @param service the service to be checked.
-     * @return true if the service is currently being barred, otherwise false
-     */
-    public boolean isServiceBarred(@BarringServiceType int service) {
-        BarringServiceInfo bsi = mBarringServiceInfos.get(service);
-        return bsi != null && (bsi.isBarred());
-    }
-
-    /**
      * Get the BarringServiceInfo for a specified service.
      *
      * @return a BarringServiceInfo struct describing the current barring status for a service
@@ -319,9 +314,8 @@
         // type as UNKNOWN; if the modem reports barring info but doesn't report for a particular
         // service then we can safely assume that the service isn't barred (for instance because
         // that particular service isn't applicable to the current RAN).
-        return (bsi != null) ? bsi : new BarringServiceInfo(
-                mBarringServiceInfos.size() > 0 ? BarringServiceInfo.BARRING_TYPE_NONE :
-                        BarringServiceInfo.BARRING_TYPE_UNKNOWN);
+        return (bsi != null) ? bsi : mBarringServiceInfos.size() > 0
+                ? BARRING_SERVICE_INFO_UNBARRED : BARRING_SERVICE_INFO_UNKNOWN;
     }
 
     /** @hide */
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index 5b09cd9..a5a1ebc 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -1925,6 +1925,8 @@
 
     /**
      * Get the network registration state for the transport type and network domain.
+     * If multiple domains are in the input bitmask, only the first one from
+     * networkRegistrationInfo.getDomain() will be returned.
      *
      * @param domain The network {@link NetworkRegistrationInfo.Domain domain}
      * @param transportType The transport type
@@ -2072,11 +2074,18 @@
     public boolean isIwlanPreferred() {
         return mIsIwlanPreferred;
     }
-     /**
-     * @return {@code true}Returns True whenever the modem is searching for service.
-     * To check both CS and PS domain
-     */
 
+    /**
+     * This indicates whether the device is searching for service.
+     *
+     * This API reports the modem searching status for
+     * {@link AccessNetworkConstants#TRANSPORT_TYPE_WWAN} (cellular) service in either
+     * {@link NetworkRegistrationInfo#DOMAIN_CS} or {@link NetworkRegistrationInfo#DOMAIN_PS}.
+     * This API will not report searching status for
+     * {@link AccessNetworkConstants#TRANSPORT_TYPE_WLAN}.
+     *
+     * @return {@code true} whenever the modem is searching for service.
+     */
     public boolean isSearching() {
         NetworkRegistrationInfo psRegState = getNetworkRegistrationInfo(
                 NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
diff --git a/tests/PlatformCompatGating/Android.bp b/tests/PlatformCompatGating/Android.bp
index 609896e..5e9ef8e 100644
--- a/tests/PlatformCompatGating/Android.bp
+++ b/tests/PlatformCompatGating/Android.bp
@@ -18,6 +18,7 @@
     name: "PlatformCompatGating",
     // Only compile source java files in this apk.
     srcs: ["src/**/*.java"],
+    certificate: "platform",
     libs: [
         "android.test.runner",
         "android.test.base",
diff --git a/tests/PlatformCompatGating/test-rules/src/android/compat/testing/PlatformCompatChangeRule.java b/tests/PlatformCompatGating/test-rules/src/android/compat/testing/PlatformCompatChangeRule.java
index c00aa2a..932ec64 100644
--- a/tests/PlatformCompatGating/test-rules/src/android/compat/testing/PlatformCompatChangeRule.java
+++ b/tests/PlatformCompatGating/test-rules/src/android/compat/testing/PlatformCompatChangeRule.java
@@ -16,9 +16,7 @@
 
 package android.compat.testing;
 
-import android.Manifest;
 import android.app.Instrumentation;
-import android.app.UiAutomation;
 import android.compat.Compatibility;
 import android.compat.Compatibility.ChangeConfig;
 import android.content.Context;
@@ -85,16 +83,12 @@
         @Override
         public void evaluate() throws Throwable {
             Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
-            UiAutomation uiAutomation = instrumentation.getUiAutomation();
             String packageName = instrumentation.getTargetContext().getPackageName();
             IPlatformCompat platformCompat = IPlatformCompat.Stub
                     .asInterface(ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE));
             if (platformCompat == null) {
                 throw new IllegalStateException("Could not get IPlatformCompat service!");
             }
-            uiAutomation.adoptShellPermissionIdentity(
-                    Manifest.permission.READ_COMPAT_CHANGE_CONFIG,
-                    Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG);
             Compatibility.setOverrides(mConfig);
             try {
                 platformCompat.setOverridesForTest(new CompatibilityChangeConfig(mConfig),
@@ -107,7 +101,6 @@
             } catch (RemoteException e) {
                 throw new RuntimeException("Could not call IPlatformCompat binder method!", e);
             } finally {
-                uiAutomation.dropShellPermissionIdentity();
                 Compatibility.clearOverrides();
             }
         }
diff --git a/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerMultiWindowTest.java b/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerMultiWindowTest.java
index bc8d3c3..3fb0050 100644
--- a/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerMultiWindowTest.java
+++ b/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerMultiWindowTest.java
@@ -25,6 +25,7 @@
 import android.content.Intent;
 import android.graphics.Rect;
 import android.os.Bundle;
+import android.os.RemoteException;
 import android.view.ITaskOrganizer;
 import android.view.IWindowContainer;
 import android.view.SurfaceControl;
@@ -47,13 +48,16 @@
 
     class Organizer extends ITaskOrganizer.Stub {
         @Override
-        public void taskAppeared(IWindowContainer wc, ActivityManager.RunningTaskInfo ti) {
-            mView.reparentTask(wc);
+        public void taskAppeared(ActivityManager.RunningTaskInfo ti) {
+            mView.reparentTask(ti.token);
         }
         public void taskVanished(IWindowContainer wc) {
         }
         public void transactionReady(int id, SurfaceControl.Transaction t) {
         }
+        @Override
+        public void onTaskInfoChanged(ActivityManager.RunningTaskInfo info) {
+        }
     }
 
     Organizer mOrganizer = new Organizer();
diff --git a/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerPipTest.java b/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerPipTest.java
index fc1be28..8f3cb34 100644
--- a/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerPipTest.java
+++ b/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskOrganizerPipTest.java
@@ -21,18 +21,14 @@
 import android.app.ActivityManager;
 import android.app.ActivityTaskManager;
 import android.app.Service;
-import android.app.WindowConfiguration;
-import android.content.Context;
 import android.content.Intent;
 import android.graphics.Rect;
 import android.os.IBinder;
 import android.view.ITaskOrganizer;
 import android.view.IWindowContainer;
-import android.view.WindowContainerTransaction;
 import android.view.SurfaceControl;
-import android.view.SurfaceHolder;
-import android.view.SurfaceView;
 import android.view.ViewGroup;
+import android.view.WindowContainerTransaction;
 import android.view.WindowManager;
 import android.widget.FrameLayout;
 
@@ -43,13 +39,13 @@
     TaskView mTaskView;
 
     class Organizer extends ITaskOrganizer.Stub {
-        public void taskAppeared(IWindowContainer wc, ActivityManager.RunningTaskInfo ti) {
-            mTaskView.reparentTask(wc);
+        public void taskAppeared(ActivityManager.RunningTaskInfo ti) {
+            mTaskView.reparentTask(ti.token);
 
             final WindowContainerTransaction wct = new WindowContainerTransaction();
-            wct.scheduleFinishEnterPip(wc, new Rect(0, 0, PIP_WIDTH, PIP_HEIGHT));
+            wct.scheduleFinishEnterPip(ti.token, new Rect(0, 0, PIP_WIDTH, PIP_HEIGHT));
             try {
-                ActivityTaskManager.getService().applyContainerTransaction(wct);
+                ActivityTaskManager.getTaskOrganizerController().applyContainerTransaction(wct);
             } catch (Exception e) {
             }
         }
@@ -57,6 +53,8 @@
         }
         public void transactionReady(int id, SurfaceControl.Transaction t) {
         }
+        public void onTaskInfoChanged(ActivityManager.RunningTaskInfo info) {
+        }
     }
 
     Organizer mOrganizer = new Organizer();
diff --git a/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskView.java b/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskView.java
index ff73340..9f32bb8 100644
--- a/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskView.java
+++ b/tests/TaskOrganizerTest/src/com/android/test/taskembed/TaskView.java
@@ -44,7 +44,7 @@
     @Override
     public void surfaceCreated(SurfaceHolder holder) {
         try {
-            ActivityTaskManager.getService().registerTaskOrganizer(mTaskOrganizer,
+            ActivityTaskManager.getTaskOrganizerController().registerTaskOrganizer(mTaskOrganizer,
                     mWindowingMode);
         } catch (Exception e) {
         }
diff --git a/tests/net/TEST_MAPPING b/tests/net/TEST_MAPPING
index a7853b6..005cbe9 100644
--- a/tests/net/TEST_MAPPING
+++ b/tests/net/TEST_MAPPING
@@ -1,7 +1,12 @@
 {
-  "postsubmit": [
+  "presubmit": [
     {
       "name": "FrameworksNetIntegrationTests"
     }
+  ],
+  "postsubmit": [
+    {
+      "name": "FrameworksNetDeflakeTest"
+    }
   ]
 }
\ No newline at end of file
diff --git a/tests/net/common/java/android/net/NetworkScoreTest.kt b/tests/net/common/java/android/net/NetworkScoreTest.kt
index 30836b7..a63d58d 100644
--- a/tests/net/common/java/android/net/NetworkScoreTest.kt
+++ b/tests/net/common/java/android/net/NetworkScoreTest.kt
@@ -16,7 +16,7 @@
 
 package android.net
 
-import android.os.Parcelable
+import android.net.NetworkScore.Metrics.BANDWIDTH_UNKNOWN
 import androidx.test.filters.SmallTest
 import androidx.test.runner.AndroidJUnit4
 import com.android.testutils.assertParcelSane
@@ -28,77 +28,49 @@
 import org.junit.runner.RunWith
 
 private const val TEST_SCORE = 80
-private const val KEY_DEFAULT_CAPABILITIES = "DEFAULT_CAPABILITIES"
 
 @RunWith(AndroidJUnit4::class)
 @SmallTest
 class NetworkScoreTest {
     @Test
     fun testParcelNetworkScore() {
-        val networkScore = NetworkScore()
         val defaultCap = NetworkCapabilities()
-        networkScore.putExtension(KEY_DEFAULT_CAPABILITIES, defaultCap)
-        assertEquals(defaultCap, networkScore.getExtension(KEY_DEFAULT_CAPABILITIES))
-        networkScore.putIntExtension(NetworkScore.LEGACY_SCORE, TEST_SCORE)
-        assertEquals(TEST_SCORE, networkScore.getIntExtension(NetworkScore.LEGACY_SCORE))
-        assertParcelSane(networkScore, 1)
-    }
+        val builder = NetworkScore.Builder().setLegacyScore(TEST_SCORE)
+        assertEquals(TEST_SCORE, builder.build().getLegacyScore())
+        assertParcelSane(builder.build(), 7)
 
-    @Test
-    fun testNullKeyAndValue() {
-        val networkScore = NetworkScore()
-        val defaultCap = NetworkCapabilities()
-        networkScore.putIntExtension(null, TEST_SCORE)
-        assertEquals(TEST_SCORE, networkScore.getIntExtension(null))
-        networkScore.putExtension(null, defaultCap)
-        assertEquals(defaultCap, networkScore.getExtension(null))
-        networkScore.putExtension(null, null)
-        val result: Parcelable? = networkScore.getExtension(null)
-        assertEquals(null, result)
-    }
+        builder.addPolicy(NetworkScore.POLICY_IGNORE_ON_WIFI)
+                .addPolicy(NetworkScore.POLICY_DEFAULT_SUBSCRIPTION)
+                .setLinkLayerMetrics(NetworkScore.Metrics(44 /* latency */,
+                        380 /* downlinkBandwidth */, BANDWIDTH_UNKNOWN /* uplinkBandwidth */))
+                .setEndToEndMetrics(NetworkScore.Metrics(11 /* latency */,
+                        BANDWIDTH_UNKNOWN /* downlinkBandwidth */, 100_000 /* uplinkBandwidth */))
+                .setRange(NetworkScore.RANGE_MEDIUM)
+        assertParcelSane(builder.build(), 7)
+        builder.clearPolicy(NetworkScore.POLICY_IGNORE_ON_WIFI)
+        val ns = builder.build()
+        assertParcelSane(ns, 7)
+        assertFalse(ns.hasPolicy(NetworkScore.POLICY_IGNORE_ON_WIFI))
+        assertTrue(ns.hasPolicy(NetworkScore.POLICY_DEFAULT_SUBSCRIPTION))
 
-    @Test
-    fun testRemoveExtension() {
-        val networkScore = NetworkScore()
-        val defaultCap = NetworkCapabilities()
-        networkScore.putExtension(KEY_DEFAULT_CAPABILITIES, defaultCap)
-        networkScore.putIntExtension(NetworkScore.LEGACY_SCORE, TEST_SCORE)
-        assertEquals(defaultCap, networkScore.getExtension(KEY_DEFAULT_CAPABILITIES))
-        assertEquals(TEST_SCORE, networkScore.getIntExtension(NetworkScore.LEGACY_SCORE))
-        networkScore.removeExtension(KEY_DEFAULT_CAPABILITIES)
-        networkScore.removeExtension(NetworkScore.LEGACY_SCORE)
-        val result: Parcelable? = networkScore.getExtension(KEY_DEFAULT_CAPABILITIES)
-        assertEquals(null, result)
-        assertEquals(0, networkScore.getIntExtension(NetworkScore.LEGACY_SCORE))
+        val exitingNs = ns.withExiting(true)
+        assertNotEquals(ns.isExiting, exitingNs.isExiting)
+        assertNotEquals(ns, exitingNs)
+        assertParcelSane(exitingNs, 7)
     }
 
     @Test
     fun testEqualsNetworkScore() {
-        val ns1 = NetworkScore()
-        val ns2 = NetworkScore()
-        assertTrue(ns1.equals(ns2))
-        assertEquals(ns1.hashCode(), ns2.hashCode())
+        val builder1 = NetworkScore.Builder()
+        val builder2 = NetworkScore.Builder()
+        assertTrue(builder1.build().equals(builder2.build()))
+        assertEquals(builder1.build().hashCode(), builder2.build().hashCode())
 
-        ns1.putIntExtension(NetworkScore.LEGACY_SCORE, TEST_SCORE)
-        assertFalse(ns1.equals(ns2))
-        assertNotEquals(ns1.hashCode(), ns2.hashCode())
-        ns2.putIntExtension(NetworkScore.LEGACY_SCORE, TEST_SCORE)
-        assertTrue(ns1.equals(ns2))
-        assertEquals(ns1.hashCode(), ns2.hashCode())
-
-        val defaultCap = NetworkCapabilities()
-        ns1.putExtension(KEY_DEFAULT_CAPABILITIES, defaultCap)
-        assertFalse(ns1.equals(ns2))
-        assertNotEquals(ns1.hashCode(), ns2.hashCode())
-        ns2.putExtension(KEY_DEFAULT_CAPABILITIES, defaultCap)
-        assertTrue(ns1.equals(ns2))
-        assertEquals(ns1.hashCode(), ns2.hashCode())
-
-        ns1.putIntExtension(null, 10)
-        assertFalse(ns1.equals(ns2))
-        assertNotEquals(ns1.hashCode(), ns2.hashCode())
-        ns2.putIntExtension(null, 10)
-        assertTrue(ns1.equals(ns2))
-        assertEquals(ns1.hashCode(), ns2.hashCode())
+        builder1.setLegacyScore(TEST_SCORE)
+        assertFalse(builder1.build().equals(builder2.build()))
+        assertNotEquals(builder1.hashCode(), builder2.hashCode())
+        builder2.setLegacyScore(TEST_SCORE)
+        assertTrue(builder1.build().equals(builder2.build()))
+        assertEquals(builder1.build().hashCode(), builder2.build().hashCode())
     }
 }
diff --git a/tests/net/deflake/Android.bp b/tests/net/deflake/Android.bp
index 1c48c74..b1b0171 100644
--- a/tests/net/deflake/Android.bp
+++ b/tests/net/deflake/Android.bp
@@ -26,4 +26,5 @@
         "net-host-tests-utils",
     ],
     data: [":FrameworksNetTests"],
-}
\ No newline at end of file
+    test_suites: ["device-tests"],
+}
diff --git a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
index a35fb40..7ae9e95 100644
--- a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
+++ b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
@@ -40,6 +40,7 @@
 import android.net.NetworkCapabilities;
 import android.net.NetworkInfo;
 import android.net.NetworkProvider;
+import android.net.NetworkScore;
 import android.net.NetworkSpecifier;
 import android.net.SocketKeepalive;
 import android.net.UidRange;
@@ -155,9 +156,13 @@
         }
     }
 
+    private NetworkScore makeNetworkScore(final int legacyScore) {
+        return new NetworkScore.Builder().setLegacyScore(legacyScore).build();
+    }
+
     public void adjustScore(int change) {
         mScore += change;
-        mNetworkAgent.sendNetworkScore(mScore);
+        mNetworkAgent.sendNetworkScore(makeNetworkScore(mScore));
     }
 
     public int getScore() {
diff --git a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java b/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
index e863266..25e9057 100644
--- a/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/LingerMonitorTest.java
@@ -353,8 +353,7 @@
         NetworkCapabilities caps = new NetworkCapabilities();
         caps.addCapability(0);
         caps.addTransportType(transport);
-        NetworkScore ns = new NetworkScore();
-        ns.putIntExtension(NetworkScore.LEGACY_SCORE, 50);
+        NetworkScore ns = new NetworkScore.Builder().setLegacyScore(50).build();
         NetworkAgentInfo nai = new NetworkAgentInfo(null, null, new Network(netId), info, null,
                 caps, ns, mCtx, null, null /* config */, mConnService, mNetd, mDnsResolver, mNMS,
                 NetworkProvider.ID_NONE);
diff --git a/tools/hiddenapi/Android.bp b/tools/hiddenapi/Android.bp
new file mode 100644
index 0000000..e0eb06cb
--- /dev/null
+++ b/tools/hiddenapi/Android.bp
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+python_binary_host {
+	name: "merge_csv",
+	main: "merge_csv.py",
+	srcs: ["merge_csv.py"],
+	version: {
+		py2: {
+			enabled: false,
+		},
+		py3: {
+			enabled: true,
+			embedded_launcher: true
+		},
+	},
+}
diff --git a/tools/hiddenapi/merge_csv.py b/tools/hiddenapi/merge_csv.py
index 48c0755..9661927 100755
--- a/tools/hiddenapi/merge_csv.py
+++ b/tools/hiddenapi/merge_csv.py
@@ -21,20 +21,19 @@
 import sys
 
 csv_readers = [
-    csv.DictReader(open(csv_file, 'rb'), delimiter=',', quotechar='|')
+    csv.DictReader(open(csv_file, 'r'), delimiter=',', quotechar='|')
     for csv_file in sys.argv[1:]
 ]
 
 # Build union of all columns from source files:
 headers = set()
 for reader in csv_readers:
-  headers = headers.union(reader.fieldnames)
+    headers = headers.union(reader.fieldnames)
 
 # Concatenate all files to output:
-out = csv.DictWriter(sys.stdout, delimiter=',', quotechar='|', fieldnames = sorted(headers))
+out = csv.DictWriter(sys.stdout, delimiter=',', quotechar='|', quoting=csv.QUOTE_MINIMAL,
+                     dialect='unix', fieldnames=sorted(headers))
 out.writeheader()
 for reader in csv_readers:
-  for row in reader:
-    out.writerow(row)
-
-
+    for row in reader:
+        out.writerow(row)
diff --git a/tools/stats_log_api_gen/java_writer_q.cpp b/tools/stats_log_api_gen/java_writer_q.cpp
index 1259a68..a68c3a2 100644
--- a/tools/stats_log_api_gen/java_writer_q.cpp
+++ b/tools/stats_log_api_gen/java_writer_q.cpp
@@ -577,6 +577,50 @@
     }
 }
 
+// This method is called in main.cpp to generate StatsLog for modules that's compatible with
+// Q at compile-time.
+int write_stats_log_java_q_for_module(FILE* out, const Atoms& atoms,
+                                      const AtomDecl &attributionDecl, const string& moduleName,
+                                      const string& javaClass, const string& javaPackage,
+                                      const bool supportWorkSource) {
+    // Print prelude
+    fprintf(out, "// This file is autogenerated\n");
+    fprintf(out, "\n");
+    fprintf(out, "package %s;\n", javaPackage.c_str());
+    fprintf(out, "\n");
+    fprintf(out, "import static java.nio.charset.StandardCharsets.UTF_8;\n");
+    fprintf(out, "\n");
+    fprintf(out, "import android.util.StatsLog;\n");
+    fprintf(out, "import android.os.SystemClock;\n");
+    fprintf(out, "\n");
+    fprintf(out, "\n");
+    fprintf(out, "/**\n");
+    fprintf(out, " * Utility class for logging statistics events.\n");
+    fprintf(out, " */\n");
+    fprintf(out, "public class %s {\n", javaClass.c_str());
+
+    write_java_q_logging_constants(out, "    ");
+
+    write_java_atom_codes(out, atoms, moduleName);
+
+    write_java_enum_values(out, atoms, moduleName);
+
+    int errors = 0;
+    // Print write methods
+    fprintf(out, "    // Write methods\n");
+    errors += write_java_methods_q_schema(out, atoms.signatures_to_modules, attributionDecl,
+            moduleName, "    ");
+    errors += write_java_non_chained_methods(out, atoms.non_chained_signatures_to_modules,
+            moduleName);
+    if (supportWorkSource) {
+        errors += write_java_work_source_methods(out, atoms.signatures_to_modules, moduleName);
+    }
+
+    fprintf(out, "}\n");
+
+    return errors;
+}
+
 #if defined(STATS_SCHEMA_LEGACY)
 static void write_java_method(
         FILE* out,
@@ -639,48 +683,6 @@
 
     return 0;
 }
-
-int write_stats_log_java_q_for_module(FILE* out, const Atoms& atoms,
-                                      const AtomDecl &attributionDecl, const string& moduleName,
-                                      const string& javaClass, const string& javaPackage,
-                                      const bool supportWorkSource) {
-    // Print prelude
-    fprintf(out, "// This file is autogenerated\n");
-    fprintf(out, "\n");
-    fprintf(out, "package %s;\n", javaPackage.c_str());
-    fprintf(out, "\n");
-    fprintf(out, "import static java.nio.charset.StandardCharsets.UTF_8;\n");
-    fprintf(out, "\n");
-    fprintf(out, "import android.util.StatsLog;\n");
-    fprintf(out, "import android.os.SystemClock;\n");
-    fprintf(out, "\n");
-    fprintf(out, "\n");
-    fprintf(out, "/**\n");
-    fprintf(out, " * Utility class for logging statistics events.\n");
-    fprintf(out, " */\n");
-    fprintf(out, "public class %s {\n", javaClass.c_str());
-
-    write_java_q_logging_constants(out, "    ");
-
-    write_java_atom_codes(out, atoms, moduleName);
-
-    write_java_enum_values(out, atoms, moduleName);
-
-    int errors = 0;
-    // Print write methods
-    fprintf(out, "    // Write methods\n");
-    errors += write_java_methods_q_schema(out, atoms.signatures_to_modules, attributionDecl,
-            moduleName, "    ");
-    errors += write_java_non_chained_methods(out, atoms.non_chained_signatures_to_modules,
-            moduleName);
-    if (supportWorkSource) {
-        errors += write_java_work_source_methods(out, atoms.signatures_to_modules, moduleName);
-    }
-
-    fprintf(out, "}\n");
-
-    return errors;
-}
 #endif
 
 }  // namespace stats_log_api_gen
diff --git a/tools/stats_log_api_gen/java_writer_q.h b/tools/stats_log_api_gen/java_writer_q.h
index 36df1d8..7d734df 100644
--- a/tools/stats_log_api_gen/java_writer_q.h
+++ b/tools/stats_log_api_gen/java_writer_q.h
@@ -45,13 +45,13 @@
         const int requiredHelpers,
         const string& indent);
 
-#if defined(STATS_SCHEMA_LEGACY)
-int write_stats_log_java_q(FILE* out, const Atoms& atoms, const AtomDecl &attributionDecl,
-                           const bool supportWorkSource);
-
 int write_stats_log_java_q_for_module(FILE* out, const Atoms& atoms,
         const AtomDecl &attributionDecl, const string& moduleName, const string& javaClass,
         const string& javaPackage, const bool supportWorkSource);
+
+#if defined(STATS_SCHEMA_LEGACY)
+int write_stats_log_java_q(FILE* out, const Atoms& atoms, const AtomDecl &attributionDecl,
+                           const bool supportWorkSource);
 #endif
 }  // namespace stats_log_api_gen
 }  // namespace android
diff --git a/tools/stats_log_api_gen/main.cpp b/tools/stats_log_api_gen/main.cpp
index 6089532..ddbf22c 100644
--- a/tools/stats_log_api_gen/main.cpp
+++ b/tools/stats_log_api_gen/main.cpp
@@ -511,8 +511,10 @@
     fprintf(stderr, "  --javaClass CLASS    the class name of the java class.\n");
     fprintf(stderr, "                       Optional for Java with module.\n");
     fprintf(stderr, "                       Default is \"StatsLogInternal\"\n");
-    fprintf(stderr, "  --supportQ           Include support for Android Q.\n");
+    fprintf(stderr, "  --supportQ           Include runtime support for Android Q.\n");
     fprintf(stderr, "  --worksource         Include support for logging WorkSource objects.\n");
+    fprintf(stderr, "  --compileQ           Include compile-time support for Android Q "
+            "(Java only).\n");
 }
 
 /**
@@ -536,6 +538,7 @@
     string javaClass = DEFAULT_JAVA_CLASS;
     bool supportQ = false;
     bool supportWorkSource = false;
+    bool compileQ = false;
 
     int index = 1;
     while (index < argc) {
@@ -630,6 +633,8 @@
             supportQ = true;
         } else if (0 == strcmp("--worksource", argv[index])) {
             supportWorkSource = true;
+        } else if (0 == strcmp("--compileQ", argv[index])) {
+            compileQ = true;
         }
 
         index++;
@@ -645,12 +650,18 @@
         return 1;
     }
 
-    if (DEFAULT_MODULE_NAME == moduleName && supportQ) {
+    if (DEFAULT_MODULE_NAME == moduleName && (supportQ || compileQ)) {
         // Support for Q schema is not needed for default module.
         fprintf(stderr, "%s cannot support Q schema\n", moduleName.c_str());
         return 1;
     }
 
+    if (supportQ && compileQ) {
+        // Runtime Q support is redundant if compile-time Q support is required.
+        fprintf(stderr, "Cannot specify compileQ and supportQ simultaneously.\n");
+        return 1;
+    }
+
     // Collate the parameters
     Atoms atoms;
     int errorCount = collate_atoms(Atom::descriptor(), &atoms);
@@ -748,9 +759,15 @@
             javaClass = "StatsLogInternal";
             javaPackage = "android.util";
         }
-        errorCount = android::stats_log_api_gen::write_stats_log_java(
-                out, atoms, attributionDecl, moduleName, javaClass, javaPackage, supportQ,
-                supportWorkSource);
+        if (compileQ) {
+            errorCount = android::stats_log_api_gen::write_stats_log_java_q_for_module(
+                    out, atoms, attributionDecl, moduleName, javaClass, javaPackage,
+                    supportWorkSource);
+        } else {
+            errorCount = android::stats_log_api_gen::write_stats_log_java(
+                    out, atoms, attributionDecl, moduleName, javaClass, javaPackage, supportQ,
+                    supportWorkSource);
+        }
 #endif
 
         fclose(out);
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 0ef224a..8d95cb0 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -373,6 +373,7 @@
      * ECDHE_ECDSA
      * ECDHE_RSA
      * </pre>
+     * @hide
      */
     public static class SuiteBCipher {
         private SuiteBCipher() { }
@@ -715,8 +716,8 @@
     public BitSet allowedGroupManagementCiphers;
     /**
      * The set of SuiteB ciphers supported by this configuration.
-     * To be used for WPA3-Enterprise mode.
-     * See {@link SuiteBCipher} for descriptions of the values.
+     * To be used for WPA3-Enterprise mode. Set automatically by the framework based on the
+     * certificate type that is used in this configuration.
      */
     @NonNull
     public BitSet allowedSuiteBCiphers;
@@ -1929,54 +1930,38 @@
     private NetworkSelectionStatus mNetworkSelectionStatus = new NetworkSelectionStatus();
 
     /**
-     * @hide
      * This class is intended to store extra failure reason information for the most recent
      * connection attempt, so that it may be surfaced to the settings UI
+     * @hide
      */
-    @SystemApi
+    // TODO(b/148626966): called by SUW via reflection, remove once SUW is updated
     public static class RecentFailure {
 
         private RecentFailure() {}
 
-        /** @hide */
-        @Retention(RetentionPolicy.SOURCE)
-        @IntDef(value = {NONE, STATUS_AP_UNABLE_TO_HANDLE_NEW_STA})
-        public @interface AssociationStatus {}
-
-        /**
-         * No recent failure, or no specific reason given for the recent connection failure
-         */
-        public static final int NONE = 0;
-        /**
-         * Connection to this network recently failed due to Association Rejection Status 17
-         * (AP is full)
-         */
-        public static final int STATUS_AP_UNABLE_TO_HANDLE_NEW_STA = 17;
         /**
          * Association Rejection Status code (NONE for success/non-association-rejection-fail)
          */
-        @AssociationStatus
-        private int mAssociationStatus = NONE;
+        @RecentFailureReason
+        private int mAssociationStatus = RECENT_FAILURE_NONE;
 
         /**
          * @param status the association status code for the recent failure
-         * @hide
          */
-        public void setAssociationStatus(@AssociationStatus int status) {
+        public void setAssociationStatus(@RecentFailureReason int status) {
             mAssociationStatus = status;
         }
         /**
          * Sets the RecentFailure to NONE
-         * @hide
          */
         public void clear() {
-            mAssociationStatus = NONE;
+            mAssociationStatus = RECENT_FAILURE_NONE;
         }
         /**
-         * Get the recent failure code. One of {@link #NONE} or
-         * {@link #STATUS_AP_UNABLE_TO_HANDLE_NEW_STA}.
+         * Get the recent failure code. One of {@link #RECENT_FAILURE_NONE} or
+         * {@link #RECENT_FAILURE_AP_UNABLE_TO_HANDLE_NEW_STA}.
          */
-        @AssociationStatus
+        @RecentFailureReason
         public int getAssociationStatus() {
             return mAssociationStatus;
         }
@@ -1986,10 +1971,47 @@
      * RecentFailure member
      * @hide
      */
+    // TODO(b/148626966): called by SUW via reflection, once SUW is updated, make private and
+    //  rename to mRecentFailure
     @NonNull
-    @SystemApi
     public final RecentFailure recentFailure = new RecentFailure();
 
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "RECENT_FAILURE_", value = {
+            RECENT_FAILURE_NONE,
+            RECENT_FAILURE_AP_UNABLE_TO_HANDLE_NEW_STA})
+    public @interface RecentFailureReason {}
+
+    /**
+     * No recent failure, or no specific reason given for the recent connection failure
+     * @hide
+     */
+    @SystemApi
+    public static final int RECENT_FAILURE_NONE = 0;
+    /**
+     * Connection to this network recently failed due to Association Rejection Status 17
+     * (AP is full)
+     * @hide
+     */
+    @SystemApi
+    public static final int RECENT_FAILURE_AP_UNABLE_TO_HANDLE_NEW_STA = 17;
+
+    /**
+     * Get the failure reason for the most recent connection attempt, or
+     * {@link #RECENT_FAILURE_NONE} if there was no failure.
+     *
+     * Failure reasons include:
+     * {@link #RECENT_FAILURE_AP_UNABLE_TO_HANDLE_NEW_STA}
+     *
+     * @hide
+     */
+    @RecentFailureReason
+    @SystemApi
+    public int getRecentFailureReason() {
+        return recentFailure.getAssociationStatus();
+    }
+
     /**
      * Get the network selection status.
      * @hide
diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java
index 6f01350..8250a95 100644
--- a/wifi/java/android/net/wifi/WifiScanner.java
+++ b/wifi/java/android/net/wifi/WifiScanner.java
@@ -836,6 +836,8 @@
      * Enable/Disable wifi scanning.
      *
      * @param enable set to true to enable scanning, set to false to disable all types of scanning.
+     *
+     * @see WifiManager#ACTION_WIFI_SCAN_AVAILABLE
      * {@hide}
      */
     @SystemApi
diff --git a/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java b/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java
index 3a0d080..756d679 100644
--- a/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java
+++ b/wifi/java/android/net/wifi/hotspot2/PasspointConfiguration.java
@@ -19,6 +19,7 @@
 import static android.net.wifi.WifiConfiguration.METERED_OVERRIDE_NONE;
 import static android.net.wifi.WifiConfiguration.MeteredOverride;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.net.wifi.hotspot2.pps.Credential;
@@ -895,4 +896,18 @@
     public boolean isOsuProvisioned() {
         return getUpdateIdentifier() != Integer.MIN_VALUE;
     }
+
+    /**
+     * Get a unique identifier for a PasspointConfiguration object.
+     *
+     * @return A unique identifier
+     * @throws IllegalStateException if Credential or HomeSP nodes are not initialized
+     */
+    public @NonNull String getUniqueId() throws IllegalStateException {
+        if (mCredential == null || mHomeSp == null || TextUtils.isEmpty(mHomeSp.getFqdn())) {
+            throw new IllegalStateException("Credential or HomeSP are not initialized");
+        }
+
+        return mHomeSp.getFqdn();
+    }
 }
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java b/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java
index 9562f95..36c7213 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pConfig.java
@@ -137,12 +137,12 @@
 
     /** @hide */
     @UnsupportedAppUsage
-    public int netId = WifiP2pGroup.PERSISTENT_NET_ID;
+    public int netId = WifiP2pGroup.NETWORK_ID_PERSISTENT;
 
     /**
      * Get the network ID of this P2P configuration.
-     * @return either a non-negative network ID, or one of {@link WifiP2pGroup#PERSISTENT_NET_ID} or
-     * {@link WifiP2pGroup#TEMPORARY_NET_ID}.
+     * @return either a non-negative network ID, or one of
+     * {@link WifiP2pGroup#NETWORK_ID_PERSISTENT} or {@link WifiP2pGroup#NETWORK_ID_TEMPORARY}.
      */
     public int getNetworkId() {
         return netId;
@@ -280,7 +280,7 @@
         private String mPassphrase = "";
         private int mGroupOperatingBand = GROUP_OWNER_BAND_AUTO;
         private int mGroupOperatingFrequency = GROUP_OWNER_BAND_AUTO;
-        private int mNetId = WifiP2pGroup.TEMPORARY_NET_ID;
+        private int mNetId = WifiP2pGroup.NETWORK_ID_TEMPORARY;
 
         /**
          * Specify the peer's MAC address. If not set, the device will
@@ -460,9 +460,9 @@
          */
         public @NonNull Builder enablePersistentMode(boolean persistent) {
             if (persistent) {
-                mNetId = WifiP2pGroup.PERSISTENT_NET_ID;
+                mNetId = WifiP2pGroup.NETWORK_ID_PERSISTENT;
             } else {
-                mNetId = WifiP2pGroup.TEMPORARY_NET_ID;
+                mNetId = WifiP2pGroup.NETWORK_ID_TEMPORARY;
             }
             return this;
         }
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pGroup.java b/wifi/java/android/net/wifi/p2p/WifiP2pGroup.java
index 21f6704..e497b22 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pGroup.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pGroup.java
@@ -41,7 +41,15 @@
      * The temporary network id.
      * @see #getNetworkId()
      */
-    public static final int TEMPORARY_NET_ID = -1;
+    public static final int NETWORK_ID_TEMPORARY = -1;
+
+    /**
+     * The temporary network id.
+     *
+     * @hide
+     */
+    @UnsupportedAppUsage
+    public static final int TEMPORARY_NET_ID = NETWORK_ID_TEMPORARY;
 
     /**
      * The persistent network id.
@@ -49,7 +57,7 @@
      * Otherwise, create a new persistent profile.
      * @see #getNetworkId()
      */
-    public static final int PERSISTENT_NET_ID = -2;
+    public static final int NETWORK_ID_PERSISTENT = -2;
 
     /** The network name */
     private String mNetworkName;
@@ -130,13 +138,13 @@
             mPassphrase = match.group(4);
             mOwner = new WifiP2pDevice(match.group(5));
             if (match.group(6) != null) {
-                mNetId = PERSISTENT_NET_ID;
+                mNetId = NETWORK_ID_PERSISTENT;
             } else {
-                mNetId = TEMPORARY_NET_ID;
+                mNetId = NETWORK_ID_TEMPORARY;
             }
         } else if (tokens[0].equals("P2P-INVITATION-RECEIVED")) {
             String sa = null;
-            mNetId = PERSISTENT_NET_ID;
+            mNetId = NETWORK_ID_PERSISTENT;
             for (String token : tokens) {
                 String[] nameValue = token.split("=");
                 if (nameValue.length != 2) continue;
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
index 3459c94..0fe0675 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pManager.java
@@ -1293,7 +1293,7 @@
     @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION)
     public void createGroup(Channel c, ActionListener listener) {
         checkChannel(c);
-        c.mAsyncChannel.sendMessage(CREATE_GROUP, WifiP2pGroup.PERSISTENT_NET_ID,
+        c.mAsyncChannel.sendMessage(CREATE_GROUP, WifiP2pGroup.NETWORK_ID_PERSISTENT,
                 c.putListener(listener));
     }
 
diff --git a/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java b/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java
index 654154d..e78c5bf 100644
--- a/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java
+++ b/wifi/tests/src/android/net/wifi/hotspot2/PasspointConfigurationTest.java
@@ -18,6 +18,7 @@
 
 import static android.net.wifi.WifiConfiguration.METERED_OVERRIDE_NONE;
 
+import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
@@ -364,4 +365,54 @@
         assertTrue(config.validateForR2());
         assertTrue(config.isOsuProvisioned());
     }
+
+    /**
+     * Verify that the unique identifier generated is correct.
+     *
+     * @throws Exception
+     */
+    @Test
+    public void validateUniqueId() throws Exception {
+        PasspointConfiguration config = PasspointTestUtils.createConfig();
+        String uniqueId;
+        uniqueId = config.getUniqueId();
+        assertEquals(uniqueId, config.getHomeSp().getFqdn());
+    }
+
+    /**
+     * Verify that the unique identifier API generates an exception if HomeSP is not initialized.
+     *
+     * @throws Exception
+     */
+    @Test
+    public void validateUniqueIdExceptionWithEmptyHomeSp() throws Exception {
+        PasspointConfiguration config = PasspointTestUtils.createConfig();
+        config.setHomeSp(null);
+        boolean exceptionCaught = false;
+        try {
+            String uniqueId = config.getUniqueId();
+        } catch (IllegalStateException e) {
+            exceptionCaught = true;
+        }
+        assertTrue(exceptionCaught);
+    }
+
+    /**
+     * Verify that the unique identifier API generates an exception if Credential is not
+     * initialized.
+     *
+     * @throws Exception
+     */
+    @Test
+    public void validateUniqueIdExceptionWithEmptyCredential() throws Exception {
+        PasspointConfiguration config = PasspointTestUtils.createConfig();
+        config.setCredential(null);
+        boolean exceptionCaught = false;
+        try {
+            String uniqueId = config.getUniqueId();
+        } catch (IllegalStateException e) {
+            exceptionCaught = true;
+        }
+        assertTrue(exceptionCaught);
+    }
 }