Merge "Fix InTouchMode is not consistent between WMS and InputFlinger"
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/TEST_MAPPING b/apex/blobstore/TEST_MAPPING
index 4dc0c49..cfe19a5 100644
--- a/apex/blobstore/TEST_MAPPING
+++ b/apex/blobstore/TEST_MAPPING
@@ -2,6 +2,14 @@
   "presubmit": [
     {
       "name": "CtsBlobStoreTestCases"
+    },
+    {
+      "name": "FrameworksServicesTests",
+      "options": [
+        {
+          "include-filter": "com.android.server.blob"
+        }
+      ]
     }
   ]
 }
\ No newline at end of file
diff --git a/apex/blobstore/framework/java/android/app/blob/BlobHandle.java b/apex/blobstore/framework/java/android/app/blob/BlobHandle.java
index f7e6a98..f110b36 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;
     }
 
     /**
@@ -215,12 +214,47 @@
     }
 
     /** @hide */
-    public void dump(IndentingPrintWriter fout) {
-        fout.println("algo: " + algorithm);
-        fout.println("digest: " + Base64.encodeToString(digest, Base64.NO_WRAP));
-        fout.println("label: " + label);
-        fout.println("expiryMs: " + expiryTimeMillis);
-        fout.println("tag: " + tag);
+    public void dump(IndentingPrintWriter fout, boolean dumpFull) {
+        if (dumpFull) {
+            fout.println("algo: " + algorithm);
+            fout.println("digest: " + (dumpFull ? encodeDigest() : safeDigest()));
+            fout.println("label: " + label);
+            fout.println("expiryMs: " + expiryTimeMillis);
+            fout.println("tag: " + tag);
+        } else {
+            fout.println(toString());
+        }
+    }
+
+    /** @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");
+    }
+
+    @Override
+    public String toString() {
+        return "BlobHandle {"
+                + "algo:" + algorithm + ","
+                + "digest:" + safeDigest() + ","
+                + "label:" + label + ","
+                + "expiryMs:" + expiryTimeMillis + ","
+                + "tag:" + tag
+                + "}";
+    }
+
+    private String safeDigest() {
+        final String digestStr = encodeDigest();
+        return digestStr.substring(0, 2) + ".." + digestStr.substring(digestStr.length() - 2);
+    }
+
+    private String encodeDigest() {
+        return Base64.encodeToString(digest, Base64.NO_WRAP);
     }
 
     public static final @NonNull Creator<BlobHandle> CREATOR = new Creator<BlobHandle>() {
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java b/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java
index e9838d6..aba3e8c 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobMetadata.java
@@ -48,6 +48,7 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.XmlUtils;
+import com.android.server.blob.BlobStoreManagerService.DumpArgs;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -156,6 +157,12 @@
         }
     }
 
+    boolean hasLeases() {
+        synchronized (mMetadataLock) {
+            return mLeasees.isEmpty();
+        }
+    }
+
     boolean isAccessAllowedForCaller(String callingPackage, int callingUid) {
         // TODO: verify blob is still valid (expiryTime is not elapsed)
         synchronized (mMetadataLock) {
@@ -234,10 +241,10 @@
         return revocableFd.getRevocableFileDescriptor();
     }
 
-    void dump(IndentingPrintWriter fout) {
+    void dump(IndentingPrintWriter fout, DumpArgs dumpArgs) {
         fout.println("blobHandle:");
         fout.increaseIndent();
-        blobHandle.dump(fout);
+        blobHandle.dump(fout, dumpArgs.shouldDumpFull());
         fout.decreaseIndent();
 
         fout.println("Committers:");
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 fcc30e3..13f095e 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerService.java
@@ -40,6 +40,7 @@
 import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.annotation.UserIdInt;
 import android.app.blob.BlobHandle;
 import android.app.blob.IBlobStoreManager;
 import android.app.blob.IBlobStoreSession;
@@ -49,6 +50,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;
@@ -67,6 +69,8 @@
 import android.util.Xml;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.CollectionUtils;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.IndentingPrintWriter;
@@ -91,6 +95,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.
@@ -112,20 +117,35 @@
 
     private final Context mContext;
     private final Handler mHandler;
+    private final Injector mInjector;
     private final SessionStateChangeListener mSessionStateChangeListener =
             new SessionStateChangeListener();
 
     private PackageManagerInternal mPackageManagerInternal;
 
-    public BlobStoreManagerService(Context context) {
-        super(context);
-        mContext = context;
+    private final Runnable mSaveBlobsInfoRunnable = this::writeBlobsInfo;
+    private final Runnable mSaveSessionsRunnable = this::writeBlobSessions;
 
+    public BlobStoreManagerService(Context context) {
+        this(context, new Injector());
+    }
+
+    @VisibleForTesting
+    BlobStoreManagerService(Context context, Injector injector) {
+        super(context);
+
+        mContext = context;
+        mInjector = injector;
+        mHandler = mInjector.initializeMessageHandler();
+    }
+
+    private static Handler initializeMessageHandler() {
         final HandlerThread handlerThread = new ServiceThread(TAG,
                 Process.THREAD_PRIORITY_BACKGROUND, true /* allowIo */);
         handlerThread.start();
-        mHandler = new Handler(handlerThread.getLooper());
-        Watchdog.getInstance().addThread(mHandler);
+        final Handler handler = new Handler(handlerThread.getLooper());
+        Watchdog.getInstance().addThread(handler);
+        return handler;
     }
 
     @Override
@@ -181,6 +201,20 @@
         return userBlobs;
     }
 
+    @VisibleForTesting
+    void addUserSessionsForTest(LongSparseArray<BlobStoreSession> userSessions, int userId) {
+        synchronized (mBlobsLock) {
+            mSessions.put(userId, userSessions);
+        }
+    }
+
+    @VisibleForTesting
+    void addUserBlobsForTest(ArrayMap<BlobHandle, BlobMetadata> userBlobs, int userId) {
+        synchronized (mBlobsLock) {
+            mBlobsMap.put(userId, userBlobs);
+        }
+    }
+
     private long createSessionInternal(BlobHandle blobHandle,
             int callingUid, String callingPackage) {
         synchronized (mBlobsLock) {
@@ -293,23 +327,23 @@
                 case STATE_ABANDONED:
                 case STATE_VERIFIED_INVALID:
                     session.getSessionFile().delete();
-                    getUserSessionsLocked(UserHandle.getUserId(session.ownerUid))
-                            .remove(session.sessionId);
+                    getUserSessionsLocked(UserHandle.getUserId(session.getOwnerUid()))
+                            .remove(session.getSessionId());
                     break;
                 case STATE_COMMITTED:
                     session.verifyBlobData();
                     break;
                 case STATE_VERIFIED_VALID:
-                    final int userId = UserHandle.getUserId(session.ownerUid);
+                    final int userId = UserHandle.getUserId(session.getOwnerUid());
                     final ArrayMap<BlobHandle, BlobMetadata> userBlobs = getUserBlobsLocked(userId);
-                    BlobMetadata blob = userBlobs.get(session.blobHandle);
+                    BlobMetadata blob = userBlobs.get(session.getBlobHandle());
                     if (blob == null) {
                         blob = new BlobMetadata(mContext,
-                                session.sessionId, session.blobHandle, userId);
-                        userBlobs.put(session.blobHandle, blob);
+                                session.getSessionId(), session.getBlobHandle(), userId);
+                        userBlobs.put(session.getBlobHandle(), blob);
                     }
-                    final Committer newCommitter = new Committer(session.ownerPackageName,
-                            session.ownerUid, session.getBlobAccessMode());
+                    final Committer newCommitter = new Committer(session.getOwnerPackageName(),
+                            session.getOwnerUid(), session.getBlobAccessMode());
                     final Committer existingCommitter = blob.getExistingCommitter(newCommitter);
                     blob.addCommitter(newCommitter);
                     try {
@@ -319,8 +353,8 @@
                         blob.addCommitter(existingCommitter);
                         session.sendCommitCallbackResult(COMMIT_RESULT_ERROR);
                     }
-                    getUserSessionsLocked(UserHandle.getUserId(session.ownerUid))
-                            .remove(session.sessionId);
+                    getUserSessionsLocked(UserHandle.getUserId(session.getOwnerUid()))
+                            .remove(session.getSessionId());
                     break;
                 default:
                     Slog.wtf(TAG, "Invalid session state: "
@@ -399,17 +433,17 @@
                         continue;
                     }
                     final SparseArray<String> userPackages = allPackages.get(
-                            UserHandle.getUserId(session.ownerUid));
+                            UserHandle.getUserId(session.getOwnerUid()));
                     if (userPackages != null
-                            && session.ownerPackageName.equals(
-                                    userPackages.get(session.ownerUid))) {
-                        getUserSessionsLocked(UserHandle.getUserId(session.ownerUid)).put(
-                                session.sessionId, session);
+                            && session.getOwnerPackageName().equals(
+                                    userPackages.get(session.getOwnerUid()))) {
+                        getUserSessionsLocked(UserHandle.getUserId(session.getOwnerUid())).put(
+                                session.getSessionId(), session);
                     } else {
                         // Unknown package or the session data does not belong to this package.
                         session.getSessionFile().delete();
                     }
-                    mCurrentMaxSessionId = Math.max(mCurrentMaxSessionId, session.sessionId);
+                    mCurrentMaxSessionId = Math.max(mCurrentMaxSessionId, session.getSessionId());
                 }
             }
         } catch (Exception e) {
@@ -504,9 +538,9 @@
     }
 
     private void writeBlobsInfoAsync() {
-        mHandler.post(PooledLambda.obtainRunnable(
-                BlobStoreManagerService::writeBlobsInfo,
-                BlobStoreManagerService.this).recycleOnUse());
+        if (!mHandler.hasCallbacks(mSaveBlobsInfoRunnable)) {
+            mHandler.post(mSaveBlobsInfoRunnable);
+        }
     }
 
     private void writeBlobSessions() {
@@ -520,9 +554,9 @@
     }
 
     private void writeBlobSessionsAsync() {
-        mHandler.post(PooledLambda.obtainRunnable(
-                BlobStoreManagerService::writeBlobSessions,
-                BlobStoreManagerService.this).recycleOnUse());
+        if (!mHandler.hasCallbacks(mSaveSessionsRunnable)) {
+            mHandler.post(mSaveSessionsRunnable);
+        }
     }
 
     private int getPackageUid(String packageName, int userId) {
@@ -568,7 +602,8 @@
         return new AtomicFile(file, "blobs_index" /* commitLogTag */);
     }
 
-    private void handlePackageRemoved(String packageName, int uid) {
+    @VisibleForTesting
+    void handlePackageRemoved(String packageName, int uid) {
         synchronized (mBlobsLock) {
             // Clean up any pending sessions
             final LongSparseArray<BlobStoreSession> userSessions =
@@ -576,25 +611,35 @@
             final ArrayList<Integer> indicesToRemove = new ArrayList<>();
             for (int i = 0, count = userSessions.size(); i < count; ++i) {
                 final BlobStoreSession session = userSessions.valueAt(i);
-                if (session.ownerUid == uid
-                        && session.ownerPackageName.equals(packageName)) {
+                if (session.getOwnerUid() == uid
+                        && session.getOwnerPackageName().equals(packageName)) {
                     session.getSessionFile().delete();
                     indicesToRemove.add(i);
                 }
             }
             for (int i = 0, count = indicesToRemove.size(); i < count; ++i) {
-                userSessions.removeAt(i);
+                userSessions.removeAt(indicesToRemove.get(i));
             }
+            writeBlobSessionsAsync();
 
             // Remove the package from the committer and leasee list
             final ArrayMap<BlobHandle, BlobMetadata> userBlobs =
                     getUserBlobsLocked(UserHandle.getUserId(uid));
+            indicesToRemove.clear();
             for (int i = 0, count = userBlobs.size(); i < count; ++i) {
                 final BlobMetadata blobMetadata = userBlobs.valueAt(i);
                 blobMetadata.removeCommitter(packageName, uid);
                 blobMetadata.removeLeasee(packageName, uid);
+                // Delete the blob if it doesn't have any active leases.
+                if (!blobMetadata.hasLeases()) {
+                    blobMetadata.getBlobFile().delete();
+                    indicesToRemove.add(i);
+                }
             }
-            // TODO: clean-up blobs which doesn't have any active leases.
+            for (int i = 0, count = indicesToRemove.size(); i < count; ++i) {
+                userBlobs.removeAt(indicesToRemove.get(i));
+            }
+            writeBlobsInfoAsync();
         }
     }
 
@@ -620,6 +665,80 @@
         }
     }
 
+    void runClearAllSessions(@UserIdInt int userId) {
+        synchronized (mBlobsLock) {
+            if (userId == UserHandle.USER_ALL) {
+                mSessions.clear();
+            } else {
+                mSessions.remove(userId);
+            }
+            writeBlobSessionsAsync();
+        }
+    }
+
+    void runClearAllBlobs(@UserIdInt int userId) {
+        synchronized (mBlobsLock) {
+            if (userId == UserHandle.USER_ALL) {
+                mBlobsMap.clear();
+            } else {
+                mBlobsMap.remove(userId);
+            }
+            writeBlobsInfoAsync();
+        }
+    }
+
+    @GuardedBy("mBlobsLock")
+    private void dumpSessionsLocked(IndentingPrintWriter fout, DumpArgs dumpArgs) {
+        for (int i = 0, userCount = mSessions.size(); i < userCount; ++i) {
+            final int userId = mSessions.keyAt(i);
+            if (!dumpArgs.shouldDumpUser(userId)) {
+                continue;
+            }
+            final LongSparseArray<BlobStoreSession> userSessions = mSessions.valueAt(i);
+            fout.println("List of sessions in user #"
+                    + userId + " (" + userSessions.size() + "):");
+            fout.increaseIndent();
+            for (int j = 0, sessionsCount = userSessions.size(); j < sessionsCount; ++j) {
+                final long sessionId = userSessions.keyAt(j);
+                final BlobStoreSession session = userSessions.valueAt(j);
+                if (!dumpArgs.shouldDumpSession(session.getOwnerPackageName(),
+                        session.getOwnerUid(), session.getSessionId())) {
+                    continue;
+                }
+                fout.println("Session #" + sessionId);
+                fout.increaseIndent();
+                session.dump(fout, dumpArgs);
+                fout.decreaseIndent();
+            }
+            fout.decreaseIndent();
+        }
+    }
+
+    @GuardedBy("mBlobsLock")
+    private void dumpBlobsLocked(IndentingPrintWriter fout, DumpArgs dumpArgs) {
+        for (int i = 0, userCount = mBlobsMap.size(); i < userCount; ++i) {
+            final int userId = mBlobsMap.keyAt(i);
+            if (!dumpArgs.shouldDumpUser(userId)) {
+                continue;
+            }
+            final ArrayMap<BlobHandle, BlobMetadata> userBlobs = mBlobsMap.valueAt(i);
+            fout.println("List of blobs in user #"
+                    + userId + " (" + userBlobs.size() + "):");
+            fout.increaseIndent();
+            for (int j = 0, blobsCount = userBlobs.size(); j < blobsCount; ++j) {
+                final BlobMetadata blobMetadata = userBlobs.valueAt(j);
+                if (!dumpArgs.shouldDumpBlob(blobMetadata.blobId)) {
+                    continue;
+                }
+                fout.println("Blob #" + blobMetadata.blobId);
+                fout.increaseIndent();
+                blobMetadata.dump(fout, dumpArgs);
+                fout.decreaseIndent();
+            }
+            fout.decreaseIndent();
+        }
+    }
+
     private class PackageChangedReceiver extends BroadcastReceiver {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -658,10 +777,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 +800,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 +813,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 +824,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 +846,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 +876,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());
@@ -766,47 +888,164 @@
         public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter writer,
                 @Nullable String[] args) {
             // TODO: add proto-based version of this.
-            if (!DumpUtils.checkDumpPermission(mContext, TAG, writer)) return;
+            if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, writer)) return;
+
+            final DumpArgs dumpArgs = DumpArgs.parse(args);
 
             final IndentingPrintWriter fout = new IndentingPrintWriter(writer, "    ");
             synchronized (mBlobsLock) {
                 fout.println("mCurrentMaxSessionId: " + mCurrentMaxSessionId);
                 fout.println();
-                for (int i = 0, userCount = mSessions.size(); i < userCount; ++i) {
-                    final int userId = mSessions.keyAt(i);
-                    final LongSparseArray<BlobStoreSession> userSessions = mSessions.valueAt(i);
-                    fout.println("List of sessions in user #"
-                            + userId + " (" + userSessions.size() + "):");
-                    fout.increaseIndent();
-                    for (int j = 0, sessionsCount = userSessions.size(); j < sessionsCount; ++j) {
-                        final long sessionId = userSessions.keyAt(j);
-                        final BlobStoreSession session = userSessions.valueAt(j);
-                        fout.println("Session #" + sessionId);
-                        fout.increaseIndent();
-                        session.dump(fout);
-                        fout.decreaseIndent();
-                    }
-                    fout.decreaseIndent();
+
+                if (dumpArgs.shouldDumpSessions()) {
+                    dumpSessionsLocked(fout, dumpArgs);
+                    fout.println();
                 }
-
-                fout.print("\n\n");
-
-                for (int i = 0, userCount = mBlobsMap.size(); i < userCount; ++i) {
-                    final int userId = mBlobsMap.keyAt(i);
-                    final ArrayMap<BlobHandle, BlobMetadata> userBlobs = mBlobsMap.valueAt(i);
-                    fout.println("List of blobs in user #"
-                            + userId + " (" + userBlobs.size() + "):");
-                    fout.increaseIndent();
-                    for (int j = 0, blobsCount = userBlobs.size(); j < blobsCount; ++j) {
-                        final BlobMetadata blobMetadata = userBlobs.valueAt(j);
-                        fout.println("Blob #" + blobMetadata.blobId);
-                        fout.increaseIndent();
-                        blobMetadata.dump(fout);
-                        fout.decreaseIndent();
-                    }
-                    fout.decreaseIndent();
+                if (dumpArgs.shouldDumpBlobs()) {
+                    dumpBlobsLocked(fout, dumpArgs);
+                    fout.println();
                 }
             }
         }
+
+        @Override
+        public int handleShellCommand(@NonNull ParcelFileDescriptor in,
+                @NonNull ParcelFileDescriptor out, @NonNull ParcelFileDescriptor err,
+                @NonNull String[] args) {
+            return (new BlobStoreManagerShellCommand(BlobStoreManagerService.this)).exec(this,
+                    in.getFileDescriptor(), out.getFileDescriptor(), err.getFileDescriptor(), args);
+        }
+    }
+
+    static final class DumpArgs {
+        private boolean mDumpFull;
+        private final ArrayList<String> mDumpPackages = new ArrayList<>();
+        private final ArrayList<Integer> mDumpUids = new ArrayList<>();
+        private final ArrayList<Integer> mDumpUserIds = new ArrayList<>();
+        private final ArrayList<Long> mDumpBlobIds = new ArrayList<>();
+        private boolean mDumpOnlySelectedSections;
+        private boolean mDumpSessions;
+        private boolean mDumpBlobs;
+
+        public boolean shouldDumpSession(String packageName, int uid, long blobId) {
+            if (!CollectionUtils.isEmpty(mDumpPackages)
+                    && mDumpPackages.indexOf(packageName) < 0) {
+                return false;
+            }
+            if (!CollectionUtils.isEmpty(mDumpUids)
+                    && mDumpUids.indexOf(uid) < 0) {
+                return false;
+            }
+            if (!CollectionUtils.isEmpty(mDumpBlobIds)
+                    && mDumpBlobIds.indexOf(blobId) < 0) {
+                return false;
+            }
+            return true;
+        }
+
+        public boolean shouldDumpSessions() {
+            if (!mDumpOnlySelectedSections) {
+                return true;
+            }
+            return mDumpSessions;
+        }
+
+        public boolean shouldDumpBlobs() {
+            if (!mDumpOnlySelectedSections) {
+                return true;
+            }
+            return mDumpBlobs;
+        }
+
+        public boolean shouldDumpBlob(long blobId) {
+            return CollectionUtils.isEmpty(mDumpBlobIds)
+                    || mDumpBlobIds.indexOf(blobId) >= 0;
+        }
+
+        public boolean shouldDumpFull() {
+            return mDumpFull;
+        }
+
+        public boolean shouldDumpUser(int userId) {
+            return CollectionUtils.isEmpty(mDumpUserIds)
+                    || mDumpUserIds.indexOf(userId) >= 0;
+        }
+
+        private DumpArgs() {}
+
+        public static DumpArgs parse(String[] args) {
+            final DumpArgs dumpArgs = new DumpArgs();
+            if (args == null) {
+                return dumpArgs;
+            }
+
+            for (int i = 0; i < args.length; ++i) {
+                final String opt = args[i];
+                if ("--full".equals(opt) || "-f".equals(opt)) {
+                    final int callingUid = Binder.getCallingUid();
+                    if (callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID) {
+                        dumpArgs.mDumpFull = true;
+                    }
+                } else if ("--sessions".equals(opt)) {
+                    dumpArgs.mDumpOnlySelectedSections = true;
+                    dumpArgs.mDumpSessions = true;
+                } else if ("--blobs".equals(opt)) {
+                    dumpArgs.mDumpOnlySelectedSections = true;
+                    dumpArgs.mDumpBlobs = true;
+                } else if ("--package".equals(opt) || "-p".equals(opt)) {
+                    dumpArgs.mDumpPackages.add(getStringArgRequired(args, ++i, "packageName"));
+                } else if ("--uid".equals(opt) || "-u".equals(opt)) {
+                    dumpArgs.mDumpUids.add(getIntArgRequired(args, ++i, "uid"));
+                } else if ("--user".equals(opt)) {
+                    dumpArgs.mDumpUserIds.add(getIntArgRequired(args, ++i, "userId"));
+                } else if ("--blob".equals(opt) || "-b".equals(opt)) {
+                    dumpArgs.mDumpBlobIds.add(getLongArgRequired(args, ++i, "blobId"));
+                } else {
+                    // Everything else is assumed to be blob ids.
+                    dumpArgs.mDumpBlobIds.add(getLongArgRequired(args, i, "blobId"));
+                }
+            }
+            return dumpArgs;
+        }
+
+        private static String getStringArgRequired(String[] args, int index, String argName) {
+            if (index >= args.length) {
+                throw new IllegalArgumentException("Missing " + argName);
+            }
+            return args[index];
+        }
+
+        private static int getIntArgRequired(String[] args, int index, String argName) {
+            if (index >= args.length) {
+                throw new IllegalArgumentException("Missing " + argName);
+            }
+            final int value;
+            try {
+                value = Integer.parseInt(args[index]);
+            } catch (NumberFormatException e) {
+                throw new IllegalArgumentException("Invalid " + argName + ": " + args[index]);
+            }
+            return value;
+        }
+
+        private static long getLongArgRequired(String[] args, int index, String argName) {
+            if (index >= args.length) {
+                throw new IllegalArgumentException("Missing " + argName);
+            }
+            final long value;
+            try {
+                value = Long.parseLong(args[index]);
+            } catch (NumberFormatException e) {
+                throw new IllegalArgumentException("Invalid " + argName + ": " + args[index]);
+            }
+            return value;
+        }
+    }
+
+    @VisibleForTesting
+    static class Injector {
+        public Handler initializeMessageHandler() {
+            return BlobStoreManagerService.initializeMessageHandler();
+        }
     }
 }
\ No newline at end of file
diff --git a/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerShellCommand.java b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerShellCommand.java
new file mode 100644
index 0000000..3ac30f8
--- /dev/null
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreManagerShellCommand.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright 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.blob;
+
+import android.os.ShellCommand;
+import android.os.UserHandle;
+
+import java.io.PrintWriter;
+
+class BlobStoreManagerShellCommand extends ShellCommand {
+
+    private final BlobStoreManagerService mService;
+
+    BlobStoreManagerShellCommand(BlobStoreManagerService blobStoreManagerService) {
+        mService = blobStoreManagerService;
+    }
+
+    @Override
+    public int onCommand(String cmd) {
+        if (cmd == null) {
+            return handleDefaultCommands(null);
+        }
+        final PrintWriter pw = getOutPrintWriter();
+        switch (cmd) {
+            case "clear-all-sessions":
+                return runClearAllSessions(pw);
+            case "clear-all-blobs":
+                return runClearAllBlobs(pw);
+            default:
+                return handleDefaultCommands(cmd);
+        }
+    }
+
+    private int runClearAllSessions(PrintWriter pw) {
+        final ParsedArgs args = new ParsedArgs();
+        args.userId = UserHandle.USER_ALL;
+
+        if (parseOptions(pw, args) < 0) {
+            return -1;
+        }
+
+        mService.runClearAllSessions(args.userId);
+        return 0;
+    }
+
+    private int runClearAllBlobs(PrintWriter pw) {
+        final ParsedArgs args = new ParsedArgs();
+        args.userId = UserHandle.USER_ALL;
+
+        if (parseOptions(pw, args) < 0) {
+            return -1;
+        }
+
+        mService.runClearAllBlobs(args.userId);
+        return 0;
+    }
+
+    @Override
+    public void onHelp() {
+        final PrintWriter pw = getOutPrintWriter();
+        pw.println("BlobStore service (blob_store) commands:");
+        pw.println("help");
+        pw.println("    Print this help text.");
+        pw.println();
+        pw.println("clear-all-sessions [-u | --user USER_ID]");
+        pw.println("    Remove all sessions.");
+        pw.println("    Options:");
+        pw.println("      -u or --user: specify which user's sessions to be removed;");
+        pw.println("                    If not specified, sessions in all users are removed.");
+        pw.println();
+        pw.println("clear-all-blobs [-u | --user USER_ID]");
+        pw.println("    Remove all blobs.");
+        pw.println("    Options:");
+        pw.println("      -u or --user: specify which user's blobs to be removed;");
+        pw.println("                    If not specified, blobs in all users are removed.");
+        pw.println();
+    }
+
+    private int parseOptions(PrintWriter pw, ParsedArgs args) {
+        String opt;
+        while ((opt = getNextOption()) != null) {
+            switch (opt) {
+                case "-u":
+                case "--user":
+                    args.userId = Integer.parseInt(getNextArgRequired());
+                    break;
+                default:
+                    pw.println("Error: unknown option '" + opt + "'");
+                    return -1;
+            }
+        }
+        return 0;
+    }
+
+    private static class ParsedArgs {
+        public int userId;
+    }
+}
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..54a2997 100644
--- a/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java
+++ b/apex/blobstore/service/java/com/android/server/blob/BlobStoreSession.java
@@ -47,9 +47,11 @@
 import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.Preconditions;
 import com.android.internal.util.XmlUtils;
+import com.android.server.blob.BlobStoreManagerService.DumpArgs;
 import com.android.server.blob.BlobStoreManagerService.SessionStateChangeListener;
 
 import org.xmlpull.v1.XmlPullParser;
@@ -62,9 +64,11 @@
 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 {
+@VisibleForTesting
+class BlobStoreSession extends IBlobStoreSession.Stub {
 
     static final int STATE_OPENED = 1;
     static final int STATE_CLOSED = 0;
@@ -78,10 +82,10 @@
     private final Context mContext;
     private final SessionStateChangeListener mListener;
 
-    public final BlobHandle blobHandle;
-    public final long sessionId;
-    public final int ownerUid;
-    public final String ownerPackageName;
+    private final BlobHandle mBlobHandle;
+    private final long mSessionId;
+    private final int mOwnerUid;
+    private final String mOwnerPackageName;
 
     // Do not access this directly, instead use getSessionFile().
     private File mSessionFile;
@@ -101,15 +105,31 @@
     BlobStoreSession(Context context, long sessionId, BlobHandle blobHandle,
             int ownerUid, String ownerPackageName, SessionStateChangeListener listener) {
         this.mContext = context;
-        this.blobHandle = blobHandle;
-        this.sessionId = sessionId;
-        this.ownerUid = ownerUid;
-        this.ownerPackageName = ownerPackageName;
+        this.mBlobHandle = blobHandle;
+        this.mSessionId = sessionId;
+        this.mOwnerUid = ownerUid;
+        this.mOwnerPackageName = ownerPackageName;
         this.mListener = listener;
     }
 
+    public BlobHandle getBlobHandle() {
+        return mBlobHandle;
+    }
+
+    public long getSessionId() {
+        return mSessionId;
+    }
+
+    public int getOwnerUid() {
+        return mOwnerUid;
+    }
+
+    public String getOwnerPackageName() {
+        return mOwnerPackageName;
+    }
+
     boolean hasAccess(int callingUid, String callingPackageName) {
-        return ownerUid == callingUid && ownerPackageName.equals(callingPackageName);
+        return mOwnerUid == callingUid && mOwnerPackageName.equals(callingPackageName);
     }
 
     void open() {
@@ -155,6 +175,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 +264,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 +302,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: "
@@ -357,12 +381,12 @@
     void verifyBlobData() {
         byte[] actualDigest = null;
         try {
-            actualDigest = FileUtils.digest(getSessionFile(), blobHandle.algorithm);
+            actualDigest = FileUtils.digest(getSessionFile(), mBlobHandle.algorithm);
         } catch (IOException | NoSuchAlgorithmException e) {
             Slog.e(TAG, "Error computing the digest", e);
         }
         synchronized (mSessionLock) {
-            if (actualDigest != null && Arrays.equals(actualDigest, blobHandle.digest)) {
+            if (actualDigest != null && Arrays.equals(actualDigest, mBlobHandle.digest)) {
                 mState = STATE_VERIFIED_VALID;
                 // Commit callback will be sent once the data is persisted.
             } else {
@@ -401,7 +425,7 @@
     @Nullable
     File getSessionFile() {
         if (mSessionFile == null) {
-            mSessionFile = BlobStoreConfig.prepareBlobFile(sessionId);
+            mSessionFile = BlobStoreConfig.prepareBlobFile(mSessionId);
         }
         return mSessionFile;
     }
@@ -425,20 +449,20 @@
 
     private void assertCallerIsOwner() {
         final int callingUid = Binder.getCallingUid();
-        if (callingUid != ownerUid) {
-            throw new SecurityException(ownerUid + " is not the session owner");
+        if (callingUid != mOwnerUid) {
+            throw new SecurityException(mOwnerUid + " is not the session owner");
         }
     }
 
-    void dump(IndentingPrintWriter fout) {
+    void dump(IndentingPrintWriter fout, DumpArgs dumpArgs) {
         synchronized (mSessionLock) {
             fout.println("state: " + stateToString(mState));
-            fout.println("ownerUid: " + ownerUid);
-            fout.println("ownerPkg: " + ownerPackageName);
+            fout.println("ownerUid: " + mOwnerUid);
+            fout.println("ownerPkg: " + mOwnerPackageName);
 
             fout.println("blobHandle:");
             fout.increaseIndent();
-            blobHandle.dump(fout);
+            mBlobHandle.dump(fout, dumpArgs.shouldDumpFull());
             fout.decreaseIndent();
 
             fout.println("accessMode:");
@@ -452,12 +476,12 @@
 
     void writeToXml(@NonNull XmlSerializer out) throws IOException {
         synchronized (mSessionLock) {
-            XmlUtils.writeLongAttribute(out, ATTR_ID, sessionId);
-            XmlUtils.writeStringAttribute(out, ATTR_PACKAGE, ownerPackageName);
-            XmlUtils.writeIntAttribute(out, ATTR_UID, ownerUid);
+            XmlUtils.writeLongAttribute(out, ATTR_ID, mSessionId);
+            XmlUtils.writeStringAttribute(out, ATTR_PACKAGE, mOwnerPackageName);
+            XmlUtils.writeIntAttribute(out, ATTR_UID, mOwnerUid);
 
             out.startTag(null, TAG_BLOB_HANDLE);
-            blobHandle.writeToXml(out);
+            mBlobHandle.writeToXml(out);
             out.endTag(null, TAG_BLOB_HANDLE);
 
             out.startTag(null, TAG_ACCESS_MODE);
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/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..5ac58d0 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -2875,9 +2875,17 @@
     method public boolean takeScreenshot(int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.graphics.Bitmap>);
     field public static final int GESTURE_2_FINGER_DOUBLE_TAP = 20; // 0x14
     field public static final int GESTURE_2_FINGER_SINGLE_TAP = 19; // 0x13
+    field public static final int GESTURE_2_FINGER_SWIPE_DOWN = 26; // 0x1a
+    field public static final int GESTURE_2_FINGER_SWIPE_LEFT = 27; // 0x1b
+    field public static final int GESTURE_2_FINGER_SWIPE_RIGHT = 28; // 0x1c
+    field public static final int GESTURE_2_FINGER_SWIPE_UP = 25; // 0x19
     field public static final int GESTURE_2_FINGER_TRIPLE_TAP = 21; // 0x15
     field public static final int GESTURE_3_FINGER_DOUBLE_TAP = 23; // 0x17
     field public static final int GESTURE_3_FINGER_SINGLE_TAP = 22; // 0x16
+    field public static final int GESTURE_3_FINGER_SWIPE_DOWN = 30; // 0x1e
+    field public static final int GESTURE_3_FINGER_SWIPE_LEFT = 31; // 0x1f
+    field public static final int GESTURE_3_FINGER_SWIPE_RIGHT = 32; // 0x20
+    field public static final int GESTURE_3_FINGER_SWIPE_UP = 29; // 0x1d
     field public static final int GESTURE_3_FINGER_TRIPLE_TAP = 24; // 0x18
     field public static final int GESTURE_DOUBLE_TAP = 17; // 0x11
     field public static final int GESTURE_DOUBLE_TAP_AND_HOLD = 18; // 0x12
@@ -8631,7 +8639,7 @@
     field public static final String ACTION_ACL_CONNECTED = "android.bluetooth.device.action.ACL_CONNECTED";
     field public static final String ACTION_ACL_DISCONNECTED = "android.bluetooth.device.action.ACL_DISCONNECTED";
     field public static final String ACTION_ACL_DISCONNECT_REQUESTED = "android.bluetooth.device.action.ACL_DISCONNECT_REQUESTED";
-    field public static final String ACTION_ALIAS_CHANGED = "android.bluetooth.action.ALIAS_CHANGED";
+    field public static final String ACTION_ALIAS_CHANGED = "android.bluetooth.device.action.ALIAS_CHANGED";
     field public static final String ACTION_BOND_STATE_CHANGED = "android.bluetooth.device.action.BOND_STATE_CHANGED";
     field public static final String ACTION_CLASS_CHANGED = "android.bluetooth.device.action.CLASS_CHANGED";
     field public static final String ACTION_FOUND = "android.bluetooth.device.action.FOUND";
@@ -26729,6 +26737,7 @@
     method public int getVolume();
     method public int getVolumeHandling();
     method public int getVolumeMax();
+    method public boolean isSystemRoute();
     method public void writeToParcel(@NonNull android.os.Parcel, int);
     field public static final int CONNECTION_STATE_CONNECTED = 2; // 0x2
     field public static final int CONNECTION_STATE_CONNECTING = 1; // 0x1
@@ -31129,11 +31138,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 +31620,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 +31801,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 +36949,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 +46386,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
@@ -54811,7 +54815,6 @@
     method public final void removeOnFrameMetricsAvailableListener(android.view.Window.OnFrameMetricsAvailableListener);
     method public boolean requestFeature(int);
     method @NonNull public final <T extends android.view.View> T requireViewById(@IdRes int);
-    method public void resetOnContentApplyWindowInsetsListener();
     method public abstract void restoreHierarchyState(android.os.Bundle);
     method public abstract android.os.Bundle saveHierarchyState();
     method public void setAllowEnterTransitionOverlap(boolean);
@@ -54829,6 +54832,7 @@
     method public abstract void setContentView(android.view.View);
     method public abstract void setContentView(android.view.View, android.view.ViewGroup.LayoutParams);
     method public abstract void setDecorCaptionShade(int);
+    method public void setDecorFitsSystemWindows(boolean);
     method protected void setDefaultWindowFormat(int);
     method public void setDimAmount(float);
     method public void setElevation(float);
@@ -54850,7 +54854,6 @@
     method public abstract void setNavigationBarColor(@ColorInt int);
     method public void setNavigationBarContrastEnforced(boolean);
     method public void setNavigationBarDividerColor(@ColorInt int);
-    method public void setOnContentApplyWindowInsetsListener(@Nullable android.view.Window.OnContentApplyWindowInsetsListener);
     method public void setPreferMinimalPostProcessing(boolean);
     method public void setReenterTransition(android.transition.Transition);
     method public abstract void setResizingCaptionDrawable(android.graphics.drawable.Drawable);
@@ -54945,10 +54948,6 @@
     method @Nullable public android.view.ActionMode onWindowStartingActionMode(android.view.ActionMode.Callback, int);
   }
 
-  public static interface Window.OnContentApplyWindowInsetsListener {
-    method @NonNull public android.util.Pair<android.graphics.Insets,android.view.WindowInsets> onContentApplyWindowInsets(@NonNull android.view.WindowInsets);
-  }
-
   public static interface Window.OnFrameMetricsAvailableListener {
     method public void onFrameMetricsAvailable(android.view.Window, android.view.FrameMetrics, int);
   }
@@ -55099,16 +55098,13 @@
   }
 
   public interface WindowInsetsController {
-    method public default void controlInputMethodAnimation(long, @Nullable android.view.animation.Interpolator, @NonNull android.view.WindowInsetsAnimationControlListener);
     method public void controlWindowInsetsAnimation(int, long, @Nullable android.view.animation.Interpolator, @NonNull android.view.WindowInsetsAnimationControlListener);
     method public int getSystemBarsAppearance();
     method public int getSystemBarsBehavior();
     method public void hide(int);
-    method public default void hideInputMethod();
     method public void setSystemBarsAppearance(int, int);
     method public void setSystemBarsBehavior(int);
     method public void show(int);
-    method public default void showInputMethod();
     field public static final int APPEARANCE_LIGHT_NAVIGATION_BARS = 16; // 0x10
     field public static final int APPEARANCE_LIGHT_STATUS_BARS = 8; // 0x8
     field public static final int BEHAVIOR_SHOW_BARS_BY_SWIPE = 1; // 0x1
diff --git a/api/system-current.txt b/api/system-current.txt
index 0930f68..ed9f134 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1482,8 +1482,9 @@
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public long getDiscoveryEndMillis();
     method public boolean isBleScanAlwaysAvailable();
     method public boolean isLeEnabled();
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean removeActiveDevice(int);
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean removeOnMetadataChangedListener(@NonNull android.bluetooth.BluetoothDevice, @NonNull android.bluetooth.BluetoothAdapter.OnMetadataChangedListener);
-    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean setActiveDevice(@Nullable android.bluetooth.BluetoothDevice, int);
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setActiveDevice(@NonNull android.bluetooth.BluetoothDevice, int);
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public boolean setScanMode(int, int);
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public boolean setScanMode(int);
     field public static final String ACTION_BLE_STATE_CHANGED = "android.bluetooth.adapter.action.BLE_STATE_CHANGED";
@@ -1556,7 +1557,7 @@
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public boolean isEncrypted();
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean isInSilenceMode();
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean removeBond();
-    method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public boolean setAlias(@NonNull String);
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setAlias(@NonNull String);
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setMessageAccessPermission(int);
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setMetadata(int, @NonNull byte[]);
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public boolean setPhonebookAccessPermission(int);
@@ -1604,7 +1605,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 {
@@ -4104,7 +4105,6 @@
   public class Location implements android.os.Parcelable {
     method public boolean isComplete();
     method public void makeComplete();
-    method public void setExtraLocation(@Nullable String, @Nullable android.location.Location);
     method public void setIsFromMockProvider(boolean);
     field public static final String EXTRA_NO_GPS_LOCATION = "noGPSLocation";
   }
@@ -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();
@@ -10062,6 +10107,7 @@
     ctor public AugmentedAutofillService();
     method protected final void dump(java.io.FileDescriptor, java.io.PrintWriter, String[]);
     method protected void dump(@NonNull java.io.PrintWriter, @NonNull String[]);
+    method @Nullable public final android.service.autofill.FillEventHistory getFillEventHistory();
     method public void onConnected();
     method public void onDisconnected();
     method public void onFillRequest(@NonNull android.service.autofill.augmented.FillRequest, @NonNull android.os.CancellationSignal, @NonNull android.service.autofill.augmented.FillController, @NonNull android.service.autofill.augmented.FillCallback);
@@ -10091,6 +10137,7 @@
   public static final class FillResponse.Builder {
     ctor public FillResponse.Builder();
     method @NonNull public android.service.autofill.augmented.FillResponse build();
+    method @NonNull public android.service.autofill.augmented.FillResponse.Builder setClientState(@Nullable android.os.Bundle);
     method @NonNull public android.service.autofill.augmented.FillResponse.Builder setFillWindow(@Nullable android.service.autofill.augmented.FillWindow);
     method @NonNull public android.service.autofill.augmented.FillResponse.Builder setInlineSuggestions(@Nullable java.util.List<android.service.autofill.Dataset>);
   }
diff --git a/api/test-current.txt b/api/test-current.txt
index e0b5b34..fda3ab4 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -1271,7 +1271,6 @@
 
   public class Location implements android.os.Parcelable {
     method public void makeComplete();
-    method public void setExtraLocation(@Nullable String, @Nullable android.location.Location);
     field public static final String EXTRA_NO_GPS_LOCATION = "noGPSLocation";
   }
 
@@ -1387,6 +1386,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 +1691,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";
   }
@@ -3052,6 +3104,7 @@
     ctor public AugmentedAutofillService();
     method protected final void dump(java.io.FileDescriptor, java.io.PrintWriter, String[]);
     method protected void dump(@NonNull java.io.PrintWriter, @NonNull String[]);
+    method @Nullable public final android.service.autofill.FillEventHistory getFillEventHistory();
     method public void onConnected();
     method public void onDisconnected();
     method public void onFillRequest(@NonNull android.service.autofill.augmented.FillRequest, @NonNull android.os.CancellationSignal, @NonNull android.service.autofill.augmented.FillController, @NonNull android.service.autofill.augmented.FillCallback);
@@ -3081,6 +3134,7 @@
   public static final class FillResponse.Builder {
     ctor public FillResponse.Builder();
     method @NonNull public android.service.autofill.augmented.FillResponse build();
+    method @NonNull public android.service.autofill.augmented.FillResponse.Builder setClientState(@Nullable android.os.Bundle);
     method @NonNull public android.service.autofill.augmented.FillResponse.Builder setFillWindow(@Nullable android.service.autofill.augmented.FillWindow);
     method @NonNull public android.service.autofill.augmented.FillResponse.Builder setInlineSuggestions(@Nullable java.util.List<android.service.autofill.Dataset>);
   }
diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp
index fdc7fce..bd4397a 100644
--- a/cmds/statsd/Android.bp
+++ b/cmds/statsd/Android.bp
@@ -66,13 +66,11 @@
         "src/config/ConfigManager.cpp",
         "src/external/GpuStatsPuller.cpp",
         "src/external/Perfetto.cpp",
-        "src/external/PowerStatsPuller.cpp",
         "src/external/PullResultReceiver.cpp",
         "src/external/puller_util.cpp",
         "src/external/StatsCallbackPuller.cpp",
         "src/external/StatsPuller.cpp",
         "src/external/StatsPullerManager.cpp",
-        "src/external/SubsystemSleepStatePuller.cpp",
         "src/external/TrainInfoPuller.cpp",
         "src/FieldValue.cpp",
         "src/guardrail/StatsdStats.cpp",
@@ -121,9 +119,6 @@
 
     static_libs: [
         "android.frameworks.stats@1.0",
-        "android.hardware.power.stats@1.0",
-        "android.hardware.power@1.0",
-        "android.hardware.power@1.1",
         "libbase",
         "libcutils",
         "liblog",
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/external/PowerStatsPuller.cpp b/cmds/statsd/src/external/PowerStatsPuller.cpp
deleted file mode 100644
index dc69b78..0000000
--- a/cmds/statsd/src/external/PowerStatsPuller.cpp
+++ /dev/null
@@ -1,170 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define DEBUG false  // STOPSHIP if true
-#include "Log.h"
-
-#include <android/hardware/power/stats/1.0/IPowerStats.h>
-
-#include <vector>
-
-#include "PowerStatsPuller.h"
-#include "statslog.h"
-#include "stats_log_util.h"
-
-using android::hardware::hidl_vec;
-using android::hardware::power::stats::V1_0::IPowerStats;
-using android::hardware::power::stats::V1_0::EnergyData;
-using android::hardware::power::stats::V1_0::RailInfo;
-using android::hardware::power::stats::V1_0::Status;
-using android::hardware::Return;
-using android::hardware::Void;
-
-using std::make_shared;
-using std::shared_ptr;
-
-namespace android {
-namespace os {
-namespace statsd {
-
-static sp<android::hardware::power::stats::V1_0::IPowerStats> gPowerStatsHal = nullptr;
-static std::mutex gPowerStatsHalMutex;
-static bool gPowerStatsExist = true; // Initialized to ensure making a first attempt.
-static std::vector<RailInfo> gRailInfo;
-
-struct PowerStatsPullerDeathRecipient : virtual public hardware::hidl_death_recipient {
-    virtual void serviceDied(uint64_t cookie,
-            const wp<android::hidl::base::V1_0::IBase>& who) override {
-        // The HAL just died. Reset all handles to HAL services.
-        std::lock_guard<std::mutex> lock(gPowerStatsHalMutex);
-        gPowerStatsHal = nullptr;
-    }
-};
-
-static sp<PowerStatsPullerDeathRecipient> gDeathRecipient = new PowerStatsPullerDeathRecipient();
-
-static bool getPowerStatsHalLocked() {
-    if (gPowerStatsHal == nullptr && gPowerStatsExist) {
-        gPowerStatsHal = android::hardware::power::stats::V1_0::IPowerStats::getService();
-        if (gPowerStatsHal == nullptr) {
-            ALOGW("Couldn't load power.stats HAL service");
-            gPowerStatsExist = false;
-        } else {
-            // Link death recipient to power.stats service handle
-            hardware::Return<bool> linked = gPowerStatsHal->linkToDeath(gDeathRecipient, 0);
-            if (!linked.isOk()) {
-                ALOGE("Transaction error in linking to power.stats HAL death: %s",
-                        linked.description().c_str());
-                gPowerStatsHal = nullptr;
-                return false;
-            } else if (!linked) {
-                ALOGW("Unable to link to power.stats HAL death notifications");
-                // We should still continue even though linking failed
-            }
-        }
-    }
-    return gPowerStatsHal != nullptr;
-}
-
-PowerStatsPuller::PowerStatsPuller() : StatsPuller(android::util::ON_DEVICE_POWER_MEASUREMENT) {
-}
-
-bool PowerStatsPuller::PullInternal(vector<shared_ptr<LogEvent>>* data) {
-    std::lock_guard<std::mutex> lock(gPowerStatsHalMutex);
-
-    if (!getPowerStatsHalLocked()) {
-        return false;
-    }
-
-    int64_t wallClockTimestampNs = getWallClockNs();
-    int64_t elapsedTimestampNs = getElapsedRealtimeNs();
-
-    data->clear();
-
-    // Pull getRailInfo if necessary
-    if (gRailInfo.empty()) {
-        bool resultSuccess = true;
-        Return<void> ret = gPowerStatsHal->getRailInfo(
-                [&resultSuccess](const hidl_vec<RailInfo> &list, Status status) {
-                    resultSuccess = (status == Status::SUCCESS || status == Status::NOT_SUPPORTED);
-                    if (status != Status::SUCCESS) return;
-
-                    gRailInfo.reserve(list.size());
-                    for (size_t i = 0; i < list.size(); ++i) {
-                        gRailInfo.push_back(list[i]);
-                    }
-                });
-        if (!resultSuccess || !ret.isOk()) {
-            ALOGE("power.stats getRailInfo() failed. Description: %s", ret.description().c_str());
-            gPowerStatsHal = nullptr;
-            return false;
-        }
-        // If SUCCESS but empty, or if NOT_SUPPORTED, then never try again.
-        if (gRailInfo.empty()) {
-            ALOGE("power.stats has no rail information");
-            gPowerStatsExist = false; // No rail info, so never try again.
-            gPowerStatsHal = nullptr;
-            return false;
-        }
-    }
-
-    // Pull getEnergyData and write the data out
-    const hidl_vec<uint32_t> desiredRailIndices; // Empty vector indicates we want all.
-    bool resultSuccess = true;
-    Return<void> ret = gPowerStatsHal->getEnergyData(desiredRailIndices,
-                [&data, wallClockTimestampNs, elapsedTimestampNs, &resultSuccess]
-                (hidl_vec<EnergyData> energyDataList, Status status) {
-                    resultSuccess = (status == Status::SUCCESS);
-                    if (!resultSuccess) return;
-
-                    for (size_t i = 0; i < energyDataList.size(); i++) {
-                        const EnergyData& energyData = energyDataList[i];
-
-                        if (energyData.index >= gRailInfo.size()) {
-                            ALOGE("power.stats getEnergyData() returned an invalid rail index %u.",
-                                    energyData.index);
-                            resultSuccess = false;
-                            return;
-                        }
-                        const RailInfo& rail = gRailInfo[energyData.index];
-
-                        auto ptr = make_shared<LogEvent>(android::util::ON_DEVICE_POWER_MEASUREMENT,
-                              wallClockTimestampNs, elapsedTimestampNs);
-                        ptr->write(rail.subsysName);
-                        ptr->write(rail.railName);
-                        ptr->write(energyData.timestamp);
-                        ptr->write(energyData.energy);
-                        ptr->init();
-                        data->push_back(ptr);
-
-                        VLOG("power.stat: %s.%s: %llu, %llu",
-                             rail.subsysName.c_str(),
-                             rail.railName.c_str(),
-                             (unsigned long long)energyData.timestamp,
-                             (unsigned long long)energyData.energy);
-                    }
-                });
-    if (!resultSuccess || !ret.isOk()) {
-        ALOGE("power.stats getEnergyData() failed. Description: %s", ret.description().c_str());
-        gPowerStatsHal = nullptr;
-        return false;
-    }
-    return true;
-}
-
-}  // namespace statsd
-}  // namespace os
-}  // namespace android
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/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp
index 668c11e..fef213d 100644
--- a/cmds/statsd/src/external/StatsPullerManager.cpp
+++ b/cmds/statsd/src/external/StatsPullerManager.cpp
@@ -33,9 +33,7 @@
 #include "../stats_log_util.h"
 #include "../statscompanion_util.h"
 #include "GpuStatsPuller.h"
-#include "PowerStatsPuller.h"
 #include "StatsCallbackPuller.h"
-#include "SubsystemSleepStatePuller.h"
 #include "TrainInfoPuller.h"
 #include "statslog.h"
 
@@ -55,12 +53,6 @@
 
 StatsPullerManager::StatsPullerManager()
     : kAllPullAtomInfo({
-              // subsystem_sleep_state
-              {{.atomTag = android::util::SUBSYSTEM_SLEEP_STATE}, new SubsystemSleepStatePuller()},
-
-              // on_device_power_measurement
-              {{.atomTag = android::util::ON_DEVICE_POWER_MEASUREMENT}, new PowerStatsPuller()},
-
               // TrainInfo.
               {{.atomTag = android::util::TRAIN_INFO}, new TrainInfoPuller()},
 
diff --git a/cmds/statsd/src/external/SubsystemSleepStatePuller.cpp b/cmds/statsd/src/external/SubsystemSleepStatePuller.cpp
deleted file mode 100644
index f6a4aea..0000000
--- a/cmds/statsd/src/external/SubsystemSleepStatePuller.cpp
+++ /dev/null
@@ -1,378 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define DEBUG false  // STOPSHIP if true
-#include "Log.h"
-
-#include <android/hardware/power/1.0/IPower.h>
-#include <android/hardware/power/1.1/IPower.h>
-#include <android/hardware/power/stats/1.0/IPowerStats.h>
-
-#include <fcntl.h>
-#include <hardware/power.h>
-#include <hardware_legacy/power.h>
-#include <inttypes.h>
-#include <semaphore.h>
-#include <stddef.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include "external/SubsystemSleepStatePuller.h"
-#include "external/StatsPuller.h"
-
-#include "SubsystemSleepStatePuller.h"
-#include "logd/LogEvent.h"
-#include "statslog.h"
-#include "stats_log_util.h"
-
-using android::hardware::hidl_vec;
-using android::hardware::power::V1_0::IPower;
-using android::hardware::power::V1_0::PowerStatePlatformSleepState;
-using android::hardware::power::V1_0::PowerStateVoter;
-using android::hardware::power::V1_1::PowerStateSubsystem;
-using android::hardware::power::V1_1::PowerStateSubsystemSleepState;
-using android::hardware::power::stats::V1_0::PowerEntityInfo;
-using android::hardware::power::stats::V1_0::PowerEntityStateResidencyResult;
-using android::hardware::power::stats::V1_0::PowerEntityStateSpace;
-
-using android::hardware::Return;
-using android::hardware::Void;
-
-using std::make_shared;
-using std::shared_ptr;
-
-namespace android {
-namespace os {
-namespace statsd {
-
-static std::function<bool(vector<shared_ptr<LogEvent>>* data)> gPuller = {};
-
-static sp<android::hardware::power::V1_0::IPower> gPowerHalV1_0 = nullptr;
-static sp<android::hardware::power::V1_1::IPower> gPowerHalV1_1 = nullptr;
-static sp<android::hardware::power::stats::V1_0::IPowerStats> gPowerStatsHalV1_0 = nullptr;
-
-static std::unordered_map<uint32_t, std::string> gEntityNames = {};
-static std::unordered_map<uint32_t, std::unordered_map<uint32_t, std::string>> gStateNames = {};
-
-static std::mutex gPowerHalMutex;
-
-// The caller must be holding gPowerHalMutex.
-static void deinitPowerStatsLocked() {
-    gPowerHalV1_0 = nullptr;
-    gPowerHalV1_1 = nullptr;
-    gPowerStatsHalV1_0 = nullptr;
-}
-
-struct SubsystemSleepStatePullerDeathRecipient : virtual public hardware::hidl_death_recipient {
-    virtual void serviceDied(uint64_t cookie,
-            const wp<android::hidl::base::V1_0::IBase>& who) override {
-
-        // The HAL just died. Reset all handles to HAL services.
-        std::lock_guard<std::mutex> lock(gPowerHalMutex);
-        deinitPowerStatsLocked();
-    }
-};
-
-static sp<SubsystemSleepStatePullerDeathRecipient> gDeathRecipient =
-        new SubsystemSleepStatePullerDeathRecipient();
-
-SubsystemSleepStatePuller::SubsystemSleepStatePuller() :
-    StatsPuller(android::util::SUBSYSTEM_SLEEP_STATE) {
-}
-
-// The caller must be holding gPowerHalMutex.
-static bool checkResultLocked(const Return<void> &ret, const char* function) {
-    if (!ret.isOk()) {
-        ALOGE("%s failed: requested HAL service not available. Description: %s",
-            function, ret.description().c_str());
-        if (ret.isDeadObject()) {
-            deinitPowerStatsLocked();
-        }
-        return false;
-    }
-    return true;
-}
-
-// The caller must be holding gPowerHalMutex.
-// gPowerStatsHalV1_0 must not be null
-static bool initializePowerStats() {
-    using android::hardware::power::stats::V1_0::Status;
-
-    // Clear out previous content if we are re-initializing
-    gEntityNames.clear();
-    gStateNames.clear();
-
-    Return<void> ret;
-    ret = gPowerStatsHalV1_0->getPowerEntityInfo([](auto infos, auto status) {
-        if (status != Status::SUCCESS) {
-            ALOGE("Error getting power entity info");
-            return;
-        }
-
-        // construct lookup table of powerEntityId to power entity name
-        for (auto info : infos) {
-            gEntityNames.emplace(info.powerEntityId, info.powerEntityName);
-        }
-    });
-    if (!checkResultLocked(ret, __func__)) {
-        return false;
-    }
-
-    ret = gPowerStatsHalV1_0->getPowerEntityStateInfo({}, [](auto stateSpaces, auto status) {
-        if (status != Status::SUCCESS) {
-            ALOGE("Error getting state info");
-            return;
-        }
-
-        // construct lookup table of powerEntityId, powerEntityStateId to power entity state name
-        for (auto stateSpace : stateSpaces) {
-            std::unordered_map<uint32_t, std::string> stateNames = {};
-            for (auto state : stateSpace.states) {
-                stateNames.emplace(state.powerEntityStateId,
-                    state.powerEntityStateName);
-            }
-            gStateNames.emplace(stateSpace.powerEntityId, stateNames);
-        }
-    });
-    if (!checkResultLocked(ret, __func__)) {
-        return false;
-    }
-
-    return (!gEntityNames.empty()) && (!gStateNames.empty());
-}
-
-// The caller must be holding gPowerHalMutex.
-static bool getPowerStatsHalLocked() {
-    if(gPowerStatsHalV1_0 == nullptr) {
-        gPowerStatsHalV1_0 = android::hardware::power::stats::V1_0::IPowerStats::getService();
-        if (gPowerStatsHalV1_0 == nullptr) {
-            ALOGE("Unable to get power.stats HAL service.");
-            return false;
-        }
-
-        // Link death recipient to power.stats service handle
-        hardware::Return<bool> linked = gPowerStatsHalV1_0->linkToDeath(gDeathRecipient, 0);
-        if (!linked.isOk()) {
-            ALOGE("Transaction error in linking to power.stats HAL death: %s",
-                    linked.description().c_str());
-            deinitPowerStatsLocked();
-            return false;
-        } else if (!linked) {
-            ALOGW("Unable to link to power.stats HAL death notifications");
-            // We should still continue even though linking failed
-        }
-        return initializePowerStats();
-    }
-    return true;
-}
-
-// The caller must be holding gPowerHalMutex.
-static bool getIPowerStatsDataLocked(vector<shared_ptr<LogEvent>>* data) {
-    using android::hardware::power::stats::V1_0::Status;
-
-    if(!getPowerStatsHalLocked()) {
-        return false;
-    }
-
-    int64_t wallClockTimestampNs = getWallClockNs();
-    int64_t elapsedTimestampNs = getElapsedRealtimeNs();
-
-    // Get power entity state residency data
-    bool success = false;
-    Return<void> ret = gPowerStatsHalV1_0->getPowerEntityStateResidencyData({},
-        [&data, &success, wallClockTimestampNs, elapsedTimestampNs]
-        (auto results, auto status) {
-        if (status == Status::NOT_SUPPORTED) {
-            ALOGW("getPowerEntityStateResidencyData is not supported");
-            success = false;
-            return;
-        }
-
-        for(auto result : results) {
-            for(auto stateResidency : result.stateResidencyData) {
-                auto statePtr = make_shared<LogEvent>(
-                        android::util::SUBSYSTEM_SLEEP_STATE,
-                        wallClockTimestampNs, elapsedTimestampNs);
-                statePtr->write(gEntityNames.at(result.powerEntityId));
-                statePtr->write(gStateNames.at(result.powerEntityId)
-                    .at(stateResidency.powerEntityStateId));
-                statePtr->write(stateResidency.totalStateEntryCount);
-                statePtr->write(stateResidency.totalTimeInStateMs);
-                statePtr->init();
-                data->emplace_back(statePtr);
-            }
-        }
-        success = true;
-    });
-    // Intentionally not returning early here.
-    // bool success determines if this succeeded or not.
-    checkResultLocked(ret, __func__);
-
-    return success;
-}
-
-// The caller must be holding gPowerHalMutex.
-static bool getPowerHalLocked() {
-    if(gPowerHalV1_0 == nullptr) {
-        gPowerHalV1_0 = android::hardware::power::V1_0::IPower::getService();
-        if(gPowerHalV1_0 == nullptr) {
-            ALOGE("Unable to get power HAL service.");
-            return false;
-        }
-        gPowerHalV1_1 = android::hardware::power::V1_1::IPower::castFrom(gPowerHalV1_0);
-
-        // Link death recipient to power service handle
-        hardware::Return<bool> linked = gPowerHalV1_0->linkToDeath(gDeathRecipient, 0);
-        if (!linked.isOk()) {
-            ALOGE("Transaction error in linking to power HAL death: %s",
-                    linked.description().c_str());
-            gPowerHalV1_0 = nullptr;
-            return false;
-        } else if (!linked) {
-            ALOGW("Unable to link to power. death notifications");
-            // We should still continue even though linking failed
-        }
-    }
-    return true;
-}
-
-// The caller must be holding gPowerHalMutex.
-static bool getIPowerDataLocked(vector<shared_ptr<LogEvent>>* data) {
-    using android::hardware::power::V1_0::Status;
-
-    if(!getPowerHalLocked()) {
-        return false;
-    }
-
-    int64_t wallClockTimestampNs = getWallClockNs();
-    int64_t elapsedTimestampNs = getElapsedRealtimeNs();
-        Return<void> ret;
-        ret = gPowerHalV1_0->getPlatformLowPowerStats(
-                [&data, wallClockTimestampNs, elapsedTimestampNs]
-                    (hidl_vec<PowerStatePlatformSleepState> states, Status status) {
-                    if (status != Status::SUCCESS) return;
-
-                    for (size_t i = 0; i < states.size(); i++) {
-                        const PowerStatePlatformSleepState& state = states[i];
-
-                        auto statePtr = make_shared<LogEvent>(
-                            android::util::SUBSYSTEM_SLEEP_STATE,
-                            wallClockTimestampNs, elapsedTimestampNs);
-                        statePtr->write(state.name);
-                        statePtr->write("");
-                        statePtr->write(state.totalTransitions);
-                        statePtr->write(state.residencyInMsecSinceBoot);
-                        statePtr->init();
-                        data->push_back(statePtr);
-                        VLOG("powerstate: %s, %lld, %lld, %d", state.name.c_str(),
-                             (long long)state.residencyInMsecSinceBoot,
-                             (long long)state.totalTransitions,
-                             state.supportedOnlyInSuspend ? 1 : 0);
-                        for (const auto& voter : state.voters) {
-                            auto voterPtr = make_shared<LogEvent>(
-                                android::util::SUBSYSTEM_SLEEP_STATE,
-                                wallClockTimestampNs, elapsedTimestampNs);
-                            voterPtr->write(state.name);
-                            voterPtr->write(voter.name);
-                            voterPtr->write(voter.totalNumberOfTimesVotedSinceBoot);
-                            voterPtr->write(voter.totalTimeInMsecVotedForSinceBoot);
-                            voterPtr->init();
-                            data->push_back(voterPtr);
-                            VLOG("powerstatevoter: %s, %s, %lld, %lld", state.name.c_str(),
-                                 voter.name.c_str(),
-                                 (long long)voter.totalTimeInMsecVotedForSinceBoot,
-                                 (long long)voter.totalNumberOfTimesVotedSinceBoot);
-                        }
-                    }
-                });
-        if (!checkResultLocked(ret, __func__)) {
-            return false;
-        }
-
-        // Trying to cast to IPower 1.1, this will succeed only for devices supporting 1.1
-        sp<android::hardware::power::V1_1::IPower> gPowerHal_1_1 =
-                android::hardware::power::V1_1::IPower::castFrom(gPowerHalV1_0);
-        if (gPowerHal_1_1 != nullptr) {
-            ret = gPowerHal_1_1->getSubsystemLowPowerStats(
-            [&data, wallClockTimestampNs, elapsedTimestampNs]
-            (hidl_vec<PowerStateSubsystem> subsystems, Status status) {
-                if (status != Status::SUCCESS) return;
-
-                if (subsystems.size() > 0) {
-                    for (size_t i = 0; i < subsystems.size(); i++) {
-                        const PowerStateSubsystem& subsystem = subsystems[i];
-                        for (size_t j = 0; j < subsystem.states.size(); j++) {
-                            const PowerStateSubsystemSleepState& state =
-                                    subsystem.states[j];
-                            auto subsystemStatePtr = make_shared<LogEvent>(
-                                android::util::SUBSYSTEM_SLEEP_STATE,
-                                wallClockTimestampNs, elapsedTimestampNs);
-                            subsystemStatePtr->write(subsystem.name);
-                            subsystemStatePtr->write(state.name);
-                            subsystemStatePtr->write(state.totalTransitions);
-                            subsystemStatePtr->write(state.residencyInMsecSinceBoot);
-                            subsystemStatePtr->init();
-                            data->push_back(subsystemStatePtr);
-                            VLOG("subsystemstate: %s, %s, %lld, %lld, %lld",
-                                 subsystem.name.c_str(), state.name.c_str(),
-                                 (long long)state.residencyInMsecSinceBoot,
-                                 (long long)state.totalTransitions,
-                                 (long long)state.lastEntryTimestampMs);
-                        }
-                    }
-                }
-            });
-        }
-        return true;
-}
-
-// The caller must be holding gPowerHalMutex.
-std::function<bool(vector<shared_ptr<LogEvent>>* data)> getPullerLocked() {
-    std::function<bool(vector<shared_ptr<LogEvent>>* data)> ret = {};
-
-    // First see if power.stats HAL is available. Fall back to power HAL if
-    // power.stats HAL is unavailable.
-    if(android::hardware::power::stats::V1_0::IPowerStats::getService() != nullptr) {
-        ALOGI("Using power.stats HAL");
-        ret = getIPowerStatsDataLocked;
-    } else if(android::hardware::power::V1_0::IPower::getService() != nullptr) {
-        ALOGI("Using power HAL");
-        ret = getIPowerDataLocked;
-    }
-
-    return ret;
-}
-
-bool SubsystemSleepStatePuller::PullInternal(vector<shared_ptr<LogEvent>>* data) {
-    std::lock_guard<std::mutex> lock(gPowerHalMutex);
-
-    if(!gPuller) {
-        gPuller = getPullerLocked();
-    }
-
-    if(gPuller) {
-        return gPuller(data);
-    }
-
-    ALOGE("Unable to load Power Hal or power.stats HAL");
-    return false;
-}
-
-}  // namespace statsd
-}  // namespace os
-}  // namespace android
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/accessibilityservice/AccessibilityGestureEvent.java b/core/java/android/accessibilityservice/AccessibilityGestureEvent.java
index 47fc7e1..9cf1de9 100644
--- a/core/java/android/accessibilityservice/AccessibilityGestureEvent.java
+++ b/core/java/android/accessibilityservice/AccessibilityGestureEvent.java
@@ -19,9 +19,17 @@
 
 import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_DOUBLE_TAP;
 import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SINGLE_TAP;
+import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_DOWN;
+import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_LEFT;
+import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_RIGHT;
+import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_UP;
 import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_TRIPLE_TAP;
 import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_DOUBLE_TAP;
 import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SINGLE_TAP;
+import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SWIPE_DOWN;
+import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SWIPE_LEFT;
+import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SWIPE_RIGHT;
+import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SWIPE_UP;
 import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_TRIPLE_TAP;
 import static android.accessibilityservice.AccessibilityService.GESTURE_DOUBLE_TAP;
 import static android.accessibilityservice.AccessibilityService.GESTURE_DOUBLE_TAP_AND_HOLD;
@@ -89,7 +97,15 @@
             GESTURE_SWIPE_RIGHT,
             GESTURE_SWIPE_RIGHT_AND_UP,
             GESTURE_SWIPE_RIGHT_AND_LEFT,
-            GESTURE_SWIPE_RIGHT_AND_DOWN
+            GESTURE_SWIPE_RIGHT_AND_DOWN,
+            GESTURE_2_FINGER_SWIPE_DOWN,
+            GESTURE_2_FINGER_SWIPE_LEFT,
+            GESTURE_2_FINGER_SWIPE_RIGHT,
+            GESTURE_2_FINGER_SWIPE_UP,
+            GESTURE_3_FINGER_SWIPE_DOWN,
+            GESTURE_3_FINGER_SWIPE_LEFT,
+            GESTURE_3_FINGER_SWIPE_RIGHT,
+            GESTURE_3_FINGER_SWIPE_UP
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface GestureId {}
@@ -167,6 +183,14 @@
             case GESTURE_SWIPE_UP_AND_LEFT: return "GESTURE_SWIPE_UP_AND_LEFT";
             case GESTURE_SWIPE_UP_AND_DOWN: return "GESTURE_SWIPE_UP_AND_DOWN";
             case GESTURE_SWIPE_UP_AND_RIGHT: return "GESTURE_SWIPE_UP_AND_RIGHT";
+            case GESTURE_2_FINGER_SWIPE_DOWN: return "GESTURE_2_FINGER_SWIPE_DOWN";
+            case GESTURE_2_FINGER_SWIPE_LEFT: return "GESTURE_2_FINGER_SWIPE_LEFT";
+            case GESTURE_2_FINGER_SWIPE_RIGHT: return "GESTURE_2_FINGER_SWIPE_RIGHT";
+            case GESTURE_2_FINGER_SWIPE_UP: return "GESTURE_2_FINGER_SWIPE_UP";
+            case GESTURE_3_FINGER_SWIPE_DOWN: return "GESTURE_3_FINGER_SWIPE_DOWN";
+            case GESTURE_3_FINGER_SWIPE_LEFT: return "GESTURE_3_FINGER_SWIPE_LEFT";
+            case GESTURE_3_FINGER_SWIPE_RIGHT: return "GESTURE_3_FINGER_SWIPE_RIGHT";
+            case GESTURE_3_FINGER_SWIPE_UP: return "GESTURE_3_FINGER_SWIPE_UP";
             default: return Integer.toHexString(eventType);
         }
     }
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 27cd285..2165fb3 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -349,6 +349,46 @@
     public static final int GESTURE_3_FINGER_TRIPLE_TAP = 24;
 
     /**
+     * The user has performed a two-finger swipe up gesture on the touch screen.
+     */
+    public static final int GESTURE_2_FINGER_SWIPE_UP = 25;
+
+    /**
+     * The user has performed a two-finger swipe down gesture on the touch screen.
+     */
+    public static final int GESTURE_2_FINGER_SWIPE_DOWN = 26;
+
+    /**
+     * The user has performed a two-finger swipe left gesture on the touch screen.
+     */
+    public static final int GESTURE_2_FINGER_SWIPE_LEFT = 27;
+
+    /**
+     * The user has performed a two-finger swipe right gesture on the touch screen.
+     */
+    public static final int GESTURE_2_FINGER_SWIPE_RIGHT = 28;
+
+    /**
+     * The user has performed a three-finger swipe up gesture on the touch screen.
+     */
+    public static final int GESTURE_3_FINGER_SWIPE_UP = 29;
+
+    /**
+     * The user has performed a three-finger swipe down gesture on the touch screen.
+     */
+    public static final int GESTURE_3_FINGER_SWIPE_DOWN = 30;
+
+    /**
+     * The user has performed a three-finger swipe left gesture on the touch screen.
+     */
+    public static final int GESTURE_3_FINGER_SWIPE_LEFT = 31;
+
+    /**
+     * The user has performed a three-finger swipe right gesture on the touch screen.
+     */
+    public static final int GESTURE_3_FINGER_SWIPE_RIGHT = 32;
+
+    /**
      * The {@link Intent} that must be declared as handled by the service.
      */
     public static final String SERVICE_INTERFACE =
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/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index c09aa1f..71cb4a4 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -618,36 +618,22 @@
         return hasSystemFeature(name, 0);
     }
 
-    private boolean hasSystemFeatureUncached(String name, int version) {
-        try {
-            return mPM.hasSystemFeature(name, version);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    // Make this cache relatively large.  There are many system features and
-    // none are ever invalidated.  MPTS tests suggests that the cache should
-    // hold at least 150 entries.
-    private static final int SYS_FEATURE_CACHE_SIZE = 256;
-    private static final String CACHE_KEY_SYS_FEATURE_PROPERTY = "cache_key.has_system_feature";
-
-    private class SystemFeatureQuery {
+    private class HasSystemFeatureQuery {
         public final String name;
         public final int version;
-        public SystemFeatureQuery(String n, int v) {
+        public HasSystemFeatureQuery(String n, int v) {
             name = n;
             version = v;
         }
         @Override
         public String toString() {
-            return String.format("SystemFeatureQuery(name=\"%s\", version=%d)",
+            return String.format("HasSystemFeatureQuery(name=\"%s\", version=%d)",
                     name, version);
         }
         @Override
         public boolean equals(Object o) {
-            if (o instanceof SystemFeatureQuery) {
-                SystemFeatureQuery r = (SystemFeatureQuery) o;
+            if (o instanceof HasSystemFeatureQuery) {
+                HasSystemFeatureQuery r = (HasSystemFeatureQuery) o;
                 return Objects.equals(name, r.name) &&  version == r.version;
             } else {
                 return false;
@@ -655,33 +641,41 @@
         }
         @Override
         public int hashCode() {
-            return Objects.hashCode(name) + version;
+            return Objects.hashCode(name) * 13 + version;
         }
     }
 
-    private final PropertyInvalidatedCache<SystemFeatureQuery, Boolean> mSysFeatureCache =
-            new PropertyInvalidatedCache<SystemFeatureQuery, Boolean>(
-                SYS_FEATURE_CACHE_SIZE,
-                CACHE_KEY_SYS_FEATURE_PROPERTY) {
+    // Make this cache relatively large.  There are many system features and
+    // none are ever invalidated.  MPTS tests suggests that the cache should
+    // hold at least 150 entries.
+    private final static PropertyInvalidatedCache<HasSystemFeatureQuery, Boolean>
+            mHasSystemFeatureCache =
+            new PropertyInvalidatedCache<HasSystemFeatureQuery, Boolean>(
+                256, "cache_key.has_system_feature") {
                 @Override
-                protected Boolean recompute(SystemFeatureQuery query) {
-                    return hasSystemFeatureUncached(query.name, query.version);
+                protected Boolean recompute(HasSystemFeatureQuery query) {
+                    try {
+                        return ActivityThread.currentActivityThread().getPackageManager().
+                            hasSystemFeature(query.name, query.version);
+                    } catch (RemoteException e) {
+                        throw e.rethrowFromSystemServer();
+                    }
                 }
             };
 
     @Override
     public boolean hasSystemFeature(String name, int version) {
-        return mSysFeatureCache.query(new SystemFeatureQuery(name, version)).booleanValue();
+        return mHasSystemFeatureCache.query(new HasSystemFeatureQuery(name, version));
     }
 
     /** @hide */
-    public void disableSysFeatureCache() {
-        mSysFeatureCache.disableLocal();
+    public void disableHasSystemFeatureCache() {
+        mHasSystemFeatureCache.disableLocal();
     }
 
     /** @hide */
-    public static void invalidateSysFeatureCache() {
-        PropertyInvalidatedCache.invalidateCache(CACHE_KEY_SYS_FEATURE_PROPERTY);
+    public static void invalidateHasSystemFeatureCache() {
+        mHasSystemFeatureCache.invalidateCache();
     }
 
     @Override
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..b73d311 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -1766,6 +1766,45 @@
     }
 
     /**
+     * Removes the active device for the grouping of @ActiveDeviceUse specified
+     *
+     * @param profiles represents the purpose for which we are setting this as the active device.
+     *                 Possible values are:
+     *                 {@link BluetoothAdapter#ACTIVE_DEVICE_AUDIO},
+     *                 {@link BluetoothAdapter#ACTIVE_DEVICE_PHONE_CALL},
+     *                 {@link BluetoothAdapter#ACTIVE_DEVICE_ALL}
+     * @return false on immediate error, true otherwise
+     * @throws IllegalArgumentException if device is null or profiles is not one of
+     * {@link ActiveDeviceUse}
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+    public boolean removeActiveDevice(@ActiveDeviceUse int profiles) {
+        if (profiles != ACTIVE_DEVICE_AUDIO && profiles != ACTIVE_DEVICE_PHONE_CALL
+                && profiles != ACTIVE_DEVICE_ALL) {
+            Log.e(TAG, "Invalid profiles param value in removeActiveDevice");
+            throw new IllegalArgumentException("Profiles must be one of "
+                    + "BluetoothAdapter.ACTIVE_DEVICE_AUDIO, "
+                    + "BluetoothAdapter.ACTIVE_DEVICE_PHONE_CALL, or "
+                    + "BluetoothAdapter.ACTIVE_DEVICE_ALL");
+        }
+        try {
+            mServiceLock.readLock().lock();
+            if (mService != null) {
+                return mService.removeActiveDevice(profiles);
+            }
+        } catch (RemoteException e) {
+            Log.e(TAG, "", e);
+        } finally {
+            mServiceLock.readLock().unlock();
+        }
+
+        return false;
+    }
+
+    /**
+     * Sets device as the active devices for the profiles passed into the function
      *
      * @param device is the remote bluetooth device
      * @param profiles represents the purpose for which we are setting this as the active device.
@@ -1774,18 +1813,26 @@
      *                 {@link BluetoothAdapter#ACTIVE_DEVICE_PHONE_CALL},
      *                 {@link BluetoothAdapter#ACTIVE_DEVICE_ALL}
      * @return false on immediate error, true otherwise
+     * @throws IllegalArgumentException if device is null or profiles is not one of
+     * {@link ActiveDeviceUse}
      * @hide
      */
     @SystemApi
-    @RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
-    public boolean setActiveDevice(@Nullable BluetoothDevice device,
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+    public boolean setActiveDevice(@NonNull BluetoothDevice device,
             @ActiveDeviceUse int profiles) {
+        if (device == null) {
+            Log.e(TAG, "setActiveDevice: Null device passed as parameter");
+            throw new IllegalArgumentException("device cannot be null");
+        }
         if (profiles != ACTIVE_DEVICE_AUDIO && profiles != ACTIVE_DEVICE_PHONE_CALL
                 && profiles != ACTIVE_DEVICE_ALL) {
             Log.e(TAG, "Invalid profiles param value in setActiveDevice");
-            return false;
+            throw new IllegalArgumentException("Profiles must be one of "
+                    + "BluetoothAdapter.ACTIVE_DEVICE_AUDIO, "
+                    + "BluetoothAdapter.ACTIVE_DEVICE_PHONE_CALL, or "
+                    + "BluetoothAdapter.ACTIVE_DEVICE_ALL");
         }
-
         try {
             mServiceLock.readLock().lock();
             if (mService != null) {
@@ -2162,6 +2209,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/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index 12dc814c..5b60b85 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -23,6 +23,7 @@
 import android.annotation.RequiresPermission;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
+import android.annotation.SuppressLint;
 import android.annotation.SystemApi;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
@@ -179,9 +180,10 @@
      * <p>Always contains the extra field {@link #EXTRA_DEVICE}.
      * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
      */
+    @SuppressLint("ActionValue")
     @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
     public static final String ACTION_ALIAS_CHANGED =
-            "android.bluetooth.action.ALIAS_CHANGED";
+            "android.bluetooth.device.action.ALIAS_CHANGED";
 
     /**
      * Broadcast Action: Indicates a change in the bond state of a remote
@@ -1091,7 +1093,7 @@
      * @hide
      */
     @SystemApi
-    @RequiresPermission(Manifest.permission.BLUETOOTH)
+    @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
     public boolean setAlias(@NonNull String alias) {
         final IBluetooth service = sService;
         if (service == null) {
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/provider/Settings.java b/core/java/android/provider/Settings.java
index 605c585..b62142c 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -2417,6 +2417,10 @@
     public static class NameValueTable implements BaseColumns {
         public static final String NAME = "name";
         public static final String VALUE = "value";
+        // A flag indicating whether the current value of a setting should be preserved during
+        // restore.
+        /** @hide */
+        public static final String IS_PRESERVED_IN_RESTORE = "is_preserved_in_restore";
 
         protected static boolean putString(ContentResolver resolver, Uri uri,
                 String name, String value) {
diff --git a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
index 6334d9d..368c94c 100644
--- a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
+++ b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
@@ -31,6 +31,7 @@
 import android.content.Intent;
 import android.graphics.Rect;
 import android.os.Build;
+import android.os.Bundle;
 import android.os.CancellationSignal;
 import android.os.Handler;
 import android.os.IBinder;
@@ -39,6 +40,7 @@
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.service.autofill.Dataset;
+import android.service.autofill.FillEventHistory;
 import android.service.autofill.augmented.PresentationParams.SystemPopupPresentationParams;
 import android.util.Log;
 import android.util.Pair;
@@ -319,6 +321,31 @@
         pw.print(getClass().getName()); pw.println(": nothing to dump");
     }
 
+    /**
+     * Gets the inline augmented autofill events that happened after the last
+     * {@link #onFillRequest(FillRequest, CancellationSignal, FillController, FillCallback)} call.
+     *
+     * <p>The history is not persisted over reboots, and it's cleared every time the service
+     * replies to a
+     * {@link #onFillRequest(FillRequest, CancellationSignal, FillController, FillCallback)}
+     * by calling {@link FillCallback#onSuccess(FillResponse)}. Hence, the service should call
+     * {@link #getFillEventHistory() before finishing the {@link FillCallback}.
+     *
+     * <p>Also note that the events from the dropdown suggestion UI is not stored in the history
+     * since the service owns the UI.
+     *
+     * @return The history or {@code null} if there are no events.
+     */
+    @Nullable public final FillEventHistory getFillEventHistory() {
+        final AutofillManager afm = getSystemService(AutofillManager.class);
+
+        if (afm == null) {
+            return null;
+        } else {
+            return afm.getFillEventHistory();
+        }
+    }
+
     /** @hide */
     static final class AutofillProxy {
 
@@ -487,9 +514,10 @@
             }
         }
 
-        public void onInlineSuggestionsDataReady(@NonNull List<Dataset> inlineSuggestionsData) {
+        public void onInlineSuggestionsDataReady(@NonNull List<Dataset> inlineSuggestionsData,
+                @Nullable Bundle clientState) {
             try {
-                mCallback.onSuccess(inlineSuggestionsData.toArray(new Dataset[]{}));
+                mCallback.onSuccess(inlineSuggestionsData.toArray(new Dataset[]{}), clientState);
             } catch (RemoteException e) {
                 Log.e(TAG, "Error calling back with the inline suggestions data: " + e);
             }
@@ -511,7 +539,8 @@
                         }
                     }
                     try {
-                        mCallback.onSuccess(/* mInlineSuggestionsData= */null);
+                        mCallback.onSuccess(/* inlineSuggestionsData= */null, /* clientState=*/
+                                null);
                     } catch (RemoteException e) {
                         Log.e(TAG, "Error reporting success: " + e);
                     }
diff --git a/core/java/android/service/autofill/augmented/FillCallback.java b/core/java/android/service/autofill/augmented/FillCallback.java
index b767799..d0ffd7b 100644
--- a/core/java/android/service/autofill/augmented/FillCallback.java
+++ b/core/java/android/service/autofill/augmented/FillCallback.java
@@ -60,7 +60,7 @@
 
         List<Dataset> inlineSuggestions = response.getInlineSuggestions();
         if (inlineSuggestions != null && !inlineSuggestions.isEmpty()) {
-            mProxy.onInlineSuggestionsDataReady(inlineSuggestions);
+            mProxy.onInlineSuggestionsDataReady(inlineSuggestions, response.getClientState());
             return;
         }
 
diff --git a/core/java/android/service/autofill/augmented/FillResponse.java b/core/java/android/service/autofill/augmented/FillResponse.java
index e8e6ff0..68ba63a 100644
--- a/core/java/android/service/autofill/augmented/FillResponse.java
+++ b/core/java/android/service/autofill/augmented/FillResponse.java
@@ -19,6 +19,7 @@
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
+import android.os.Bundle;
 import android.service.autofill.Dataset;
 
 import com.android.internal.util.DataClass;
@@ -50,6 +51,13 @@
     @DataClass.PluralOf("inlineSuggestion")
     private @Nullable List<Dataset> mInlineSuggestions;
 
+    /**
+     * The client state that {@link AugmentedAutofillService} implementation can put anything in to
+     * identify the request and the response when calling
+     * {@link AugmentedAutofillService#getFillEventHistory()}.
+     */
+    private @Nullable Bundle mClientState;
+
     private static FillWindow defaultFillWindow() {
         return null;
     }
@@ -58,6 +66,10 @@
         return null;
     }
 
+    private static Bundle defaultClientState() {
+        return null;
+    }
+
 
     /** @hide */
     abstract static class BaseBuilder {
@@ -82,9 +94,11 @@
     @DataClass.Generated.Member
     /* package-private */ FillResponse(
             @Nullable FillWindow fillWindow,
-            @Nullable List<Dataset> inlineSuggestions) {
+            @Nullable List<Dataset> inlineSuggestions,
+            @Nullable Bundle clientState) {
         this.mFillWindow = fillWindow;
         this.mInlineSuggestions = inlineSuggestions;
+        this.mClientState = clientState;
 
         // onConstructed(); // You can define this method to get a callback
     }
@@ -111,6 +125,18 @@
     }
 
     /**
+     * The client state that {@link AugmentedAutofillService} implementation can put anything in to
+     * identify the request and the response when calling
+     * {@link AugmentedAutofillService#getFillEventHistory()}.
+     *
+     * @hide
+     */
+    @DataClass.Generated.Member
+    public @Nullable Bundle getClientState() {
+        return mClientState;
+    }
+
+    /**
      * A builder for {@link FillResponse}
      */
     @SuppressWarnings("WeakerAccess")
@@ -119,6 +145,7 @@
 
         private @Nullable FillWindow mFillWindow;
         private @Nullable List<Dataset> mInlineSuggestions;
+        private @Nullable Bundle mClientState;
 
         private long mBuilderFieldsSet = 0L;
 
@@ -157,10 +184,23 @@
             return this;
         }
 
+        /**
+         * The client state that {@link AugmentedAutofillService} implementation can put anything in to
+         * identify the request and the response when calling
+         * {@link AugmentedAutofillService#getFillEventHistory()}.
+         */
+        @DataClass.Generated.Member
+        public @NonNull Builder setClientState(@Nullable Bundle value) {
+            checkNotUsed();
+            mBuilderFieldsSet |= 0x4;
+            mClientState = value;
+            return this;
+        }
+
         /** Builds the instance. This builder should not be touched after calling this! */
         public @NonNull FillResponse build() {
             checkNotUsed();
-            mBuilderFieldsSet |= 0x4; // Mark builder used
+            mBuilderFieldsSet |= 0x8; // Mark builder used
 
             if ((mBuilderFieldsSet & 0x1) == 0) {
                 mFillWindow = defaultFillWindow();
@@ -168,14 +208,18 @@
             if ((mBuilderFieldsSet & 0x2) == 0) {
                 mInlineSuggestions = defaultInlineSuggestions();
             }
+            if ((mBuilderFieldsSet & 0x4) == 0) {
+                mClientState = defaultClientState();
+            }
             FillResponse o = new FillResponse(
                     mFillWindow,
-                    mInlineSuggestions);
+                    mInlineSuggestions,
+                    mClientState);
             return o;
         }
 
         private void checkNotUsed() {
-            if ((mBuilderFieldsSet & 0x4) != 0) {
+            if ((mBuilderFieldsSet & 0x8) != 0) {
                 throw new IllegalStateException(
                         "This Builder should not be reused. Use a new Builder instance instead");
             }
@@ -183,10 +227,10 @@
     }
 
     @DataClass.Generated(
-            time = 1577476012370L,
+            time = 1580335256422L,
             codegenVersion = "1.0.14",
             sourceFile = "frameworks/base/core/java/android/service/autofill/augmented/FillResponse.java",
-            inputSignatures = "private @android.annotation.Nullable android.service.autofill.augmented.FillWindow mFillWindow\nprivate @com.android.internal.util.DataClass.PluralOf(\"inlineSuggestion\") @android.annotation.Nullable java.util.List<android.service.autofill.Dataset> mInlineSuggestions\nprivate static  android.service.autofill.augmented.FillWindow defaultFillWindow()\nprivate static  java.util.List<android.service.autofill.Dataset> defaultInlineSuggestions()\nclass FillResponse extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genBuilder=true, genHiddenGetters=true)\nabstract  android.service.autofill.augmented.FillResponse.Builder addInlineSuggestion(android.service.autofill.Dataset)\nclass BaseBuilder extends java.lang.Object implements []")
+            inputSignatures = "private @android.annotation.Nullable android.service.autofill.augmented.FillWindow mFillWindow\nprivate @com.android.internal.util.DataClass.PluralOf(\"inlineSuggestion\") @android.annotation.Nullable java.util.List<android.service.autofill.Dataset> mInlineSuggestions\nprivate @android.annotation.Nullable android.os.Bundle mClientState\nprivate static  android.service.autofill.augmented.FillWindow defaultFillWindow()\nprivate static  java.util.List<android.service.autofill.Dataset> defaultInlineSuggestions()\nprivate static  android.os.Bundle defaultClientState()\nclass FillResponse extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genBuilder=true, genHiddenGetters=true)\nabstract  android.service.autofill.augmented.FillResponse.Builder addInlineSuggestion(android.service.autofill.Dataset)\nclass BaseBuilder extends java.lang.Object implements []")
     @Deprecated
     private void __metadata() {}
 
diff --git a/core/java/android/service/autofill/augmented/IFillCallback.aidl b/core/java/android/service/autofill/augmented/IFillCallback.aidl
index 3ccb311..31e77f3 100644
--- a/core/java/android/service/autofill/augmented/IFillCallback.aidl
+++ b/core/java/android/service/autofill/augmented/IFillCallback.aidl
@@ -16,6 +16,7 @@
 
 package android.service.autofill.augmented;
 
+import android.os.Bundle;
 import android.os.ICancellationSignal;
 
 import android.service.autofill.Dataset;
@@ -27,7 +28,7 @@
  */
 interface IFillCallback {
     void onCancellable(in ICancellationSignal cancellation);
-    void onSuccess(in @nullable Dataset[] mInlineSuggestionsData);
+    void onSuccess(in @nullable Dataset[] inlineSuggestionsData, in @nullable Bundle clientState);
     boolean isCompleted();
     void cancel();
 }
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/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index 06fccaf..5fdac81 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -47,6 +47,9 @@
     /** @hide */
     public static final String BACKUP_NO_KV_DATA_CHANGE_CALLS =
             "backup_enable_no_data_notification_calls";
+    /** @hide */
+    public static final String SETTINGS_DO_NOT_RESTORE_PRESERVED =
+            "settings_do_not_restore_preserved";
 
     private static final Map<String, String> DEFAULT_FLAGS;
 
@@ -68,6 +71,11 @@
 
         // Disabled until backup transports support it.
         DEFAULT_FLAGS.put(BACKUP_NO_KV_DATA_CHANGE_CALLS, "false");
+        // Disabled by default until b/148278926 is resolved. This flags guards a feature
+        // introduced in R and will be removed in the next release (b/148367230).
+        DEFAULT_FLAGS.put(SETTINGS_DO_NOT_RESTORE_PRESERVED, "false");
+
+        DEFAULT_FLAGS.put("settings_tether_all_in_one", "false");
     }
 
     /**
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/ImeInsetsSourceConsumer.java b/core/java/android/view/ImeInsetsSourceConsumer.java
index 23ba097..f226369 100644
--- a/core/java/android/view/ImeInsetsSourceConsumer.java
+++ b/core/java/android/view/ImeInsetsSourceConsumer.java
@@ -17,6 +17,7 @@
 package android.view;
 
 import static android.view.InsetsState.ITYPE_IME;
+import static android.view.InsetsState.toPublicType;
 
 import android.annotation.Nullable;
 import android.inputmethodservice.InputMethodService;
@@ -44,6 +45,12 @@
      */
     private boolean mShowOnNextImeRender;
 
+    /**
+     * Tracks whether we have an outstanding request from the IME to show, but weren't able to
+     * execute it because we didn't have control yet.
+     */
+    private boolean mImeRequestedShow;
+
     public ImeInsetsSourceConsumer(
             InsetsState state, Supplier<Transaction> transactionSupplier,
             InsetsController controller) {
@@ -81,13 +88,14 @@
     public void onWindowFocusLost() {
         super.onWindowFocusLost();
         getImm().unregisterImeConsumer(this);
+        mImeRequestedShow = false;
     }
 
     @Override
-    public void setControl(@Nullable InsetsSourceControl control) {
-        super.setControl(control);
-        if (control == null) {
-            hide();
+    public void show(boolean fromIme) {
+        super.show(fromIme);
+        if (fromIme) {
+            mImeRequestedShow = true;
         }
     }
 
@@ -99,7 +107,11 @@
     public @ShowResult int requestShow(boolean fromIme) {
         // TODO: ResultReceiver for IME.
         // TODO: Set mShowOnNextImeRender to automatically show IME and guard it with a flag.
-        if (fromIme) {
+
+        // If we had a request before to show from IME (tracked with mImeRequestedShow), reaching
+        // this code here means that we now got control, so we can start the animation immediately.
+        if (fromIme || mImeRequestedShow) {
+            mImeRequestedShow = false;
             return ShowResult.SHOW_IMMEDIATELY;
         }
 
@@ -115,6 +127,15 @@
         getImm().notifyImeHidden();
     }
 
+    @Override
+    public void setControl(@Nullable InsetsSourceControl control, int[] showTypes,
+            int[] hideTypes) {
+        super.setControl(control, showTypes, hideTypes);
+        if (control == null) {
+            hide();
+        }
+    }
+
     private boolean isDummyOrEmptyEditor(EditorInfo info) {
         // TODO(b/123044812): Handle dummy input gracefully in IME Insets API
         return info == null || (info.fieldId <= 0 && info.inputType <= 0);
diff --git a/core/java/android/view/InsetsAnimationControlImpl.java b/core/java/android/view/InsetsAnimationControlImpl.java
index 5c24047..0653b06 100644
--- a/core/java/android/view/InsetsAnimationControlImpl.java
+++ b/core/java/android/view/InsetsAnimationControlImpl.java
@@ -16,8 +16,6 @@
 
 package android.view;
 
-import static android.view.InsetsController.LAYOUT_INSETS_DURING_ANIMATION_HIDDEN;
-import static android.view.InsetsController.LAYOUT_INSETS_DURING_ANIMATION_SHOWN;
 import static android.view.InsetsState.ISIDE_BOTTOM;
 import static android.view.InsetsState.ISIDE_FLOATING;
 import static android.view.InsetsState.ISIDE_LEFT;
@@ -188,7 +186,7 @@
 
     @Override
     public void finish(boolean shown) {
-        if (mCancelled) {
+        if (mCancelled || mFinished) {
             return;
         }
         setInsetsAndAlpha(shown ? mShownInsets : mHiddenInsets, 1f /* alpha */, 1f /* fraction */);
@@ -210,6 +208,10 @@
         mListener.onCancelled();
     }
 
+    public boolean isCancelled() {
+        return mCancelled;
+    }
+
     InsetsAnimation getAnimation() {
         return mAnimation;
     }
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 6f76497..22d6f37 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -39,6 +39,7 @@
 import android.util.Pair;
 import android.util.Property;
 import android.util.SparseArray;
+import android.view.InputDevice.MotionRange;
 import android.view.InsetsSourceConsumer.ShowResult;
 import android.view.InsetsState.InternalInsetsType;
 import android.view.SurfaceControl.Transaction;
@@ -69,7 +70,7 @@
     private static final int ANIMATION_DURATION_HIDE_MS = 340;
     private static final int PENDING_CONTROL_TIMEOUT_MS = 2000;
 
-    static final Interpolator INTERPOLATOR = new PathInterpolator(0.4f, 0f, 0.2f, 1f);
+    public static final Interpolator INTERPOLATOR = new PathInterpolator(0.4f, 0f, 0.2f, 1f);
 
     /**
      * Layout mode during insets animation: The views should be laid out as if the changing inset
@@ -77,7 +78,7 @@
      * be called as if the changing insets types are shown, which will result in the views being
      * laid out as if the insets are fully shown.
      */
-    static final int LAYOUT_INSETS_DURING_ANIMATION_SHOWN = 0;
+    public static final int LAYOUT_INSETS_DURING_ANIMATION_SHOWN = 0;
 
     /**
      * Layout mode during insets animation: The views should be laid out as if the changing inset
@@ -85,7 +86,7 @@
      * be called as if the changing insets types are hidden, which will result in the views being
      * laid out as if the insets are fully hidden.
      */
-    static final int LAYOUT_INSETS_DURING_ANIMATION_HIDDEN = 1;
+    public static final int LAYOUT_INSETS_DURING_ANIMATION_HIDDEN = 1;
 
     /**
      * Determines the behavior of how the views should be laid out during an insets animation that
@@ -148,7 +149,7 @@
         @Override
         public void set(WindowInsetsAnimationController controller, Insets value) {
             controller.setInsetsAndAlpha(
-                    value, 1f /* alpha */, (((DefaultAnimationControlListener)
+                    value, 1f /* alpha */, (((InternalAnimationControlListener)
                             ((InsetsAnimationControlImpl) controller).getListener())
                                     .getRawFraction()));
         }
@@ -165,7 +166,7 @@
         private ObjectAnimator mAnimator;
         protected boolean mShow;
 
-        InternalAnimationControlListener(boolean show) {
+        public InternalAnimationControlListener(boolean show) {
             mShow = show;
         }
 
@@ -213,7 +214,10 @@
             return (float) mAnimator.getCurrentPlayTime() / mAnimator.getDuration();
         }
 
-        protected long getDurationMs() {
+        /**
+         * To get the animation duration in MS.
+         */
+        public long getDurationMs() {
             if (mAnimator != null) {
                 return mAnimator.getDuration();
             }
@@ -273,7 +277,7 @@
     private final String TAG = "InsetsControllerImpl";
 
     private final InsetsState mState = new InsetsState();
-    private final InsetsState mTmpState = new InsetsState();
+    private final InsetsState mLastDispachedState = new InsetsState();
 
     private final Rect mFrame = new Rect();
     private final BiFunction<InsetsController, Integer, InsetsSourceConsumer> mConsumerCreator;
@@ -367,15 +371,20 @@
         return mState;
     }
 
-    boolean onStateChanged(InsetsState state) {
-        if (mState.equals(state)) {
+    public InsetsState getLastDispatchedState() {
+        return mLastDispachedState;
+    }
+
+    @VisibleForTesting
+    public boolean onStateChanged(InsetsState state) {
+        if (mState.equals(state) && mLastDispachedState.equals(state)) {
             return false;
         }
         mState.set(state);
-        mTmpState.set(state, true /* copySources */);
+        mLastDispachedState.set(state, true /* copySources */);
         applyLocalVisibilityOverride();
         mViewRoot.notifyInsetsChanged();
-        if (!mState.equals(mTmpState)) {
+        if (!mState.equals(mLastDispachedState)) {
             sendStateToWindowManager();
         }
         return true;
@@ -419,6 +428,9 @@
             }
         }
 
+        int[] showTypes = new int[1];
+        int[] hideTypes = new int[1];
+
         // Ensure to update all existing source consumers
         for (int i = mSourceConsumers.size() - 1; i >= 0; i--) {
             final InsetsSourceConsumer consumer = mSourceConsumers.valueAt(i);
@@ -426,15 +438,23 @@
 
             // control may be null, but we still need to update the control to null if it got
             // revoked.
-            consumer.setControl(control);
+            consumer.setControl(control, showTypes, hideTypes);
         }
 
         // Ensure to create source consumers if not available yet.
         for (int i = mTmpControlArray.size() - 1; i >= 0; i--) {
             final InsetsSourceControl control = mTmpControlArray.valueAt(i);
-            getSourceConsumer(control.getType()).setControl(control);
+            InsetsSourceConsumer consumer = getSourceConsumer(control.getType());
+            consumer.setControl(control, showTypes, hideTypes);
+
         }
         mTmpControlArray.clear();
+        if (showTypes[0] != 0) {
+            applyAnimation(showTypes[0], true /* show */, false /* fromIme */);
+        }
+        if (hideTypes[0] != 0) {
+            applyAnimation(hideTypes[0], false /* show */, false /* fromIme */);
+        }
     }
 
     @Override
@@ -465,7 +485,7 @@
             @InternalInsetsType int internalType = internalTypes.valueAt(i);
             @AnimationType int animationType = getAnimationType(internalType);
             InsetsSourceConsumer consumer = getSourceConsumer(internalType);
-            if (mState.getSource(internalType).isVisible() && animationType == ANIMATION_TYPE_NONE
+            if (consumer.isRequestedVisible() && animationType == ANIMATION_TYPE_NONE
                     || animationType == ANIMATION_TYPE_SHOW) {
                 // no-op: already shown or animating in (because window visibility is
                 // applied before starting animation).
@@ -488,7 +508,7 @@
             @InternalInsetsType int internalType = internalTypes.valueAt(i);
             @AnimationType int animationType = getAnimationType(internalType);
             InsetsSourceConsumer consumer = getSourceConsumer(internalType);
-            if (!mState.getSource(internalType).isVisible() && animationType == ANIMATION_TYPE_NONE
+            if (!consumer.isRequestedVisible() && animationType == ANIMATION_TYPE_NONE
                     || animationType == ANIMATION_TYPE_HIDE) {
                 // no-op: already hidden or animating out.
                 continue;
@@ -535,7 +555,7 @@
         final SparseArray<InsetsSourceControl> controls = new SparseArray<>();
 
         Pair<Integer, Boolean> typesReadyPair = collectSourceControls(
-                fromIme, internalTypes, controls);
+                fromIme, internalTypes, controls, animationType);
         int typesReady = typesReadyPair.first;
         boolean imeReady = typesReadyPair.second;
         if (!imeReady) {
@@ -562,17 +582,20 @@
      * @return Pair of (types ready to animate, IME ready to animate).
      */
     private Pair<Integer, Boolean> collectSourceControls(boolean fromIme,
-            ArraySet<Integer> internalTypes, SparseArray<InsetsSourceControl> controls) {
+            ArraySet<Integer> internalTypes, SparseArray<InsetsSourceControl> controls,
+            @AnimationType int animationType) {
         int typesReady = 0;
         boolean imeReady = true;
         for (int i = internalTypes.size() - 1; i >= 0; i--) {
             InsetsSourceConsumer consumer = getSourceConsumer(internalTypes.valueAt(i));
-            boolean setVisible = !consumer.isRequestedVisible();
-            if (setVisible) {
+            boolean show = animationType == ANIMATION_TYPE_SHOW
+                    || animationType == ANIMATION_TYPE_USER;
+            boolean canRun = false;
+            if (show) {
                 // Show request
                 switch(consumer.requestShow(fromIme)) {
                     case ShowResult.SHOW_IMMEDIATELY:
-                        typesReady |= InsetsState.toPublicType(consumer.getType());
+                        canRun = true;
                         break;
                     case ShowResult.IME_SHOW_DELAYED:
                         imeReady = false;
@@ -589,11 +612,22 @@
                 if (!fromIme) {
                     consumer.notifyHidden();
                 }
-                typesReady |= InsetsState.toPublicType(consumer.getType());
+                canRun = true;
+            }
+            if (!canRun) {
+                continue;
             }
             final InsetsSourceControl control = consumer.getControl();
             if (control != null) {
                 controls.put(consumer.getType(), control);
+                typesReady |= toPublicType(consumer.getType());
+            } else if (animationType == ANIMATION_TYPE_SHOW) {
+
+                // We don't have a control at the moment. However, we still want to update requested
+                // visibility state such that in case we get control, we can apply show animation.
+                consumer.show(fromIme);
+            } else if (animationType == ANIMATION_TYPE_HIDE) {
+                consumer.hide();
             }
         }
         return new Pair<>(typesReady, imeReady);
@@ -808,7 +842,7 @@
     private void showDirectly(@InsetsType int types) {
         final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types);
         for (int i = internalTypes.size() - 1; i >= 0; i--) {
-            getSourceConsumer(internalTypes.valueAt(i)).show();
+            getSourceConsumer(internalTypes.valueAt(i)).show(false /* fromIme */);
         }
     }
 
@@ -840,6 +874,9 @@
             @Override
             public boolean onPreDraw() {
                 mViewRoot.mView.getViewTreeObserver().removeOnPreDrawListener(this);
+                if (controller.isCancelled()) {
+                    return true;
+                }
                 mViewRoot.mView.dispatchWindowInsetsAnimationStart(animation, bounds);
                 listener.onReady(controller, types);
                 return true;
diff --git a/core/java/android/view/InsetsSource.java b/core/java/android/view/InsetsSource.java
index 37034ee..d4961ea 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;
     }
@@ -189,6 +193,15 @@
         dest.writeBoolean(mVisible);
     }
 
+    @Override
+    public String toString() {
+        return "InsetsSource: {"
+                + "mType=" + InsetsState.typeToString(mType)
+                + ", mFrame=" + mFrame.toShortString()
+                + ", mVisible" + mVisible
+                + "}";
+    }
+
     public static final @android.annotation.NonNull Creator<InsetsSource> CREATOR = new Creator<InsetsSource>() {
 
         public InsetsSource createFromParcel(Parcel in) {
diff --git a/core/java/android/view/InsetsSourceConsumer.java b/core/java/android/view/InsetsSourceConsumer.java
index 9901d05..e6497c0 100644
--- a/core/java/android/view/InsetsSourceConsumer.java
+++ b/core/java/android/view/InsetsSourceConsumer.java
@@ -17,11 +17,14 @@
 package android.view;
 
 import static android.view.InsetsController.ANIMATION_TYPE_NONE;
+import static android.view.InsetsState.toPublicType;
 
 import android.annotation.IntDef;
 import android.annotation.Nullable;
+import android.util.MutableShort;
 import android.view.InsetsState.InternalInsetsType;
 import android.view.SurfaceControl.Transaction;
+import android.view.WindowInsets.Type.InsetsType;
 
 import com.android.internal.annotations.VisibleForTesting;
 
@@ -71,18 +74,48 @@
         mRequestedVisible = InsetsState.getDefaultVisibility(type);
     }
 
-    public void setControl(@Nullable InsetsSourceControl control) {
+    /**
+     * Updates the control delivered from the server.
+
+     * @param showTypes An integer array with a single entry that determines which types a show
+     *                  animation should be run after setting the control.
+     * @param hideTypes An integer array with a single entry that determines which types a hide
+     *                  animation should be run after setting the control.
+     */
+    public void setControl(@Nullable InsetsSourceControl control,
+            @InsetsType int[] showTypes, @InsetsType int[] hideTypes) {
         if (mSourceControl == control) {
             return;
         }
         mSourceControl = control;
-        applyHiddenToControl();
+
+        // We are loosing control
+        if (mSourceControl == null) {
+            mController.notifyControlRevoked(this);
+
+            // Restore server visibility.
+            mState.getSource(getType()).setVisible(
+                    mController.getLastDispatchedState().getSource(getType()).isVisible());
+            applyLocalVisibilityOverride();
+            return;
+        }
+
+        // We are gaining control, and need to run an animation since previous state didn't match
+        if (mRequestedVisible != mState.getSource(mType).isVisible()) {
+            if (mRequestedVisible) {
+                showTypes[0] |= toPublicType(getType());
+            } else {
+                hideTypes[0] |= toPublicType(getType());
+            }
+            return;
+        }
+
+        // We are gaining control, but don't need to run an animation. However make sure that the
+        // leash visibility is still up to date.
         if (applyLocalVisibilityOverride()) {
             mController.notifyVisibilityChanged();
         }
-        if (mSourceControl == null) {
-            mController.notifyControlRevoked(this);
-        }
+        applyHiddenToControl();
     }
 
     @VisibleForTesting
@@ -95,7 +128,7 @@
     }
 
     @VisibleForTesting
-    public void show() {
+    public void show(boolean fromIme) {
         setRequestedVisible(true);
     }
 
@@ -172,17 +205,13 @@
      * the moment.
      */
     private void setRequestedVisible(boolean requestedVisible) {
-        if (mRequestedVisible == requestedVisible) {
-            return;
-        }
         mRequestedVisible = requestedVisible;
-        applyLocalVisibilityOverride();
-        mController.notifyVisibilityChanged();
+        if (applyLocalVisibilityOverride()) {
+            mController.notifyVisibilityChanged();
+        }
     }
 
     private void applyHiddenToControl() {
-
-        // TODO: Handle case properly when animation is running already (it shouldn't!)
         if (mSourceControl == null || mSourceControl.getLeash() == null) {
             return;
         }
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index bd19799..8648682 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -21,7 +21,6 @@
 import static android.view.ViewRootImpl.NEW_INSETS_MODE_IME;
 import static android.view.ViewRootImpl.NEW_INSETS_MODE_NONE;
 import static android.view.ViewRootImpl.sNewInsetsMode;
-import static android.view.WindowInsets.Type.IME;
 import static android.view.WindowInsets.Type.MANDATORY_SYSTEM_GESTURES;
 import static android.view.WindowInsets.Type.SIZE;
 import static android.view.WindowInsets.Type.SYSTEM_GESTURES;
@@ -43,7 +42,6 @@
 import android.util.SparseIntArray;
 import android.view.WindowInsets.Type;
 import android.view.WindowInsets.Type.InsetsType;
-import android.view.WindowManager.LayoutParams;
 import android.view.WindowManager.LayoutParams.SoftInputModeFlags;
 
 import java.io.PrintWriter;
@@ -366,7 +364,12 @@
         return result;
     }
 
-    static @Type.InsetsType int toPublicType(@InternalInsetsType int type) {
+    /**
+     * Converting a internal type to the public type.
+     * @param type internal insets type, {@code InternalInsetsType}.
+     * @return public insets type, {@code Type.InsetsType}.
+     */
+    public static @Type.InsetsType int toPublicType(@InternalInsetsType int type) {
         switch (type) {
             case ITYPE_STATUS_BAR:
                 return Type.STATUS_BARS;
@@ -510,5 +513,13 @@
             mSources.put(source.getType(), source);
         }
     }
+
+    @Override
+    public String toString() {
+        return "InsetsState: {"
+                + "mDisplayFrame=" + mDisplayFrame
+                + ", mSources=" + mSources
+                + "}";
+    }
 }
 
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index d70bf22f..ee7f6fb 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -3654,8 +3654,7 @@
      *
      * @deprecated For floating windows, use {@link LayoutParams#setFitInsetsTypes(int)} with
      * {@link Type#navigationBars()}. For non-floating windows that fill the screen, call
-     * {@link Window#setOnContentApplyWindowInsetsListener} with {@code null} or a listener that
-     * doesn't fit the navigation bar on the window content level.
+     * {@link Window#setDecorFitsSystemWindows(boolean)} with {@code false}.
      */
     public static final int SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION = 0x00000200;
 
@@ -3683,8 +3682,7 @@
      *
      * @deprecated For floating windows, use {@link LayoutParams#setFitInsetsTypes(int)} with
      * {@link Type#statusBars()} ()}. For non-floating windows that fill the screen, call
-     * {@link Window#setOnContentApplyWindowInsetsListener} with {@code null} or a listener that
-     * doesn't fit the status bar on the window content level.
+     * {@link Window#setDecorFitsSystemWindows(boolean)} with {@code false}.
      */
     @Deprecated
     public static final int SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN = 0x00000400;
@@ -4673,7 +4671,7 @@
 
         private ArrayList<OnUnhandledKeyEventListener> mUnhandledKeyListeners;
 
-        private WindowInsetsAnimationCallback mWindowInsetsAnimationCallback;
+        WindowInsetsAnimationCallback mWindowInsetsAnimationCallback;
 
         /**
          * This lives here since it's only valid for interactive views.
@@ -11537,7 +11535,7 @@
     }
 
     /**
-     * @see #PFLAG4_OPTIONAL_FITS_SYSTEM_WINDOWS
+     * @see #PFLAG4_FRAMEWORK_OPTIONAL_FITS_SYSTEM_WINDOWS
      * @hide
      */
     public void makeFrameworkOptionalFitsSystemWindows() {
@@ -11545,6 +11543,13 @@
     }
 
     /**
+     * @hide
+     */
+    public boolean isFrameworkOptionalFitsSystemWindows() {
+        return (mPrivateFlags4 & PFLAG4_FRAMEWORK_OPTIONAL_FITS_SYSTEM_WINDOWS) != 0;
+    }
+
+    /**
      * Returns the visibility status for this view.
      *
      * @return One of {@link #VISIBLE}, {@link #INVISIBLE}, or {@link #GONE}.
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 4f03ca1..d416d42 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -7239,6 +7239,18 @@
     public void dispatchWindowInsetsAnimationPrepare(
             @NonNull InsetsAnimation animation) {
         super.dispatchWindowInsetsAnimationPrepare(animation);
+
+        // If we are root-level content view that fits insets, set dispatch mode to stop to imitate
+        // consume behavior.
+        boolean isOptionalFitSystemWindows = (mViewFlags & OPTIONAL_FITS_SYSTEM_WINDOWS) != 0
+                || isFrameworkOptionalFitsSystemWindows();
+        if (isOptionalFitSystemWindows && mAttachInfo != null
+                && getListenerInfo().mWindowInsetsAnimationCallback == null
+                && mAttachInfo.mContentOnApplyWindowInsetsListener != null) {
+            mInsetsAnimationDispatchMode = DISPATCH_MODE_STOP;
+            return;
+        }
+
         if (mInsetsAnimationDispatchMode == DISPATCH_MODE_STOP) {
             return;
         }
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 51ea30b..2b4b71f 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -4887,8 +4887,14 @@
                     break;
                 case MSG_INSETS_CONTROL_CHANGED: {
                     SomeArgs args = (SomeArgs) msg.obj;
-                    mInsetsController.onControlsChanged((InsetsSourceControl[]) args.arg2);
+
+                    // Deliver state change before control change, such that:
+                    // a) When gaining control, controller can compare with server state to evaluate
+                    // whether it needs to run animation.
+                    // b) When loosing control, controller can restore server state by taking last
+                    // dispatched state as truth.
                     mInsetsController.onStateChanged((InsetsState) args.arg1);
+                    mInsetsController.onControlsChanged((InsetsSourceControl[]) args.arg2);
                     break;
                 }
                 case MSG_SHOW_INSETS: {
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index 0ef4e33..4b284db 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -697,12 +697,10 @@
     }
 
     /**
-     * Listener for applying window insets on the content of a window in a custom way.
+     * Listener for applying window insets on the content of a window. Used only by the framework to
+     * fit content according to legacy SystemUI flags.
      *
-     * <p>Apps may choose to implement this interface if they want to apply custom policy
-     * to the way that window insets are treated for fitting root-level content views.
-     *
-     * @see Window#setOnContentApplyWindowInsetsListener(OnContentApplyWindowInsetsListener)
+     * @hide
      */
     public interface OnContentApplyWindowInsetsListener {
 
@@ -716,13 +714,15 @@
          *
          * @param insets The root level insets that are about to be dispatched
          * @return A pair, with the first element containing the insets to apply as margin to the
-         *         root-level content views, and the second element determining what should be
-         *         dispatched to the content view.
+         * root-level content views, and the second element determining what should be
+         * dispatched to the content view.
          */
-        @NonNull Pair<Insets, WindowInsets> onContentApplyWindowInsets(
+        @NonNull
+        Pair<Insets, WindowInsets> onContentApplyWindowInsets(
                 @NonNull WindowInsets insets);
     }
 
+
     public Window(Context context) {
         mContext = context;
         mFeatures = mLocalFeatures = getDefaultFeatures(context);
@@ -1311,33 +1311,21 @@
     }
 
     /**
-     * Sets the listener to be invoked when fitting root-level content views.
+     * Sets whether the decor view should fit root-level content views for {@link WindowInsets}.
      * <p>
-     * By default, a listener that inspects the now deprecated {@link View#SYSTEM_UI_LAYOUT_FLAGS}
-     * as well the {@link WindowManager.LayoutParams#SOFT_INPUT_ADJUST_RESIZE} flag is installed and
-     * fits content according to these flags.
+     * If set to {@code true}, the framework will inspect the now deprecated
+     * {@link View#SYSTEM_UI_LAYOUT_FLAGS} as well the
+     * {@link WindowManager.LayoutParams#SOFT_INPUT_ADJUST_RESIZE} flag and fits content according
+     * to these flags.
      * </p>
-     * @param contentOnApplyWindowInsetsListener The listener to use for fitting root-level content
-     *                                           views, or {@code null} to disable any kind of
-     *                                           content fitting on the window level and letting the
-     *                                           {@link WindowInsets} pass through to the content
-     *                                           view.
-     * @see OnContentApplyWindowInsetsListener
-     */
-    public void setOnContentApplyWindowInsetsListener(
-            @Nullable OnContentApplyWindowInsetsListener contentOnApplyWindowInsetsListener) {
-    }
-
-    /**
-     * Resets the listener set via {@link #setOnContentApplyWindowInsetsListener} to the default
-     * state.
      * <p>
-     * By default, a listener that inspects the now deprecated {@link View#SYSTEM_UI_LAYOUT_FLAGS}
-     * as well the {@link WindowManager.LayoutParams#SOFT_INPUT_ADJUST_RESIZE} flag is installed and
-     * fits content according to these flags.
+     * If set to {@code false}, the framework will not fit the content view to the insets and will
+     * just pass through the {@link WindowInsets} to the content view.
      * </p>
+     * @param decorFitsSystemWindows Whether the decor view should fit root-level content views for
+     *                               insets.
      */
-    public void resetOnContentApplyWindowInsetsListener() {
+    public void setDecorFitsSystemWindows(boolean decorFitsSystemWindows) {
     }
 
     /**
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/android/view/WindowInsetsAnimationCallback.java b/core/java/android/view/WindowInsetsAnimationCallback.java
index 1e04d02..4c8463b3 100644
--- a/core/java/android/view/WindowInsetsAnimationCallback.java
+++ b/core/java/android/view/WindowInsetsAnimationCallback.java
@@ -86,9 +86,9 @@
      * following:
      * <p>
      * <ul>
-     *     <li>Application calls {@link WindowInsetsController#hideInputMethod()},
-     *     {@link WindowInsetsController#showInputMethod()},
-     *     {@link WindowInsetsController#controlInputMethodAnimation}</li>
+     *     <li>Application calls {@link WindowInsetsController#hide(int)},
+     *     {@link WindowInsetsController#show(int)},
+     *     {@link WindowInsetsController#controlWindowInsetsAnimation}</li>
      *     <li>onPrepare is called on the view hierarchy listeners</li>
      *     <li>{@link View#onApplyWindowInsets} will be called with the end state of the
      *     animation</li>
@@ -106,12 +106,12 @@
      * related methods.
      * <p>
      * Note: If the animation is application controlled by using
-     * {@link WindowInsetsController#controlInputMethodAnimation}, the end state of the animation
+     * {@link WindowInsetsController#controlWindowInsetsAnimation}, the end state of the animation
      * is undefined as the application may decide on the end state only by passing in the
      * {@code shown} parameter when calling {@link WindowInsetsAnimationController#finish}. In this
      * situation, the system will dispatch the insets in the opposite visibility state before the
      * animation starts. Example: When controlling the input method with
-     * {@link WindowInsetsController#controlInputMethodAnimation} and the input method is currently
+     * {@link WindowInsetsController#controlWindowInsetsAnimation} and the input method is currently
      * showing, {@link View#onApplyWindowInsets} will receive a {@link WindowInsets} instance for
      * which {@link WindowInsets#isVisible} will return {@code false} for {@link Type#ime}.
      *
@@ -246,7 +246,7 @@
          * be the same as the application passed into
          * {@link WindowInsetsAnimationController#setInsetsAndAlpha(Insets, float, float)},
          * interpolated with the interpolator passed into
-         * {@link WindowInsetsController#controlInputMethodAnimation}.
+         * {@link WindowInsetsController#controlWindowInsetsAnimation}.
          * </p>
          * <p>
          * Note: For system-initiated animations, this will always return a valid value between 0
diff --git a/core/java/android/view/WindowInsetsAnimationControlListener.java b/core/java/android/view/WindowInsetsAnimationControlListener.java
index f91254d..701bd31 100644
--- a/core/java/android/view/WindowInsetsAnimationControlListener.java
+++ b/core/java/android/view/WindowInsetsAnimationControlListener.java
@@ -39,7 +39,7 @@
      * @param controller The controller to control the inset animation.
      * @param types The {@link InsetsType}s it was able to gain control over. Note that this may be
      *              different than the types passed into
-     *              {@link WindowInsetsController#controlInputMethodAnimation} in case the window
+     *              {@link WindowInsetsController#controlWindowInsetsAnimation} in case the window
      *              wasn't able to gain the controls because it wasn't the IME target or not
      *              currently the window that's controlling the system bars.
      */
diff --git a/core/java/android/view/WindowInsetsAnimationController.java b/core/java/android/view/WindowInsetsAnimationController.java
index 2bf0d27..4a864be 100644
--- a/core/java/android/view/WindowInsetsAnimationController.java
+++ b/core/java/android/view/WindowInsetsAnimationController.java
@@ -23,6 +23,7 @@
 import android.graphics.Insets;
 import android.view.WindowInsets.Type.InsetsType;
 import android.view.WindowInsetsAnimationCallback.AnimationBounds;
+import android.view.animation.Interpolator;
 
 /**
  * Controller for app-driven animation of system windows.
@@ -32,7 +33,7 @@
  *  synchronized, such that changes the system windows and the app's current frame
  *  are rendered at the same time.
  *  <p>
- *  Control is obtained through {@link WindowInsetsController#controlInputMethodAnimation}.
+ *  Control is obtained through {@link WindowInsetsController#controlWindowInsetsAnimation}.
  */
 @SuppressLint("NotClosable")
 public interface WindowInsetsAnimationController {
diff --git a/core/java/android/view/WindowInsetsController.java b/core/java/android/view/WindowInsetsController.java
index 02323cf..f501de9 100644
--- a/core/java/android/view/WindowInsetsController.java
+++ b/core/java/android/view/WindowInsetsController.java
@@ -167,62 +167,6 @@
             @NonNull WindowInsetsAnimationControlListener listener);
 
     /**
-     * Lets the application control the animation for showing the IME in a frame-by-frame manner by
-     * modifying the position of the IME when it's causing insets.
-     *
-     * @param durationMillis Duration of the animation in
-     *                       {@link java.util.concurrent.TimeUnit#MILLISECONDS}, or -1 if the
-     *                       animation doesn't have a predetermined duration. This value will be
-     *                       passed to {@link InsetsAnimation#getDurationMillis()}
-     * @param interpolator The interpolator used for this animation, or {@code null} if this
-     *                     animation doesn't follow an interpolation curve. This value will be
-     *                     passed to {@link InsetsAnimation#getInterpolator()} and used to calculate
-     *                     {@link InsetsAnimation#getInterpolatedFraction()}.
-     * @param listener The {@link WindowInsetsAnimationControlListener} that gets called when the
-     *                 IME are ready to be controlled, among other callbacks.
-     *
-     * @see InsetsAnimation#getFraction()
-     * @see InsetsAnimation#getInterpolatedFraction()
-     * @see InsetsAnimation#getInterpolator()
-     * @see InsetsAnimation#getDurationMillis()
-     */
-    default void controlInputMethodAnimation(long durationMillis,
-            @Nullable Interpolator interpolator,
-            @NonNull WindowInsetsAnimationControlListener listener) {
-        controlWindowInsetsAnimation(ime(), durationMillis, interpolator, listener);
-    }
-
-    /**
-     * Makes the IME appear on screen.
-     * <p>
-     * Note that if the window currently doesn't have control over the IME, because it doesn't have
-     * focus, it will apply the change as soon as the window gains control. The app can listen to
-     * the event by observing {@link View#onApplyWindowInsets} and checking visibility with
-     * {@link WindowInsets#isVisible}.
-     *
-     * @see #controlInputMethodAnimation
-     * @see #hideInputMethod()
-     */
-    default void showInputMethod() {
-        show(ime());
-    }
-
-    /**
-     * Makes the IME disappear on screen.
-     * <p>
-     * Note that if the window currently doesn't have control over IME, because it doesn't have
-     * focus, it will apply the change as soon as the window gains control. The app can listen to
-     * the event by observing {@link View#onApplyWindowInsets} and checking visibility with
-     * {@link WindowInsets#isVisible}.
-     *
-     * @see #controlInputMethodAnimation
-     * @see #showInputMethod()
-     */
-    default void hideInputMethod() {
-        hide(ime());
-    }
-
-    /**
      * Controls the appearance of system bars.
      * <p>
      * For example, the following statement adds {@link #APPEARANCE_LIGHT_STATUS_BARS}:
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index a6450a1..dad7671 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -80,6 +80,7 @@
 import android.text.TextUtils;
 import android.util.Log;
 import android.util.proto.ProtoOutputStream;
+import android.view.View.OnApplyWindowInsetsListener;
 import android.view.WindowInsets.Side;
 import android.view.WindowInsets.Side.InsetsSide;
 import android.view.WindowInsets.Type;
@@ -2193,8 +2194,9 @@
          * value for {@link #softInputMode} will be ignored; the window will
          * not resize, but will stay fullscreen.
          *
-         * @deprecated Use {@link Window#setOnContentApplyWindowInsetsListener} instead with a
-         * listener that fits {@link Type#ime()} instead.
+         * @deprecated Call {@link Window#setDecorFitsSystemWindows(boolean)} with {@code false} and
+         * install an {@link OnApplyWindowInsetsListener} on your root content view that fits insets
+         * of type {@link Type#ime()}.
          */
         @Deprecated
         public static final int SOFT_INPUT_ADJUST_RESIZE = 0x10;
diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java
index db009f6..c6ed624 100644
--- a/core/java/com/android/internal/os/RuntimeInit.java
+++ b/core/java/com/android/internal/os/RuntimeInit.java
@@ -67,9 +67,10 @@
 
     private static volatile boolean mCrashing = false;
 
-    /*
+    /**
      * Native heap allocations will now have a non-zero tag in the most significant byte.
-     * See {@linktourl https://source.android.com/devices/tech/debug/tagged-pointers}.
+     * See
+     * <a href="https://source.android.com/devices/tech/debug/tagged-pointers">https://source.android.com/devices/tech/debug/tagged-pointers</a>.
      */
     @ChangeId
     @EnabledAfter(targetSdkVersion = VersionCodes.Q)
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index f13a638..46d7f4e 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -320,7 +320,8 @@
     /** @see ViewRootImpl#mActivityConfigCallback */
     private ActivityConfigCallback mActivityConfigCallback;
 
-    private OnContentApplyWindowInsetsListener mPendingOnContentApplyWindowInsetsListener;
+    private OnContentApplyWindowInsetsListener mPendingOnContentApplyWindowInsetsListener
+            = createDefaultContentWindowInsetsListener();
 
     static class WindowManagerHolder {
         static final IWindowManager sWindowManager = IWindowManager.Stub.asInterface(
@@ -2109,14 +2110,9 @@
     /** Notify when decor view is attached to window and {@link ViewRootImpl} is available. */
     void onViewRootImplSet(ViewRootImpl viewRoot) {
         viewRoot.setActivityConfigCallback(mActivityConfigCallback);
-        if (mPendingOnContentApplyWindowInsetsListener != null) {
-            viewRoot.setOnContentApplyWindowInsetsListener(
-                    mPendingOnContentApplyWindowInsetsListener);
-            mPendingOnContentApplyWindowInsetsListener = null;
-        } else {
-            viewRoot.setOnContentApplyWindowInsetsListener(
-                    createDefaultContentWindowInsetsListener());
-        }
+        viewRoot.setOnContentApplyWindowInsetsListener(
+                mPendingOnContentApplyWindowInsetsListener);
+        mPendingOnContentApplyWindowInsetsListener = null;
     }
 
     private OnContentApplyWindowInsetsListener createDefaultContentWindowInsetsListener() {
@@ -2125,8 +2121,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();
@@ -3902,17 +3899,15 @@
     }
 
     @Override
-    public void setOnContentApplyWindowInsetsListener(OnContentApplyWindowInsetsListener listener) {
+    public void setDecorFitsSystemWindows(boolean decorFitsSystemWindows) {
         ViewRootImpl impl = getViewRootImpl();
+        OnContentApplyWindowInsetsListener listener = decorFitsSystemWindows
+                ? createDefaultContentWindowInsetsListener()
+                : null;
         if (impl != null) {
             impl.setOnContentApplyWindowInsetsListener(listener);
         } else {
             mPendingOnContentApplyWindowInsetsListener = listener;
         }
     }
-
-    @Override
-    public void resetOnContentApplyWindowInsetsListener() {
-        setOnContentApplyWindowInsetsListener(createDefaultContentWindowInsetsListener());
-    }
 }
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/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 790ac08..924cd81 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -621,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];
@@ -816,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");
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/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index c0743e5..7c8f62c 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -150,9 +150,9 @@
     // Will be removed soon.
     optional PinnedStackControllerProto pinned_stack_controller = 5 [deprecated=true];
     /* non app windows */
-    repeated WindowTokenProto above_app_windows = 6;
-    repeated WindowTokenProto below_app_windows = 7;
-    repeated WindowTokenProto ime_windows = 8;
+    repeated WindowTokenProto above_app_windows = 6 [deprecated=true];
+    repeated WindowTokenProto below_app_windows = 7 [deprecated=true];
+    repeated WindowTokenProto ime_windows = 8 [deprecated=true];
     optional int32 dpi = 9;
     optional .android.view.DisplayInfoProto display_info = 10;
     optional int32 rotation = 11;
@@ -165,8 +165,33 @@
     repeated IdentifierProto closing_apps = 18;
     repeated IdentifierProto changing_apps = 19;
     repeated WindowTokenProto overlay_windows = 20;
+    optional DisplayAreaProto root_display_area = 21;
 }
 
+/* represents DisplayArea object */
+message DisplayAreaProto {
+    option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+    optional WindowContainerProto window_container = 1;
+    optional string name = 2 [ (.android.privacy).dest = DEST_EXPLICIT ];
+    repeated DisplayAreaChildProto children = 3;
+}
+
+/* represents a generic child of a DisplayArea */
+message DisplayAreaChildProto {
+    option (.android.msg_privacy).dest = DEST_AUTOMATIC;
+
+    /* At most one of the following should be present: */
+
+    /* represents a DisplayArea child */
+    optional DisplayAreaProto display_area = 1;
+    /* represents a WindowToken child */
+    optional WindowTokenProto window = 2;
+    /* represents an unknown child - the class name is recorded */
+    repeated string unknown = 3;
+}
+
+
 /* represents DisplayFrames */
 message DisplayFramesProto {
     option (.android.msg_privacy).dest = DEST_AUTOMATIC;
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/InsetsAnimationControlImplTest.java b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
index 169716f..bdb8021 100644
--- a/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
+++ b/core/tests/coretests/src/android/view/InsetsAnimationControlImplTest.java
@@ -109,13 +109,14 @@
         InsetsSourceConsumer topConsumer = new InsetsSourceConsumer(ITYPE_STATUS_BAR, mInsetsState,
                 () -> mMockTransaction, mMockController);
         topConsumer.setControl(
-                new InsetsSourceControl(ITYPE_STATUS_BAR, mTopLeash, new Point(0, 0)));
+                new InsetsSourceControl(ITYPE_STATUS_BAR, mTopLeash, new Point(0, 0)),
+                new int[1], new int[1]);
 
         InsetsSourceConsumer navConsumer = new InsetsSourceConsumer(ITYPE_NAVIGATION_BAR,
                 mInsetsState, () -> mMockTransaction, mMockController);
-        navConsumer.hide();
         navConsumer.setControl(new InsetsSourceControl(ITYPE_NAVIGATION_BAR, mNavLeash,
-                new Point(400, 0)));
+                new Point(400, 0)), new int[1], new int[1]);
+        navConsumer.hide();
 
         SparseArray<InsetsSourceControl> controls = new SparseArray<>();
         controls.put(ITYPE_STATUS_BAR, topConsumer.getControl());
diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java
index e68c4b8..2c9dba1 100644
--- a/core/tests/coretests/src/android/view/InsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java
@@ -121,9 +121,20 @@
                 if (type == ITYPE_IME) {
                     return new InsetsSourceConsumer(type, controller.getState(),
                             Transaction::new, controller) {
+
+                        private boolean mImeRequestedShow;
+
+                        @Override
+                        public void show(boolean fromIme) {
+                            super.show(fromIme);
+                            if (fromIme) {
+                                mImeRequestedShow = true;
+                            }
+                        }
+
                         @Override
                         public int requestShow(boolean fromController) {
-                            if (fromController) {
+                            if (fromController || mImeRequestedShow) {
                                 return SHOW_IMMEDIATELY;
                             } else {
                                 return IME_SHOW_DELAYED;
@@ -399,6 +410,84 @@
     }
 
     @Test
+    public void testRestoreStartsAnimation() {
+        InsetsSourceControl control =
+                new InsetsSourceControl(ITYPE_STATUS_BAR, mLeash, new Point());
+        mController.onControlsChanged(new InsetsSourceControl[]{control});
+
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+            mController.hide(Type.statusBars());
+            mController.cancelExistingAnimation();
+            assertFalse(mController.getSourceConsumer(ITYPE_STATUS_BAR).isRequestedVisible());
+            assertFalse(mController.getState().getSource(ITYPE_STATUS_BAR).isVisible());
+
+            // Loosing control
+            InsetsState state = new InsetsState(mController.getState());
+            state.setSourceVisible(ITYPE_STATUS_BAR, true);
+            mController.onStateChanged(state);
+            mController.onControlsChanged(new InsetsSourceControl[0]);
+            assertFalse(mController.getSourceConsumer(ITYPE_STATUS_BAR).isRequestedVisible());
+            assertTrue(mController.getState().getSource(ITYPE_STATUS_BAR).isVisible());
+
+            // Gaining control
+            mController.onControlsChanged(new InsetsSourceControl[]{control});
+            assertEquals(ANIMATION_TYPE_HIDE, mController.getAnimationType(ITYPE_STATUS_BAR));
+            mController.cancelExistingAnimation();
+            assertFalse(mController.getSourceConsumer(ITYPE_STATUS_BAR).isRequestedVisible());
+            assertFalse(mController.getState().getSource(ITYPE_STATUS_BAR).isVisible());
+        });
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+    }
+
+    @Test
+    public void testStartImeAnimationAfterGettingControl() {
+        InsetsSourceControl control =
+                new InsetsSourceControl(ITYPE_IME, mLeash, new Point());
+
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+
+            mController.show(ime());
+            assertFalse(mController.getState().getSource(ITYPE_IME).isVisible());
+
+            // Pretend IME is calling
+            mController.show(ime(), true /* fromIme */);
+
+            // Gaining control shortly after
+            mController.onControlsChanged(new InsetsSourceControl[]{control});
+
+            assertEquals(ANIMATION_TYPE_SHOW, mController.getAnimationType(ITYPE_IME));
+            mController.cancelExistingAnimation();
+            assertTrue(mController.getSourceConsumer(ITYPE_IME).isRequestedVisible());
+            assertTrue(mController.getState().getSource(ITYPE_IME).isVisible());
+        });
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+    }
+
+    @Test
+    public void testStartImeAnimationAfterGettingControl_imeLater() {
+        InsetsSourceControl control =
+                new InsetsSourceControl(ITYPE_IME, mLeash, new Point());
+
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+
+            mController.show(ime());
+            assertFalse(mController.getState().getSource(ITYPE_IME).isVisible());
+
+            // Gaining control shortly after
+            mController.onControlsChanged(new InsetsSourceControl[]{control});
+
+            // Pretend IME is calling
+            mController.show(ime(), true /* fromIme */);
+
+            assertEquals(ANIMATION_TYPE_SHOW, mController.getAnimationType(ITYPE_IME));
+            mController.cancelExistingAnimation();
+            assertTrue(mController.getSourceConsumer(ITYPE_IME).isRequestedVisible());
+            assertTrue(mController.getState().getSource(ITYPE_IME).isVisible());
+        });
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+    }
+
+    @Test
     public void testAnimationEndState_controller() throws Exception {
         InsetsSourceControl control =
                 new InsetsSourceControl(ITYPE_STATUS_BAR, mLeash, new Point());
@@ -432,7 +521,7 @@
         InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
             WindowInsetsAnimationControlListener listener =
                     mock(WindowInsetsAnimationControlListener.class);
-            mController.controlInputMethodAnimation(0, new LinearInterpolator(), listener);
+            mController.controlWindowInsetsAnimation(ime(), 0, new LinearInterpolator(), listener);
 
             // Ready gets deferred until next predraw
             mViewRoot.getView().getViewTreeObserver().dispatchOnPreDraw();
@@ -456,7 +545,7 @@
         InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
             WindowInsetsAnimationControlListener listener =
                     mock(WindowInsetsAnimationControlListener.class);
-            mController.controlInputMethodAnimation(0, new LinearInterpolator(), listener);
+            mController.controlWindowInsetsAnimation(ime(), 0, new LinearInterpolator(), listener);
 
             // Ready gets deferred until next predraw
             mViewRoot.getView().getViewTreeObserver().dispatchOnPreDraw();
@@ -476,7 +565,7 @@
         InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
             WindowInsetsAnimationControlListener listener =
                     mock(WindowInsetsAnimationControlListener.class);
-            mController.controlInputMethodAnimation(0, new LinearInterpolator(), listener);
+            mController.controlWindowInsetsAnimation(ime(), 0, new LinearInterpolator(), listener);
 
             // Ready gets deferred until next predraw
             mViewRoot.getView().getViewTreeObserver().dispatchOnPreDraw();
diff --git a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
index 492c036..5e9e2f0 100644
--- a/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsSourceConsumerTest.java
@@ -18,6 +18,8 @@
 
 import static android.view.InsetsState.ITYPE_STATUS_BAR;
 
+import static android.view.WindowInsets.Type.statusBars;
+import static junit.framework.Assert.assertEquals;
 import static junit.framework.TestCase.assertFalse;
 import static junit.framework.TestCase.assertTrue;
 
@@ -90,34 +92,44 @@
         });
         instrumentation.waitForIdleSync();
 
-        mConsumer.setControl(new InsetsSourceControl(ITYPE_STATUS_BAR, mLeash, new Point()));
+        mConsumer.setControl(new InsetsSourceControl(ITYPE_STATUS_BAR, mLeash, new Point()),
+                new int[1], new int[1]);
     }
 
     @Test
     public void testHide() {
-        mConsumer.hide();
-        assertFalse("Consumer should not be visible", mConsumer.isRequestedVisible());
-        verify(mSpyInsetsSource).setVisible(eq(false));
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+            mConsumer.hide();
+            assertFalse("Consumer should not be visible", mConsumer.isRequestedVisible());
+            verify(mSpyInsetsSource).setVisible(eq(false));
+        });
+
     }
 
     @Test
     public void testShow() {
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+            // Insets source starts out visible
+            mConsumer.hide();
+            mConsumer.show(false /* fromIme */);
+            assertTrue("Consumer should be visible", mConsumer.isRequestedVisible());
+            verify(mSpyInsetsSource).setVisible(eq(false));
+            verify(mSpyInsetsSource).setVisible(eq(true));
+        });
 
-        // Insets source starts out visible
-        mConsumer.hide();
-        mConsumer.show();
-        assertTrue("Consumer should be visible", mConsumer.isRequestedVisible());
-        verify(mSpyInsetsSource).setVisible(eq(false));
-        verify(mSpyInsetsSource).setVisible(eq(true));
     }
 
     @Test
     public void testRestore() {
-        mConsumer.setControl(null);
-        reset(mMockTransaction);
-        mConsumer.hide();
-        verifyZeroInteractions(mMockTransaction);
-        mConsumer.setControl(new InsetsSourceControl(ITYPE_STATUS_BAR, mLeash, new Point()));
-        verify(mMockTransaction).hide(eq(mLeash));
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+            mConsumer.setControl(null, new int[1], new int[1]);
+            reset(mMockTransaction);
+            mConsumer.hide();
+            verifyZeroInteractions(mMockTransaction);
+            int[] hideTypes = new int[1];
+            mConsumer.setControl(new InsetsSourceControl(ITYPE_STATUS_BAR, mLeash, new Point()),
+                    new int[1], hideTypes);
+            assertEquals(statusBars(), hideTypes[0]);
+        });
     }
 }
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/services.core.protolog.json b/data/etc/services.core.protolog.json
index 91c5fbe..13dcb4c 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -1573,12 +1573,6 @@
       "group": "WM_ERROR",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
-    "1004585481": {
-      "message": "%s forcing orientation to %d for display id=%d",
-      "level": "VERBOSE",
-      "group": "WM_DEBUG_ORIENTATION",
-      "at": "com\/android\/server\/wm\/DisplayContent.java"
-    },
     "1051545910": {
       "message": "Exit animation finished in %s: remove=%b",
       "level": "VERBOSE",
@@ -1717,6 +1711,12 @@
       "group": "WM_DEBUG_IME",
       "at": "com\/android\/server\/wm\/ImeInsetsSourceProvider.java"
     },
+    "1389009035": {
+      "message": "NonAppWindowContainer cannot set orientation: %s",
+      "level": "WARN",
+      "group": "WM_DEBUG_ORIENTATION",
+      "at": "com\/android\/server\/wm\/DisplayContent.java"
+    },
     "1401700824": {
       "message": "Window drawn win=%s",
       "level": "DEBUG",
@@ -1891,6 +1891,12 @@
       "group": "WM_DEBUG_STARTING_WINDOW",
       "at": "com\/android\/server\/wm\/ActivityRecord.java"
     },
+    "1674747211": {
+      "message": "%s forcing orientation to %d for display id=%d",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_ORIENTATION",
+      "at": "com\/android\/server\/wm\/DisplayArea.java"
+    },
     "1677260366": {
       "message": "Finish starting %s: first real window is shown, no animation",
       "level": "VERBOSE",
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/location/java/android/location/Location.java b/location/java/android/location/Location.java
index 7a12cee..eb76c29 100644
--- a/location/java/android/location/Location.java
+++ b/location/java/android/location/Location.java
@@ -16,7 +16,6 @@
 
 package android.location;
 
-import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
 import android.compat.annotation.UnsupportedAppUsage;
@@ -1214,23 +1213,6 @@
     }
 
     /**
-     * Attaches an extra {@link Location} to this Location. This is useful for location providers
-     * to set the {@link #EXTRA_NO_GPS_LOCATION} extra to provide coarse locations for clients.
-     *
-     * @param key the key associated with the Location extra
-     * @param value the Location to attach
-     * @hide
-     */
-    @TestApi
-    @SystemApi
-    public void setExtraLocation(@Nullable String key, @Nullable Location value) {
-        if (mExtras == null) {
-            mExtras = new Bundle();
-        }
-        mExtras.putParcelable(key, value);
-    }
-
-    /**
      * Returns true if the Location came from a mock provider.
      *
      * @return true if this Location came from a mock provider, false otherwise
diff --git a/media/java/android/media/MediaRoute2Info.java b/media/java/android/media/MediaRoute2Info.java
index dd01243..0e88c75 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;
@@ -197,6 +198,7 @@
     final List<String> mFeatures;
     @DeviceType
     final int mDeviceType;
+    final boolean mIsSystem;
     final Uri mIconUri;
     final CharSequence mDescription;
     @ConnectionState
@@ -213,6 +215,7 @@
         mName = builder.mName;
         mFeatures = builder.mFeatures;
         mDeviceType = builder.mDeviceType;
+        mIsSystem = builder.mIsSystem;
         mIconUri = builder.mIconUri;
         mDescription = builder.mDescription;
         mConnectionState = builder.mConnectionState;
@@ -229,6 +232,7 @@
         mName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
         mFeatures = in.createStringArrayList();
         mDeviceType = in.readInt();
+        mIsSystem = in.readBoolean();
         mIconUri = in.readParcelable(null);
         mDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
         mConnectionState = in.readInt();
@@ -287,6 +291,17 @@
     }
 
     /**
+     * Returns whether the route is a system route or not.
+     * <p>
+     * System routes are media routes directly controlled by the system
+     * such as phone speaker, wired headset, and Bluetooth devices.
+     * </p>
+     */
+    public boolean isSystemRoute() {
+        return mIsSystem;
+    }
+
+    /**
      * Gets the URI of the icon representing this route.
      * <p>
      * This icon will be used in picker UIs if available.
@@ -360,6 +375,7 @@
      * @hide
      */
     @NonNull
+    @TestApi
     public String getOriginalId() {
         return mId;
     }
@@ -423,6 +439,7 @@
                 && Objects.equals(mName, other.mName)
                 && Objects.equals(mFeatures, other.mFeatures)
                 && (mDeviceType == other.mDeviceType)
+                && (mIsSystem == other.mIsSystem)
                 && Objects.equals(mIconUri, other.mIconUri)
                 && Objects.equals(mDescription, other.mDescription)
                 && (mConnectionState == other.mConnectionState)
@@ -436,7 +453,7 @@
     @Override
     public int hashCode() {
         // Note: mExtras is not included.
-        return Objects.hash(mId, mName, mFeatures, mDeviceType, mIconUri, mDescription,
+        return Objects.hash(mId, mName, mFeatures, mDeviceType, mIsSystem, mIconUri, mDescription,
                 mConnectionState, mClientPackageName, mVolumeHandling, mVolumeMax, mVolume,
                 mProviderId);
     }
@@ -473,6 +490,7 @@
         TextUtils.writeToParcel(mName, dest, flags);
         dest.writeStringList(mFeatures);
         dest.writeInt(mDeviceType);
+        dest.writeBoolean(mIsSystem);
         dest.writeParcelable(mIconUri, flags);
         TextUtils.writeToParcel(mDescription, dest, flags);
         dest.writeInt(mConnectionState);
@@ -494,6 +512,7 @@
 
         @DeviceType
         int mDeviceType = DEVICE_TYPE_UNKNOWN;
+        boolean mIsSystem;
         Uri mIconUri;
         CharSequence mDescription;
         @ConnectionState
@@ -540,6 +559,7 @@
             mName = routeInfo.mName;
             mFeatures = new ArrayList<>(routeInfo.mFeatures);
             mDeviceType = routeInfo.mDeviceType;
+            mIsSystem = routeInfo.mIsSystem;
             mIconUri = routeInfo.mIconUri;
             mDescription = routeInfo.mDescription;
             mConnectionState = routeInfo.mConnectionState;
@@ -608,6 +628,16 @@
         }
 
         /**
+         * Sets whether the route is a system route or not.
+         * @hide
+         */
+        @NonNull
+        public Builder setSystemRoute(boolean isSystem) {
+            mIsSystem = isSystem;
+            return this;
+        }
+
+        /**
          * Sets the URI of the icon representing this route.
          * <p>
          * This icon will be used in picker UIs if available.
diff --git a/media/java/android/media/MediaRoute2ProviderInfo.java b/media/java/android/media/MediaRoute2ProviderInfo.java
index c9a2ec7..afe002e 100644
--- a/media/java/android/media/MediaRoute2ProviderInfo.java
+++ b/media/java/android/media/MediaRoute2ProviderInfo.java
@@ -180,6 +180,23 @@
         }
 
         /**
+         * Sets whether the provider provides system routes or not
+         */
+        @NonNull
+        public Builder setSystemRouteProvider(boolean isSystem) {
+            int count = mRoutes.size();
+            for (int i = 0; i < count; i++) {
+                MediaRoute2Info route = mRoutes.valueAt(i);
+                if (route.isSystemRoute() != isSystem) {
+                    mRoutes.setValueAt(i, new MediaRoute2Info.Builder(route)
+                            .setSystemRoute(isSystem)
+                            .build());
+                }
+            }
+            return this;
+        }
+
+        /**
          * Adds a route to the provider
          */
         @NonNull
diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java
index 7b9a44f..f751a22 100644
--- a/media/java/android/media/MediaRouter2.java
+++ b/media/java/android/media/MediaRouter2.java
@@ -230,7 +230,11 @@
     /**
      * Gets the unmodifiable list of {@link MediaRoute2Info routes} currently
      * known to the media router.
+     * <p>
+     * {@link MediaRoute2Info#isSystemRoute() System routes} such as phone speaker,
+     * Bluetooth devices are always included in the list.
      * Please note that the list can be changed before callbacks are invoked.
+     * </p>
      *
      * @return the list of routes that contains at least one of the route features in discovery
      * preferences registered by the application
@@ -243,7 +247,8 @@
 
                 List<MediaRoute2Info> filteredRoutes = new ArrayList<>();
                 for (MediaRoute2Info route : mRoutes.values()) {
-                    if (route.hasAnyFeatures(mDiscoveryPreference.getPreferredFeatures())) {
+                    if (route.isSystemRoute()
+                            || route.hasAnyFeatures(mDiscoveryPreference.getPreferredFeatures())) {
                         filteredRoutes.add(route);
                     }
                 }
@@ -307,12 +312,18 @@
      * with the given route.
      *
      * @param route the route you want to create a controller with.
+     * @throws IllegalArgumentException if the given route is
+     * {@link MediaRoute2Info#isSystemRoute() system route}
      *
      * @see RoutingControllerCallback#onControllerCreated
      * @see RoutingControllerCallback#onControllerCreationFailed
      */
     public void requestCreateController(@NonNull MediaRoute2Info route) {
         Objects.requireNonNull(route, "route must not be null");
+        if (route.isSystemRoute()) {
+            throw new IllegalArgumentException("Can't create a route controller with "
+                    + "a system route. Use getSystemController().");
+        }
         // TODO: Check the given route exists
 
         final int requestId;
diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java
index 61e2f77..662eeb1 100644
--- a/media/java/android/media/MediaRouter2Manager.java
+++ b/media/java/android/media/MediaRouter2Manager.java
@@ -161,14 +161,15 @@
     public List<MediaRoute2Info> getAvailableRoutes(@NonNull String packageName) {
         Objects.requireNonNull(packageName, "packageName must not be null");
 
+        List<MediaRoute2Info> routes = new ArrayList<>();
+
         List<String> preferredFeatures = mPreferredFeaturesMap.get(packageName);
         if (preferredFeatures == null) {
-            return Collections.emptyList();
+            preferredFeatures = Collections.emptyList();
         }
-        List<MediaRoute2Info> routes = new ArrayList<>();
         synchronized (mRoutesLock) {
             for (MediaRoute2Info route : mRoutes.values()) {
-                if (route.hasAnyFeatures(preferredFeatures)) {
+                if (route.isSystemRoute() || route.hasAnyFeatures(preferredFeatures)) {
                     routes.add(route);
                 }
             }
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/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
index 4a2044a..16259ab 100644
--- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
+++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java
@@ -196,7 +196,14 @@
     public void testRouteFeatures() throws Exception {
         Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(FEATURES_SPECIAL);
 
-        assertEquals(1, routes.size());
+        int routeCount = 0;
+        for (MediaRoute2Info route : routes.values()) {
+            if (!route.isSystemRoute()) {
+                routeCount++;
+            }
+        }
+
+        assertEquals(1, routeCount);
         assertNotNull(routes.get(ROUTE_ID_SPECIAL_FEATURE));
     }
 
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/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
index ec445d4..38cf5ab 100644
--- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
@@ -318,6 +318,16 @@
                 return false;
             }
 
+            // Allow all directories on USB, including the root.
+            try {
+                RootInfo rootInfo = getRootFromDocId(docId);
+                if ((rootInfo.flags & Root.FLAG_REMOVABLE_USB) == Root.FLAG_REMOVABLE_USB) {
+                    return false;
+                }
+            } catch (FileNotFoundException e) {
+                Log.e(TAG, "Failed to determine rootInfo for docId");
+            }
+
             final String path = getPathFromDocId(docId);
 
             // Block the root of the storage
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index 2b143e4..b31841d 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -42,7 +42,8 @@
     <string name="connected_via_app" msgid="3532267661404276584">"Gekoppel via <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"Beskikbaar via %1$s"</string>
     <string name="tap_to_sign_up" msgid="5356397741063740395">"Tik om aan te meld"</string>
-    <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Gekoppel, geen internet nie"</string>
+    <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+    <skip />
     <string name="private_dns_broken" msgid="1984159464346556931">"Daar kan nie by private DNS-bediener ingegaan word nie"</string>
     <string name="wifi_limited_connection" msgid="1184778285475204682">"Beperkte verbinding"</string>
     <string name="wifi_status_no_internet" msgid="3799933875988829048">"Geen internet nie"</string>
diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
index 2a93e017..4955ad8 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -42,7 +42,8 @@
     <string name="connected_via_app" msgid="3532267661404276584">"በ <xliff:g id="NAME">%1$s</xliff:g> በኩል ተገናኝተዋል"</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"በ%1$s በኩል የሚገኝ"</string>
     <string name="tap_to_sign_up" msgid="5356397741063740395">"ለመመዝገብ መታ ያድርጉ"</string>
-    <string name="wifi_connected_no_internet" msgid="2381729074310543563">"ተገናኝቷል፣ ምንም በይነመረብ የለም"</string>
+    <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+    <skip />
     <string name="private_dns_broken" msgid="1984159464346556931">"የግል ዲኤንኤስ አገልጋይ ሊደረስበት አይችልም"</string>
     <string name="wifi_limited_connection" msgid="1184778285475204682">"የተገደበ ግንኙነት"</string>
     <string name="wifi_status_no_internet" msgid="3799933875988829048">"ምንም በይነመረብ የለም"</string>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index 5103345..7b26be4 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -42,7 +42,8 @@
     <string name="connected_via_app" msgid="3532267661404276584">"تم الاتصال عبر <xliff:g id="NAME">%1$s</xliff:g>."</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"‏متوفرة عبر %1$s"</string>
     <string name="tap_to_sign_up" msgid="5356397741063740395">"انقر للاشتراك."</string>
-    <string name="wifi_connected_no_internet" msgid="2381729074310543563">"متصلة ولكن بلا إنترنت"</string>
+    <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+    <skip />
     <string name="private_dns_broken" msgid="1984159464346556931">"لا يمكن الوصول إلى خادم أسماء نظام نطاقات خاص"</string>
     <string name="wifi_limited_connection" msgid="1184778285475204682">"اتصال محدود"</string>
     <string name="wifi_status_no_internet" msgid="3799933875988829048">"لا يتوفر اتصال إنترنت."</string>
diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml
index fa26b9b..dcebe5b 100644
--- a/packages/SettingsLib/res/values-as/strings.xml
+++ b/packages/SettingsLib/res/values-as/strings.xml
@@ -42,7 +42,8 @@
     <string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g>ৰ জৰিয়তে সংযুক্ত"</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"%1$sৰ মাধ্যমেৰে উপলব্ধ"</string>
     <string name="tap_to_sign_up" msgid="5356397741063740395">"ছাইন আপ কৰিবলৈ টিপক"</string>
-    <string name="wifi_connected_no_internet" msgid="2381729074310543563">"সংযোজিত, ইণ্টাৰনেট নাই"</string>
+    <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+    <skip />
     <string name="private_dns_broken" msgid="1984159464346556931">"ব্যক্তিগত DNS ছাৰ্ভাৰ এক্সেছ কৰিব নোৱাৰি"</string>
     <string name="wifi_limited_connection" msgid="1184778285475204682">"ইণ্টাৰনেট সংযোগ সীমিত"</string>
     <string name="wifi_status_no_internet" msgid="3799933875988829048">"ইণ্টাৰনেট সংযোগ নাই"</string>
diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml
index 2e80fdc..7f3db37 100644
--- a/packages/SettingsLib/res/values-az/strings.xml
+++ b/packages/SettingsLib/res/values-az/strings.xml
@@ -42,7 +42,8 @@
     <string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g> ilə qoşulub"</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"%1$s vasitəsilə əlçatandır"</string>
     <string name="tap_to_sign_up" msgid="5356397741063740395">"Qeydiyyatdan keçmək üçün klikləyin"</string>
-    <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Qoşuludur, internet yoxdur"</string>
+    <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+    <skip />
     <string name="private_dns_broken" msgid="1984159464346556931">"Özəl DNS serverinə giriş mümkün deyil"</string>
     <string name="wifi_limited_connection" msgid="1184778285475204682">"Məhdud bağlantı"</string>
     <string name="wifi_status_no_internet" msgid="3799933875988829048">"İnternet yoxdur"</string>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
index ae5c936..b789cb0 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
@@ -42,7 +42,8 @@
     <string name="connected_via_app" msgid="3532267661404276584">"Povezano preko: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"Dostupna je preko pristupne tačke %1$s"</string>
     <string name="tap_to_sign_up" msgid="5356397741063740395">"Dodirnite da biste se registrovali"</string>
-    <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Veza je uspostavljena, nema interneta"</string>
+    <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+    <skip />
     <string name="private_dns_broken" msgid="1984159464346556931">"Pristup privatnom DNS serveru nije uspeo"</string>
     <string name="wifi_limited_connection" msgid="1184778285475204682">"Ograničena veza"</string>
     <string name="wifi_status_no_internet" msgid="3799933875988829048">"Nema interneta"</string>
diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml
index ad201d0..ce191eb 100644
--- a/packages/SettingsLib/res/values-be/strings.xml
+++ b/packages/SettingsLib/res/values-be/strings.xml
@@ -42,7 +42,8 @@
     <string name="connected_via_app" msgid="3532267661404276584">"Падключана праз праграму \"<xliff:g id="NAME">%1$s</xliff:g>\""</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"Даступна праз %1$s"</string>
     <string name="tap_to_sign_up" msgid="5356397741063740395">"Націсніце, каб зарэгістравацца"</string>
-    <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Падключана, без доступу да інтэрнэту"</string>
+    <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+    <skip />
     <string name="private_dns_broken" msgid="1984159464346556931">"Не ўдалося атрымаць доступ да прыватнага DNS-сервера"</string>
     <string name="wifi_limited_connection" msgid="1184778285475204682">"Абмежаваныя магчымасці падключэння"</string>
     <string name="wifi_status_no_internet" msgid="3799933875988829048">"Не падключана да інтэрнэту"</string>
diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml
index 94f78ad..3e6f77d 100644
--- a/packages/SettingsLib/res/values-bg/strings.xml
+++ b/packages/SettingsLib/res/values-bg/strings.xml
@@ -42,7 +42,8 @@
     <string name="connected_via_app" msgid="3532267661404276584">"Установена е връзка през <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"Мрежата е достъпна през „%1$s“"</string>
     <string name="tap_to_sign_up" msgid="5356397741063740395">"Докоснете, за да се регистрирате"</string>
-    <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Установена е връзка – няма достъп до интернет"</string>
+    <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+    <skip />
     <string name="private_dns_broken" msgid="1984159464346556931">"Не може да се осъществи достъп до частния DNS сървър"</string>
     <string name="wifi_limited_connection" msgid="1184778285475204682">"Ограничена връзка"</string>
     <string name="wifi_status_no_internet" msgid="3799933875988829048">"Няма връзка с интернет"</string>
diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml
index 51719160..7f6938a 100644
--- a/packages/SettingsLib/res/values-bn/strings.xml
+++ b/packages/SettingsLib/res/values-bn/strings.xml
@@ -42,7 +42,8 @@
     <string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g>-এর মাধ্যমে কানেক্ট করা আছে"</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"%1$s এর মাধ্যমে উপলব্ধ"</string>
     <string name="tap_to_sign_up" msgid="5356397741063740395">"সাইন-আপ করতে ট্যাপ করুন"</string>
-    <string name="wifi_connected_no_internet" msgid="2381729074310543563">"কানেক্ট, ইন্টারনেট নেই"</string>
+    <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+    <skip />
     <string name="private_dns_broken" msgid="1984159464346556931">"ব্যক্তিগত ডিএনএস সার্ভার অ্যাক্সেস করা যাবে না"</string>
     <string name="wifi_limited_connection" msgid="1184778285475204682">"সীমিত কানেকশন"</string>
     <string name="wifi_status_no_internet" msgid="3799933875988829048">"ইন্টারনেট কানেকশন নেই"</string>
diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml
index f74bcee..0563abd 100644
--- a/packages/SettingsLib/res/values-bs/strings.xml
+++ b/packages/SettingsLib/res/values-bs/strings.xml
@@ -42,7 +42,8 @@
     <string name="connected_via_app" msgid="3532267661404276584">"Povezano preko <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"Dostupan preko %1$s"</string>
     <string name="tap_to_sign_up" msgid="5356397741063740395">"Dodirnite za prijavu"</string>
-    <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Povezano, nema interneta"</string>
+    <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+    <skip />
     <string name="private_dns_broken" msgid="1984159464346556931">"Nije moguće pristupiti privatnom DNS serveru"</string>
     <string name="wifi_limited_connection" msgid="1184778285475204682">"Ograničena veza"</string>
     <string name="wifi_status_no_internet" msgid="3799933875988829048">"Nema internetske veze"</string>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index 1b23ecf..71b2c5d 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -42,7 +42,8 @@
     <string name="connected_via_app" msgid="3532267661404276584">"Connectat mitjançant <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"Disponible mitjançant %1$s"</string>
     <string name="tap_to_sign_up" msgid="5356397741063740395">"Toca per registrar-te"</string>
-    <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Connectada, sense Internet"</string>
+    <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+    <skip />
     <string name="private_dns_broken" msgid="1984159464346556931">"No es pot accedir al servidor DNS privat"</string>
     <string name="wifi_limited_connection" msgid="1184778285475204682">"Connexió limitada"</string>
     <string name="wifi_status_no_internet" msgid="3799933875988829048">"Sense connexió a Internet"</string>
diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
index 22603cc..a381772 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -42,7 +42,8 @@
     <string name="connected_via_app" msgid="3532267661404276584">"Připojeno přes <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"Dostupné prostřednictvím %1$s"</string>
     <string name="tap_to_sign_up" msgid="5356397741063740395">"Klepnutím se zaregistrujete"</string>
-    <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Připojeno, není k dispozici internet"</string>
+    <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+    <skip />
     <string name="private_dns_broken" msgid="1984159464346556931">"Nelze získat přístup k soukromému serveru DNS"</string>
     <string name="wifi_limited_connection" msgid="1184778285475204682">"Omezené připojení"</string>
     <string name="wifi_status_no_internet" msgid="3799933875988829048">"Nejste připojeni k internetu"</string>
diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
index bef1855..f6e8576 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -42,7 +42,8 @@
     <string name="connected_via_app" msgid="3532267661404276584">"Forbundet via <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"Tilgængelig via %1$s"</string>
     <string name="tap_to_sign_up" msgid="5356397741063740395">"Tryk for at registrere dig"</string>
-    <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Tilsluttet – intet internet"</string>
+    <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+    <skip />
     <string name="private_dns_broken" msgid="1984159464346556931">"Der er ikke adgang til den private DNS-server"</string>
     <string name="wifi_limited_connection" msgid="1184778285475204682">"Begrænset forbindelse"</string>
     <string name="wifi_status_no_internet" msgid="3799933875988829048">"Intet internet"</string>
diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml
index c8c97bd..99a0910 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -42,7 +42,8 @@
     <string name="connected_via_app" msgid="3532267661404276584">"Verbunden über <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"Verfügbar über %1$s"</string>
     <string name="tap_to_sign_up" msgid="5356397741063740395">"Zum Anmelden tippen"</string>
-    <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Verbunden, kein Internet"</string>
+    <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+    <skip />
     <string name="private_dns_broken" msgid="1984159464346556931">"Auf den privaten DNS-Server kann nicht zugegriffen werden"</string>
     <string name="wifi_limited_connection" msgid="1184778285475204682">"Eingeschränkte Verbindung"</string>
     <string name="wifi_status_no_internet" msgid="3799933875988829048">"Kein Internet"</string>
diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml
index 371075c..d37ea44 100644
--- a/packages/SettingsLib/res/values-el/strings.xml
+++ b/packages/SettingsLib/res/values-el/strings.xml
@@ -42,7 +42,8 @@
     <string name="connected_via_app" msgid="3532267661404276584">"Συνδέθηκε μέσω <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"Διαθέσιμο μέσω %1$s"</string>
     <string name="tap_to_sign_up" msgid="5356397741063740395">"Πατήστε για εγγραφή"</string>
-    <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Συνδέθηκε, χωρίς σύνδεση στο διαδίκτυο"</string>
+    <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+    <skip />
     <string name="private_dns_broken" msgid="1984159464346556931">"Δεν είναι δυνατή η πρόσβαση στον ιδιωτικό διακομιστή DNS."</string>
     <string name="wifi_limited_connection" msgid="1184778285475204682">"Περιορισμένη σύνδεση"</string>
     <string name="wifi_status_no_internet" msgid="3799933875988829048">"Δεν υπάρχει σύνδεση στο διαδίκτυο"</string>
diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml
index b314d17..429cd3e 100644
--- a/packages/SettingsLib/res/values-en-rAU/strings.xml
+++ b/packages/SettingsLib/res/values-en-rAU/strings.xml
@@ -42,7 +42,8 @@
     <string name="connected_via_app" msgid="3532267661404276584">"Connected via <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"Available via %1$s"</string>
     <string name="tap_to_sign_up" msgid="5356397741063740395">"Tap to sign up"</string>
-    <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Connected, no Internet"</string>
+    <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+    <skip />
     <string name="private_dns_broken" msgid="1984159464346556931">"Private DNS server cannot be accessed"</string>
     <string name="wifi_limited_connection" msgid="1184778285475204682">"Limited connection"</string>
     <string name="wifi_status_no_internet" msgid="3799933875988829048">"No Internet"</string>
diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml
index b314d17..429cd3e 100644
--- a/packages/SettingsLib/res/values-en-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-en-rCA/strings.xml
@@ -42,7 +42,8 @@
     <string name="connected_via_app" msgid="3532267661404276584">"Connected via <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"Available via %1$s"</string>
     <string name="tap_to_sign_up" msgid="5356397741063740395">"Tap to sign up"</string>
-    <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Connected, no Internet"</string>
+    <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+    <skip />
     <string name="private_dns_broken" msgid="1984159464346556931">"Private DNS server cannot be accessed"</string>
     <string name="wifi_limited_connection" msgid="1184778285475204682">"Limited connection"</string>
     <string name="wifi_status_no_internet" msgid="3799933875988829048">"No Internet"</string>
diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml
index b314d17..429cd3e 100644
--- a/packages/SettingsLib/res/values-en-rGB/strings.xml
+++ b/packages/SettingsLib/res/values-en-rGB/strings.xml
@@ -42,7 +42,8 @@
     <string name="connected_via_app" msgid="3532267661404276584">"Connected via <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"Available via %1$s"</string>
     <string name="tap_to_sign_up" msgid="5356397741063740395">"Tap to sign up"</string>
-    <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Connected, no Internet"</string>
+    <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+    <skip />
     <string name="private_dns_broken" msgid="1984159464346556931">"Private DNS server cannot be accessed"</string>
     <string name="wifi_limited_connection" msgid="1184778285475204682">"Limited connection"</string>
     <string name="wifi_status_no_internet" msgid="3799933875988829048">"No Internet"</string>
diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml
index b314d17..429cd3e 100644
--- a/packages/SettingsLib/res/values-en-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-en-rIN/strings.xml
@@ -42,7 +42,8 @@
     <string name="connected_via_app" msgid="3532267661404276584">"Connected via <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"Available via %1$s"</string>
     <string name="tap_to_sign_up" msgid="5356397741063740395">"Tap to sign up"</string>
-    <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Connected, no Internet"</string>
+    <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+    <skip />
     <string name="private_dns_broken" msgid="1984159464346556931">"Private DNS server cannot be accessed"</string>
     <string name="wifi_limited_connection" msgid="1184778285475204682">"Limited connection"</string>
     <string name="wifi_status_no_internet" msgid="3799933875988829048">"No Internet"</string>
diff --git a/packages/SettingsLib/res/values-en-rXC/strings.xml b/packages/SettingsLib/res/values-en-rXC/strings.xml
index 95944dc..1aa6cdb 100644
--- a/packages/SettingsLib/res/values-en-rXC/strings.xml
+++ b/packages/SettingsLib/res/values-en-rXC/strings.xml
@@ -42,7 +42,8 @@
     <string name="connected_via_app" msgid="3532267661404276584">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‏‎‎‎‏‎‎‎‎‎‏‎‏‎‎‏‎‎‎‏‎‏‎‏‏‏‎‎‏‎‎‏‎‏‎‎‏‏‎‎‏‏‎‏‎‎‎‎‎‎‏‏‏‎‏‏‎‏‎‎‎‎Connected via ‎‏‎‎‏‏‎<xliff:g id="NAME">%1$s</xliff:g>‎‏‎‎‏‏‏‎‎‏‎‎‏‎"</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‏‏‎‏‏‏‏‏‎‏‎‎‎‎‎‏‏‏‎‏‎‏‎‎‏‎‎‏‎‏‎‏‎‏‎‎‏‎‏‏‎‏‏‏‎‎‏‏‎‏‏‎‎‎‎‎‏‎‎‎‏‎‎Available via %1$s‎‏‎‎‏‎"</string>
     <string name="tap_to_sign_up" msgid="5356397741063740395">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‎‏‎‏‎‎‏‎‏‎‏‎‏‏‎‏‏‏‏‏‏‎‏‎‏‏‏‎‏‏‎‏‏‎‏‏‏‏‏‎‎‏‏‏‏‎‎‎‏‏‎‏‏‏‏‏‎‏‎‏‏‎Tap to sign up‎‏‎‎‏‎"</string>
-    <string name="wifi_connected_no_internet" msgid="2381729074310543563">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‎‎‎‎‏‎‎‎‎‏‏‎‏‏‎‎‏‏‎‎‏‏‏‏‏‏‎‎‏‎‏‏‎‎‎‎‏‎‏‏‏‏‏‏‏‏‏‏‏‏‎‎‎‏‏‎‎‏‎‏‏‎Connected, no internet‎‏‎‎‏‎"</string>
+    <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+    <skip />
     <string name="private_dns_broken" msgid="1984159464346556931">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‏‏‏‎‏‏‏‎‎‎‏‎‎‏‎‎‏‎‎‏‏‎‏‎‎‎‎‏‎‏‎‎‏‏‎‎‏‎‎‎‎‏‏‎‏‏‎‎‎‏‏‎‏‎‎‎‎‎‎‎‏‏‎Private DNS server cannot be accessed‎‏‎‎‏‎"</string>
     <string name="wifi_limited_connection" msgid="1184778285475204682">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‏‏‎‎‎‎‎‏‏‏‎‎‎‏‎‎‏‎‏‏‎‏‏‎‎‏‎‎‏‎‏‏‎‎‏‎‏‏‎‏‎‏‏‎‎‎‏‎‎‎‏‏‏‎‎‏‎‎‏‎‏‎‎Limited connection‎‏‎‎‏‎"</string>
     <string name="wifi_status_no_internet" msgid="3799933875988829048">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‏‎‏‎‎‏‎‏‏‏‏‎‎‎‎‎‏‎‎‏‏‏‎‏‏‏‎‏‎‎‎‏‏‎‎‎‎‎‏‎‎‏‎‎‏‏‎‎‏‎‎‏‏‎‏‏‏‏‎‎‎‎No internet‎‏‎‎‏‎"</string>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index c6dfdd3..7d28a32 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -42,7 +42,8 @@
     <string name="connected_via_app" msgid="3532267661404276584">"Conexión a través de <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"Disponible a través de %1$s"</string>
     <string name="tap_to_sign_up" msgid="5356397741063740395">"Presiona para registrarte"</string>
-    <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Conectado pero sin conexión a Internet"</string>
+    <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+    <skip />
     <string name="private_dns_broken" msgid="1984159464346556931">"No se puede acceder al servidor DNS privado"</string>
     <string name="wifi_limited_connection" msgid="1184778285475204682">"Conexión limitada"</string>
     <string name="wifi_status_no_internet" msgid="3799933875988829048">"Sin Internet"</string>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index ba4a9ff..b35696f5 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -42,7 +42,8 @@
     <string name="connected_via_app" msgid="3532267661404276584">"Conectado a través de <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"Disponible a través de %1$s"</string>
     <string name="tap_to_sign_up" msgid="5356397741063740395">"Toca para registrarte"</string>
-    <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Conexión sin Internet"</string>
+    <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+    <skip />
     <string name="private_dns_broken" msgid="1984159464346556931">"No se ha podido acceder al servidor DNS privado"</string>
     <string name="wifi_limited_connection" msgid="1184778285475204682">"Conexión limitada"</string>
     <string name="wifi_status_no_internet" msgid="3799933875988829048">"Sin Internet"</string>
diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml
index df3b792..b6c112b 100644
--- a/packages/SettingsLib/res/values-et/strings.xml
+++ b/packages/SettingsLib/res/values-et/strings.xml
@@ -42,7 +42,8 @@
     <string name="connected_via_app" msgid="3532267661404276584">"Ühendatud võrgu <xliff:g id="NAME">%1$s</xliff:g> kaudu"</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"Saadaval üksuse %1$s kaudu"</string>
     <string name="tap_to_sign_up" msgid="5356397741063740395">"Puudutage registreerumiseks"</string>
-    <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Ühendatud, Interneti-ühendus puudub"</string>
+    <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+    <skip />
     <string name="private_dns_broken" msgid="1984159464346556931">"Privaatsele DNS-serverile ei pääse juurde"</string>
     <string name="wifi_limited_connection" msgid="1184778285475204682">"Piiratud ühendus"</string>
     <string name="wifi_status_no_internet" msgid="3799933875988829048">"Interneti-ühendus puudub"</string>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index fcb320f..4fd9add 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -42,7 +42,8 @@
     <string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g> bidez konektatuta"</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"%1$s bidez erabilgarri"</string>
     <string name="tap_to_sign_up" msgid="5356397741063740395">"Sakatu erregistratzeko"</string>
-    <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Konektatuta; ezin da atzitu Internet"</string>
+    <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+    <skip />
     <string name="private_dns_broken" msgid="1984159464346556931">"Ezin da atzitu DNS zerbitzari pribatua"</string>
     <string name="wifi_limited_connection" msgid="1184778285475204682">"Konexio mugatua"</string>
     <string name="wifi_status_no_internet" msgid="3799933875988829048">"Ez dago Interneteko konexiorik"</string>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index 261a438..32a98be 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -42,7 +42,8 @@
     <string name="connected_via_app" msgid="3532267661404276584">"متصل شده ازطریق <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"‏در دسترس از طریق %1$s"</string>
     <string name="tap_to_sign_up" msgid="5356397741063740395">"برای ثبت‌نام ضربه بزنید"</string>
-    <string name="wifi_connected_no_internet" msgid="2381729074310543563">"متصل، بدون اینترنت"</string>
+    <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+    <skip />
     <string name="private_dns_broken" msgid="1984159464346556931">"‏سرور DNS خصوصی قابل دسترسی نیست"</string>
     <string name="wifi_limited_connection" msgid="1184778285475204682">"اتصال محدود"</string>
     <string name="wifi_status_no_internet" msgid="3799933875988829048">"عدم دسترسی به اینترنت"</string>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index 4ccf430..84c5372 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -42,7 +42,8 @@
     <string name="connected_via_app" msgid="3532267661404276584">"Yhdistetty (<xliff:g id="NAME">%1$s</xliff:g>)"</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"Käytettävissä seuraavan kautta: %1$s"</string>
     <string name="tap_to_sign_up" msgid="5356397741063740395">"Rekisteröidy napauttamalla"</string>
-    <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Yhdistetty, ei internetyhteyttä"</string>
+    <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+    <skip />
     <string name="private_dns_broken" msgid="1984159464346556931">"Ei pääsyä yksityiselle DNS-palvelimelle"</string>
     <string name="wifi_limited_connection" msgid="1184778285475204682">"Rajallinen yhteys"</string>
     <string name="wifi_status_no_internet" msgid="3799933875988829048">"Ei internetyhteyttä"</string>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index 84a9797..995eab6 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -42,7 +42,8 @@
     <string name="connected_via_app" msgid="3532267661404276584">"Connecté sur le réseau <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"Accessible par %1$s"</string>
     <string name="tap_to_sign_up" msgid="5356397741063740395">"Toucher pour vous connecter"</string>
-    <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Connecté, aucun accès à Internet"</string>
+    <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+    <skip />
     <string name="private_dns_broken" msgid="1984159464346556931">"Impossible d\'accéder au serveur DNS privé"</string>
     <string name="wifi_limited_connection" msgid="1184778285475204682">"Connexion limitée"</string>
     <string name="wifi_status_no_internet" msgid="3799933875988829048">"Aucune connexion Internet"</string>
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index 030a7f9..2afacab 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -42,7 +42,8 @@
     <string name="connected_via_app" msgid="3532267661404276584">"Connecté via <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"Disponible via %1$s"</string>
     <string name="tap_to_sign_up" msgid="5356397741063740395">"Appuyez ici pour vous connecter"</string>
-    <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Connecté, aucun accès à Internet"</string>
+    <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+    <skip />
     <string name="private_dns_broken" msgid="1984159464346556931">"Impossible d\'accéder au serveur DNS privé"</string>
     <string name="wifi_limited_connection" msgid="1184778285475204682">"Connexion limitée"</string>
     <string name="wifi_status_no_internet" msgid="3799933875988829048">"Aucun accès à Internet"</string>
@@ -57,7 +58,7 @@
     <string name="osu_sign_up_complete" msgid="7640183358878916847">"Inscription terminée. Connexion…"</string>
     <string name="speed_label_very_slow" msgid="8526005255731597666">"Très lente"</string>
     <string name="speed_label_slow" msgid="6069917670665664161">"Lente"</string>
-    <string name="speed_label_okay" msgid="1253594383880810424">"Correct"</string>
+    <string name="speed_label_okay" msgid="1253594383880810424">"Correcte"</string>
     <string name="speed_label_medium" msgid="9078405312828606976">"Moyenne"</string>
     <string name="speed_label_fast" msgid="2677719134596044051">"Élevée"</string>
     <string name="speed_label_very_fast" msgid="8215718029533182439">"Très rapide"</string>
diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml
index af033cf..a106e60 100644
--- a/packages/SettingsLib/res/values-gl/strings.xml
+++ b/packages/SettingsLib/res/values-gl/strings.xml
@@ -42,7 +42,8 @@
     <string name="connected_via_app" msgid="3532267661404276584">"Wifi conectada a través de <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"Dispoñible a través de %1$s"</string>
     <string name="tap_to_sign_up" msgid="5356397741063740395">"Toca para rexistrarte"</string>
-    <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Conexión sen Internet"</string>
+    <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+    <skip />
     <string name="private_dns_broken" msgid="1984159464346556931">"Non se puido acceder ao servidor DNS privado"</string>
     <string name="wifi_limited_connection" msgid="1184778285475204682">"Pouca conexión"</string>
     <string name="wifi_status_no_internet" msgid="3799933875988829048">"Non hai conexión a Internet"</string>
@@ -316,7 +317,7 @@
     <string name="usb_audio_disable_routing" msgid="3367656923544254975">"Desactivar encamiñamento audio USB"</string>
     <string name="usb_audio_disable_routing_summary" msgid="8768242894849534699">"Desactiva o encamiñamento automático a periféricos de audio USB"</string>
     <string name="debug_layout" msgid="1659216803043339741">"Mostrar límites de deseño"</string>
-    <string name="debug_layout_summary" msgid="8825829038287321978">"Mostra os límites dos clips, as marxes, etc."</string>
+    <string name="debug_layout_summary" msgid="8825829038287321978">"Mostra os límites dos clips, as marxes etc."</string>
     <string name="force_rtl_layout_all_locales" msgid="8690762598501599796">"Forzar dirección do deseño RTL"</string>
     <string name="force_rtl_layout_all_locales_summary" msgid="6663016859517239880">"Forza a dirección de pantalla a RTL (dereita a esquerda) para todas as configuración rexionais"</string>
     <string name="force_msaa" msgid="4081288296137775550">"Forzar MSAA 4x"</string>
diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml
index 87fd876..5f2d5cd 100644
--- a/packages/SettingsLib/res/values-gu/strings.xml
+++ b/packages/SettingsLib/res/values-gu/strings.xml
@@ -42,7 +42,8 @@
     <string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g> દ્વારા કનેક્ટ થયેલ"</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"%1$s દ્વારા ઉપલબ્ધ"</string>
     <string name="tap_to_sign_up" msgid="5356397741063740395">"સાઇન અપ કરવા માટે ટૅપ કરો"</string>
-    <string name="wifi_connected_no_internet" msgid="2381729074310543563">"કનેક્ટ કર્યું, કોઈ ઇન્ટરનેટ નથી"</string>
+    <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+    <skip />
     <string name="private_dns_broken" msgid="1984159464346556931">"ખાનગી DNS સર્વર ઍક્સેસ કરી શકાતા નથી"</string>
     <string name="wifi_limited_connection" msgid="1184778285475204682">"મર્યાદિત કનેક્શન"</string>
     <string name="wifi_status_no_internet" msgid="3799933875988829048">"ઇન્ટરનેટ ઍક્સેસ નથી"</string>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index 02b5c96..d60eead 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -42,7 +42,8 @@
     <string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g> के ज़रिए कनेक्ट किया गया"</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"%1$s के द्वारा उपलब्ध"</string>
     <string name="tap_to_sign_up" msgid="5356397741063740395">"साइन अप करने के लिए टैप करें"</string>
-    <string name="wifi_connected_no_internet" msgid="2381729074310543563">"कनेक्ट हो गया है, लेकिन इंटरनेट नहीं है"</string>
+    <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+    <skip />
     <string name="private_dns_broken" msgid="1984159464346556931">"निजी डीएनएस सर्वर को ऐक्सेस नहीं किया जा सकता"</string>
     <string name="wifi_limited_connection" msgid="1184778285475204682">"सीमित कनेक्शन"</string>
     <string name="wifi_status_no_internet" msgid="3799933875988829048">"इंटरनेट कनेक्शन नहीं है"</string>
diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml
index 598cfe2..28e3460 100644
--- a/packages/SettingsLib/res/values-hr/strings.xml
+++ b/packages/SettingsLib/res/values-hr/strings.xml
@@ -42,7 +42,8 @@
     <string name="connected_via_app" msgid="3532267661404276584">"Povezan putem mreže <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"Dostupno putem %1$s"</string>
     <string name="tap_to_sign_up" msgid="5356397741063740395">"Dodirnite da biste se registrirali"</string>
-    <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Povezano, bez interneta"</string>
+    <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+    <skip />
     <string name="private_dns_broken" msgid="1984159464346556931">"Nije moguće pristupiti privatnom DNS poslužitelju"</string>
     <string name="wifi_limited_connection" msgid="1184778285475204682">"Ograničena veza"</string>
     <string name="wifi_status_no_internet" msgid="3799933875988829048">"Nema interneta"</string>
diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml
index d970c73..22e03a7 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -42,7 +42,8 @@
     <string name="connected_via_app" msgid="3532267661404276584">"Kapcsolódva a következőn keresztül: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"Elérhető a következőn keresztül: %1$s"</string>
     <string name="tap_to_sign_up" msgid="5356397741063740395">"Koppintson a regisztrációhoz"</string>
-    <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Csatlakozva, nincs internet-hozzáférés"</string>
+    <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+    <skip />
     <string name="private_dns_broken" msgid="1984159464346556931">"A privát DNS-kiszolgálóhoz nem lehet hozzáférni"</string>
     <string name="wifi_limited_connection" msgid="1184778285475204682">"Korlátozott kapcsolat"</string>
     <string name="wifi_status_no_internet" msgid="3799933875988829048">"Nincs internetkapcsolat"</string>
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index 9086934..ecb615a 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -42,7 +42,8 @@
     <string name="connected_via_app" msgid="3532267661404276584">"Միացված է <xliff:g id="NAME">%1$s</xliff:g>-ի միջոցով"</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"Հասանելի է %1$s-ի միջոցով"</string>
     <string name="tap_to_sign_up" msgid="5356397741063740395">"Հպեք՝ գրանցվելու համար"</string>
-    <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Միացված է, սակայն ինտերնետ կապ չկա"</string>
+    <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+    <skip />
     <string name="private_dns_broken" msgid="1984159464346556931">"Մասնավոր DNS սերվերն անհասանելի է"</string>
     <string name="wifi_limited_connection" msgid="1184778285475204682">"Սահմանափակ կապ"</string>
     <string name="wifi_status_no_internet" msgid="3799933875988829048">"Ինտերնետ կապ չկա"</string>
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index 3c1504c..cfbda04 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -42,7 +42,8 @@
     <string name="connected_via_app" msgid="3532267661404276584">"Tersambung melalui <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"Tersedia melalui %1$s"</string>
     <string name="tap_to_sign_up" msgid="5356397741063740395">"Ketuk untuk mendaftar"</string>
-    <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Tersambung, tidak ada internet"</string>
+    <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+    <skip />
     <string name="private_dns_broken" msgid="1984159464346556931">"Server DNS pribadi tidak dapat diakses"</string>
     <string name="wifi_limited_connection" msgid="1184778285475204682">"Koneksi terbatas"</string>
     <string name="wifi_status_no_internet" msgid="3799933875988829048">"Tidak ada internet"</string>
diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml
index 438e900..ba4c009 100644
--- a/packages/SettingsLib/res/values-is/strings.xml
+++ b/packages/SettingsLib/res/values-is/strings.xml
@@ -42,7 +42,8 @@
     <string name="connected_via_app" msgid="3532267661404276584">"Tenging í gegnum <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"Í boði í gegnum %1$s"</string>
     <string name="tap_to_sign_up" msgid="5356397741063740395">"Ýttu til að skrá þig"</string>
-    <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Tengt, enginn netaðgangur"</string>
+    <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+    <skip />
     <string name="private_dns_broken" msgid="1984159464346556931">"Ekki næst í DNS-einkaþjón"</string>
     <string name="wifi_limited_connection" msgid="1184778285475204682">"Takmörkuð tenging"</string>
     <string name="wifi_status_no_internet" msgid="3799933875988829048">"Engin nettenging"</string>
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index 114b33b..50e5777 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -42,7 +42,8 @@
     <string name="connected_via_app" msgid="3532267661404276584">"Connesso tramite <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"Disponibile tramite %1$s"</string>
     <string name="tap_to_sign_up" msgid="5356397741063740395">"Tocca per registrarti"</string>
-    <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Connesso, senza Internet"</string>
+    <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+    <skip />
     <string name="private_dns_broken" msgid="1984159464346556931">"Non è possibile accedere al server DNS privato"</string>
     <string name="wifi_limited_connection" msgid="1184778285475204682">"Connessione limitata"</string>
     <string name="wifi_status_no_internet" msgid="3799933875988829048">"Nessuna connessione a Internet"</string>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index 62085a8..9f1e457 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -42,7 +42,8 @@
     <string name="connected_via_app" msgid="3532267661404276584">"מחוברת באמצעות <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"‏זמינה דרך %1$s"</string>
     <string name="tap_to_sign_up" msgid="5356397741063740395">"יש להקיש כדי להירשם"</string>
-    <string name="wifi_connected_no_internet" msgid="2381729074310543563">"מחובר. אין אינטרנט"</string>
+    <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+    <skip />
     <string name="private_dns_broken" msgid="1984159464346556931">"‏לא ניתן לגשת לשרת DNS הפרטי"</string>
     <string name="wifi_limited_connection" msgid="1184778285475204682">"חיבור מוגבל"</string>
     <string name="wifi_status_no_internet" msgid="3799933875988829048">"אין אינטרנט"</string>
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index 28b98ee..47020ed 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -42,7 +42,8 @@
     <string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g> で接続しました"</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"%1$s経由で使用可能"</string>
     <string name="tap_to_sign_up" msgid="5356397741063740395">"タップして登録してください"</string>
-    <string name="wifi_connected_no_internet" msgid="2381729074310543563">"接続済み、インターネット接続なし"</string>
+    <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+    <skip />
     <string name="private_dns_broken" msgid="1984159464346556931">"プライベート DNS サーバーにアクセスできません"</string>
     <string name="wifi_limited_connection" msgid="1184778285475204682">"接続が制限されています"</string>
     <string name="wifi_status_no_internet" msgid="3799933875988829048">"インターネット未接続"</string>
diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml
index 6f5d0b3..116488d 100644
--- a/packages/SettingsLib/res/values-ka/strings.xml
+++ b/packages/SettingsLib/res/values-ka/strings.xml
@@ -42,7 +42,8 @@
     <string name="connected_via_app" msgid="3532267661404276584">"დაკავშირებულია <xliff:g id="NAME">%1$s</xliff:g>-ით"</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"ხელმისაწვდომია %1$s-ით"</string>
     <string name="tap_to_sign_up" msgid="5356397741063740395">"შეეხეთ რეგისტრაციისთვის"</string>
-    <string name="wifi_connected_no_internet" msgid="2381729074310543563">"დაკავშირებულია, ინტერნეტის გარეშე"</string>
+    <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+    <skip />
     <string name="private_dns_broken" msgid="1984159464346556931">"პირად DNS სერვერზე წვდომა შეუძლებელია"</string>
     <string name="wifi_limited_connection" msgid="1184778285475204682">"შეზღუდული კავშირი"</string>
     <string name="wifi_status_no_internet" msgid="3799933875988829048">"ინტერნეტ-კავშირი არ არის"</string>
diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml
index 3fe426e..3c4fdb7 100644
--- a/packages/SettingsLib/res/values-kk/strings.xml
+++ b/packages/SettingsLib/res/values-kk/strings.xml
@@ -42,7 +42,8 @@
     <string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g> арқылы жалғанған"</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"%1$s арқылы қолжетімді"</string>
     <string name="tap_to_sign_up" msgid="5356397741063740395">"Тіркелу үшін түртіңіз."</string>
-    <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Қосылған, интернет жоқ"</string>
+    <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+    <skip />
     <string name="private_dns_broken" msgid="1984159464346556931">"Жеке DNS серверіне кіру мүмкін емес."</string>
     <string name="wifi_limited_connection" msgid="1184778285475204682">"Шектеулі байланыс"</string>
     <string name="wifi_status_no_internet" msgid="3799933875988829048">"Интернетпен байланыс жоқ"</string>
diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml
index 24bfa35..87a4f19 100644
--- a/packages/SettingsLib/res/values-km/strings.xml
+++ b/packages/SettingsLib/res/values-km/strings.xml
@@ -42,7 +42,8 @@
     <string name="connected_via_app" msgid="3532267661404276584">"ភ្ជាប់​តាម <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"មានតាមរយៈ %1$s"</string>
     <string name="tap_to_sign_up" msgid="5356397741063740395">"ចុច​ដើម្បី​ចុះឈ្មោះ"</string>
-    <string name="wifi_connected_no_internet" msgid="2381729074310543563">"បាន​ភ្ជាប់ ប៉ុន្តែ​គ្មាន​អ៊ីនធឺណិត​ទេ"</string>
+    <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+    <skip />
     <string name="private_dns_broken" msgid="1984159464346556931">"មិនអាច​ចូលប្រើ​ម៉ាស៊ីនមេ DNS ឯកជន​បានទេ"</string>
     <string name="wifi_limited_connection" msgid="1184778285475204682">"ការតភ្ជាប់មានកម្រិត"</string>
     <string name="wifi_status_no_internet" msgid="3799933875988829048">"គ្មាន​អ៊ីនធឺណិតទេ"</string>
diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml
index 0699bbc..144dddb 100644
--- a/packages/SettingsLib/res/values-kn/strings.xml
+++ b/packages/SettingsLib/res/values-kn/strings.xml
@@ -42,7 +42,8 @@
     <string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g> ಆ್ಯಪ್ ಮೂಲಕ ಸಂಪರ್ಕಿಸಲಾಗಿದೆ"</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"%1$s ಮೂಲಕ ಲಭ್ಯವಿದೆ"</string>
     <string name="tap_to_sign_up" msgid="5356397741063740395">"ಸೈನ್ ಅಪ್ ಮಾಡಲು ಟ್ಯಾಪ್‌ ಮಾಡಿ"</string>
-    <string name="wifi_connected_no_internet" msgid="2381729074310543563">"ಸಂಪರ್ಕಪಡಿಸಲಾಗಿದೆ, ಇಂಟರ್ನೆಟ್ ಇಲ್ಲ"</string>
+    <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+    <skip />
     <string name="private_dns_broken" msgid="1984159464346556931">"ಖಾಸಗಿ DNS ಸರ್ವರ್ ಅನ್ನು ಪ್ರವೇಶಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ"</string>
     <string name="wifi_limited_connection" msgid="1184778285475204682">"ಸೀಮಿತ ಸಂಪರ್ಕ"</string>
     <string name="wifi_status_no_internet" msgid="3799933875988829048">"ಇಂಟರ್ನೆಟ್ ಇಲ್ಲ"</string>
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index 25e9cfe..df9f21d 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -42,7 +42,8 @@
     <string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g>을(를) 통해 연결됨"</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"%1$s을(를) 통해 사용 가능"</string>
     <string name="tap_to_sign_up" msgid="5356397741063740395">"탭하여 가입"</string>
-    <string name="wifi_connected_no_internet" msgid="2381729074310543563">"연결됨, 인터넷 사용 불가"</string>
+    <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+    <skip />
     <string name="private_dns_broken" msgid="1984159464346556931">"비공개 DNS 서버에 액세스할 수 없습니다."</string>
     <string name="wifi_limited_connection" msgid="1184778285475204682">"제한된 연결"</string>
     <string name="wifi_status_no_internet" msgid="3799933875988829048">"인터넷 연결 없음"</string>
diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml
index 3dfce1e..13c4144 100644
--- a/packages/SettingsLib/res/values-ky/strings.xml
+++ b/packages/SettingsLib/res/values-ky/strings.xml
@@ -42,7 +42,8 @@
     <string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g> аркылуу туташты"</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"%1$s аркылуу жеткиликтүү"</string>
     <string name="tap_to_sign_up" msgid="5356397741063740395">"Катталуу үчүн таптап коюңуз"</string>
-    <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Туташып турат, Интернет жок"</string>
+    <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+    <skip />
     <string name="private_dns_broken" msgid="1984159464346556931">"Жеке DNS сервери жеткиликсиз"</string>
     <string name="wifi_limited_connection" msgid="1184778285475204682">"Байланыш чектелген"</string>
     <string name="wifi_status_no_internet" msgid="3799933875988829048">"Интернет жок"</string>
diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml
index 48e5093..48224f4 100644
--- a/packages/SettingsLib/res/values-lo/strings.xml
+++ b/packages/SettingsLib/res/values-lo/strings.xml
@@ -42,7 +42,8 @@
     <string name="connected_via_app" msgid="3532267661404276584">"ເຊື່ອມ​ຕໍ່​ຜ່ານ <xliff:g id="NAME">%1$s</xliff:g> ແລ້ວ"</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"ມີ​ໃຫ້​ຜ່ານ %1$s"</string>
     <string name="tap_to_sign_up" msgid="5356397741063740395">"ແຕະເພື່ອສະໝັກ"</string>
-    <string name="wifi_connected_no_internet" msgid="2381729074310543563">"ເຊື່ອມຕໍ່ແລ້ວ, ບໍ່ມີອິນເຕີເນັດ"</string>
+    <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+    <skip />
     <string name="private_dns_broken" msgid="1984159464346556931">"ບໍ່ສາມາດເຂົ້າເຖິງເຊີບເວີ DNS ສ່ວນຕົວໄດ້"</string>
     <string name="wifi_limited_connection" msgid="1184778285475204682">"ການເຊື່ອມຕໍ່ຈຳກັດ"</string>
     <string name="wifi_status_no_internet" msgid="3799933875988829048">"ບໍ່ມີອິນເຕີເນັດ"</string>
diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml
index 8b3fbad..d080334 100644
--- a/packages/SettingsLib/res/values-lt/strings.xml
+++ b/packages/SettingsLib/res/values-lt/strings.xml
@@ -42,7 +42,8 @@
     <string name="connected_via_app" msgid="3532267661404276584">"Prisijungta naudojant programą „<xliff:g id="NAME">%1$s</xliff:g>“"</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"Pasiekiama naudojant „%1$s“"</string>
     <string name="tap_to_sign_up" msgid="5356397741063740395">"Palieskite, kad prisiregistruotumėte"</string>
-    <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Prisijungta, nėra interneto"</string>
+    <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+    <skip />
     <string name="private_dns_broken" msgid="1984159464346556931">"Privataus DNS serverio negalima pasiekti"</string>
     <string name="wifi_limited_connection" msgid="1184778285475204682">"Ribotas ryšys"</string>
     <string name="wifi_status_no_internet" msgid="3799933875988829048">"Nėra interneto ryšio"</string>
diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml
index 4fc5b22..e13a50d 100644
--- a/packages/SettingsLib/res/values-lv/strings.xml
+++ b/packages/SettingsLib/res/values-lv/strings.xml
@@ -42,7 +42,8 @@
     <string name="connected_via_app" msgid="3532267661404276584">"Savienojums ar <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"Pieejams, izmantojot %1$s"</string>
     <string name="tap_to_sign_up" msgid="5356397741063740395">"Pieskarieties, lai reģistrētos"</string>
-    <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Savienojums izveidots, nav piekļuves internetam"</string>
+    <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+    <skip />
     <string name="private_dns_broken" msgid="1984159464346556931">"Nevar piekļūt privātam DNS serverim."</string>
     <string name="wifi_limited_connection" msgid="1184778285475204682">"Ierobežots savienojums"</string>
     <string name="wifi_status_no_internet" msgid="3799933875988829048">"Nav piekļuves internetam"</string>
diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml
index 288e526..d027ffe 100644
--- a/packages/SettingsLib/res/values-mk/strings.xml
+++ b/packages/SettingsLib/res/values-mk/strings.xml
@@ -42,7 +42,8 @@
     <string name="connected_via_app" msgid="3532267661404276584">"Поврзано преку <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"Достапно преку %1$s"</string>
     <string name="tap_to_sign_up" msgid="5356397741063740395">"Допрете за да се регистрирате"</string>
-    <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Поврзана, нема интернет"</string>
+    <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+    <skip />
     <string name="private_dns_broken" msgid="1984159464346556931">"Не може да се пристапи до приватниот DNS-сервер"</string>
     <string name="wifi_limited_connection" msgid="1184778285475204682">"Ограничена врска"</string>
     <string name="wifi_status_no_internet" msgid="3799933875988829048">"Нема интернет"</string>
diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml
index e775297..e2a3d9f 100644
--- a/packages/SettingsLib/res/values-ml/strings.xml
+++ b/packages/SettingsLib/res/values-ml/strings.xml
@@ -42,7 +42,8 @@
     <string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g> മുഖേന കണക്‌റ്റ് ചെയ്‌തു"</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"%1$s വഴി ലഭ്യം"</string>
     <string name="tap_to_sign_up" msgid="5356397741063740395">"സൈൻ അപ്പ് ചെയ്യാൻ ടാപ്പ് ചെയ്യുക"</string>
-    <string name="wifi_connected_no_internet" msgid="2381729074310543563">"കണക്റ്റ് ചെയ്‌തു, ഇന്റർനെറ്റ് ഇല്ല"</string>
+    <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+    <skip />
     <string name="private_dns_broken" msgid="1984159464346556931">"സ്വകാര്യ DNS സെർവർ ആക്‌സസ് ചെയ്യാനാവില്ല"</string>
     <string name="wifi_limited_connection" msgid="1184778285475204682">"പരിമിത കണക്‌ഷൻ"</string>
     <string name="wifi_status_no_internet" msgid="3799933875988829048">"ഇന്റർനെറ്റ് ഇല്ല"</string>
diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml
index ba69f9b..dd08c9e 100644
--- a/packages/SettingsLib/res/values-mn/strings.xml
+++ b/packages/SettingsLib/res/values-mn/strings.xml
@@ -42,7 +42,8 @@
     <string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g>-р холбогдсон"</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"%1$s-р боломжтой"</string>
     <string name="tap_to_sign_up" msgid="5356397741063740395">"Бүртгүүлэхийн тулд товшино уу"</string>
-    <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Холбогдсон хэдий ч интернет алга"</string>
+    <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+    <skip />
     <string name="private_dns_broken" msgid="1984159464346556931">"Хувийн DNS серверт хандах боломжгүй байна"</string>
     <string name="wifi_limited_connection" msgid="1184778285475204682">"Хязгаарлагдмал холболт"</string>
     <string name="wifi_status_no_internet" msgid="3799933875988829048">"Интернэт алга"</string>
diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index 1930cdf..fb7cd1f 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -42,7 +42,8 @@
     <string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g> द्वारे कनेक्ट केले"</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"%1$s द्वारे उपलब्‍ध"</string>
     <string name="tap_to_sign_up" msgid="5356397741063740395">"साइन अप करण्यासाठी टॅप करा"</string>
-    <string name="wifi_connected_no_internet" msgid="2381729074310543563">"कनेक्‍ट केले, इंटरनेट नाही"</string>
+    <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+    <skip />
     <string name="private_dns_broken" msgid="1984159464346556931">"खाजगी DNS सर्व्हर ॲक्सेस करू शकत नाही"</string>
     <string name="wifi_limited_connection" msgid="1184778285475204682">"मर्यादित कनेक्शन"</string>
     <string name="wifi_status_no_internet" msgid="3799933875988829048">"इंटरनेट नाही"</string>
diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml
index e493188..72b15ff 100644
--- a/packages/SettingsLib/res/values-ms/strings.xml
+++ b/packages/SettingsLib/res/values-ms/strings.xml
@@ -42,7 +42,8 @@
     <string name="connected_via_app" msgid="3532267661404276584">"Disambungkan melalui <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"Tersedia melalui %1$s"</string>
     <string name="tap_to_sign_up" msgid="5356397741063740395">"Ketik untuk daftar"</string>
-    <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Disambungkan, tiada Internet"</string>
+    <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+    <skip />
     <string name="private_dns_broken" msgid="1984159464346556931">"Pelayan DNS peribadi tidak boleh diakses"</string>
     <string name="wifi_limited_connection" msgid="1184778285475204682">"Sambungan terhad"</string>
     <string name="wifi_status_no_internet" msgid="3799933875988829048">"Tiada Internet"</string>
diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml
index 2c4b32c..24017ec 100644
--- a/packages/SettingsLib/res/values-my/strings.xml
+++ b/packages/SettingsLib/res/values-my/strings.xml
@@ -42,7 +42,8 @@
     <string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g> မှတစ်ဆင့် ချိတ်ဆက်ထားသည်"</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"%1$s မှတစ်ဆင့်ရနိုင်သည်"</string>
     <string name="tap_to_sign_up" msgid="5356397741063740395">"အကောင့်ဖွင့်ရန် တို့ပါ"</string>
-    <string name="wifi_connected_no_internet" msgid="2381729074310543563">"ချိတ်ဆက်ထားသည်၊ အင်တာနက်မရှိ"</string>
+    <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+    <skip />
     <string name="private_dns_broken" msgid="1984159464346556931">"သီးသန့် ဒီအန်အက်စ် (DNS) ဆာဗာကို သုံး၍မရပါ။"</string>
     <string name="wifi_limited_connection" msgid="1184778285475204682">"ချိတ်ဆက်မှု ကန့်သတ်ထားသည်"</string>
     <string name="wifi_status_no_internet" msgid="3799933875988829048">"အင်တာနက် မရှိပါ"</string>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index 093c06f..aca13a7 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -42,7 +42,8 @@
     <string name="connected_via_app" msgid="3532267661404276584">"Tilkoblet via <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"Tilgjengelig via %1$s"</string>
     <string name="tap_to_sign_up" msgid="5356397741063740395">"Trykk for å registrere deg"</string>
-    <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Tilkoblet – ingen Internett-tilgang"</string>
+    <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+    <skip />
     <string name="private_dns_broken" msgid="1984159464346556931">"Den private DNS-tjeneren kan ikke nås"</string>
     <string name="wifi_limited_connection" msgid="1184778285475204682">"Begrenset tilkobling"</string>
     <string name="wifi_status_no_internet" msgid="3799933875988829048">"Ingen internettilkobling"</string>
diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml
index fb8b737..b2c7518 100644
--- a/packages/SettingsLib/res/values-ne/strings.xml
+++ b/packages/SettingsLib/res/values-ne/strings.xml
@@ -42,7 +42,8 @@
     <string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g> मार्फत जडान गरिएको"</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"%1$s मार्फत उपलब्ध"</string>
     <string name="tap_to_sign_up" msgid="5356397741063740395">"साइन अप गर्न ट्याप गर्नुहोस्"</string>
-    <string name="wifi_connected_no_internet" msgid="2381729074310543563">"जडान गरियो तर इन्टरनेट छैन"</string>
+    <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+    <skip />
     <string name="private_dns_broken" msgid="1984159464346556931">"निजी DNS सर्भरमाथि पहुँच प्राप्त गर्न सकिँदैन"</string>
     <string name="wifi_limited_connection" msgid="1184778285475204682">"सीमित जडान"</string>
     <string name="wifi_status_no_internet" msgid="3799933875988829048">"इन्टरनेटमाथिको पहुँच छैन"</string>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index 267dab4..c74683b 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -42,7 +42,8 @@
     <string name="connected_via_app" msgid="3532267661404276584">"Verbonden via <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"Beschikbaar via %1$s"</string>
     <string name="tap_to_sign_up" msgid="5356397741063740395">"Tik om aan te melden"</string>
-    <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Verbonden, geen internet"</string>
+    <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+    <skip />
     <string name="private_dns_broken" msgid="1984159464346556931">"Geen toegang tot privé-DNS-server"</string>
     <string name="wifi_limited_connection" msgid="1184778285475204682">"Beperkte verbinding"</string>
     <string name="wifi_status_no_internet" msgid="3799933875988829048">"Geen internet"</string>
diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml
index d8ae3bf..704fc42 100644
--- a/packages/SettingsLib/res/values-or/strings.xml
+++ b/packages/SettingsLib/res/values-or/strings.xml
@@ -42,7 +42,8 @@
     <string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g> ଦ୍ବାରା ସଂଯୋଗ କରାଯାଇଛି"</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"%1$s ମାଧ୍ୟମରେ ଉପଲବ୍ଧ"</string>
     <string name="tap_to_sign_up" msgid="5356397741063740395">"ସାଇନ୍ ଅପ୍ ପାଇଁ ଟାପ୍ କରନ୍ତୁ"</string>
-    <string name="wifi_connected_no_internet" msgid="2381729074310543563">"ସଂଯୁକ୍ତ, ଇଣ୍ଟର୍‌ନେଟ୍‌ ନାହିଁ"</string>
+    <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+    <skip />
     <string name="private_dns_broken" msgid="1984159464346556931">"ବ୍ୟକ୍ତିଗତ DNS ସର୍ଭର୍ ଆକ୍ସେସ୍ କରିହେବ ନାହିଁ"</string>
     <string name="wifi_limited_connection" msgid="1184778285475204682">"ସୀମିତ ସଂଯୋଗ"</string>
     <string name="wifi_status_no_internet" msgid="3799933875988829048">"କୌଣସି ଇଣ୍ଟରନେଟ୍‌ ନାହିଁ"</string>
diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml
index 6a78486..bf5a32c 100644
--- a/packages/SettingsLib/res/values-pa/strings.xml
+++ b/packages/SettingsLib/res/values-pa/strings.xml
@@ -42,7 +42,8 @@
     <string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g> ਰਾਹੀਂ ਕਨੈਕਟ ਕੀਤਾ ਗਿਆ"</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"%1$s ਰਾਹੀਂ ਉਪਲਬਧ"</string>
     <string name="tap_to_sign_up" msgid="5356397741063740395">"ਸਾਈਨ-ਅੱਪ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ"</string>
-    <string name="wifi_connected_no_internet" msgid="2381729074310543563">"ਕਨੈਕਟ ਕੀਤਾ, ਕੋਈ ਇੰਟਰਨੈੱਟ ਨਹੀਂ"</string>
+    <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+    <skip />
     <string name="private_dns_broken" msgid="1984159464346556931">"ਨਿੱਜੀ ਡੋਮੇਨ ਨਾਮ ਪ੍ਰਣਾਲੀ (DNS) ਸਰਵਰ \'ਤੇ ਪਹੁੰਚ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕੀ"</string>
     <string name="wifi_limited_connection" msgid="1184778285475204682">"ਸੀਮਤ ਕਨੈਕਸ਼ਨ"</string>
     <string name="wifi_status_no_internet" msgid="3799933875988829048">"ਇੰਟਰਨੈੱਟ ਨਹੀਂ"</string>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index 8c5547c..d907760 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -42,7 +42,8 @@
     <string name="connected_via_app" msgid="3532267661404276584">"Połączenie przez: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"Dostępne przez %1$s"</string>
     <string name="tap_to_sign_up" msgid="5356397741063740395">"Kliknij, by się zarejestrować"</string>
-    <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Połączono, brak internetu"</string>
+    <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+    <skip />
     <string name="private_dns_broken" msgid="1984159464346556931">"Brak dostępu do prywatnego serwera DNS"</string>
     <string name="wifi_limited_connection" msgid="1184778285475204682">"Ograniczone połączenie"</string>
     <string name="wifi_status_no_internet" msgid="3799933875988829048">"Brak internetu"</string>
diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml
index 8c03616..e61c84e 100644
--- a/packages/SettingsLib/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml
@@ -42,7 +42,8 @@
     <string name="connected_via_app" msgid="3532267661404276584">"Conectado via <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"Disponível via %1$s"</string>
     <string name="tap_to_sign_up" msgid="5356397741063740395">"Toque para se inscrever"</string>
-    <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Conectada, sem Internet"</string>
+    <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+    <skip />
     <string name="private_dns_broken" msgid="1984159464346556931">"Não é possível acessar o servidor DNS privado"</string>
     <string name="wifi_limited_connection" msgid="1184778285475204682">"Conexão limitada"</string>
     <string name="wifi_status_no_internet" msgid="3799933875988829048">"Sem Internet"</string>
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index 6aeff1c..8ceeb63 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -42,7 +42,8 @@
     <string name="connected_via_app" msgid="3532267661404276584">"Ligado via <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"Disponível através de %1$s"</string>
     <string name="tap_to_sign_up" msgid="5356397741063740395">"Toque para se inscrever"</string>
-    <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Ligado, sem Internet."</string>
+    <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+    <skip />
     <string name="private_dns_broken" msgid="1984159464346556931">"Não é possível aceder ao servidor DNS."</string>
     <string name="wifi_limited_connection" msgid="1184778285475204682">"Ligação limitada"</string>
     <string name="wifi_status_no_internet" msgid="3799933875988829048">"Sem Internet"</string>
diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml
index 8c03616..e61c84e 100644
--- a/packages/SettingsLib/res/values-pt/strings.xml
+++ b/packages/SettingsLib/res/values-pt/strings.xml
@@ -42,7 +42,8 @@
     <string name="connected_via_app" msgid="3532267661404276584">"Conectado via <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"Disponível via %1$s"</string>
     <string name="tap_to_sign_up" msgid="5356397741063740395">"Toque para se inscrever"</string>
-    <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Conectada, sem Internet"</string>
+    <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+    <skip />
     <string name="private_dns_broken" msgid="1984159464346556931">"Não é possível acessar o servidor DNS privado"</string>
     <string name="wifi_limited_connection" msgid="1184778285475204682">"Conexão limitada"</string>
     <string name="wifi_status_no_internet" msgid="3799933875988829048">"Sem Internet"</string>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index 387441f..5ad8dfd 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -42,7 +42,8 @@
     <string name="connected_via_app" msgid="3532267661404276584">"Conectat prin <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"Disponibilă prin %1$s"</string>
     <string name="tap_to_sign_up" msgid="5356397741063740395">"Atingeți pentru a vă înscrie"</string>
-    <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Conectată, fără internet"</string>
+    <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+    <skip />
     <string name="private_dns_broken" msgid="1984159464346556931">"Serverul DNS privat nu poate fi accesat"</string>
     <string name="wifi_limited_connection" msgid="1184778285475204682">"Conexiune limitată"</string>
     <string name="wifi_status_no_internet" msgid="3799933875988829048">"Fără conexiune la internet"</string>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index 361e29f..104efdc 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -42,7 +42,8 @@
     <string name="connected_via_app" msgid="3532267661404276584">"Подключено через приложение \"<xliff:g id="NAME">%1$s</xliff:g>\"."</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"Доступно через %1$s"</string>
     <string name="tap_to_sign_up" msgid="5356397741063740395">"Нажмите, чтобы зарегистрироваться"</string>
-    <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Подключено, без доступа к Интернету"</string>
+    <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+    <skip />
     <string name="private_dns_broken" msgid="1984159464346556931">"Доступа к частному DNS-серверу нет."</string>
     <string name="wifi_limited_connection" msgid="1184778285475204682">"Подключение к сети ограничено."</string>
     <string name="wifi_status_no_internet" msgid="3799933875988829048">"Нет подключения к Интернету"</string>
diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml
index faa848f..f5dad87 100644
--- a/packages/SettingsLib/res/values-si/strings.xml
+++ b/packages/SettingsLib/res/values-si/strings.xml
@@ -42,7 +42,8 @@
     <string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g> හරහා සම්බන්ධයි"</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"%1$s හරහා ලබා ගැනීමට හැකිය"</string>
     <string name="tap_to_sign_up" msgid="5356397741063740395">"ලියාපදිංචි වීමට තට්ටු කරන්න"</string>
-    <string name="wifi_connected_no_internet" msgid="2381729074310543563">"සම්බන්ධයි, අන්තර්ජාලය නැත"</string>
+    <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+    <skip />
     <string name="private_dns_broken" msgid="1984159464346556931">"පුද්ගලික DNS සේවාදායකයට ප්‍රවේශ වීමට නොහැකිය"</string>
     <string name="wifi_limited_connection" msgid="1184778285475204682">"සීමිත සම්බන්ධතාව"</string>
     <string name="wifi_status_no_internet" msgid="3799933875988829048">"අන්තර්ජාලය නැත"</string>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index 2035d88c..527dafb 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -42,7 +42,8 @@
     <string name="connected_via_app" msgid="3532267661404276584">"Pripojené prostredníctvom siete <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"K dispozícii prostredníctvom %1$s"</string>
     <string name="tap_to_sign_up" msgid="5356397741063740395">"Prihláste sa klepnutím"</string>
-    <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Pripojené, žiadny internet"</string>
+    <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+    <skip />
     <string name="private_dns_broken" msgid="1984159464346556931">"K súkromnému serveru DNS sa nepodarilo získať prístup"</string>
     <string name="wifi_limited_connection" msgid="1184778285475204682">"Obmedzené pripojenie"</string>
     <string name="wifi_status_no_internet" msgid="3799933875988829048">"Žiadny internet"</string>
diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml
index 2889619..4816950 100644
--- a/packages/SettingsLib/res/values-sl/strings.xml
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -42,7 +42,8 @@
     <string name="connected_via_app" msgid="3532267661404276584">"Povezava vzpostavljena prek omrežja <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"Na voljo prek: %1$s"</string>
     <string name="tap_to_sign_up" msgid="5356397741063740395">"Dotaknite se, če se želite registrirati"</string>
-    <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Vzpostavljena povezava, brez interneta"</string>
+    <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+    <skip />
     <string name="private_dns_broken" msgid="1984159464346556931">"Do zasebnega strežnika DNS ni mogoče dostopati"</string>
     <string name="wifi_limited_connection" msgid="1184778285475204682">"Omejena povezava"</string>
     <string name="wifi_status_no_internet" msgid="3799933875988829048">"Brez internetne povezave"</string>
diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml
index ccd4e30..12511dd3 100644
--- a/packages/SettingsLib/res/values-sq/strings.xml
+++ b/packages/SettingsLib/res/values-sq/strings.xml
@@ -42,7 +42,8 @@
     <string name="connected_via_app" msgid="3532267661404276584">"Lidhur përmes <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"E mundshme përmes %1$s"</string>
     <string name="tap_to_sign_up" msgid="5356397741063740395">"Trokit për t\'u regjistruar"</string>
-    <string name="wifi_connected_no_internet" msgid="2381729074310543563">"U lidh, por nuk ka internet"</string>
+    <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+    <skip />
     <string name="private_dns_broken" msgid="1984159464346556931">"Serveri privat DNS nuk mund të qaset"</string>
     <string name="wifi_limited_connection" msgid="1184778285475204682">"Lidhje e kufizuar"</string>
     <string name="wifi_status_no_internet" msgid="3799933875988829048">"Nuk ka internet"</string>
diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml
index 08e2bc8..4724be5 100644
--- a/packages/SettingsLib/res/values-sr/strings.xml
+++ b/packages/SettingsLib/res/values-sr/strings.xml
@@ -42,7 +42,8 @@
     <string name="connected_via_app" msgid="3532267661404276584">"Повезано преко: <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"Доступна је преко приступне тачке %1$s"</string>
     <string name="tap_to_sign_up" msgid="5356397741063740395">"Додирните да бисте се регистровали"</string>
-    <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Веза је успостављена, нема интернета"</string>
+    <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+    <skip />
     <string name="private_dns_broken" msgid="1984159464346556931">"Приступ приватном DNS серверу није успео"</string>
     <string name="wifi_limited_connection" msgid="1184778285475204682">"Ограничена веза"</string>
     <string name="wifi_status_no_internet" msgid="3799933875988829048">"Нема интернета"</string>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index c0cdbc9..9d1bc42 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -42,7 +42,8 @@
     <string name="connected_via_app" msgid="3532267661404276584">"Anslutet via <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"Tillgängligt via %1$s"</string>
     <string name="tap_to_sign_up" msgid="5356397741063740395">"Tryck för att logga in"</string>
-    <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Ansluten, inget internet"</string>
+    <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+    <skip />
     <string name="private_dns_broken" msgid="1984159464346556931">"Det går inte att komma åt den privata DNS-servern."</string>
     <string name="wifi_limited_connection" msgid="1184778285475204682">"Begränsad anslutning"</string>
     <string name="wifi_status_no_internet" msgid="3799933875988829048">"Inget internet"</string>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index f00dea3..1e2489f 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -42,7 +42,8 @@
     <string name="connected_via_app" msgid="3532267661404276584">"Imeunganishwa kupitia <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"Inapatikana kupitia %1$s"</string>
     <string name="tap_to_sign_up" msgid="5356397741063740395">"Gusa ili ujisajili"</string>
-    <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Imeunganishwa, hakuna intaneti"</string>
+    <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+    <skip />
     <string name="private_dns_broken" msgid="1984159464346556931">"Seva ya faragha ya DNS haiwezi kufikiwa"</string>
     <string name="wifi_limited_connection" msgid="1184778285475204682">"Muunganisho hafifu"</string>
     <string name="wifi_status_no_internet" msgid="3799933875988829048">"Hakuna intaneti"</string>
diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml
index 52e0363..fcb801b 100644
--- a/packages/SettingsLib/res/values-ta/strings.xml
+++ b/packages/SettingsLib/res/values-ta/strings.xml
@@ -42,7 +42,8 @@
     <string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g> மூலம் இணைக்கப்பட்டது"</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"%1$s வழியாகக் கிடைக்கிறது"</string>
     <string name="tap_to_sign_up" msgid="5356397741063740395">"பதிவு செய்யத் தட்டவும்"</string>
-    <string name="wifi_connected_no_internet" msgid="2381729074310543563">"இணைக்கப்பட்டுள்ளது, ஆனால் இண்டர்நெட் இல்லை"</string>
+    <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+    <skip />
     <string name="private_dns_broken" msgid="1984159464346556931">"தனிப்பட்ட DNS சேவையகத்தை அணுக இயலாது"</string>
     <string name="wifi_limited_connection" msgid="1184778285475204682">"வரம்பிற்கு உட்பட்ட இணைப்பு"</string>
     <string name="wifi_status_no_internet" msgid="3799933875988829048">"இணைய இணைப்பு இல்லை"</string>
diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml
index a39c4e1..7a80451 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -42,7 +42,8 @@
     <string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g> ద్వారా కనెక్ట్ చేయబడింది"</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"%1$s ద్వారా అందుబాటులో ఉంది"</string>
     <string name="tap_to_sign_up" msgid="5356397741063740395">"సైన్ అప్ చేయడానికి నొక్కండి"</string>
-    <string name="wifi_connected_no_internet" msgid="2381729074310543563">"కనెక్ట్ చేయబడింది, ఇంటర్నెట్ లేదు"</string>
+    <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+    <skip />
     <string name="private_dns_broken" msgid="1984159464346556931">"ప్రైవేట్ DNS సర్వర్‌ను యాక్సెస్ చేయడం సాధ్యపడదు"</string>
     <string name="wifi_limited_connection" msgid="1184778285475204682">"పరిమిత కనెక్షన్"</string>
     <string name="wifi_status_no_internet" msgid="3799933875988829048">"ఇంటర్నెట్ లేదు"</string>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index 635d77a..130e1c4 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -42,7 +42,8 @@
     <string name="connected_via_app" msgid="3532267661404276584">"เชื่อมต่อแล้วผ่าน <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"พร้อมใช้งานผ่านทาง %1$s"</string>
     <string name="tap_to_sign_up" msgid="5356397741063740395">"แตะเพื่อลงชื่อสมัครใช้"</string>
-    <string name="wifi_connected_no_internet" msgid="2381729074310543563">"เชื่อมต่อแล้ว ไม่พบอินเทอร์เน็ต"</string>
+    <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+    <skip />
     <string name="private_dns_broken" msgid="1984159464346556931">"เข้าถึงเซิร์ฟเวอร์ DNS ไม่ได้"</string>
     <string name="wifi_limited_connection" msgid="1184778285475204682">"การเชื่อมต่อที่จำกัด"</string>
     <string name="wifi_status_no_internet" msgid="3799933875988829048">"ไม่มีอินเทอร์เน็ต"</string>
diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml
index 3f7f0ff..bc1a405 100644
--- a/packages/SettingsLib/res/values-tl/strings.xml
+++ b/packages/SettingsLib/res/values-tl/strings.xml
@@ -42,7 +42,8 @@
     <string name="connected_via_app" msgid="3532267661404276584">"Nakakonekta sa pamamagitan ng <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"Available sa pamamagitan ng %1$s"</string>
     <string name="tap_to_sign_up" msgid="5356397741063740395">"I-tap para mag-sign up"</string>
-    <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Nakakonekta, walang internet"</string>
+    <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+    <skip />
     <string name="private_dns_broken" msgid="1984159464346556931">"Hindi ma-access ang pribadong DNS server"</string>
     <string name="wifi_limited_connection" msgid="1184778285475204682">"Limitadong koneksyon"</string>
     <string name="wifi_status_no_internet" msgid="3799933875988829048">"Walang internet"</string>
diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml
index 7ad6fcd..4e263ef 100644
--- a/packages/SettingsLib/res/values-tr/strings.xml
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -42,7 +42,8 @@
     <string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g> ile bağlandı"</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"%1$s üzerinden kullanılabilir"</string>
     <string name="tap_to_sign_up" msgid="5356397741063740395">"Kaydolmak için dokunun"</string>
-    <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Bağlı, internet yok"</string>
+    <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+    <skip />
     <string name="private_dns_broken" msgid="1984159464346556931">"Gizli DNS sunucusuna erişilemiyor"</string>
     <string name="wifi_limited_connection" msgid="1184778285475204682">"Sınırlı bağlantı"</string>
     <string name="wifi_status_no_internet" msgid="3799933875988829048">"İnternet yok"</string>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index b5dd618..ad9f600 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -42,7 +42,8 @@
     <string name="connected_via_app" msgid="3532267661404276584">"Підключено через додаток <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"Доступ через %1$s"</string>
     <string name="tap_to_sign_up" msgid="5356397741063740395">"Торкніться, щоб увійти"</string>
-    <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Під’єднано, але немає доступу до Інтернету"</string>
+    <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+    <skip />
     <string name="private_dns_broken" msgid="1984159464346556931">"Немає доступу до приватного DNS-сервера"</string>
     <string name="wifi_limited_connection" msgid="1184778285475204682">"Обмежене з’єднання"</string>
     <string name="wifi_status_no_internet" msgid="3799933875988829048">"Немає Інтернету"</string>
diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml
index ef9b2a1..b995da1 100644
--- a/packages/SettingsLib/res/values-ur/strings.xml
+++ b/packages/SettingsLib/res/values-ur/strings.xml
@@ -42,7 +42,8 @@
     <string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g> کے ذریعے منسلک"</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"‏دستیاب بذریعہ ‎%1$s"</string>
     <string name="tap_to_sign_up" msgid="5356397741063740395">"سائن اپ کے لیے تھپتھپائیں"</string>
-    <string name="wifi_connected_no_internet" msgid="2381729074310543563">"منسلک، انٹرنیٹ نہیں ہے"</string>
+    <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+    <skip />
     <string name="private_dns_broken" msgid="1984159464346556931">"‏نجی DNS سرور تک رسائی حاصل نہیں کی جا سکی"</string>
     <string name="wifi_limited_connection" msgid="1184778285475204682">"محدود کنکشن"</string>
     <string name="wifi_status_no_internet" msgid="3799933875988829048">"انٹرنیٹ نہیں ہے"</string>
diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml
index b202d64..f02b639 100644
--- a/packages/SettingsLib/res/values-uz/strings.xml
+++ b/packages/SettingsLib/res/values-uz/strings.xml
@@ -42,7 +42,8 @@
     <string name="connected_via_app" msgid="3532267661404276584">"<xliff:g id="NAME">%1$s</xliff:g> orqali ulandi"</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"%1$s orqali ishlaydi"</string>
     <string name="tap_to_sign_up" msgid="5356397741063740395">"Yozilish uchun bosing"</string>
-    <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Ulangan, lekin internet aloqasi yo‘q"</string>
+    <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+    <skip />
     <string name="private_dns_broken" msgid="1984159464346556931">"Xususiy DNS server ishlamayapti"</string>
     <string name="wifi_limited_connection" msgid="1184778285475204682">"Cheklangan aloqa"</string>
     <string name="wifi_status_no_internet" msgid="3799933875988829048">"Internet yo‘q"</string>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index 5bcff5a..8fcf1f2 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -42,7 +42,8 @@
     <string name="connected_via_app" msgid="3532267661404276584">"Đã kết nối qua <xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"Có sẵn qua %1$s"</string>
     <string name="tap_to_sign_up" msgid="5356397741063740395">"Nhấn để đăng ký"</string>
-    <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Đã kết nối, không có Internet"</string>
+    <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+    <skip />
     <string name="private_dns_broken" msgid="1984159464346556931">"Không thể truy cập máy chủ DNS riêng tư"</string>
     <string name="wifi_limited_connection" msgid="1184778285475204682">"Kết nối giới hạn"</string>
     <string name="wifi_status_no_internet" msgid="3799933875988829048">"Không có Internet"</string>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index 418370b..755cbee 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -42,7 +42,8 @@
     <string name="connected_via_app" msgid="3532267661404276584">"已通过<xliff:g id="NAME">%1$s</xliff:g>连接到网络"</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"可通过%1$s连接"</string>
     <string name="tap_to_sign_up" msgid="5356397741063740395">"点按即可注册"</string>
-    <string name="wifi_connected_no_internet" msgid="2381729074310543563">"已连接,但无法访问互联网"</string>
+    <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+    <skip />
     <string name="private_dns_broken" msgid="1984159464346556931">"无法访问私人 DNS 服务器"</string>
     <string name="wifi_limited_connection" msgid="1184778285475204682">"网络连接受限"</string>
     <string name="wifi_status_no_internet" msgid="3799933875988829048">"无法访问互联网"</string>
diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml
index 79b5579..e50acf5 100644
--- a/packages/SettingsLib/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml
@@ -42,7 +42,8 @@
     <string name="connected_via_app" msgid="3532267661404276584">"已透過「<xliff:g id="NAME">%1$s</xliff:g>」連線"</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"可透過 %1$s 連線"</string>
     <string name="tap_to_sign_up" msgid="5356397741063740395">"輕按即可登入"</string>
-    <string name="wifi_connected_no_internet" msgid="2381729074310543563">"已連線,但沒有互聯網"</string>
+    <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+    <skip />
     <string name="private_dns_broken" msgid="1984159464346556931">"無法存取私人 DNS 伺服器"</string>
     <string name="wifi_limited_connection" msgid="1184778285475204682">"連線受限"</string>
     <string name="wifi_status_no_internet" msgid="3799933875988829048">"沒有互聯網連線"</string>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index 47ab764..7df6fed 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -42,7 +42,8 @@
     <string name="connected_via_app" msgid="3532267661404276584">"透過「<xliff:g id="NAME">%1$s</xliff:g>」連線"</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"可透過 %1$s 使用"</string>
     <string name="tap_to_sign_up" msgid="5356397741063740395">"輕觸即可註冊"</string>
-    <string name="wifi_connected_no_internet" msgid="2381729074310543563">"已連線,沒有網際網路"</string>
+    <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+    <skip />
     <string name="private_dns_broken" msgid="1984159464346556931">"無法存取私人 DNS 伺服器"</string>
     <string name="wifi_limited_connection" msgid="1184778285475204682">"連線能力受限"</string>
     <string name="wifi_status_no_internet" msgid="3799933875988829048">"沒有網際網路連線"</string>
diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml
index 87f45de..46dffbe 100644
--- a/packages/SettingsLib/res/values-zu/strings.xml
+++ b/packages/SettingsLib/res/values-zu/strings.xml
@@ -42,7 +42,8 @@
     <string name="connected_via_app" msgid="3532267661404276584">"Ixhumeke nge-<xliff:g id="NAME">%1$s</xliff:g>"</string>
     <string name="available_via_passpoint" msgid="1716000261192603682">"Iyatholakala nge-%1$s"</string>
     <string name="tap_to_sign_up" msgid="5356397741063740395">"Thepha ukuze ubhalisele"</string>
-    <string name="wifi_connected_no_internet" msgid="2381729074310543563">"Kuxhunyiwe, ayikho i-inthanethi"</string>
+    <!-- no translation found for wifi_connected_no_internet (5087420713443350646) -->
+    <skip />
     <string name="private_dns_broken" msgid="1984159464346556931">"Iseva eyimfihlo ye-DNS ayikwazi ukufinyelelwa"</string>
     <string name="wifi_limited_connection" msgid="1184778285475204682">"Iqoqo elikhawulelwe"</string>
     <string name="wifi_status_no_internet" msgid="3799933875988829048">"Ayikho i-inthanethi"</string>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index a7df6db..dd21e5c 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -129,7 +129,7 @@
     <string name="certinstaller_package" translatable="false">com.android.certinstaller</string>
 
     <!-- Summary for Connected wifi network without internet -->
-    <string name="wifi_connected_no_internet">Connected, no internet</string>
+    <string name="wifi_connected_no_internet">No internet</string>
 
     <!-- Summary for connected network without internet due to private dns validation failed [CHAR LIMIT=NONE] -->
     <string name="private_dns_broken">Private DNS server cannot be accessed</string>
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/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
index e008cd03..a4be46c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
@@ -40,15 +40,16 @@
     final Executor mExecutor = Executors.newSingleThreadExecutor();
     @VisibleForTesting
     MediaRouter2Manager mRouterManager;
+    @VisibleForTesting
+    String mPackageName;
 
-    private String mPackageName;
     private MediaDevice mCurrentConnectedDevice;
 
     public InfoMediaManager(Context context, String packageName, Notification notification) {
         super(context, notification);
 
         mRouterManager = MediaRouter2Manager.getInstance(context);
-        if (packageName != null) {
+        if (!TextUtils.isEmpty(packageName)) {
             mPackageName = packageName;
         }
     }
@@ -57,6 +58,7 @@
     public void startScan() {
         mMediaDevices.clear();
         mRouterManager.registerCallback(mExecutor, mMediaRouterCallback);
+        refreshDevices();
     }
 
     @VisibleForTesting
@@ -79,21 +81,37 @@
         return mCurrentConnectedDevice;
     }
 
-    class RouterManagerCallback extends MediaRouter2Manager.Callback {
-
-        private void refreshDevices() {
-            mMediaDevices.clear();
-            mCurrentConnectedDevice = null;
-            for (MediaRoute2Info route : mRouterManager.getAvailableRoutes(mPackageName)) {
-                final MediaDevice device = new InfoMediaDevice(mContext, mRouterManager, route,
-                        mPackageName);
-                if (TextUtils.equals(route.getClientPackageName(), mPackageName)) {
-                    mCurrentConnectedDevice = device;
-                }
-                mMediaDevices.add(device);
-            }
-            dispatchDeviceListAdded();
+    private void refreshDevices() {
+        mMediaDevices.clear();
+        mCurrentConnectedDevice = null;
+        if (TextUtils.isEmpty(mPackageName)) {
+            buildAllRoutes();
+        } else {
+            buildAvailableRoutes();
         }
+        dispatchDeviceListAdded();
+    }
+
+    private void buildAllRoutes() {
+        for (MediaRoute2Info route : mRouterManager.getAllRoutes()) {
+            final MediaDevice device = new InfoMediaDevice(mContext, mRouterManager, route,
+                    mPackageName);
+            mMediaDevices.add(device);
+        }
+    }
+
+    private void buildAvailableRoutes() {
+        for (MediaRoute2Info route : mRouterManager.getAvailableRoutes(mPackageName)) {
+            final MediaDevice device = new InfoMediaDevice(mContext, mRouterManager, route,
+                    mPackageName);
+            if (TextUtils.equals(route.getClientPackageName(), mPackageName)) {
+                mCurrentConnectedDevice = device;
+            }
+            mMediaDevices.add(device);
+        }
+    }
+
+    class RouterManagerCallback extends MediaRouter2Manager.Callback {
 
         @Override
         public void onRoutesAdded(List<MediaRoute2Info> routes) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
index e85a102..50196d2 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
@@ -18,7 +18,6 @@
 import android.app.Notification;
 import android.bluetooth.BluetoothProfile;
 import android.content.Context;
-import android.text.TextUtils;
 import android.util.Log;
 
 import androidx.annotation.IntDef;
@@ -162,10 +161,8 @@
         mMediaDevices.clear();
         mBluetoothMediaManager.registerCallback(mMediaDeviceCallback);
         mBluetoothMediaManager.startScan();
-        if (!TextUtils.isEmpty(mPackageName)) {
-            mInfoMediaManager.registerCallback(mMediaDeviceCallback);
-            mInfoMediaManager.startScan();
-        }
+        mInfoMediaManager.registerCallback(mMediaDeviceCallback);
+        mInfoMediaManager.startScan();
     }
 
     private void addPhoneDeviceIfNecessary() {
@@ -208,10 +205,8 @@
     public void stopScan() {
         mBluetoothMediaManager.unregisterCallback(mMediaDeviceCallback);
         mBluetoothMediaManager.stopScan();
-        if (!TextUtils.isEmpty(mPackageName)) {
-            mInfoMediaManager.unregisterCallback(mMediaDeviceCallback);
-            mInfoMediaManager.stopScan();
-        }
+        mInfoMediaManager.unregisterCallback(mMediaDeviceCallback);
+        mInfoMediaManager.stopScan();
     }
 
     /**
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/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
index 67f6dd90..3726fb2 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
@@ -74,7 +74,7 @@
     }
 
     @Test
-    public void onRouteAdded_shouldAddMediaDevice() {
+    public void onRouteAdded_getAvailableRoutes_shouldAddMediaDevice() {
         final MediaRoute2Info info = mock(MediaRoute2Info.class);
         when(info.getId()).thenReturn(TEST_ID);
         when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
@@ -95,6 +95,27 @@
     }
 
     @Test
+    public void onRouteAdded_buildAllRoutes_shouldAddMediaDevice() {
+        final MediaRoute2Info info = mock(MediaRoute2Info.class);
+        when(info.getId()).thenReturn(TEST_ID);
+        when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
+
+        final List<MediaRoute2Info> routes = new ArrayList<>();
+        routes.add(info);
+        when(mRouterManager.getAllRoutes()).thenReturn(routes);
+
+        final MediaDevice mediaDevice = mInfoMediaManager.findMediaDevice(TEST_ID);
+        assertThat(mediaDevice).isNull();
+
+        mInfoMediaManager.mPackageName = "";
+        mInfoMediaManager.mMediaRouterCallback.onRoutesAdded(routes);
+
+        final MediaDevice infoDevice = mInfoMediaManager.mMediaDevices.get(0);
+        assertThat(infoDevice.getId()).isEqualTo(TEST_ID);
+        assertThat(mInfoMediaManager.mMediaDevices).hasSize(routes.size());
+    }
+
+    @Test
     public void onControlCategoriesChanged_samePackageName_shouldAddMediaDevice() {
         final MediaRoute2Info info = mock(MediaRoute2Info.class);
         when(info.getId()).thenReturn(TEST_ID);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
index cdf9728..3c52f54 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
@@ -45,6 +45,7 @@
 import android.util.ArrayMap;
 import android.util.ArraySet;
 import android.util.BackupUtils;
+import android.util.FeatureFlagUtils;
 import android.util.Log;
 import android.util.Slog;
 import android.view.Display;
@@ -280,6 +281,16 @@
         Settings.Secure.getMovedToGlobalSettings(movedToGlobal);
         Set<String> movedToSecure = getMovedToSecureSettings();
 
+        Set<String> preservedGlobalSettings = getSettingsToPreserveInRestore(
+                Settings.Global.CONTENT_URI);
+        Set<String> preservedSecureSettings = getSettingsToPreserveInRestore(
+                Settings.Secure.CONTENT_URI);
+        Set<String> preservedSystemSettings = getSettingsToPreserveInRestore(
+                Settings.System.CONTENT_URI);
+        Set<String> preservedSettings = new HashSet<>(preservedGlobalSettings);
+        preservedSettings.addAll(preservedSecureSettings);
+        preservedSettings.addAll(preservedSystemSettings);
+
         byte[] restoredWifiSupplicantData = null;
         byte[] restoredWifiIpConfigData = null;
 
@@ -300,7 +311,8 @@
                 case KEY_SYSTEM :
                     restoreSettings(data, Settings.System.CONTENT_URI, movedToGlobal,
                             movedToSecure, R.array.restore_blocked_system_settings,
-                            dynamicBlockList);
+                            dynamicBlockList,
+                            preservedSystemSettings);
                     mSettingsHelper.applyAudioSettings();
                     break;
 
@@ -311,7 +323,8 @@
                             movedToGlobal,
                             null,
                             R.array.restore_blocked_secure_settings,
-                            dynamicBlockList);
+                            dynamicBlockList,
+                            preservedSecureSettings);
                     break;
 
                 case KEY_GLOBAL :
@@ -321,7 +334,8 @@
                             null,
                             movedToSecure,
                             R.array.restore_blocked_global_settings,
-                            dynamicBlockList);
+                            dynamicBlockList,
+                            preservedGlobalSettings);
                     break;
 
                 case KEY_WIFI_SUPPLICANT :
@@ -368,7 +382,8 @@
                     restoreDeviceSpecificConfig(
                             restoredDeviceSpecificConfig,
                             R.array.restore_blocked_device_specific_settings,
-                            dynamicBlockList);
+                            dynamicBlockList,
+                            preservedSettings);
                     break;
 
                 default :
@@ -418,7 +433,7 @@
             in.readFully(buffer, 0, nBytes);
             restoreSettings(buffer, nBytes, Settings.System.CONTENT_URI, movedToGlobal,
                     movedToSecure, R.array.restore_blocked_system_settings,
-                    Collections.emptySet());
+                    Collections.emptySet(), Collections.emptySet());
 
             // secure settings
             nBytes = in.readInt();
@@ -432,7 +447,7 @@
                     movedToGlobal,
                     null,
                     R.array.restore_blocked_secure_settings,
-                    Collections.emptySet());
+                    Collections.emptySet(), Collections.emptySet());
 
             // Global only if sufficiently new
             if (version >= FULL_BACKUP_ADDED_GLOBAL) {
@@ -443,7 +458,7 @@
                 movedToGlobal.clear();  // no redirection; this *is* the global namespace
                 restoreSettings(buffer, nBytes, Settings.Global.CONTENT_URI, movedToGlobal,
                         movedToSecure, R.array.restore_blocked_global_settings,
-                        Collections.emptySet());
+                        Collections.emptySet(), Collections.emptySet());
             }
 
             // locale
@@ -608,6 +623,40 @@
     }
 
     /**
+     * Get names of the settings for which the current value should be preserved during restore.
+     */
+    private Set<String> getSettingsToPreserveInRestore(Uri settingsUri) {
+        if (!FeatureFlagUtils.isEnabled(getApplicationContext(),
+                FeatureFlagUtils.SETTINGS_DO_NOT_RESTORE_PRESERVED)) {
+            return Collections.emptySet();
+        }
+
+        Cursor cursor = getContentResolver().query(settingsUri, new String[] {
+                Settings.NameValueTable.NAME, Settings.NameValueTable.IS_PRESERVED_IN_RESTORE },
+                /* selection */ null, /* selectionArgs */ null, /* sortOrder */ null);
+
+        if (!cursor.moveToFirst()) {
+            Slog.i(TAG, "No settings to be preserved in restore");
+            return Collections.emptySet();
+        }
+
+        int nameIndex = cursor.getColumnIndex(Settings.NameValueTable.NAME);
+        int isPreservedIndex = cursor.getColumnIndex(
+                Settings.NameValueTable.IS_PRESERVED_IN_RESTORE);
+
+        Set<String> preservedSettings = new HashSet<>();
+        while (!cursor.isAfterLast()) {
+            if (Boolean.parseBoolean(cursor.getString(isPreservedIndex))) {
+                preservedSettings.add(getQualifiedKeyForSetting(cursor.getString(nameIndex),
+                        settingsUri));
+            }
+            cursor.moveToNext();
+        }
+
+        return preservedSettings;
+    }
+
+    /**
      * Serialize the owner info and other lock settings
      */
     private byte[] getLockSettings(@UserIdInt int userId) {
@@ -650,7 +699,8 @@
             HashSet<String> movedToGlobal,
             Set<String> movedToSecure,
             int blockedSettingsArrayId,
-            Set<String> dynamicBlockList) {
+            Set<String> dynamicBlockList,
+            Set<String> settingsToPreserve) {
         byte[] settings = new byte[data.getDataSize()];
         try {
             data.readEntityData(settings, 0, settings.length);
@@ -665,7 +715,8 @@
                 movedToGlobal,
                 movedToSecure,
                 blockedSettingsArrayId,
-                dynamicBlockList);
+                dynamicBlockList,
+                settingsToPreserve);
     }
 
     private void restoreSettings(
@@ -675,7 +726,8 @@
             HashSet<String> movedToGlobal,
             Set<String> movedToSecure,
             int blockedSettingsArrayId,
-            Set<String> dynamicBlockList) {
+            Set<String> dynamicBlockList,
+            Set<String> settingsToPreserve) {
         restoreSettings(
                 settings,
                 0,
@@ -684,10 +736,12 @@
                 movedToGlobal,
                 movedToSecure,
                 blockedSettingsArrayId,
-                dynamicBlockList);
+                dynamicBlockList,
+                settingsToPreserve);
     }
 
-    private void restoreSettings(
+    @VisibleForTesting
+    void restoreSettings(
             byte[] settings,
             int pos,
             int bytes,
@@ -695,31 +749,13 @@
             HashSet<String> movedToGlobal,
             Set<String> movedToSecure,
             int blockedSettingsArrayId,
-            Set<String> dynamicBlockList) {
+            Set<String> dynamicBlockList,
+            Set<String> settingsToPreserve) {
         if (DEBUG) {
             Log.i(TAG, "restoreSettings: " + contentUri);
         }
 
-        // Figure out the white list and redirects to the global table.  We restore anything
-        // in either the backup whitelist or the legacy-restore whitelist for this table.
-        final String[] whitelist;
-        Map<String, Validator> validators = null;
-        if (contentUri.equals(Settings.Secure.CONTENT_URI)) {
-            whitelist = ArrayUtils.concatElements(String.class, SecureSettings.SETTINGS_TO_BACKUP,
-                    Settings.Secure.LEGACY_RESTORE_SETTINGS,
-                    DeviceSpecificSettings.DEVICE_SPECIFIC_SETTINGS_TO_BACKUP);
-            validators = SecureSettingsValidators.VALIDATORS;
-        } else if (contentUri.equals(Settings.System.CONTENT_URI)) {
-            whitelist = ArrayUtils.concatElements(String.class, SystemSettings.SETTINGS_TO_BACKUP,
-                    Settings.System.LEGACY_RESTORE_SETTINGS);
-            validators = SystemSettingsValidators.VALIDATORS;
-        } else if (contentUri.equals(Settings.Global.CONTENT_URI)) {
-            whitelist = ArrayUtils.concatElements(String.class, GlobalSettings.SETTINGS_TO_BACKUP,
-                    Settings.Global.LEGACY_RESTORE_SETTINGS);
-            validators = GlobalSettingsValidators.VALIDATORS;
-        } else {
-            throw new IllegalArgumentException("Unknown URI: " + contentUri);
-        }
+        SettingsBackupWhitelist whitelist = getBackupWhitelist(contentUri);
 
         // Restore only the white list data.
         final ArrayMap<String, String> cachedEntries = new ArrayMap<>();
@@ -729,7 +765,7 @@
 
         Set<String> blockedSettings = getBlockedSettings(blockedSettingsArrayId);
 
-        for (String key : whitelist) {
+        for (String key : whitelist.mSettingsWhitelist) {
             boolean isBlockedBySystem = blockedSettings != null && blockedSettings.contains(key);
             if (isBlockedBySystem || isBlockedByDynamicList(dynamicBlockList, contentUri,  key)) {
                 Log.i(
@@ -742,6 +778,12 @@
                 continue;
             }
 
+            if (settingsToPreserve.contains(getQualifiedKeyForSetting(key, contentUri))) {
+                Log.i(TAG, "Skipping restore for setting " + key + " as it is marked as "
+                        + "preserved");
+                continue;
+            }
+
             String value = null;
             boolean hasValueToRestore = false;
             if (cachedEntries.indexOfKey(key) >= 0) {
@@ -775,7 +817,7 @@
             }
 
             // only restore the settings that have valid values
-            if (!isValidSettingValue(key, value, validators)) {
+            if (!isValidSettingValue(key, value, whitelist.mSettingsValidators)) {
                 Log.w(TAG, "Attempted restore of " + key + " setting, but its value didn't pass"
                         + " validation, value: " + value);
                 continue;
@@ -798,11 +840,42 @@
         }
     }
 
+    @VisibleForTesting
+    SettingsBackupWhitelist getBackupWhitelist(Uri contentUri) {
+        // Figure out the white list and redirects to the global table.  We restore anything
+        // in either the backup whitelist or the legacy-restore whitelist for this table.
+        String[] whitelist;
+        Map<String, Validator> validators = null;
+        if (contentUri.equals(Settings.Secure.CONTENT_URI)) {
+            whitelist = ArrayUtils.concatElements(String.class, SecureSettings.SETTINGS_TO_BACKUP,
+                    Settings.Secure.LEGACY_RESTORE_SETTINGS,
+                    DeviceSpecificSettings.DEVICE_SPECIFIC_SETTINGS_TO_BACKUP);
+            validators = SecureSettingsValidators.VALIDATORS;
+        } else if (contentUri.equals(Settings.System.CONTENT_URI)) {
+            whitelist = ArrayUtils.concatElements(String.class, SystemSettings.SETTINGS_TO_BACKUP,
+                    Settings.System.LEGACY_RESTORE_SETTINGS);
+            validators = SystemSettingsValidators.VALIDATORS;
+        } else if (contentUri.equals(Settings.Global.CONTENT_URI)) {
+            whitelist = ArrayUtils.concatElements(String.class, GlobalSettings.SETTINGS_TO_BACKUP,
+                    Settings.Global.LEGACY_RESTORE_SETTINGS);
+            validators = GlobalSettingsValidators.VALIDATORS;
+        } else {
+            throw new IllegalArgumentException("Unknown URI: " + contentUri);
+        }
+
+        return new SettingsBackupWhitelist(whitelist, validators);
+    }
+
     private boolean isBlockedByDynamicList(Set<String> dynamicBlockList, Uri areaUri, String key) {
         String contentKey = Uri.withAppendedPath(areaUri, key).toString();
         return dynamicBlockList.contains(contentKey);
     }
 
+    @VisibleForTesting
+    static String getQualifiedKeyForSetting(String settingName, Uri settingUri) {
+        return Uri.withAppendedPath(settingUri, settingName).toString();
+    }
+
     // There may be other sources of blocked settings, so I'm separating out this
     // code to make it easy to modify in the future.
     @VisibleForTesting
@@ -1089,7 +1162,7 @@
      */
     @VisibleForTesting
     boolean restoreDeviceSpecificConfig(byte[] data, int blockedSettingsArrayId,
-            Set<String> dynamicBlocklist) {
+            Set<String> dynamicBlocklist, Set<String> preservedSettings) {
         // We're using an AtomicInteger to wrap the position int and allow called methods to
         // modify it.
         AtomicInteger pos = new AtomicInteger(0);
@@ -1108,7 +1181,8 @@
                 null,
                 null,
                 blockedSettingsArrayId,
-                dynamicBlocklist);
+                dynamicBlocklist,
+                preservedSettings);
 
         updateWindowManagerIfNeeded(originalDensity);
 
@@ -1240,4 +1314,20 @@
                 | ((in[pos + 3] & 0xFF) <<  0);
         return result;
     }
+
+    /**
+     * Store the whitelist of settings to be backed up and validators for them.
+     */
+    @VisibleForTesting
+    static class SettingsBackupWhitelist {
+        final String[] mSettingsWhitelist;
+        final Map<String, Validator> mSettingsValidators;
+
+
+        SettingsBackupWhitelist(String[] settingsWhitelist,
+                Map<String, Validator> settingsValidators) {
+            mSettingsWhitelist = settingsWhitelist;
+            mSettingsValidators = settingsValidators;
+        }
+    }
 }
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 874e299..c969bfd 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -180,10 +180,17 @@
     private static final int MUTATION_OPERATION_UPDATE = 3;
     private static final int MUTATION_OPERATION_RESET = 4;
 
+    private static final String[] LEGACY_SQL_COLUMNS = new String[] {
+            Settings.NameValueTable._ID,
+            Settings.NameValueTable.NAME,
+            Settings.NameValueTable.VALUE,
+    };
+
     private static final String[] ALL_COLUMNS = new String[] {
             Settings.NameValueTable._ID,
             Settings.NameValueTable.NAME,
-            Settings.NameValueTable.VALUE
+            Settings.NameValueTable.VALUE,
+            Settings.NameValueTable.IS_PRESERVED_IN_RESTORE,
     };
 
     public static final int SETTINGS_TYPE_GLOBAL = SettingsState.SETTINGS_TYPE_GLOBAL;
@@ -2353,6 +2360,10 @@
                 case Settings.NameValueTable.VALUE: {
                     values[i] = setting.getValue();
                 } break;
+
+                case Settings.NameValueTable.IS_PRESERVED_IN_RESTORE: {
+                    values[i] = String.valueOf(setting.isValuePreservedInRestore());
+                } break;
             }
         }
 
@@ -3097,7 +3108,7 @@
             SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
             queryBuilder.setTables(table);
 
-            Cursor cursor = queryBuilder.query(database, ALL_COLUMNS,
+            Cursor cursor = queryBuilder.query(database, LEGACY_SQL_COLUMNS,
                     null, null, null, null, null);
 
             if (cursor == null) {
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsState.java
index db18213..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/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupAgentTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupAgentTest.java
index e650882..f5334fb 100644
--- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupAgentTest.java
+++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsBackupAgentTest.java
@@ -32,6 +32,8 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.provider.Settings;
+import android.provider.settings.validators.SettingsValidators;
+import android.provider.settings.validators.Validator;
 import android.test.mock.MockContentProvider;
 import android.test.mock.MockContentResolver;
 
@@ -43,6 +45,7 @@
 
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
+import java.nio.ByteBuffer;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
@@ -55,12 +58,24 @@
 /** Tests for the SettingsHelperTest */
 @RunWith(AndroidJUnit4.class)
 public class SettingsBackupAgentTest extends BaseSettingsProviderTest {
-
+    private static final Uri TEST_URI = Uri.EMPTY;
     private static final String TEST_DISPLAY_DENSITY_FORCED = "123";
+    private static final String OVERRIDDEN_TEST_SETTING = "overridden_setting";
+    private static final String PRESERVED_TEST_SETTING = "preserved_setting";
+    private static final Map<String, String> DEVICE_SPECIFIC_TEST_VALUES = new HashMap<>();
     private static final Map<String, String> TEST_VALUES = new HashMap<>();
+    private static final Map<String, Validator> TEST_VALUES_VALIDATORS = new HashMap<>();
 
     static {
-        TEST_VALUES.put(Settings.Secure.DISPLAY_DENSITY_FORCED, TEST_DISPLAY_DENSITY_FORCED);
+        DEVICE_SPECIFIC_TEST_VALUES.put(Settings.Secure.DISPLAY_DENSITY_FORCED,
+                TEST_DISPLAY_DENSITY_FORCED);
+
+        TEST_VALUES.put(OVERRIDDEN_TEST_SETTING, "123");
+        TEST_VALUES.put(PRESERVED_TEST_SETTING, "124");
+
+        TEST_VALUES_VALIDATORS.put(OVERRIDDEN_TEST_SETTING,
+                SettingsValidators.ANY_STRING_VALIDATOR);
+        TEST_VALUES_VALIDATORS.put(PRESERVED_TEST_SETTING, SettingsValidators.ANY_STRING_VALIDATOR);
     }
 
     private TestFriendlySettingsBackupAgent mAgentUnderTest;
@@ -83,14 +98,15 @@
 
         byte[] settingsBackup = mAgentUnderTest.getDeviceSpecificConfiguration();
 
-        assertEquals("Not all values backed up.", TEST_VALUES.keySet(), helper.mReadEntries);
+        assertEquals("Not all values backed up.", DEVICE_SPECIFIC_TEST_VALUES.keySet(), helper.mReadEntries);
 
         mAgentUnderTest.restoreDeviceSpecificConfig(
                 settingsBackup,
                 R.array.restore_blocked_device_specific_settings,
+                Collections.emptySet(),
                 Collections.emptySet());
 
-        assertEquals("Not all values were restored.", TEST_VALUES, helper.mWrittenValues);
+        assertEquals("Not all values were restored.", DEVICE_SPECIFIC_TEST_VALUES, helper.mWrittenValues);
     }
 
     @Test
@@ -100,12 +116,13 @@
 
         byte[] settingsBackup = mAgentUnderTest.getDeviceSpecificConfiguration();
 
-        assertEquals("Not all values backed up.", TEST_VALUES.keySet(), helper.mReadEntries);
-        mAgentUnderTest.setBlockedSettings(TEST_VALUES.keySet().toArray(new String[0]));
+        assertEquals("Not all values backed up.", DEVICE_SPECIFIC_TEST_VALUES.keySet(), helper.mReadEntries);
+        mAgentUnderTest.setBlockedSettings(DEVICE_SPECIFIC_TEST_VALUES.keySet().toArray(new String[0]));
 
         mAgentUnderTest.restoreDeviceSpecificConfig(
                 settingsBackup,
                 R.array.restore_blocked_device_specific_settings,
+                Collections.emptySet(),
                 Collections.emptySet());
 
         assertTrue("Not all values were blocked.", helper.mWrittenValues.isEmpty());
@@ -172,9 +189,50 @@
                 mAgentUnderTest.restoreDeviceSpecificConfig(
                         data,
                         R.array.restore_blocked_device_specific_settings,
+                        Collections.emptySet(),
                         Collections.emptySet()));
     }
 
+    @Test
+    public void testOnRestore_preservedSettingsAreNotRestored() {
+        SettingsBackupAgent.SettingsBackupWhitelist whitelist =
+                new SettingsBackupAgent.SettingsBackupWhitelist(
+                        new String[] { OVERRIDDEN_TEST_SETTING, PRESERVED_TEST_SETTING },
+                        TEST_VALUES_VALIDATORS);
+        mAgentUnderTest.setSettingsWhitelist(whitelist);
+        mAgentUnderTest.setBlockedSettings();
+        TestSettingsHelper settingsHelper = new TestSettingsHelper(mContext);
+        mAgentUnderTest.mSettingsHelper = settingsHelper;
+
+        byte[] backupData = generateBackupData(TEST_VALUES);
+        mAgentUnderTest.restoreSettings(backupData, /* pos */ 0, backupData.length, TEST_URI, new HashSet<>(),
+                Collections.emptySet(), /* blockedSettingsArrayId */ 0, Collections.emptySet(),
+                new HashSet<>(Collections.singletonList(SettingsBackupAgent.getQualifiedKeyForSetting(PRESERVED_TEST_SETTING, TEST_URI))));
+
+        assertTrue(settingsHelper.mWrittenValues.containsKey(OVERRIDDEN_TEST_SETTING));
+        assertFalse(settingsHelper.mWrittenValues.containsKey(PRESERVED_TEST_SETTING));
+    }
+
+    private byte[] generateBackupData(Map<String, String> keyValueData) {
+        int totalBytes = 0;
+        for (String key : keyValueData.keySet()) {
+            totalBytes += 2 * Integer.BYTES + key.getBytes().length
+                    + keyValueData.get(key).getBytes().length;
+        }
+
+        ByteBuffer buffer = ByteBuffer.allocate(totalBytes);
+        for (String key : keyValueData.keySet()) {
+            byte[] keyBytes = key.getBytes();
+            byte[] valueBytes = keyValueData.get(key).getBytes();
+            buffer.putInt(keyBytes.length);
+            buffer.put(keyBytes);
+            buffer.putInt(valueBytes.length);
+            buffer.put(valueBytes);
+        }
+
+        return buffer.array();
+    }
+
     private byte[] generateUncorruptedHeader() throws IOException {
         try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
             mAgentUnderTest.writeHeader(os);
@@ -219,6 +277,7 @@
     private static class TestFriendlySettingsBackupAgent extends SettingsBackupAgent {
         private Boolean mForcedDeviceInfoRestoreAcceptability = null;
         private String[] mBlockedSettings = null;
+        private SettingsBackupWhitelist mSettingsWhitelist = null;
 
         void setForcedDeviceInfoRestoreAcceptability(boolean value) {
             mForcedDeviceInfoRestoreAcceptability = value;
@@ -228,6 +287,10 @@
             mBlockedSettings = blockedSettings;
         }
 
+        void setSettingsWhitelist(SettingsBackupWhitelist settingsWhitelist) {
+            mSettingsWhitelist = settingsWhitelist;
+        }
+
         @Override
         protected Set<String> getBlockedSettings(int blockedSettingsArrayId) {
             return mBlockedSettings == null
@@ -241,6 +304,15 @@
                     ? super.isSourceAcceptable(data, pos)
                     : mForcedDeviceInfoRestoreAcceptability;
         }
+
+        @Override
+        SettingsBackupWhitelist getBackupWhitelist(Uri contentUri) {
+            if (mSettingsWhitelist == null) {
+                return super.getBackupWhitelist(contentUri);
+            }
+
+            return mSettingsWhitelist;
+        }
     }
 
     /** The TestSettingsHelper tracks which values have been backed up and/or restored. */
@@ -257,7 +329,7 @@
         @Override
         public String onBackupValue(String key, String value) {
             mReadEntries.add(key);
-            String readValue = TEST_VALUES.get(key);
+            String readValue = DEVICE_SPECIFIC_TEST_VALUES.get(key);
             assert readValue != null;
             return readValue;
         }
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/DejankUtils.java b/packages/SystemUI/src/com/android/systemui/DejankUtils.java
index 97578e1..3fce55f 100644
--- a/packages/SystemUI/src/com/android/systemui/DejankUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/DejankUtils.java
@@ -61,19 +61,21 @@
                         || !isMainThread() || sTemporarilyIgnoreStrictMode) {
                     return null;
                 }
+            }
 
-                try {
-                    String description = binder.getInterfaceDescriptor();
+            try {
+                String description = binder.getInterfaceDescriptor();
+                synchronized (sLock) {
                     if (sWhitelistedFrameworkClasses.contains(description)) {
                         return null;
                     }
-                } catch (RemoteException e) {
-                    e.printStackTrace();
                 }
-
-                StrictMode.noteSlowCall("IPC detected on critical path: " + sBlockingIpcs.peek());
-                return null;
+            } catch (RemoteException e) {
+                e.printStackTrace();
             }
+
+            StrictMode.noteSlowCall("IPC detected on critical path: " + sBlockingIpcs.peek());
+            return null;
         }
 
         @Override
@@ -126,9 +128,11 @@
         if (STRICT_MODE_ENABLED && sBlockingIpcs.empty()) {
             synchronized (sLock) {
                 sBlockingIpcs.push("detectBlockingIpcs");
-                try {
-                    runnable.run();
-                } finally {
+            }
+            try {
+                runnable.run();
+            } finally {
+                synchronized (sLock) {
                     sBlockingIpcs.pop();
                 }
             }
@@ -177,9 +181,11 @@
         if (STRICT_MODE_ENABLED && !sTemporarilyIgnoreStrictMode) {
             synchronized (sLock) {
                 sTemporarilyIgnoreStrictMode = true;
-                try {
-                    runnable.run();
-                } finally {
+            }
+            try {
+                runnable.run();
+            } finally {
+                synchronized (sLock) {
                     sTemporarilyIgnoreStrictMode = false;
                 }
             }
@@ -196,14 +202,16 @@
         if (STRICT_MODE_ENABLED && !sTemporarilyIgnoreStrictMode) {
             synchronized (sLock) {
                 sTemporarilyIgnoreStrictMode = true;
-                final T val;
-                try {
-                    val = supplier.get();
-                } finally {
+            }
+            final T val;
+            try {
+                val = supplier.get();
+            } finally {
+                synchronized (sLock) {
                     sTemporarilyIgnoreStrictMode = false;
                 }
-                return val;
             }
+            return val;
         } else {
             return supplier.get();
         }
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/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java
index 7dc5c5f..020b32a 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/TetheringService.java
@@ -77,8 +77,8 @@
         mLog.mark("onCreate");
         mDeps = getTetheringDependencies();
         mContext = mDeps.getContext();
-        mTethering = makeTethering(mDeps);
         mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+        mTethering = makeTethering(mDeps);
     }
 
     /**
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java b/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java
index 1fe162c..5d170d3 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/GestureManifold.java
@@ -18,9 +18,17 @@
 
 import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_DOUBLE_TAP;
 import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SINGLE_TAP;
+import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_DOWN;
+import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_LEFT;
+import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_RIGHT;
+import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_SWIPE_UP;
 import static android.accessibilityservice.AccessibilityService.GESTURE_2_FINGER_TRIPLE_TAP;
 import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_DOUBLE_TAP;
 import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SINGLE_TAP;
+import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SWIPE_DOWN;
+import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SWIPE_LEFT;
+import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SWIPE_RIGHT;
+import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_SWIPE_UP;
 import static android.accessibilityservice.AccessibilityService.GESTURE_3_FINGER_TRIPLE_TAP;
 import static android.accessibilityservice.AccessibilityService.GESTURE_DOUBLE_TAP;
 import static android.accessibilityservice.AccessibilityService.GESTURE_DOUBLE_TAP_AND_HOLD;
@@ -110,6 +118,7 @@
         mGestures.add(new Swipe(context, UP, DOWN, GESTURE_SWIPE_UP_AND_DOWN, this));
         mGestures.add(new Swipe(context, UP, LEFT, GESTURE_SWIPE_UP_AND_LEFT, this));
         mGestures.add(new Swipe(context, UP, RIGHT, GESTURE_SWIPE_UP_AND_RIGHT, this));
+        // Set up multi-finger gestures to be enabled later.
         // Two-finger taps.
         mMultiFingerGestures.add(
                 new MultiFingerMultiTap(mContext, 2, 1, GESTURE_2_FINGER_SINGLE_TAP, this));
@@ -124,6 +133,24 @@
                 new MultiFingerMultiTap(mContext, 3, 2, GESTURE_3_FINGER_DOUBLE_TAP, this));
         mMultiFingerGestures.add(
                 new MultiFingerMultiTap(mContext, 3, 3, GESTURE_3_FINGER_TRIPLE_TAP, this));
+        // Two-finger swipes.
+        mMultiFingerGestures.add(
+                new MultiFingerSwipe(context, 2, DOWN, GESTURE_2_FINGER_SWIPE_DOWN, this));
+        mMultiFingerGestures.add(
+                new MultiFingerSwipe(context, 2, LEFT, GESTURE_2_FINGER_SWIPE_LEFT, this));
+        mMultiFingerGestures.add(
+                new MultiFingerSwipe(context, 2, RIGHT, GESTURE_2_FINGER_SWIPE_RIGHT, this));
+        mMultiFingerGestures.add(
+                new MultiFingerSwipe(context, 2, UP, GESTURE_2_FINGER_SWIPE_UP, this));
+        // Three-finger swipes.
+        mMultiFingerGestures.add(
+                new MultiFingerSwipe(context, 3, DOWN, GESTURE_3_FINGER_SWIPE_DOWN, this));
+        mMultiFingerGestures.add(
+                new MultiFingerSwipe(context, 3, LEFT, GESTURE_3_FINGER_SWIPE_LEFT, this));
+        mMultiFingerGestures.add(
+                new MultiFingerSwipe(context, 3, RIGHT, GESTURE_3_FINGER_SWIPE_RIGHT, this));
+        mMultiFingerGestures.add(
+                new MultiFingerSwipe(context, 3, UP, GESTURE_3_FINGER_SWIPE_UP, this));
     }
 
     /**
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/GestureUtils.java b/services/accessibility/java/com/android/server/accessibility/gestures/GestureUtils.java
index 0f5dd08..ac67480 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/GestureUtils.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/GestureUtils.java
@@ -8,6 +8,9 @@
  */
 public final class GestureUtils {
 
+    public static int MM_PER_CM = 10;
+    public static float CM_PER_INCH = 2.54f;
+
     private GestureUtils() {
         /* cannot be instantiated */
     }
@@ -85,4 +88,12 @@
 
         return true;
     }
+
+    /**
+     * Gets the index of the pointer that went up or down from a motion event.
+     */
+    public static int getActionIndex(MotionEvent event) {
+        return (event.getAction()
+                & MotionEvent.ACTION_POINTER_INDEX_MASK) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
+    }
 }
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerSwipe.java b/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerSwipe.java
new file mode 100644
index 0000000..8249239
--- /dev/null
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/MultiFingerSwipe.java
@@ -0,0 +1,496 @@
+/*
+ * 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.accessibility.gestures;
+
+import static android.view.MotionEvent.INVALID_POINTER_ID;
+
+import static com.android.server.accessibility.gestures.GestureUtils.MM_PER_CM;
+import static com.android.server.accessibility.gestures.GestureUtils.getActionIndex;
+import static com.android.server.accessibility.gestures.Swipe.CANCEL_ON_PAUSE_THRESHOLD_NOT_STARTED_MS;
+import static com.android.server.accessibility.gestures.Swipe.CANCEL_ON_PAUSE_THRESHOLD_STARTED_MS;
+import static com.android.server.accessibility.gestures.Swipe.GESTURE_CONFIRM_CM;
+import static com.android.server.accessibility.gestures.TouchExplorer.DEBUG;
+
+import android.content.Context;
+import android.graphics.PointF;
+import android.os.Handler;
+import android.util.DisplayMetrics;
+import android.util.Slog;
+import android.util.TypedValue;
+import android.view.MotionEvent;
+import android.view.ViewConfiguration;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * This class is responsible for matching one-finger swipe gestures. Each instance matches one swipe
+ * gesture. A swipe is specified as a series of one or more directions e.g. left, left and up, etc.
+ * At this time swipes with more than two directions are not supported.
+ */
+class MultiFingerSwipe extends GestureMatcher {
+
+    // Direction constants.
+    public static final int LEFT = 0;
+    public static final int RIGHT = 1;
+    public static final int UP = 2;
+    public static final int DOWN = 3;
+    // This is the calculated movement threshold used track if the user is still
+    // moving their finger.
+    private final float mGestureDetectionThresholdPixels;
+
+    // Buffer for storing points for gesture detection.
+    private final ArrayList<PointF>[] mStrokeBuffers;
+
+    // The swipe direction for this matcher.
+    private int mDirection;
+    private int[] mPointerIds;
+    // The starting point of each finger's path in the gesture.
+    private PointF[] mBase;
+    // The most recent entry in each finger's gesture path.
+    private PointF[] mPreviousGesturePoint;
+    private int mTargetFingerCount;
+    private int mCurrentFingerCount;
+    // Whether the appropriate number of fingers have gone down at some point. This is reset only on
+    // clear.
+    private boolean mTargetFingerCountReached = false;
+    // Constants for sampling motion event points.
+    // We sample based on a minimum distance between points, primarily to improve accuracy by
+    // reducing noisy minor changes in direction.
+    private static final float MIN_CM_BETWEEN_SAMPLES = 0.25f;
+    private final float mMinPixelsBetweenSamplesX;
+    private final float mMinPixelsBetweenSamplesY;
+    // The minmimum distance the finger must travel before we evaluate the initial direction of the
+    // swipe.
+    // Anything less is still considered a touch.
+    private int mTouchSlop;
+
+    MultiFingerSwipe(
+            Context context,
+            int fingerCount,
+            int direction,
+            int gesture,
+            GestureMatcher.StateChangeListener listener) {
+        super(gesture, new Handler(context.getMainLooper()), listener);
+        mTargetFingerCount = fingerCount;
+        mPointerIds = new int[mTargetFingerCount];
+        mBase = new PointF[mTargetFingerCount];
+        mPreviousGesturePoint = new PointF[mTargetFingerCount];
+        mStrokeBuffers = new ArrayList[mTargetFingerCount];
+        mDirection = direction;
+        DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
+        mGestureDetectionThresholdPixels =
+                TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_MM, MM_PER_CM, displayMetrics)
+                        * GESTURE_CONFIRM_CM;
+        // Calculate minimum gesture velocity
+        final float pixelsPerCmX = displayMetrics.xdpi / GestureUtils.CM_PER_INCH;
+        final float pixelsPerCmY = displayMetrics.ydpi / GestureUtils.CM_PER_INCH;
+        mMinPixelsBetweenSamplesX = MIN_CM_BETWEEN_SAMPLES * pixelsPerCmX;
+        mMinPixelsBetweenSamplesY = MIN_CM_BETWEEN_SAMPLES * pixelsPerCmY;
+        mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
+        clear();
+    }
+
+    @Override
+    protected void clear() {
+        mTargetFingerCountReached = false;
+        mCurrentFingerCount = 0;
+        for (int i = 0; i < mTargetFingerCount; ++i) {
+            mPointerIds[i] = INVALID_POINTER_ID;
+            if (mBase[i] == null) {
+                mBase[i] = new PointF();
+            }
+            mBase[i].x = Float.NaN;
+            mBase[i].y = Float.NaN;
+            if (mPreviousGesturePoint[i] == null) {
+                mPreviousGesturePoint[i] = new PointF();
+            }
+            mPreviousGesturePoint[i].x = Float.NaN;
+            mPreviousGesturePoint[i].y = Float.NaN;
+            if (mStrokeBuffers[i] == null) {
+                mStrokeBuffers[i] = new ArrayList<>(100);
+            }
+            mStrokeBuffers[i].clear();
+        }
+        super.clear();
+    }
+
+    @Override
+    protected void onDown(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+        if (mCurrentFingerCount > 0) {
+            cancelGesture(event, rawEvent, policyFlags);
+            return;
+        }
+        mCurrentFingerCount = 1;
+        final int actionIndex = getActionIndex(rawEvent);
+        final int pointerId = rawEvent.getPointerId(actionIndex);
+        int pointerIndex = rawEvent.getPointerCount() - 1;
+        if (pointerId < 0 || pointerId > rawEvent.getPointerCount() - 1) {
+            // Nonsensical pointer id.
+            cancelGesture(event, rawEvent, policyFlags);
+            return;
+        }
+        if (mPointerIds[pointerIndex] != INVALID_POINTER_ID) {
+            // Inconsistent event stream.
+            cancelGesture(event, rawEvent, policyFlags);
+            return;
+        }
+        mPointerIds[pointerIndex] = pointerId;
+        cancelAfterPauseThreshold(event, rawEvent, policyFlags);
+        if (Float.isNaN(mBase[pointerIndex].x) && Float.isNaN(mBase[pointerIndex].y)) {
+            final float x = rawEvent.getX(actionIndex);
+            final float y = rawEvent.getY(actionIndex);
+            if (x < 0f || y < 0f) {
+                cancelGesture(event, rawEvent, policyFlags);
+                return;
+            }
+            mBase[pointerIndex].x = x;
+            mBase[pointerIndex].y = y;
+            mPreviousGesturePoint[pointerIndex].x = x;
+            mPreviousGesturePoint[pointerIndex].y = y;
+        } else {
+            // This  event doesn't make sense in the middle of a gesture.
+            cancelGesture(event, rawEvent, policyFlags);
+            return;
+        }
+    }
+
+    @Override
+    protected void onPointerDown(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+        if (event.getPointerCount() > mTargetFingerCount) {
+            cancelGesture(event, rawEvent, policyFlags);
+            return;
+        }
+        mCurrentFingerCount += 1;
+        if (mCurrentFingerCount != rawEvent.getPointerCount()) {
+            cancelGesture(event, rawEvent, policyFlags);
+            return;
+        }
+        if (mCurrentFingerCount == mTargetFingerCount) {
+            mTargetFingerCountReached = true;
+        }
+        final int actionIndex = getActionIndex(rawEvent);
+        final int pointerId = rawEvent.getPointerId(actionIndex);
+        if (pointerId < 0 || pointerId > rawEvent.getPointerCount() - 1) {
+            // Nonsensical pointer id.
+            cancelGesture(event, rawEvent, policyFlags);
+            return;
+        }
+        int pointerIndex = mCurrentFingerCount - 1;
+        if (mPointerIds[pointerIndex] != INVALID_POINTER_ID) {
+            // Inconsistent event stream.
+            cancelGesture(event, rawEvent, policyFlags);
+            return;
+        }
+        mPointerIds[pointerIndex] = pointerId;
+        cancelAfterPauseThreshold(event, rawEvent, policyFlags);
+        if (Float.isNaN(mBase[pointerIndex].x) && Float.isNaN(mBase[pointerIndex].y)) {
+            final float x = rawEvent.getX(actionIndex);
+            final float y = rawEvent.getY(actionIndex);
+            if (x < 0f || y < 0f) {
+                cancelGesture(event, rawEvent, policyFlags);
+                return;
+            }
+            mBase[pointerIndex].x = x;
+            mBase[pointerIndex].y = y;
+            mPreviousGesturePoint[pointerIndex].x = x;
+            mPreviousGesturePoint[pointerIndex].y = y;
+        } else {
+            cancelGesture(event, rawEvent, policyFlags);
+            return;
+        }
+    }
+
+    @Override
+    protected void onPointerUp(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+        if (!mTargetFingerCountReached) {
+            cancelGesture(event, rawEvent, policyFlags);
+            return;
+        }
+        mCurrentFingerCount -= 1;
+        final int actionIndex = getActionIndex(event);
+        final int pointerId = event.getPointerId(actionIndex);
+        if (pointerId < 0 || pointerId > rawEvent.getPointerCount() - 1) {
+            // Nonsensical pointer id.
+            cancelGesture(event, rawEvent, policyFlags);
+            return;
+        }
+        final int pointerIndex = Arrays.binarySearch(mPointerIds, pointerId);
+        if (pointerIndex < 0) {
+            cancelGesture(event, rawEvent, policyFlags);
+            return;
+        }
+        final float x = rawEvent.getX(actionIndex);
+        final float y = rawEvent.getY(actionIndex);
+        if (x < 0f || y < 0f) {
+            cancelGesture(event, rawEvent, policyFlags);
+            return;
+        }
+        final float dX = Math.abs(x - mPreviousGesturePoint[pointerIndex].x);
+        final float dY = Math.abs(y - mPreviousGesturePoint[pointerIndex].y);
+        if (dX >= mMinPixelsBetweenSamplesX || dY >= mMinPixelsBetweenSamplesY) {
+            mStrokeBuffers[pointerIndex].add(new PointF(x, y));
+        }
+        // We will evaluate all the paths on ACTION_UP.
+    }
+
+    @Override
+    protected void onMove(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+        for (int pointerIndex = 0; pointerIndex < rawEvent.getPointerCount(); ++pointerIndex) {
+            if (DEBUG) {
+                Slog.d(getGestureName(), "Processing move on finger " + pointerIndex);
+            }
+            int index = rawEvent.findPointerIndex(mPointerIds[pointerIndex]);
+            final float x = rawEvent.getX(index);
+            final float y = rawEvent.getY(index);
+            if (x < 0f || y < 0f) {
+                cancelGesture(event, rawEvent, policyFlags);
+                return;
+            }
+            final float dX = Math.abs(x - mPreviousGesturePoint[pointerIndex].x);
+            final float dY = Math.abs(y - mPreviousGesturePoint[pointerIndex].y);
+            final double moveDelta =
+                    Math.hypot(
+                            Math.abs(x - mBase[pointerIndex].x),
+                            Math.abs(y - mBase[pointerIndex].y));
+            if (DEBUG) {
+                Slog.d(
+                        getGestureName(),
+                        "moveDelta:"
+                                + Double.toString(moveDelta)
+                                + " mGestureDetectionThreshold: "
+                                + Float.toString(mGestureDetectionThresholdPixels));
+            }
+            if (getState() == STATE_CLEAR) {
+                if (moveDelta < mTouchSlop) {
+                    // This still counts as a touch not a swipe.
+                    continue;
+                } else if (mStrokeBuffers[pointerIndex].size() == 0) {
+                    // First, make sure we have the right number of fingers down.
+                    if (mCurrentFingerCount != mTargetFingerCount) {
+                        cancelGesture(event, rawEvent, policyFlags);
+                        return;
+                    }
+                    // Then, make sure the pointer is going in the right direction.
+                    int direction =
+                            toDirection(x - mBase[pointerIndex].x, y - mBase[pointerIndex].y);
+                    if (direction != mDirection) {
+                        cancelGesture(event, rawEvent, policyFlags);
+                        return;
+                    } else {
+                        // This is confirmed to be some kind of swipe so start tracking points.
+                        cancelAfterPauseThreshold(event, rawEvent, policyFlags);
+                        for (int i = 0; i < mTargetFingerCount; ++i) {
+                            mStrokeBuffers[i].add(new PointF(mBase[i]));
+                        }
+                    }
+                }
+                if (moveDelta > mGestureDetectionThresholdPixels) {
+                    // Try to cancel if the finger starts to go the wrong way.
+                    // Note that this only works because this matcher assumes one direction.
+                    int direction =
+                            toDirection(x - mBase[pointerIndex].x, y - mBase[pointerIndex].y);
+                    if (direction != mDirection) {
+                        cancelGesture(event, rawEvent, policyFlags);
+                        return;
+                    }
+                    // If the pointer has moved more than the threshold,
+                    // update the stored values.
+                    mBase[pointerIndex].x = x;
+                    mBase[pointerIndex].y = y;
+                    mPreviousGesturePoint[pointerIndex].x = x;
+                    mPreviousGesturePoint[pointerIndex].y = y;
+                    if (getState() == STATE_CLEAR) {
+                        startGesture(event, rawEvent, policyFlags);
+                        cancelAfterPauseThreshold(event, rawEvent, policyFlags);
+                    }
+                }
+            }
+            if (getState() == STATE_GESTURE_STARTED) {
+                if (dX >= mMinPixelsBetweenSamplesX || dY >= mMinPixelsBetweenSamplesY) {
+                    // Sample every 2.5 MM in order to guard against minor variations in path.
+                    mPreviousGesturePoint[pointerIndex].x = x;
+                    mPreviousGesturePoint[pointerIndex].y = y;
+                    mStrokeBuffers[pointerIndex].add(new PointF(x, y));
+                    cancelAfterPauseThreshold(event, rawEvent, policyFlags);
+                }
+            }
+        }
+    }
+
+    @Override
+    protected void onUp(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+        if (getState() != STATE_GESTURE_STARTED) {
+            cancelGesture(event, rawEvent, policyFlags);
+            return;
+        }
+        mCurrentFingerCount = 0;
+        final int actionIndex = getActionIndex(event);
+        final int pointerId = event.getPointerId(actionIndex);
+        final int pointerIndex = Arrays.binarySearch(mPointerIds, pointerId);
+        if (pointerIndex < 0) {
+            cancelGesture(event, rawEvent, policyFlags);
+            return;
+        }
+        final float x = rawEvent.getX(actionIndex);
+        final float y = rawEvent.getY(actionIndex);
+        if (x < 0f || y < 0f) {
+            cancelGesture(event, rawEvent, policyFlags);
+            return;
+        }
+        final float dX = Math.abs(x - mPreviousGesturePoint[pointerIndex].x);
+        final float dY = Math.abs(y - mPreviousGesturePoint[pointerIndex].y);
+        if (dX >= mMinPixelsBetweenSamplesX || dY >= mMinPixelsBetweenSamplesY) {
+            mStrokeBuffers[pointerIndex].add(new PointF(x, y));
+        }
+        recognizeGesture(event, rawEvent, policyFlags);
+    }
+
+    /**
+     * queues a transition to STATE_GESTURE_CANCEL based on the current state. If we have
+     * transitioned to STATE_GESTURE_STARTED the delay is longer.
+     */
+    private void cancelAfterPauseThreshold(
+            MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+        cancelPendingTransitions();
+        switch (getState()) {
+            case STATE_CLEAR:
+                cancelAfter(CANCEL_ON_PAUSE_THRESHOLD_NOT_STARTED_MS, event, rawEvent, policyFlags);
+                break;
+            case STATE_GESTURE_STARTED:
+                cancelAfter(CANCEL_ON_PAUSE_THRESHOLD_STARTED_MS, event, rawEvent, policyFlags);
+                break;
+            default:
+                break;
+        }
+    }
+    /**
+     * Looks at the sequence of motions in mStrokeBuffer, classifies the gesture, then transitions
+     * to the complete or cancel state depending on the result.
+     */
+    private void recognizeGesture(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+        // Check the path of each finger against the specified direction.
+        // Note that we sample every 2.5 MMm, and the direction matching is extremely tolerant (each
+        // direction has a 90-degree arch of tolerance) meaning that minor perpendicular movements
+        // should not create false negatives.
+        for (int i = 0; i < mTargetFingerCount; ++i) {
+            if (DEBUG) {
+                Slog.d(getGestureName(), "Recognizing finger: " + i);
+            }
+            if (mStrokeBuffers[i].size() < 2) {
+                Slog.d(getGestureName(), "Too few points.");
+                cancelGesture(event, rawEvent, policyFlags);
+                return;
+            }
+            ArrayList<PointF> path = mStrokeBuffers[i];
+
+            if (DEBUG) {
+                Slog.d(getGestureName(), "path=" + path.toString());
+            }
+            // Classify line segments, and call Listener callbacks.
+            if (!recognizeGesturePath(event, rawEvent, policyFlags, path)) {
+                cancelGesture(event, rawEvent, policyFlags);
+                return;
+            }
+        }
+        // If we reach this point then all paths match.
+        completeGesture(event, rawEvent, policyFlags);
+    }
+
+    /**
+     * Tests the path of a given finger against the direction specified in this matcher.
+     *
+     * @return True if the path matches the specified direction for this matcher, otherwise false.
+     */
+    private boolean recognizeGesturePath(
+            MotionEvent event, MotionEvent rawEvent, int policyFlags, ArrayList<PointF> path) {
+
+        final int displayId = event.getDisplayId();
+        for (int i = 0; i < path.size() - 1; ++i) {
+            PointF start = path.get(i);
+            PointF end = path.get(i + 1);
+
+            float dX = end.x - start.x;
+            float dY = end.y - start.y;
+            int direction = toDirection(dX, dY);
+            if (direction != mDirection) {
+                if (DEBUG) {
+                    Slog.d(
+                            getGestureName(),
+                            "Found direction "
+                                    + directionToString(direction)
+                                    + " when expecting "
+                                    + directionToString(mDirection));
+                }
+                return false;
+            }
+        }
+        if (DEBUG) {
+            Slog.d(getGestureName(), "Completed.");
+        }
+        return true;
+    }
+
+    private static int toDirection(float dX, float dY) {
+        if (Math.abs(dX) > Math.abs(dY)) {
+            // Horizontal
+            return (dX < 0) ? LEFT : RIGHT;
+        } else {
+            // Vertical
+            return (dY < 0) ? UP : DOWN;
+        }
+    }
+
+    public static String directionToString(int direction) {
+        switch (direction) {
+            case LEFT:
+                return "left";
+            case RIGHT:
+                return "right";
+            case UP:
+                return "up";
+            case DOWN:
+                return "down";
+            default:
+                return "Unknown Direction";
+        }
+    }
+
+    @Override
+    String getGestureName() {
+        StringBuilder builder = new StringBuilder();
+        builder.append(mTargetFingerCount).append("-finger ");
+        builder.append("Swipe ").append(directionToString(mDirection));
+        return builder.toString();
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder builder = new StringBuilder(super.toString());
+        if (getState() != STATE_GESTURE_CANCELED) {
+            builder.append(", mBase: ")
+                    .append(mBase.toString())
+                    .append(", mGestureDetectionThreshold:")
+                    .append(mGestureDetectionThresholdPixels)
+                    .append(", mMinPixelsBetweenSamplesX:")
+                    .append(mMinPixelsBetweenSamplesX)
+                    .append(", mMinPixelsBetweenSamplesY:")
+                    .append(mMinPixelsBetweenSamplesY);
+        }
+        return builder.toString();
+    }
+}
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/SecondFingerMultiTap.java b/services/accessibility/java/com/android/server/accessibility/gestures/SecondFingerMultiTap.java
index 8a566fc..ada251f 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/SecondFingerMultiTap.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/SecondFingerMultiTap.java
@@ -18,6 +18,8 @@
 
 import static android.view.MotionEvent.INVALID_POINTER_ID;
 
+import static com.android.server.accessibility.gestures.GestureUtils.getActionIndex;
+
 import android.content.Context;
 import android.os.Handler;
 import android.view.MotionEvent;
@@ -155,11 +157,6 @@
         return moveDelta <= slop;
     }
 
-    private int getActionIndex(MotionEvent event) {
-        return event.getAction()
-                & MotionEvent.ACTION_POINTER_INDEX_MASK << MotionEvent.ACTION_POINTER_INDEX_SHIFT;
-    }
-
     @Override
     public String toString() {
         return super.toString()
diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/Swipe.java b/services/accessibility/java/com/android/server/accessibility/gestures/Swipe.java
index a285c10..9108c69 100644
--- a/services/accessibility/java/com/android/server/accessibility/gestures/Swipe.java
+++ b/services/accessibility/java/com/android/server/accessibility/gestures/Swipe.java
@@ -16,10 +16,10 @@
 
 package com.android.server.accessibility.gestures;
 
+import static com.android.server.accessibility.gestures.GestureUtils.MM_PER_CM;
 import static com.android.server.accessibility.gestures.TouchExplorer.DEBUG;
 
 import android.content.Context;
-import android.gesture.GesturePoint;
 import android.graphics.PointF;
 import android.os.Handler;
 import android.util.DisplayMetrics;
@@ -44,10 +44,10 @@
     public static final int DOWN = 3;
     // This is the calculated movement threshold used track if the user is still
     // moving their finger.
-    private final float mGestureDetectionThreshold;
+    private final float mGestureDetectionThresholdPixels;
 
     // Buffer for storing points for gesture detection.
-    private final ArrayList<GesturePoint> mStrokeBuffer = new ArrayList<GesturePoint>(100);
+    private final ArrayList<PointF> mStrokeBuffer = new ArrayList<>(100);
 
     // The minimal delta between moves to add a gesture point.
     private static final int TOUCH_TOLERANCE_PIX = 3;
@@ -56,7 +56,7 @@
     private static final float MIN_PREDICTION_SCORE = 2.0f;
 
     // Distance a finger must travel before we decide if it is a gesture or not.
-    private static final int GESTURE_CONFIRM_CM = 1;
+    public static final int GESTURE_CONFIRM_CM = 1;
 
     // Time threshold used to determine if an interaction is a gesture or not.
     // If the first movement of 1cm takes longer than this value, we assume it's
@@ -67,17 +67,16 @@
     // all gestures started with the initial movement taking less than 100ms.
     // When touch exploring, the first movement almost always takes longer than
     // 200ms.
-    private static final long CANCEL_ON_PAUSE_THRESHOLD_NOT_STARTED_MS = 150;
+    public static final long CANCEL_ON_PAUSE_THRESHOLD_NOT_STARTED_MS = 150;
 
     // Time threshold used to determine if a gesture should be cancelled.  If
     // the finger takes more than this time to move 1cm, the ongoing gesture is
     // cancelled.
-    private static final long CANCEL_ON_PAUSE_THRESHOLD_STARTED_MS = 300;
+    public static final long CANCEL_ON_PAUSE_THRESHOLD_STARTED_MS = 300;
 
     private int[] mDirections;
     private float mBaseX;
     private float mBaseY;
-    private long mBaseTime;
     private float mPreviousGestureX;
     private float mPreviousGestureY;
     // Constants for sampling motion event points.
@@ -119,8 +118,8 @@
         super(gesture, new Handler(context.getMainLooper()), listener);
         mDirections = directions;
         DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics();
-        mGestureDetectionThreshold =
-                TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_MM, 10, displayMetrics)
+        mGestureDetectionThresholdPixels =
+                TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_MM, MM_PER_CM, displayMetrics)
                         * GESTURE_CONFIRM_CM;
         // Calculate minimum gesture velocity
         final float pixelsPerCmX = displayMetrics.xdpi / 2.54f;
@@ -135,18 +134,16 @@
     protected void clear() {
         mBaseX = Float.NaN;
         mBaseY = Float.NaN;
-        mBaseTime = 0;
         mStrokeBuffer.clear();
         super.clear();
     }
 
     @Override
     protected void onDown(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
-        cancelAfterDelay(event, rawEvent, policyFlags);
+        cancelAfterPauseThreshold(event, rawEvent, policyFlags);
         if (Float.isNaN(mBaseX) && Float.isNaN(mBaseY)) {
             mBaseX = rawEvent.getX();
             mBaseY = rawEvent.getY();
-            mBaseTime = event.getEventTime();
             mPreviousGestureX = mBaseX;
             mPreviousGestureY = mBaseY;
         }
@@ -157,10 +154,8 @@
     protected void onMove(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
         final float x = rawEvent.getX();
         final float y = rawEvent.getY();
-        final long time = event.getEventTime();
         final float dX = Math.abs(x - mPreviousGestureX);
         final float dY = Math.abs(y - mPreviousGestureY);
-        final long timeDelta = time - mBaseTime;
         final double moveDelta = Math.hypot(Math.abs(x - mBaseX), Math.abs(y - mBaseY));
         if (DEBUG) {
             Slog.d(
@@ -168,7 +163,7 @@
                     "moveDelta:"
                             + Double.toString(moveDelta)
                             + " mGestureDetectionThreshold: "
-                            + Float.toString(mGestureDetectionThreshold));
+                            + Float.toString(mGestureDetectionThresholdPixels));
         }
         if (getState() == STATE_CLEAR) {
             if (moveDelta < mTouchSlop) {
@@ -176,25 +171,24 @@
                 return;
             } else if (mStrokeBuffer.size() == 0) {
                 // First, make sure the pointer is going in the right direction.
-                cancelAfterDelay(event, rawEvent, policyFlags);
+                cancelAfterPauseThreshold(event, rawEvent, policyFlags);
                 int direction = toDirection(x - mBaseX, y - mBaseY);
                 if (direction != mDirections[0]) {
                     cancelGesture(event, rawEvent, policyFlags);
                     return;
                 } else {
                     // This is confirmed to be some kind of swipe so start tracking points.
-                    mStrokeBuffer.add(new GesturePoint(mBaseX, mBaseY, mBaseTime));
+                    mStrokeBuffer.add(new PointF(mBaseX, mBaseY));
                 }
             }
-            if (moveDelta > mGestureDetectionThreshold) {
+            if (moveDelta > mGestureDetectionThresholdPixels) {
                 // If the pointer has moved more than the threshold,
                 // update the stored values.
                 mBaseX = x;
                 mBaseY = y;
-                mBaseTime = time;
                 if (getState() == STATE_CLEAR) {
                     startGesture(event, rawEvent, policyFlags);
-                    cancelAfterDelay(event, rawEvent, policyFlags);
+                    cancelAfterPauseThreshold(event, rawEvent, policyFlags);
                 }
             }
         }
@@ -202,8 +196,8 @@
             if (dX >= mMinPixelsBetweenSamplesX || dY >= mMinPixelsBetweenSamplesY) {
                 mPreviousGestureX = x;
                 mPreviousGestureY = y;
-                mStrokeBuffer.add(new GesturePoint(x, y, time));
-                cancelAfterDelay(event, rawEvent, policyFlags);
+                mStrokeBuffer.add(new PointF(x, y));
+                cancelAfterPauseThreshold(event, rawEvent, policyFlags);
             }
         }
     }
@@ -217,11 +211,10 @@
 
         final float x = rawEvent.getX();
         final float y = rawEvent.getY();
-        final long time = event.getEventTime();
         final float dX = Math.abs(x - mPreviousGestureX);
         final float dY = Math.abs(y - mPreviousGestureY);
         if (dX >= mMinPixelsBetweenSamplesX || dY >= mMinPixelsBetweenSamplesY) {
-            mStrokeBuffer.add(new GesturePoint(x, y, time));
+            mStrokeBuffer.add(new PointF(x, y));
         }
         recognizeGesture(event, rawEvent, policyFlags);
     }
@@ -240,7 +233,8 @@
      * queues a transition to STATE_GESTURE_CANCEL based on the current state. If we have
      * transitioned to STATE_GESTURE_STARTED the delay is longer.
      */
-    private void cancelAfterDelay(MotionEvent event, MotionEvent rawEvent, int policyFlags) {
+    private void cancelAfterPauseThreshold(
+            MotionEvent event, MotionEvent rawEvent, int policyFlags) {
         cancelPendingTransitions();
         switch (getState()) {
             case STATE_CLEAR:
@@ -275,7 +269,7 @@
         // 90 degrees.
 
         ArrayList<PointF> path = new ArrayList<>();
-        PointF lastDelimiter = new PointF(mStrokeBuffer.get(0).x, mStrokeBuffer.get(0).y);
+        PointF lastDelimiter = mStrokeBuffer.get(0);
         path.add(lastDelimiter);
 
         float dX = 0; // Sum of unit vectors from last delimiter to each following point
@@ -283,9 +277,9 @@
         int count = 0; // Number of points since last delimiter
         float length = 0; // Vector length from delimiter to most recent point
 
-        PointF next = new PointF();
+        PointF next = null;
         for (int i = 1; i < mStrokeBuffer.size(); ++i) {
-            next = new PointF(mStrokeBuffer.get(i).x, mStrokeBuffer.get(i).y);
+            next = mStrokeBuffer.get(i);
             if (count > 0) {
                 // Average of unit vectors from delimiter to following points
                 float currentDX = dX / count;
@@ -428,7 +422,7 @@
                     .append(", mBaseY: ")
                     .append(mBaseY)
                     .append(", mGestureDetectionThreshold:")
-                    .append(mGestureDetectionThreshold)
+                    .append(mGestureDetectionThresholdPixels)
                     .append(", mMinPixelsBetweenSamplesX:")
                     .append(mMinPixelsBetweenSamplesX)
                     .append(", mMinPixelsBetweenSamplesY:")
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 4f18f35..1a4fc32 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -154,6 +154,13 @@
     @GuardedBy("mLock")
     private FillEventHistory mEventHistory;
 
+    /**
+     * The last inline augmented autofill selection. Note that we don't log the selection from the
+     * dropdown UI since the service owns the UI in that case.
+     */
+    @GuardedBy("mLock")
+    private FillEventHistory mAugmentedAutofillEventHistory;
+
     /** Shared instance, doesn't need to be logged */
     private final AutofillCompatState mAutofillCompatState;
 
@@ -707,6 +714,13 @@
         }
     }
 
+    void setLastAugmentedAutofillResponse(int sessionId) {
+        synchronized (mLock) {
+            mAugmentedAutofillEventHistory = new FillEventHistory(sessionId, /* clientState= */
+                    null);
+        }
+    }
+
     /**
      * Resets the last fill selection.
      */
@@ -716,6 +730,12 @@
         }
     }
 
+    void resetLastAugmentedAutofillResponse() {
+        synchronized (mLock) {
+            mAugmentedAutofillEventHistory = null;
+        }
+    }
+
     @GuardedBy("mLock")
     private boolean isValidEventLocked(String method, int sessionId) {
         if (mEventHistory == null) {
@@ -798,6 +818,32 @@
         }
     }
 
+    void logAugmentedAutofillSelected(int sessionId, @Nullable String suggestionId,
+            @Nullable Bundle clientState) {
+        synchronized (mLock) {
+            if (mAugmentedAutofillEventHistory == null
+                    || mAugmentedAutofillEventHistory.getSessionId() != sessionId) {
+                return;
+            }
+            mAugmentedAutofillEventHistory.addEvent(
+                    new Event(Event.TYPE_DATASET_SELECTED, suggestionId, clientState, null, null,
+                            null, null, null, null, null, null));
+        }
+    }
+
+    void logAugmentedAutofillShown(int sessionId, @Nullable Bundle clientState) {
+        synchronized (mLock) {
+            if (mAugmentedAutofillEventHistory == null
+                    || mAugmentedAutofillEventHistory.getSessionId() != sessionId) {
+                return;
+            }
+            mAugmentedAutofillEventHistory.addEvent(
+                    new Event(Event.TYPE_DATASETS_SHOWN, null, clientState, null, null, null,
+                            null, null, null, null, null));
+
+        }
+    }
+
     /**
      * Updates the last fill response when an autofill context is committed.
      */
@@ -881,8 +927,8 @@
      * Gets the fill event history.
      *
      * @param callingUid The calling uid
-     *
-     * @return The history or {@code null} if there is none.
+     * @return The history for the autofill or the augmented autofill events depending on the {@code
+     * callingUid}, or {@code null} if there is none.
      */
     FillEventHistory getFillEventHistory(int callingUid) {
         synchronized (mLock) {
@@ -890,6 +936,10 @@
                     && isCalledByServiceLocked("getFillEventHistory", callingUid)) {
                 return mEventHistory;
             }
+            if (mAugmentedAutofillEventHistory != null && isCalledByAugmentedAutofillServiceLocked(
+                    "getFillEventHistory", callingUid)) {
+                return mAugmentedAutofillEventHistory;
+            }
         }
         return null;
     }
@@ -1163,8 +1213,32 @@
                 Slog.v(TAG, "getRemoteAugmentedAutofillServiceLocked(): " + componentName);
             }
 
-            mRemoteAugmentedAutofillService = new RemoteAugmentedAutofillService(getContext(),
-                    componentName, mUserId, new RemoteAugmentedAutofillServiceCallbacks() {
+            final RemoteAugmentedAutofillServiceCallbacks callbacks =
+                    new RemoteAugmentedAutofillServiceCallbacks() {
+                        @Override
+                        public void resetLastResponse() {
+                            AutofillManagerServiceImpl.this.resetLastAugmentedAutofillResponse();
+                        }
+
+                        @Override
+                        public void setLastResponse(int sessionId) {
+                            AutofillManagerServiceImpl.this.setLastAugmentedAutofillResponse(
+                                    sessionId);
+                        }
+
+                        @Override
+                        public void logAugmentedAutofillShown(int sessionId, Bundle clientState) {
+                            AutofillManagerServiceImpl.this.logAugmentedAutofillShown(sessionId,
+                                    clientState);
+                        }
+
+                        @Override
+                        public void logAugmentedAutofillSelected(int sessionId, String suggestionId,
+                                Bundle clientState) {
+                            AutofillManagerServiceImpl.this.logAugmentedAutofillSelected(sessionId,
+                                    suggestionId, clientState);
+                        }
+
                         @Override
                         public void onServiceDied(@NonNull RemoteAugmentedAutofillService service) {
                             Slog.w(TAG, "remote augmented autofill service died");
@@ -1175,8 +1249,10 @@
                             }
                             mRemoteAugmentedAutofillService = null;
                         }
-                    }, mMaster.isInstantServiceAllowed(), mMaster.verbose,
-                    mMaster.mAugmentedServiceIdleUnbindTimeoutMs,
+                    };
+            mRemoteAugmentedAutofillService = new RemoteAugmentedAutofillService(getContext(),
+                    componentName, mUserId, callbacks, mMaster.isInstantServiceAllowed(),
+                    mMaster.verbose, mMaster.mAugmentedServiceIdleUnbindTimeoutMs,
                     mMaster.mAugmentedServiceRequestTimeoutMs);
         }
 
diff --git a/services/autofill/java/com/android/server/autofill/InlineSuggestionFactory.java b/services/autofill/java/com/android/server/autofill/InlineSuggestionFactory.java
index c7be80c..cb6c8f5 100644
--- a/services/autofill/java/com/android/server/autofill/InlineSuggestionFactory.java
+++ b/services/autofill/java/com/android/server/autofill/InlineSuggestionFactory.java
@@ -28,7 +28,6 @@
 import android.view.SurfaceControl;
 import android.view.View;
 import android.view.autofill.AutofillId;
-import android.view.autofill.IAutoFillManagerClient;
 import android.view.inputmethod.InlineSuggestion;
 import android.view.inputmethod.InlineSuggestionInfo;
 import android.view.inputmethod.InlineSuggestionsResponse;
@@ -49,15 +48,24 @@
     private static final String TAG = "InlineSuggestionFactory";
 
     /**
+     * Callback from the inline suggestion Ui.
+     */
+    public interface InlineSuggestionUiCallback {
+        /**
+         * Callback to autofill a dataset to the client app.
+         */
+        void autofill(@NonNull Dataset dataset);
+    }
+
+    /**
      * Creates an {@link InlineSuggestionsResponse} with the {@code datasets} provided by
      * augmented autofill service.
      */
     public static InlineSuggestionsResponse createAugmentedInlineSuggestionsResponse(
-            int sessionId,
             @NonNull Dataset[] datasets,
             @NonNull AutofillId autofillId,
             @NonNull Context context,
-            @NonNull IAutoFillManagerClient client) {
+            @NonNull InlineSuggestionUiCallback inlineSuggestionUiCallback) {
         if (sDebug) Slog.d(TAG, "createAugmentedInlineSuggestionsResponse called");
 
         final ArrayList<InlineSuggestion> inlineSuggestions = new ArrayList<>();
@@ -74,8 +82,8 @@
                 Slog.w(TAG, "InlinePresentation not found in dataset");
                 return null;
             }
-            InlineSuggestion inlineSuggestion = createAugmentedInlineSuggestion(sessionId, dataset,
-                    inlinePresentation, inlineSuggestionUi, client);
+            InlineSuggestion inlineSuggestion = createAugmentedInlineSuggestion(dataset,
+                    inlinePresentation, inlineSuggestionUi, inlineSuggestionUiCallback);
             inlineSuggestions.add(inlineSuggestion);
         }
         return new InlineSuggestionsResponse(inlineSuggestions);
@@ -114,22 +122,17 @@
         return new InlineSuggestionsResponse(inlineSuggestions);
     }
 
-    private static InlineSuggestion createAugmentedInlineSuggestion(int sessionId,
-            @NonNull Dataset dataset,
+    private static InlineSuggestion createAugmentedInlineSuggestion(@NonNull Dataset dataset,
             @NonNull InlinePresentation inlinePresentation,
             @NonNull InlineSuggestionUi inlineSuggestionUi,
-            @NonNull IAutoFillManagerClient client) {
+            @NonNull InlineSuggestionUiCallback inlineSuggestionUiCallback) {
         // TODO(b/146453195): fill in the autofill hint properly.
         final InlineSuggestionInfo inlineSuggestionInfo = new InlineSuggestionInfo(
                 inlinePresentation.getInlinePresentationSpec(),
                 InlineSuggestionInfo.SOURCE_PLATFORM, new String[]{""},
                 InlineSuggestionInfo.TYPE_SUGGESTION);
         final View.OnClickListener onClickListener = v -> {
-            try {
-                client.autofill(sessionId, dataset.getFieldIds(), dataset.getFieldValues());
-            } catch (RemoteException e) {
-                Slog.w(TAG, "Encounter exception autofilling the values");
-            }
+            inlineSuggestionUiCallback.autofill(dataset);
         };
         final InlineSuggestion inlineSuggestion = new InlineSuggestion(inlineSuggestionInfo,
                 createInlineContentProvider(inlinePresentation, inlineSuggestionUi,
diff --git a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
index 5fbdd25..5e6f6fea 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteAugmentedAutofillService.java
@@ -69,6 +69,7 @@
     private final int mIdleUnbindTimeoutMs;
     private final int mRequestTimeoutMs;
     private final ComponentName mComponentName;
+    private final RemoteAugmentedAutofillServiceCallbacks mCallbacks;
 
     RemoteAugmentedAutofillService(Context context, ComponentName serviceName,
             int userId, RemoteAugmentedAutofillServiceCallbacks callbacks,
@@ -81,6 +82,7 @@
         mIdleUnbindTimeoutMs = idleUnbindTimeoutMs;
         mRequestTimeoutMs = requestTimeoutMs;
         mComponentName = serviceName;
+        mCallbacks = callbacks;
 
         // Bind right away.
         connect();
@@ -159,9 +161,12 @@
                             focusedId, focusedValue, requestTime, inlineSuggestionsRequest,
                             new IFillCallback.Stub() {
                                 @Override
-                                public void onSuccess(@Nullable Dataset[] inlineSuggestionsData) {
-                                    maybeHandleInlineSuggestions(sessionId, inlineSuggestionsData,
-                                            focusedId, inlineSuggestionsCallback, client);
+                                public void onSuccess(@Nullable Dataset[] inlineSuggestionsData,
+                                        @Nullable Bundle clientState) {
+                                    mCallbacks.resetLastResponse();
+                                    maybeRequestShowInlineSuggestions(sessionId,
+                                            inlineSuggestionsData, focusedId,
+                                            inlineSuggestionsCallback, client, clientState);
                                     requestAutofill.complete(null);
                                 }
 
@@ -223,20 +228,32 @@
         });
     }
 
-    private void maybeHandleInlineSuggestions(int sessionId,
+    private void maybeRequestShowInlineSuggestions(int sessionId,
             @Nullable Dataset[] inlineSuggestionsData, @NonNull AutofillId focusedId,
             @Nullable IInlineSuggestionsResponseCallback inlineSuggestionsCallback,
-            @NonNull IAutoFillManagerClient client) {
+            @NonNull IAutoFillManagerClient client, @Nullable Bundle clientState) {
         if (ArrayUtils.isEmpty(inlineSuggestionsData) || inlineSuggestionsCallback == null) {
             return;
         }
+        mCallbacks.setLastResponse(sessionId);
         try {
             inlineSuggestionsCallback.onInlineSuggestionsResponse(
-                    InlineSuggestionFactory.createAugmentedInlineSuggestionsResponse(sessionId,
-                            inlineSuggestionsData, focusedId, mContext, client));
+                    InlineSuggestionFactory.createAugmentedInlineSuggestionsResponse(
+                            inlineSuggestionsData, focusedId, mContext,
+                            dataset -> {
+                                mCallbacks.logAugmentedAutofillSelected(sessionId,
+                                        dataset.getId(), clientState);
+                                try {
+                                    client.autofill(sessionId, dataset.getFieldIds(),
+                                            dataset.getFieldValues());
+                                } catch (RemoteException e) {
+                                    Slog.w(TAG, "Encounter exception autofilling the values");
+                                }
+                            }));
         } catch (RemoteException e) {
             Slog.w(TAG, "Exception sending inline suggestions response back to IME.");
         }
+        mCallbacks.logAugmentedAutofillShown(sessionId, clientState);
     }
 
     @Override
@@ -254,8 +271,13 @@
 
     public interface RemoteAugmentedAutofillServiceCallbacks
             extends AbstractRemoteService.VultureCallback<RemoteAugmentedAutofillService> {
-        // NOTE: so far we don't need to notify the callback implementation (an inner class on
-        // AutofillManagerServiceImpl) of the request results (success, timeouts, etc..), so this
-        // callback interface is empty.
+        void resetLastResponse();
+
+        void setLastResponse(int sessionId);
+
+        void logAugmentedAutofillShown(int sessionId, @Nullable Bundle clientState);
+
+        void logAugmentedAutofillSelected(int sessionId, @Nullable String suggestionId,
+                @Nullable Bundle clientState);
     }
 }
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index ee37de5..415ecd8 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -2674,7 +2674,9 @@
 
         if (response.supportsInlineSuggestions()) {
             if (requestShowInlineSuggestions(response)) {
-                //TODO(b/137800469): Add logging instead of bypassing below logic.
+                //TODO(b/137800469): Fix it to log showed only when IME asks for inflation, rather
+                // than here where framework sends back the response.
+                mService.logDatasetShown(id, mClientState);
                 return;
             }
         }
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/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index e342dd7..50843b0 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -2644,7 +2644,12 @@
             if (noGPSLocation == null && lastNoGPSLocation != null) {
                 // New location has no no-GPS location: adopt last no-GPS location. This is set
                 // directly into location because we do not want to notify COARSE clients.
-                location.setExtraLocation(Location.EXTRA_NO_GPS_LOCATION, lastNoGPSLocation);
+                Bundle extras = location.getExtras();
+                if (extras == null) {
+                    extras = new Bundle();
+                }
+                extras.putParcelable(Location.EXTRA_NO_GPS_LOCATION, lastNoGPSLocation);
+                location.setExtras(extras);
             }
         }
         lastLocation.set(location);
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 9d4c783..1bb3c3a 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -86,12 +86,12 @@
 import android.util.Slog;
 import android.util.SparseBooleanArray;
 import android.util.SparseIntArray;
-import android.util.StatsLog;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.IBatteryStats;
 import com.android.internal.util.DumpUtils;
+import com.android.internal.util.FrameworkStatsLog;
 import com.android.internal.util.HexDump;
 import com.android.internal.util.Preconditions;
 
@@ -412,8 +412,8 @@
                     getBatteryStats().noteMobileRadioPowerState(powerState, tsNanos, uid);
                 } catch (RemoteException e) {
                 }
-                StatsLog.write_non_chained(StatsLog.MOBILE_RADIO_POWER_STATE_CHANGED, uid, null,
-                        powerState);
+                FrameworkStatsLog.write_non_chained(
+                        FrameworkStatsLog.MOBILE_RADIO_POWER_STATE_CHANGED, uid, null, powerState);
             }
         }
 
@@ -424,8 +424,8 @@
                     getBatteryStats().noteWifiRadioPowerState(powerState, tsNanos, uid);
                 } catch (RemoteException e) {
                 }
-                StatsLog.write_non_chained(StatsLog.WIFI_RADIO_POWER_STATE_CHANGED, uid, null,
-                        powerState);
+                FrameworkStatsLog.write_non_chained(
+                        FrameworkStatsLog.WIFI_RADIO_POWER_STATE_CHANGED, uid, null, powerState);
             }
         }
 
diff --git a/services/core/java/com/android/server/ServiceWatcher.java b/services/core/java/com/android/server/ServiceWatcher.java
index 14cd3a5..b43ae36 100644
--- a/services/core/java/com/android/server/ServiceWatcher.java
+++ b/services/core/java/com/android/server/ServiceWatcher.java
@@ -49,6 +49,8 @@
 import com.android.internal.content.PackageMonitor;
 import com.android.internal.util.Preconditions;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
 import java.util.List;
 import java.util.Objects;
 import java.util.concurrent.Callable;
@@ -183,9 +185,7 @@
 
     // write from handler thread only, read anywhere
     private volatile ServiceInfo mServiceInfo;
-
-    // read/write from handler thread only
-    private IBinder mBinder;
+    private volatile IBinder mBinder;
 
     public ServiceWatcher(Context context, Handler handler, String action,
             @Nullable BinderRunner onBind, @Nullable Runnable onUnbind,
@@ -345,20 +345,25 @@
         }
 
         mBinder = binder;
+
+        // we always run the on bind callback even if we know that the binder is dead already so
+        // that there are always balance pairs of bind/unbind callbacks
         if (mOnBind != null) {
-            runOnBinder(mOnBind);
-        }
-    }
-
-    @Override
-    public void onBindingDied(ComponentName component) {
-        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
-
-        if (D) {
-            Log.i(TAG, getLogPrefix() + " " + component.toShortString() + " died");
+            try {
+                mOnBind.run(binder);
+            } catch (RuntimeException | RemoteException e) {
+                // binders may propagate some specific non-RemoteExceptions from the other side
+                // through the binder as well - we cannot allow those to crash the system server
+                Log.e(TAG, getLogPrefix() + " exception running on " + mServiceInfo, e);
+            }
         }
 
-        onBestServiceChanged(true);
+        try {
+            // setting the binder to null lets us skip queued transactions
+            binder.linkToDeath(() -> mBinder = null, 0);
+        } catch (RemoteException e) {
+            mBinder = null;
+        }
     }
 
     @Override
@@ -375,6 +380,17 @@
         }
     }
 
+    @Override
+    public void onBindingDied(ComponentName component) {
+        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
+
+        if (D) {
+            Log.i(TAG, getLogPrefix() + " " + component.toShortString() + " died");
+        }
+
+        onBestServiceChanged(true);
+    }
+
     private void onUserSwitched(@UserIdInt int userId) {
         mCurrentUserId = userId;
         onBestServiceChanged(false);
@@ -398,7 +414,7 @@
      * RemoteException thrown during execution.
      */
     public final void runOnBinder(BinderRunner runner) {
-        runOnHandler(() -> {
+        mHandler.post(() -> {
             if (mBinder == null) {
                 return;
             }
@@ -447,14 +463,6 @@
         }
     }
 
-    private void runOnHandler(Runnable r) {
-        if (Looper.myLooper() == mHandler.getLooper()) {
-            r.run();
-        } else {
-            mHandler.post(r);
-        }
-    }
-
     private <T> T runOnHandlerBlocking(Callable<T> c)
             throws InterruptedException, TimeoutException {
         if (Looper.myLooper() == mHandler.getLooper()) {
@@ -489,4 +497,12 @@
     public String toString() {
         return mServiceInfo.toString();
     }
+
+    /**
+     * Dump for debugging.
+     */
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        pw.println("service=" + mServiceInfo);
+        pw.println("connected=" + (mBinder != null));
+    }
 }
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/AppExitInfoTracker.java b/services/core/java/com/android/server/am/AppExitInfoTracker.java
index bab133f..60aba27 100644
--- a/services/core/java/com/android/server/am/AppExitInfoTracker.java
+++ b/services/core/java/com/android/server/am/AppExitInfoTracker.java
@@ -17,6 +17,7 @@
 package com.android.server.am;
 
 import static android.app.ActivityManager.RunningAppProcessInfo.procStateToImportance;
+import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
 
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROCESSES;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
@@ -52,6 +53,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.ProcessMap;
 import com.android.server.IoThread;
+import com.android.server.ServiceThread;
 import com.android.server.SystemServiceManager;
 
 import java.io.File;
@@ -179,9 +181,12 @@
         mRawRecordsPool = new SynchronizedPool<ApplicationExitInfo>(APP_EXIT_RAW_INFO_POOL_SIZE);
     }
 
-    void init(ActivityManagerService service, Looper looper) {
+    void init(ActivityManagerService service) {
         mService = service;
-        mKillHandler = new KillHandler(looper);
+        ServiceThread thread = new ServiceThread(TAG + ":killHandler",
+                THREAD_PRIORITY_BACKGROUND, true /* allowIo */);
+        thread.start();
+        mKillHandler = new KillHandler(thread.getLooper());
         mProcExitInfoFile = new File(SystemServiceManager.ensureSystemDir(), APP_EXIT_INFO_FILE);
 
         mAppExitInfoHistoryListSize = service.mContext.getResources().getInteger(
diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
index d5fc14b..39f79ca 100644
--- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
+++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
@@ -550,7 +550,6 @@
                     return data;
                 }
             }
-            Slog.e(TAG, "no controller energy info supplied for " + receiver.getName());
         } catch (TimeoutException e) {
             Slog.w(TAG, "timeout reading " + receiver.getName() + " stats");
         }
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 7c36a7e..3c7d6b8 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -739,7 +739,7 @@
                         mSystemServerSocketForZygote.getFileDescriptor(),
                         EVENT_INPUT, this::handleZygoteMessages);
             }
-            mAppExitInfoTracker.init(mService, sKillThread.getLooper());
+            mAppExitInfoTracker.init(mService);
             mImperceptibleKillRunner = new ImperceptibleKillRunner(sKillThread.getLooper());
         }
     }
@@ -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/NetdEventListenerService.java b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
index 17e2f69..f3d2012 100644
--- a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
+++ b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
@@ -35,11 +35,11 @@
 import android.util.ArrayMap;
 import android.util.Log;
 import android.util.SparseArray;
-import android.util.StatsLog;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.BitUtils;
+import com.android.internal.util.FrameworkStatsLog;
 import com.android.internal.util.RingBuffer;
 import com.android.internal.util.TokenBucket;
 import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityEvent;
@@ -278,7 +278,7 @@
         addWakeupEvent(event);
 
         String dstMac = event.dstHwAddr.toString();
-        StatsLog.write(StatsLog.PACKET_WAKEUP_OCCURRED,
+        FrameworkStatsLog.write(FrameworkStatsLog.PACKET_WAKEUP_OCCURRED,
                 uid, iface, ethertype, dstMac, srcIp, dstIp, ipNextHeader, srcPort, dstPort);
     }
 
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/location/LocationFudger.java b/services/core/java/com/android/server/location/LocationFudger.java
index ae71fe3..04c7714 100644
--- a/services/core/java/com/android/server/location/LocationFudger.java
+++ b/services/core/java/com/android/server/location/LocationFudger.java
@@ -16,17 +16,19 @@
 
 package com.android.server.location;
 
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.security.SecureRandom;
 import android.content.Context;
 import android.database.ContentObserver;
 import android.location.Location;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.SystemClock;
 import android.provider.Settings;
 import android.util.Log;
 
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.security.SecureRandom;
+
 
 /**
  * Contains the logic to obfuscate (fudge) locations for coarse applications.
@@ -177,7 +179,12 @@
 
     private Location addCoarseLocationExtraLocked(Location location) {
         Location coarse = createCoarseLocked(location);
-        location.setExtraLocation(Location.EXTRA_COARSE_LOCATION, coarse);
+        Bundle extras = location.getExtras();
+        if (extras == null) {
+            extras = new Bundle();
+        }
+        extras.putParcelable(Location.EXTRA_COARSE_LOCATION, coarse);
+        location.setExtras(extras);
         return coarse;
     }
 
diff --git a/services/core/java/com/android/server/location/LocationProviderProxy.java b/services/core/java/com/android/server/location/LocationProviderProxy.java
index 19fb669..96ffaa6 100644
--- a/services/core/java/com/android/server/location/LocationProviderProxy.java
+++ b/services/core/java/com/android/server/location/LocationProviderProxy.java
@@ -139,13 +139,13 @@
 
     @GuardedBy("mLock")
     private boolean mBound;
-    @GuardedBy("mLock")
-    private ProviderRequest mRequest;
+
+    private volatile ProviderRequest mRequest;
 
     private LocationProviderProxy(Context context, String action, int enableOverlayResId,
             int nonOverlayPackageResId) {
-        // safe to use direct executor even though this class has internal locks - all of our
-        // callbacks go to oneway binder transactions which cannot possibly be re-entrant
+        // safe to use direct executor since our locks are not acquired in a code path invoked by
+        // our owning provider
         super(DIRECT_EXECUTOR, Collections.emptySet());
 
         mContext = context;
@@ -167,8 +167,10 @@
             mBound = true;
 
             provider.setLocationProviderManager(mManager);
-            if (!mRequest.equals(ProviderRequest.EMPTY_REQUEST)) {
-                provider.setRequest(mRequest, mRequest.workSource);
+
+            ProviderRequest request = mRequest;
+            if (!request.equals(ProviderRequest.EMPTY_REQUEST)) {
+                provider.setRequest(request, request.workSource);
             }
 
             ComponentName service = mServiceWatcher.getBoundService().component;
@@ -187,10 +189,7 @@
 
     @Override
     public void onSetRequest(ProviderRequest request) {
-        synchronized (mLock) {
-            mRequest = request;
-        }
-
+        mRequest = request;
         mServiceWatcher.runOnBinder(binder -> {
             ILocationProvider service = ILocationProvider.Stub.asInterface(binder);
             service.setRequest(request, request.workSource);
@@ -215,9 +214,6 @@
 
     @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        pw.println("service=" + mServiceWatcher);
-        synchronized (mLock) {
-            pw.println("bound=" + mBound);
-        }
+        mServiceWatcher.dump(fd, pw, args);
     }
 }
diff --git a/services/core/java/com/android/server/media/MediaRoute2Provider.java b/services/core/java/com/android/server/media/MediaRoute2Provider.java
index 1cd8aad..5123362 100644
--- a/services/core/java/com/android/server/media/MediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/MediaRoute2Provider.java
@@ -36,6 +36,7 @@
     final Object mLock = new Object();
 
     Callback mCallback;
+    boolean mIsSystemRouteProvider;
     private volatile MediaRoute2ProviderInfo mProviderInfo;
 
     @GuardedBy("mLock")
@@ -85,6 +86,7 @@
         } else {
             mProviderInfo = new MediaRoute2ProviderInfo.Builder(providerInfo)
                     .setUniqueId(mUniqueId)
+                    .setSystemRouteProvider(mIsSystemRouteProvider)
                     .build();
         }
     }
diff --git a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
index 558eb8d..924a9b7 100644
--- a/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
+++ b/services/core/java/com/android/server/media/SystemMediaRoute2Provider.java
@@ -83,6 +83,8 @@
         super(sComponentName);
         setCallback(callback);
 
+        mIsSystemRouteProvider = true;
+
         mContext = context;
         mHandler = new Handler(Looper.getMainLooper());
 
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..148c5ad 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());
                         }
                     }
@@ -2727,7 +2728,7 @@
         t.traceBegin("get system config");
         SystemConfig systemConfig = SystemConfig.getInstance();
         mAvailableFeatures = systemConfig.getAvailableFeatures();
-        ApplicationPackageManager.invalidateSysFeatureCache();
+        ApplicationPackageManager.invalidateHasSystemFeatureCache();
         t.traceEnd();
 
         mProtectedPackages = new ProtectedPackages(mContext);
@@ -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/stats/pull/StatsPullAtomService.java b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
index 4f8a86d..96f1219 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -256,6 +256,8 @@
         mContext = context;
     }
 
+    private native void nativeInit();
+
     /**
      * Use of this StatsPullAtomCallbackImpl means we avoid one class per tagId, which we would
      * get if we used lambdas.
@@ -399,6 +401,7 @@
         super.onBootPhase(phase);
         if (phase == PHASE_SYSTEM_SERVICES_READY) {
             BackgroundThread.getHandler().post(() -> {
+                nativeInit();
                 initializePullersState();
                 registerAllPullers();
                 registerEventListeners();
@@ -896,7 +899,6 @@
                     return data;
                 }
             }
-            Slog.e(TAG, "no controller energy info supplied for " + receiver.getName());
         } catch (TimeoutException e) {
             Slog.w(TAG, "timeout reading " + receiver.getName() + " stats");
         }
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/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java
new file mode 100644
index 0000000..b3edc91
--- /dev/null
+++ b/services/core/java/com/android/server/wm/DisplayArea.java
@@ -0,0 +1,265 @@
+/*
+ * 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.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
+import static android.view.WindowManagerPolicyConstants.APPLICATION_LAYER;
+
+import static com.android.internal.util.Preconditions.checkState;
+import static com.android.server.wm.DisplayAreaChildProto.DISPLAY_AREA;
+import static com.android.server.wm.DisplayAreaChildProto.UNKNOWN;
+import static com.android.server.wm.DisplayAreaChildProto.WINDOW;
+import static com.android.server.wm.DisplayAreaProto.CHILDREN;
+import static com.android.server.wm.DisplayAreaProto.NAME;
+import static com.android.server.wm.DisplayAreaProto.WINDOW_CONTAINER;
+import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ORIENTATION;
+
+import android.graphics.Rect;
+import android.util.proto.ProtoOutputStream;
+
+import com.android.server.policy.WindowManagerPolicy;
+import com.android.server.protolog.common.ProtoLog;
+
+import java.util.Comparator;
+import java.util.function.Predicate;
+
+/**
+ * Container for grouping WindowContainer below DisplayContent.
+ *
+ * DisplayAreas are managed by a {@link DisplayAreaPolicy}, and can override configurations and
+ * can be leashed.
+ *
+ * DisplayAreas can contain nested DisplayAreas.
+ *
+ * DisplayAreas come in three flavors, to ensure that windows have the right Z-Order:
+ * - BELOW_TASKS: Can only contain BELOW_TASK DisplayAreas and WindowTokens that go below tasks.
+ * - ABOVE_TASKS: Can only contain ABOVE_TASK DisplayAreas and WindowTokens that go above tasks.
+ * - ANY: Can contain any kind of DisplayArea, and any kind of WindowToken or the Task container.
+ *        Cannot have a sibling that is of type ANY.
+ *
+ * @param <T> type of the children of the DisplayArea.
+ */
+public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> {
+
+    protected final Type mType;
+    private final String mName;
+
+    DisplayArea(WindowManagerService wms, Type type, String name) {
+        super(wms);
+        // TODO(display-area): move this up to ConfigurationContainer
+        mOrientation = SCREEN_ORIENTATION_UNSET;
+        mType = type;
+        mName = name;
+    }
+
+    @Override
+    void onChildPositionChanged(WindowContainer child) {
+        super.onChildPositionChanged(child);
+
+        // Verify that we have proper ordering
+        Type.checkChild(mType, Type.typeOf(child));
+
+        if (child instanceof ActivityStack) {
+            // TODO(display-area): ActivityStacks are type ANY, but are allowed to have siblings.
+            //                     They might need a separate type.
+            return;
+        }
+
+        for (int i = 1; i < getChildCount(); i++) {
+            final WindowContainer top = getChildAt(i - 1);
+            final WindowContainer bottom = getChildAt(i);
+            if (child == top || child == bottom) {
+                Type.checkSiblings(Type.typeOf(top), Type.typeOf(bottom));
+            }
+        }
+    }
+
+    @Override
+    boolean fillsParent() {
+        return true;
+    }
+
+    @Override
+    String getName() {
+        return mName;
+    }
+
+    @Override
+    public final void dumpDebug(ProtoOutputStream proto, long fieldId, int logLevel) {
+        final long token = proto.start(fieldId);
+        super.dumpDebug(proto, WINDOW_CONTAINER, logLevel);
+        proto.write(NAME, mName);
+        for (int i = 0; i < getChildCount(); i++) {
+            final long childToken = proto.start(CHILDREN);
+            final T child = getChildAt(i);
+            if (child instanceof ActivityStack) {
+                // TODO(display-area): Dump stacks & tasks here, instead of in DisplayContent's
+                //  dumpDebug. For now, skip them here to avoid dumping them as UNKNOWN.
+            } else if (child instanceof WindowToken) {
+                ((WindowToken) child).dumpDebug(proto, WINDOW, logLevel);
+            } else if (child instanceof DisplayArea) {
+                child.dumpDebug(proto, DISPLAY_AREA, logLevel);
+            } else {
+                proto.write(UNKNOWN, child.getClass().getSimpleName());
+            }
+            proto.end(childToken);
+        }
+        proto.end(token);
+    }
+
+    /**
+     * DisplayArea that contains WindowTokens, and orders them according to their type.
+     */
+    public static class Tokens extends DisplayArea<WindowToken> {
+        int mLastKeyguardForcedOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
+
+        private final Comparator<WindowToken> mWindowComparator =
+                Comparator.comparingInt(WindowToken::getWindowLayerFromType);
+
+        private final Predicate<WindowState> mGetOrientingWindow = w -> {
+            final WindowManagerPolicy policy = mWmService.mPolicy;
+            if (policy.isKeyguardHostWindow(w.mAttrs)) {
+                if (mWmService.mKeyguardGoingAway) {
+                    return false;
+                }
+                // Consider unoccluding only when all unknown visibilities have been
+                // resolved, as otherwise we just may be starting another occluding activity.
+                final boolean isUnoccluding =
+                        mDisplayContent.mAppTransition.getAppTransition()
+                                == TRANSIT_KEYGUARD_UNOCCLUDE
+                                && mDisplayContent.mUnknownAppVisibilityController.allResolved();
+                // If keyguard is showing, or we're unoccluding, force the keyguard's orientation,
+                // even if SystemUI hasn't updated the attrs yet.
+                if (policy.isKeyguardShowingAndNotOccluded() || isUnoccluding) {
+                    return true;
+                }
+            }
+            final int req = w.mAttrs.screenOrientation;
+            if (req == SCREEN_ORIENTATION_UNSPECIFIED || req == SCREEN_ORIENTATION_BEHIND
+                    || req == SCREEN_ORIENTATION_UNSET) {
+                return false;
+            }
+            return true;
+        };
+
+        Tokens(WindowManagerService wms, Type type, String name) {
+            super(wms, type, name);
+        }
+
+        void addChild(WindowToken token) {
+            addChild(token, mWindowComparator);
+        }
+
+        @Override
+        int getOrientation(int candidate) {
+            // Find a window requesting orientation.
+            final WindowState win = getWindow(mGetOrientingWindow);
+
+            if (win == null) {
+                return candidate;
+            }
+            int req = win.mAttrs.screenOrientation;
+            ProtoLog.v(WM_DEBUG_ORIENTATION, "%s forcing orientation to %d for display id=%d",
+                    win, req, mDisplayContent.getDisplayId());
+            if (mWmService.mPolicy.isKeyguardHostWindow(win.mAttrs)) {
+                // SystemUI controls the Keyguard orientation asynchronously, and mAttrs may be
+                // stale. We record / use the last known override.
+                if (req != SCREEN_ORIENTATION_UNSET && req != SCREEN_ORIENTATION_UNSPECIFIED) {
+                    mLastKeyguardForcedOrientation = req;
+                } else {
+                    req = mLastKeyguardForcedOrientation;
+                }
+            }
+            return req;
+        }
+    }
+
+    /**
+     * Top-most DisplayArea under DisplayContent.
+     */
+    public static class Root extends DisplayArea<DisplayArea> {
+        private final Dimmer mDimmer = new Dimmer(this);
+        private final Rect mTmpDimBoundsRect = new Rect();
+
+        Root(WindowManagerService wms) {
+            super(wms, Type.ANY, "DisplayArea.Root");
+        }
+
+        @Override
+        Dimmer getDimmer() {
+            return mDimmer;
+        }
+
+        @Override
+        void prepareSurfaces() {
+            mDimmer.resetDimStates();
+            super.prepareSurfaces();
+            getBounds(mTmpDimBoundsRect);
+
+            if (mDimmer.updateDims(getPendingTransaction(), mTmpDimBoundsRect)) {
+                scheduleAnimation();
+            }
+        }
+    }
+
+    enum Type {
+        /** Can only contain WindowTokens above the APPLICATION_LAYER. */
+        ABOVE_TASKS,
+        /** Can only contain WindowTokens below the APPLICATION_LAYER. */
+        BELOW_TASKS,
+        /** Can contain anything. */
+        ANY;
+
+        static void checkSiblings(Type bottom, Type top) {
+            checkState(!(bottom == ANY && top == ANY), "ANY cannot be a sibling of ANY");
+            checkState(!(bottom != BELOW_TASKS && top == BELOW_TASKS),
+                    bottom + " must be above BELOW_TASKS");
+            checkState(!(bottom == ABOVE_TASKS && top != ABOVE_TASKS),
+                    top + " must be below ABOVE_TASKS");
+        }
+
+        static void checkChild(Type parent, Type child) {
+            switch (parent) {
+                case ABOVE_TASKS:
+                    checkState(child == ABOVE_TASKS, "ABOVE_TASKS can only contain ABOVE_TASKS");
+                    break;
+                case BELOW_TASKS:
+                    checkState(child == BELOW_TASKS, "BELOW_TASKS can only contain BELOW_TASKS");
+                    break;
+            }
+        }
+
+        static Type typeOf(WindowContainer c) {
+            if (c instanceof DisplayArea) {
+                return ((DisplayArea) c).mType;
+            } else if (c instanceof WindowToken && !(c instanceof ActivityRecord)) {
+                return typeOf((WindowToken) c);
+            } else if (c instanceof ActivityStack) {
+                return ANY;
+            } else {
+                throw new IllegalArgumentException("Unknown container: " + c);
+            }
+        }
+
+        private static Type typeOf(WindowToken c) {
+            return c.getWindowLayerFromType() < APPLICATION_LAYER ? BELOW_TASKS : ABOVE_TASKS;
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/wm/DisplayAreaPolicy.java b/services/core/java/com/android/server/wm/DisplayAreaPolicy.java
new file mode 100644
index 0000000..06e7b48
--- /dev/null
+++ b/services/core/java/com/android/server/wm/DisplayAreaPolicy.java
@@ -0,0 +1,123 @@
+/*
+ * 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.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
+
+import com.android.server.wm.DisplayContent.TaskContainers;
+
+/**
+ * Policy that manages DisplayAreas.
+ */
+public abstract class DisplayAreaPolicy {
+    protected final WindowManagerService mWmService;
+    protected final DisplayContent mContent;
+
+    /**
+     * The root DisplayArea. Attach all DisplayAreas to this area (directly or indirectly).
+     */
+    protected final DisplayArea.Root mRoot;
+
+    /**
+     * The IME container. The IME's windows are automatically added to this container.
+     */
+    protected final DisplayArea<? extends WindowContainer> mImeContainer;
+
+    /**
+     * The Tasks container. Tasks etc. are automatically added to this container.
+     */
+    protected final TaskContainers mTaskContainers;
+
+    DisplayAreaPolicy(WindowManagerService wmService,
+            DisplayContent content, DisplayArea.Root root,
+            DisplayArea<? extends WindowContainer> imeContainer, TaskContainers taskContainers) {
+        mWmService = wmService;
+        mContent = content;
+        mRoot = root;
+        mImeContainer = imeContainer;
+        mTaskContainers = taskContainers;
+    }
+
+    /**
+     * Called to ask the policy to set up the DisplayArea hierarchy. At a minimum this must:
+     *
+     * - attach mImeContainer to mRoot (or one of its descendants)
+     * - attach mTaskStacks to mRoot (or one of its descendants)
+     *
+     * Additionally, this is the right place to set up any other DisplayAreas as desired.
+     */
+    public abstract void attachDisplayAreas();
+
+    /**
+     * Called to ask the policy to attach the given WindowToken to the DisplayArea hierarchy.
+     *
+     * This must attach the token to mRoot (or one of its descendants).
+     */
+    public abstract void addWindow(WindowToken token);
+
+    /**
+     * Default policy that has no special features.
+     */
+    public static class Default extends DisplayAreaPolicy {
+
+        public Default(WindowManagerService wmService, DisplayContent content,
+                DisplayArea.Root root,
+                DisplayArea<? extends WindowContainer> imeContainer,
+                TaskContainers taskContainers) {
+            super(wmService, content, root, imeContainer, taskContainers);
+        }
+
+        private final DisplayArea.Tokens mBelow = new DisplayArea.Tokens(mWmService,
+                DisplayArea.Type.BELOW_TASKS, "BelowTasks");
+        private final DisplayArea<DisplayArea> mAbove = new DisplayArea<>(mWmService,
+                DisplayArea.Type.ABOVE_TASKS, "AboveTasks");
+        private final DisplayArea.Tokens mAboveBelowIme = new DisplayArea.Tokens(mWmService,
+                DisplayArea.Type.ABOVE_TASKS, "AboveTasksBelowIme");
+        private final DisplayArea.Tokens mAboveAboveIme = new DisplayArea.Tokens(mWmService,
+                DisplayArea.Type.ABOVE_TASKS, "AboveTasksAboveIme");
+
+        @Override
+        public void attachDisplayAreas() {
+            mRoot.addChild(mBelow, 0);
+            mRoot.addChild(mTaskContainers, 1);
+            mRoot.addChild(mAbove, 2);
+
+            mAbove.addChild(mAboveBelowIme, 0);
+            mAbove.addChild(mImeContainer, 1);
+            mAbove.addChild(mAboveAboveIme, 2);
+        }
+
+        @Override
+        public void addWindow(WindowToken token) {
+            switch (DisplayArea.Type.typeOf(token)) {
+                case ABOVE_TASKS:
+                    if (token.getWindowLayerFromType()
+                            < mWmService.mPolicy.getWindowLayerFromTypeLw(TYPE_INPUT_METHOD)) {
+                        mAboveBelowIme.addChild(token);
+                    } else {
+                        mAboveAboveIme.addChild(token);
+                    }
+                    break;
+                case BELOW_TASKS:
+                    mBelow.addChild(token);
+                    break;
+                default:
+                    throw new IllegalArgumentException("don't know how to sort " + token);
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 6e479b2..81af0fe 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -80,7 +80,6 @@
 import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
 import static android.view.WindowManager.TRANSIT_ACTIVITY_OPEN;
-import static android.view.WindowManager.TRANSIT_KEYGUARD_UNOCCLUDE;
 import static android.view.WindowManager.TRANSIT_TASK_OPEN;
 import static android.view.WindowManager.TRANSIT_TASK_TO_FRONT;
 
@@ -98,9 +97,7 @@
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_STATES;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_TASKS;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_STACK;
-import static com.android.server.wm.DisplayContentProto.ABOVE_APP_WINDOWS;
 import static com.android.server.wm.DisplayContentProto.APP_TRANSITION;
-import static com.android.server.wm.DisplayContentProto.BELOW_APP_WINDOWS;
 import static com.android.server.wm.DisplayContentProto.CHANGING_APPS;
 import static com.android.server.wm.DisplayContentProto.CLOSING_APPS;
 import static com.android.server.wm.DisplayContentProto.DISPLAY_FRAMES;
@@ -109,9 +106,9 @@
 import static com.android.server.wm.DisplayContentProto.DPI;
 import static com.android.server.wm.DisplayContentProto.FOCUSED_APP;
 import static com.android.server.wm.DisplayContentProto.ID;
-import static com.android.server.wm.DisplayContentProto.IME_WINDOWS;
 import static com.android.server.wm.DisplayContentProto.OPENING_APPS;
 import static com.android.server.wm.DisplayContentProto.OVERLAY_WINDOWS;
+import static com.android.server.wm.DisplayContentProto.ROOT_DISPLAY_AREA;
 import static com.android.server.wm.DisplayContentProto.ROTATION;
 import static com.android.server.wm.DisplayContentProto.SCREEN_ROTATION_ANIMATION;
 import static com.android.server.wm.DisplayContentProto.STACKS;
@@ -207,6 +204,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;
@@ -295,14 +293,7 @@
     /** The containers below are the only child containers {@link #mWindowContainers} can have. */
     // Contains all window containers that are related to apps (Activities)
     private final TaskContainers mTaskContainers = new TaskContainers(mWmService);
-    // Contains all non-app window containers that should be displayed above the app containers
-    // (e.g. Status bar)
-    private final AboveAppWindowContainers mAboveAppWindowsContainers =
-            new AboveAppWindowContainers("mAboveAppWindowsContainers", mWmService);
-    // Contains all non-app window containers that should be displayed below the app containers
-    // (e.g. Wallpaper).
-    private final NonAppWindowContainers mBelowAppWindowsContainers =
-            new NonAppWindowContainers("mBelowAppWindowsContainers", mWmService);
+
     // Contains all IME window containers. Note that the z-ordering of the IME windows will depend
     // on the IME target. We mainly have this container grouping so we can keep track of all the IME
     // window containers together and move them in-sync if/when needed. We use a subclass of
@@ -310,6 +301,11 @@
     // TODO(display-area): is "no magnification" in the comment still true?
     private final ImeContainer mImeWindowsContainers = new ImeContainer(mWmService);
 
+    private final DisplayArea.Root mRootDisplayArea = new DisplayArea.Root(mWmService);
+
+    private final DisplayAreaPolicy mDisplayAreaPolicy = new DisplayAreaPolicy.Default(
+            mWmService, this, mRootDisplayArea, mImeWindowsContainers, mTaskContainers);
+
     private WindowState mTmpWindow;
     private WindowState mTmpWindow2;
     private boolean mUpdateImeTarget;
@@ -401,14 +397,6 @@
     private int mCurrentOverrideConfigurationChanges;
 
     /**
-     * Last orientation forced by the keyguard. It is applied when keyguard is shown and is not
-     * occluded.
-     *
-     * @see NonAppWindowContainers#getOrientation()
-     */
-    private int mLastKeyguardForcedOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
-
-    /**
      * The maximum aspect ratio (longerSide/shorterSide) that is treated as close-to-square. The
      * orientation requests from apps would be ignored if the display is close-to-square.
      */
@@ -664,6 +652,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;
@@ -1104,18 +1095,16 @@
             // Add non-app token to container hierarchy on the display. App tokens are added through
             // the parent container managing them (e.g. Tasks).
             switch (token.windowType) {
-                case TYPE_WALLPAPER:
-                    mBelowAppWindowsContainers.addChild(token);
-                    break;
                 case TYPE_INPUT_METHOD:
                 case TYPE_INPUT_METHOD_DIALOG:
                     mImeWindowsContainers.addChild(token);
                     break;
                 case TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY:
+                    // TODO(display-area): Migrate to DisplayArea
                     mOverlayContainers.addChild(token);
                     break;
                 default:
-                    mAboveAppWindowsContainers.addChild(token);
+                    mDisplayAreaPolicy.addWindow(token);
                     break;
             }
         }
@@ -2129,13 +2118,7 @@
                 return getLastOrientation();
             }
         }
-        final int orientation = mAboveAppWindowsContainers.getOrientation();
-        if (orientation != SCREEN_ORIENTATION_UNSET) {
-            return orientation;
-        }
-
-        // Top system windows are not requesting an orientation. Start searching from apps.
-        return mTaskContainers.getOrientation();
+        return mRootDisplayArea.getOrientation();
     }
 
     void updateDisplayInfo() {
@@ -2782,23 +2765,12 @@
         final long token = proto.start(fieldId);
         super.dumpDebug(proto, WINDOW_CONTAINER, logLevel);
         proto.write(ID, mDisplayId);
+        mRootDisplayArea.dumpDebug(proto, ROOT_DISPLAY_AREA, logLevel);
         for (int stackNdx = mTaskContainers.getChildCount() - 1; stackNdx >= 0; --stackNdx) {
             final ActivityStack stack = mTaskContainers.getChildAt(stackNdx);
             stack.dumpDebugInnerStackOnly(proto, STACKS, logLevel);
         }
         mDividerControllerLocked.dumpDebug(proto, DOCKED_STACK_DIVIDER_CONTROLLER);
-        for (int i = mAboveAppWindowsContainers.getChildCount() - 1; i >= 0; --i) {
-            final WindowToken windowToken = mAboveAppWindowsContainers.getChildAt(i);
-            windowToken.dumpDebug(proto, ABOVE_APP_WINDOWS, logLevel);
-        }
-        for (int i = mBelowAppWindowsContainers.getChildCount() - 1; i >= 0; --i) {
-            final WindowToken windowToken = mBelowAppWindowsContainers.getChildAt(i);
-            windowToken.dumpDebug(proto, BELOW_APP_WINDOWS, logLevel);
-        }
-        for (int i = mImeWindowsContainers.getChildCount() - 1; i >= 0; --i) {
-            final WindowToken windowToken = mImeWindowsContainers.getChildAt(i);
-            windowToken.dumpDebug(proto, IME_WINDOWS, logLevel);
-        }
         for (int i = mOverlayContainers.getChildCount() - 1; i >= 0; --i) {
             final WindowToken windowToken = mOverlayContainers.getChildAt(i);
             windowToken.dumpDebug(proto, OVERLAY_WINDOWS, logLevel);
@@ -4212,7 +4184,7 @@
      * Window container class that contains all containers on this display relating to Apps.
      * I.e Activities.
      */
-    private final class TaskContainers extends DisplayChildWindowContainer<ActivityStack> {
+    final class TaskContainers extends DisplayArea<ActivityStack> {
         /**
          * A control placed at the appropriate level for transitions to occur.
          */
@@ -4222,16 +4194,15 @@
 
         /**
          * Given that the split-screen divider does not have an AppWindowToken, it
-         * will have to live inside of a "NonAppWindowContainer", in particular
-         * {@link DisplayContent#mAboveAppWindowsContainers}. However, in visual Z order
+         * will have to live inside of a "NonAppWindowContainer". However, in visual Z order
          * it will need to be interleaved with some of our children, appearing on top of
          * both docked stacks but underneath any assistant stacks.
          *
          * To solve this problem we have this anchor control, which will always exist so
          * we can always assign it the correct value in our {@link #assignChildLayers}.
-         * Likewise since it always exists, {@link AboveAppWindowContainers} can always
+         * Likewise since it always exists, we can always
          * assign the divider a layer relative to it. This way we prevent linking lifecycle
-         * events between the two containers.
+         * events between tasks and the divider window.
          */
         SurfaceControl mSplitScreenDividerAnchor = null;
 
@@ -4242,7 +4213,7 @@
         private ActivityStack mRootSplitScreenPrimaryTask = null;
 
         TaskContainers(WindowManagerService service) {
-            super(service);
+            super(service, Type.ANY, "TaskContainers");
         }
 
         /**
@@ -4275,8 +4246,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 +4296,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,31 +4717,16 @@
                 mSplitScreenDividerAnchor = null;
             }
         }
-    }
-
-    private final class AboveAppWindowContainers extends NonAppWindowContainers {
-        AboveAppWindowContainers(String name, WindowManagerService service) {
-            super(name, service);
-        }
 
         @Override
-        void assignChildLayers(SurfaceControl.Transaction t) {
-            boolean needAssignIme = mImeWindowsContainers.getSurfaceControl() != null;
-            for (int j = 0; j < mChildren.size(); ++j) {
-                final WindowToken wt = mChildren.get(j);
-
-                wt.assignLayer(t, j);
-                wt.assignChildLayers(t);
-
-                int layer = mWmService.mPolicy.getWindowLayerFromTypeLw(
-                        wt.windowType, wt.mOwnerCanManageAppTokens);
-
-                if (needAssignIme && layer >= mWmService.mPolicy.getWindowLayerFromTypeLw(
-                        TYPE_INPUT_METHOD_DIALOG, true)) {
-                    mImeWindowsContainers.assignRelativeLayer(t, wt.getSurfaceControl(), -1);
-                    needAssignIme = false;
-                }
+        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 */);
         }
     }
 
@@ -4774,13 +4741,10 @@
         @Override
         void assignChildLayers(SurfaceControl.Transaction t) {
             mImeWindowsContainers.setNeedsLayer();
-            mBelowAppWindowsContainers.assignLayer(t, 0);
-            mTaskContainers.assignLayer(t, 1);
-            mAboveAppWindowsContainers.assignLayer(t, 2);
+
+            mRootDisplayArea.assignLayer(t, 0);
 
             final WindowState imeTarget = mInputMethodTarget;
-            boolean needAssignIme = true;
-
             // In the case where we have an IME target that is not in split-screen mode IME
             // assignment is easy. We just need the IME to go directly above the target. This way
             // children of the target will naturally go above the IME and everyone is happy.
@@ -4813,11 +4777,7 @@
 
             // Above we have assigned layers to our children, now we ask them to assign
             // layers to their children.
-            mBelowAppWindowsContainers.assignChildLayers(t);
-            mTaskContainers.assignChildLayers(t);
-            mAboveAppWindowsContainers.assignChildLayers(t);
-            mImeWindowsContainers.assignRelativeLayer(t, getSurfaceControl(), Integer.MAX_VALUE);
-            mImeWindowsContainers.assignChildLayers(t);
+            mRootDisplayArea.assignChildLayers(t);
         }
 
         @Override
@@ -4826,10 +4786,8 @@
         }
 
         void addChildren() {
-            addChild(mBelowAppWindowsContainers, null);
-            addChild(mTaskContainers, null);
-            addChild(mAboveAppWindowsContainers, null);
-            addChild(mImeWindowsContainers, null);
+            addChild(mRootDisplayArea, 0);
+            mDisplayAreaPolicy.attachDisplayAreas();
         }
 
         @Override
@@ -4856,32 +4814,6 @@
                         < mWmService.mPolicy.getWindowLayerFromTypeLw(token2.windowType,
                         token2.mOwnerCanManageAppTokens) ? -1 : 1;
 
-        private final Predicate<WindowState> mGetOrientingWindow = w -> {
-            final WindowManagerPolicy policy = mWmService.mPolicy;
-            if (policy.isKeyguardHostWindow(w.mAttrs)) {
-                if (mWmService.mKeyguardGoingAway) {
-                    return false;
-                }
-                // Consider unoccluding only when all unknown visibilities have been
-                // resolved, as otherwise we just may be starting another occluding activity.
-                final boolean isUnoccluding =
-                        mDisplayContent.mAppTransition.getAppTransition()
-                                == TRANSIT_KEYGUARD_UNOCCLUDE
-                                && mDisplayContent.mUnknownAppVisibilityController.allResolved();
-                // If keyguard is showing, or we're unoccluding, force the keyguard's orientation,
-                // even if SystemUI hasn't updated the attrs yet.
-                if (policy.isKeyguardShowingAndNotOccluded() || isUnoccluding) {
-                    return true;
-                }
-            }
-            final int req = w.mAttrs.screenOrientation;
-            if (req == SCREEN_ORIENTATION_UNSPECIFIED || req == SCREEN_ORIENTATION_BEHIND
-                    || req == SCREEN_ORIENTATION_UNSET) {
-                return false;
-            }
-            return true;
-        };
-
         private final String mName;
         private final Dimmer mDimmer = new Dimmer(this);
         private final Rect mTmpDimBoundsRect = new Rect();
@@ -4903,26 +4835,9 @@
 
         @Override
         int getOrientation(int candidate) {
-            // Find a window requesting orientation.
-            final WindowState win = getWindow(mGetOrientingWindow);
-
-            if (win != null) {
-                int req = win.mAttrs.screenOrientation;
-                ProtoLog.v(WM_DEBUG_ORIENTATION,
-                        "%s forcing orientation to %d for display id=%d", win, req,
-                        mDisplayId);
-                if (mWmService.mPolicy.isKeyguardHostWindow(win.mAttrs)) {
-                    // SystemUI controls the Keyguard orientation asynchronously, and mAttrs may be
-                    // stale. We record / use the last known override.
-                    if (req != SCREEN_ORIENTATION_UNSET && req != SCREEN_ORIENTATION_UNSPECIFIED) {
-                        mDisplayContent.mLastKeyguardForcedOrientation = req;
-                    } else {
-                        req = mDisplayContent.mLastKeyguardForcedOrientation;
-                    }
-                }
-                return req;
-            }
-            return candidate;
+            ProtoLog.w(WM_DEBUG_ORIENTATION, "NonAppWindowContainer cannot set orientation: %s",
+                    this);
+            return SCREEN_ORIENTATION_UNSET;
         }
 
         @Override
@@ -4957,11 +4872,11 @@
      * - the container doesn't always participate in window traversal, according to
      *   {@link #skipImeWindowsDuringTraversal()}
      */
-    private class ImeContainer extends NonAppWindowContainers {
+    private static class ImeContainer extends DisplayArea.Tokens {
         boolean mNeedsLayer = false;
 
         ImeContainer(WindowManagerService wms) {
-            super("ImeContainer", wms);
+            super(wms, Type.ABOVE_TASKS, "ImeContainer");
         }
 
         public void setNeedsLayer() {
@@ -6271,6 +6186,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 +6604,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/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
index 2d4211a..d0179ad 100644
--- a/services/core/java/com/android/server/wm/InsetsPolicy.java
+++ b/services/core/java/com/android/server/wm/InsetsPolicy.java
@@ -20,17 +20,31 @@
 import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.view.InsetsController.LAYOUT_INSETS_DURING_ANIMATION_HIDDEN;
+import static android.view.InsetsController.LAYOUT_INSETS_DURING_ANIMATION_SHOWN;
 import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
 import static android.view.InsetsState.ITYPE_STATUS_BAR;
+import static android.view.SyncRtSurfaceTransactionApplier.applyParams;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_SHOW_STATUS_BAR;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_STATUS_FORCE_SHOW_NAVIGATION;
 
 import android.annotation.Nullable;
 import android.app.StatusBarManager;
 import android.util.IntArray;
+import android.util.SparseArray;
+import android.view.InsetsAnimationControlCallbacks;
+import android.view.InsetsAnimationControlImpl;
+import android.view.InsetsController;
+import android.view.InsetsSourceControl;
 import android.view.InsetsState;
 import android.view.InsetsState.InternalInsetsType;
+import android.view.SurfaceControl;
+import android.view.SyncRtSurfaceTransactionApplier;
 import android.view.ViewRootImpl;
+import android.view.WindowInsetsAnimationCallback;
+import android.view.WindowInsetsAnimationControlListener;
+
+import com.android.server.DisplayThread;
 
 /**
  * Policy that implements who gets control over the windows generating insets.
@@ -46,6 +60,8 @@
     private WindowState mFocusedWin;
     private BarWindow mStatusBar = new BarWindow(StatusBarManager.WINDOW_STATUS_BAR);
     private BarWindow mNavBar = new BarWindow(StatusBarManager.WINDOW_NAVIGATION_BAR);
+    private boolean mAnimatingShown;
+    private final float[] mTmpFloat9 = new float[9];
 
     InsetsPolicy(InsetsStateController stateController, DisplayContent displayContent) {
         mStateController = stateController;
@@ -91,11 +107,14 @@
             changed = true;
         }
         if (changed) {
-            updateBarControlTarget(mFocusedWin);
-            mPolicy.getStatusBarManagerInternal().showTransient(mDisplayContent.getDisplayId(),
-                    mShowingTransientTypes.toArray());
-            mStateController.notifyInsetsChanged();
-            // TODO(b/118118435): Animation
+            startAnimation(mShowingTransientTypes, true, () -> {
+                synchronized (mDisplayContent.mWmService.mGlobalLock) {
+                    mPolicy.getStatusBarManagerInternal().showTransient(
+                            mDisplayContent.getDisplayId(),
+                            mShowingTransientTypes.toArray());
+                    mStateController.notifyInsetsChanged();
+                }
+            });
         }
     }
 
@@ -103,11 +122,13 @@
         if (mShowingTransientTypes.size() == 0) {
             return;
         }
-
-        // TODO(b/118118435): Animation
-        mShowingTransientTypes.clear();
-        updateBarControlTarget(mFocusedWin);
-        mStateController.notifyInsetsChanged();
+        startAnimation(mShowingTransientTypes, false, () -> {
+            synchronized (mDisplayContent.mWmService.mGlobalLock) {
+                mShowingTransientTypes.clear();
+                mStateController.notifyInsetsChanged();
+                updateBarControlTarget(mFocusedWin);
+            }
+        });
     }
 
     boolean isTransient(@InternalInsetsType int type) {
@@ -247,6 +268,29 @@
         return isDockedStackVisible || isFreeformStackVisible || isResizing;
     }
 
+    private void startAnimation(IntArray internalTypes, boolean show, Runnable callback) {
+        int typesReady = 0;
+        final SparseArray<InsetsSourceControl> controls = new SparseArray<>();
+        updateBarControlTarget(mFocusedWin);
+        for (int i = internalTypes.size() - 1; i >= 0; i--) {
+            InsetsSourceProvider provider =
+                    mStateController.getSourceProvider(internalTypes.get(i));
+            if (provider == null) continue;
+            InsetsSourceControl control = provider.getControl(provider.getControlTarget());
+            if (control == null || control.getLeash() == null) continue;
+            typesReady |= InsetsState.toPublicType(internalTypes.get(i));
+            controls.put(control.getType(), control);
+        }
+        controlAnimationUnchecked(typesReady, controls, show, callback);
+    }
+
+    private void controlAnimationUnchecked(int typesReady,
+            SparseArray<InsetsSourceControl> controls, boolean show, Runnable callback) {
+        InsetsPolicyAnimationControlListener listener =
+                new InsetsPolicyAnimationControlListener(show, callback);
+        listener.mControlCallbacks.controlAnimationUnchecked(typesReady, controls, show);
+    }
+
     private class BarWindow {
 
         private final int mId;
@@ -267,7 +311,103 @@
         }
     }
 
-    // TODO(b/118118435): Implement animations for it (with SurfaceAnimator)
+    private class InsetsPolicyAnimationControlListener extends
+            InsetsController.InternalAnimationControlListener {
+        Runnable mFinishCallback;
+        InsetsPolicyAnimationControlCallbacks mControlCallbacks;
+
+        InsetsPolicyAnimationControlListener(boolean show, Runnable finishCallback) {
+            super(show);
+            mFinishCallback = finishCallback;
+            mControlCallbacks = new InsetsPolicyAnimationControlCallbacks(this);
+        }
+
+        @Override
+        protected void onAnimationFinish() {
+            super.onAnimationFinish();
+            mControlCallbacks.mAnimationControl.finish(mAnimatingShown);
+            DisplayThread.getHandler().post(mFinishCallback);
+        }
+
+        private class InsetsPolicyAnimationControlCallbacks implements
+                InsetsAnimationControlCallbacks {
+            private InsetsAnimationControlImpl mAnimationControl = null;
+            private InsetsPolicyAnimationControlListener mListener;
+
+            InsetsPolicyAnimationControlCallbacks(InsetsPolicyAnimationControlListener listener) {
+                super();
+                mListener = listener;
+            }
+
+            private void controlAnimationUnchecked(int typesReady,
+                    SparseArray<InsetsSourceControl> controls, boolean show) {
+                if (typesReady == 0) {
+                    // nothing to animate.
+                    return;
+                }
+                mAnimatingShown = show;
+
+                mAnimationControl = new InsetsAnimationControlImpl(controls,
+                        mFocusedWin.getDisplayContent().getBounds(), getState(),
+                        mListener, typesReady, this, mListener.getDurationMs(),
+                        InsetsController.INTERPOLATOR, true,
+                        show ? LAYOUT_INSETS_DURING_ANIMATION_SHOWN
+                                : LAYOUT_INSETS_DURING_ANIMATION_HIDDEN);
+            }
+
+            /** Called on SurfaceAnimationThread lock without global WM lock held. */
+            @Override
+            public void scheduleApplyChangeInsets() {
+                InsetsState state = getState();
+                if (mAnimationControl.applyChangeInsets(state)) {
+                    mAnimationControl.finish(mAnimatingShown);
+                }
+            }
+
+            @Override
+            public void notifyFinished(InsetsAnimationControlImpl controller, boolean shown) {
+                // Nothing's needed here. Finish steps is handled in the listener
+                // onAnimationFinished callback.
+            }
+
+            /**
+             * This method will return a state with fullscreen frame override. No need to make copy
+             * after getting state from this method.
+             * @return The client insets state with full display frame override.
+             */
+            private InsetsState getState() {
+                // To animate the transient animation correctly, we need to let the state hold
+                // the full display frame.
+                InsetsState overrideState = new InsetsState(mFocusedWin.getRequestedInsetsState(),
+                        true);
+                overrideState.setDisplayFrame(mFocusedWin.getDisplayContent().getBounds());
+                return overrideState;
+            }
+
+            /** Called on SurfaceAnimationThread lock without global WM lock held. */
+            @Override
+            public void applySurfaceParams(
+                    final SyncRtSurfaceTransactionApplier.SurfaceParams... params) {
+                SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+                for (int i = params.length - 1; i >= 0; i--) {
+                    SyncRtSurfaceTransactionApplier.SurfaceParams surfaceParams = params[i];
+                    applyParams(t, surfaceParams, mTmpFloat9);
+                }
+                t.apply();
+            }
+
+            /** Called on SurfaceAnimationThread lock without global WM lock held. */
+            @Override
+            public void startAnimation(InsetsAnimationControlImpl controller,
+                    WindowInsetsAnimationControlListener listener, int types,
+                    WindowInsetsAnimationCallback.InsetsAnimation animation,
+                    WindowInsetsAnimationCallback.AnimationBounds bounds,
+                    int layoutDuringAnimation) {
+                SurfaceAnimationThread.getHandler().post(() -> listener.onReady(controller, types));
+            }
+        }
+    }
+
     private class TransientControlTarget implements InsetsControlTarget {
 
         @Override
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..28dc2a4 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,10 @@
         info.taskId = mTaskId;
         info.displayId = getDisplayId();
         info.isRunning = getTopNonFinishingActivity() != null;
-        info.baseIntent = new Intent(getBaseIntent());
+        final Intent baseIntent = getBaseIntent();
+        // Make a copy of base intent because this is like a snapshot info.
+        // Besides, {@link RecentTasks#getRecentTasksImpl} may modify it.
+        info.baseIntent = baseIntent == null ? new Intent() : new Intent(baseIntent);
         info.baseActivity = mReuseActivitiesReport.base != null
                 ? mReuseActivitiesReport.base.intent.getComponent()
                 : null;
@@ -3229,6 +3232,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 +3382,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 +3872,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 +3905,9 @@
    }
 
     void setTaskOrganizer(ITaskOrganizer organizer) {
+        if (mTaskOrganizer == organizer) {
+            return;
+        }
         // Let the old organizer know it has lost control.
         if (mTaskOrganizer != null) {
             sendTaskVanished();
@@ -3918,8 +3933,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/Android.bp b/services/core/jni/Android.bp
index 4a2636e..390068e 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -22,6 +22,8 @@
         "BroadcastRadio/TunerCallback.cpp",
         "BroadcastRadio/convert.cpp",
         "BroadcastRadio/regions.cpp",
+        "stats/PowerStatsPuller.cpp",
+        "stats/SubsystemSleepStatePuller.cpp",
         "com_android_server_am_BatteryStatsService.cpp",
         "com_android_server_connectivity_Vpn.cpp",
         "com_android_server_ConsumerIrService.cpp",
@@ -37,6 +39,7 @@
         "com_android_server_security_VerityUtils.cpp",
         "com_android_server_SerialService.cpp",
         "com_android_server_soundtrigger_middleware_AudioSessionProviderImpl.cpp",
+        "com_android_server_stats_pull_StatsPullAtomService.cpp",
         "com_android_server_storage_AppFuseBridge.cpp",
         "com_android_server_SystemServer.cpp",
         "com_android_server_TestNetworkService.cpp",
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/core/jni/com_android_server_stats_pull_StatsPullAtomService.cpp b/services/core/jni/com_android_server_stats_pull_StatsPullAtomService.cpp
new file mode 100644
index 0000000..f5b778e
--- /dev/null
+++ b/services/core/jni/com_android_server_stats_pull_StatsPullAtomService.cpp
@@ -0,0 +1,73 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "StatsPullAtomService"
+
+#include <jni.h>
+#include <log/log.h>
+#include <nativehelper/JNIHelp.h>
+#include <stats_event.h>
+#include <stats_pull_atom_callback.h>
+#include <statslog.h>
+
+#include "stats/PowerStatsPuller.h"
+#include "stats/SubsystemSleepStatePuller.h"
+
+namespace android {
+
+static server::stats::PowerStatsPuller gPowerStatsPuller;
+static server::stats::SubsystemSleepStatePuller gSubsystemSleepStatePuller;
+
+static status_pull_atom_return_t onDevicePowerMeasurementCallback(int32_t atom_tag,
+                                                                  pulled_stats_event_list* data,
+                                                                  void* cookie) {
+    return gPowerStatsPuller.Pull(atom_tag, data);
+}
+
+static status_pull_atom_return_t subsystemSleepStateCallback(int32_t atom_tag,
+                                                             pulled_stats_event_list* data,
+                                                             void* cookie) {
+    return gSubsystemSleepStatePuller.Pull(atom_tag, data);
+}
+
+static void nativeInit(JNIEnv* env, jobject javaObject) {
+    // on device power measurement
+    gPowerStatsPuller = server::stats::PowerStatsPuller();
+    register_stats_pull_atom_callback(android::util::ON_DEVICE_POWER_MEASUREMENT,
+                                      onDevicePowerMeasurementCallback,
+                                      /* metadata= */ nullptr,
+                                      /* cookie= */ nullptr);
+
+    // subsystem sleep state
+    gSubsystemSleepStatePuller = server::stats::SubsystemSleepStatePuller();
+    register_stats_pull_atom_callback(android::util::SUBSYSTEM_SLEEP_STATE,
+                                      subsystemSleepStateCallback,
+                                      /* metadata= */ nullptr,
+                                      /* cookie= */ nullptr);
+}
+
+static const JNINativeMethod sMethods[] = {{"nativeInit", "()V", (void*)nativeInit}};
+
+int register_android_server_stats_pull_StatsPullAtomService(JNIEnv* env) {
+    int res = jniRegisterNativeMethods(env, "com/android/server/stats/pull/StatsPullAtomService",
+                                       sMethods, NELEM(sMethods));
+    if (res < 0) {
+        ALOGE("failed to register native methods");
+    }
+    return res;
+}
+
+} // namespace android
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index 19fa062..1202ad3 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -59,6 +59,7 @@
 int register_com_android_server_soundtrigger_middleware_AudioSessionProviderImpl(
         JNIEnv* env);
 int register_android_server_incremental_IncrementalManagerService(JNIEnv* env);
+int register_android_server_stats_pull_StatsPullAtomService(JNIEnv* env);
 };
 
 using namespace android;
@@ -111,5 +112,6 @@
     register_com_android_server_soundtrigger_middleware_AudioSessionProviderImpl(
             env);
     register_android_server_incremental_IncrementalManagerService(env);
+    register_android_server_stats_pull_StatsPullAtomService(env);
     return JNI_VERSION_1_4;
 }
diff --git a/services/core/jni/stats/OWNERS b/services/core/jni/stats/OWNERS
new file mode 100644
index 0000000..a61babf
--- /dev/null
+++ b/services/core/jni/stats/OWNERS
@@ -0,0 +1,9 @@
+jeffreyhuang@google.com
+joeo@google.com
+jtnguyen@google.com
+muhammadq@google.com
+ruchirr@google.com
+singhtejinder@google.com
+tsaichristine@google.com
+yaochen@google.com
+yro@google.com
diff --git a/services/core/jni/stats/PowerStatsPuller.cpp b/services/core/jni/stats/PowerStatsPuller.cpp
new file mode 100644
index 0000000..e80b5cf
--- /dev/null
+++ b/services/core/jni/stats/PowerStatsPuller.cpp
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define DEBUG false // STOPSHIP if true
+#define LOG_TAG "PowerStatsPuller"
+
+#include <android/hardware/power/stats/1.0/IPowerStats.h>
+#include <log/log.h>
+#include <statslog.h>
+
+#include <vector>
+
+#include "PowerStatsPuller.h"
+
+using android::hardware::hidl_vec;
+using android::hardware::Return;
+using android::hardware::Void;
+using android::hardware::power::stats::V1_0::EnergyData;
+using android::hardware::power::stats::V1_0::IPowerStats;
+using android::hardware::power::stats::V1_0::RailInfo;
+using android::hardware::power::stats::V1_0::Status;
+
+namespace android {
+namespace server {
+namespace stats {
+
+static sp<android::hardware::power::stats::V1_0::IPowerStats> gPowerStatsHal = nullptr;
+static std::mutex gPowerStatsHalMutex;
+static bool gPowerStatsExist = true; // Initialized to ensure making a first attempt.
+static std::vector<RailInfo> gRailInfo;
+
+struct PowerStatsPullerDeathRecipient : virtual public hardware::hidl_death_recipient {
+    virtual void serviceDied(uint64_t cookie,
+                             const wp<android::hidl::base::V1_0::IBase>& who) override {
+        // The HAL just died. Reset all handles to HAL services.
+        std::lock_guard<std::mutex> lock(gPowerStatsHalMutex);
+        gPowerStatsHal = nullptr;
+    }
+};
+
+static sp<PowerStatsPullerDeathRecipient> gDeathRecipient = new PowerStatsPullerDeathRecipient();
+
+static bool getPowerStatsHalLocked() {
+    if (gPowerStatsHal == nullptr && gPowerStatsExist) {
+        gPowerStatsHal = android::hardware::power::stats::V1_0::IPowerStats::getService();
+        if (gPowerStatsHal == nullptr) {
+            ALOGW("Couldn't load power.stats HAL service");
+            gPowerStatsExist = false;
+        } else {
+            // Link death recipient to power.stats service handle
+            hardware::Return<bool> linked = gPowerStatsHal->linkToDeath(gDeathRecipient, 0);
+            if (!linked.isOk()) {
+                ALOGE("Transaction error in linking to power.stats HAL death: %s",
+                      linked.description().c_str());
+                gPowerStatsHal = nullptr;
+                return false;
+            } else if (!linked) {
+                ALOGW("Unable to link to power.stats HAL death notifications");
+                // We should still continue even though linking failed
+            }
+        }
+    }
+    return gPowerStatsHal != nullptr;
+}
+
+PowerStatsPuller::PowerStatsPuller() {}
+
+status_pull_atom_return_t PowerStatsPuller::Pull(int32_t atomTag, pulled_stats_event_list* data) {
+    std::lock_guard<std::mutex> lock(gPowerStatsHalMutex);
+
+    if (!getPowerStatsHalLocked()) {
+        return STATS_PULL_SKIP;
+    }
+
+    // Pull getRailInfo if necessary
+    if (gRailInfo.empty()) {
+        bool resultSuccess = true;
+        Return<void> ret = gPowerStatsHal->getRailInfo(
+                [&resultSuccess](const hidl_vec<RailInfo>& list, Status status) {
+                    resultSuccess = (status == Status::SUCCESS || status == Status::NOT_SUPPORTED);
+                    if (status != Status::SUCCESS) return;
+                    gRailInfo.reserve(list.size());
+                    for (size_t i = 0; i < list.size(); ++i) {
+                        gRailInfo.push_back(list[i]);
+                    }
+                });
+        if (!resultSuccess || !ret.isOk()) {
+            ALOGE("power.stats getRailInfo() failed. Description: %s", ret.description().c_str());
+            gPowerStatsHal = nullptr;
+            return STATS_PULL_SKIP;
+        }
+        // If SUCCESS but empty, or if NOT_SUPPORTED, then never try again.
+        if (gRailInfo.empty()) {
+            ALOGE("power.stats has no rail information");
+            gPowerStatsExist = false; // No rail info, so never try again.
+            gPowerStatsHal = nullptr;
+            return STATS_PULL_SKIP;
+        }
+    }
+
+    // Pull getEnergyData and write the data out
+    const hidl_vec<uint32_t> desiredRailIndices; // Empty vector indicates we want all.
+    bool resultSuccess = true;
+    Return<void> ret =
+            gPowerStatsHal
+                    ->getEnergyData(desiredRailIndices,
+                                    [&data, &resultSuccess](hidl_vec<EnergyData> energyDataList,
+                                                            Status status) {
+                                        resultSuccess = (status == Status::SUCCESS);
+                                        if (!resultSuccess) return;
+
+                                        for (size_t i = 0; i < energyDataList.size(); i++) {
+                                            const EnergyData& energyData = energyDataList[i];
+
+                                            if (energyData.index >= gRailInfo.size()) {
+                                                ALOGE("power.stats getEnergyData() returned an "
+                                                      "invalid rail index %u.",
+                                                      energyData.index);
+                                                resultSuccess = false;
+                                                return;
+                                            }
+                                            const RailInfo& rail = gRailInfo[energyData.index];
+
+                                            stats_event* event = add_stats_event_to_pull_data(data);
+                                            stats_event_set_atom_id(event,
+                                                                    android::util::ON_DEVICE_POWER_MEASUREMENT);
+                                            stats_event_write_string8(event,
+                                                                      rail.subsysName.c_str());
+                                            stats_event_write_string8(event, rail.railName.c_str());
+                                            stats_event_write_int64(event, energyData.timestamp);
+                                            stats_event_write_int64(event, energyData.energy);
+                                            stats_event_build(event);
+
+                                            ALOGV("power.stat: %s.%s: %llu, %llu",
+                                                  rail.subsysName.c_str(), rail.railName.c_str(),
+                                                  (unsigned long long)energyData.timestamp,
+                                                  (unsigned long long)energyData.energy);
+                                        }
+                                    });
+    if (!resultSuccess || !ret.isOk()) {
+        ALOGE("power.stats getEnergyData() failed. Description: %s", ret.description().c_str());
+        gPowerStatsHal = nullptr;
+        return STATS_PULL_SKIP;
+    }
+    return STATS_PULL_SUCCESS;
+}
+
+} // namespace stats
+} // namespace server
+} // namespace android
diff --git a/cmds/statsd/src/external/PowerStatsPuller.h b/services/core/jni/stats/PowerStatsPuller.h
similarity index 72%
rename from cmds/statsd/src/external/PowerStatsPuller.h
rename to services/core/jni/stats/PowerStatsPuller.h
index 6f15bd6..048dbb9 100644
--- a/cmds/statsd/src/external/PowerStatsPuller.h
+++ b/services/core/jni/stats/PowerStatsPuller.h
@@ -16,23 +16,22 @@
 
 #pragma once
 
-#include "StatsPuller.h"
+#include <stats_event.h>
+#include <stats_pull_atom_callback.h>
 
 namespace android {
-namespace os {
-namespace statsd {
+namespace server {
+namespace stats {
 
 /**
  * Reads hal for power.stats
  */
-class PowerStatsPuller : public StatsPuller {
+class PowerStatsPuller {
 public:
     PowerStatsPuller();
-
-private:
-    bool PullInternal(vector<std::shared_ptr<LogEvent>>* data) override;
+    status_pull_atom_return_t Pull(int32_t atomTag, pulled_stats_event_list* data);
 };
 
-}  // namespace statsd
-}  // namespace os
-}  // namespace android
+} // namespace stats
+} // namespace server
+} // namespace android
diff --git a/services/core/jni/stats/SubsystemSleepStatePuller.cpp b/services/core/jni/stats/SubsystemSleepStatePuller.cpp
new file mode 100644
index 0000000..c6a836c
--- /dev/null
+++ b/services/core/jni/stats/SubsystemSleepStatePuller.cpp
@@ -0,0 +1,367 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define DEBUG false  // STOPSHIP if true
+#define LOG_TAG "SubsystemSleepStatePuller"
+
+#include <log/log.h>
+#include <statslog.h>
+
+#include <android/hardware/power/1.0/IPower.h>
+#include <android/hardware/power/1.1/IPower.h>
+#include <android/hardware/power/stats/1.0/IPowerStats.h>
+
+#include <fcntl.h>
+#include <hardware/power.h>
+#include <hardware_legacy/power.h>
+#include <inttypes.h>
+#include <semaphore.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <unordered_map>
+#include "SubsystemSleepStatePuller.h"
+
+using android::hardware::hidl_vec;
+using android::hardware::power::V1_0::IPower;
+using android::hardware::power::V1_0::PowerStatePlatformSleepState;
+using android::hardware::power::V1_0::PowerStateVoter;
+using android::hardware::power::V1_1::PowerStateSubsystem;
+using android::hardware::power::V1_1::PowerStateSubsystemSleepState;
+using android::hardware::power::stats::V1_0::PowerEntityInfo;
+using android::hardware::power::stats::V1_0::PowerEntityStateResidencyResult;
+using android::hardware::power::stats::V1_0::PowerEntityStateSpace;
+
+using android::hardware::Return;
+using android::hardware::Void;
+
+namespace android {
+namespace server {
+namespace stats {
+
+static std::function<status_pull_atom_return_t(int32_t atomTag, pulled_stats_event_list* data)>
+        gPuller = {};
+
+static sp<android::hardware::power::V1_0::IPower> gPowerHalV1_0 = nullptr;
+static sp<android::hardware::power::V1_1::IPower> gPowerHalV1_1 = nullptr;
+static sp<android::hardware::power::stats::V1_0::IPowerStats> gPowerStatsHalV1_0 = nullptr;
+
+static std::unordered_map<uint32_t, std::string> gEntityNames = {};
+static std::unordered_map<uint32_t, std::unordered_map<uint32_t, std::string>> gStateNames = {};
+
+static std::mutex gPowerHalMutex;
+
+// The caller must be holding gPowerHalMutex.
+static void deinitPowerStatsLocked() {
+    gPowerHalV1_0 = nullptr;
+    gPowerHalV1_1 = nullptr;
+    gPowerStatsHalV1_0 = nullptr;
+}
+
+struct SubsystemSleepStatePullerDeathRecipient : virtual public hardware::hidl_death_recipient {
+    virtual void serviceDied(uint64_t cookie,
+            const wp<android::hidl::base::V1_0::IBase>& who) override {
+
+        // The HAL just died. Reset all handles to HAL services.
+        std::lock_guard<std::mutex> lock(gPowerHalMutex);
+        deinitPowerStatsLocked();
+    }
+};
+
+static sp<SubsystemSleepStatePullerDeathRecipient> gDeathRecipient =
+        new SubsystemSleepStatePullerDeathRecipient();
+
+SubsystemSleepStatePuller::SubsystemSleepStatePuller() {}
+
+// The caller must be holding gPowerHalMutex.
+static bool checkResultLocked(const Return<void> &ret, const char* function) {
+    if (!ret.isOk()) {
+        ALOGE("%s failed: requested HAL service not available. Description: %s",
+            function, ret.description().c_str());
+        if (ret.isDeadObject()) {
+            deinitPowerStatsLocked();
+        }
+        return false;
+    }
+    return true;
+}
+
+// The caller must be holding gPowerHalMutex.
+// gPowerStatsHalV1_0 must not be null
+static bool initializePowerStats() {
+    using android::hardware::power::stats::V1_0::Status;
+
+    // Clear out previous content if we are re-initializing
+    gEntityNames.clear();
+    gStateNames.clear();
+
+    Return<void> ret;
+    ret = gPowerStatsHalV1_0->getPowerEntityInfo([](auto infos, auto status) {
+        if (status != Status::SUCCESS) {
+            ALOGE("Error getting power entity info");
+            return;
+        }
+
+        // construct lookup table of powerEntityId to power entity name
+        for (auto info : infos) {
+            gEntityNames.emplace(info.powerEntityId, info.powerEntityName);
+        }
+    });
+    if (!checkResultLocked(ret, __func__)) {
+        return false;
+    }
+
+    ret = gPowerStatsHalV1_0->getPowerEntityStateInfo({}, [](auto stateSpaces, auto status) {
+        if (status != Status::SUCCESS) {
+            ALOGE("Error getting state info");
+            return;
+        }
+
+        // construct lookup table of powerEntityId, powerEntityStateId to power entity state name
+        for (auto stateSpace : stateSpaces) {
+            std::unordered_map<uint32_t, std::string> stateNames = {};
+            for (auto state : stateSpace.states) {
+                stateNames.emplace(state.powerEntityStateId,
+                    state.powerEntityStateName);
+            }
+            gStateNames.emplace(stateSpace.powerEntityId, stateNames);
+        }
+    });
+    if (!checkResultLocked(ret, __func__)) {
+        return false;
+    }
+
+    return (!gEntityNames.empty()) && (!gStateNames.empty());
+}
+
+// The caller must be holding gPowerHalMutex.
+static bool getPowerStatsHalLocked() {
+    if(gPowerStatsHalV1_0 == nullptr) {
+        gPowerStatsHalV1_0 = android::hardware::power::stats::V1_0::IPowerStats::getService();
+        if (gPowerStatsHalV1_0 == nullptr) {
+            ALOGE("Unable to get power.stats HAL service.");
+            return false;
+        }
+
+        // Link death recipient to power.stats service handle
+        hardware::Return<bool> linked = gPowerStatsHalV1_0->linkToDeath(gDeathRecipient, 0);
+        if (!linked.isOk()) {
+            ALOGE("Transaction error in linking to power.stats HAL death: %s",
+                    linked.description().c_str());
+            deinitPowerStatsLocked();
+            return false;
+        } else if (!linked) {
+            ALOGW("Unable to link to power.stats HAL death notifications");
+            // We should still continue even though linking failed
+        }
+        return initializePowerStats();
+    }
+    return true;
+}
+
+// The caller must be holding gPowerHalMutex.
+static status_pull_atom_return_t getIPowerStatsDataLocked(int32_t atomTag,
+                                                          pulled_stats_event_list* data) {
+    using android::hardware::power::stats::V1_0::Status;
+
+    if(!getPowerStatsHalLocked()) {
+        return STATS_PULL_SKIP;
+    }
+    // Get power entity state residency data
+    bool success = false;
+    Return<void> ret = gPowerStatsHalV1_0->getPowerEntityStateResidencyData(
+            {}, [&data, &success](auto results, auto status) {
+                if (status == Status::NOT_SUPPORTED) {
+                    ALOGW("getPowerEntityStateResidencyData is not supported");
+                    success = false;
+                    return;
+                }
+                for (auto result : results) {
+                    for (auto stateResidency : result.stateResidencyData) {
+                        stats_event* event = add_stats_event_to_pull_data(data);
+                        stats_event_set_atom_id(event, android::util::SUBSYSTEM_SLEEP_STATE);
+                        stats_event_write_string8(event,
+                                                  gEntityNames.at(result.powerEntityId).c_str());
+                        stats_event_write_string8(event,
+                                                  gStateNames.at(result.powerEntityId)
+                                                          .at(stateResidency.powerEntityStateId)
+                                                          .c_str());
+                        stats_event_write_int64(event, stateResidency.totalStateEntryCount);
+                        stats_event_write_int64(event, stateResidency.totalTimeInStateMs);
+                        stats_event_build(event);
+                    }
+                }
+                success = true;
+            });
+    // Intentionally not returning early here.
+    // bool success determines if this succeeded or not.
+    checkResultLocked(ret, __func__);
+    if (!success) {
+        return STATS_PULL_SKIP;
+    }
+    return STATS_PULL_SUCCESS;
+}
+
+// The caller must be holding gPowerHalMutex.
+static bool getPowerHalLocked() {
+    if(gPowerHalV1_0 == nullptr) {
+        gPowerHalV1_0 = android::hardware::power::V1_0::IPower::getService();
+        if(gPowerHalV1_0 == nullptr) {
+            ALOGE("Unable to get power HAL service.");
+            return false;
+        }
+        gPowerHalV1_1 = android::hardware::power::V1_1::IPower::castFrom(gPowerHalV1_0);
+
+        // Link death recipient to power service handle
+        hardware::Return<bool> linked = gPowerHalV1_0->linkToDeath(gDeathRecipient, 0);
+        if (!linked.isOk()) {
+            ALOGE("Transaction error in linking to power HAL death: %s",
+                    linked.description().c_str());
+            gPowerHalV1_0 = nullptr;
+            return false;
+        } else if (!linked) {
+            ALOGW("Unable to link to power. death notifications");
+            // We should still continue even though linking failed
+        }
+    }
+    return true;
+}
+
+// The caller must be holding gPowerHalMutex.
+static status_pull_atom_return_t getIPowerDataLocked(int32_t atomTag,
+                                                     pulled_stats_event_list* data) {
+    using android::hardware::power::V1_0::Status;
+
+    if(!getPowerHalLocked()) {
+        return STATS_PULL_SKIP;
+    }
+
+        Return<void> ret;
+        ret = gPowerHalV1_0->getPlatformLowPowerStats(
+                [&data](hidl_vec<PowerStatePlatformSleepState> states, Status status) {
+                    if (status != Status::SUCCESS) return;
+
+                    for (size_t i = 0; i < states.size(); i++) {
+                        const PowerStatePlatformSleepState& state = states[i];
+                        stats_event* event = add_stats_event_to_pull_data(data);
+                        stats_event_set_atom_id(event, android::util::SUBSYSTEM_SLEEP_STATE);
+                        stats_event_write_string8(event, state.name.c_str());
+                        stats_event_write_string8(event, "");
+                        stats_event_write_int64(event, state.totalTransitions);
+                        stats_event_write_int64(event, state.residencyInMsecSinceBoot);
+                        stats_event_build(event);
+
+                        ALOGV("powerstate: %s, %lld, %lld, %d", state.name.c_str(),
+                              (long long)state.residencyInMsecSinceBoot,
+                              (long long)state.totalTransitions,
+                              state.supportedOnlyInSuspend ? 1 : 0);
+                        for (const auto& voter : state.voters) {
+                            stats_event* event = add_stats_event_to_pull_data(data);
+                            stats_event_set_atom_id(event, android::util::SUBSYSTEM_SLEEP_STATE);
+                            stats_event_write_string8(event, state.name.c_str());
+                            stats_event_write_string8(event, voter.name.c_str());
+                            stats_event_write_int64(event, voter.totalNumberOfTimesVotedSinceBoot);
+                            stats_event_write_int64(event, voter.totalTimeInMsecVotedForSinceBoot);
+                            stats_event_build(event);
+
+                            ALOGV("powerstatevoter: %s, %s, %lld, %lld", state.name.c_str(),
+                                  voter.name.c_str(),
+                                  (long long)voter.totalTimeInMsecVotedForSinceBoot,
+                                  (long long)voter.totalNumberOfTimesVotedSinceBoot);
+                        }
+                    }
+                });
+        if (!checkResultLocked(ret, __func__)) {
+            return STATS_PULL_SKIP;
+        }
+
+        // Trying to cast to IPower 1.1, this will succeed only for devices supporting 1.1
+        sp<android::hardware::power::V1_1::IPower> gPowerHal_1_1 =
+                android::hardware::power::V1_1::IPower::castFrom(gPowerHalV1_0);
+        if (gPowerHal_1_1 != nullptr) {
+            ret = gPowerHal_1_1->getSubsystemLowPowerStats(
+                    [&data](hidl_vec<PowerStateSubsystem> subsystems, Status status) {
+                        if (status != Status::SUCCESS) return;
+
+                        if (subsystems.size() > 0) {
+                            for (size_t i = 0; i < subsystems.size(); i++) {
+                                const PowerStateSubsystem& subsystem = subsystems[i];
+                                for (size_t j = 0; j < subsystem.states.size(); j++) {
+                                    const PowerStateSubsystemSleepState& state =
+                                            subsystem.states[j];
+                                    stats_event* event = add_stats_event_to_pull_data(data);
+                                    stats_event_set_atom_id(event,
+                                                            android::util::SUBSYSTEM_SLEEP_STATE);
+                                    stats_event_write_string8(event, subsystem.name.c_str());
+                                    stats_event_write_string8(event, state.name.c_str());
+                                    stats_event_write_int64(event, state.totalTransitions);
+                                    stats_event_write_int64(event, state.residencyInMsecSinceBoot);
+                                    stats_event_build(event);
+
+                                    ALOGV("subsystemstate: %s, %s, %lld, %lld, %lld",
+                                          subsystem.name.c_str(), state.name.c_str(),
+                                          (long long)state.residencyInMsecSinceBoot,
+                                          (long long)state.totalTransitions,
+                                          (long long)state.lastEntryTimestampMs);
+                                }
+                            }
+                        }
+                    });
+        }
+        return STATS_PULL_SUCCESS;
+}
+
+// The caller must be holding gPowerHalMutex.
+std::function<status_pull_atom_return_t(int32_t atomTag, pulled_stats_event_list* data)>
+getPullerLocked() {
+    std::function<status_pull_atom_return_t(int32_t atomTag, pulled_stats_event_list * data)> ret =
+            {};
+
+    // First see if power.stats HAL is available. Fall back to power HAL if
+    // power.stats HAL is unavailable.
+    if(android::hardware::power::stats::V1_0::IPowerStats::getService() != nullptr) {
+        ALOGI("Using power.stats HAL");
+        ret = getIPowerStatsDataLocked;
+    } else if(android::hardware::power::V1_0::IPower::getService() != nullptr) {
+        ALOGI("Using power HAL");
+        ret = getIPowerDataLocked;
+    }
+
+    return ret;
+}
+
+status_pull_atom_return_t SubsystemSleepStatePuller::Pull(int32_t atomTag,
+                                                          pulled_stats_event_list* data) {
+    std::lock_guard<std::mutex> lock(gPowerHalMutex);
+
+    if(!gPuller) {
+        gPuller = getPullerLocked();
+    }
+
+    if(gPuller) {
+        return gPuller(atomTag, data);
+    }
+
+    ALOGE("Unable to load Power Hal or power.stats HAL");
+    return STATS_PULL_SKIP;
+}
+
+} // namespace stats
+} // namespace server
+}  // namespace android
diff --git a/cmds/statsd/src/external/SubsystemSleepStatePuller.h b/services/core/jni/stats/SubsystemSleepStatePuller.h
similarity index 71%
rename from cmds/statsd/src/external/SubsystemSleepStatePuller.h
rename to services/core/jni/stats/SubsystemSleepStatePuller.h
index 87f5f02..59dbbd2 100644
--- a/cmds/statsd/src/external/SubsystemSleepStatePuller.h
+++ b/services/core/jni/stats/SubsystemSleepStatePuller.h
@@ -16,24 +16,22 @@
 
 #pragma once
 
-#include <utils/String16.h>
-#include "StatsPuller.h"
+#include <stats_event.h>
+#include <stats_pull_atom_callback.h>
 
 namespace android {
-namespace os {
-namespace statsd {
+namespace server {
+namespace stats {
 
 /**
  * Reads hal for sleep states
  */
-class SubsystemSleepStatePuller : public StatsPuller {
+class SubsystemSleepStatePuller {
 public:
     SubsystemSleepStatePuller();
-
-private:
-    bool PullInternal(vector<std::shared_ptr<LogEvent>>* data) override;
+    status_pull_atom_return_t Pull(int32_t atomTag, pulled_stats_event_list* data);
 };
 
-}  // namespace statsd
-}  // namespace os
-}  // namespace android
+} // namespace stats
+} // namespace server
+} // namespace android
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index b7162f9..6ba4330 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -342,6 +342,7 @@
 
     private static final String START_SENSOR_SERVICE = "StartSensorService";
     private static final String START_HIDL_SERVICES = "StartHidlServices";
+    private static final String START_BLOB_STORE_SERVICE = "startBlobStoreManagerService";
 
     private static final String SYSPROP_START_COUNT = "sys.system_server.start_count";
     private static final String SYSPROP_START_ELAPSED = "sys.system_server.start_elapsed";
@@ -349,6 +350,7 @@
 
     private Future<?> mSensorServiceStart;
     private Future<?> mZygotePreload;
+    private Future<?> mBlobStoreServiceStart;
 
     /**
      * Start the sensor service. This is a blocking call and can take time.
@@ -365,6 +367,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 +507,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();
@@ -1784,6 +1797,13 @@
                 t.traceEnd();
             }
 
+            mBlobStoreServiceStart = SystemServerInitThreadPool.submit(() -> {
+                final TimingsTraceAndSlog traceLog = TimingsTraceAndSlog.newAsyncLog();
+                traceLog.traceBegin(START_BLOB_STORE_SERVICE);
+                mSystemServiceManager.startService(BLOB_STORE_MANAGER_SERVICE_CLASS);
+                traceLog.traceEnd();
+            }, START_BLOB_STORE_SERVICE);
+
             // Dreams (interactive idle-time views, a/k/a screen savers, and doze mode)
             t.traceBegin("StartDreamManager");
             mSystemServiceManager.startService(DreamManagerService.class);
@@ -2028,10 +2048,6 @@
         mSystemServiceManager.startService(ClipboardService.class);
         t.traceEnd();
 
-        t.traceBegin("StartBlobStoreManagerService");
-        mSystemServiceManager.startService(BLOB_STORE_MANAGER_SERVICE_CLASS);
-        t.traceEnd();
-
         t.traceBegin("AppServiceManager");
         mSystemServiceManager.startService(AppBindingService.Lifecycle.class);
         t.traceEnd();
@@ -2149,6 +2165,9 @@
         mSystemServiceManager.startService(APP_SEARCH_MANAGER_SERVICE_CLASS);
         t.traceEnd();
 
+        ConcurrentUtils.waitForFutureNoInterrupt(mBlobStoreServiceStart,
+                START_BLOB_STORE_SERVICE);
+
         // These are needed to propagate to the runnable below.
         final NetworkManagementService networkManagementF = networkManagement;
         final NetworkStatsService networkStatsF = networkStats;
diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp
index f990810..8381205 100644
--- a/services/tests/servicestests/Android.bp
+++ b/services/tests/servicestests/Android.bp
@@ -46,6 +46,7 @@
         "service-appsearch",
         "service-jobscheduler",
         "service-permission",
+        "service-blobstore",
         // TODO: remove once Android migrates to JUnit 4.12,
         // which provides assertThrows
         "testng",
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/servicestests/src/com/android/server/blob/BlobStoreManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/blob/BlobStoreManagerServiceTest.java
new file mode 100644
index 0000000..ff728e7
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/blob/BlobStoreManagerServiceTest.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright 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.blob;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.blob.BlobHandle;
+import android.content.Context;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.UserHandle;
+import android.platform.test.annotations.Presubmit;
+import android.util.ArrayMap;
+import android.util.LongSparseArray;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.blob.BlobStoreManagerService.Injector;
+import com.android.server.blob.BlobStoreManagerService.SessionStateChangeListener;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+@Presubmit
+public class BlobStoreManagerServiceTest {
+    private Context mContext;
+    private Handler mHandler;
+    private BlobStoreManagerService mService;
+
+    private LongSparseArray<BlobStoreSession> mUserSessions;
+    private ArrayMap<BlobHandle, BlobMetadata> mUserBlobs;
+
+    private SessionStateChangeListener mStateChangeListener;
+
+    private static final String TEST_PKG1 = "com.example1";
+    private static final String TEST_PKG2 = "com.example2";
+    private static final String TEST_PKG3 = "com.example3";
+
+    private static final int TEST_UID1 = 10001;
+    private static final int TEST_UID2 = 10002;
+    private static final int TEST_UID3 = 10003;
+
+    @Before
+    public void setUp() {
+        // Share classloader to allow package private access.
+        System.setProperty("dexmaker.share_classloader", "true");
+
+        mContext = InstrumentationRegistry.getTargetContext();
+        mHandler = new TestHandler(Looper.getMainLooper());
+        mService = new BlobStoreManagerService(mContext, new TestInjector());
+        mUserSessions = new LongSparseArray<>();
+        mUserBlobs = new ArrayMap<>();
+
+        mService.addUserSessionsForTest(mUserSessions, UserHandle.myUserId());
+        mService.addUserBlobsForTest(mUserBlobs, UserHandle.myUserId());
+
+        mStateChangeListener = mService.new SessionStateChangeListener();
+    }
+
+    @Test
+    public void testHandlePackageRemoved() throws Exception {
+        // Setup sessions
+        final File sessionFile1 = mock(File.class);
+        final long sessionId1 = 11;
+        final BlobStoreSession session1 = createBlobStoreSessionMock(TEST_PKG1, TEST_UID1,
+                sessionId1, sessionFile1);
+        mUserSessions.append(sessionId1, session1);
+
+        final File sessionFile2 = mock(File.class);
+        final long sessionId2 = 25;
+        final BlobStoreSession session2 = createBlobStoreSessionMock(TEST_PKG2, TEST_UID2,
+                sessionId2, sessionFile2);
+        mUserSessions.append(sessionId2, session2);
+
+        final File sessionFile3 = mock(File.class);
+        final long sessionId3 = 37;
+        final BlobStoreSession session3 = createBlobStoreSessionMock(TEST_PKG3, TEST_UID3,
+                sessionId3, sessionFile3);
+        mUserSessions.append(sessionId3, session3);
+
+        final File sessionFile4 = mock(File.class);
+        final long sessionId4 = 48;
+        final BlobStoreSession session4 = createBlobStoreSessionMock(TEST_PKG1, TEST_UID1,
+                sessionId4, sessionFile4);
+        mUserSessions.append(sessionId4, session4);
+
+        // Setup blobs
+        final File blobFile1 = mock(File.class);
+        final BlobHandle blobHandle1 = BlobHandle.createWithSha256("digest1".getBytes(),
+                "label1", System.currentTimeMillis(), "tag1");
+        final BlobMetadata blobMetadata1 = createBlobMetadataMock(blobFile1, true);
+        mUserBlobs.put(blobHandle1, blobMetadata1);
+
+        final File blobFile2 = mock(File.class);
+        final BlobHandle blobHandle2 = BlobHandle.createWithSha256("digest2".getBytes(),
+                "label2", System.currentTimeMillis(), "tag2");
+        final BlobMetadata blobMetadata2 = createBlobMetadataMock(blobFile2, false);
+        mUserBlobs.put(blobHandle2, blobMetadata2);
+
+        // Invoke test method
+        mService.handlePackageRemoved(TEST_PKG1, TEST_UID1);
+
+        // Verify sessions are removed
+        verify(sessionFile1).delete();
+        verify(sessionFile2, never()).delete();
+        verify(sessionFile3, never()).delete();
+        verify(sessionFile4).delete();
+
+        assertThat(mUserSessions.size()).isEqualTo(2);
+        assertThat(mUserSessions.get(sessionId1)).isNull();
+        assertThat(mUserSessions.get(sessionId2)).isNotNull();
+        assertThat(mUserSessions.get(sessionId3)).isNotNull();
+        assertThat(mUserSessions.get(sessionId4)).isNull();
+
+        // Verify blobs are removed
+        verify(blobMetadata1).removeCommitter(TEST_PKG1, TEST_UID1);
+        verify(blobMetadata1).removeLeasee(TEST_PKG1, TEST_UID1);
+        verify(blobMetadata2).removeCommitter(TEST_PKG1, TEST_UID1);
+        verify(blobMetadata2).removeLeasee(TEST_PKG1, TEST_UID1);
+
+        verify(blobFile1, never()).delete();
+        verify(blobFile2).delete();
+
+        assertThat(mUserBlobs.size()).isEqualTo(1);
+        assertThat(mUserBlobs.get(blobHandle1)).isNotNull();
+        assertThat(mUserBlobs.get(blobHandle2)).isNull();
+    }
+
+    private BlobStoreSession createBlobStoreSessionMock(String ownerPackageName, int ownerUid,
+            long sessionId, File sessionFile) {
+        final BlobStoreSession session = mock(BlobStoreSession.class);
+        when(session.getOwnerPackageName()).thenReturn(ownerPackageName);
+        when(session.getOwnerUid()).thenReturn(ownerUid);
+        when(session.getSessionId()).thenReturn(sessionId);
+        when(session.getSessionFile()).thenReturn(sessionFile);
+        return session;
+    }
+
+    private BlobMetadata createBlobMetadataMock(File blobFile, boolean hasLeases) {
+        final BlobMetadata blobMetadata = mock(BlobMetadata.class);
+        when(blobMetadata.getBlobFile()).thenReturn(blobFile);
+        when(blobMetadata.hasLeases()).thenReturn(hasLeases);
+        return blobMetadata;
+    }
+
+    private class TestHandler extends Handler {
+        TestHandler(Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public void dispatchMessage(Message msg) {
+            // Ignore all messages
+        }
+    }
+
+    private class TestInjector extends Injector {
+        @Override
+        public Handler initializeMessageHandler() {
+            return mHandler;
+        }
+    }
+}
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/DisplayAreaTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
new file mode 100644
index 0000000..618e608
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
@@ -0,0 +1,128 @@
+/*
+ * 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.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_PRESENTATION;
+import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
+
+import static com.android.server.wm.DisplayArea.Type.ABOVE_TASKS;
+import static com.android.server.wm.DisplayArea.Type.ANY;
+import static com.android.server.wm.DisplayArea.Type.BELOW_TASKS;
+import static com.android.server.wm.DisplayArea.Type.checkChild;
+import static com.android.server.wm.DisplayArea.Type.checkSiblings;
+import static com.android.server.wm.DisplayArea.Type.typeOf;
+import static com.android.server.wm.testing.Assert.assertThrows;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+
+import android.os.Binder;
+import android.platform.test.annotations.Presubmit;
+import android.view.SurfaceControl;
+
+import org.junit.Rule;
+import org.junit.Test;
+
+@Presubmit
+public class DisplayAreaTest {
+
+    @Rule
+    public SystemServicesTestRule mWmsRule = new SystemServicesTestRule();
+
+    @Test
+    public void testDisplayArea_positionChanged_throwsIfIncompatibleChild() {
+        WindowManagerService wms = mWmsRule.getWindowManagerService();
+        DisplayArea<WindowContainer> parent = new DisplayArea<>(wms, BELOW_TASKS, "Parent");
+        DisplayArea<WindowContainer> child = new DisplayArea<>(wms, ANY, "Child");
+
+        assertThrows(IllegalStateException.class, () -> parent.addChild(child, 0));
+    }
+
+    @Test
+    public void testDisplayArea_positionChanged_throwsIfIncompatibleSibling() {
+        WindowManagerService wms = mWmsRule.getWindowManagerService();
+        DisplayArea<WindowContainer> parent = new SurfacelessDisplayArea<>(wms, ANY, "Parent");
+        DisplayArea<WindowContainer> child1 = new DisplayArea<>(wms, ANY, "Child1");
+        DisplayArea<WindowContainer> child2 = new DisplayArea<>(wms, ANY, "Child2");
+
+        parent.addChild(child1, 0);
+        assertThrows(IllegalStateException.class, () -> parent.addChild(child2, 0));
+    }
+
+    @Test
+    public void testType_typeOf() {
+        WindowManagerService wms = mWmsRule.getWindowManagerService();
+
+        assertEquals(ABOVE_TASKS, typeOf(new DisplayArea<>(wms, ABOVE_TASKS, "test")));
+        assertEquals(ANY, typeOf(new DisplayArea<>(wms, ANY, "test")));
+        assertEquals(BELOW_TASKS, typeOf(new DisplayArea<>(wms, BELOW_TASKS, "test")));
+
+        assertEquals(ABOVE_TASKS, typeOf(createWindowToken(TYPE_APPLICATION_OVERLAY)));
+        assertEquals(ABOVE_TASKS, typeOf(createWindowToken(TYPE_PRESENTATION)));
+        assertEquals(BELOW_TASKS, typeOf(createWindowToken(TYPE_WALLPAPER)));
+
+        assertThrows(IllegalArgumentException.class, () -> typeOf(mock(ActivityRecord.class)));
+        assertThrows(IllegalArgumentException.class, () -> typeOf(mock(WindowContainer.class)));
+    }
+
+    @Test
+    public void testType_checkSiblings() {
+        checkSiblings(BELOW_TASKS, BELOW_TASKS);
+        checkSiblings(BELOW_TASKS, ANY);
+        checkSiblings(BELOW_TASKS, ABOVE_TASKS);
+        checkSiblings(ANY, ABOVE_TASKS);
+        checkSiblings(ABOVE_TASKS, ABOVE_TASKS);
+
+        assertThrows(IllegalStateException.class, () -> checkSiblings(ABOVE_TASKS, BELOW_TASKS));
+        assertThrows(IllegalStateException.class, () -> checkSiblings(ABOVE_TASKS, ANY));
+        assertThrows(IllegalStateException.class, () -> checkSiblings(ANY, ANY));
+        assertThrows(IllegalStateException.class, () -> checkSiblings(ANY, BELOW_TASKS));
+    }
+
+    @Test
+    public void testType_checkChild() {
+        checkChild(ANY, ANY);
+        checkChild(ANY, ABOVE_TASKS);
+        checkChild(ANY, BELOW_TASKS);
+        checkChild(ABOVE_TASKS, ABOVE_TASKS);
+        checkChild(BELOW_TASKS, BELOW_TASKS);
+
+        assertThrows(IllegalStateException.class, () -> checkChild(ABOVE_TASKS, BELOW_TASKS));
+        assertThrows(IllegalStateException.class, () -> checkChild(ABOVE_TASKS, ANY));
+        assertThrows(IllegalStateException.class, () -> checkChild(BELOW_TASKS, ABOVE_TASKS));
+        assertThrows(IllegalStateException.class, () -> checkChild(BELOW_TASKS, ANY));
+    }
+
+    private WindowToken createWindowToken(int type) {
+        return new WindowToken(mWmsRule.getWindowManagerService(), new Binder(),
+                type, false /* persist */, null /* displayContent */,
+                false /* canManageTokens */);
+    }
+
+    private static class SurfacelessDisplayArea<T extends WindowContainer> extends DisplayArea<T> {
+
+        SurfacelessDisplayArea(WindowManagerService wms, Type type, String name) {
+            super(wms, type, name);
+        }
+
+        @Override
+        SurfaceControl.Builder makeChildSurface(WindowContainer child) {
+            return new MockSurfaceControlBuilder();
+        }
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 1637370..2ea00ce 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -139,11 +139,11 @@
                 mAppWindow,
                 mChildAppWindowAbove,
                 mDockedDividerWindow,
+                mImeWindow,
+                mImeDialogWindow,
                 mStatusBarWindow,
                 mNotificationShadeWindow,
-                mNavBarWindow,
-                mImeWindow,
-                mImeDialogWindow));
+                mNavBarWindow));
     }
 
     @Test
@@ -232,11 +232,11 @@
                 mChildAppWindowAbove,
                 mDockedDividerWindow,
                 voiceInteractionWindow,
+                mImeWindow,
+                mImeDialogWindow,
                 mStatusBarWindow,
                 mNotificationShadeWindow,
-                mNavBarWindow,
-                mImeWindow,
-                mImeDialogWindow));
+                mNavBarWindow));
     }
 
     @Test
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/services/tests/wmtests/src/com/android/server/wm/testing/Assert.java b/services/tests/wmtests/src/com/android/server/wm/testing/Assert.java
new file mode 100644
index 0000000..1e98277
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/testing/Assert.java
@@ -0,0 +1,48 @@
+/*
+ * 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.testing;
+
+/**
+ * Assertions for WM tests.
+ */
+public class Assert {
+
+    /**
+     * Runs {@code r} and asserts that an exception of type {@code expectedThrowable} is thrown.
+     * @param expectedThrowable the type of throwable that is expected to be thrown
+     * @param r the {@link Runnable} which is run and expected to throw.
+     * @throws AssertionError if {@code r} does not throw, or throws a runnable that is not an
+     *                        instance of {@code expectedThrowable}.
+     */
+    // TODO: remove once Android migrates to JUnit 4.13, which provides assertThrows
+    public static void assertThrows(Class<? extends Throwable> expectedThrowable, Runnable r) {
+        try {
+            r.run();
+        } catch (Throwable t) {
+            if (expectedThrowable.isInstance(t)) {
+                return;
+            } else if (t instanceof Exception) {
+                throw new AssertionError("Expected " + expectedThrowable
+                        + ", but got " + t.getClass(), t);
+            } else {
+                // Re-throw Errors and other non-Exception throwables.
+                throw t;
+            }
+        }
+        throw new AssertionError("Expected " + expectedThrowable + ", but nothing was thrown");
+    }
+}
diff --git a/services/tests/wmtests/src/com/android/server/wm/testing/AssertTest.java b/services/tests/wmtests/src/com/android/server/wm/testing/AssertTest.java
new file mode 100644
index 0000000..df12761
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/testing/AssertTest.java
@@ -0,0 +1,85 @@
+/*
+ * 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.testing;
+
+import static com.android.server.wm.testing.Assert.assertThrows;
+
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+public class AssertTest {
+
+    @Rule
+    public ExpectedException mExpectedException = ExpectedException.none();
+
+    @Test
+    public void assertThrows_runsRunnable() {
+        boolean[] ran = new boolean[] { false };
+        assertThrows(TestException.class, () -> {
+            ran[0] = true;
+            throw new TestException();
+        });
+        assertTrue(ran[0]);
+    }
+
+    @Test
+    public void assertThrows_failsIfNothingThrown() {
+        mExpectedException.expect(AssertionError.class);
+        assertThrows(TestException.class, () -> {
+        });
+    }
+
+    @Test
+    public void assertThrows_failsIfWrongExceptionThrown() {
+        mExpectedException.expect(AssertionError.class);
+        assertThrows(TestException.class, () -> {
+            throw new RuntimeException();
+        });
+    }
+
+    @Test
+    public void assertThrows_succeedsIfGivenExceptionThrown() {
+        assertThrows(TestException.class, () -> {
+            throw new TestException();
+        });
+    }
+
+    @Test
+    public void assertThrows_succeedsIfSubExceptionThrown() {
+        assertThrows(RuntimeException.class, () -> {
+            throw new TestException();
+        });
+    }
+
+    @Test
+    public void assertThrows_rethrowsUnexpectedErrors() {
+        mExpectedException.expect(TestError.class);
+        assertThrows(TestException.class, () -> {
+            throw new TestError();
+        });
+    }
+
+    static class TestException extends RuntimeException {
+    }
+
+    static class TestError extends Error {
+    }
+
+}
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);
+    }
 }