Merge changes from topics "FrameInfo", "_advanceFrame"

* changes:
  Implement FrameInfo methods on AImageDecoder
  Implement AImageDecoder _advanceFrame and _rewind
diff --git a/Android.bp b/Android.bp
index df0cf0b..1a73e9d 100644
--- a/Android.bp
+++ b/Android.bp
@@ -528,6 +528,7 @@
         "android.hardware.vibrator-V1.1-java",
         "android.hardware.vibrator-V1.2-java",
         "android.hardware.vibrator-V1.3-java",
+        "android.security.apc-java",
         "android.system.keystore2-java",
         "android.system.suspend.control.internal-java",
         "cameraprotosnano",
diff --git a/OWNERS b/OWNERS
index 4160122..710f13e 100644
--- a/OWNERS
+++ b/OWNERS
@@ -9,6 +9,7 @@
 jjaggi@google.com
 jsharkey@android.com
 jsharkey@google.com
+lorenzo@google.com
 michaelwr@google.com
 nandana@google.com
 narayan@google.com
diff --git a/apct-tests/perftests/core/src/android/mtp_perf/AppFusePerfTest.java b/apct-tests/perftests/core/src/android/mtp_perf/AppFusePerfTest.java
new file mode 100644
index 0000000..fcbfc72
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/mtp_perf/AppFusePerfTest.java
@@ -0,0 +1,122 @@
+/*
+ * 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.mtp_perf;
+
+import android.app.Activity;
+import android.content.Context;
+import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
+import android.os.ProxyFileDescriptorCallback;
+import android.os.storage.StorageManager;
+import android.system.ErrnoException;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.IOException;
+
+@RunWith(AndroidJUnit4.class)
+@LargeTest
+public class AppFusePerfTest {
+    static final int SIZE = 10 * 1024 * 1024; // 10MB
+
+    @Test
+    public void testReadWriteFile() throws IOException {
+        final Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
+        final StorageManager storageManager = context.getSystemService(StorageManager.class);
+
+        final byte[] bytes = new byte[SIZE];
+        final int samples = 100;
+        final double[] readTime = new double[samples];
+        final double[] writeTime = new double[samples];
+
+        for (int i = 0; i < samples; i++) {
+            final ParcelFileDescriptor fd = storageManager.openProxyFileDescriptor(
+                    ParcelFileDescriptor.MODE_READ_ONLY, new TestCallback());
+            try (final ParcelFileDescriptor.AutoCloseInputStream stream =
+                    new ParcelFileDescriptor.AutoCloseInputStream(fd)) {
+                final long startTime = System.nanoTime();
+                stream.read(bytes);
+                readTime[i] = (System.nanoTime() - startTime) / 1000.0 / 1000.0;
+            }
+        }
+
+        for (int i = 0; i < samples; i++) {
+            final ParcelFileDescriptor fd = storageManager.openProxyFileDescriptor(
+                    ParcelFileDescriptor.MODE_WRITE_ONLY | ParcelFileDescriptor.MODE_TRUNCATE,
+                    new TestCallback());
+            try (final ParcelFileDescriptor.AutoCloseOutputStream stream =
+                    new ParcelFileDescriptor.AutoCloseOutputStream(fd)) {
+                final long startTime = System.nanoTime();
+                stream.write(bytes);
+                writeTime[i] = (System.nanoTime() - startTime) / 1000.0 / 1000.0;
+            }
+        }
+
+        double readAverage = 0;
+        double writeAverage = 0;
+        double readSquaredAverage = 0;
+        double writeSquaredAverage = 0;
+        for (int i = 0; i < samples; i++) {
+            readAverage += readTime[i];
+            writeAverage += writeTime[i];
+            readSquaredAverage += readTime[i] * readTime[i];
+            writeSquaredAverage += writeTime[i] * writeTime[i];
+        }
+
+        readAverage /= samples;
+        writeAverage /= samples;
+        readSquaredAverage /= samples;
+        writeSquaredAverage /= samples;
+
+        final Bundle results = new Bundle();
+        results.putDouble("readAverage", readAverage);
+        results.putDouble("readStandardDeviation",
+                Math.sqrt(readSquaredAverage - readAverage * readAverage));
+        results.putDouble("writeAverage", writeAverage);
+        results.putDouble("writeStandardDeviation",
+                Math.sqrt(writeSquaredAverage - writeAverage * writeAverage));
+        InstrumentationRegistry.getInstrumentation().sendStatus(Activity.RESULT_OK, results);
+    }
+
+    private static class TestCallback extends ProxyFileDescriptorCallback {
+        @Override
+        public long onGetSize() throws ErrnoException {
+            return SIZE;
+        }
+
+        @Override
+        public int onRead(long offset, int size, byte[] data) throws ErrnoException {
+            return size;
+        }
+
+        @Override
+        public int onWrite(long offset, int size, byte[] data) throws ErrnoException {
+            return size;
+        }
+
+        @Override
+        public void onFsync() throws ErrnoException {}
+
+        @Override
+        public void onRelease() {}
+    }
+}
diff --git a/apct-tests/perftests/windowmanager/src/android/wm/WindowAddRemovePerfTest.java b/apct-tests/perftests/windowmanager/src/android/wm/WindowAddRemovePerfTest.java
index 9403e8b..c37f6d9 100644
--- a/apct-tests/perftests/windowmanager/src/android/wm/WindowAddRemovePerfTest.java
+++ b/apct-tests/perftests/windowmanager/src/android/wm/WindowAddRemovePerfTest.java
@@ -107,8 +107,8 @@
 
                 long startTime = SystemClock.elapsedRealtimeNanos();
                 session.addToDisplay(this, mLayoutParams, View.VISIBLE,
-                        Display.DEFAULT_DISPLAY, mRequestedVisibility, mOutFrame,
-                        mOutDisplayCutout, inputChannel, mOutInsetsState, mOutControls);
+                        Display.DEFAULT_DISPLAY, mRequestedVisibility, mOutFrame, inputChannel,
+                        mOutInsetsState, mOutControls);
                 final long elapsedTimeNsOfAdd = SystemClock.elapsedRealtimeNanos() - startTime;
                 state.addExtraResult("add", elapsedTimeNsOfAdd);
 
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
index da9b8bd..d628fb5 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java
@@ -23,6 +23,9 @@
 import android.os.ParcelableException;
 import android.os.RemoteException;
 import android.util.ArraySet;
+import android.util.Log;
+
+import com.android.internal.util.Preconditions;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -39,10 +42,13 @@
  * This class is thread safe.
  */
 public final class AppSearchSession {
+    private static final String TAG = "AppSearchSession";
     private final String mDatabaseName;
     @UserIdInt
     private final int mUserId;
     private final IAppSearchManager mService;
+    private boolean mIsMutated = false;
+    private boolean mIsClosed = false;
 
     static void createSearchSession(
             @NonNull AppSearchManager.SearchContext searchContext,
@@ -150,6 +156,7 @@
         Objects.requireNonNull(request);
         Objects.requireNonNull(executor);
         Objects.requireNonNull(callback);
+        Preconditions.checkState(!mIsClosed, "AppSearchSession has already been closed");
         List<Bundle> schemaBundles = new ArrayList<>(request.getSchemas().size());
         for (AppSearchSchema schema : request.getSchemas()) {
             schemaBundles.add(schema.getBundle());
@@ -166,6 +173,7 @@
                             executor.execute(() -> callback.accept(result));
                         }
                     });
+            mIsMutated = true;
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -182,6 +190,7 @@
             @NonNull Consumer<AppSearchResult<Set<AppSearchSchema>>> callback) {
         Objects.requireNonNull(executor);
         Objects.requireNonNull(callback);
+        Preconditions.checkState(!mIsClosed, "AppSearchSession has already been closed");
         try {
             mService.getSchema(
                     mDatabaseName,
@@ -232,6 +241,7 @@
         Objects.requireNonNull(request);
         Objects.requireNonNull(executor);
         Objects.requireNonNull(callback);
+        Preconditions.checkState(!mIsClosed, "AppSearchSession has already been closed");
         List<GenericDocument> documents = request.getDocuments();
         List<Bundle> documentBundles = new ArrayList<>(documents.size());
         for (int i = 0; i < documents.size(); i++) {
@@ -248,6 +258,7 @@
                             executor.execute(() -> callback.onSystemError(exception.getCause()));
                         }
                     });
+            mIsMutated = true;
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -275,6 +286,7 @@
         Objects.requireNonNull(request);
         Objects.requireNonNull(executor);
         Objects.requireNonNull(callback);
+        Preconditions.checkState(!mIsClosed, "AppSearchSession has already been closed");
         try {
             mService.getDocuments(mDatabaseName, request.getNamespace(),
                     new ArrayList<>(request.getUris()), mUserId,
@@ -379,6 +391,7 @@
         Objects.requireNonNull(queryExpression);
         Objects.requireNonNull(searchSpec);
         Objects.requireNonNull(executor);
+        Preconditions.checkState(!mIsClosed, "AppSearchSession has already been closed");
         return new SearchResults(mService, mDatabaseName, queryExpression, searchSpec, mUserId,
                 executor);
     }
@@ -404,6 +417,7 @@
         Objects.requireNonNull(request);
         Objects.requireNonNull(executor);
         Objects.requireNonNull(callback);
+        Preconditions.checkState(!mIsClosed, "AppSearchSession has already been closed");
         try {
             mService.removeByUri(mDatabaseName, request.getNamespace(),
                     new ArrayList<>(request.getUris()), mUserId,
@@ -416,6 +430,7 @@
                             executor.execute(() -> callback.onSystemError(exception.getCause()));
                         }
                     });
+            mIsMutated = true;
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -448,6 +463,7 @@
         Objects.requireNonNull(searchSpec);
         Objects.requireNonNull(executor);
         Objects.requireNonNull(callback);
+        Preconditions.checkState(!mIsClosed, "AppSearchSession has already been closed");
         try {
             mService.removeByQuery(mDatabaseName, queryExpression, searchSpec.getBundle(), mUserId,
                     new IAppSearchResultCallback.Stub() {
@@ -455,8 +471,26 @@
                             executor.execute(() -> callback.accept(result));
                         }
                     });
+            mIsMutated = true;
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
     }
+
+    /**
+     * Closes the SearchSessionImpl to persists all update/delete requests to the disk.
+     *
+     * @hide
+     */
+    // TODO(b/175637134) when unhide it, implement Closeable and remove this method.
+    public void close() {
+        if (mIsMutated && !mIsClosed) {
+            try {
+                mService.persistToDisk(mUserId);
+                mIsClosed = true;
+            } catch (RemoteException e) {
+                Log.e(TAG, "Unable to close the AppSearchSession", e);
+            }
+        }
+    }
 }
diff --git a/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl b/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl
index c1c8047..af8b613 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl
+++ b/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl
@@ -193,6 +193,13 @@
         in IAppSearchResultCallback callback);
 
     /**
+     * Persists all update/delete requests to the disk.
+     *
+     * @param userId Id of the calling user
+     */
+    void persistToDisk(in int userId);
+
+    /**
      * Creates and initializes AppSearchImpl for the calling app.
      *
      * @param userId Id of the calling user
diff --git a/apex/appsearch/framework/java/android/app/appsearch/SearchResults.java b/apex/appsearch/framework/java/android/app/appsearch/SearchResults.java
index 05735cc..0cb0ea4 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/SearchResults.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/SearchResults.java
@@ -24,6 +24,8 @@
 import android.os.RemoteException;
 import android.util.Log;
 
+import com.android.internal.util.Preconditions;
+
 import java.io.Closeable;
 import java.util.List;
 import java.util.Objects;
@@ -45,33 +47,37 @@
 
     private final IAppSearchManager mService;
 
+    // The database name to search over. If null, this will search over all database names.
     @Nullable
     private final String mDatabaseName;
 
-    @UserIdInt
-    private final int mUserId;
-
     private final String mQueryExpression;
 
     private final SearchSpec mSearchSpec;
 
+    @UserIdInt
+    private final int mUserId;
+
     private final Executor mExecutor;
 
     private long mNextPageToken;
 
     private boolean mIsFirstLoad = true;
 
-    SearchResults(@NonNull IAppSearchManager service,
+    private boolean mIsClosed = false;
+
+    SearchResults(
+            @NonNull IAppSearchManager service,
             @Nullable String databaseName,
             @NonNull String queryExpression,
             @NonNull SearchSpec searchSpec,
             @UserIdInt int userId,
             @NonNull @CallbackExecutor Executor executor) {
         mService = Objects.requireNonNull(service);
-        mUserId = userId;
-        mDatabaseName = Objects.requireNonNull(databaseName);
+        mDatabaseName = databaseName;
         mQueryExpression = Objects.requireNonNull(queryExpression);
         mSearchSpec = Objects.requireNonNull(searchSpec);
+        mUserId = userId;
         mExecutor = Objects.requireNonNull(executor);
     }
 
@@ -86,15 +92,19 @@
      * @param callback Callback to receive the pending result of performing this operation.
      */
     public void getNextPage(@NonNull Consumer<AppSearchResult<List<SearchResult>>> callback) {
+        Preconditions.checkState(!mIsClosed, "SearchResults has already been closed");
         try {
             if (mIsFirstLoad) {
                 mIsFirstLoad = false;
                 if (mDatabaseName == null) {
+                    // Global query, there's no one package-database combination to check.
                     mService.globalQuery(mQueryExpression, mSearchSpec.getBundle(), mUserId,
                             wrapCallback(callback));
                 } else {
-                    mService.query(mDatabaseName, mQueryExpression, mSearchSpec.getBundle(),
-                            mUserId, wrapCallback(callback));
+                    // Normal local query, pass in specified database.
+                    mService.query(
+                            mDatabaseName, mQueryExpression, mSearchSpec.getBundle(), mUserId,
+                            wrapCallback(callback));
                 }
             } else {
                 mService.getNextPage(mNextPageToken, mUserId, wrapCallback(callback));
@@ -104,6 +114,27 @@
         }
     }
 
+    @Override
+    public void close() {
+        if (!mIsClosed) {
+            try {
+                mService.invalidateNextPageToken(mNextPageToken, mUserId);
+                mIsClosed = true;
+            } catch (RemoteException e) {
+                Log.e(TAG, "Unable to close the SearchResults", e);
+            }
+        }
+    }
+
+    private IAppSearchResultCallback wrapCallback(
+            @NonNull Consumer<AppSearchResult<List<SearchResult>>> callback) {
+        return new IAppSearchResultCallback.Stub() {
+            public void onResult(AppSearchResult result) {
+                mExecutor.execute(() -> invokeCallback(result, callback));
+            }
+        };
+    }
+
     private void invokeCallback(AppSearchResult result,
             @NonNull Consumer<AppSearchResult<List<SearchResult>>> callback) {
         if (result.isSuccess()) {
@@ -120,23 +151,4 @@
             callback.accept(result);
         }
     }
-    @Override
-    public void close() {
-        mExecutor.execute(() -> {
-            try {
-                mService.invalidateNextPageToken(mNextPageToken, mUserId);
-            } catch (RemoteException e) {
-                Log.d(TAG, "Unable to close the SearchResults", e);
-            }
-        });
-    }
-
-    private IAppSearchResultCallback wrapCallback(
-            @NonNull Consumer<AppSearchResult<List<SearchResult>>> callback) {
-        return new IAppSearchResultCallback.Stub() {
-            public void onResult(AppSearchResult result) {
-                mExecutor.execute(() -> invokeCallback(result, callback));
-            }
-        };
-    }
 }
diff --git a/apex/appsearch/framework/java/external/android/app/appsearch/SearchSpec.java b/apex/appsearch/framework/java/external/android/app/appsearch/SearchSpec.java
index 68e31f0..400b630 100644
--- a/apex/appsearch/framework/java/external/android/app/appsearch/SearchSpec.java
+++ b/apex/appsearch/framework/java/external/android/app/appsearch/SearchSpec.java
@@ -23,6 +23,7 @@
 import android.app.appsearch.exceptions.AppSearchException;
 import android.app.appsearch.exceptions.IllegalSearchSpecException;
 import android.os.Bundle;
+import android.util.ArrayMap;
 
 import com.android.internal.util.Preconditions;
 
@@ -33,6 +34,8 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
+import java.util.Map;
+import java.util.Set;
 
 /**
  * This class represents the specification logic for AppSearch. It can be used to set the type of
@@ -40,6 +43,15 @@
  */
 // TODO(sidchhabra) : AddResultSpec fields for Snippets etc.
 public final class SearchSpec {
+    /**
+     * Schema type to be used in {@link SearchSpec.Builder#addProjectionTypePropertyPath} to apply
+     * property paths to all results, excepting any types that have had their own, specific property
+     * paths set.
+     *
+     * @hide
+     */
+    public static final String PROJECTION_SCHEMA_TYPE_WILDCARD = "*";
+
     static final String TERM_MATCH_TYPE_FIELD = "termMatchType";
     static final String SCHEMA_TYPE_FIELD = "schemaType";
     static final String NAMESPACE_FIELD = "namespace";
@@ -49,6 +61,7 @@
     static final String SNIPPET_COUNT_FIELD = "snippetCount";
     static final String SNIPPET_COUNT_PER_PROPERTY_FIELD = "snippetCountPerProperty";
     static final String MAX_SNIPPET_FIELD = "maxSnippet";
+    static final String PROJECTION_TYPE_PROPERTY_PATHS_FIELD = "projectionTypeFieldMasks";
 
     /** @hide */
     public static final int DEFAULT_NUM_PER_PAGE = 10;
@@ -206,12 +219,35 @@
         return mBundle.getInt(MAX_SNIPPET_FIELD);
     }
 
+    /**
+     * Returns a map from schema type to property paths to be used for projection.
+     *
+     * <p>If the map is empty, then all properties will be retrieved for all results.
+     *
+     * <p>Calling this function repeatedly is inefficient. Prefer to retain the Map returned by this
+     * function, rather than calling it multiple times.
+     *
+     * @hide
+     */
+    @NonNull
+    public Map<String, List<String>> getProjectionTypePropertyPaths() {
+        Bundle typePropertyPathsBundle = mBundle.getBundle(PROJECTION_TYPE_PROPERTY_PATHS_FIELD);
+        Set<String> schemaTypes = typePropertyPathsBundle.keySet();
+        Map<String, List<String>> typePropertyPathsMap = new ArrayMap<>(schemaTypes.size());
+        for (String schemaType : schemaTypes) {
+            typePropertyPathsMap.put(
+                    schemaType, typePropertyPathsBundle.getStringArrayList(schemaType));
+        }
+        return typePropertyPathsMap;
+    }
+
     /** Builder for {@link SearchSpec objects}. */
     public static final class Builder {
 
         private final Bundle mBundle;
         private final ArrayList<String> mSchemaTypes = new ArrayList<>();
         private final ArrayList<String> mNamespaces = new ArrayList<>();
+        private final Bundle mProjectionTypePropertyMasks = new Bundle();
         private boolean mBuilt = false;
 
         /** Creates a new {@link SearchSpec.Builder}. */
@@ -386,6 +422,109 @@
         }
 
         /**
+         * Adds property paths for the specified type to be used for projection. If property paths
+         * are added for a type, then only the properties referred to will be retrieved for results
+         * of that type. If a property path that is specified isn't present in a result, it will be
+         * ignored for that result. Property paths cannot be null.
+         *
+         * <p>If no property paths are added for a particular type, then all properties of results
+         * of that type will be retrieved.
+         *
+         * <p>If property path is added for the {@link SearchSpec#PROJECTION_SCHEMA_TYPE_WILDCARD},
+         * then those property paths will apply to all results, excepting any types that have their
+         * own, specific property paths set.
+         *
+         * <p>Suppose the following document is in the index.
+         *
+         * <pre>{@code
+         * Email: Document {
+         *   sender: Document {
+         *     name: "Mr. Person"
+         *     email: "mrperson123@google.com"
+         *   }
+         *   recipients: [
+         *     Document {
+         *       name: "John Doe"
+         *       email: "johndoe123@google.com"
+         *     }
+         *     Document {
+         *       name: "Jane Doe"
+         *       email: "janedoe123@google.com"
+         *     }
+         *   ]
+         *   subject: "IMPORTANT"
+         *   body: "Limited time offer!"
+         * }
+         * }</pre>
+         *
+         * <p>Then, suppose that a query for "important" is issued with the following projection
+         * type property paths:
+         *
+         * <pre>{@code
+         * {schemaType: "Email", ["subject", "sender.name", "recipients.name"]}
+         * }</pre>
+         *
+         * <p>The above document will be returned as:
+         *
+         * <pre>{@code
+         * Email: Document {
+         *   sender: Document {
+         *     name: "Mr. Body"
+         *   }
+         *   recipients: [
+         *     Document {
+         *       name: "John Doe"
+         *     }
+         *     Document {
+         *       name: "Jane Doe"
+         *     }
+         *   ]
+         *   subject: "IMPORTANT"
+         * }
+         * }</pre>
+         *
+         * @hide
+         */
+        @NonNull
+        public SearchSpec.Builder addProjectionTypePropertyPaths(
+                @NonNull String schemaType, @NonNull String... propertyPaths) {
+            Preconditions.checkNotNull(propertyPaths);
+            return addProjectionTypePropertyPaths(schemaType, Arrays.asList(propertyPaths));
+        }
+
+        /**
+         * Adds property paths for the specified type to be used for projection. If property paths
+         * are added for a type, then only the properties referred to will be retrieved for results
+         * of that type. If a property path that is specified isn't present in a result, it will be
+         * ignored for that result. Property paths cannot be null.
+         *
+         * <p>If no property paths are added for a particular type, then all properties of results
+         * of that type will be retrieved.
+         *
+         * <p>If property path is added for the {@link SearchSpec#PROJECTION_SCHEMA_TYPE_WILDCARD},
+         * then those property paths will apply to all results, excepting any types that have their
+         * own, specific property paths set.
+         *
+         * <p>{@see SearchSpec.Builder#addProjectionTypePropertyPath(String, String...)}
+         *
+         * @hide
+         */
+        @NonNull
+        public SearchSpec.Builder addProjectionTypePropertyPaths(
+                @NonNull String schemaType, @NonNull Collection<String> propertyPaths) {
+            Preconditions.checkState(!mBuilt, "Builder has already been used");
+            Preconditions.checkNotNull(schemaType);
+            Preconditions.checkNotNull(propertyPaths);
+            ArrayList<String> propertyPathsArrayList = new ArrayList<>(propertyPaths.size());
+            for (String propertyPath : propertyPaths) {
+                Preconditions.checkNotNull(propertyPath);
+                propertyPathsArrayList.add(propertyPath);
+            }
+            mProjectionTypePropertyMasks.putStringArrayList(schemaType, propertyPathsArrayList);
+            return this;
+        }
+
+        /**
          * Constructs a new {@link SearchSpec} from the contents of this builder.
          *
          * <p>After calling this method, the builder must no longer be used.
@@ -398,6 +537,7 @@
             }
             mBundle.putStringArrayList(NAMESPACE_FIELD, mNamespaces);
             mBundle.putStringArrayList(SCHEMA_TYPE_FIELD, mSchemaTypes);
+            mBundle.putBundle(PROJECTION_TYPE_PROPERTY_PATHS_FIELD, mProjectionTypePropertyMasks);
             mBuilt = true;
             return new SearchSpec(mBundle);
         }
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
index 8cf1ca0..87c41e5 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
@@ -279,7 +279,7 @@
                 AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId);
                 impl.invalidateNextPageToken(nextPageToken);
             } catch (Throwable t) {
-                Log.d(TAG, "Unable to invalidate the query page token", t);
+                Log.e(TAG, "Unable to invalidate the query page token", t);
             } finally {
                 Binder.restoreCallingIdentity(callingIdentity);
             }
@@ -348,6 +348,21 @@
         }
 
         @Override
+        public void persistToDisk(@UserIdInt int userId) {
+            int callingUid = Binder.getCallingUidOrThrow();
+            int callingUserId = handleIncomingUser(userId, callingUid);
+            final long callingIdentity = Binder.clearCallingIdentity();
+            try {
+                AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId);
+                impl.persistToDisk();
+            } catch (Throwable t) {
+                Log.e(TAG, "Unable to persist the data to disk", t);
+            } finally {
+                Binder.restoreCallingIdentity(callingIdentity);
+            }
+        }
+
+        @Override
         public void initialize(@UserIdInt int userId, @NonNull IAppSearchResultCallback callback) {
             Preconditions.checkNotNull(callback);
             int callingUid = Binder.getCallingUid();
@@ -388,7 +403,7 @@
             try {
                 callback.onResult(result);
             } catch (RemoteException e) {
-                Log.d(TAG, "Unable to send result to the callback", e);
+                Log.e(TAG, "Unable to send result to the callback", e);
             }
         }
 
@@ -398,7 +413,7 @@
             try {
                 callback.onResult(result);
             } catch (RemoteException e) {
-                Log.d(TAG, "Unable to send result to the callback", e);
+                Log.e(TAG, "Unable to send result to the callback", e);
             }
         }
 
@@ -411,7 +426,7 @@
             try {
                 callback.onResult(throwableToFailedResult(throwable));
             } catch (RemoteException e) {
-                Log.d(TAG, "Unable to send result to the callback", e);
+                Log.e(TAG, "Unable to send result to the callback", e);
             }
         }
 
@@ -425,7 +440,7 @@
             try {
                 callback.onSystemError(new ParcelableException(throwable));
             } catch (RemoteException e) {
-                Log.d(TAG, "Unable to send error to the callback", e);
+                Log.e(TAG, "Unable to send error to the callback", e);
             }
         }
     }
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java
index 1d5287b..47a81eb 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java
@@ -46,6 +46,7 @@
 import com.google.android.icing.proto.IcingSearchEngineOptions;
 import com.google.android.icing.proto.InitializeResultProto;
 import com.google.android.icing.proto.OptimizeResultProto;
+import com.google.android.icing.proto.PersistToDiskResultProto;
 import com.google.android.icing.proto.PropertyConfigProto;
 import com.google.android.icing.proto.PropertyProto;
 import com.google.android.icing.proto.PutResultProto;
@@ -631,6 +632,24 @@
     }
 
     /**
+     * Persists all update/delete requests to the disk.
+     *
+     * <p>If the app crashes after a call to PersistToDisk(), Icing would be able to fully recover
+     * all data written up to this point without a costly recovery process.
+     *
+     * <p>If the app crashes before a call to PersistToDisk(), Icing would trigger a costly recovery
+     * process in next initialization. After that, Icing would still be able to recover all written
+     * data.
+     *
+     * @throws AppSearchException
+     */
+    public void persistToDisk() throws AppSearchException {
+        PersistToDiskResultProto persistToDiskResultProto =
+                mIcingSearchEngineLocked.persistToDisk();
+        checkSuccess(persistToDiskResultProto.getStatus());
+    }
+
+    /**
      * Clears documents and schema across all packages and databaseNames.
      *
      * <p>This method also clear all data in {@link VisibilityStore}, an {@link
diff --git a/apex/appsearch/synced_jetpack_changeid.txt b/apex/appsearch/synced_jetpack_changeid.txt
index f9a0bed..2b1ec08 100644
--- a/apex/appsearch/synced_jetpack_changeid.txt
+++ b/apex/appsearch/synced_jetpack_changeid.txt
@@ -1 +1 @@
-I0577839bfddf95a555399df441d317b00c7c7c48
+I596ad1269b4d3a4f26db67f5d970aeaa3bf94a9d
diff --git a/apex/appsearch/testing/java/com/android/server/appsearch/testing/AppSearchSessionShimImpl.java b/apex/appsearch/testing/java/com/android/server/appsearch/testing/AppSearchSessionShimImpl.java
index 1d2382a..b0478d5 100644
--- a/apex/appsearch/testing/java/com/android/server/appsearch/testing/AppSearchSessionShimImpl.java
+++ b/apex/appsearch/testing/java/com/android/server/appsearch/testing/AppSearchSessionShimImpl.java
@@ -58,10 +58,17 @@
     @NonNull
     public static ListenableFuture<AppSearchSessionShim> createSearchSession(
             @NonNull AppSearchManager.SearchContext searchContext) {
+        return createSearchSession(searchContext, Executors.newCachedThreadPool());
+    }
+
+    /**  Creates the SearchSession with given ExecutorService. */
+    @NonNull
+    public static ListenableFuture<AppSearchSessionShim> createSearchSession(
+            @NonNull AppSearchManager.SearchContext searchContext,
+            @NonNull ExecutorService executor) {
         Context context = ApplicationProvider.getApplicationContext();
         AppSearchManager appSearchManager = context.getSystemService(AppSearchManager.class);
         SettableFuture<AppSearchResult<AppSearchSession>> future = SettableFuture.create();
-        ExecutorService executor = Executors.newCachedThreadPool();
         appSearchManager.createSearchSession(searchContext, executor, future::set);
         return Futures.transform(
                 future,
@@ -138,6 +145,11 @@
         return Futures.transformAsync(future, this::transformResult, mExecutor);
     }
 
+    @Override
+    public void close() {
+        mAppSearchSession.close();
+    }
+
     private <T> ListenableFuture<T> transformResult(
             @NonNull AppSearchResult<T> result) throws AppSearchException {
         if (!result.isSuccess()) {
diff --git a/apex/appsearch/testing/java/com/android/server/appsearch/testing/GlobalSearchSessionShimImpl.java b/apex/appsearch/testing/java/com/android/server/appsearch/testing/GlobalSearchSessionShimImpl.java
index 67af6b1..9e22bf6 100644
--- a/apex/appsearch/testing/java/com/android/server/appsearch/testing/GlobalSearchSessionShimImpl.java
+++ b/apex/appsearch/testing/java/com/android/server/appsearch/testing/GlobalSearchSessionShimImpl.java
@@ -20,6 +20,7 @@
 import android.app.appsearch.AppSearchManager;
 import android.app.appsearch.AppSearchResult;
 import android.app.appsearch.GlobalSearchSession;
+import android.app.appsearch.GlobalSearchSessionShim;
 import android.app.appsearch.SearchResults;
 import android.app.appsearch.SearchResultsShim;
 import android.app.appsearch.SearchSpec;
@@ -40,7 +41,7 @@
  * a consistent interface.
  * @hide
  */
-public class GlobalSearchSessionShimImpl {
+public class GlobalSearchSessionShimImpl implements GlobalSearchSessionShim {
     private final GlobalSearchSession mGlobalSearchSession;
     private final ExecutorService mExecutor;
 
@@ -64,6 +65,7 @@
     }
 
     @NonNull
+    @Override
     public SearchResultsShim query(
             @NonNull String queryExpression, @NonNull SearchSpec searchSpec) {
         SearchResults searchResults =
diff --git a/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchSessionShim.java b/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchSessionShim.java
index 8c42b2d..8383df4 100644
--- a/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchSessionShim.java
+++ b/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchSessionShim.java
@@ -208,4 +208,13 @@
     @NonNull
     ListenableFuture<Void> removeByQuery(
             @NonNull String queryExpression, @NonNull SearchSpec searchSpec);
+
+    /**
+     * Closes the SearchSessionImpl to persists all update/delete requests to the disk.
+     *
+     * @hide
+     */
+
+    // TODO(b/175637134) when unhide it, extends Closeable and remove this method.
+    void close();
 }
diff --git a/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchTestUtils.java b/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchTestUtils.java
index 9653def..459fd151 100644
--- a/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchTestUtils.java
+++ b/apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchTestUtils.java
@@ -17,6 +17,7 @@
 package com.android.server.appsearch.testing;
 
 import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
 
 import android.app.appsearch.AppSearchBatchResult;
 import android.app.appsearch.AppSearchSessionShim;
@@ -25,8 +26,6 @@
 import android.app.appsearch.SearchResult;
 import android.app.appsearch.SearchResultsShim;
 
-import junit.framework.AssertionFailedError;
-
 import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.Future;
@@ -36,9 +35,9 @@
     public static <K, V> AppSearchBatchResult<K, V> checkIsBatchResultSuccess(
             Future<AppSearchBatchResult<K, V>> future) throws Exception {
         AppSearchBatchResult<K, V> result = future.get();
-        if (!result.isSuccess()) {
-            throw new AssertionFailedError("AppSearchBatchResult not successful: " + result);
-        }
+        assertWithMessage("AppSearchBatchResult not successful: " + result)
+                .that(result.isSuccess())
+                .isTrue();
         return result;
     }
 
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index 1910553..df0a0ee 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -1502,11 +1502,14 @@
         mControllers.add(mBatteryController);
         mStorageController = new StorageController(this);
         mControllers.add(mStorageController);
-        mControllers.add(new BackgroundJobsController(this));
+        final BackgroundJobsController backgroundJobsController =
+                new BackgroundJobsController(this);
+        mControllers.add(backgroundJobsController);
         mControllers.add(new ContentObserverController(this));
         mDeviceIdleJobsController = new DeviceIdleJobsController(this);
         mControllers.add(mDeviceIdleJobsController);
-        mQuotaController = new QuotaController(this);
+        mQuotaController =
+                new QuotaController(this, backgroundJobsController, connectivityController);
         mControllers.add(mQuotaController);
         mControllers.add(new ComponentController(this));
 
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java
index f647db9..04d6947 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/BackgroundJobsController.java
@@ -78,6 +78,11 @@
     }
 
     @Override
+    public void evaluateStateLocked(JobStatus jobStatus) {
+        updateSingleJobRestrictionLocked(jobStatus, UNKNOWN);
+    }
+
+    @Override
     public void dumpControllerStateLocked(final IndentingPrintWriter pw,
             final Predicate<JobStatus> predicate) {
         mAppStateTracker.dump(pw);
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
index cd71247..51525e0 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
@@ -1151,6 +1151,11 @@
         if (setConstraintSatisfied(CONSTRAINT_WITHIN_EXPEDITED_QUOTA, state)) {
             // The constraint was changed. Update the ready flag.
             mReadyWithinExpeditedQuota = state;
+            // DeviceIdleJobsController currently only tracks jobs with the WILL_BE_FOREGROUND flag.
+            // Making it also track requested-expedited jobs would add unnecessary hops since the
+            // controller would then defer to canRunInDoze. Avoid the hops and just update
+            // mReadyNotDozing directly.
+            mReadyNotDozing = isConstraintSatisfied(CONSTRAINT_DEVICE_NOT_DOZING) || canRunInDoze();
             return true;
         }
         return false;
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
index 7b87dfb..2d55aa5 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
@@ -347,6 +347,9 @@
     private final QcHandler mHandler;
     private final QcConstants mQcConstants;
 
+    private final BackgroundJobsController mBackgroundJobsController;
+    private final ConnectivityController mConnectivityController;
+
     /** How much time each app will have to run jobs within their standby bucket window. */
     private long mAllowedTimePerPeriodMs = QcConstants.DEFAULT_ALLOWED_TIME_PER_PERIOD_MS;
 
@@ -552,7 +555,9 @@
      */
     private static final int MSG_PROCESS_USAGE_EVENT = 5;
 
-    public QuotaController(JobSchedulerService service) {
+    public QuotaController(@NonNull JobSchedulerService service,
+            @NonNull BackgroundJobsController backgroundJobsController,
+            @NonNull ConnectivityController connectivityController) {
         super(service);
         mHandler = new QcHandler(mContext.getMainLooper());
         mChargeTracker = new ChargingTracker();
@@ -560,6 +565,8 @@
         mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
         mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
         mQcConstants = new QcConstants();
+        mBackgroundJobsController = backgroundJobsController;
+        mConnectivityController = connectivityController;
 
         final IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
         mContext.registerReceiverAsUser(mPackageAddedReceiver, UserHandle.ALL, filter, null, null);
@@ -596,7 +603,7 @@
         final boolean outOfEJQuota;
         if (jobStatus.isRequestedExpeditedJob()) {
             final boolean isWithinEJQuota = isWithinEJQuotaLocked(jobStatus);
-            jobStatus.setExpeditedJobQuotaConstraintSatisfied(isWithinEJQuota);
+            setExpeditedConstraintSatisfied(jobStatus, isWithinEJQuota);
             outOfEJQuota = !isWithinEJQuota;
         } else {
             outOfEJQuota = false;
@@ -1473,7 +1480,7 @@
 
             if (js.isRequestedExpeditedJob()) {
                 boolean isWithinEJQuota = isWithinEJQuotaLocked(js);
-                changed |= js.setExpeditedJobQuotaConstraintSatisfied(isWithinEJQuota);
+                changed |= setExpeditedConstraintSatisfied(js, isWithinEJQuota);
                 outOfEJQuota |= !isWithinEJQuota;
             }
         }
@@ -1499,7 +1506,7 @@
             final boolean outOfEJQuota;
             if (jobStatus.isRequestedExpeditedJob()) {
                 final boolean isWithinEJQuota = isWithinEJQuotaLocked(jobStatus);
-                wasJobChanged |= jobStatus.setExpeditedJobQuotaConstraintSatisfied(isWithinEJQuota);
+                wasJobChanged |= setExpeditedConstraintSatisfied(jobStatus, isWithinEJQuota);
                 outOfEJQuota = !isWithinEJQuota;
             } else {
                 outOfEJQuota = false;
@@ -1650,6 +1657,23 @@
         return jobStatus.setQuotaConstraintSatisfied(isWithinQuota);
     }
 
+    /**
+     * If the satisfaction changes, this will tell connectivity & background jobs controller to
+     * also re-evaluate their state.
+     */
+    private boolean setExpeditedConstraintSatisfied(@NonNull JobStatus jobStatus,
+            boolean isWithinQuota) {
+        if (jobStatus.setExpeditedJobQuotaConstraintSatisfied(isWithinQuota)) {
+            mBackgroundJobsController.evaluateStateLocked(jobStatus);
+            mConnectivityController.evaluateStateLocked(jobStatus);
+            if (isWithinQuota && jobStatus.isReady()) {
+                mStateChangedListener.onRunJobNow(jobStatus);
+            }
+            return true;
+        }
+        return false;
+    }
+
     private final class ChargingTracker extends BroadcastReceiver {
         /**
          * Track whether we're charging. This has a slightly different definition than that of
diff --git a/core/api/current.txt b/core/api/current.txt
index 326bb73..4ad5e49 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -8977,6 +8977,8 @@
     method public int getConnectionState(android.bluetooth.BluetoothDevice);
     method public java.util.List<android.bluetooth.BluetoothDevice> getDevicesMatchingConnectionStates(int[]);
     method public boolean isAudioConnected(android.bluetooth.BluetoothDevice);
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public boolean isNoiseReductionSupported(@NonNull android.bluetooth.BluetoothDevice);
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public boolean isVoiceRecognitionSupported(@NonNull android.bluetooth.BluetoothDevice);
     method public boolean sendVendorSpecificResultCode(android.bluetooth.BluetoothDevice, String, String);
     method public boolean startVoiceRecognition(android.bluetooth.BluetoothDevice);
     method public boolean stopVoiceRecognition(android.bluetooth.BluetoothDevice);
@@ -11641,6 +11643,14 @@
     method public final int compare(android.content.pm.ApplicationInfo, android.content.pm.ApplicationInfo);
   }
 
+  public final class Attribution implements android.os.Parcelable {
+    method public int describeContents();
+    method @IdRes public int getLabel();
+    method @NonNull public String getTag();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.Attribution> CREATOR;
+  }
+
   public final class ChangedPackages implements android.os.Parcelable {
     ctor public ChangedPackages(int, @NonNull java.util.List<java.lang.String>);
     method public int describeContents();
@@ -11890,6 +11900,7 @@
     field public static final int REQUESTED_PERMISSION_GRANTED = 2; // 0x2
     field public android.content.pm.ActivityInfo[] activities;
     field public android.content.pm.ApplicationInfo applicationInfo;
+    field @Nullable public android.content.pm.Attribution[] attributions;
     field public int baseRevisionCode;
     field public android.content.pm.ConfigurationInfo[] configPreferences;
     field public android.content.pm.FeatureGroupInfo[] featureGroups;
@@ -12338,6 +12349,7 @@
     field public static final int FLAG_PERMISSION_WHITELIST_SYSTEM = 1; // 0x1
     field public static final int FLAG_PERMISSION_WHITELIST_UPGRADE = 4; // 0x4
     field public static final int GET_ACTIVITIES = 1; // 0x1
+    field public static final int GET_ATTRIBUTIONS = -2147483648; // 0x80000000
     field public static final int GET_CONFIGURATIONS = 16384; // 0x4000
     field @Deprecated public static final int GET_DISABLED_COMPONENTS = 512; // 0x200
     field @Deprecated public static final int GET_DISABLED_UNTIL_USED_COMPONENTS = 32768; // 0x8000
@@ -16244,7 +16256,7 @@
     method public static android.graphics.drawable.Icon createWithResource(android.content.Context, @DrawableRes int);
     method public static android.graphics.drawable.Icon createWithResource(String, @DrawableRes int);
     method public int describeContents();
-    method @IdRes public int getResId();
+    method @DrawableRes public int getResId();
     method @NonNull public String getResPackage();
     method public int getType();
     method @NonNull public android.net.Uri getUri();
@@ -18930,7 +18942,9 @@
   }
 
   public static class GnssAntennaInfo.Builder {
-    ctor public GnssAntennaInfo.Builder();
+    ctor @Deprecated public GnssAntennaInfo.Builder();
+    ctor public GnssAntennaInfo.Builder(double, @NonNull android.location.GnssAntennaInfo.PhaseCenterOffset);
+    ctor public GnssAntennaInfo.Builder(@NonNull android.location.GnssAntennaInfo);
     method @NonNull public android.location.GnssAntennaInfo build();
     method @NonNull public android.location.GnssAntennaInfo.Builder setCarrierFrequencyMHz(@FloatRange(from=0.0f) double);
     method @NonNull public android.location.GnssAntennaInfo.Builder setPhaseCenterOffset(@NonNull android.location.GnssAntennaInfo.PhaseCenterOffset);
@@ -18938,8 +18952,8 @@
     method @NonNull public android.location.GnssAntennaInfo.Builder setSignalGainCorrections(@Nullable android.location.GnssAntennaInfo.SphericalCorrections);
   }
 
-  public static interface GnssAntennaInfo.Listener {
-    method public void onGnssAntennaInfoReceived(@NonNull java.util.List<android.location.GnssAntennaInfo>);
+  @Deprecated public static interface GnssAntennaInfo.Listener {
+    method @Deprecated public void onGnssAntennaInfoReceived(@NonNull java.util.List<android.location.GnssAntennaInfo>);
   }
 
   public static final class GnssAntennaInfo.PhaseCenterOffset implements android.os.Parcelable {
@@ -18966,8 +18980,23 @@
     field @NonNull public static final android.os.Parcelable.Creator<android.location.GnssAntennaInfo.SphericalCorrections> CREATOR;
   }
 
-  public final class GnssCapabilities {
-    method public boolean hasGnssAntennaInfo();
+  public final class GnssCapabilities implements android.os.Parcelable {
+    method public int describeContents();
+    method public boolean hasAntennaInfo();
+    method @Deprecated public boolean hasGnssAntennaInfo();
+    method public boolean hasMeasurements();
+    method public boolean hasNavigationMessages();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.location.GnssCapabilities> CREATOR;
+  }
+
+  public static final class GnssCapabilities.Builder {
+    ctor public GnssCapabilities.Builder();
+    ctor public GnssCapabilities.Builder(@NonNull android.location.GnssCapabilities);
+    method @NonNull public android.location.GnssCapabilities build();
+    method @NonNull public android.location.GnssCapabilities.Builder setHasAntennaInfo(boolean);
+    method @NonNull public android.location.GnssCapabilities.Builder setHasMeasurements(boolean);
+    method @NonNull public android.location.GnssCapabilities.Builder setHasNavigationMessages(boolean);
   }
 
   public final class GnssClock implements android.os.Parcelable {
@@ -19097,11 +19126,11 @@
   public abstract static class GnssMeasurementsEvent.Callback {
     ctor public GnssMeasurementsEvent.Callback();
     method public void onGnssMeasurementsReceived(android.location.GnssMeasurementsEvent);
-    method public void onStatusChanged(int);
-    field public static final int STATUS_LOCATION_DISABLED = 2; // 0x2
-    field public static final int STATUS_NOT_ALLOWED = 3; // 0x3
-    field public static final int STATUS_NOT_SUPPORTED = 0; // 0x0
-    field public static final int STATUS_READY = 1; // 0x1
+    method @Deprecated public void onStatusChanged(int);
+    field @Deprecated public static final int STATUS_LOCATION_DISABLED = 2; // 0x2
+    field @Deprecated public static final int STATUS_NOT_ALLOWED = 3; // 0x3
+    field @Deprecated public static final int STATUS_NOT_SUPPORTED = 0; // 0x0
+    field @Deprecated public static final int STATUS_READY = 1; // 0x1
   }
 
   public final class GnssNavigationMessage implements android.os.Parcelable {
@@ -19137,10 +19166,10 @@
   public abstract static class GnssNavigationMessage.Callback {
     ctor public GnssNavigationMessage.Callback();
     method public void onGnssNavigationMessageReceived(android.location.GnssNavigationMessage);
-    method public void onStatusChanged(int);
-    field public static final int STATUS_LOCATION_DISABLED = 2; // 0x2
-    field public static final int STATUS_NOT_SUPPORTED = 0; // 0x0
-    field public static final int STATUS_READY = 1; // 0x1
+    method @Deprecated public void onStatusChanged(int);
+    field @Deprecated public static final int STATUS_LOCATION_DISABLED = 2; // 0x2
+    field @Deprecated public static final int STATUS_NOT_SUPPORTED = 0; // 0x0
+    field @Deprecated public static final int STATUS_READY = 1; // 0x1
   }
 
   public final class GnssStatus implements android.os.Parcelable {
@@ -19298,17 +19327,20 @@
     method @Nullable public String getBestProvider(@NonNull android.location.Criteria, boolean);
     method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void getCurrentLocation(@NonNull String, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.location.Location>);
     method @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public void getCurrentLocation(@NonNull String, @NonNull android.location.LocationRequest, @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.location.Location>);
+    method @Nullable public java.util.List<android.location.GnssAntennaInfo> getGnssAntennaInfos();
     method @NonNull public android.location.GnssCapabilities getGnssCapabilities();
     method @Nullable public String getGnssHardwareModelName();
     method public int getGnssYearOfHardware();
     method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public android.location.GpsStatus getGpsStatus(@Nullable android.location.GpsStatus);
     method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.ACCESS_COARSE_LOCATION, android.Manifest.permission.ACCESS_FINE_LOCATION}) public android.location.Location getLastKnownLocation(@NonNull String);
-    method @Nullable public android.location.LocationProvider getProvider(@NonNull String);
+    method @Deprecated @Nullable public android.location.LocationProvider getProvider(@NonNull String);
+    method @Nullable public android.location.ProviderProperties getProviderProperties(@NonNull String);
     method @NonNull public java.util.List<java.lang.String> getProviders(boolean);
     method @NonNull public java.util.List<java.lang.String> getProviders(@NonNull android.location.Criteria, boolean);
+    method public boolean hasProvider(@NonNull String);
     method public boolean isLocationEnabled();
     method public boolean isProviderEnabled(@NonNull String);
-    method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public boolean registerAntennaInfoListener(@NonNull java.util.concurrent.Executor, @NonNull android.location.GnssAntennaInfo.Listener);
+    method @Deprecated public boolean registerAntennaInfoListener(@NonNull java.util.concurrent.Executor, @NonNull android.location.GnssAntennaInfo.Listener);
     method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public boolean registerGnssMeasurementsCallback(@NonNull android.location.GnssMeasurementsEvent.Callback);
     method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public boolean registerGnssMeasurementsCallback(@NonNull android.location.GnssMeasurementsEvent.Callback, @Nullable android.os.Handler);
     method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public boolean registerGnssMeasurementsCallback(@NonNull java.util.concurrent.Executor, @NonNull android.location.GnssMeasurementsEvent.Callback);
@@ -19345,10 +19377,14 @@
     method public void setTestProviderEnabled(@NonNull String, boolean);
     method public void setTestProviderLocation(@NonNull String, @NonNull android.location.Location);
     method @Deprecated public void setTestProviderStatus(@NonNull String, int, @Nullable android.os.Bundle, long);
-    method public void unregisterAntennaInfoListener(@NonNull android.location.GnssAntennaInfo.Listener);
+    method @Deprecated public void unregisterAntennaInfoListener(@NonNull android.location.GnssAntennaInfo.Listener);
     method public void unregisterGnssMeasurementsCallback(@NonNull android.location.GnssMeasurementsEvent.Callback);
     method public void unregisterGnssNavigationMessageCallback(@NonNull android.location.GnssNavigationMessage.Callback);
     method public void unregisterGnssStatusCallback(@NonNull android.location.GnssStatus.Callback);
+    field public static final String ACTION_GNSS_ANTENNA_INFOS_CHANGED = "android.location.action.GNSS_ANTENNA_INFOS_CHANGED";
+    field public static final String ACTION_GNSS_CAPABILITIES_CHANGED = "android.location.action.GNSS_CAPABILITIES_CHANGED";
+    field public static final String EXTRA_GNSS_ANTENNA_INFOS = "android.location.extra.GNSS_ANTENNA_INFOS";
+    field public static final String EXTRA_GNSS_CAPABILITIES = "android.location.extra.GNSS_CAPABILITIES";
     field public static final String EXTRA_LOCATION_ENABLED = "android.location.extra.LOCATION_ENABLED";
     field public static final String EXTRA_PROVIDER_ENABLED = "android.location.extra.PROVIDER_ENABLED";
     field public static final String EXTRA_PROVIDER_NAME = "android.location.extra.PROVIDER_NAME";
@@ -19366,18 +19402,18 @@
     field public static final String PROVIDERS_CHANGED_ACTION = "android.location.PROVIDERS_CHANGED";
   }
 
-  public class LocationProvider {
-    method public int getAccuracy();
-    method public String getName();
-    method public int getPowerRequirement();
-    method public boolean hasMonetaryCost();
-    method public boolean meetsCriteria(android.location.Criteria);
-    method public boolean requiresCell();
-    method public boolean requiresNetwork();
-    method public boolean requiresSatellite();
-    method public boolean supportsAltitude();
-    method public boolean supportsBearing();
-    method public boolean supportsSpeed();
+  @Deprecated public class LocationProvider {
+    method @Deprecated public int getAccuracy();
+    method @Deprecated public String getName();
+    method @Deprecated public int getPowerRequirement();
+    method @Deprecated public boolean hasMonetaryCost();
+    method @Deprecated public boolean meetsCriteria(android.location.Criteria);
+    method @Deprecated public boolean requiresCell();
+    method @Deprecated public boolean requiresNetwork();
+    method @Deprecated public boolean requiresSatellite();
+    method @Deprecated public boolean supportsAltitude();
+    method @Deprecated public boolean supportsBearing();
+    method @Deprecated public boolean supportsSpeed();
     field @Deprecated public static final int AVAILABLE = 2; // 0x2
     field @Deprecated public static final int OUT_OF_SERVICE = 0; // 0x0
     field @Deprecated public static final int TEMPORARILY_UNAVAILABLE = 1; // 0x1
@@ -19430,6 +19466,26 @@
     method public void onNmeaMessage(String, long);
   }
 
+  public final class ProviderProperties implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getAccuracy();
+    method public int getPowerUsage();
+    method public boolean hasAltitudeSupport();
+    method public boolean hasBearingSupport();
+    method public boolean hasCellRequirement();
+    method public boolean hasMonetaryCost();
+    method public boolean hasNetworkRequirement();
+    method public boolean hasSatelliteRequirement();
+    method public boolean hasSpeedSupport();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field public static final int ACCURACY_COARSE = 2; // 0x2
+    field public static final int ACCURACY_FINE = 1; // 0x1
+    field @NonNull public static final android.os.Parcelable.Creator<android.location.ProviderProperties> CREATOR;
+    field public static final int POWER_USAGE_HIGH = 3; // 0x3
+    field public static final int POWER_USAGE_LOW = 1; // 0x1
+    field public static final int POWER_USAGE_MEDIUM = 2; // 0x2
+  }
+
   public abstract class SettingInjectorService extends android.app.Service {
     ctor public SettingInjectorService(String);
     method public final android.os.IBinder onBind(android.content.Intent);
@@ -19914,6 +19970,7 @@
   public final class AudioPlaybackConfiguration implements android.os.Parcelable {
     method public int describeContents();
     method public android.media.AudioAttributes getAudioAttributes();
+    method @Nullable public android.media.AudioDeviceInfo getAudioDeviceInfo();
     method public void writeToParcel(android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.media.AudioPlaybackConfiguration> CREATOR;
   }
@@ -21865,6 +21922,8 @@
     field public static final int METADATA_KEY_VIDEO_ROTATION = 24; // 0x18
     field public static final int METADATA_KEY_VIDEO_WIDTH = 18; // 0x12
     field public static final int METADATA_KEY_WRITER = 11; // 0xb
+    field public static final int METADATA_KEY_XMP_LENGTH = 42; // 0x2a
+    field public static final int METADATA_KEY_XMP_OFFSET = 41; // 0x29
     field public static final int METADATA_KEY_YEAR = 8; // 0x8
     field public static final int OPTION_CLOSEST = 3; // 0x3
     field public static final int OPTION_CLOSEST_SYNC = 2; // 0x2
@@ -24227,7 +24286,9 @@
     field public static final String COLUMN_APP_LINK_INTENT_URI = "app_link_intent_uri";
     field public static final String COLUMN_APP_LINK_POSTER_ART_URI = "app_link_poster_art_uri";
     field public static final String COLUMN_APP_LINK_TEXT = "app_link_text";
+    field public static final String COLUMN_BROADCAST_GENRE = "broadcast_genre";
     field public static final String COLUMN_BROWSABLE = "browsable";
+    field public static final String COLUMN_CHANNEL_LIST_ID = "channel_list_id";
     field public static final String COLUMN_DESCRIPTION = "description";
     field public static final String COLUMN_DISPLAY_NAME = "display_name";
     field public static final String COLUMN_DISPLAY_NUMBER = "display_number";
@@ -24242,6 +24303,8 @@
     field public static final String COLUMN_LOCKED = "locked";
     field public static final String COLUMN_NETWORK_AFFILIATION = "network_affiliation";
     field public static final String COLUMN_ORIGINAL_NETWORK_ID = "original_network_id";
+    field public static final String COLUMN_REMOTE_CONTROL_KEY_PRESET_NUMBER = "remote_control_key_preset_number";
+    field public static final String COLUMN_SCRAMBLED = "scrambled";
     field public static final String COLUMN_SEARCHABLE = "searchable";
     field public static final String COLUMN_SERVICE_ID = "service_id";
     field public static final String COLUMN_SERVICE_TYPE = "service_type";
@@ -24250,6 +24313,7 @@
     field public static final String COLUMN_TYPE = "type";
     field public static final String COLUMN_VERSION_NUMBER = "version_number";
     field public static final String COLUMN_VIDEO_FORMAT = "video_format";
+    field public static final String COLUMN_VIDEO_RESOLUTION = "video_resolution";
     field public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/channel";
     field public static final String CONTENT_TYPE = "vnd.android.cursor.dir/channel";
     field public static final android.net.Uri CONTENT_URI;
@@ -45224,7 +45288,7 @@
     field public static final java.util.regex.Pattern DOMAIN_NAME;
     field public static final java.util.regex.Pattern EMAIL_ADDRESS;
     field @Deprecated public static final String GOOD_IRI_CHAR = "a-zA-Z0-9\u00a0-\ud7ff\uf900-\ufdcf\ufdf0-\uffef";
-    field public static final java.util.regex.Pattern IP_ADDRESS;
+    field @Deprecated public static final java.util.regex.Pattern IP_ADDRESS;
     field public static final java.util.regex.Pattern PHONE;
     field @Deprecated public static final java.util.regex.Pattern TOP_LEVEL_DOMAIN;
     field @Deprecated public static final String TOP_LEVEL_DOMAIN_STR = "((aero|arpa|asia|a[cdefgilmnoqrstuwxz])|(biz|b[abdefghijmnorstvwyz])|(cat|com|coop|c[acdfghiklmnoruvxyz])|d[ejkmoz]|(edu|e[cegrstu])|f[ijkmor]|(gov|g[abdefghilmnpqrstuwy])|h[kmnrtu]|(info|int|i[delmnoqrst])|(jobs|j[emop])|k[eghimnprwyz]|l[abcikrstuvy]|(mil|mobi|museum|m[acdeghklmnopqrstuvwxyz])|(name|net|n[acefgilopruz])|(org|om)|(pro|p[aefghklmnrstwy])|qa|r[eosuw]|s[abcdeghijklmnortuvyz]|(tel|travel|t[cdfghjklmnoprtvwz])|u[agksyz]|v[aceginu]|w[fs]|(\u03b4\u03bf\u03ba\u03b9\u03bc\u03ae|\u0438\u0441\u043f\u044b\u0442\u0430\u043d\u0438\u0435|\u0440\u0444|\u0441\u0440\u0431|\u05d8\u05e2\u05e1\u05d8|\u0622\u0632\u0645\u0627\u06cc\u0634\u06cc|\u0625\u062e\u062a\u0628\u0627\u0631|\u0627\u0644\u0627\u0631\u062f\u0646|\u0627\u0644\u062c\u0632\u0627\u0626\u0631|\u0627\u0644\u0633\u0639\u0648\u062f\u064a\u0629|\u0627\u0644\u0645\u063a\u0631\u0628|\u0627\u0645\u0627\u0631\u0627\u062a|\u0628\u06be\u0627\u0631\u062a|\u062a\u0648\u0646\u0633|\u0633\u0648\u0631\u064a\u0629|\u0641\u0644\u0633\u0637\u064a\u0646|\u0642\u0637\u0631|\u0645\u0635\u0631|\u092a\u0930\u0940\u0915\u094d\u0937\u093e|\u092d\u093e\u0930\u0924|\u09ad\u09be\u09b0\u09a4|\u0a2d\u0a3e\u0a30\u0a24|\u0aad\u0abe\u0ab0\u0aa4|\u0b87\u0ba8\u0bcd\u0ba4\u0bbf\u0baf\u0bbe|\u0b87\u0bb2\u0b99\u0bcd\u0b95\u0bc8|\u0b9a\u0bbf\u0b99\u0bcd\u0b95\u0baa\u0bcd\u0baa\u0bc2\u0bb0\u0bcd|\u0baa\u0bb0\u0bbf\u0b9f\u0bcd\u0b9a\u0bc8|\u0c2d\u0c3e\u0c30\u0c24\u0c4d|\u0dbd\u0d82\u0d9a\u0dcf|\u0e44\u0e17\u0e22|\u30c6\u30b9\u30c8|\u4e2d\u56fd|\u4e2d\u570b|\u53f0\u6e7e|\u53f0\u7063|\u65b0\u52a0\u5761|\u6d4b\u8bd5|\u6e2c\u8a66|\u9999\u6e2f|\ud14c\uc2a4\ud2b8|\ud55c\uad6d|xn\\-\\-0zwm56d|xn\\-\\-11b5bs3a9aj6g|xn\\-\\-3e0b707e|xn\\-\\-45brj9c|xn\\-\\-80akhbyknj4f|xn\\-\\-90a3ac|xn\\-\\-9t4b11yi5a|xn\\-\\-clchc0ea0b2g2a9gcd|xn\\-\\-deba0ad|xn\\-\\-fiqs8s|xn\\-\\-fiqz9s|xn\\-\\-fpcrj9c3d|xn\\-\\-fzc2c9e2c|xn\\-\\-g6w251d|xn\\-\\-gecrj9c|xn\\-\\-h2brj9c|xn\\-\\-hgbk6aj7f53bba|xn\\-\\-hlcj6aya9esc7a|xn\\-\\-j6w193g|xn\\-\\-jxalpdlp|xn\\-\\-kgbechtv|xn\\-\\-kprw13d|xn\\-\\-kpry57d|xn\\-\\-lgbbat1ad8j|xn\\-\\-mgbaam7a8h|xn\\-\\-mgbayh7gpa|xn\\-\\-mgbbh1a71e|xn\\-\\-mgbc0a9azcg|xn\\-\\-mgberp4a5d4ar|xn\\-\\-o3cw4h|xn\\-\\-ogbpf8fl|xn\\-\\-p1ai|xn\\-\\-pgbs0dh|xn\\-\\-s9brj9c|xn\\-\\-wgbh1c|xn\\-\\-wgbl6a|xn\\-\\-xkc2al3hye2a|xn\\-\\-xkc2dl3a5ee0h|xn\\-\\-yfro4i67o|xn\\-\\-ygbi2ammx|xn\\-\\-zckzah|xxx)|y[et]|z[amw])";
@@ -47911,7 +47975,6 @@
     field public static final int AUTOFILL_TYPE_DATE = 4; // 0x4
     field public static final int AUTOFILL_TYPE_LIST = 3; // 0x3
     field public static final int AUTOFILL_TYPE_NONE = 0; // 0x0
-    field public static final int AUTOFILL_TYPE_RICH_CONTENT = 5; // 0x5
     field public static final int AUTOFILL_TYPE_TEXT = 1; // 0x1
     field public static final int AUTOFILL_TYPE_TOGGLE = 2; // 0x2
     field public static final int DRAG_FLAG_GLOBAL = 256; // 0x100
@@ -50184,17 +50247,14 @@
     method public int describeContents();
     method public static android.view.autofill.AutofillValue forDate(long);
     method public static android.view.autofill.AutofillValue forList(int);
-    method @NonNull public static android.view.autofill.AutofillValue forRichContent(@NonNull android.content.ClipData);
     method public static android.view.autofill.AutofillValue forText(@Nullable CharSequence);
     method public static android.view.autofill.AutofillValue forToggle(boolean);
     method public long getDateValue();
     method public int getListValue();
-    method @NonNull public android.content.ClipData getRichContentValue();
     method @NonNull public CharSequence getTextValue();
     method public boolean getToggleValue();
     method public boolean isDate();
     method public boolean isList();
-    method public boolean isRichContent();
     method public boolean isText();
     method public boolean isToggle();
     method public void writeToParcel(android.os.Parcel, int);
@@ -50404,6 +50464,7 @@
     method public int describeContents();
     method public void dump(android.util.Printer, String);
     method @Nullable public CharSequence getInitialSelectedText(int);
+    method @Nullable public android.view.inputmethod.SurroundingText getInitialSurroundingText(@IntRange(from=0) int, @IntRange(from=0) int, int);
     method @Nullable public CharSequence getInitialTextAfterCursor(int, int);
     method @Nullable public CharSequence getInitialTextBeforeCursor(int, int);
     method public final void makeCompatible(int);
@@ -50567,6 +50628,7 @@
     method public boolean sendKeyEvent(android.view.KeyEvent);
     method public boolean setComposingRegion(int, int);
     method public boolean setComposingText(CharSequence, int);
+    method public default boolean setImeTemporarilyConsumesInput(boolean);
     method public boolean setSelection(int, int);
     field public static final int CURSOR_UPDATE_IMMEDIATE = 1; // 0x1
     field public static final int CURSOR_UPDATE_MONITOR = 2; // 0x2
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index d6b7b08..ef6c8b7 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -80,8 +80,6 @@
 
   public class MediaMetadataRetriever implements java.lang.AutoCloseable {
     field public static final int METADATA_KEY_VIDEO_CODEC_MIME_TYPE = 40; // 0x28
-    field public static final int METADATA_KEY_XMP_LENGTH = 42; // 0x2a
-    field public static final int METADATA_KEY_XMP_OFFSET = 41; // 0x29
   }
 
   public class MediaServiceManager {
@@ -111,8 +109,11 @@
 
   public final class MediaSessionManager {
     method public void addOnActiveSessionsChangedListener(@NonNull android.media.session.MediaSessionManager.OnActiveSessionsChangedListener, @Nullable android.content.ComponentName, int, @Nullable android.os.Handler);
+    method public void dispatchMediaKeyEvent(@NonNull android.view.KeyEvent);
+    method public void dispatchMediaKeyEvent(@NonNull android.view.KeyEvent, boolean);
     method public void dispatchMediaKeyEventAsSystemService(@NonNull android.view.KeyEvent);
     method public boolean dispatchMediaKeyEventToSessionAsSystemService(@NonNull android.view.KeyEvent, @NonNull android.media.session.MediaSession.Token);
+    method public void dispatchVolumeKeyEvent(@NonNull android.view.KeyEvent, int, boolean);
     method public void dispatchVolumeKeyEventAsSystemService(@NonNull android.view.KeyEvent, int);
     method public void dispatchVolumeKeyEventToSessionAsSystemService(@NonNull android.view.KeyEvent, @NonNull android.media.session.MediaSession.Token);
     method @NonNull public java.util.List<android.media.session.MediaController> getActiveSessionsForUser(@Nullable android.content.ComponentName, int);
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 576963d..dfed8b1 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -51,6 +51,7 @@
     field public static final String BIND_TELEPHONY_DATA_SERVICE = "android.permission.BIND_TELEPHONY_DATA_SERVICE";
     field public static final String BIND_TELEPHONY_NETWORK_SERVICE = "android.permission.BIND_TELEPHONY_NETWORK_SERVICE";
     field public static final String BIND_TEXTCLASSIFIER_SERVICE = "android.permission.BIND_TEXTCLASSIFIER_SERVICE";
+    field public static final String BIND_TIME_ZONE_PROVIDER_SERVICE = "android.permission.BIND_TIME_ZONE_PROVIDER_SERVICE";
     field public static final String BIND_TRUST_AGENT = "android.permission.BIND_TRUST_AGENT";
     field public static final String BIND_TV_REMOTE_SERVICE = "android.permission.BIND_TV_REMOTE_SERVICE";
     field public static final String BRICK = "android.permission.BRICK";
@@ -99,7 +100,7 @@
     field public static final String INJECT_EVENTS = "android.permission.INJECT_EVENTS";
     field public static final String INSTALL_DYNAMIC_SYSTEM = "android.permission.INSTALL_DYNAMIC_SYSTEM";
     field public static final String INSTALL_GRANT_RUNTIME_PERMISSIONS = "android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS";
-    field public static final String INSTALL_LOCATION_TIME_ZONE_PROVIDER = "android.permission.INSTALL_LOCATION_TIME_ZONE_PROVIDER";
+    field public static final String INSTALL_LOCATION_TIME_ZONE_PROVIDER_SERVICE = "android.permission.INSTALL_LOCATION_TIME_ZONE_PROVIDER_SERVICE";
     field public static final String INSTALL_PACKAGE_UPDATES = "android.permission.INSTALL_PACKAGE_UPDATES";
     field public static final String INSTALL_SELF_UPDATES = "android.permission.INSTALL_SELF_UPDATES";
     field public static final String INTENT_FILTER_VERIFICATION_AGENT = "android.permission.INTENT_FILTER_VERIFICATION_AGENT";
@@ -660,6 +661,10 @@
     field public static final int FLAG_AUTOGROUP_SUMMARY = 1024; // 0x400
   }
 
+  public static class Notification.Action implements android.os.Parcelable {
+    field public static final int SEMANTIC_ACTION_MARK_CONVERSATION_AS_PRIORITY = 11; // 0xb
+  }
+
   public static final class Notification.TvExtender implements android.app.Notification.Extender {
     ctor public Notification.TvExtender();
     ctor public Notification.TvExtender(android.app.Notification);
@@ -1346,7 +1351,6 @@
 
   public abstract class RoleControllerService extends android.app.Service {
     ctor public RoleControllerService();
-    method @NonNull public android.app.role.RolePrivileges getRolePrivileges(@NonNull String);
     method @WorkerThread public abstract boolean onAddRoleHolder(@NonNull String, @NonNull String, int);
     method @Nullable public final android.os.IBinder onBind(@Nullable android.content.Intent);
     method @WorkerThread public abstract boolean onClearRoleHolders(@NonNull String, int);
@@ -1373,18 +1377,6 @@
     field public static final int MANAGE_HOLDERS_FLAG_DONT_KILL_APP = 1; // 0x1
   }
 
-  public final class RolePrivileges implements android.os.Parcelable {
-    ctor public RolePrivileges(@NonNull java.util.List<java.lang.String>, @NonNull java.util.List<java.lang.String>, @NonNull java.util.List<java.lang.String>, @NonNull java.util.List<java.lang.String>);
-    method public int describeContents();
-    method @NonNull public java.util.List<java.lang.String> getAppOpPermissions();
-    method @NonNull public java.util.List<java.lang.String> getAppOps();
-    method @NonNull public java.util.List<java.lang.String> getCapabilities();
-    method @NonNull public java.util.List<java.lang.String> getPermissions();
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field public static final String CAPABILITY_NOTIFICATION_LISTENER = "android.app.role.capability.NOTIFICATION_LISTENER";
-    field @NonNull public static final android.os.Parcelable.Creator<android.app.role.RolePrivileges> CREATOR;
-  }
-
 }
 
 package android.app.search {
@@ -2234,7 +2226,7 @@
   }
 
   public static class LauncherApps.ShortcutQuery {
-    field public static final int FLAG_GET_PERSONS_DATA = 2048; // 0x800
+    field @RequiresPermission(android.Manifest.permission.ACCESS_SHORTCUTS) public static final int FLAG_GET_PERSONS_DATA = 2048; // 0x800
   }
 
   public class PackageInstaller {
@@ -3950,19 +3942,29 @@
     method public void onLocationBatch(java.util.List<android.location.Location>);
   }
 
-  public final class GnssCapabilities {
+  public final class GnssCapabilities implements android.os.Parcelable {
     method public boolean hasGeofencing();
     method public boolean hasLowPowerMode();
     method public boolean hasMeasurementCorrections();
     method public boolean hasMeasurementCorrectionsExcessPathLength();
     method public boolean hasMeasurementCorrectionsLosSats();
-    method public boolean hasMeasurementCorrectionsReflectingPane();
-    method public boolean hasMeasurements();
-    method public boolean hasNavMessages();
+    method @Deprecated public boolean hasMeasurementCorrectionsReflectingPane();
+    method public boolean hasMeasurementCorrectionsReflectingPlane();
+    method @Deprecated public boolean hasNavMessages();
     method @Deprecated public boolean hasSatelliteBlacklist();
     method public boolean hasSatelliteBlocklist();
   }
 
+  public static final class GnssCapabilities.Builder {
+    method @NonNull public android.location.GnssCapabilities.Builder setHasGeofencing(boolean);
+    method @NonNull public android.location.GnssCapabilities.Builder setHasLowPowerMode(boolean);
+    method @NonNull public android.location.GnssCapabilities.Builder setHasMeasurementCorrections(boolean);
+    method @NonNull public android.location.GnssCapabilities.Builder setHasMeasurementCorrectionsExcessPathLength(boolean);
+    method @NonNull public android.location.GnssCapabilities.Builder setHasMeasurementCorrectionsLosSats(boolean);
+    method @NonNull public android.location.GnssCapabilities.Builder setHasMeasurementCorrectionsReflectingPlane(boolean);
+    method @NonNull public android.location.GnssCapabilities.Builder setHasSatelliteBlocklist(boolean);
+  }
+
   public final class GnssMeasurementCorrections implements android.os.Parcelable {
     method public int describeContents();
     method @FloatRange(from=-1000.0F, to=10000.0f) public double getAltitudeMeters();
@@ -8360,6 +8362,7 @@
 
   public abstract class PermissionControllerService extends android.app.Service {
     ctor public PermissionControllerService();
+    method @NonNull @RequiresPermission("android.permission.MANAGE_COMPANION_DEVICES") public String getPrivilegesDescriptionStringForProfile(@NonNull String);
     method @BinderThread public void onApplyStagedRuntimePermissionBackup(@NonNull String, @NonNull android.os.UserHandle, @NonNull java.util.function.Consumer<java.lang.Boolean>);
     method @NonNull public final android.os.IBinder onBind(android.content.Intent);
     method @BinderThread public abstract void onCountPermissionApps(@NonNull java.util.List<java.lang.String>, int, @NonNull java.util.function.IntConsumer);
@@ -9213,6 +9216,7 @@
     method @NonNull public android.content.ComponentName getActivityComponent();
     method @NonNull public android.view.autofill.AutofillId getFocusedId();
     method @NonNull public android.view.autofill.AutofillValue getFocusedValue();
+    method @Nullable public android.app.assist.AssistStructure.ViewNode getFocusedViewNode();
     method @Nullable public android.view.inputmethod.InlineSuggestionsRequest getInlineSuggestionsRequest();
     method @Nullable public android.service.autofill.augmented.PresentationParams getPresentationParams();
     method public int getTaskId();
@@ -9776,7 +9780,6 @@
     method public final void reportPermanentFailure(@NonNull Throwable);
     method public final void reportSuggestion(@NonNull android.service.timezone.TimeZoneProviderSuggestion);
     method public final void reportUncertain();
-    field public static final String BIND_PERMISSION = "android.permission.INSTALL_LOCATION_TIME_ZONE_PROVIDER";
     field public static final String PRIMARY_LOCATION_TIME_ZONE_PROVIDER_SERVICE_INTERFACE = "android.service.timezone.PrimaryLocationTimeZoneProviderService";
     field public static final String SECONDARY_LOCATION_TIME_ZONE_PROVIDER_SERVICE_INTERFACE = "android.service.timezone.SecondaryLocationTimeZoneProviderService";
   }
@@ -9853,6 +9856,7 @@
     field public static final int RECOGNITION_FLAG_ENABLE_AUDIO_NOISE_SUPPRESSION = 8; // 0x8
     field public static final int RECOGNITION_MODE_USER_IDENTIFICATION = 2; // 0x2
     field public static final int RECOGNITION_MODE_VOICE_TRIGGER = 1; // 0x1
+    field public static final int STATE_ERROR = 3; // 0x3
     field public static final int STATE_HARDWARE_UNAVAILABLE = -2; // 0xfffffffe
     field public static final int STATE_KEYPHRASE_ENROLLED = 2; // 0x2
     field public static final int STATE_KEYPHRASE_UNENROLLED = 1; // 0x1
@@ -11426,34 +11430,6 @@
     field public static final String TYPE_XCAP_STRING = "xcap";
   }
 
-  public final class ApnThrottleStatus implements android.os.Parcelable {
-    method public int describeContents();
-    method public int getApnType();
-    method public int getRetryType();
-    method public int getSlotIndex();
-    method public long getThrottleExpiryTimeMillis();
-    method public int getThrottleType();
-    method public int getTransportType();
-    method public void writeToParcel(@NonNull android.os.Parcel, int);
-    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.data.ApnThrottleStatus> CREATOR;
-    field public static final int RETRY_TYPE_HANDOVER = 3; // 0x3
-    field public static final int RETRY_TYPE_NEW_CONNECTION = 2; // 0x2
-    field public static final int RETRY_TYPE_NONE = 1; // 0x1
-    field public static final int THROTTLE_TYPE_ELAPSED_TIME = 2; // 0x2
-    field public static final int THROTTLE_TYPE_NONE = 1; // 0x1
-  }
-
-  public static final class ApnThrottleStatus.Builder {
-    ctor public ApnThrottleStatus.Builder();
-    method @NonNull public android.telephony.data.ApnThrottleStatus build();
-    method @NonNull public android.telephony.data.ApnThrottleStatus.Builder setApnType(int);
-    method @NonNull public android.telephony.data.ApnThrottleStatus.Builder setNoThrottle();
-    method @NonNull public android.telephony.data.ApnThrottleStatus.Builder setRetryType(int);
-    method @NonNull public android.telephony.data.ApnThrottleStatus.Builder setSlotIndex(int);
-    method @NonNull public android.telephony.data.ApnThrottleStatus.Builder setThrottleExpiryTimeMillis(long);
-    method @NonNull public android.telephony.data.ApnThrottleStatus.Builder setTransportType(int);
-  }
-
   public final class DataCallResponse implements android.os.Parcelable {
     method public int describeContents();
     method @NonNull public java.util.List<android.net.LinkAddress> getAddresses();
@@ -11610,7 +11586,7 @@
     ctor public QualifiedNetworksService.NetworkAvailabilityProvider(int);
     method public abstract void close();
     method public final int getSlotIndex();
-    method public void reportApnThrottleStatusChanged(@NonNull java.util.List<android.telephony.data.ApnThrottleStatus>);
+    method public void reportThrottleStatusChanged(@NonNull java.util.List<android.telephony.data.ThrottleStatus>);
     method public final void updateQualifiedNetworkTypes(int, @NonNull java.util.List<java.lang.Integer>);
   }
 
@@ -11640,6 +11616,34 @@
     method @NonNull public android.telephony.data.SliceInfo.Builder setSliceServiceType(int);
   }
 
+  public final class ThrottleStatus implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getApnType();
+    method public int getRetryType();
+    method public int getSlotIndex();
+    method public long getThrottleExpiryTimeMillis();
+    method public int getThrottleType();
+    method public int getTransportType();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.telephony.data.ThrottleStatus> CREATOR;
+    field public static final int RETRY_TYPE_HANDOVER = 3; // 0x3
+    field public static final int RETRY_TYPE_NEW_CONNECTION = 2; // 0x2
+    field public static final int RETRY_TYPE_NONE = 1; // 0x1
+    field public static final int THROTTLE_TYPE_ELAPSED_TIME = 2; // 0x2
+    field public static final int THROTTLE_TYPE_NONE = 1; // 0x1
+  }
+
+  public static final class ThrottleStatus.Builder {
+    ctor public ThrottleStatus.Builder();
+    method @NonNull public android.telephony.data.ThrottleStatus build();
+    method @NonNull public android.telephony.data.ThrottleStatus.Builder setApnType(int);
+    method @NonNull public android.telephony.data.ThrottleStatus.Builder setNoThrottle();
+    method @NonNull public android.telephony.data.ThrottleStatus.Builder setRetryType(int);
+    method @NonNull public android.telephony.data.ThrottleStatus.Builder setSlotIndex(int);
+    method @NonNull public android.telephony.data.ThrottleStatus.Builder setThrottleExpiryTimeMillis(long);
+    method @NonNull public android.telephony.data.ThrottleStatus.Builder setTransportType(int);
+  }
+
 }
 
 package android.telephony.euicc {
@@ -12127,7 +12131,9 @@
     method public void callSessionHoldFailed(android.telephony.ims.ImsReasonInfo);
     method public void callSessionHoldReceived(android.telephony.ims.ImsCallProfile);
     method public void callSessionInitiated(android.telephony.ims.ImsCallProfile);
-    method public void callSessionInitiatedFailed(android.telephony.ims.ImsReasonInfo);
+    method @Deprecated public void callSessionInitiatedFailed(android.telephony.ims.ImsReasonInfo);
+    method public void callSessionInitiating(@NonNull android.telephony.ims.ImsCallProfile);
+    method public void callSessionInitiatingFailed(@NonNull android.telephony.ims.ImsReasonInfo);
     method public void callSessionInviteParticipantsRequestDelivered();
     method public void callSessionInviteParticipantsRequestFailed(android.telephony.ims.ImsReasonInfo);
     method @Deprecated public void callSessionMayHandover(int, int);
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 5b86e8d..1086577 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -144,15 +144,13 @@
     method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public void resizePrimarySplitScreen(@NonNull android.graphics.Rect, @NonNull android.graphics.Rect);
     method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public void resizeTask(int, android.graphics.Rect);
     method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public boolean setTaskWindowingMode(int, int, boolean) throws java.lang.SecurityException;
-    method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public boolean setTaskWindowingModeSplitScreenPrimary(int, int, boolean, boolean, android.graphics.Rect, boolean) throws java.lang.SecurityException;
+    method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public boolean setTaskWindowingModeSplitScreenPrimary(int, boolean);
     method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public void startSystemLockTaskMode(int);
     method @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS) public void stopSystemLockTaskMode();
     method public static boolean supportsMultiWindow(android.content.Context);
     method public static boolean supportsSplitScreenMultiWindow(android.content.Context);
     field public static final int DEFAULT_MINIMAL_SPLIT_SCREEN_DISPLAY_SIZE_DP = 440; // 0x1b8
     field public static final int INVALID_STACK_ID = -1; // 0xffffffff
-    field public static final int SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT = 1; // 0x1
-    field public static final int SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT = 0; // 0x0
   }
 
   public class ActivityView extends android.view.ViewGroup {
@@ -384,6 +382,18 @@
     field public static final int OPERATION_LOGOUT_USER = 9; // 0x9
     field public static final int OPERATION_REBOOT = 7; // 0x7
     field public static final int OPERATION_REMOVE_USER = 6; // 0x6
+    field public static final int OPERATION_SET_APPLICATION_HIDDEN = 15; // 0xf
+    field public static final int OPERATION_SET_APPLICATION_RESTRICTIONS = 16; // 0x10
+    field public static final int OPERATION_SET_KEEP_UNINSTALLED_PACKAGES = 17; // 0x11
+    field public static final int OPERATION_SET_KEYGUARD_DISABLED = 12; // 0xc
+    field public static final int OPERATION_SET_LOCK_TASK_FEATURES = 18; // 0x12
+    field public static final int OPERATION_SET_LOCK_TASK_PACKAGES = 19; // 0x13
+    field public static final int OPERATION_SET_PACKAGES_SUSPENDED = 20; // 0x14
+    field public static final int OPERATION_SET_STATUS_BAR_DISABLED = 13; // 0xd
+    field public static final int OPERATION_SET_SYSTEM_SETTING = 11; // 0xb
+    field public static final int OPERATION_SET_SYSTEM_UPDATE_POLICY = 14; // 0xe
+    field public static final int OPERATION_SET_TRUST_AGENT_CONFIGURATION = 21; // 0x15
+    field public static final int OPERATION_SET_USER_CONTROL_DISABLED_PACKAGES = 22; // 0x16
     field public static final int OPERATION_SET_USER_RESTRICTION = 10; // 0xa
     field public static final int OPERATION_START_USER_IN_BACKGROUND = 3; // 0x3
     field public static final int OPERATION_STOP_USER = 4; // 0x4
@@ -1704,6 +1714,7 @@
     method @NonNull public android.content.ComponentName getActivityComponent();
     method @NonNull public android.view.autofill.AutofillId getFocusedId();
     method @NonNull public android.view.autofill.AutofillValue getFocusedValue();
+    method @Nullable public android.app.assist.AssistStructure.ViewNode getFocusedViewNode();
     method @Nullable public android.view.inputmethod.InlineSuggestionsRequest getInlineSuggestionsRequest();
     method @Nullable public android.service.autofill.augmented.PresentationParams getPresentationParams();
     method public int getTaskId();
@@ -2189,6 +2200,7 @@
     ctor public AutofillId(int, int);
     ctor public AutofillId(@NonNull android.view.autofill.AutofillId, long, int);
     method public boolean equalsIgnoreSession(@Nullable android.view.autofill.AutofillId);
+    method public boolean isNonVirtual();
     method @NonNull public static android.view.autofill.AutofillId withoutSession(@NonNull android.view.autofill.AutofillId);
   }
 
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 294a363..55df824 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -890,6 +890,9 @@
     @UnsupportedAppUsage
     final FragmentController mFragments = FragmentController.createController(new HostCallbacks());
 
+    /** The options for scene transition. */
+    ActivityOptions mPendingOptions;
+
     private static final class ManagedCursor {
         ManagedCursor(Cursor cursor) {
             mCursor = cursor;
@@ -7258,7 +7261,7 @@
     }
 
     /**
-     * Retrieve the ActivityOptions passed in from the launching activity or passed back
+     * Takes the ActivityOptions passed in from the launching activity or passed back
      * from an activity launched by this activity in its call to {@link
      * #convertToTranslucent(TranslucentConversionListener, ActivityOptions)}
      *
@@ -7267,7 +7270,10 @@
      */
     @UnsupportedAppUsage
     ActivityOptions getActivityOptions() {
-        return ActivityOptions.fromBundle(ActivityClient.getInstance().getActivityOptions(mToken));
+        final ActivityOptions options = mPendingOptions;
+        // The option only applies once.
+        mPendingOptions = null;
+        return options;
     }
 
     /**
diff --git a/core/java/android/app/ActivityClient.java b/core/java/android/app/ActivityClient.java
index 64d795c..d465b22 100644
--- a/core/java/android/app/ActivityClient.java
+++ b/core/java/android/app/ActivityClient.java
@@ -237,14 +237,6 @@
         }
     }
 
-    Bundle getActivityOptions(IBinder token) {
-        try {
-            return getActivityClientController().getActivityOptions(token);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
     public void setRequestedOrientation(IBinder token, int requestedOrientation) {
         try {
             getActivityClientController().setRequestedOrientation(token, requestedOrientation);
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 1227cc2..8c62e9c 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -32,7 +32,7 @@
 import android.annotation.SystemService;
 import android.annotation.TestApi;
 import android.compat.annotation.ChangeId;
-import android.compat.annotation.Disabled;
+import android.compat.annotation.EnabledSince;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.Context;
@@ -44,18 +44,13 @@
 import android.content.pm.PackageManager;
 import android.content.pm.ParceledListSlice;
 import android.content.pm.UserInfo;
-import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.Color;
-import android.graphics.ColorSpace;
-import android.graphics.GraphicBuffer;
 import android.graphics.Matrix;
 import android.graphics.Point;
-import android.graphics.Rect;
 import android.graphics.drawable.Icon;
-import android.hardware.HardwareBuffer;
 import android.os.BatteryStats;
 import android.os.Binder;
 import android.os.Build;
@@ -81,8 +76,6 @@
 import android.util.Size;
 import android.util.TypedXmlPullParser;
 import android.util.TypedXmlSerializer;
-import android.view.Surface;
-import android.view.WindowInsetsController.Appearance;
 
 import com.android.internal.app.LocalePicker;
 import com.android.internal.app.procstats.ProcessStats;
@@ -882,7 +875,6 @@
      */
     @TestApi
     @ChangeId
-    @Disabled
     public static final long DROP_CLOSE_SYSTEM_DIALOGS = 174664120L;
 
     /**
@@ -898,8 +890,7 @@
      */
     @TestApi
     @ChangeId
-    @Disabled
-    // @EnabledSince(targetSdkVersion = VERSION_CODES.S)
+    @EnabledSince(targetSdkVersion = VERSION_CODES.S)
     public static final long LOCK_DOWN_CLOSE_SYSTEM_DIALOGS = 174664365L;
 
     /** @hide */
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index a2b9157..f541e1a 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -17,7 +17,6 @@
 package android.app;
 
 import static android.Manifest.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS;
-import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 import static android.view.Display.INVALID_DISPLAY;
@@ -256,13 +255,6 @@
             "android.activity.freezeRecentTasksReordering";
 
     /**
-     * Where the split-screen-primary stack should be positioned.
-     * @hide
-     */
-    private static final String KEY_SPLIT_SCREEN_CREATE_MODE =
-            "android:activity.splitScreenCreateMode";
-
-    /**
      * Determines whether to disallow the outgoing activity from entering picture-in-picture as the
      * result of a new activity being launched.
      * @hide
@@ -373,7 +365,6 @@
     private int mLaunchActivityType = ACTIVITY_TYPE_UNDEFINED;
     private int mLaunchTaskId = -1;
     private int mPendingIntentLaunchFlags;
-    private int mSplitScreenCreateMode = SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
     private boolean mLockTaskMode = false;
     private boolean mDisallowEnterPictureInPictureWhileLaunching;
     private boolean mApplyActivityFlagsForBubbles;
@@ -1049,8 +1040,6 @@
         mTaskOverlayCanResume = opts.getBoolean(KEY_TASK_OVERLAY_CAN_RESUME, false);
         mAvoidMoveToFront = opts.getBoolean(KEY_AVOID_MOVE_TO_FRONT, false);
         mFreezeRecentTasksReordering = opts.getBoolean(KEY_FREEZE_RECENT_TASKS_REORDERING, false);
-        mSplitScreenCreateMode = opts.getInt(KEY_SPLIT_SCREEN_CREATE_MODE,
-                SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT);
         mDisallowEnterPictureInPictureWhileLaunching = opts.getBoolean(
                 KEY_DISALLOW_ENTER_PICTURE_IN_PICTURE_WHILE_LAUNCHING, false);
         mApplyActivityFlagsForBubbles = opts.getBoolean(
@@ -1469,14 +1458,9 @@
     }
 
     /** @hide */
-    public int getSplitScreenCreateMode() {
-        return mSplitScreenCreateMode;
-    }
-
-    /** @hide */
     @UnsupportedAppUsage
     public void setSplitScreenCreateMode(int splitScreenCreateMode) {
-        mSplitScreenCreateMode = splitScreenCreateMode;
+        // Remove this method after @UnsupportedAppUsage can be removed.
     }
 
     /** @hide */
@@ -1709,9 +1693,6 @@
         if (mFreezeRecentTasksReordering) {
             b.putBoolean(KEY_FREEZE_RECENT_TASKS_REORDERING, mFreezeRecentTasksReordering);
         }
-        if (mSplitScreenCreateMode != SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT) {
-            b.putInt(KEY_SPLIT_SCREEN_CREATE_MODE, mSplitScreenCreateMode);
-        }
         if (mDisallowEnterPictureInPictureWhileLaunching) {
             b.putBoolean(KEY_DISALLOW_ENTER_PICTURE_IN_PICTURE_WHILE_LAUNCHING,
                     mDisallowEnterPictureInPictureWhileLaunching);
diff --git a/core/java/android/app/ActivityTaskManager.java b/core/java/android/app/ActivityTaskManager.java
index 2060252..fbc3b0d 100644
--- a/core/java/android/app/ActivityTaskManager.java
+++ b/core/java/android/app/ActivityTaskManager.java
@@ -16,6 +16,8 @@
 
 package android.app;
 
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+
 import android.annotation.NonNull;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemService;
@@ -58,20 +60,6 @@
     public static final int INVALID_TASK_ID = -1;
 
     /**
-     * Parameter to {@link IActivityTaskManager#setTaskWindowingModeSplitScreenPrimary} which
-     * specifies the position of the created docked stack at the top half of the screen if
-     * in portrait mode or at the left half of the screen if in landscape mode.
-     */
-    public static final int SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT = 0;
-
-    /**
-     * Parameter to {@link IActivityTaskManager#setTaskWindowingModeSplitScreenPrimary} which
-     * specifies the position of the created docked stack at the bottom half of the screen if
-     * in portrait mode or at the right half of the screen if in landscape mode.
-     */
-    public static final int SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT = 1;
-
-    /**
      * Input parameter to {@link IActivityTaskManager#resizeTask} which indicates
      * that the resize doesn't need to preserve the window, and can be skipped if bounds
      * is unchanged. This mode is used by window manager in most cases.
@@ -199,28 +187,12 @@
     /**
      * Moves the input task to the primary-split-screen stack.
      * @param taskId Id of task to move.
-     * @param createMode The mode the primary split screen stack should be created in if it doesn't
-     *                   exist already. See
-     *                   {@link ActivityTaskManager#SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT}
-     *                   and
-     *                   {@link android.app.ActivityManager
-     *                        #SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT}
      * @param toTop If the task and stack should be moved to the top.
-     * @param animate Whether we should play an animation for the moving the task
-     * @param initialBounds If the primary stack gets created, it will use these bounds for the
-     *                      docked stack. Pass {@code null} to use default bounds.
-     * @param showRecents If the recents activity should be shown on the other side of the task
-     *                    going into split-screen mode.
      * @return Whether the task was successfully put into splitscreen.
      */
     @RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS)
-    public boolean setTaskWindowingModeSplitScreenPrimary(int taskId, int createMode, boolean toTop,
-            boolean animate, Rect initialBounds, boolean showRecents) throws SecurityException {
-        try {
-            return getService().setTaskWindowingModeSplitScreenPrimary(taskId, toTop);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+    public boolean setTaskWindowingModeSplitScreenPrimary(int taskId, boolean toTop) {
+        return setTaskWindowingMode(taskId, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, toTop);
     }
 
     /**
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 69482bc..2fe1711 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -567,6 +567,9 @@
         @UnsupportedAppUsage
         boolean mPreserveWindow;
 
+        /** The options for scene transition. */
+        ActivityOptions mActivityOptions;
+
         /**
          * If non-null, the activity is launching with a specified rotation, the adjustments should
          * be consumed before activity creation.
@@ -587,8 +590,8 @@
                 ActivityInfo info, Configuration overrideConfig, CompatibilityInfo compatInfo,
                 String referrer, IVoiceInteractor voiceInteractor, Bundle state,
                 PersistableBundle persistentState, List<ResultInfo> pendingResults,
-                List<ReferrerIntent> pendingNewIntents, boolean isForward,
-                ProfilerInfo profilerInfo, ClientTransactionHandler client,
+                List<ReferrerIntent> pendingNewIntents, ActivityOptions activityOptions,
+                boolean isForward, ProfilerInfo profilerInfo, ClientTransactionHandler client,
                 IBinder assistToken, FixedRotationAdjustments fixedRotationAdjustments) {
             this.token = token;
             this.assistToken = assistToken;
@@ -607,6 +610,7 @@
             this.overrideConfig = overrideConfig;
             this.packageInfo = client.getPackageInfoNoCheck(activityInfo.applicationInfo,
                     compatInfo);
+            mActivityOptions = activityOptions;
             mPendingFixedRotationAdjustments = fixedRotationAdjustments;
             init();
         }
@@ -3469,6 +3473,10 @@
                     activity.setTheme(theme);
                 }
 
+                if (r.mActivityOptions != null) {
+                    activity.mPendingOptions = r.mActivityOptions;
+                    r.mActivityOptions = null;
+                }
                 activity.mCalled = false;
                 if (r.isPersistable()) {
                     mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
@@ -3509,7 +3517,7 @@
 
     @Override
     public void handleStartActivity(ActivityClientRecord r,
-            PendingTransactionActions pendingActions) {
+            PendingTransactionActions pendingActions, ActivityOptions activityOptions) {
         final Activity activity = r.activity;
         if (!r.stopped) {
             throw new IllegalStateException("Can't start activity that is not stopped.");
@@ -3520,6 +3528,9 @@
         }
 
         unscheduleGcIdler();
+        if (activityOptions != null) {
+            activity.mPendingOptions = activityOptions;
+        }
 
         // Start
         activity.performStart("handleStartActivity");
diff --git a/core/java/android/app/ClientTransactionHandler.java b/core/java/android/app/ClientTransactionHandler.java
index ac50676..0e1c827 100644
--- a/core/java/android/app/ClientTransactionHandler.java
+++ b/core/java/android/app/ClientTransactionHandler.java
@@ -164,7 +164,7 @@
 
     /** Perform activity start. */
     public abstract void handleStartActivity(@NonNull ActivityClientRecord r,
-            PendingTransactionActions pendingActions);
+            PendingTransactionActions pendingActions, ActivityOptions activityOptions);
 
     /** Get package info. */
     public abstract LoadedApk getPackageInfoNoCheck(ApplicationInfo ai,
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 700d8ff..73327011 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -2489,6 +2489,20 @@
         return new WindowContext(this, display, type, options);
     }
 
+    @NonNull
+    @Override
+    public Context createTokenContext(@NonNull IBinder token, @NonNull Display display) {
+        if (display == null) {
+            throw new UnsupportedOperationException("Token context can only be created from "
+                    + "other visual contexts, such as Activity or one created with "
+                    + "Context#createDisplayContext(Display)");
+        }
+        final ContextImpl tokenContext = createBaseWindowContext(token, display);
+        tokenContext.setResources(createWindowContextResources());
+        return tokenContext;
+    }
+
+
     ContextImpl createBaseWindowContext(IBinder token, Display display) {
         ContextImpl context = new ContextImpl(this, mMainThread, mPackageInfo, mAttributionTag,
                 mSplitName, token, mUser, mFlags, mClassLoader, null);
diff --git a/core/java/android/app/IActivityClientController.aidl b/core/java/android/app/IActivityClientController.aidl
index e1e0a8a..ebf1027 100644
--- a/core/java/android/app/IActivityClientController.aidl
+++ b/core/java/android/app/IActivityClientController.aidl
@@ -32,15 +32,15 @@
  */
 interface IActivityClientController {
     oneway void activityIdle(in IBinder token, in Configuration config, in boolean stopProfiling);
-    void activityResumed(in IBinder token);
-    void activityTopResumedStateLost();
-    void activityPaused(in IBinder token);
-    void activityStopped(in IBinder token, in Bundle state, in PersistableBundle persistentState,
-            in CharSequence description);
+    oneway void activityResumed(in IBinder token);
+    oneway void activityTopResumedStateLost();
+    oneway void activityPaused(in IBinder token);
+    oneway void activityStopped(in IBinder token, in Bundle state,
+            in PersistableBundle persistentState, in CharSequence description);
     oneway void activityDestroyed(in IBinder token);
-    void activityRelaunched(in IBinder token);
+    oneway void activityRelaunched(in IBinder token);
 
-    void reportSizeConfigurations(in IBinder token, in int[] horizontalSizeConfiguration,
+    oneway void reportSizeConfigurations(in IBinder token, in int[] horizontalSizeConfiguration,
             in int[] verticalSizeConfigurations, in int[] smallestWidthConfigurations);
     boolean moveActivityTaskToBack(in IBinder token, boolean nonRoot);
     boolean shouldUpRecreateTask(in IBinder token, in String destAffinity);
@@ -60,7 +60,6 @@
     String getCallingPackage(in IBinder token);
     int getLaunchedFromUid(in IBinder token);
     String getLaunchedFromPackage(in IBinder token);
-    Bundle getActivityOptions(in IBinder token);
 
     void setRequestedOrientation(in IBinder token, int requestedOrientation);
     int getRequestedOrientation(in IBinder token);
@@ -75,8 +74,8 @@
     void setPictureInPictureParams(in IBinder token, in PictureInPictureParams params);
     void toggleFreeformWindowingMode(in IBinder token);
 
-    void startLockTaskModeByToken(in IBinder token);
-    void stopLockTaskModeByToken(in IBinder token);
+    oneway void startLockTaskModeByToken(in IBinder token);
+    oneway void stopLockTaskModeByToken(in IBinder token);
     oneway void showLockTaskEscapeMessage(in IBinder token);
     void setTaskDescription(in IBinder token, in ActivityManager.TaskDescription values);
 
@@ -85,16 +84,16 @@
     void startLocalVoiceInteraction(in IBinder token, in Bundle options);
     void stopLocalVoiceInteraction(in IBinder token);
 
-    void setShowWhenLocked(in IBinder token, boolean showWhenLocked);
-    void setInheritShowWhenLocked(in IBinder token, boolean setInheritShownWhenLocked);
-    void setTurnScreenOn(in IBinder token, boolean turnScreenOn);
-    void reportActivityFullyDrawn(in IBinder token, boolean restoredFromBundle);
-    void overridePendingTransition(in IBinder token, in String packageName,
+    oneway void setShowWhenLocked(in IBinder token, boolean showWhenLocked);
+    oneway void setInheritShowWhenLocked(in IBinder token, boolean setInheritShownWhenLocked);
+    oneway void setTurnScreenOn(in IBinder token, boolean turnScreenOn);
+    oneway void reportActivityFullyDrawn(in IBinder token, boolean restoredFromBundle);
+    oneway void overridePendingTransition(in IBinder token, in String packageName,
             int enterAnim, int exitAnim);
     int setVrMode(in IBinder token, boolean enabled, in ComponentName packageName);
 
     /** See {@link android.app.Activity#setDisablePreviewScreenshots}. */
-    void setDisablePreviewScreenshots(in IBinder token, boolean disable);
+    oneway void setDisablePreviewScreenshots(in IBinder token, boolean disable);
 
     /** Registers remote animations for a specific activity. */
     void registerRemoteAnimations(in IBinder token, in RemoteAnimationDefinition definition);
@@ -106,5 +105,5 @@
      * Reports that an Activity received a back key press when there were no additional activities
      * on the back stack.
      */
-    void onBackPressedOnTaskRoot(in IBinder token);
+    oneway void onBackPressedOnTaskRoot(in IBinder token);
 }
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index 523c155..1d65711 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -218,7 +218,7 @@
      */
     boolean setTaskWindowingMode(int taskId, int windowingMode, boolean toTop);
     void moveTaskToRootTask(int taskId, int rootTaskId, boolean toTop);
-    boolean setTaskWindowingModeSplitScreenPrimary(int taskId, boolean toTop);
+
     /**
      * Removes root tasks in the input windowing modes from the system if they are of activity type
      * ACTIVITY_TYPE_STANDARD or ACTIVITY_TYPE_UNDEFINED
diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index a8ce73d..697a377 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -35,6 +35,7 @@
 import android.service.notification.IConditionListener;
 import android.service.notification.IConditionProvider;
 import android.service.notification.INotificationListener;
+import android.service.notification.NotificationListenerFilter;
 import android.service.notification.StatusBarNotification;
 import android.app.AutomaticZenRule;
 import android.service.notification.ZenModeConfig;
@@ -224,4 +225,7 @@
     boolean getPrivateNotificationsAllowed();
 
     long pullStats(long startNs, int report, boolean doAgg, out List<ParcelFileDescriptor> stats);
+
+    NotificationListenerFilter getListenerFilter(in ComponentName cn, int userId);
+    void setListenerFilter(in ComponentName cn, int userId, in NotificationListenerFilter nlf);
 }
diff --git a/core/java/android/app/LocalActivityManager.java b/core/java/android/app/LocalActivityManager.java
index 2e7c9f1..74e6125 100644
--- a/core/java/android/app/LocalActivityManager.java
+++ b/core/java/android/app/LocalActivityManager.java
@@ -178,7 +178,8 @@
                 pendingActions = null;
             }
 
-            mActivityThread.handleStartActivity(clientRecord, pendingActions);
+            mActivityThread.handleStartActivity(clientRecord, pendingActions,
+                    null /* activityOptions */);
             r.curState = STARTED;
             
             if (desiredState == RESUMED) {
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 8242e4d..27e0a4f 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -83,6 +83,7 @@
 import android.util.Log;
 import android.util.Pair;
 import android.util.SparseArray;
+import android.util.TypedValue;
 import android.util.proto.ProtoOutputStream;
 import android.view.ContextThemeWrapper;
 import android.view.Gravity;
@@ -1523,6 +1524,14 @@
          */
         public static final int SEMANTIC_ACTION_CALL = 10;
 
+        /**
+         * {@code SemanticAction}: Mark the conversation associated with the notification as a
+         * priority. Note that this is only for use by the notification assistant services.
+         * @hide
+         */
+        @SystemApi
+        public static final int SEMANTIC_ACTION_MARK_CONVERSATION_AS_PRIORITY = 11;
+
         private final Bundle mExtras;
         @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
         private Icon mIcon;
@@ -2233,7 +2242,8 @@
                 SEMANTIC_ACTION_UNMUTE,
                 SEMANTIC_ACTION_THUMBS_UP,
                 SEMANTIC_ACTION_THUMBS_DOWN,
-                SEMANTIC_ACTION_CALL
+                SEMANTIC_ACTION_CALL,
+                SEMANTIC_ACTION_MARK_CONVERSATION_AS_PRIORITY
         })
         @Retention(RetentionPolicy.SOURCE)
         public @interface SemanticAction {}
@@ -4911,7 +4921,8 @@
                 setTextViewColorPrimary(contentView, R.id.title, p);
                 contentView.setViewLayoutWidth(R.id.title, showProgress
                         ? ViewGroup.LayoutParams.WRAP_CONTENT
-                        : ViewGroup.LayoutParams.MATCH_PARENT);
+                        : ViewGroup.LayoutParams.MATCH_PARENT,
+                        TypedValue.COMPLEX_UNIT_PX);
             }
             if (p.text != null && p.text.length() != 0) {
                 int textId = showProgress ? com.android.internal.R.id.text_line_1
@@ -5356,8 +5367,9 @@
             final boolean snoozeEnabled = mContext.getContentResolver() != null
                     && (Settings.Secure.getInt(mContext.getContentResolver(),
                         Settings.Secure.SHOW_NOTIFICATION_SNOOZE, 0) == 1);
-            big.setViewLayoutMarginBottomDimen(R.id.notification_action_list_margin_target,
-                    snoozeEnabled ? 0 : R.dimen.notification_content_margin);
+            int bottomMarginDimen = snoozeEnabled ? 0 : R.dimen.notification_content_margin;
+            big.setViewLayoutMarginDimen(R.id.notification_action_list_margin_target,
+                    RemoteViews.MARGIN_BOTTOM, bottomMarginDimen);
         }
 
         private static List<Notification.Action> filterOutContextualActions(
@@ -5389,7 +5401,8 @@
             if (N > 0) {
                 big.setViewVisibility(R.id.actions_container, View.VISIBLE);
                 big.setViewVisibility(R.id.actions, View.VISIBLE);
-                big.setViewLayoutMarginBottomDimen(R.id.notification_action_list_margin_target, 0);
+                big.setViewLayoutMarginDimen(R.id.notification_action_list_margin_target,
+                        RemoteViews.MARGIN_BOTTOM, 0);
                 if (N>MAX_ACTION_BUTTONS) N=MAX_ACTION_BUTTONS;
                 for (int i=0; i<N; i++) {
                     Action action = nonContextualActions.get(i);
@@ -7788,8 +7801,8 @@
                 // also update the end margin if there is an image
                 // NOTE: This template doesn't support moving this icon to the left, so we don't
                 // need to fully apply the MarginSet
-                contentView.setViewLayoutMarginEnd(R.id.notification_messaging,
-                        bindResult.mHeadingExtraMarginSet.getValue());
+                contentView.setViewLayoutMargin(R.id.notification_messaging, RemoteViews.MARGIN_END,
+                        bindResult.mHeadingExtraMarginSet.getValue(), TypedValue.COMPLEX_UNIT_PX);
             }
             contentView.setInt(R.id.status_bar_latest_event_content, "setLayoutColor",
                     mBuilder.isColorized(p)
@@ -8613,7 +8626,8 @@
             if (mBuilder.mN.hasLargeIcon()) {
                 endMargin = R.dimen.notification_media_image_margin_end;
             }
-            view.setViewLayoutMarginEndDimen(R.id.notification_main_column, endMargin);
+            view.setViewLayoutMarginDimen(R.id.notification_main_column,
+                            RemoteViews.MARGIN_END, endMargin);
             return view;
         }
 
@@ -8650,8 +8664,8 @@
 
         private void handleImage(RemoteViews contentView) {
             if (mBuilder.mN.hasLargeIcon()) {
-                contentView.setViewLayoutMarginEndDimen(R.id.line1, 0);
-                contentView.setViewLayoutMarginEndDimen(R.id.text, 0);
+                contentView.setViewLayoutMarginDimen(R.id.line1, RemoteViews.MARGIN_END, 0);
+                contentView.setViewLayoutMarginDimen(R.id.text, RemoteViews.MARGIN_END, 0);
             }
         }
 
@@ -11080,7 +11094,8 @@
                 if (viewId == R.id.notification_header) {
                     views.setInt(R.id.notification_header, "setTopLineExtraMarginEnd", marginEnd);
                 } else {
-                    views.setViewLayoutMarginEnd(viewId, marginEnd);
+                    views.setViewLayoutMargin(viewId, RemoteViews.MARGIN_END,
+                                    marginEnd, TypedValue.COMPLEX_UNIT_PX);
                 }
                 if (mRightIconVisible) {
                     views.setIntTag(viewId, R.id.tag_margin_end_when_icon_visible,
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 48d2dfe..ae1c894 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -1719,7 +1719,7 @@
                 synchronized (cache) {
                     // Return it if we already have a cached instance.
                     T service = (T) cache[mCacheIndex];
-                    if (service != null || gates[mCacheIndex] == ContextImpl.STATE_NOT_FOUND) {
+                    if (service != null) {
                         ret = service;
                         break; // exit the for (;;)
                     }
@@ -1729,7 +1729,9 @@
                     // Grr... if gate is STATE_READY, then this means we initialized the service
                     // once but someone cleared it.
                     // We start over from STATE_UNINITIALIZED.
-                    if (gates[mCacheIndex] == ContextImpl.STATE_READY) {
+                    // Similarly, if the previous attempt returned null, we'll retry again.
+                    if (gates[mCacheIndex] == ContextImpl.STATE_READY
+                            || gates[mCacheIndex] == ContextImpl.STATE_NOT_FOUND) {
                         gates[mCacheIndex] = ContextImpl.STATE_UNINITIALIZED;
                     }
 
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 4d0b90b..259c1a1 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -2624,6 +2624,42 @@
     /** @hide */
     @TestApi
     public static final int OPERATION_SET_USER_RESTRICTION = 10;
+    /** @hide */
+    @TestApi
+    public static final int OPERATION_SET_SYSTEM_SETTING = 11;
+    /** @hide */
+    @TestApi
+    public static final int OPERATION_SET_KEYGUARD_DISABLED = 12;
+    /** @hide */
+    @TestApi
+    public static final int OPERATION_SET_STATUS_BAR_DISABLED = 13;
+    /** @hide */
+    @TestApi
+    public static final int OPERATION_SET_SYSTEM_UPDATE_POLICY = 14;
+    /** @hide */
+    @TestApi
+    public static final int OPERATION_SET_APPLICATION_HIDDEN = 15;
+    /** @hide */
+    @TestApi
+    public static final int OPERATION_SET_APPLICATION_RESTRICTIONS = 16;
+    /** @hide */
+    @TestApi
+    public static final int OPERATION_SET_KEEP_UNINSTALLED_PACKAGES = 17;
+    /** @hide */
+    @TestApi
+    public static final int OPERATION_SET_LOCK_TASK_FEATURES = 18;
+    /** @hide */
+    @TestApi
+    public static final int OPERATION_SET_LOCK_TASK_PACKAGES = 19;
+    /** @hide */
+    @TestApi
+    public static final int OPERATION_SET_PACKAGES_SUSPENDED = 20;
+    /** @hide */
+    @TestApi
+    public static final int OPERATION_SET_TRUST_AGENT_CONFIGURATION = 21;
+    /** @hide */
+    @TestApi
+    public static final int OPERATION_SET_USER_CONTROL_DISABLED_PACKAGES = 22;
 
     private static final String PREFIX_OPERATION = "OPERATION_";
 
@@ -2638,7 +2674,19 @@
             OPERATION_REBOOT,
             OPERATION_WIPE_DATA,
             OPERATION_LOGOUT_USER,
-            OPERATION_SET_USER_RESTRICTION
+            OPERATION_SET_USER_RESTRICTION,
+            OPERATION_SET_SYSTEM_SETTING,
+            OPERATION_SET_KEYGUARD_DISABLED,
+            OPERATION_SET_STATUS_BAR_DISABLED,
+            OPERATION_SET_SYSTEM_UPDATE_POLICY,
+            OPERATION_SET_APPLICATION_HIDDEN,
+            OPERATION_SET_APPLICATION_RESTRICTIONS,
+            OPERATION_SET_KEEP_UNINSTALLED_PACKAGES,
+            OPERATION_SET_LOCK_TASK_FEATURES,
+            OPERATION_SET_LOCK_TASK_PACKAGES,
+            OPERATION_SET_PACKAGES_SUSPENDED,
+            OPERATION_SET_TRUST_AGENT_CONFIGURATION,
+            OPERATION_SET_USER_CONTROL_DISABLED_PACKAGES
     })
     @Retention(RetentionPolicy.SOURCE)
     public static @interface DevicePolicyOperation {
diff --git a/core/java/android/app/admin/DevicePolicySafetyChecker.java b/core/java/android/app/admin/DevicePolicySafetyChecker.java
index 1f8a933..b1a80c5 100644
--- a/core/java/android/app/admin/DevicePolicySafetyChecker.java
+++ b/core/java/android/app/admin/DevicePolicySafetyChecker.java
@@ -18,6 +18,8 @@
 import android.annotation.NonNull;
 import android.app.admin.DevicePolicyManager.DevicePolicyOperation;
 
+import com.android.internal.os.IResultReceiver;
+
 /**
  * Interface responsible to check if a {@link DevicePolicyManager} API can be safely executed.
  *
@@ -28,9 +30,7 @@
     /**
      * Returns whether the given {@code operation} can be safely executed at the moment.
      */
-    default boolean isDevicePolicyOperationSafe(@DevicePolicyOperation int operation) {
-        return true;
-    }
+    boolean isDevicePolicyOperationSafe(@DevicePolicyOperation int operation);
 
     /**
      * Returns a new exception for when the given {@code operation} cannot be safely executed.
@@ -39,4 +39,13 @@
     default UnsafeStateException newUnsafeStateException(@DevicePolicyOperation int operation) {
         return new UnsafeStateException(operation);
     }
+
+    /**
+     * Called when a request was made to factory reset the device, so it can be delayed if it's not
+     * safe to proceed.
+     *
+     * @param callback callback whose {@code send()} method must be called when it's safe to factory
+     * reset.
+     */
+    void onFactoryReset(IResultReceiver callback);
 }
diff --git a/core/java/android/app/assist/AssistStructure.aidl b/core/java/android/app/assist/AssistStructure.aidl
index ae0a34c..b997bbb 100644
--- a/core/java/android/app/assist/AssistStructure.aidl
+++ b/core/java/android/app/assist/AssistStructure.aidl
@@ -17,3 +17,8 @@
 package android.app.assist;
 
 parcelable AssistStructure;
+
+/**
+ * {@hide}
+ */
+parcelable AssistStructure.ViewNodeParcelable;
diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java
index c15504c..0f7fac4 100644
--- a/core/java/android/app/assist/AssistStructure.java
+++ b/core/java/android/app/assist/AssistStructure.java
@@ -271,7 +271,8 @@
                     + ", views=" + mNumWrittenViews
                     + ", level=" + (mCurViewStackPos+levelAdj));
             out.writeInt(VALIDATE_VIEW_TOKEN);
-            int flags = child.writeSelfToParcel(out, pwriter, mSanitizeOnWrite, mTmpMatrix);
+            int flags = child.writeSelfToParcel(out, pwriter, mSanitizeOnWrite,
+                    mTmpMatrix, /*willWriteChildren=*/true);
             mNumWrittenViews++;
             // If the child has children, push it on the stack to write them next.
             if ((flags&ViewNode.FLAGS_HAS_CHILDREN) != 0) {
@@ -724,11 +725,51 @@
         public ViewNode() {
         }
 
+        ViewNode(@NonNull Parcel in) {
+            initializeFromParcelWithoutChildren(in, /*preader=*/null, /*tmpMatrix=*/null);
+        }
+
         ViewNode(ParcelTransferReader reader, int nestingLevel) {
             final Parcel in = reader.readParcel(VALIDATE_VIEW_TOKEN, nestingLevel);
             reader.mNumReadViews++;
-            final PooledStringReader preader = reader.mStringReader;
-            mClassName = preader.readString();
+            initializeFromParcelWithoutChildren(in, Objects.requireNonNull(reader.mStringReader),
+                    Objects.requireNonNull(reader.mTmpMatrix));
+            if ((mFlags & FLAGS_HAS_CHILDREN) != 0) {
+                final int numChildren = in.readInt();
+                if (DEBUG_PARCEL_TREE || DEBUG_PARCEL_CHILDREN) {
+                    Log.d(TAG,
+                            "Preparing to read " + numChildren
+                                    + " children: @ #" + reader.mNumReadViews
+                                    + ", level " + nestingLevel);
+                }
+                mChildren = new ViewNode[numChildren];
+                for (int i = 0; i < numChildren; i++) {
+                    mChildren[i] = new ViewNode(reader, nestingLevel + 1);
+                }
+            }
+        }
+
+        private static void writeString(@NonNull Parcel out, @Nullable PooledStringWriter pwriter,
+                @Nullable String str) {
+            if (pwriter != null) {
+                pwriter.writeString(str);
+            } else {
+                out.writeString(str);
+            }
+        }
+
+        @Nullable
+        private static String readString(@NonNull Parcel in, @Nullable PooledStringReader preader) {
+            if (preader != null) {
+                return preader.readString();
+            }
+            return in.readString();
+        }
+
+        // This does not read the child nodes.
+        void initializeFromParcelWithoutChildren(Parcel in, @Nullable PooledStringReader preader,
+                @Nullable float[] tmpMatrix) {
+            mClassName = readString(in, preader);
             mFlags = in.readInt();
             final int flags = mFlags;
             mAutofillFlags = in.readInt();
@@ -736,10 +777,10 @@
             if ((flags&FLAGS_HAS_ID) != 0) {
                 mId = in.readInt();
                 if (mId != View.NO_ID) {
-                    mIdEntry = preader.readString();
+                    mIdEntry = readString(in, preader);
                     if (mIdEntry != null) {
-                        mIdType = preader.readString();
-                        mIdPackage = preader.readString();
+                        mIdType = readString(in, preader);
+                        mIdPackage = readString(in, preader);
                     }
                 }
             }
@@ -784,10 +825,10 @@
                     mMaxLength = in.readInt();
                 }
                 if ((autofillFlags & AUTOFILL_FLAGS_HAS_TEXT_ID_ENTRY) != 0) {
-                    mTextIdEntry = preader.readString();
+                    mTextIdEntry = readString(in, preader);
                 }
                 if ((autofillFlags & AUTOFILL_FLAGS_HAS_HINT_ID_ENTRY) != 0) {
-                    mHintIdEntry = preader.readString();
+                    mHintIdEntry = readString(in, preader);
                 }
             }
             if ((flags&FLAGS_HAS_LARGE_COORDS) != 0) {
@@ -809,8 +850,11 @@
             }
             if ((flags&FLAGS_HAS_MATRIX) != 0) {
                 mMatrix = new Matrix();
-                in.readFloatArray(reader.mTmpMatrix);
-                mMatrix.setValues(reader.mTmpMatrix);
+                if (tmpMatrix == null) {
+                    tmpMatrix = new float[9];
+                }
+                in.readFloatArray(tmpMatrix);
+                mMatrix.setValues(tmpMatrix);
             }
             if ((flags&FLAGS_HAS_ELEVATION) != 0) {
                 mElevation = in.readFloat();
@@ -839,21 +883,16 @@
             if ((flags&FLAGS_HAS_EXTRAS) != 0) {
                 mExtras = in.readBundle();
             }
-            if ((flags&FLAGS_HAS_CHILDREN) != 0) {
-                final int NCHILDREN = in.readInt();
-                if (DEBUG_PARCEL_TREE || DEBUG_PARCEL_CHILDREN) Log.d(TAG,
-                        "Preparing to read " + NCHILDREN
-                                + " children: @ #" + reader.mNumReadViews
-                                + ", level " + nestingLevel);
-                mChildren = new ViewNode[NCHILDREN];
-                for (int i=0; i<NCHILDREN; i++) {
-                    mChildren[i] = new ViewNode(reader, nestingLevel + 1);
-                }
-            }
         }
 
-        int writeSelfToParcel(Parcel out, PooledStringWriter pwriter, boolean sanitizeOnWrite,
-                float[] tmpMatrix) {
+        /**
+         * This does not write the child nodes.
+         *
+         * @param willWriteChildren whether child nodes will be written to the parcel or not after
+         *                          calling this method.
+         */
+        int writeSelfToParcel(@NonNull Parcel out, @Nullable PooledStringWriter pwriter,
+                boolean sanitizeOnWrite, @Nullable float[] tmpMatrix, boolean willWriteChildren) {
             // Guard used to skip non-sanitized data when writing for autofill.
             boolean writeSensitive = true;
 
@@ -903,7 +942,7 @@
             if (mExtras != null) {
                 flags |= FLAGS_HAS_EXTRAS;
             }
-            if (mChildren != null) {
+            if (mChildren != null && willWriteChildren) {
                 flags |= FLAGS_HAS_CHILDREN;
             }
             if (mAutofillId != null) {
@@ -946,7 +985,7 @@
                 autofillFlags |= AUTOFILL_FLAGS_HAS_HINT_ID_ENTRY;
             }
 
-            pwriter.writeString(mClassName);
+            writeString(out, pwriter, mClassName);
 
             int writtenFlags = flags;
             if (autofillFlags != 0 && (mSanitized || !sanitizeOnWrite)) {
@@ -966,10 +1005,10 @@
             if ((flags&FLAGS_HAS_ID) != 0) {
                 out.writeInt(mId);
                 if (mId != View.NO_ID) {
-                    pwriter.writeString(mIdEntry);
+                    writeString(out, pwriter, mIdEntry);
                     if (mIdEntry != null) {
-                        pwriter.writeString(mIdType);
-                        pwriter.writeString(mIdPackage);
+                        writeString(out, pwriter, mIdType);
+                        writeString(out, pwriter, mIdPackage);
                     }
                 }
             }
@@ -1020,10 +1059,10 @@
                     out.writeInt(mMaxLength);
                 }
                 if ((autofillFlags & AUTOFILL_FLAGS_HAS_TEXT_ID_ENTRY) != 0) {
-                    pwriter.writeString(mTextIdEntry);
+                    writeString(out, pwriter, mTextIdEntry);
                 }
                 if ((autofillFlags & AUTOFILL_FLAGS_HAS_HINT_ID_ENTRY) != 0) {
-                    pwriter.writeString(mHintIdEntry);
+                    writeString(out, pwriter, mHintIdEntry);
                 }
             }
             if ((flags&FLAGS_HAS_LARGE_COORDS) != 0) {
@@ -1040,6 +1079,9 @@
                 out.writeInt(mScrollY);
             }
             if ((flags&FLAGS_HAS_MATRIX) != 0) {
+                if (tmpMatrix == null) {
+                    tmpMatrix = new float[9];
+                }
                 mMatrix.getValues(tmpMatrix);
                 out.writeFloatArray(tmpMatrix);
             }
@@ -1695,6 +1737,57 @@
     }
 
     /**
+     * A parcelable wrapper class around {@link ViewNode}.
+     *
+     * <p>This class, when parceled and unparceled, does not carry the child nodes.
+     *
+     * @hide
+     */
+    public static final class ViewNodeParcelable implements Parcelable {
+
+        @NonNull
+        private final ViewNode mViewNode;
+
+        public ViewNodeParcelable(@NonNull ViewNode viewNode) {
+            mViewNode = viewNode;
+        }
+
+        public ViewNodeParcelable(@NonNull Parcel in) {
+            mViewNode = new ViewNode(in);
+        }
+
+        @NonNull
+        public ViewNode getViewNode() {
+            return mViewNode;
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(@NonNull Parcel parcel, int flags) {
+            mViewNode.writeSelfToParcel(parcel, /*pwriter=*/null, /*sanitizeOnWrite=*/false,
+                    /*tmpMatrix*/null, /*willWriteChildren=*/ false);
+        }
+
+        @NonNull
+        public static final Parcelable.Creator<ViewNodeParcelable> CREATOR =
+                new Parcelable.Creator<ViewNodeParcelable>() {
+                    @Override
+                    public ViewNodeParcelable createFromParcel(@NonNull Parcel in) {
+                        return new ViewNodeParcelable(in);
+                    }
+
+                    @Override
+                    public ViewNodeParcelable[] newArray(int size) {
+                        return new ViewNodeParcelable[size];
+                    }
+                };
+    }
+
+    /**
      * POJO used to override some autofill-related values when the node is parcelized.
      *
      * @hide
@@ -1704,17 +1797,35 @@
         public AutofillValue value;
     }
 
-    static class ViewNodeBuilder extends ViewStructure {
+    /**
+     * @hide
+     */
+    public static class ViewNodeBuilder extends ViewStructure {
         final AssistStructure mAssist;
         final ViewNode mNode;
         final boolean mAsync;
 
+        /**
+         * Used to instantiate a builder for a stand-alone {@link ViewNode} which is not associated
+         * to a properly created {@link AssistStructure}.
+         */
+        public ViewNodeBuilder() {
+            mAssist = new AssistStructure();
+            mNode = new ViewNode();
+            mAsync = false;
+        }
+
         ViewNodeBuilder(AssistStructure assist, ViewNode node, boolean async) {
             mAssist = assist;
             mNode = node;
             mAsync = async;
         }
 
+        @NonNull
+        public ViewNode getViewNode() {
+            return mNode;
+        }
+
         @Override
         public void setId(int id, String packageName, String typeName, String entryName) {
             mNode.mId = id;
diff --git a/core/java/android/app/people/OWNERS b/core/java/android/app/people/OWNERS
new file mode 100644
index 0000000..7371a88
--- /dev/null
+++ b/core/java/android/app/people/OWNERS
@@ -0,0 +1,4 @@
+# Bug component: 978868
+
+danningc@google.com
+juliacr@google.com
\ No newline at end of file
diff --git a/core/java/android/app/role/IRoleController.aidl b/core/java/android/app/role/IRoleController.aidl
index fbf79a4..8a43d7f 100644
--- a/core/java/android/app/role/IRoleController.aidl
+++ b/core/java/android/app/role/IRoleController.aidl
@@ -16,9 +16,7 @@
 
 package android.app.role;
 
-import android.app.role.RolePrivileges;
 import android.os.RemoteCallback;
-import com.android.internal.infra.AndroidFuture;
 
 /**
  * @hide
@@ -42,6 +40,4 @@
             in RemoteCallback callback);
 
     void isRoleVisible(in String roleName, in RemoteCallback callback);
-
-    void getRolePrivileges(in String roleName, in AndroidFuture<RolePrivileges> callback);
 }
diff --git a/core/java/android/app/role/RoleControllerService.java b/core/java/android/app/role/RoleControllerService.java
index 4c6aa8d..d92c956 100644
--- a/core/java/android/app/role/RoleControllerService.java
+++ b/core/java/android/app/role/RoleControllerService.java
@@ -16,8 +16,6 @@
 
 package android.app.role;
 
-import static java.util.Collections.emptyList;
-
 import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
@@ -34,7 +32,6 @@
 import android.os.RemoteCallback;
 import android.os.UserHandle;
 
-import com.android.internal.infra.AndroidFuture;
 import com.android.internal.util.Preconditions;
 import com.android.internal.util.function.pooled.PooledLambda;
 
@@ -180,20 +177,6 @@
                 boolean visible = onIsRoleVisible(roleName);
                 callback.sendResult(visible ? Bundle.EMPTY : null);
             }
-
-            @Override
-            public void getRolePrivileges(String roleName, AndroidFuture<RolePrivileges> callback) {
-                enforceCallingPermission(Manifest.permission.MANAGE_ROLE_HOLDERS, null);
-
-                Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
-                Objects.requireNonNull(callback, "callback cannot be null");
-
-                try {
-                    callback.complete(RoleControllerService.this.getRolePrivileges(roleName));
-                } catch (Throwable t) {
-                    callback.completeExceptionally(t);
-                }
-            }
         };
     }
 
@@ -319,14 +302,4 @@
      * @return whether the role should be visible to user
      */
     public abstract boolean onIsRoleVisible(@NonNull String roleName);
-
-    /**
-     * Queries the {@link RolePrivileges privileges} that the given role grants.
-     *
-     * @param roleName name of the role to quey for
-     * @return the {@link RolePrivileges} for the role
-     */
-    public @NonNull RolePrivileges getRolePrivileges(@NonNull String roleName) {
-        return new RolePrivileges(emptyList(), emptyList(), emptyList(), emptyList());
-    }
 }
diff --git a/core/java/android/app/role/RolePrivileges.java b/core/java/android/app/role/RolePrivileges.java
deleted file mode 100644
index 5fc0b0a08..0000000
--- a/core/java/android/app/role/RolePrivileges.java
+++ /dev/null
@@ -1,221 +0,0 @@
-/*
- * 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.role;
-
-import android.annotation.NonNull;
-import android.annotation.SystemApi;
-import android.os.Parcelable;
-
-import com.android.internal.util.DataClass;
-
-import java.util.List;
-
-/**
- * Describes a set of privileges granted by a {@link RoleManager role}
- *
- * @hide
- */
-@SystemApi
-@DataClass
-public final class RolePrivileges implements Parcelable {
-
-    /**
-     * An identifier of a role holder app being granted the
-     * {@link android.service.notification.NotificationListenerService Notification Access}
-     * privilege.
-     */
-    public static final String CAPABILITY_NOTIFICATION_LISTENER =
-            "android.app.role.capability.NOTIFICATION_LISTENER";
-
-    /**
-     * Permissions granted to the role holder(s).
-     */
-    private @NonNull List<String> mPermissions;
-    /**
-     * Appop permissions granted to the role holder(s).
-     */
-    private @NonNull List<String> mAppOpPermissions;
-    /**
-     * Appops granted to the role holder(s).
-     */
-    private @NonNull List<String> mAppOps;
-    /**
-     * Special access granted to the role holder(s).
-     *
-     * @see #CAPABILITY_NOTIFICATION_LISTENER
-     */
-    private @NonNull List<String> mCapabilities;
-
-
-
-    // Code below generated by codegen v1.0.22.
-    //
-    // DO NOT MODIFY!
-    // CHECKSTYLE:OFF Generated code
-    //
-    // To regenerate run:
-    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/app/role/RolePrivileges.java
-    //
-    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
-    //   Settings > Editor > Code Style > Formatter Control
-    //@formatter:off
-
-
-    /**
-     * Creates a new RolePrivileges.
-     *
-     * @param permissions
-     *   Permissions granted to the role holder(s).
-     * @param appOpPermissions
-     *   Appop permissions granted to the role holder(s).
-     * @param appOps
-     *   Appops granted to the role holder(s).
-     * @param capabilities
-     *   Special access granted to the role holder(s).
-     */
-    @DataClass.Generated.Member
-    public RolePrivileges(
-            @NonNull List<String> permissions,
-            @NonNull List<String> appOpPermissions,
-            @NonNull List<String> appOps,
-            @NonNull List<String> capabilities) {
-        this.mPermissions = permissions;
-        com.android.internal.util.AnnotationValidations.validate(
-                NonNull.class, null, mPermissions);
-        this.mAppOpPermissions = appOpPermissions;
-        com.android.internal.util.AnnotationValidations.validate(
-                NonNull.class, null, mAppOpPermissions);
-        this.mAppOps = appOps;
-        com.android.internal.util.AnnotationValidations.validate(
-                NonNull.class, null, mAppOps);
-        this.mCapabilities = capabilities;
-        com.android.internal.util.AnnotationValidations.validate(
-                NonNull.class, null, mCapabilities);
-
-        // onConstructed(); // You can define this method to get a callback
-    }
-
-    /**
-     * Permissions granted to the role holder(s).
-     */
-    @DataClass.Generated.Member
-    public @NonNull List<String> getPermissions() {
-        return mPermissions;
-    }
-
-    /**
-     * Appop permissions granted to the role holder(s).
-     */
-    @DataClass.Generated.Member
-    public @NonNull List<String> getAppOpPermissions() {
-        return mAppOpPermissions;
-    }
-
-    /**
-     * Appops granted to the role holder(s).
-     */
-    @DataClass.Generated.Member
-    public @NonNull List<String> getAppOps() {
-        return mAppOps;
-    }
-
-    /**
-     * Special access granted to the role holder(s).
-     *
-     * @see #CAPABILITY_NOTIFICATION_LISTENER
-     */
-    @DataClass.Generated.Member
-    public @NonNull List<String> getCapabilities() {
-        return mCapabilities;
-    }
-
-    @Override
-    @DataClass.Generated.Member
-    public void writeToParcel(@NonNull android.os.Parcel dest, int flags) {
-        // You can override field parcelling by defining methods like:
-        // void parcelFieldName(Parcel dest, int flags) { ... }
-
-        dest.writeStringList(mPermissions);
-        dest.writeStringList(mAppOpPermissions);
-        dest.writeStringList(mAppOps);
-        dest.writeStringList(mCapabilities);
-    }
-
-    @Override
-    @DataClass.Generated.Member
-    public int describeContents() { return 0; }
-
-    /** @hide */
-    @SuppressWarnings({"unchecked", "RedundantCast"})
-    @DataClass.Generated.Member
-    /* package-private */ RolePrivileges(@NonNull android.os.Parcel in) {
-        // You can override field unparcelling by defining methods like:
-        // static FieldType unparcelFieldName(Parcel in) { ... }
-
-        List<String> permissions = new java.util.ArrayList<>();
-        in.readStringList(permissions);
-        List<String> appOpPermissions = new java.util.ArrayList<>();
-        in.readStringList(appOpPermissions);
-        List<String> appOps = new java.util.ArrayList<>();
-        in.readStringList(appOps);
-        List<String> capabilities = new java.util.ArrayList<>();
-        in.readStringList(capabilities);
-
-        this.mPermissions = permissions;
-        com.android.internal.util.AnnotationValidations.validate(
-                NonNull.class, null, mPermissions);
-        this.mAppOpPermissions = appOpPermissions;
-        com.android.internal.util.AnnotationValidations.validate(
-                NonNull.class, null, mAppOpPermissions);
-        this.mAppOps = appOps;
-        com.android.internal.util.AnnotationValidations.validate(
-                NonNull.class, null, mAppOps);
-        this.mCapabilities = capabilities;
-        com.android.internal.util.AnnotationValidations.validate(
-                NonNull.class, null, mCapabilities);
-
-        // onConstructed(); // You can define this method to get a callback
-    }
-
-    @DataClass.Generated.Member
-    public static final @NonNull Parcelable.Creator<RolePrivileges> CREATOR
-            = new Parcelable.Creator<RolePrivileges>() {
-        @Override
-        public RolePrivileges[] newArray(int size) {
-            return new RolePrivileges[size];
-        }
-
-        @Override
-        public RolePrivileges createFromParcel(@NonNull android.os.Parcel in) {
-            return new RolePrivileges(in);
-        }
-    };
-
-    @DataClass.Generated(
-            time = 1607546429137L,
-            codegenVersion = "1.0.22",
-            sourceFile = "frameworks/base/core/java/android/app/role/RolePrivileges.java",
-            inputSignatures = "public static final  java.lang.String CAPABILITY_NOTIFICATION_LISTENER\nprivate @android.annotation.NonNull java.util.List<java.lang.String> mPermissions\nprivate @android.annotation.NonNull java.util.List<java.lang.String> mAppOpPermissions\nprivate @android.annotation.NonNull java.util.List<java.lang.String> mAppOps\nprivate @android.annotation.NonNull java.util.List<java.lang.String> mCapabilities\nclass RolePrivileges extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass")
-    @Deprecated
-    private void __metadata() {}
-
-
-    //@formatter:on
-    // End of generated code
-
-}
diff --git a/core/java/android/app/servertransaction/LaunchActivityItem.java b/core/java/android/app/servertransaction/LaunchActivityItem.java
index 3758cb4..73a9cec 100644
--- a/core/java/android/app/servertransaction/LaunchActivityItem.java
+++ b/core/java/android/app/servertransaction/LaunchActivityItem.java
@@ -21,6 +21,7 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityClient;
+import android.app.ActivityOptions;
 import android.app.ActivityThread.ActivityClientRecord;
 import android.app.ClientTransactionHandler;
 import android.app.IActivityClientController;
@@ -66,6 +67,7 @@
     private PersistableBundle mPersistentState;
     private List<ResultInfo> mPendingResults;
     private List<ReferrerIntent> mPendingNewIntents;
+    private ActivityOptions mActivityOptions;
     private boolean mIsForward;
     private ProfilerInfo mProfilerInfo;
     private IBinder mAssistToken;
@@ -92,8 +94,8 @@
         Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
         ActivityClientRecord r = new ActivityClientRecord(token, mIntent, mIdent, mInfo,
                 mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor, mState, mPersistentState,
-                mPendingResults, mPendingNewIntents, mIsForward,
-                mProfilerInfo, client, mAssistToken, mFixedRotationAdjustments);
+                mPendingResults, mPendingNewIntents, mActivityOptions, mIsForward, mProfilerInfo,
+                client, mAssistToken, mFixedRotationAdjustments);
         client.handleLaunchActivity(r, pendingActions, null /* customIntent */);
         Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
     }
@@ -114,8 +116,9 @@
             Configuration curConfig, Configuration overrideConfig, CompatibilityInfo compatInfo,
             String referrer, IVoiceInteractor voiceInteractor, int procState, Bundle state,
             PersistableBundle persistentState, List<ResultInfo> pendingResults,
-            List<ReferrerIntent> pendingNewIntents, boolean isForward, ProfilerInfo profilerInfo,
-            IBinder assistToken, IActivityClientController activityClientController,
+            List<ReferrerIntent> pendingNewIntents, ActivityOptions activityOptions,
+            boolean isForward, ProfilerInfo profilerInfo, IBinder assistToken,
+            IActivityClientController activityClientController,
             FixedRotationAdjustments fixedRotationAdjustments) {
         LaunchActivityItem instance = ObjectPool.obtain(LaunchActivityItem.class);
         if (instance == null) {
@@ -123,8 +126,8 @@
         }
         setValues(instance, intent, ident, info, curConfig, overrideConfig, compatInfo, referrer,
                 voiceInteractor, procState, state, persistentState, pendingResults,
-                pendingNewIntents, isForward, profilerInfo, assistToken, activityClientController,
-                fixedRotationAdjustments);
+                pendingNewIntents, activityOptions, isForward, profilerInfo, assistToken,
+                activityClientController, fixedRotationAdjustments);
 
         return instance;
     }
@@ -132,7 +135,7 @@
     @Override
     public void recycle() {
         setValues(this, null, 0, null, null, null, null, null, null, 0, null, null, null, null,
-                false, null, null, null, null);
+                null, false, null, null, null, null);
         ObjectPool.recycle(this);
     }
 
@@ -155,6 +158,7 @@
         dest.writePersistableBundle(mPersistentState);
         dest.writeTypedList(mPendingResults, flags);
         dest.writeTypedList(mPendingNewIntents, flags);
+        dest.writeBundle(mActivityOptions != null ? mActivityOptions.toBundle() : null);
         dest.writeBoolean(mIsForward);
         dest.writeTypedObject(mProfilerInfo, flags);
         dest.writeStrongBinder(mAssistToken);
@@ -172,7 +176,8 @@
                 in.readBundle(getClass().getClassLoader()),
                 in.readPersistableBundle(getClass().getClassLoader()),
                 in.createTypedArrayList(ResultInfo.CREATOR),
-                in.createTypedArrayList(ReferrerIntent.CREATOR), in.readBoolean(),
+                in.createTypedArrayList(ReferrerIntent.CREATOR),
+                ActivityOptions.fromBundle(in.readBundle()), in.readBoolean(),
                 in.readTypedObject(ProfilerInfo.CREATOR),
                 in.readStrongBinder(),
                 IActivityClientController.Stub.asInterface(in.readStrongBinder()),
@@ -210,6 +215,7 @@
                 && areBundlesEqualRoughly(mPersistentState, other.mPersistentState)
                 && Objects.equals(mPendingResults, other.mPendingResults)
                 && Objects.equals(mPendingNewIntents, other.mPendingNewIntents)
+                && (mActivityOptions == null) == (other.mActivityOptions == null)
                 && mIsForward == other.mIsForward
                 && Objects.equals(mProfilerInfo, other.mProfilerInfo)
                 && Objects.equals(mAssistToken, other.mAssistToken)
@@ -230,6 +236,7 @@
         result = 31 * result + getRoughBundleHashCode(mPersistentState);
         result = 31 * result + Objects.hashCode(mPendingResults);
         result = 31 * result + Objects.hashCode(mPendingNewIntents);
+        result = 31 * result + (mActivityOptions != null ? 1 : 0);
         result = 31 * result + (mIsForward ? 1 : 0);
         result = 31 * result + Objects.hashCode(mProfilerInfo);
         result = 31 * result + Objects.hashCode(mAssistToken);
@@ -268,9 +275,9 @@
                 + ",curConfig=" + mCurConfig + ",overrideConfig=" + mOverrideConfig
                 + ",referrer=" + mReferrer + ",procState=" + mProcState + ",state=" + mState
                 + ",persistentState=" + mPersistentState + ",pendingResults=" + mPendingResults
-                + ",pendingNewIntents=" + mPendingNewIntents + ",profilerInfo=" + mProfilerInfo
-                + ",assistToken=" + mAssistToken + ",rotationAdj=" + mFixedRotationAdjustments
-                + "}";
+                + ",pendingNewIntents=" + mPendingNewIntents + ",options=" + mActivityOptions
+                + ",profilerInfo=" + mProfilerInfo + ",assistToken=" + mAssistToken
+                + ",rotationAdj=" + mFixedRotationAdjustments + "}";
     }
 
     // Using the same method to set and clear values to make sure we don't forget anything
@@ -279,8 +286,8 @@
             CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor,
             int procState, Bundle state, PersistableBundle persistentState,
             List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
-            boolean isForward, ProfilerInfo profilerInfo, IBinder assistToken,
-            IActivityClientController activityClientController,
+            ActivityOptions activityOptions, boolean isForward, ProfilerInfo profilerInfo,
+            IBinder assistToken, IActivityClientController activityClientController,
             FixedRotationAdjustments fixedRotationAdjustments) {
         instance.mIntent = intent;
         instance.mIdent = ident;
@@ -295,6 +302,7 @@
         instance.mPersistentState = persistentState;
         instance.mPendingResults = pendingResults;
         instance.mPendingNewIntents = pendingNewIntents;
+        instance.mActivityOptions = activityOptions;
         instance.mIsForward = isForward;
         instance.mProfilerInfo = profilerInfo;
         instance.mAssistToken = assistToken;
diff --git a/core/java/android/app/servertransaction/StartActivityItem.java b/core/java/android/app/servertransaction/StartActivityItem.java
index 483f9de..15f65f6 100644
--- a/core/java/android/app/servertransaction/StartActivityItem.java
+++ b/core/java/android/app/servertransaction/StartActivityItem.java
@@ -20,6 +20,7 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.app.ActivityOptions;
 import android.app.ActivityThread.ActivityClientRecord;
 import android.app.ClientTransactionHandler;
 import android.os.Parcel;
@@ -33,11 +34,13 @@
 
     private static final String TAG = "StartActivityItem";
 
+    private ActivityOptions mActivityOptions;
+
     @Override
     public void execute(ClientTransactionHandler client, ActivityClientRecord r,
             PendingTransactionActions pendingActions) {
         Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "startActivityItem");
-        client.handleStartActivity(r, pendingActions);
+        client.handleStartActivity(r, pendingActions, mActivityOptions);
         Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
     }
 
@@ -52,11 +55,12 @@
     private StartActivityItem() {}
 
     /** Obtain an instance initialized with provided params. */
-    public static StartActivityItem obtain() {
+    public static StartActivityItem obtain(ActivityOptions activityOptions) {
         StartActivityItem instance = ObjectPool.obtain(StartActivityItem.class);
         if (instance == null) {
             instance = new StartActivityItem();
         }
+        instance.mActivityOptions = activityOptions;
 
         return instance;
     }
@@ -64,6 +68,7 @@
     @Override
     public void recycle() {
         super.recycle();
+        mActivityOptions = null;
         ObjectPool.recycle(this);
     }
 
@@ -73,12 +78,12 @@
     /** Write to Parcel. */
     @Override
     public void writeToParcel(Parcel dest, int flags) {
-        // Empty
+        dest.writeBundle(mActivityOptions != null ? mActivityOptions.toBundle() : null);
     }
 
     /** Read from Parcel. */
     private StartActivityItem(Parcel in) {
-        // Empty
+        mActivityOptions = ActivityOptions.fromBundle(in.readBundle());
     }
 
     public static final @NonNull Creator<StartActivityItem> CREATOR =
@@ -100,17 +105,20 @@
         if (o == null || getClass() != o.getClass()) {
             return false;
         }
-        return true;
+        final StartActivityItem other = (StartActivityItem) o;
+        return (mActivityOptions == null) == (other.mActivityOptions == null);
     }
 
     @Override
     public int hashCode() {
-        return 17;
+        int result = 17;
+        result = 31 * result + (mActivityOptions != null ? 1 : 0);
+        return result;
     }
 
     @Override
     public String toString() {
-        return "StartActivityItem{}";
+        return "StartActivityItem{options=" + mActivityOptions + "}";
     }
 }
 
diff --git a/core/java/android/app/servertransaction/TransactionExecutor.java b/core/java/android/app/servertransaction/TransactionExecutor.java
index 3dcf2cb..25ff8a7 100644
--- a/core/java/android/app/servertransaction/TransactionExecutor.java
+++ b/core/java/android/app/servertransaction/TransactionExecutor.java
@@ -218,7 +218,8 @@
                             null /* customIntent */);
                     break;
                 case ON_START:
-                    mTransactionHandler.handleStartActivity(r, mPendingActions);
+                    mTransactionHandler.handleStartActivity(r, mPendingActions,
+                            null /* activityOptions */);
                     break;
                 case ON_RESUME:
                     mTransactionHandler.handleResumeActivity(r, false /* finalStateRequest */,
diff --git a/core/java/android/app/servertransaction/TransactionExecutorHelper.java b/core/java/android/app/servertransaction/TransactionExecutorHelper.java
index 56bf59b..92f7dee 100644
--- a/core/java/android/app/servertransaction/TransactionExecutorHelper.java
+++ b/core/java/android/app/servertransaction/TransactionExecutorHelper.java
@@ -186,7 +186,7 @@
         switch (prevState) {
             // TODO(lifecycler): Extend to support all possible states.
             case ON_START:
-                lifecycleItem = StartActivityItem.obtain();
+                lifecycleItem = StartActivityItem.obtain(null /* activityOptions */);
                 break;
             case ON_PAUSE:
                 lifecycleItem = PauseActivityItem.obtain();
diff --git a/core/java/android/app/time/LocationTimeZoneManager.java b/core/java/android/app/time/LocationTimeZoneManager.java
new file mode 100644
index 0000000..d909c0c
--- /dev/null
+++ b/core/java/android/app/time/LocationTimeZoneManager.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 android.app.time;
+
+/**
+ * Constants related to the LocationTimeZoneManager service that are used by shell commands and
+ * tests.
+ *
+ * @hide
+ */
+public final class LocationTimeZoneManager {
+
+    /**
+     * The name of the primary location time zone provider, used for shell commands.
+     */
+    public static final String PRIMARY_PROVIDER_NAME = "primary";
+
+    /**
+     * The name of the secondary location time zone provider, used for shell commands.
+     */
+    public static final String SECONDARY_PROVIDER_NAME = "secondary";
+
+    /**
+     * The name of the service for shell commands
+     */
+    public static final String SHELL_COMMAND_SERVICE_NAME = "location_time_zone_manager";
+
+    /**
+     * Shell command that starts the service (after stop).
+     */
+    public static final String SHELL_COMMAND_START = "start";
+
+    /**
+     * Shell command that stops the service.
+     */
+    public static final String SHELL_COMMAND_STOP = "stop";
+
+    /**
+     * Shell command that sends test commands to a provider
+     */
+    public static final String SHELL_COMMAND_SEND_PROVIDER_TEST_COMMAND =
+            "send_provider_test_command";
+
+    /**
+     * Simulated provider test command that simulates the bind succeeding.
+     */
+    public static final String SIMULATED_PROVIDER_TEST_COMMAND_ON_BIND = "on_bind";
+
+    /**
+     * Simulated provider test command that simulates the provider unbinding.
+     */
+    public static final String SIMULATED_PROVIDER_TEST_COMMAND_ON_UNBIND = "on_unbind";
+
+    /**
+     * Simulated provider test command that simulates the provider entering the "permanent failure"
+     * state.
+     */
+    public static final String SIMULATED_PROVIDER_TEST_COMMAND_PERM_FAILURE = "perm_fail";
+
+    /**
+     * Simulated provider test command that simulates the provider entering the "success" (time
+     * zone(s) detected) state.
+     */
+    public static final String SIMULATED_PROVIDER_TEST_COMMAND_SUCCESS = "success";
+
+    /**
+     * Argument for {@link #SIMULATED_PROVIDER_TEST_COMMAND_SUCCESS} to specify TZDB time zone IDs.
+     */
+    public static final String SIMULATED_PROVIDER_TEST_COMMAND_SUCCESS_ARG_KEY_TZ = "tz";
+
+    /**
+     * Simulated provider test command that simulates the provider entering the "uncertain"
+     * state.
+     */
+    public static final String SIMULATED_PROVIDER_TEST_COMMAND_UNCERTAIN = "uncertain";
+
+    private static final String SYSTEM_PROPERTY_KEY_PROVIDER_MODE_OVERRIDE_PREFIX =
+            "persist.sys.geotz.";
+
+    /**
+     * The name of the system property that can be used to set the primary provider into test mode
+     * (value = {@link #SYSTEM_PROPERTY_VALUE_PROVIDER_MODE_SIMULATED}) or disabled (value = {@link
+     * #SYSTEM_PROPERTY_VALUE_PROVIDER_MODE_DISABLED}).
+     */
+    public static final String SYSTEM_PROPERTY_KEY_PROVIDER_MODE_OVERRIDE_PRIMARY =
+            SYSTEM_PROPERTY_KEY_PROVIDER_MODE_OVERRIDE_PREFIX + PRIMARY_PROVIDER_NAME;
+
+    /**
+     * The name of the system property that can be used to set the secondary provider into test mode
+     * (value = {@link #SYSTEM_PROPERTY_VALUE_PROVIDER_MODE_SIMULATED}) or disabled (value = {@link
+     * #SYSTEM_PROPERTY_VALUE_PROVIDER_MODE_DISABLED}).
+     */
+    public static final String SYSTEM_PROPERTY_KEY_PROVIDER_MODE_OVERRIDE_SECONDARY =
+            SYSTEM_PROPERTY_KEY_PROVIDER_MODE_OVERRIDE_PREFIX + SECONDARY_PROVIDER_NAME;
+
+    /**
+     * The value of the provider mode system property to put a provider into test mode.
+     */
+    public static final String SYSTEM_PROPERTY_VALUE_PROVIDER_MODE_SIMULATED = "simulated";
+
+    /**
+     * The value of the provider mode system property to put a provider into disabled mode.
+     */
+    public static final String SYSTEM_PROPERTY_VALUE_PROVIDER_MODE_DISABLED = "disabled";
+
+    private LocationTimeZoneManager() {
+        // No need to instantiate.
+    }
+}
diff --git a/core/java/android/app/timezonedetector/TimeZoneDetector.java b/core/java/android/app/timezonedetector/TimeZoneDetector.java
index 486232d..e0b7ffe 100644
--- a/core/java/android/app/timezonedetector/TimeZoneDetector.java
+++ b/core/java/android/app/timezonedetector/TimeZoneDetector.java
@@ -29,6 +29,13 @@
 @SystemService(Context.TIME_ZONE_DETECTOR_SERVICE)
 public interface TimeZoneDetector {
 
+    /** @hide */
+    String SHELL_COMMAND_SUGGEST_GEO_LOCATION_TIME_ZONE = "suggest_geo_location_time_zone";
+    /** @hide */
+    String SHELL_COMMAND_SUGGEST_MANUAL_TIME_ZONE = "suggest_manual_time_zone";
+    /** @hide */
+    String SHELL_COMMAND_SUGGEST_TELEPHONY_TIME_ZONE = "suggest_telephony_time_zone";
+
     /**
      * A shared utility method to create a {@link ManualTimeZoneSuggestion}.
      *
diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java
index 57d1411..4161096 100644
--- a/core/java/android/bluetooth/BluetoothHeadset.java
+++ b/core/java/android/bluetooth/BluetoothHeadset.java
@@ -682,6 +682,48 @@
     }
 
     /**
+     * Checks whether the headset supports some form of noise reduction
+     *
+     * @param device Bluetooth device
+     * @return true if echo cancellation and/or noise reduction is supported, false otherwise
+     */
+    @RequiresPermission(Manifest.permission.BLUETOOTH)
+    public boolean isNoiseReductionSupported(@NonNull BluetoothDevice device) {
+        if (DBG) log("isNoiseReductionSupported()");
+        final IBluetoothHeadset service = mService;
+        if (service != null && isEnabled() && isValidDevice(device)) {
+            try {
+                return service.isNoiseReductionSupported(device);
+            } catch (RemoteException e) {
+                Log.e(TAG, Log.getStackTraceString(new Throwable()));
+            }
+        }
+        if (service == null) Log.w(TAG, "Proxy not attached to service");
+        return false;
+    }
+
+    /**
+     * Checks whether the headset supports voice recognition
+     *
+     * @param device Bluetooth device
+     * @return true if voice recognition is supported, false otherwise
+     */
+    @RequiresPermission(Manifest.permission.BLUETOOTH)
+    public boolean isVoiceRecognitionSupported(@NonNull BluetoothDevice device) {
+        if (DBG) log("isVoiceRecognitionSupported()");
+        final IBluetoothHeadset service = mService;
+        if (service != null && isEnabled() && isValidDevice(device)) {
+            try {
+                return service.isVoiceRecognitionSupported(device);
+            } catch (RemoteException e) {
+                Log.e(TAG, Log.getStackTraceString(new Throwable()));
+            }
+        }
+        if (service == null) Log.w(TAG, "Proxy not attached to service");
+        return false;
+    }
+
+    /**
      * Start Bluetooth voice recognition. This methods sends the voice
      * recognition AT command to the headset and establishes the
      * audio connection.
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index abe7fda..29ffa0b 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -6014,10 +6014,13 @@
     }
 
     /**
-     * A special version of {@link #createWindowContext(int, Bundle)} which also takes
-     * {@link Display}. The only difference between this API and
-     * {@link #createWindowContext(int, Bundle)} is that this API can create window context from
-     * any context even if the context which is not associated to a {@link Display} instance.
+     * Creates a {@code Context} for a non-{@link android.app.Activity activity} window on the given
+     * {@link Display}.
+     *
+     * <p>
+     * Similar to {@link #createWindowContext(int, Bundle)}, but the {@code display} is passed in,
+     * instead of implicitly using the {@link #getDisplay() original Context's Display}.
+     * </p>
      *
      * @param display The {@link Display} to associate with
      * @param type Window type in {@link WindowManager.LayoutParams}
@@ -6123,6 +6126,21 @@
     public abstract Context createCredentialProtectedStorageContext();
 
     /**
+     * Creates a UI context with a {@code token}. The users of this API should handle this context's
+     * configuration changes.
+     *
+     * @param token The token to associate with the {@link Resources}
+     * @param display The display to associate with the token context
+     *
+     * @hide
+     */
+    @UiContext
+    @NonNull
+    public Context createTokenContext(@NonNull IBinder token, @NonNull Display display) {
+        throw new RuntimeException("Not implemented. Must override in a subclass.");
+    }
+
+    /**
      * Gets the display adjustments holder for this context.  This information
      * is provided on a per-application or activity basis and is used to simulate lower density
      * display metrics for legacy applications and restricted screen sizes.
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index e450c08..078fa0d 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -21,6 +21,7 @@
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
+import android.annotation.UiContext;
 import android.app.IApplicationThread;
 import android.app.IServiceConnection;
 import android.compat.annotation.UnsupportedAppUsage;
@@ -946,7 +947,7 @@
 
     /** @hide */
     @Override
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    @UnsupportedAppUsage(trackingBug = 175981568)
     public Context createApplicationContext(ApplicationInfo application,
             int flags) throws PackageManager.NameNotFoundException {
         return mBase.createApplicationContext(application, flags);
@@ -1049,6 +1050,14 @@
         return mBase.createCredentialProtectedStorageContext();
     }
 
+    /** @hide */
+    @UiContext
+    @NonNull
+    @Override
+    public Context createTokenContext(@NonNull IBinder token, @NonNull Display display) {
+        return mBase.createTokenContext(token, display);
+    }
+
     @Override
     public boolean isDeviceProtectedStorage() {
         return mBase.isDeviceProtectedStorage();
diff --git a/core/java/android/content/om/IOverlayManager.aidl b/core/java/android/content/om/IOverlayManager.aidl
index 44b5c44..0b950b4 100644
--- a/core/java/android/content/om/IOverlayManager.aidl
+++ b/core/java/android/content/om/IOverlayManager.aidl
@@ -17,6 +17,7 @@
 package android.content.om;
 
 import android.content.om.OverlayInfo;
+import android.content.om.OverlayManagerTransaction;
 
 /**
  * Api for getting information about overlay packages.
@@ -163,4 +164,18 @@
      * @param packageName The name of the overlay package whose idmap should be deleted.
      */
     void invalidateCachesForOverlay(in String packageName, in int userIs);
+
+    /**
+     * Perform a series of requests related to overlay packages. This is an
+     * atomic operation: either all requests were performed successfully and
+     * the changes were propagated to the rest of the system, or at least one
+     * request could not be performed successfully and nothing is changed and
+     * nothing is propagated to the rest of the system.
+     *
+     * @see OverlayManagerTransaction
+     *
+     * @param transaction the series of overlay related requests to perform
+     * @throws SecurityException if the transaction failed
+     */
+    void commit(in OverlayManagerTransaction transaction);
 }
diff --git a/core/java/android/content/om/OverlayManager.java b/core/java/android/content/om/OverlayManager.java
index 217f637c..7c14c28 100644
--- a/core/java/android/content/om/OverlayManager.java
+++ b/core/java/android/content/om/OverlayManager.java
@@ -254,6 +254,29 @@
     }
 
     /**
+     * Perform a series of requests related to overlay packages. This is an
+     * atomic operation: either all requests were performed successfully and
+     * the changes were propagated to the rest of the system, or at least one
+     * request could not be performed successfully and nothing is changed and
+     * nothing is propagated to the rest of the system.
+     *
+     * @see OverlayManagerTransaction
+     *
+     * @param transaction the series of overlay related requests to perform
+     * @throws Exception if not all the requests could be successfully and
+     *         atomically executed
+     *
+     * @hide
+     */
+    public void commit(@NonNull final OverlayManagerTransaction transaction) {
+        try {
+            mService.commit(transaction);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Starting on R, actor enforcement and app visibility changes introduce additional failure
      * cases, but the SecurityException thrown with these checks is unexpected for existing
      * consumers of the API.
diff --git a/telephony/java/android/telephony/data/ApnThrottleStatus.aidl b/core/java/android/content/om/OverlayManagerTransaction.aidl
similarity index 81%
copy from telephony/java/android/telephony/data/ApnThrottleStatus.aidl
copy to core/java/android/content/om/OverlayManagerTransaction.aidl
index 46bc4ab..6715c82 100644
--- a/telephony/java/android/telephony/data/ApnThrottleStatus.aidl
+++ b/core/java/android/content/om/OverlayManagerTransaction.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright 2020 The Android Open Source Project
+ * Copyright (C) 2019 The Android Open Source Project
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,7 +14,6 @@
  * limitations under the License.
  */
 
-/** @hide */
-package android.telephony.data;
+package android.content.om;
 
-parcelable ApnThrottleStatus;
+parcelable OverlayManagerTransaction;
diff --git a/core/java/android/content/om/OverlayManagerTransaction.java b/core/java/android/content/om/OverlayManagerTransaction.java
new file mode 100644
index 0000000..1fa8973
--- /dev/null
+++ b/core/java/android/content/om/OverlayManagerTransaction.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.om;
+
+import static com.android.internal.util.Preconditions.checkNotNull;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.UserHandle;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Container for a batch of requests to the OverlayManagerService.
+ *
+ * Transactions are created using a builder interface. Example usage:
+ *
+ * final OverlayManager om = ctx.getSystemService(OverlayManager.class);
+ * final OverlayManagerTransaction t = new OverlayManagerTransaction.Builder()
+ *     .setEnabled(...)
+ *     .setEnabled(...)
+ *     .build();
+ * om.commit(t);
+ *
+ * @hide
+ */
+public class OverlayManagerTransaction
+        implements Iterable<OverlayManagerTransaction.Request>, Parcelable {
+    // TODO: remove @hide from this class when OverlayManager is added to the
+    // SDK, but keep OverlayManagerTransaction.Request @hidden
+    private final List<Request> mRequests;
+
+    OverlayManagerTransaction(@NonNull final List<Request> requests) {
+        checkNotNull(requests);
+        if (requests.contains(null)) {
+            throw new IllegalArgumentException("null request");
+        }
+        mRequests = requests;
+    }
+
+    private OverlayManagerTransaction(@NonNull final Parcel source) {
+        final int size = source.readInt();
+        mRequests = new ArrayList<Request>(size);
+        for (int i = 0; i < size; i++) {
+            final int request = source.readInt();
+            final String packageName = source.readString();
+            final int userId = source.readInt();
+            mRequests.add(new Request(request, packageName, userId));
+        }
+    }
+
+    @Override
+    public Iterator<Request> iterator() {
+        return mRequests.iterator();
+    }
+
+    @Override
+    public String toString() {
+        return String.format("OverlayManagerTransaction { mRequests = %s }", mRequests);
+    }
+
+    /**
+     * A single unit of the transaction, such as a request to enable an
+     * overlay, or to disable an overlay.
+     *
+     * @hide
+     */
+    public static class Request {
+        @IntDef(prefix = "TYPE_", value = {
+                TYPE_SET_ENABLED,
+                TYPE_SET_DISABLED,
+        })
+        @Retention(RetentionPolicy.SOURCE)
+        @interface RequestType {}
+
+        public static final int TYPE_SET_ENABLED = 0;
+        public static final int TYPE_SET_DISABLED = 1;
+
+        @RequestType public final int type;
+        public final String packageName;
+        public final int userId;
+
+        public Request(@RequestType final int type, @NonNull final String packageName,
+                final int userId) {
+            this.type = type;
+            this.packageName = packageName;
+            this.userId = userId;
+        }
+
+        @Override
+        public String toString() {
+            return String.format("Request{type=0x%02x (%s), packageName=%s, userId=%d}",
+                    type, typeToString(), packageName, userId);
+        }
+
+        /**
+         * Translate the request type into a human readable string. Only
+         * intended for debugging.
+         *
+         * @hide
+         */
+        public String typeToString() {
+            switch (type) {
+                case TYPE_SET_ENABLED: return "TYPE_SET_ENABLED";
+                case TYPE_SET_DISABLED: return "TYPE_SET_DISABLED";
+                default: return String.format("TYPE_UNKNOWN (0x%02x)", type);
+            }
+        }
+    }
+
+    /**
+     * Builder class for OverlayManagerTransaction objects.
+     *
+     * @hide
+     */
+    public static class Builder {
+        private final List<Request> mRequests = new ArrayList<>();
+
+        /**
+         * Request that an overlay package be enabled and change its loading
+         * order to the last package to be loaded, or disabled
+         *
+         * If the caller has the correct permissions, it is always possible to
+         * disable an overlay. Due to technical and security reasons it may not
+         * always be possible to enable an overlay, for instance if the overlay
+         * does not successfully overlay any target resources due to
+         * overlayable policy restrictions.
+         *
+         * An enabled overlay is a part of target package's resources, i.e. it will
+         * be part of any lookups performed via {@link android.content.res.Resources}
+         * and {@link android.content.res.AssetManager}. A disabled overlay will no
+         * longer affect the resources of the target package. If the target is
+         * currently running, its outdated resources will be replaced by new ones.
+         *
+         * @param packageName The name of the overlay package.
+         * @param enable true to enable the overlay, false to disable it.
+         * @return this Builder object, so you can chain additional requests
+         */
+        public Builder setEnabled(@NonNull String packageName, boolean enable) {
+            return setEnabled(packageName, enable, UserHandle.myUserId());
+        }
+
+        /**
+         * @hide
+         */
+        public Builder setEnabled(@NonNull String packageName, boolean enable, int userId) {
+            checkNotNull(packageName);
+            @Request.RequestType final int type =
+                enable ? Request.TYPE_SET_ENABLED : Request.TYPE_SET_DISABLED;
+            mRequests.add(new Request(type, packageName, userId));
+            return this;
+        }
+
+        /**
+         * Create a new transaction out of the requests added so far. Execute
+         * the transaction by calling OverlayManager#commit.
+         *
+         * @see OverlayManager#commit
+         * @return a new transaction
+         */
+        public OverlayManagerTransaction build() {
+            return new OverlayManagerTransaction(mRequests);
+        }
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        final int size = mRequests.size();
+        dest.writeInt(size);
+        for (int i = 0; i < size; i++) {
+            final Request req = mRequests.get(i);
+            dest.writeInt(req.type);
+            dest.writeString(req.packageName);
+            dest.writeInt(req.userId);
+        }
+    }
+
+    public static final Parcelable.Creator<OverlayManagerTransaction> CREATOR =
+            new Parcelable.Creator<OverlayManagerTransaction>() {
+
+        @Override
+        public OverlayManagerTransaction createFromParcel(Parcel source) {
+            return new OverlayManagerTransaction(source);
+        }
+
+        @Override
+        public OverlayManagerTransaction[] newArray(int size) {
+            return new OverlayManagerTransaction[size];
+        }
+    };
+}
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index ddcfb92..b1ca12cd 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -18,6 +18,9 @@
 
 import android.annotation.IntDef;
 import android.annotation.TestApi;
+import android.app.compat.CompatChanges;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.Disabled;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.ComponentName;
 import android.content.Intent;
@@ -27,6 +30,7 @@
 import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.UserHandle;
 import android.util.Printer;
 
 import java.lang.annotation.Retention;
@@ -866,6 +870,47 @@
     };
 
     /**
+     * This change id forces the packages it is applied to to be resizable. We only allow resizing
+     * in fullscreen windowing mode, but not forcing the app into resizable multi-windowing mode.
+     * @hide
+     */
+    @ChangeId
+    @Disabled
+    public static final long FORCE_RESIZE_APP = 174042936L; // number refers to buganizer id
+
+    /**
+     * Return value for {@link #supportsSizeChanges()} indicating that this activity does not
+     * support size changes.
+     * @hide
+     */
+    public static final int SIZE_CHANGES_UNSUPPORTED = 0;
+
+    /**
+     * Return value for {@link #supportsSizeChanges()} indicating that this activity supports size
+     * changes due to the android.supports_size_changes metadata flag being set either on
+     * application or on activity level.
+     * @hide
+     */
+    public static final int SIZE_CHANGES_SUPPORTED_METADATA = 1;
+
+    /**
+     * Return value for {@link #supportsSizeChanges()} indicating that this activity has been
+     * overridden to support size changes through the compat framework change id
+     * {@link #FORCE_RESIZE_APP}.
+     * @hide
+     */
+    public static final int SIZE_CHANGES_SUPPORTED_OVERRIDE = 2;
+
+    /** @hide */
+    @IntDef(prefix = { "SIZE_CHANGES_" }, value = {
+            SIZE_CHANGES_UNSUPPORTED,
+            SIZE_CHANGES_SUPPORTED_METADATA,
+            SIZE_CHANGES_SUPPORTED_OVERRIDE,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface SizeChangesSupportMode {}
+
+    /**
      * Convert Java change bits to native.
      *
      * @hide
@@ -1146,6 +1191,25 @@
         return (flags & FLAG_SUPPORTS_PICTURE_IN_PICTURE) != 0;
     }
 
+    /**
+     * Returns whether the activity supports size changes.
+     * @hide
+     */
+    @SizeChangesSupportMode
+    public int supportsSizeChanges() {
+        if (supportsSizeChanges) {
+            return SIZE_CHANGES_SUPPORTED_METADATA;
+        }
+
+        if (CompatChanges.isChangeEnabled(FORCE_RESIZE_APP,
+                applicationInfo.packageName,
+                UserHandle.getUserHandleForUid(applicationInfo.uid))) {
+            return SIZE_CHANGES_SUPPORTED_OVERRIDE;
+        }
+
+        return SIZE_CHANGES_UNSUPPORTED;
+    }
+
     /** @hide */
     @UnsupportedAppUsage
     public static boolean isResizeableMode(int mode) {
@@ -1186,6 +1250,20 @@
         }
     }
 
+    /** @hide */
+    public static String sizeChangesSupportModeToString(@SizeChangesSupportMode int mode) {
+        switch (mode) {
+            case SIZE_CHANGES_UNSUPPORTED:
+                return "SIZE_CHANGES_UNSUPPORTED";
+            case SIZE_CHANGES_SUPPORTED_METADATA:
+                return "SIZE_CHANGES_SUPPORTED_METADATA";
+            case SIZE_CHANGES_SUPPORTED_OVERRIDE:
+                return "SIZE_CHANGES_SUPPORTED_OVERRIDE";
+            default:
+                return "unknown=" + mode;
+        }
+    }
+
     public void dump(Printer pw, String prefix) {
         dump(pw, prefix, DUMP_FLAG_ALL);
     }
diff --git a/core/java/android/app/role/RolePrivileges.aidl b/core/java/android/content/pm/Attribution.aidl
similarity index 91%
rename from core/java/android/app/role/RolePrivileges.aidl
rename to core/java/android/content/pm/Attribution.aidl
index 1561ad4..909fdb5 100644
--- a/core/java/android/app/role/RolePrivileges.aidl
+++ b/core/java/android/content/pm/Attribution.aidl
@@ -14,7 +14,6 @@
  * limitations under the License.
  */
 
+package android.content.pm;
 
-package android.app.role;
-
-parcelable RolePrivileges;
+parcelable Attribution;
diff --git a/core/java/android/content/pm/Attribution.java b/core/java/android/content/pm/Attribution.java
new file mode 100644
index 0000000..989a5b9
--- /dev/null
+++ b/core/java/android/content/pm/Attribution.java
@@ -0,0 +1,160 @@
+/*
+ * 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.content.pm;
+
+import android.annotation.IdRes;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.DataClass;
+
+/**
+ * Information about an attribution declared by a package. This corresponds to the information
+ * collected from the AndroidManifest.xml's &lt;attribution&gt; tags.
+ */
+@DataClass(genHiddenConstructor = true)
+public final class Attribution implements Parcelable {
+
+    /**
+     * The tag of this attribution. From the &lt;manifest&gt; tag's "tag" attribute
+     */
+    private @NonNull String mTag;
+
+    /**
+     * The resource ID of the label of the attribution From the &lt;manifest&gt; tag's "label"
+     * attribute
+     */
+    private final @IdRes int mLabel;
+
+
+
+    // Code below generated by codegen v1.0.22.
+    //
+    // DO NOT MODIFY!
+    // CHECKSTYLE:OFF Generated code
+    //
+    // To regenerate run:
+    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/Attribution.java
+    //
+    // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+    //   Settings > Editor > Code Style > Formatter Control
+    //@formatter:off
+
+
+    /**
+     * Creates a new Attribution.
+     *
+     * @param tag
+     *   The tag of this attribution. From the &lt;manifest&gt; tag's "tag" attribute
+     * @param label
+     *   The resource ID of the label of the attribution From the &lt;manifest&gt; tag's "label"
+     *   attribute
+     * @hide
+     */
+    @DataClass.Generated.Member
+    public Attribution(
+            @NonNull String tag,
+            @IdRes int label) {
+        this.mTag = tag;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mTag);
+        this.mLabel = label;
+        com.android.internal.util.AnnotationValidations.validate(
+                IdRes.class, null, mLabel);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    /**
+     * The tag of this attribution. From the &lt;manifest&gt; tag's "tag" attribute
+     */
+    @DataClass.Generated.Member
+    public @NonNull String getTag() {
+        return mTag;
+    }
+
+    /**
+     * The resource ID of the label of the attribution From the &lt;manifest&gt; tag's "label"
+     * attribute
+     */
+    @DataClass.Generated.Member
+    public @IdRes int getLabel() {
+        return mLabel;
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public void writeToParcel(@NonNull Parcel dest, int flags) {
+        // You can override field parcelling by defining methods like:
+        // void parcelFieldName(Parcel dest, int flags) { ... }
+
+        dest.writeString(mTag);
+        dest.writeInt(mLabel);
+    }
+
+    @Override
+    @DataClass.Generated.Member
+    public int describeContents() { return 0; }
+
+    /** @hide */
+    @SuppressWarnings({"unchecked", "RedundantCast"})
+    @DataClass.Generated.Member
+    /* package-private */ Attribution(@NonNull Parcel in) {
+        // You can override field unparcelling by defining methods like:
+        // static FieldType unparcelFieldName(Parcel in) { ... }
+
+        String tag = in.readString();
+        int label = in.readInt();
+
+        this.mTag = tag;
+        com.android.internal.util.AnnotationValidations.validate(
+                NonNull.class, null, mTag);
+        this.mLabel = label;
+        com.android.internal.util.AnnotationValidations.validate(
+                IdRes.class, null, mLabel);
+
+        // onConstructed(); // You can define this method to get a callback
+    }
+
+    @DataClass.Generated.Member
+    public static final @NonNull Parcelable.Creator<Attribution> CREATOR
+            = new Parcelable.Creator<Attribution>() {
+        @Override
+        public Attribution[] newArray(int size) {
+            return new Attribution[size];
+        }
+
+        @Override
+        public Attribution createFromParcel(@NonNull Parcel in) {
+            return new Attribution(in);
+        }
+    };
+
+    @DataClass.Generated(
+            time = 1608139558081L,
+            codegenVersion = "1.0.22",
+            sourceFile = "frameworks/base/core/java/android/content/pm/Attribution.java",
+            inputSignatures = "private @android.annotation.NonNull java.lang.String mTag\nprivate final @android.annotation.IdRes int mLabel\nclass Attribution extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genHiddenConstructor=true)")
+    @Deprecated
+    private void __metadata() {}
+
+
+    //@formatter:on
+    // End of generated code
+
+}
diff --git a/core/java/android/content/pm/LAUNCHER_OWNERS b/core/java/android/content/pm/LAUNCHER_OWNERS
new file mode 100644
index 0000000..400836f
--- /dev/null
+++ b/core/java/android/content/pm/LAUNCHER_OWNERS
@@ -0,0 +1,7 @@
+set noparent
+
+omakoto@google.com
+sunnygoyal@google.com
+mett@google.com
+jonmiranda@google.com
+pinyaoting@google.com
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index 4b7bbbf..6292575 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -441,6 +441,7 @@
          * @hide
          */
         @SystemApi
+        @RequiresPermission(android.Manifest.permission.ACCESS_SHORTCUTS)
         public static final int FLAG_GET_PERSONS_DATA = 1 << 11;
 
         /** @hide */
diff --git a/core/java/android/content/pm/OWNERS b/core/java/android/content/pm/OWNERS
index 24872e8..f88df95 100644
--- a/core/java/android/content/pm/OWNERS
+++ b/core/java/android/content/pm/OWNERS
@@ -5,4 +5,5 @@
 patb@google.com
 
 per-file PackageParser.java = chiuwinson@google.com
-per-file *Shortcut* = omakoto@google.com, yamasani@google.com, sunnygoyal@google.com, mett@google.com, pinyaoting@google.com
+per-file *Shortcut* = file:/core/java/android/content/pm/SHORTCUT_OWNERS
+per-file *Launcher* = file:/core/java/android/content/pm/LAUNCHER_OWNERS
diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java
index d7abb68..f991306 100644
--- a/core/java/android/content/pm/PackageInfo.java
+++ b/core/java/android/content/pm/PackageInfo.java
@@ -218,6 +218,14 @@
     public int[] requestedPermissionsFlags;
 
     /**
+     * Array of all {@link android.R.styleable#AndroidManifestAttribution
+     * &lt;attribution&gt;} tags included under &lt;manifest&gt;, or null if there were none. This
+     * is only filled if the flag {@link PackageManager#GET_ATTRIBUTIONS} was set.
+     */
+    @SuppressWarnings("ArrayReturn")
+    public @Nullable Attribution[] attributions;
+
+    /**
      * Flag for {@link #requestedPermissionsFlags}: the requested permission
      * is required for the application to run; the user can not optionally
      * disable it.  Currently all permissions are required.
@@ -471,6 +479,7 @@
         dest.writeTypedArray(configPreferences, parcelableFlags);
         dest.writeTypedArray(reqFeatures, parcelableFlags);
         dest.writeTypedArray(featureGroups, parcelableFlags);
+        dest.writeTypedArray(attributions, parcelableFlags);
         dest.writeInt(installLocation);
         dest.writeInt(isStub ? 1 : 0);
         dest.writeInt(coreApp ? 1 : 0);
@@ -536,6 +545,7 @@
         configPreferences = source.createTypedArray(ConfigurationInfo.CREATOR);
         reqFeatures = source.createTypedArray(FeatureInfo.CREATOR);
         featureGroups = source.createTypedArray(FeatureGroupInfo.CREATOR);
+        attributions = source.createTypedArray(Attribution.CREATOR);
         installLocation = source.readInt();
         isStub = source.readInt() != 0;
         coreApp = source.readInt() != 0;
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index bd27099..e074eab 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -449,6 +449,7 @@
             GET_DISABLED_UNTIL_USED_COMPONENTS,
             GET_UNINSTALLED_PACKAGES,
             MATCH_HIDDEN_UNTIL_INSTALLED_COMPONENTS,
+            GET_ATTRIBUTIONS,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface PackageInfoFlags {}
@@ -842,6 +843,11 @@
      */
     public static final int MATCH_DIRECT_BOOT_AUTO = 0x10000000;
 
+    /**
+     * {@link PackageInfo} flag: return all attributions declared in the package manifest
+     */
+    public static final int GET_ATTRIBUTIONS = 0x80000000;
+
     /** @hide */
     @Deprecated
     public static final int MATCH_DEBUG_TRIAGED_MISSING = MATCH_DIRECT_BOOT_AUTO;
diff --git a/core/java/android/content/pm/SHORTCUT_OWNERS b/core/java/android/content/pm/SHORTCUT_OWNERS
new file mode 100644
index 0000000..3688d5a
--- /dev/null
+++ b/core/java/android/content/pm/SHORTCUT_OWNERS
@@ -0,0 +1,7 @@
+set noparent
+
+omakoto@google.com
+yamasani@google.com
+sunnygoyal@google.com
+mett@google.com
+pinyaoting@google.com
diff --git a/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java b/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
index ddf3d90..056af77 100644
--- a/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
+++ b/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
@@ -22,6 +22,7 @@
 import android.apex.ApexInfo;
 import android.content.pm.ActivityInfo;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.Attribution;
 import android.content.pm.ComponentInfo;
 import android.content.pm.ConfigurationInfo;
 import android.content.pm.FallbackCategoryProvider;
@@ -42,6 +43,7 @@
 import android.content.pm.SigningInfo;
 import android.content.pm.parsing.component.ComponentParseUtils;
 import android.content.pm.parsing.component.ParsedActivity;
+import android.content.pm.parsing.component.ParsedAttribution;
 import android.content.pm.parsing.component.ParsedComponent;
 import android.content.pm.parsing.component.ParsedInstrumentation;
 import android.content.pm.parsing.component.ParsedMainComponent;
@@ -276,6 +278,15 @@
                 }
             }
         }
+        if ((flags & PackageManager.GET_ATTRIBUTIONS) != 0) {
+            int size = ArrayUtils.size(pkg.getAttributions());
+            if (size > 0) {
+                pi.attributions = new Attribution[size];
+                for (int i = 0; i < size; i++) {
+                    pi.attributions[i] = generateAttribution(pkg.getAttributions().get(i));
+                }
+            }
+        }
 
         if (apexInfo != null) {
             File apexFile = new File(apexInfo.modulePath);
@@ -669,6 +680,12 @@
         return pgi;
     }
 
+    @Nullable
+    public static Attribution generateAttribution(ParsedAttribution pa) {
+        if (pa == null) return null;
+        return new Attribution(pa.tag, pa.label);
+    }
+
     private static void assignSharedFieldsForComponentInfo(@NonNull ComponentInfo componentInfo,
             @NonNull ParsedMainComponent mainComponent) {
         assignSharedFieldsForPackageItemInfo(componentInfo, mainComponent);
diff --git a/core/java/android/content/pm/parsing/component/ParsedAttribution.java b/core/java/android/content/pm/parsing/component/ParsedAttribution.java
index 02b3c7d..3a4aae1 100644
--- a/core/java/android/content/pm/parsing/component/ParsedAttribution.java
+++ b/core/java/android/content/pm/parsing/component/ParsedAttribution.java
@@ -100,7 +100,7 @@
 
 
 
-    // Code below generated by codegen v1.0.14.
+    // Code below generated by codegen v1.0.22.
     //
     // DO NOT MODIFY!
     // CHECKSTYLE:OFF Generated code
@@ -215,8 +215,8 @@
     };
 
     @DataClass.Generated(
-            time = 1583436566499L,
-            codegenVersion = "1.0.14",
+            time = 1607463855175L,
+            codegenVersion = "1.0.22",
             sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedAttribution.java",
             inputSignatures = "public static final  int MAX_ATTRIBUTION_TAG_LEN\nprivate static final  int MAX_NUM_ATTRIBUTIONS\npublic final @android.annotation.NonNull java.lang.String tag\npublic final @android.annotation.StringRes int label\npublic final @android.annotation.NonNull java.util.List<java.lang.String> inheritFrom\npublic static  boolean isCombinationValid(java.util.List<android.content.pm.parsing.component.ParsedAttribution>)\nclass ParsedAttribution extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genAidl=false)")
     @Deprecated
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 260f03b..aed0823 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -2057,7 +2057,7 @@
     }
 
     /** @hide */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    @UnsupportedAppUsage(trackingBug = 176190631)
     public DisplayAdjustments getDisplayAdjustments() {
         final DisplayAdjustments overrideDisplayAdjustments = mOverrideDisplayAdjustments;
         if (overrideDisplayAdjustments != null) {
diff --git a/core/java/android/hardware/biometrics/OWNERS b/core/java/android/hardware/biometrics/OWNERS
index 33527f8..2065ffa 100644
--- a/core/java/android/hardware/biometrics/OWNERS
+++ b/core/java/android/hardware/biometrics/OWNERS
@@ -1,3 +1,8 @@
 # Bug component: 879035
 
+curtislb@google.com
+ilyamaty@google.com
 jaggies@google.com
+joshmccloskey@google.com
+kchyn@google.com
+
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index d7aee10..157e333 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -1699,7 +1699,7 @@
      * transform fields, and what its illumination target
      * is.</p>
      * <p>This control is only effective if {@link CaptureRequest#CONTROL_MODE android.control.mode} is AUTO.</p>
-     * <p>When set to the ON mode, the camera device's auto-white balance
+     * <p>When set to the AUTO mode, the camera device's auto-white balance
      * routine is enabled, overriding the application's selected
      * {@link CaptureRequest#COLOR_CORRECTION_TRANSFORM android.colorCorrection.transform}, {@link CaptureRequest#COLOR_CORRECTION_GAINS android.colorCorrection.gains} and
      * {@link CaptureRequest#COLOR_CORRECTION_MODE android.colorCorrection.mode}. Note that when {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode}
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index 66d8b50..c0eb068 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -1781,7 +1781,7 @@
      * transform fields, and what its illumination target
      * is.</p>
      * <p>This control is only effective if {@link CaptureRequest#CONTROL_MODE android.control.mode} is AUTO.</p>
-     * <p>When set to the ON mode, the camera device's auto-white balance
+     * <p>When set to the AUTO mode, the camera device's auto-white balance
      * routine is enabled, overriding the application's selected
      * {@link CaptureRequest#COLOR_CORRECTION_TRANSFORM android.colorCorrection.transform}, {@link CaptureRequest#COLOR_CORRECTION_GAINS android.colorCorrection.gains} and
      * {@link CaptureRequest#COLOR_CORRECTION_MODE android.colorCorrection.mode}. Note that when {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode}
diff --git a/core/java/android/hardware/fingerprint/OWNERS b/core/java/android/hardware/fingerprint/OWNERS
index dcead40..e55b8c56 100644
--- a/core/java/android/hardware/fingerprint/OWNERS
+++ b/core/java/android/hardware/fingerprint/OWNERS
@@ -1,3 +1,8 @@
 # Bug component: 114777
 
+curtislb@google.com
+ilyamaty@google.com
 jaggies@google.com
+joshmccloskey@google.com
+kchyn@google.com
+
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index 300d99b..9d20f6d 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -195,7 +195,7 @@
      */
     @BlockUntrustedTouchesMode
     public static final int DEFAULT_BLOCK_UNTRUSTED_TOUCHES_MODE =
-            BlockUntrustedTouchesMode.PERMISSIVE;
+            BlockUntrustedTouchesMode.BLOCK;
 
     /**
      * Prevent touches from being consumed by apps if these touches passed through a non-trusted
diff --git a/core/java/android/net/ConnectivityDiagnosticsManager.java b/core/java/android/net/ConnectivityDiagnosticsManager.java
index 704f31d..5234494 100644
--- a/core/java/android/net/ConnectivityDiagnosticsManager.java
+++ b/core/java/android/net/ConnectivityDiagnosticsManager.java
@@ -623,32 +623,41 @@
         /** @hide */
         @VisibleForTesting
         public void onConnectivityReportAvailable(@NonNull ConnectivityReport report) {
-            Binder.withCleanCallingIdentity(() -> {
+            final long token = Binder.clearCallingIdentity();
+            try {
                 mExecutor.execute(() -> {
                     mCb.onConnectivityReportAvailable(report);
                 });
-            });
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
         }
 
         /** @hide */
         @VisibleForTesting
         public void onDataStallSuspected(@NonNull DataStallReport report) {
-            Binder.withCleanCallingIdentity(() -> {
+            final long token = Binder.clearCallingIdentity();
+            try {
                 mExecutor.execute(() -> {
                     mCb.onDataStallSuspected(report);
                 });
-            });
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
         }
 
         /** @hide */
         @VisibleForTesting
         public void onNetworkConnectivityReported(
                 @NonNull Network network, boolean hasConnectivity) {
-            Binder.withCleanCallingIdentity(() -> {
+            final long token = Binder.clearCallingIdentity();
+            try {
                 mExecutor.execute(() -> {
                     mCb.onNetworkConnectivityReported(network, hasConnectivity);
                 });
-            });
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
         }
     }
 
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index f756cda..a4f88af 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -59,6 +59,7 @@
 import android.telephony.TelephonyManager;
 import android.util.ArrayMap;
 import android.util.Log;
+import android.util.Range;
 import android.util.SparseIntArray;
 
 import com.android.connectivity.aidl.INetworkAgent;
@@ -73,10 +74,12 @@
 import java.io.UncheckedIOException;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.net.DatagramSocket;
 import java.net.InetAddress;
 import java.net.InetSocketAddress;
 import java.net.Socket;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -1163,6 +1166,55 @@
     }
 
     /**
+     * Adds or removes a requirement for given UID ranges to use the VPN.
+     *
+     * If set to {@code true}, informs the system that the UIDs in the specified ranges must not
+     * have any connectivity except if a VPN is connected and applies to the UIDs, or if the UIDs
+     * otherwise have permission to bypass the VPN (e.g., because they have the
+     * {@link android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS} permission, or when
+     * using a socket protected by a method such as {@link VpnService#protect(DatagramSocket)}. If
+     * set to {@code false}, a previously-added restriction is removed.
+     * <p>
+     * Each of the UID ranges specified by this method is added and removed as is, and no processing
+     * is performed on the ranges to de-duplicate, merge, split, or intersect them. In order to
+     * remove a previously-added range, the exact range must be removed as is.
+     * <p>
+     * The changes are applied asynchronously and may not have been applied by the time the method
+     * returns. Apps will be notified about any changes that apply to them via
+     * {@link NetworkCallback#onBlockedStatusChanged} callbacks called after the changes take
+     * effect.
+     * <p>
+     * This method should be called only by the VPN code.
+     *
+     * @param ranges the UID ranges to restrict
+     * @param requireVpn whether the specified UID ranges must use a VPN
+     *
+     * TODO: expose as @SystemApi.
+     * @hide
+     */
+    @RequiresPermission(anyOf = {
+            NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+            android.Manifest.permission.NETWORK_STACK})
+    public void setRequireVpnForUids(boolean requireVpn,
+            @NonNull Collection<Range<Integer>> ranges) {
+        Objects.requireNonNull(ranges);
+        // The Range class is not parcelable. Convert to UidRange, which is what is used internally.
+        // This method is not necessarily expected to be used outside the system server, so
+        // parceling may not be necessary, but it could be used out-of-process, e.g., by the network
+        // stack process, or by tests.
+        UidRange[] rangesArray = new UidRange[ranges.size()];
+        int index = 0;
+        for (Range<Integer> range : ranges) {
+            rangesArray[index++] = new UidRange(range.getLower(), range.getUpper());
+        }
+        try {
+            mService.setRequireVpnForUids(requireVpn, rangesArray);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Returns details about the currently active default data network
      * for a given uid.  This is for internal use only to avoid spying
      * other apps.
@@ -1833,30 +1885,42 @@
             mCallback = new ISocketKeepaliveCallback.Stub() {
                 @Override
                 public void onStarted(int slot) {
-                    Binder.withCleanCallingIdentity(() ->
-                            mExecutor.execute(() -> {
-                                mSlot = slot;
-                                callback.onStarted();
-                            }));
+                    final long token = Binder.clearCallingIdentity();
+                    try {
+                        mExecutor.execute(() -> {
+                            mSlot = slot;
+                            callback.onStarted();
+                        });
+                    } finally {
+                        Binder.restoreCallingIdentity(token);
+                    }
                 }
 
                 @Override
                 public void onStopped() {
-                    Binder.withCleanCallingIdentity(() ->
-                            mExecutor.execute(() -> {
-                                mSlot = null;
-                                callback.onStopped();
-                            }));
+                    final long token = Binder.clearCallingIdentity();
+                    try {
+                        mExecutor.execute(() -> {
+                            mSlot = null;
+                            callback.onStopped();
+                        });
+                    } finally {
+                        Binder.restoreCallingIdentity(token);
+                    }
                     mExecutor.shutdown();
                 }
 
                 @Override
                 public void onError(int error) {
-                    Binder.withCleanCallingIdentity(() ->
-                            mExecutor.execute(() -> {
-                                mSlot = null;
-                                callback.onError(error);
-                            }));
+                    final long token = Binder.clearCallingIdentity();
+                    try {
+                        mExecutor.execute(() -> {
+                            mSlot = null;
+                            callback.onError(error);
+                        });
+                    } finally {
+                        Binder.restoreCallingIdentity(token);
+                    }
                     mExecutor.shutdown();
                 }
 
@@ -3120,39 +3184,6 @@
     }
 
     /**
-     * Check mobile provisioning.
-     *
-     * @param suggestedTimeOutMs, timeout in milliseconds
-     *
-     * @return time out that will be used, maybe less that suggestedTimeOutMs
-     * -1 if an error.
-     *
-     * {@hide}
-     */
-    public int checkMobileProvisioning(int suggestedTimeOutMs) {
-        int timeOutMs = -1;
-        try {
-            timeOutMs = mService.checkMobileProvisioning(suggestedTimeOutMs);
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-        return timeOutMs;
-    }
-
-    /**
-     * Get the mobile provisioning url.
-     * {@hide}
-     */
-    @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
-    public String getMobileProvisioningUrl() {
-        try {
-            return mService.getMobileProvisioningUrl();
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
-
-    /**
      * Set sign in error notification to visible or invisible
      *
      * @hide
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index cbb1197..b32c98b 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -29,6 +29,7 @@
 import android.net.NetworkState;
 import android.net.ISocketKeepaliveCallback;
 import android.net.ProxyInfo;
+import android.net.UidRange;
 import android.os.Bundle;
 import android.os.IBinder;
 import android.os.INetworkActivityListener;
@@ -146,10 +147,7 @@
     String getAlwaysOnVpnPackage(int userId);
     boolean isVpnLockdownEnabled(int userId);
     List<String> getVpnLockdownWhitelist(int userId);
-
-    int checkMobileProvisioning(int suggestedTimeOutMs);
-
-    String getMobileProvisioningUrl();
+    void setRequireVpnForUids(boolean requireVpn, in UidRange[] ranges);
 
     void setProvisioningNotificationVisible(boolean visible, int networkType, in String action);
 
diff --git a/core/java/android/net/NetworkStatsHistory.java b/core/java/android/net/NetworkStatsHistory.java
index bf25602..f413063 100644
--- a/core/java/android/net/NetworkStatsHistory.java
+++ b/core/java/android/net/NetworkStatsHistory.java
@@ -45,8 +45,8 @@
 import libcore.util.EmptyArray;
 
 import java.io.CharArrayWriter;
-import java.io.DataInputStream;
-import java.io.DataOutputStream;
+import java.io.DataInput;
+import java.io.DataOutput;
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.net.ProtocolException;
@@ -162,7 +162,7 @@
         out.writeLong(totalBytes);
     }
 
-    public NetworkStatsHistory(DataInputStream in) throws IOException {
+    public NetworkStatsHistory(DataInput in) throws IOException {
         final int version = in.readInt();
         switch (version) {
             case VERSION_INIT: {
@@ -204,7 +204,7 @@
         }
     }
 
-    public void writeToStream(DataOutputStream out) throws IOException {
+    public void writeToStream(DataOutput out) throws IOException {
         out.writeInt(VERSION_ADD_ACTIVE);
         out.writeLong(bucketDuration);
         writeVarLongArray(out, bucketStart, bucketCount);
@@ -768,7 +768,7 @@
      */
     public static class DataStreamUtils {
         @Deprecated
-        public static long[] readFullLongArray(DataInputStream in) throws IOException {
+        public static long[] readFullLongArray(DataInput in) throws IOException {
             final int size = in.readInt();
             if (size < 0) throw new ProtocolException("negative array size");
             final long[] values = new long[size];
@@ -781,7 +781,7 @@
         /**
          * Read variable-length {@link Long} using protobuf-style approach.
          */
-        public static long readVarLong(DataInputStream in) throws IOException {
+        public static long readVarLong(DataInput in) throws IOException {
             int shift = 0;
             long result = 0;
             while (shift < 64) {
@@ -797,7 +797,7 @@
         /**
          * Write variable-length {@link Long} using protobuf-style approach.
          */
-        public static void writeVarLong(DataOutputStream out, long value) throws IOException {
+        public static void writeVarLong(DataOutput out, long value) throws IOException {
             while (true) {
                 if ((value & ~0x7FL) == 0) {
                     out.writeByte((int) value);
@@ -809,7 +809,7 @@
             }
         }
 
-        public static long[] readVarLongArray(DataInputStream in) throws IOException {
+        public static long[] readVarLongArray(DataInput in) throws IOException {
             final int size = in.readInt();
             if (size == -1) return null;
             if (size < 0) throw new ProtocolException("negative array size");
@@ -820,7 +820,7 @@
             return values;
         }
 
-        public static void writeVarLongArray(DataOutputStream out, long[] values, int size)
+        public static void writeVarLongArray(DataOutput out, long[] values, int size)
                 throws IOException {
             if (values == null) {
                 out.writeInt(-1);
diff --git a/core/java/android/net/SocketKeepalive.java b/core/java/android/net/SocketKeepalive.java
index a7dce18..d007a95 100644
--- a/core/java/android/net/SocketKeepalive.java
+++ b/core/java/android/net/SocketKeepalive.java
@@ -187,38 +187,54 @@
         mCallback = new ISocketKeepaliveCallback.Stub() {
             @Override
             public void onStarted(int slot) {
-                Binder.withCleanCallingIdentity(() ->
-                        mExecutor.execute(() -> {
-                            mSlot = slot;
-                            callback.onStarted();
-                        }));
+                final long token = Binder.clearCallingIdentity();
+                try {
+                    mExecutor.execute(() -> {
+                        mSlot = slot;
+                        callback.onStarted();
+                    });
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
             }
 
             @Override
             public void onStopped() {
-                Binder.withCleanCallingIdentity(() ->
-                        executor.execute(() -> {
-                            mSlot = null;
-                            callback.onStopped();
-                        }));
+                final long token = Binder.clearCallingIdentity();
+                try {
+                    executor.execute(() -> {
+                        mSlot = null;
+                        callback.onStopped();
+                    });
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
             }
 
             @Override
             public void onError(int error) {
-                Binder.withCleanCallingIdentity(() ->
-                        executor.execute(() -> {
-                            mSlot = null;
-                            callback.onError(error);
-                        }));
+                final long token = Binder.clearCallingIdentity();
+                try {
+                    executor.execute(() -> {
+                        mSlot = null;
+                        callback.onError(error);
+                    });
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
             }
 
             @Override
             public void onDataReceived() {
-                Binder.withCleanCallingIdentity(() ->
-                        executor.execute(() -> {
-                            mSlot = null;
-                            callback.onDataReceived();
-                        }));
+                final long token = Binder.clearCallingIdentity();
+                try {
+                    executor.execute(() -> {
+                        mSlot = null;
+                        callback.onDataReceived();
+                    });
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
             }
         };
     }
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index a89de01..b846142 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -985,6 +985,15 @@
          */
         public abstract void getDeferredJobsLineLocked(StringBuilder sb, int which);
 
+        /**
+         * Returns the measured energy in microjoules that the display consumed while the screen
+         * was on and uid active.
+         * Will return {@link #ENERGY_DATA_UNAVAILABLE} if data is unavailable
+         *
+         * {@hide}
+         */
+        public abstract long getScreenOnEnergy();
+
         public static abstract class Sensor {
 
             @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
index c014ef6..0326b72 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -1463,8 +1463,7 @@
             Uri uri = MediaStore.scanFile(resolver, realFile);
             if (uri != null) {
                 Bundle opts = new Bundle();
-                // TODO(b/158465539): Use API constant
-                opts.putBoolean("android.provider.extra.ACCEPT_ORIGINAL_MEDIA_FORMAT", true);
+                opts.putBoolean(MediaStore.EXTRA_ACCEPT_ORIGINAL_MEDIA_FORMAT, true);
                 AssetFileDescriptor afd = resolver.openTypedAssetFileDescriptor(uri, "*/*", opts);
                 Log.i(TAG, "Changed to modern format dataSource for: " + realFile);
                 return afd.getFileDescriptor();
diff --git a/core/java/android/os/TransactionTooLargeException.java b/core/java/android/os/TransactionTooLargeException.java
index 10abf26..4d5b2a1 100644
--- a/core/java/android/os/TransactionTooLargeException.java
+++ b/core/java/android/os/TransactionTooLargeException.java
@@ -23,9 +23,11 @@
  * During a remote procedure call, the arguments and the return value of the call
  * are transferred as {@link Parcel} objects stored in the Binder transaction buffer.
  * If the arguments or the return value are too large to fit in the transaction buffer,
- * then the call will fail and {@link TransactionTooLargeException} will be thrown.
+ * then the call will fail.  {@link TransactionTooLargeException} is thrown as a
+ * heuristic when a transaction is large, and it fails, since these are the transactions
+ * which are most likely to overfill the transaction buffer.
  * </p><p>
- * The Binder transaction buffer has a limited fixed size, currently 1Mb, which
+ * The Binder transaction buffer has a limited fixed size, currently 1MB, which
  * is shared by all transactions in progress for the process.  Consequently this
  * exception can be thrown when there are many transactions in progress even when
  * most of the individual transactions are of moderate size.
diff --git a/core/java/android/os/image/DynamicSystemClient.java b/core/java/android/os/image/DynamicSystemClient.java
index 58268e2..0fe8894 100644
--- a/core/java/android/os/image/DynamicSystemClient.java
+++ b/core/java/android/os/image/DynamicSystemClient.java
@@ -68,6 +68,8 @@
  */
 @SystemApi
 public class DynamicSystemClient {
+    private static final String TAG = "DynamicSystemClient";
+
     /** @hide */
     @IntDef(prefix = { "STATUS_" }, value = {
             STATUS_UNKNOWN,
@@ -92,8 +94,6 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface StatusChangedCause {}
 
-    private static final String TAG = "DynSystemClient";
-
     /** Listener for installation status updates. */
     public interface OnStatusChangedListener {
         /**
@@ -240,7 +240,7 @@
 
     private class DynSystemServiceConnection implements ServiceConnection {
         public void onServiceConnected(ComponentName className, IBinder service) {
-            Slog.v(TAG, "DynSystemService connected");
+            Slog.v(TAG, "onServiceConnected: " + className);
 
             mService = new Messenger(service);
 
@@ -262,7 +262,7 @@
         }
 
         public void onServiceDisconnected(ComponentName className) {
-            Slog.v(TAG, "DynSystemService disconnected");
+            Slog.v(TAG, "onServiceDisconnected: " + className);
             mService = null;
         }
     }
diff --git a/core/java/android/os/image/DynamicSystemManager.java b/core/java/android/os/image/DynamicSystemManager.java
index 7f01cad..e8e4785 100644
--- a/core/java/android/os/image/DynamicSystemManager.java
+++ b/core/java/android/os/image/DynamicSystemManager.java
@@ -269,4 +269,16 @@
             throw new RuntimeException(e.toString());
         }
     }
+
+    /**
+     * Returns the suggested scratch partition size for overlayFS.
+     */
+    @RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
+    public long suggestScratchSize() {
+        try {
+            return mService.suggestScratchSize();
+        } catch (RemoteException e) {
+            throw new RuntimeException(e.toString());
+        }
+    }
 }
diff --git a/core/java/android/os/image/IDynamicSystemService.aidl b/core/java/android/os/image/IDynamicSystemService.aidl
index df0a69b..a5a40ad 100644
--- a/core/java/android/os/image/IDynamicSystemService.aidl
+++ b/core/java/android/os/image/IDynamicSystemService.aidl
@@ -125,4 +125,9 @@
      *                      valid VBMeta block to retrieve the AVB key from.
      */
     boolean getAvbPublicKey(out AvbPublicKey dst);
+
+    /**
+     * Returns the suggested scratch partition size for overlayFS.
+     */
+    long suggestScratchSize();
 }
diff --git a/core/java/android/permission/IPermissionController.aidl b/core/java/android/permission/IPermissionController.aidl
index f011395..084cc2f 100644
--- a/core/java/android/permission/IPermissionController.aidl
+++ b/core/java/android/permission/IPermissionController.aidl
@@ -44,4 +44,7 @@
     void grantOrUpgradeDefaultRuntimePermissions(in AndroidFuture callback);
     void notifyOneTimePermissionSessionTimeout(String packageName);
     void updateUserSensitiveForApp(int uid, in AndroidFuture callback);
+    void getPrivilegesDescriptionStringForProfile(
+            in String deviceProfileName,
+            in AndroidFuture<String> callback);
 }
diff --git a/core/java/android/permission/IPermissionManager.aidl b/core/java/android/permission/IPermissionManager.aidl
index 441908d..8c105be 100644
--- a/core/java/android/permission/IPermissionManager.aidl
+++ b/core/java/android/permission/IPermissionManager.aidl
@@ -33,25 +33,21 @@
 
     PermissionGroupInfo getPermissionGroupInfo(String groupName, int flags);
 
-    PermissionInfo getPermissionInfo(String permName, String packageName, int flags);
+    PermissionInfo getPermissionInfo(String permissionName, String packageName, int flags);
 
     ParceledListSlice queryPermissionsByGroup(String groupName, int flags);
 
-    boolean addPermission(in PermissionInfo info, boolean async);
+    boolean addPermission(in PermissionInfo permissionInfo, boolean async);
 
-    void removePermission(String name);
+    void removePermission(String permissionName);
 
-    int getPermissionFlags(String permName, String packageName, int userId);
+    int getPermissionFlags(String packageName, String permissionName, int userId);
 
-    void updatePermissionFlags(String permName, String packageName, int flagMask,
+    void updatePermissionFlags(String packageName, String permissionName, int flagMask,
             int flagValues, boolean checkAdjustPolicyFlagPermission, int userId);
 
     void updatePermissionFlagsForAllApps(int flagMask, int flagValues, int userId);
 
-    int checkPermission(String permName, String pkgName, int userId);
-
-    int checkUidPermission(String permName, int uid);
-
     void addOnPermissionsChangeListener(in IOnPermissionsChangeListener listener);
 
     void removeOnPermissionsChangeListener(in IOnPermissionsChangeListener listener);
@@ -65,16 +61,15 @@
     boolean removeAllowlistedRestrictedPermission(String packageName, String permissionName,
             int flags, int userId);
 
-    void grantRuntimePermission(String packageName, String permName, int userId);
+    void grantRuntimePermission(String packageName, String permissionName, int userId);
 
-    void revokeRuntimePermission(String packageName, String permName, int userId, String reason);
+    void revokeRuntimePermission(String packageName, String permissionName, int userId,
+            String reason);
 
-    void resetRuntimePermissions();
+    boolean shouldShowRequestPermissionRationale(String packageName, String permissionName,
+            int userId);
 
-    boolean shouldShowRequestPermissionRationale(String permName,
-            String packageName, int userId);
-
-    boolean isPermissionRevokedByPolicy(String permName, String packageName, int userId);
+    boolean isPermissionRevokedByPolicy(String packageName, String permissionName, int userId);
 
     List<SplitPermissionInfoParcelable> getSplitPermissions();
 
diff --git a/core/java/android/permission/PermGroupUsage.java b/core/java/android/permission/PermGroupUsage.java
new file mode 100644
index 0000000..3bee401
--- /dev/null
+++ b/core/java/android/permission/PermGroupUsage.java
@@ -0,0 +1,79 @@
+/*
+ * 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.permission;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+/**
+ * Represents the usage of a permission group by an app. Supports package name, user, permission
+ * group, whether or not the access is running or recent, whether the access is tied to a phone
+ * call, and an optional special attribution.
+ *
+ * @hide
+ */
+public final class PermGroupUsage {
+
+    private final String mPackageName;
+    private final int mUid;
+    private final String mPermGroupName;
+    private final boolean mIsActive;
+    private final boolean mIsPhoneCall;
+    private final CharSequence mAttribution;
+
+    PermGroupUsage(@NonNull String packageName, int uid,
+            @NonNull String permGroupName, boolean isActive, boolean isPhoneCall,
+            @Nullable CharSequence attribution) {
+        this.mPackageName = packageName;
+        this.mUid = uid;
+        this.mPermGroupName = permGroupName;
+        this.mIsActive = isActive;
+        this.mIsPhoneCall = isPhoneCall;
+        this.mAttribution = attribution;
+    }
+
+    public @NonNull String getPackageName() {
+        return mPackageName;
+    }
+
+    public int getUid() {
+        return mUid;
+    }
+
+    public @NonNull String getPermGroupName() {
+        return mPermGroupName;
+    }
+
+    public boolean isActive() {
+        return mIsActive;
+    }
+
+    public boolean isPhoneCall() {
+        return mIsPhoneCall;
+    }
+
+    public @Nullable CharSequence getAttribution() {
+        return mAttribution;
+    }
+
+    @Override
+    public String toString() {
+        return getClass().getSimpleName() + "@" + Integer.toHexString(System.identityHashCode(this))
+                + "packageName: " + mPackageName + ", UID: " + mUid + ", permGroup: "
+                + mPermGroupName + ", isActive: " + mIsActive + ",attribution: " + mAttribution;
+    }
+}
diff --git a/core/java/android/permission/PermissionControllerService.java b/core/java/android/permission/PermissionControllerService.java
index e2e6140..8441fea 100644
--- a/core/java/android/permission/PermissionControllerService.java
+++ b/core/java/android/permission/PermissionControllerService.java
@@ -32,6 +32,7 @@
 import android.Manifest;
 import android.annotation.BinderThread;
 import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
 import android.app.Service;
 import android.app.admin.DevicePolicyManager.PermissionGrantState;
@@ -61,6 +62,7 @@
 import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.Executor;
 import java.util.function.Consumer;
@@ -283,6 +285,22 @@
         throw new AbstractMethodError("Must be overridden in implementing class");
     }
 
+    /**
+     * Get a user-readable sentence, describing the set of privileges that are to be granted to a
+     * companion app managing a device of the given profile.
+     *
+     * @param deviceProfileName the
+     *      {@link android.companion.AssociationRequest.DeviceProfile device profile} name
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(Manifest.permission.MANAGE_COMPANION_DEVICES)
+    @NonNull
+    public String getPrivilegesDescriptionStringForProfile(@NonNull String deviceProfileName) {
+        throw new AbstractMethodError("Must be overridden in implementing class");
+    }
+
     @Override
     public final @NonNull IBinder onBind(Intent intent) {
         return new IPermissionController.Stub() {
@@ -517,6 +535,24 @@
 
                 PermissionControllerService.this.dump(fd, writer, args);
             }
+
+            @Override
+            public void getPrivilegesDescriptionStringForProfile(
+                    @NonNull String deviceProfileName,
+                    @NonNull AndroidFuture<String> callback) {
+                checkStringNotEmpty(deviceProfileName);
+                Objects.requireNonNull(callback);
+
+                enforceSomePermissionsGrantedToCaller(Manifest.permission.MANAGE_COMPANION_DEVICES);
+
+                try {
+                    callback.complete(PermissionControllerService
+                            .this
+                            .getPrivilegesDescriptionStringForProfile(deviceProfileName));
+                } catch (Throwable t) {
+                    callback.completeExceptionally(t);
+                }
+            }
         };
     }
 }
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index 87f3764..ff01011 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -41,6 +41,7 @@
 import android.content.pm.PermissionGroupInfo;
 import android.content.pm.PermissionInfo;
 import android.content.pm.permission.SplitPermissionInfoParcelable;
+import android.media.AudioManager;
 import android.os.Build;
 import android.os.Handler;
 import android.os.Looper;
@@ -114,6 +115,7 @@
 
     private final ArrayMap<PackageManager.OnPermissionsChangedListener,
             IOnPermissionsChangeListener> mPermissionListeners = new ArrayMap<>();
+    private PermissionUsageHelper mUsageHelper;
 
     private List<SplitPermissionInfo> mSplitPermissionInfos;
 
@@ -303,7 +305,7 @@
     public boolean isPermissionRevokedByPolicy(@NonNull String packageName,
             @NonNull String permissionName) {
         try {
-            return mPermissionManager.isPermissionRevokedByPolicy(permissionName, packageName,
+            return mPermissionManager.isPermissionRevokedByPolicy(packageName, permissionName,
                     mContext.getUserId());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -330,7 +332,7 @@
      * @param permissionName the permission name to grant
      * @param user the user for which to grant the permission
      *
-     * @see #revokeRuntimePermission(String, String, android.os.UserHandle)
+     * @see #revokeRuntimePermission(String, String, android.os.UserHandle, String)
      *
      * @hide
      */
@@ -409,7 +411,7 @@
     public int getPermissionFlags(@NonNull String packageName, @NonNull String permissionName,
             @NonNull UserHandle user) {
         try {
-            return mPermissionManager.getPermissionFlags(permissionName, packageName,
+            return mPermissionManager.getPermissionFlags(packageName, permissionName,
                     user.getIdentifier());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -448,7 +450,7 @@
         try {
             final boolean checkAdjustPolicyFlagPermission =
                     mContext.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.Q;
-            mPermissionManager.updatePermissionFlags(permissionName, packageName, flagMask,
+            mPermissionManager.updatePermissionFlags(packageName, permissionName, flagMask,
                     flagValues, checkAdjustPolicyFlagPermission, user.getIdentifier());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
@@ -719,8 +721,8 @@
     public boolean shouldShowRequestPermissionRationale(@NonNull String permissionName) {
         try {
             final String packageName = mContext.getPackageName();
-            return mPermissionManager.shouldShowRequestPermissionRationale(permissionName,
-                    packageName, mContext.getUserId());
+            return mPermissionManager.shouldShowRequestPermissionRationale(packageName,
+                    permissionName, mContext.getUserId());
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -854,6 +856,22 @@
     }
 
     /**
+     * @return A list of permission groups currently or recently used by all apps by all users in
+     * the current profile group.
+     *
+     * @hide
+     */
+    @NonNull
+    @RequiresPermission(Manifest.permission.GET_APP_OPS_STATS)
+    public List<PermGroupUsage> getIndicatorAppOpUsageData() {
+        // Lazily initialize the usage helper
+        if (mUsageHelper == null) {
+            mUsageHelper = new PermissionUsageHelper(mContext);
+        }
+        return mUsageHelper.getOpUsageData(new AudioManager().isMicrophoneMute());
+    }
+
+    /**
      * Gets the list of packages that have permissions that specified
      * {@code requestDontAutoRevokePermissions=true} in their
      * {@code application} manifest declaration.
@@ -1218,7 +1236,7 @@
     private static int checkPackageNamePermissionUncached(
             String permName, String pkgName, @UserIdInt int userId) {
         try {
-            return ActivityThread.getPermissionManager().checkPermission(
+            return ActivityThread.getPackageManager().checkPermission(
                     permName, pkgName, userId);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
diff --git a/core/java/android/permission/PermissionUsageHelper.java b/core/java/android/permission/PermissionUsageHelper.java
new file mode 100644
index 0000000..7243234
--- /dev/null
+++ b/core/java/android/permission/PermissionUsageHelper.java
@@ -0,0 +1,442 @@
+/*
+ * 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.permission;
+
+import static android.Manifest.permission_group.CAMERA;
+import static android.Manifest.permission_group.LOCATION;
+import static android.Manifest.permission_group.MICROPHONE;
+import static android.app.AppOpsManager.OPSTR_CAMERA;
+import static android.app.AppOpsManager.OPSTR_COARSE_LOCATION;
+import static android.app.AppOpsManager.OPSTR_FINE_LOCATION;
+import static android.app.AppOpsManager.OPSTR_PHONE_CALL_CAMERA;
+import static android.app.AppOpsManager.OPSTR_PHONE_CALL_MICROPHONE;
+import static android.app.AppOpsManager.OPSTR_RECORD_AUDIO;
+import static android.app.AppOpsManager.OP_FLAGS_ALL_TRUSTED;
+import static android.app.AppOpsManager.opToPermission;
+import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED;
+
+import android.annotation.NonNull;
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.location.LocationManager;
+import android.os.Process;
+import android.os.UserHandle;
+import android.provider.DeviceConfig;
+import android.util.ArrayMap;
+import android.util.Pair;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * A helper which gets all apps which have used microphone, camera, and possible location
+ * permissions within a certain timeframe, as well as possible special attributions, and if the
+ * usage is a phone call.
+ *
+ * @hide
+ */
+public class PermissionUsageHelper {
+
+    /** Whether to show the mic and camera icons.  */
+    private static final String PROPERTY_CAMERA_MIC_ICONS_ENABLED = "camera_mic_icons_enabled";
+
+    /** Whether to show the location indicators. */
+    private static final String PROPERTY_LOCATION_INDICATORS_ENABLED =
+            "location_indicators_enabled";
+
+    /** How long after an access to show it as "recent" */
+    private static final String RECENT_ACCESS_TIME_MS = "recent_acccess_time_ms";
+
+    /** How long after an access to show it as "running" */
+    private static final String RUNNING_ACCESS_TIME_MS = "running_acccess_time_ms";
+
+    private static final long DEFAULT_RUNNING_TIME_MS = 5000L;
+    private static final long DEFAULT_RECENT_TIME_MS = 30000L;
+
+    private static boolean shouldShowIndicators() {
+        return true;
+        // TODO ntmyren: remove true set when device config is configured correctly
+        //DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
+        //PROPERTY_CAMERA_MIC_ICONS_ENABLED, true);
+    }
+
+    private static boolean shouldShowLocationIndicator() {
+        return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
+                PROPERTY_LOCATION_INDICATORS_ENABLED, false);
+    }
+
+    private static long getRecentThreshold(Long now) {
+        return now - DeviceConfig.getLong(DeviceConfig.NAMESPACE_PRIVACY,
+                RECENT_ACCESS_TIME_MS, DEFAULT_RECENT_TIME_MS);
+    }
+
+    private static long getRunningThreshold(Long now) {
+        return now - DeviceConfig.getLong(DeviceConfig.NAMESPACE_PRIVACY,
+                RUNNING_ACCESS_TIME_MS, DEFAULT_RUNNING_TIME_MS);
+    }
+
+    private static final List<String> LOCATION_OPS = List.of(
+            OPSTR_COARSE_LOCATION,
+            OPSTR_FINE_LOCATION
+    );
+
+    private static final List<String> MIC_OPS = List.of(
+            OPSTR_PHONE_CALL_CAMERA,
+            OPSTR_RECORD_AUDIO
+    );
+
+    private static final List<String> CAMERA_OPS = List.of(
+            OPSTR_PHONE_CALL_CAMERA,
+            OPSTR_CAMERA
+    );
+
+    private static @NonNull String getGroupForOp(String op) {
+        switch(op) {
+            case OPSTR_RECORD_AUDIO:
+                return MICROPHONE;
+            case OPSTR_CAMERA:
+                return CAMERA;
+            case OPSTR_PHONE_CALL_MICROPHONE:
+            case OPSTR_PHONE_CALL_CAMERA:
+                return op;
+            case OPSTR_COARSE_LOCATION:
+            case OPSTR_FINE_LOCATION:
+                return LOCATION;
+            default:
+                throw new IllegalArgumentException("Unknown app op: " + op);
+        }
+    }
+
+    private Context mContext;
+    private Map<UserHandle, Context> mUserContexts;
+    private PackageManager mPkgManager;
+    private AppOpsManager mAppOpsManager;
+
+    /**
+     * Constructor for PermissionUsageHelper
+     * @param context The context from which to derive the package information
+     */
+    public PermissionUsageHelper(Context context) {
+        mContext = context;
+        mPkgManager = context.getPackageManager();
+        mAppOpsManager = context.getSystemService(AppOpsManager.class);
+        mUserContexts = Map.of(Process.myUserHandle(), mContext);
+    }
+
+    private Context getUserContext(UserHandle user) {
+        if (!(mUserContexts.containsKey(user))) {
+            mUserContexts.put(user, mContext.createContextAsUser(user, 0));
+        }
+        return mUserContexts.get(user);
+    }
+
+    /**
+     * @see PermissionManager.getIndicatorAppOpUsageData
+     */
+    public List<PermGroupUsage> getOpUsageData(boolean isMicMuted) {
+        List<PermGroupUsage> usages = new ArrayList<>();
+
+        if (!shouldShowIndicators()) {
+            return usages;
+        }
+
+        List<String> ops = new ArrayList<>(CAMERA_OPS);
+        if (shouldShowLocationIndicator()) {
+            ops.addAll(LOCATION_OPS);
+        }
+        if (!isMicMuted) {
+            ops.addAll(MIC_OPS);
+        }
+
+        Map<String, List<OpUsage>> rawUsages = getOpUsages(ops);
+        Map<PackageAttribution, CharSequence> packagesWithAttributionLabels =
+                getTrustedAttributions(rawUsages.get(MICROPHONE));
+
+        List<String> usedPermGroups = new ArrayList<>(rawUsages.keySet());
+        for (int permGroupNum = 0; permGroupNum < usedPermGroups.size(); permGroupNum++) {
+            boolean isPhone = false;
+            String permGroup = usedPermGroups.get(permGroupNum);
+            if (permGroup.equals(OPSTR_PHONE_CALL_MICROPHONE)) {
+                isPhone = true;
+                permGroup = MICROPHONE;
+            } else if (permGroup.equals(OPSTR_PHONE_CALL_CAMERA)) {
+                isPhone = true;
+                permGroup = CAMERA;
+            }
+
+            int numUsages = rawUsages.get(permGroup).size();
+            for (int usageNum = 0; usageNum < numUsages; usageNum++) {
+                OpUsage usage = rawUsages.get(permGroup).get(usageNum);
+                usages.add(new PermGroupUsage(usage.packageName, usage.uid, permGroup,
+                        usage.isRunning, isPhone,
+                        packagesWithAttributionLabels.get(usage.toPackageAttr())));
+            }
+        }
+
+        return usages;
+    }
+
+    /**
+     * Get the raw usages from the system, and then parse out the ones that are not recent enough,
+     * determine which permission group each belongs in, and removes duplicates (if the same app
+     * uses multiple permissions of the same group). Stores the package name, attribution tag, user,
+     * running/recent info, if the usage is a phone call, per permission group.
+     *
+     * @param opNames a list of op names to get usage for
+     *
+     * @return A map of permission group -> list of usages that are recent or running
+     */
+    private Map<String, List<OpUsage>> getOpUsages(List<String> opNames) {
+        List<AppOpsManager.PackageOps> ops;
+        try {
+            ops = mAppOpsManager.getPackagesForOps(opNames.toArray(new String[opNames.size()]));
+        } catch (NullPointerException e) {
+            // older builds might not support all the app-ops requested
+            return Collections.emptyMap();
+        }
+
+        long now = System.currentTimeMillis();
+        long recentThreshold = getRecentThreshold(now);
+        long runningThreshold = getRunningThreshold(now);
+        int opFlags = OP_FLAGS_ALL_TRUSTED;
+        Map<String, Map<PackageAttribution, OpUsage>> usages = new ArrayMap<>();
+
+        int numPkgOps = ops.size();
+        for (int pkgOpNum = 0; pkgOpNum < numPkgOps; pkgOpNum++) {
+            AppOpsManager.PackageOps pkgOps = ops.get(pkgOpNum);
+            int uid = pkgOps.getUid();
+            UserHandle user = UserHandle.getUserHandleForUid(uid);
+            String packageName = pkgOps.getPackageName();
+
+            int numOpEntries = pkgOps.getOps().size();
+            for (int opEntryNum = 0; opEntryNum < numOpEntries; opEntryNum++) {
+                AppOpsManager.OpEntry opEntry = pkgOps.getOps().get(opEntryNum);
+                String op = opEntry.getOpStr();
+                List<String> attributionTags =
+                        new ArrayList<>(opEntry.getAttributedOpEntries().keySet());
+
+                int numAttrEntries = opEntry.getAttributedOpEntries().size();
+                for (int attrOpEntryNum = 0; attrOpEntryNum < numAttrEntries; attrOpEntryNum++) {
+                    String attributionTag = attributionTags.get(attrOpEntryNum);
+                    AppOpsManager.AttributedOpEntry attrOpEntry =
+                            opEntry.getAttributedOpEntries().get(attributionTag);
+
+                    long lastAccessTime = attrOpEntry.getLastAccessTime(opFlags);
+                    if (lastAccessTime < recentThreshold) {
+                        continue;
+                    }
+                    if (!isUserSensitive(packageName, user, op)
+                            && !isLocationProvider(packageName, user)) {
+                        continue;
+                    }
+
+                    boolean isRunning = attrOpEntry.isRunning()
+                            || lastAccessTime >= runningThreshold;
+
+                    OpUsage proxyUsage = null;
+                    AppOpsManager.OpEventProxyInfo proxy = attrOpEntry.getLastProxyInfo(opFlags);
+                    if (proxy != null && proxy.getPackageName() != null) {
+                        proxyUsage = new OpUsage(proxy.getPackageName(), proxy.getAttributionTag(),
+                                uid, lastAccessTime, isRunning, null);
+                    }
+
+                    String permGroupName = getGroupForOp(op);
+                    OpUsage usage = new OpUsage(packageName, attributionTag, uid,
+                            lastAccessTime, isRunning, proxyUsage);
+
+                    PackageAttribution packageAttr = usage.toPackageAttr();
+                    if (!usages.containsKey(permGroupName)) {
+                        ArrayMap<PackageAttribution, OpUsage> map = new ArrayMap<>();
+                        map.put(packageAttr, usage);
+                        usages.put(permGroupName, map);
+                    } else {
+                        Map<PackageAttribution, OpUsage> permGroupUsages =
+                                usages.get(permGroupName);
+                        if (!permGroupUsages.containsKey(packageAttr)) {
+                            permGroupUsages.put(packageAttr, usage);
+                        } else if (usage.lastAccessTime
+                                > permGroupUsages.get(packageAttr).lastAccessTime) {
+                            permGroupUsages.put(packageAttr, usage);
+                        }
+                    }
+                }
+            }
+        }
+
+        Map<String, List<OpUsage>> flattenedUsages = new ArrayMap<>();
+        List<String> permGroups = new ArrayList<>(usages.keySet());
+        for (int i = 0; i < permGroups.size(); i++) {
+            String permGroupName = permGroups.get(i);
+            flattenedUsages.put(permGroupName, new ArrayList<>(usages.get(permGroupName).values()));
+        }
+        return flattenedUsages;
+    }
+
+    // TODO ntmyren: create JavaDoc and copy merging of proxy chains and trusted labels from
+    //  "usages" livedata in ReviewOngoingUsageLiveData
+    private Map<PackageAttribution, CharSequence> getTrustedAttributions(List<OpUsage> usages) {
+        ArrayMap<PackageAttribution, CharSequence> attributions = new ArrayMap<>();
+        if (usages == null) {
+            return attributions;
+        }
+        Set<List<OpUsage>> proxyChains = getProxyChains(usages);
+        Map<Pair<String, UserHandle>, CharSequence> trustedLabels = getTrustedAttributionLabels();
+
+
+        return attributions;
+    }
+
+    // TODO ntmyren: create JavaDoc and copy proxyChainsLiveData from ReviewOngoingUsageLiveData
+    private Set<List<OpUsage>> getProxyChains(List<OpUsage> usages) {
+        Map<PackageAttribution, List<OpUsage>> inProgressChains = new ArrayMap<>();
+        List<OpUsage> remainingUsages = new ArrayList<>(usages);
+        // find all one-link chains (that is, all proxied apps whose proxy is not included in
+        // the usage list)
+        for (int usageNum = 0; usageNum < usages.size(); usageNum++) {
+            OpUsage usage = usages.get(usageNum);
+            PackageAttribution usageAttr = usage.toPackageAttr();
+            if (usage.proxy == null) {
+                continue;
+            }
+            PackageAttribution proxyAttr = usage.proxy.toPackageAttr();
+            boolean proxyExists = false;
+            for (int otherUsageNum = 0; otherUsageNum < usages.size(); otherUsageNum++) {
+                if (usages.get(otherUsageNum).toPackageAttr().equals(proxyAttr)) {
+                    proxyExists = true;
+                    break;
+                }
+            }
+
+            if (!proxyExists) {
+                inProgressChains.put(usageAttr, List.of(usage));
+                remainingUsages.remove(usage);
+            }
+        }
+
+        // find all possible starting points for chains
+        for (int i = 0; i < usages.size(); i++) {
+            OpUsage usage = usages.get(i);
+        }
+
+            /*
+            // find all possible starting points for chains
+            for (usage in remainingProxyChainUsages.toList()) {
+                // if this usage has no proxy, but proxies another usage, it is the start of a chain
+                val usageAttr = getPackageAttr(usage)
+                if (usage.proxyAccess == null && remainingProxyChainUsages.any {
+                    it.proxyAccess != null && getPackageAttr(it.proxyAccess) == usageAttr
+                }) {
+                    inProgressChains[usageAttr] = mutableListOf(usage)
+                }
+
+                // if this usage is a chain start, or no usage have this usage as a proxy, remove it
+                if (usage.proxyAccess == null) {
+                    remainingProxyChainUsages.remove(usage)
+                }
+            }
+
+             */
+
+        return null;
+    }
+
+    // TODO ntmyren: create JavaDoc and copy trustedAttrsLiveData from ReviewOngoingUsageLiveData
+    private Map<Pair<String, UserHandle>, CharSequence> getTrustedAttributionLabels() {
+        return new ArrayMap<>();
+    }
+
+    private boolean isUserSensitive(String packageName, UserHandle user, String op) {
+        if (op.equals(OPSTR_PHONE_CALL_CAMERA) || op.equals(OPSTR_PHONE_CALL_MICROPHONE)) {
+            return true;
+        }
+
+        if (opToPermission(op) == null) {
+            return false;
+        }
+
+        int permFlags = mPkgManager.getPermissionFlags(opToPermission(op), packageName, user);
+        return (permFlags & FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED) != 0;
+    }
+
+    private boolean isLocationProvider(String packageName, UserHandle user) {
+        return getUserContext(user)
+                .getSystemService(LocationManager.class).isProviderPackage(packageName);
+    }
+
+    /**
+     * Represents the usage of an App op by a particular package and attribution
+     */
+    private static class OpUsage {
+
+        public final String packageName;
+        public final String attributionTag;
+        public final int uid;
+        public final long lastAccessTime;
+        public final OpUsage proxy;
+        public final boolean isRunning;
+
+        OpUsage(String packageName, String attributionTag, int uid, long lastAccessTime,
+                boolean isRunning, OpUsage proxy) {
+            this.isRunning = isRunning;
+            this.packageName = packageName;
+            this.attributionTag = attributionTag;
+            this.uid = uid;
+            this.lastAccessTime = lastAccessTime;
+            this.proxy = proxy;
+        }
+
+        public PackageAttribution toPackageAttr() {
+            return new PackageAttribution(packageName, attributionTag, uid);
+        }
+    }
+
+    /**
+     * A unique identifier for one package attribution, made up of attribution tag, package name
+     * and user
+     */
+    private static class PackageAttribution {
+        public final String packageName;
+        public final String attributionTag;
+        public final int uid;
+
+        PackageAttribution(String packageName, String attributionTag, int uid) {
+            this.packageName = packageName;
+            this.attributionTag = attributionTag;
+            this.uid = uid;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (!(obj instanceof PackageAttribution)) {
+                return false;
+            }
+            PackageAttribution other = (PackageAttribution) obj;
+            return Objects.equals(packageName, other.packageName) && Objects.equals(attributionTag,
+                    other.attributionTag) && Objects.equals(uid, other.uid);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(packageName, attributionTag, uid);
+        }
+    }
+}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 4632621..69ca28a 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -9194,6 +9194,39 @@
         public static final int ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU = 0x1;
 
         /**
+         * The size of the accessibility floating menu.
+         * <ul>
+         *     <li> 0 = small size
+         *     <li> 1 = large size
+         * </ul>
+         *
+         * @hide
+         */
+        public static final String ACCESSIBILITY_FLOATING_MENU_SIZE =
+                "accessibility_floating_menu_size";
+
+        /**
+         * The icon type of the accessibility floating menu.
+         * <ul>
+         *     <li> 0 = full circle type
+         *     <li> 1 = half circle type
+         * </ul>
+         *
+         * @hide
+         */
+        public static final String ACCESSIBILITY_FLOATING_MENU_ICON_TYPE =
+                "accessibility_floating_menu_icon_type";
+
+        /**
+         * The opacity value for the accessibility floating menu fade out effect, from 0.0
+         * (transparent) to 1.0 (opaque).
+         *
+         * @hide
+         */
+        public static final String ACCESSIBILITY_FLOATING_MENU_OPACITY =
+                "accessibility_floating_menu_opacity";
+
+        /**
          * Whether the Adaptive connectivity option is enabled.
          *
          * @hide
@@ -13423,6 +13456,16 @@
         @TestApi
         public static final String HIDDEN_API_POLICY = "hidden_api_policy";
 
+         /**
+         * Flag for forcing {@link com.android.server.compat.OverrideValidatorImpl}
+         * to consider this a non-debuggable build.
+         *
+         * @hide
+         */
+        public static final String FORCE_NON_DEBUGGABLE_FINAL_BUILD_FOR_COMPAT =
+                "force_non_debuggable_final_build_for_compat";
+
+
         /**
          * Current version of signed configuration applied.
          *
diff --git a/core/java/android/security/ConfirmationPrompt.java b/core/java/android/security/ConfirmationPrompt.java
index f67af85..2329037 100644
--- a/core/java/android/security/ConfirmationPrompt.java
+++ b/core/java/android/security/ConfirmationPrompt.java
@@ -21,6 +21,7 @@
 import android.content.Context;
 import android.provider.Settings;
 import android.provider.Settings.SettingNotFoundException;
+import android.security.keystore.AndroidKeyStoreProvider;
 import android.text.TextUtils;
 import android.util.Log;
 
@@ -36,15 +37,15 @@
  * compromised. Implementing confirmation prompts with these guarantees requires dedicated
  * hardware-support and may not always be available.
  *
- * <p>Confirmation prompts are typically used with an external entitity - the <i>Relying Party</i> -
+ * <p>Confirmation prompts are typically used with an external entity - the <i>Relying Party</i> -
  * in the following way. The setup steps are as follows:
  * <ul>
  * <li> Before first use, the application generates a key-pair with the
  * {@link android.security.keystore.KeyGenParameterSpec.Builder#setUserConfirmationRequired
- * CONFIRMATION tag} set. Device attestation,
- * e.g. {@link java.security.KeyStore#getCertificateChain getCertificateChain()}, is used to
- * generate a certificate chain that includes the public key (<code>Kpub</code> in the following)
- * of the newly generated key.
+ * CONFIRMATION tag} set. AndroidKeyStore key attestation, e.g.,
+ * {@link android.security.keystore.KeyGenParameterSpec.Builder#setAttestationChallenge(byte[])}
+ * is used to generate a certificate chain that includes the public key (<code>Kpub</code> in the
+ * following) of the newly generated key.
  * <li> The application sends <code>Kpub</code> and the certificate chain resulting from device
  * attestation to the <i>Relying Party</i>.
  * <li> The <i>Relying Party</i> validates the certificate chain which involves checking the root
@@ -78,9 +79,10 @@
  * previously created nonce. If all checks passes, the transaction is executed.
  * </ul>
  *
- * <p>A common way of implementing the "<code>promptText</code> is what is expected" check in the
- * last bullet, is to have the <i>Relying Party</i> generate <code>promptText</code> and store it
- * along the nonce in the <code>extraData</code> blob.
+ * <p>Note: It is vital to check the <code>promptText</code> because this is the only part that
+ * the user has approved. To avoid writing parsers for all of the possible locales, it is
+ * recommended that the <i>Relying Party</i> uses the same string generator as used on the device
+ * and performs a simple string comparison.
  */
 public class ConfirmationPrompt {
     private static final String TAG = "ConfirmationPrompt";
@@ -92,6 +94,14 @@
     private Context mContext;
 
     private final KeyStore mKeyStore = KeyStore.getInstance();
+    private AndroidProtectedConfirmation mProtectedConfirmation;
+
+    private AndroidProtectedConfirmation getService() {
+        if (mProtectedConfirmation == null) {
+            mProtectedConfirmation = new AndroidProtectedConfirmation();
+        }
+        return mProtectedConfirmation;
+    }
 
     private void doCallback(int responseCode, byte[] dataThatWasConfirmed,
             ConfirmationCallback callback) {
@@ -119,6 +129,32 @@
         }
     }
 
+    private void doCallback2(int responseCode, byte[] dataThatWasConfirmed,
+            ConfirmationCallback callback) {
+        switch (responseCode) {
+            case AndroidProtectedConfirmation.ERROR_OK:
+                callback.onConfirmed(dataThatWasConfirmed);
+                break;
+
+            case AndroidProtectedConfirmation.ERROR_CANCELED:
+                callback.onDismissed();
+                break;
+
+            case AndroidProtectedConfirmation.ERROR_ABORTED:
+                callback.onCanceled();
+                break;
+
+            case AndroidProtectedConfirmation.ERROR_SYSTEM_ERROR:
+                callback.onError(new Exception("System error returned by ConfirmationUI."));
+                break;
+
+            default:
+                callback.onError(new Exception("Unexpected responseCode=" + responseCode
+                        + " from onConfirmtionPromptCompleted() callback."));
+                break;
+        }
+    }
+
     private final android.os.IBinder mCallbackBinder =
             new android.security.IConfirmationPromptCallback.Stub() {
                 @Override
@@ -144,6 +180,29 @@
                 }
             };
 
+    private final android.security.apc.IConfirmationCallback mConfirmationCallback =
+            new android.security.apc.IConfirmationCallback.Stub() {
+                @Override
+                public void onCompleted(int result, byte[] dataThatWasConfirmed)
+                        throws android.os.RemoteException {
+                    if (mCallback != null) {
+                        ConfirmationCallback callback = mCallback;
+                        Executor executor = mExecutor;
+                        mCallback = null;
+                        mExecutor = null;
+                        if (executor == null) {
+                            doCallback2(result, dataThatWasConfirmed, callback);
+                        } else {
+                            executor.execute(new Runnable() {
+                                @Override public void run() {
+                                    doCallback2(result, dataThatWasConfirmed, callback);
+                                }
+                            });
+                        }
+                    }
+                }
+            };
+
     /**
      * A builder that collects arguments, to be shown on the system-provided confirmation prompt.
      */
@@ -211,6 +270,9 @@
     private static final int UI_OPTION_ACCESSIBILITY_MAGNIFIED_FLAG = 1 << 1;
 
     private int getUiOptionsAsFlags() {
+        if (AndroidKeyStoreProvider.isKeystore2Enabled()) {
+            return getUiOptionsAsFlags2();
+        }
         int uiOptionsAsFlags = 0;
         ContentResolver contentResolver = mContext.getContentResolver();
         int inversionEnabled = Settings.Secure.getInt(contentResolver,
@@ -226,6 +288,22 @@
         return uiOptionsAsFlags;
     }
 
+    private int getUiOptionsAsFlags2() {
+        int uiOptionsAsFlags = 0;
+        ContentResolver contentResolver = mContext.getContentResolver();
+        int inversionEnabled = Settings.Secure.getInt(contentResolver,
+                Settings.Secure.ACCESSIBILITY_DISPLAY_INVERSION_ENABLED, 0);
+        if (inversionEnabled == 1) {
+            uiOptionsAsFlags |= AndroidProtectedConfirmation.FLAG_UI_OPTION_INVERTED;
+        }
+        float fontScale = Settings.System.getFloat(contentResolver,
+                Settings.System.FONT_SCALE, (float) 1.0);
+        if (fontScale > 1.0) {
+            uiOptionsAsFlags |= AndroidProtectedConfirmation.FLAG_UI_OPTION_MAGNIFIED;
+        }
+        return uiOptionsAsFlags;
+    }
+
     private static boolean isAccessibilityServiceRunning(Context context) {
         boolean serviceRunning = false;
         try {
@@ -270,29 +348,53 @@
         mCallback = callback;
         mExecutor = executor;
 
-        int uiOptionsAsFlags = getUiOptionsAsFlags();
         String locale = Locale.getDefault().toLanguageTag();
-        int responseCode = mKeyStore.presentConfirmationPrompt(
-                mCallbackBinder, mPromptText.toString(), mExtraData, locale, uiOptionsAsFlags);
-        switch (responseCode) {
-            case KeyStore.CONFIRMATIONUI_OK:
-                return;
+        if (AndroidKeyStoreProvider.isKeystore2Enabled()) {
+            int uiOptionsAsFlags = getUiOptionsAsFlags2();
+            int responseCode = getService().presentConfirmationPrompt(
+                    mConfirmationCallback, mPromptText.toString(), mExtraData, locale,
+                    uiOptionsAsFlags);
+            switch (responseCode) {
+                case AndroidProtectedConfirmation.ERROR_OK:
+                    return;
 
-            case KeyStore.CONFIRMATIONUI_OPERATION_PENDING:
-                throw new ConfirmationAlreadyPresentingException();
+                case AndroidProtectedConfirmation.ERROR_OPERATION_PENDING:
+                    throw new ConfirmationAlreadyPresentingException();
 
-            case KeyStore.CONFIRMATIONUI_UNIMPLEMENTED:
-                throw new ConfirmationNotAvailableException();
+                case AndroidProtectedConfirmation.ERROR_UNIMPLEMENTED:
+                    throw new ConfirmationNotAvailableException();
 
-            case KeyStore.CONFIRMATIONUI_UIERROR:
-                throw new IllegalArgumentException();
+                default:
+                    // Unexpected error code.
+                    Log.w(TAG,
+                            "Unexpected responseCode=" + responseCode
+                                    + " from presentConfirmationPrompt() call.");
+                    throw new IllegalArgumentException();
+            }
+        } else {
+            int uiOptionsAsFlags = getUiOptionsAsFlags();
+            int responseCode = mKeyStore.presentConfirmationPrompt(
+                    mCallbackBinder, mPromptText.toString(), mExtraData, locale, uiOptionsAsFlags);
+            switch (responseCode) {
+                case KeyStore.CONFIRMATIONUI_OK:
+                    return;
 
-            default:
-                // Unexpected error code.
-                Log.w(TAG,
-                        "Unexpected responseCode=" + responseCode
-                        + " from presentConfirmationPrompt() call.");
-                throw new IllegalArgumentException();
+                case KeyStore.CONFIRMATIONUI_OPERATION_PENDING:
+                    throw new ConfirmationAlreadyPresentingException();
+
+                case KeyStore.CONFIRMATIONUI_UNIMPLEMENTED:
+                    throw new ConfirmationNotAvailableException();
+
+                case KeyStore.CONFIRMATIONUI_UIERROR:
+                    throw new IllegalArgumentException();
+
+                default:
+                    // Unexpected error code.
+                    Log.w(TAG,
+                            "Unexpected responseCode=" + responseCode
+                                    + " from presentConfirmationPrompt() call.");
+                    throw new IllegalArgumentException();
+            }
         }
     }
 
@@ -306,17 +408,33 @@
      * @throws IllegalStateException if no prompt is currently being presented.
      */
     public void cancelPrompt() {
-        int responseCode = mKeyStore.cancelConfirmationPrompt(mCallbackBinder);
-        if (responseCode == KeyStore.CONFIRMATIONUI_OK) {
-            return;
-        } else if (responseCode == KeyStore.CONFIRMATIONUI_OPERATION_PENDING) {
-            throw new IllegalStateException();
+        if (AndroidKeyStoreProvider.isKeystore2Enabled()) {
+            int responseCode =
+                    getService().cancelConfirmationPrompt(mConfirmationCallback);
+            if (responseCode == AndroidProtectedConfirmation.ERROR_OK) {
+                return;
+            } else if (responseCode == AndroidProtectedConfirmation.ERROR_OPERATION_PENDING) {
+                throw new IllegalStateException();
+            } else {
+                // Unexpected error code.
+                Log.w(TAG,
+                        "Unexpected responseCode=" + responseCode
+                                + " from cancelConfirmationPrompt() call.");
+                throw new IllegalStateException();
+            }
         } else {
-            // Unexpected error code.
-            Log.w(TAG,
-                    "Unexpected responseCode=" + responseCode
-                    + " from cancelConfirmationPrompt() call.");
-            throw new IllegalStateException();
+            int responseCode = mKeyStore.cancelConfirmationPrompt(mCallbackBinder);
+            if (responseCode == KeyStore.CONFIRMATIONUI_OK) {
+                return;
+            } else if (responseCode == KeyStore.CONFIRMATIONUI_OPERATION_PENDING) {
+                throw new IllegalStateException();
+            } else {
+                // Unexpected error code.
+                Log.w(TAG,
+                        "Unexpected responseCode=" + responseCode
+                                + " from cancelConfirmationPrompt() call.");
+                throw new IllegalStateException();
+            }
         }
     }
 
@@ -330,6 +448,9 @@
         if (isAccessibilityServiceRunning(context)) {
             return false;
         }
+        if (AndroidKeyStoreProvider.isKeystore2Enabled()) {
+            return new AndroidProtectedConfirmation().isConfirmationPromptSupported();
+        }
         return KeyStore.getInstance().isConfirmationPromptSupported();
     }
 }
diff --git a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
index b34c2dc..aeeaa97 100644
--- a/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
+++ b/core/java/android/service/autofill/augmented/AugmentedAutofillService.java
@@ -26,6 +26,8 @@
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.app.Service;
+import android.app.assist.AssistStructure.ViewNode;
+import android.app.assist.AssistStructure.ViewNodeParcelable;
 import android.content.ComponentName;
 import android.content.Intent;
 import android.graphics.Rect;
@@ -415,6 +417,8 @@
         @GuardedBy("mLock")
         private AutofillValue mFocusedValue;
         @GuardedBy("mLock")
+        private ViewNode mFocusedViewNode;
+        @GuardedBy("mLock")
         private IFillCallback mCallback;
 
         /**
@@ -532,6 +536,7 @@
             synchronized (mLock) {
                 mFocusedId = focusedId;
                 mFocusedValue = focusedValue;
+                mFocusedViewNode = null;
                 if (mCallback != null) {
                     try {
                         if (!mCallback.isCompleted()) {
@@ -570,6 +575,25 @@
             }
         }
 
+        @Nullable
+        public ViewNode getFocusedViewNode() {
+            synchronized (mLock) {
+                if (mFocusedViewNode == null) {
+                    try {
+                        final ViewNodeParcelable viewNodeParcelable = mClient.getViewNodeParcelable(
+                                mFocusedId);
+                        if (viewNodeParcelable != null) {
+                            mFocusedViewNode = viewNodeParcelable.getViewNode();
+                        }
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "Error getting the ViewNode of the focused view: " + e);
+                        return null;
+                    }
+                }
+                return mFocusedViewNode;
+            }
+        }
+
         void logEvent(@ReportEvent int event) {
             if (sVerbose) Log.v(TAG, "returnAndLogResult(): " + event);
             long duration = -1;
diff --git a/core/java/android/service/autofill/augmented/FillRequest.java b/core/java/android/service/autofill/augmented/FillRequest.java
index 6927cf6..f7f721a 100644
--- a/core/java/android/service/autofill/augmented/FillRequest.java
+++ b/core/java/android/service/autofill/augmented/FillRequest.java
@@ -19,6 +19,7 @@
 import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.annotation.TestApi;
+import android.app.assist.AssistStructure.ViewNode;
 import android.content.ComponentName;
 import android.service.autofill.augmented.AugmentedAutofillService.AutofillProxy;
 import android.view.autofill.AutofillId;
@@ -81,6 +82,14 @@
     }
 
     /**
+     * Gets the current {@link ViewNode} information of the field that triggered the request.
+     */
+    @Nullable
+    public ViewNode getFocusedViewNode() {
+        return mProxy.getFocusedViewNode();
+    }
+
+    /**
      * Gets the Smart Suggestions object used to embed the autofill UI.
      *
      * @return object used to embed the autofill UI, or {@code null} if not supported.
@@ -98,7 +107,7 @@
 
 
 
-    // Code below generated by codegen v1.0.14.
+    // Code below generated by codegen v1.0.22.
     //
     // DO NOT MODIFY!
     // CHECKSTYLE:OFF Generated code
@@ -151,10 +160,10 @@
     }
 
     @DataClass.Generated(
-            time = 1577399314707L,
-            codegenVersion = "1.0.14",
+            time = 1608160139217L,
+            codegenVersion = "1.0.22",
             sourceFile = "frameworks/base/core/java/android/service/autofill/augmented/FillRequest.java",
-            inputSignatures = "private final @android.annotation.NonNull android.service.autofill.augmented.AugmentedAutofillService.AutofillProxy mProxy\nprivate final @android.annotation.Nullable android.view.inputmethod.InlineSuggestionsRequest mInlineSuggestionsRequest\npublic  int getTaskId()\npublic @android.annotation.NonNull android.content.ComponentName getActivityComponent()\npublic @android.annotation.NonNull android.view.autofill.AutofillId getFocusedId()\npublic @android.annotation.NonNull android.view.autofill.AutofillValue getFocusedValue()\npublic @android.annotation.Nullable android.service.autofill.augmented.PresentationParams getPresentationParams()\n  java.lang.String proxyToString()\nclass FillRequest extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genToString=true, genBuilder=false, genHiddenConstructor=true)")
+            inputSignatures = "private final @android.annotation.NonNull android.service.autofill.augmented.AugmentedAutofillService.AutofillProxy mProxy\nprivate final @android.annotation.Nullable android.view.inputmethod.InlineSuggestionsRequest mInlineSuggestionsRequest\npublic  int getTaskId()\npublic @android.annotation.NonNull android.content.ComponentName getActivityComponent()\npublic @android.annotation.NonNull android.view.autofill.AutofillId getFocusedId()\npublic @android.annotation.NonNull android.view.autofill.AutofillValue getFocusedValue()\npublic @android.annotation.Nullable android.app.assist.AssistStructure.ViewNode getFocusedViewNode()\npublic @android.annotation.Nullable android.service.autofill.augmented.PresentationParams getPresentationParams()\n  java.lang.String proxyToString()\nclass FillRequest extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genToString=true, genBuilder=false, genHiddenConstructor=true)")
     @Deprecated
     private void __metadata() {}
 
diff --git a/location/java/com/android/internal/location/ProviderProperties.aidl b/core/java/android/service/notification/NotificationListenerFilter.aidl
similarity index 80%
copy from location/java/com/android/internal/location/ProviderProperties.aidl
copy to core/java/android/service/notification/NotificationListenerFilter.aidl
index b901444..c186b74 100644
--- a/location/java/com/android/internal/location/ProviderProperties.aidl
+++ b/core/java/android/service/notification/NotificationListenerFilter.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012, 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.
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.internal.location;
+package android.service.notification;
 
-parcelable ProviderProperties;
+parcelable NotificationListenerFilter;
+
diff --git a/core/java/android/service/notification/NotificationListenerFilter.java b/core/java/android/service/notification/NotificationListenerFilter.java
new file mode 100644
index 0000000..6fdfaab
--- /dev/null
+++ b/core/java/android/service/notification/NotificationListenerFilter.java
@@ -0,0 +1,104 @@
+/**
+ * Copyright (c) 2020, The Android Open Source Project
+ *
+ * Licensed under the Apache License,  2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.service.notification;
+
+import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ALERTING;
+import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_CONVERSATIONS;
+import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ONGOING;
+import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_SILENT;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.ArraySet;
+
+/**
+ * Specifies a filter for what types of notifications should be bridged to notification listeners.
+ * Each requested listener will have their own filter instance.
+ * @hide
+ */
+public class NotificationListenerFilter implements Parcelable {
+    private int mAllowedNotificationTypes;
+    private ArraySet<String> mDisallowedPackages;
+
+    public NotificationListenerFilter() {
+        mAllowedNotificationTypes = FLAG_FILTER_TYPE_CONVERSATIONS
+                | FLAG_FILTER_TYPE_ALERTING
+                | FLAG_FILTER_TYPE_SILENT
+                | FLAG_FILTER_TYPE_ONGOING;
+        mDisallowedPackages = new ArraySet<>();
+    }
+
+    public NotificationListenerFilter(int types, ArraySet<String> pkgs) {
+        mAllowedNotificationTypes = types;
+        mDisallowedPackages = pkgs;
+    }
+
+    /**
+     * @hide
+     */
+    protected NotificationListenerFilter(Parcel in) {
+        mAllowedNotificationTypes = in.readInt();
+        mDisallowedPackages = (ArraySet<String>) in.readArraySet(String.class.getClassLoader());
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mAllowedNotificationTypes);
+        dest.writeArraySet(mDisallowedPackages);
+    }
+
+    public static final Creator<NotificationListenerFilter> CREATOR =
+            new Creator<NotificationListenerFilter>() {
+                @Override
+                public NotificationListenerFilter createFromParcel(Parcel in) {
+                    return new NotificationListenerFilter(in);
+                }
+
+                @Override
+                public NotificationListenerFilter[] newArray(int size) {
+                    return new NotificationListenerFilter[size];
+                }
+            };
+
+    public boolean isTypeAllowed(int type) {
+        return (mAllowedNotificationTypes & type) != 0;
+    }
+
+    public boolean isPackageAllowed(String pkg) {
+        return !mDisallowedPackages.contains(pkg);
+    }
+
+    public int getTypes() {
+        return mAllowedNotificationTypes;
+    }
+
+    public ArraySet<String> getDisallowedPackages() {
+        return mDisallowedPackages;
+    }
+
+    public void setTypes(int types) {
+        mAllowedNotificationTypes = types;
+    }
+
+    public void setDisallowedPackages(ArraySet<String> pkgs) {
+        mDisallowedPackages = pkgs;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+}
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 440eeb1..f79b59f 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -242,6 +242,39 @@
     public @interface NotificationCancelReason{};
 
     /**
+     * @hide
+     */
+    @IntDef(flag = true, prefix = { "FLAG_FILTER_TYPE_" }, value = {
+            FLAG_FILTER_TYPE_CONVERSATIONS,
+            FLAG_FILTER_TYPE_ALERTING,
+            FLAG_FILTER_TYPE_SILENT,
+            FLAG_FILTER_TYPE_ONGOING
+    })
+    public @interface NotificationFilterTypes {}
+    /**
+     * A flag value indicating that this notification listener can see conversation type
+     * notifications.
+     * @hide
+     */
+    public static final int FLAG_FILTER_TYPE_CONVERSATIONS = 1;
+    /**
+     * A flag value indicating that this notification listener can see altering type notifications.
+     * @hide
+     */
+    public static final int FLAG_FILTER_TYPE_ALERTING = 2;
+    /**
+     * A flag value indicating that this notification listener can see silent type notifications.
+     * @hide
+     */
+    public static final int FLAG_FILTER_TYPE_SILENT = 4;
+    /**
+     * A flag value indicating that this notification listener can see important
+     * ( > {@link NotificationManager#IMPORTANCE_MIN}) ongoing type notifications.
+     * @hide
+     */
+    public static final int FLAG_FILTER_TYPE_ONGOING = 8;
+
+    /**
      * The full trim of the StatusBarNotification including all its features.
      *
      * @hide
diff --git a/core/java/android/service/timezone/ITimeZoneProvider.aidl b/core/java/android/service/timezone/ITimeZoneProvider.aidl
index 62fa157..793bcc6 100644
--- a/core/java/android/service/timezone/ITimeZoneProvider.aidl
+++ b/core/java/android/service/timezone/ITimeZoneProvider.aidl
@@ -22,7 +22,6 @@
  * @hide
  */
 oneway interface ITimeZoneProvider {
-    void setTimeZoneProviderManager(in @nullable ITimeZoneProviderManager manager);
-    void startUpdates(in long initializationTimeoutMillis);
+    void startUpdates(in ITimeZoneProviderManager manager, in long initializationTimeoutMillis);
     void stopUpdates();
 }
diff --git a/core/java/android/service/timezone/OWNERS b/core/java/android/service/timezone/OWNERS
new file mode 100644
index 0000000..28aff18
--- /dev/null
+++ b/core/java/android/service/timezone/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 847766
+nfuller@google.com
+include /core/java/android/app/timedetector/OWNERS
diff --git a/core/java/android/service/timezone/TimeZoneProviderService.java b/core/java/android/service/timezone/TimeZoneProviderService.java
index 9533a8f..d71a830 100644
--- a/core/java/android/service/timezone/TimeZoneProviderService.java
+++ b/core/java/android/service/timezone/TimeZoneProviderService.java
@@ -53,7 +53,7 @@
  * <p>Provider discovery:
  *
  * <p>You must declare the service in your manifest file with the
- * {@link android.Manifest.permission#INSTALL_LOCATION_TIME_ZONE_PROVIDER} permission,
+ * {@link android.Manifest.permission#BIND_TIME_ZONE_PROVIDER_SERVICE} permission,
  * and include an intent filter with the necessary action indicating what type of provider it is.
  *
  * <p>Device configuration can influence how {@link TimeZoneProviderService}s are discovered.
@@ -66,18 +66,29 @@
  *
  * <p>Provider types:
  *
- * <p>Android currently supports up to two location-derived time zone providers. These are called
- * the "primary" and "secondary" location time zone provider, configured using {@link
- * #PRIMARY_LOCATION_TIME_ZONE_PROVIDER_SERVICE_INTERFACE} and {@link
- * #SECONDARY_LOCATION_TIME_ZONE_PROVIDER_SERVICE_INTERFACE} respectively. The primary location time
- * zone provider is started first and will be used until becomes uncertain or fails, at which point
- * the secondary provider will be started.
+ * <p>Android supports up to two <em>location-derived</em> time zone providers. These are called the
+ * "primary" and "secondary" location time zone provider. The primary location time zone provider is
+ * started first and will be used until it becomes uncertain or fails, at which point the secondary
+ * provider will be started.
  *
- * For example:
+ * <p>Location-derived time zone providers are configured using {@link
+ * #PRIMARY_LOCATION_TIME_ZONE_PROVIDER_SERVICE_INTERFACE} and {@link
+ * #SECONDARY_LOCATION_TIME_ZONE_PROVIDER_SERVICE_INTERFACE} intent-filter actions respectively.
+ * Besides declaring the android:permission attribute mentioned above, the application supplying a
+ * location provider must be granted the {@link
+ * android.Manifest.permission#INSTALL_LOCATION_TIME_ZONE_PROVIDER_SERVICE} permission to be
+ * accepted by the system server.
+ *
+ * <p>For example:
  * <pre>
+ *   &lt;uses-permission
+ *       android:name="android.permission.INSTALL_LOCATION_TIME_ZONE_PROVIDER_SERVICE"/&gt;
+ *
+ * ...
+ *
  *     &lt;service android:name=".FooTimeZoneProviderService"
  *             android:exported="true"
- *             android:permission="android.permission.INSTALL_LOCATION_TIME_ZONE_PROVIDER"&gt;
+ *             android:permission="android.permission.BIND_TIME_ZONE_PROVIDER_SERVICE"&gt;
  *         &lt;intent-filter&gt;
  *             &lt;action
  *             android:name="android.service.timezone.SecondaryLocationTimeZoneProviderService"
@@ -88,7 +99,6 @@
  *     &lt;/service&gt;
  * </pre>
  *
- *
  * <p>Threading:
  *
  * <p>Calls to {@code report} methods can be made on on any thread and will be passed asynchronously
@@ -102,7 +112,18 @@
 
     private static final String TAG = "TimeZoneProviderService";
 
-    private final Handler mHandler = BackgroundThread.getHandler();
+    /**
+     * The test command result key indicating whether a command succeeded. Value type: boolean
+     * @hide
+     */
+    public static final String TEST_COMMAND_RESULT_SUCCESS_KEY = "SUCCESS";
+
+    /**
+     * The test command result key for the error message present when {@link
+     * #TEST_COMMAND_RESULT_SUCCESS_KEY} is false. Value type: string
+     * @hide
+     */
+    public static final String TEST_COMMAND_RESULT_ERROR_KEY = "ERROR";
 
     /**
      * The Intent action that the primary location-derived time zone provider service must respond
@@ -120,16 +141,10 @@
     public static final String SECONDARY_LOCATION_TIME_ZONE_PROVIDER_SERVICE_INTERFACE =
             "android.service.timezone.SecondaryLocationTimeZoneProviderService";
 
-    /**
-     * The permission that a service must require to ensure that only Android system can bind to it.
-     * If this permission is not enforced in the AndroidManifest of the service, the system will
-     * skip that service.
-     */
-    public static final String BIND_PERMISSION =
-            "android.permission.INSTALL_LOCATION_TIME_ZONE_PROVIDER";
-
     private final TimeZoneProviderServiceWrapper mWrapper = new TimeZoneProviderServiceWrapper();
 
+    private final Handler mHandler = BackgroundThread.getHandler();
+
     /** Set by {@link #mHandler} thread. */
     @Nullable
     private ITimeZoneProviderManager mManager;
@@ -196,11 +211,22 @@
         });
     }
 
+    private void onStartUpdatesInternal(@NonNull ITimeZoneProviderManager manager,
+            @DurationMillisLong long initializationTimeoutMillis) {
+        mManager = manager;
+        onStartUpdates(initializationTimeoutMillis);
+    }
+
     /**
      * Starts the provider sending updates.
      */
     public abstract void onStartUpdates(@DurationMillisLong long initializationTimeoutMillis);
 
+    private void onStopUpdatesInternal() {
+        onStopUpdates();
+        mManager = null;
+    }
+
     /**
      * Stops the provider sending updates.
      */
@@ -208,18 +234,14 @@
 
     private class TimeZoneProviderServiceWrapper extends ITimeZoneProvider.Stub {
 
-        @Override
-        public void setTimeZoneProviderManager(ITimeZoneProviderManager manager) {
+        public void startUpdates(@NonNull ITimeZoneProviderManager manager,
+                @DurationMillisLong long initializationTimeoutMillis) {
             Objects.requireNonNull(manager);
-            mHandler.post(() -> TimeZoneProviderService.this.mManager = manager);
-        }
-
-        public void startUpdates(@DurationMillisLong long initializationTimeoutMillis) {
-            mHandler.post(() -> onStartUpdates(initializationTimeoutMillis));
+            mHandler.post(() -> onStartUpdatesInternal(manager, initializationTimeoutMillis));
         }
 
         public void stopUpdates() {
-            mHandler.post(TimeZoneProviderService.this::onStopUpdates);
+            mHandler.post(TimeZoneProviderService.this::onStopUpdatesInternal);
         }
     }
 }
diff --git a/core/java/android/service/voice/AlwaysOnHotwordDetector.java b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
index 46a5caf..e934fa4 100644
--- a/core/java/android/service/voice/AlwaysOnHotwordDetector.java
+++ b/core/java/android/service/voice/AlwaysOnHotwordDetector.java
@@ -22,9 +22,9 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.app.ActivityThread;
 import android.annotation.RequiresPermission;
 import android.annotation.SystemApi;
+import android.app.ActivityThread;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.Context;
 import android.content.Intent;
@@ -41,6 +41,7 @@
 import android.media.permission.Identity;
 import android.os.AsyncTask;
 import android.os.Binder;
+import android.os.Build;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Message;
@@ -78,6 +79,7 @@
      * No further interaction should be performed with the detector that returns this availability.
      */
     public static final int STATE_HARDWARE_UNAVAILABLE = -2;
+
     /**
      * Indicates that recognition for the given keyphrase is not supported.
      * No further interaction should be performed with the detector that returns this availability.
@@ -88,11 +90,13 @@
      */
     @Deprecated
     public static final int STATE_KEYPHRASE_UNSUPPORTED = -1;
+
     /**
      * Indicates that the given keyphrase is not enrolled.
      * The caller may choose to begin an enrollment flow for the keyphrase.
      */
     public static final int STATE_KEYPHRASE_UNENROLLED = 1;
+
     /**
      * Indicates that the given keyphrase is currently enrolled and it's possible to start
      * recognition for it.
@@ -100,6 +104,14 @@
     public static final int STATE_KEYPHRASE_ENROLLED = 2;
 
     /**
+     * Indicates that the availability state of the active keyphrase can't be known due to an error.
+     *
+     * <p>NOTE: No further interaction should be performed with the detector that returns this
+     * state, it would be better to create {@link AlwaysOnHotwordDetector} again.
+     */
+    public static final int STATE_ERROR = 3;
+
+    /**
      * Indicates that the detector isn't ready currently.
      */
     private static final int STATE_NOT_READY = 0;
@@ -122,11 +134,13 @@
      * @hide
      */
     public static final int RECOGNITION_FLAG_NONE = 0;
+
     /**
      * Recognition flag for {@link #startRecognition(int)} that indicates
      * whether the trigger audio for hotword needs to be captured.
      */
     public static final int RECOGNITION_FLAG_CAPTURE_TRIGGER_AUDIO = 0x1;
+
     /**
      * Recognition flag for {@link #startRecognition(int)} that indicates
      * whether the recognition should keep going on even after the keyphrase triggers.
@@ -174,6 +188,7 @@
      */
     public static final int RECOGNITION_MODE_VOICE_TRIGGER
             = SoundTrigger.RECOGNITION_MODE_VOICE_TRIGGER;
+
     /**
      * User identification performed with the keyphrase recognition.
      * Returned by {@link #getSupportedRecognitionModes()}
@@ -249,6 +264,7 @@
     private final Object mLock = new Object();
     private final Handler mHandler;
     private final IBinder mBinder = new Binder();
+    private final int mTargetSdkVersion;
 
     private int mAvailability = STATE_NOT_READY;
 
@@ -401,8 +417,10 @@
          * @see AlwaysOnHotwordDetector#STATE_HARDWARE_UNAVAILABLE
          * @see AlwaysOnHotwordDetector#STATE_KEYPHRASE_UNENROLLED
          * @see AlwaysOnHotwordDetector#STATE_KEYPHRASE_ENROLLED
+         * @see AlwaysOnHotwordDetector#STATE_ERROR
          */
         public abstract void onAvailabilityChanged(int status);
+
         /**
          * Called when the keyphrase is spoken.
          * This implicitly stops listening for the keyphrase once it's detected.
@@ -414,16 +432,19 @@
          *        {@link AlwaysOnHotwordDetector#startRecognition(int)}.
          */
         public abstract void onDetected(@NonNull EventPayload eventPayload);
+
         /**
          * Called when the detection fails due to an error.
          */
         public abstract void onError();
+
         /**
          * Called when the recognition is paused temporarily for some reason.
          * This is an informational callback, and the clients shouldn't be doing anything here
          * except showing an indication on their UI if they have to.
          */
         public abstract void onRecognitionPaused();
+
         /**
          * Called when the recognition is resumed after it was temporarily paused.
          * This is an informational callback, and the clients shouldn't be doing anything here
@@ -437,11 +458,12 @@
      * @param locale The java locale for the detector.
      * @param callback A non-null Callback for receiving the recognition events.
      * @param modelManagementService A service that allows management of sound models.
+     * @param targetSdkVersion The target SDK version.
      * @hide
      */
     public AlwaysOnHotwordDetector(String text, Locale locale, Callback callback,
             KeyphraseEnrollmentInfo keyphraseEnrollmentInfo,
-            IVoiceInteractionManagerService modelManagementService) {
+            IVoiceInteractionManagerService modelManagementService, int targetSdkVersion) {
         mText = text;
         mLocale = locale;
         mKeyphraseEnrollmentInfo = keyphraseEnrollmentInfo;
@@ -449,6 +471,7 @@
         mHandler = new MyHandler();
         mInternalCallback = new SoundTriggerListener(mHandler);
         mModelManagementService = modelManagementService;
+        mTargetSdkVersion = targetSdkVersion;
         try {
             Identity identity = new Identity();
             identity.packageName = ActivityThread.currentOpPackageName();
@@ -469,7 +492,7 @@
      * @throws UnsupportedOperationException if the keyphrase itself isn't supported.
      *         Callers should only call this method after a supported state callback on
      *         {@link Callback#onAvailabilityChanged(int)} to avoid this exception.
-     * @throws IllegalStateException if the detector is in an invalid state.
+     * @throws IllegalStateException if the detector is in an invalid or error state.
      *         This may happen if another detector has been instantiated or the
      *         {@link VoiceInteractionService} hosting this detector has been shut down.
      */
@@ -481,9 +504,9 @@
     }
 
     private int getSupportedRecognitionModesLocked() {
-        if (mAvailability == STATE_INVALID) {
+        if (mAvailability == STATE_INVALID || mAvailability == STATE_ERROR) {
             throw new IllegalStateException(
-                    "getSupportedRecognitionModes called on an invalid detector");
+                    "getSupportedRecognitionModes called on an invalid detector or error state");
         }
 
         // This method only makes sense if we can actually support a recognition.
@@ -541,7 +564,7 @@
      * @throws UnsupportedOperationException if the recognition isn't supported.
      *         Callers should only call this method after a supported state callback on
      *         {@link Callback#onAvailabilityChanged(int)} to avoid this exception.
-     * @throws IllegalStateException if the detector is in an invalid state.
+     * @throws IllegalStateException if the detector is in an invalid or error state.
      *         This may happen if another detector has been instantiated or the
      *         {@link VoiceInteractionService} hosting this detector has been shut down.
      */
@@ -549,8 +572,9 @@
     public boolean startRecognition(@RecognitionFlags int recognitionFlags) {
         if (DBG) Slog.d(TAG, "startRecognition(" + recognitionFlags + ")");
         synchronized (mLock) {
-            if (mAvailability == STATE_INVALID) {
-                throw new IllegalStateException("startRecognition called on an invalid detector");
+            if (mAvailability == STATE_INVALID || mAvailability == STATE_ERROR) {
+                throw new IllegalStateException(
+                        "startRecognition called on an invalid detector or error state");
             }
 
             // Check if we can start/stop a recognition.
@@ -572,7 +596,7 @@
      * @throws UnsupportedOperationException if the recognition isn't supported.
      *         Callers should only call this method after a supported state callback on
      *         {@link Callback#onAvailabilityChanged(int)} to avoid this exception.
-     * @throws IllegalStateException if the detector is in an invalid state.
+     * @throws IllegalStateException if the detector is in an invalid or error state.
      *         This may happen if another detector has been instantiated or the
      *         {@link VoiceInteractionService} hosting this detector has been shut down.
      */
@@ -580,8 +604,9 @@
     public boolean stopRecognition() {
         if (DBG) Slog.d(TAG, "stopRecognition()");
         synchronized (mLock) {
-            if (mAvailability == STATE_INVALID) {
-                throw new IllegalStateException("stopRecognition called on an invalid detector");
+            if (mAvailability == STATE_INVALID || mAvailability == STATE_ERROR) {
+                throw new IllegalStateException(
+                        "stopRecognition called on an invalid detector or error state");
             }
 
             // Check if we can start/stop a recognition.
@@ -610,6 +635,9 @@
      *         - {@link SoundTrigger#STATUS_BAD_VALUE} invalid input parameter
      *         - {@link SoundTrigger#STATUS_INVALID_OPERATION} if the call is out of sequence or
      *           if API is not supported by HAL
+     * @throws IllegalStateException if the detector is in an invalid or error state.
+     *         This may happen if another detector has been instantiated or the
+     *         {@link VoiceInteractionService} hosting this detector has been shut down.
      */
     @RequiresPermission(allOf = {RECORD_AUDIO, CAPTURE_AUDIO_HOTWORD})
     public int setParameter(@ModelParams int modelParam, int value) {
@@ -618,8 +646,9 @@
         }
 
         synchronized (mLock) {
-            if (mAvailability == STATE_INVALID) {
-                throw new IllegalStateException("setParameter called on an invalid detector");
+            if (mAvailability == STATE_INVALID || mAvailability == STATE_ERROR) {
+                throw new IllegalStateException(
+                        "setParameter called on an invalid detector or error state");
             }
 
             return setParameterLocked(modelParam, value);
@@ -638,6 +667,9 @@
      *
      * @param modelParam   {@link ModelParams}
      * @return value of parameter
+     * @throws IllegalStateException if the detector is in an invalid or error state.
+     *         This may happen if another detector has been instantiated or the
+     *         {@link VoiceInteractionService} hosting this detector has been shut down.
      */
     @RequiresPermission(allOf = {RECORD_AUDIO, CAPTURE_AUDIO_HOTWORD})
     public int getParameter(@ModelParams int modelParam) {
@@ -646,8 +678,9 @@
         }
 
         synchronized (mLock) {
-            if (mAvailability == STATE_INVALID) {
-                throw new IllegalStateException("getParameter called on an invalid detector");
+            if (mAvailability == STATE_INVALID || mAvailability == STATE_ERROR) {
+                throw new IllegalStateException(
+                        "getParameter called on an invalid detector or error state");
             }
 
             return getParameterLocked(modelParam);
@@ -663,6 +696,9 @@
      *
      * @param modelParam {@link ModelParams}
      * @return supported range of parameter, null if not supported
+     * @throws IllegalStateException if the detector is in an invalid or error state.
+     *         This may happen if another detector has been instantiated or the
+     *         {@link VoiceInteractionService} hosting this detector has been shut down.
      */
     @RequiresPermission(allOf = {RECORD_AUDIO, CAPTURE_AUDIO_HOTWORD})
     @Nullable
@@ -672,8 +708,9 @@
         }
 
         synchronized (mLock) {
-            if (mAvailability == STATE_INVALID) {
-                throw new IllegalStateException("queryParameter called on an invalid detector");
+            if (mAvailability == STATE_INVALID || mAvailability == STATE_ERROR) {
+                throw new IllegalStateException(
+                        "queryParameter called on an invalid detector or error state");
             }
 
             return queryParameterLocked(modelParam);
@@ -735,7 +772,7 @@
      * @throws UnsupportedOperationException if managing they keyphrase isn't supported.
      *         Callers should only call this method after a supported state callback on
      *         {@link Callback#onAvailabilityChanged(int)} to avoid this exception.
-     * @throws IllegalStateException if the detector is in an invalid state.
+     * @throws IllegalStateException if the detector is in an invalid or error state.
      *         This may happen if another detector has been instantiated or the
      *         {@link VoiceInteractionService} hosting this detector has been shut down.
      */
@@ -748,8 +785,9 @@
     }
 
     private Intent getManageIntentLocked(@KeyphraseEnrollmentInfo.ManageActions int action) {
-        if (mAvailability == STATE_INVALID) {
-            throw new IllegalStateException("getManageIntent called on an invalid detector");
+        if (mAvailability == STATE_INVALID || mAvailability == STATE_ERROR) {
+            throw new IllegalStateException(
+                    "getManageIntent called on an invalid detector or error state");
         }
 
         // This method only makes sense if we can actually support a recognition.
@@ -783,8 +821,10 @@
     void onSoundModelsChanged() {
         synchronized (mLock) {
             if (mAvailability == STATE_INVALID
-                    || mAvailability == STATE_HARDWARE_UNAVAILABLE) {
-                Slog.w(TAG, "Received onSoundModelsChanged for an unsupported keyphrase/config");
+                    || mAvailability == STATE_HARDWARE_UNAVAILABLE
+                    || mAvailability == STATE_ERROR) {
+                Slog.w(TAG, "Received onSoundModelsChanged for an unsupported keyphrase/config"
+                        + " or in the error state");
                 return;
             }
 
@@ -794,7 +834,16 @@
             // The availability change callback should ensure that the client starts recognition
             // again if needed.
             if (mAvailability == STATE_KEYPHRASE_ENROLLED) {
-                stopRecognitionLocked();
+                try {
+                    stopRecognitionLocked();
+                } catch (SecurityException e) {
+                    Slog.w(TAG, "Failed to Stop the recognition", e);
+                    if (mTargetSdkVersion <= Build.VERSION_CODES.R) {
+                        throw e;
+                    }
+                    updateAndNotifyStateChangedLocked(STATE_ERROR);
+                    return;
+                }
             }
 
             // Execute a refresh availability task - which should then notify of a change.
@@ -890,6 +939,15 @@
         }
     }
 
+    private void updateAndNotifyStateChangedLocked(int availability) {
+        if (DBG) {
+            Slog.d(TAG, "Hotword availability changed from " + mAvailability
+                    + " -> " + availability);
+        }
+        mAvailability = availability;
+        notifyStateChangedLocked();
+    }
+
     private void notifyStateChangedLocked() {
         Message message = Message.obtain(mHandler, MSG_AVAILABILITY_CHANGED);
         message.arg1 = mAvailability;
@@ -976,25 +1034,30 @@
 
         @Override
         public Void doInBackground(Void... params) {
-            int availability = internalGetInitialAvailability();
+            try {
+                int availability = internalGetInitialAvailability();
 
-            synchronized (mLock) {
-                if (availability == STATE_NOT_READY) {
-                    internalUpdateEnrolledKeyphraseMetadata();
-                    if (mKeyphraseMetadata != null) {
-                        availability = STATE_KEYPHRASE_ENROLLED;
-                    } else {
-                        availability = STATE_KEYPHRASE_UNENROLLED;
+                synchronized (mLock) {
+                    if (availability == STATE_NOT_READY) {
+                        internalUpdateEnrolledKeyphraseMetadata();
+                        if (mKeyphraseMetadata != null) {
+                            availability = STATE_KEYPHRASE_ENROLLED;
+                        } else {
+                            availability = STATE_KEYPHRASE_UNENROLLED;
+                        }
                     }
+                    updateAndNotifyStateChangedLocked(availability);
                 }
-
-                if (DBG) {
-                    Slog.d(TAG, "Hotword availability changed from " + mAvailability
-                            + " -> " + availability);
+            } catch (SecurityException e) {
+                Slog.w(TAG, "Failed to refresh availability", e);
+                if (mTargetSdkVersion <= Build.VERSION_CODES.R) {
+                    throw e;
                 }
-                mAvailability = availability;
-                notifyStateChangedLocked();
+                synchronized (mLock) {
+                    updateAndNotifyStateChangedLocked(STATE_ERROR);
+                }
             }
+
             return null;
         }
 
diff --git a/core/java/android/service/voice/VoiceInteractionService.java b/core/java/android/service/voice/VoiceInteractionService.java
index fb03ed4..4aff695 100644
--- a/core/java/android/service/voice/VoiceInteractionService.java
+++ b/core/java/android/service/voice/VoiceInteractionService.java
@@ -325,7 +325,8 @@
             // Allow only one concurrent recognition via the APIs.
             safelyShutdownHotwordDetector();
             mHotwordDetector = new AlwaysOnHotwordDetector(keyphrase, locale, callback,
-                    mKeyphraseEnrollmentInfo, mSystemService);
+                    mKeyphraseEnrollmentInfo, mSystemService,
+                    getApplicationContext().getApplicationInfo().targetSdkVersion);
         }
         return mHotwordDetector;
     }
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 3763711..507dc7a 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -881,8 +881,7 @@
 
                         if (mSession.addToDisplay(mWindow, mLayout, View.VISIBLE,
                                 mDisplay.getDisplayId(), mInsetsState, mWinFrames.frame,
-                                mWinFrames.displayCutout, inputChannel, mInsetsState,
-                                mTempControls) < 0) {
+                                inputChannel, mInsetsState, mTempControls) < 0) {
                             Log.w(TAG, "Failed to add window while updating wallpaper surface.");
                             return;
                         }
@@ -924,13 +923,13 @@
                     int w = mWinFrames.frame.width();
                     int h = mWinFrames.frame.height();
 
-                    final DisplayCutout rawCutout = mWinFrames.displayCutout.get();
+                    final DisplayCutout rawCutout = mInsetsState.getDisplayCutout();
                     final Configuration config = getResources().getConfiguration();
                     final Rect visibleFrame = new Rect(mWinFrames.frame);
                     visibleFrame.intersect(mInsetsState.getDisplayFrame());
                     WindowInsets windowInsets = mInsetsState.calculateInsets(visibleFrame,
                             null /* ignoringVisibilityState */, config.isScreenRound(),
-                            false /* alwaysConsumeSystemBars */, rawCutout, mLayout.softInputMode,
+                            false /* alwaysConsumeSystemBars */, mLayout.softInputMode,
                             mLayout.flags, SYSTEM_UI_FLAG_VISIBLE, mLayout.type,
                             config.windowConfiguration.getWindowingMode(), null /* typeSideMap */);
 
diff --git a/core/java/android/util/Patterns.java b/core/java/android/util/Patterns.java
index 7ad16ff..ece069f 100644
--- a/core/java/android/util/Patterns.java
+++ b/core/java/android/util/Patterns.java
@@ -248,6 +248,13 @@
             + "[0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9]|0)\\.(25[0-5]|2[0-4][0-9]|[0-1]"
             + "[0-9]{2}|[1-9][0-9]|[1-9]|0)\\.(25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}"
             + "|[1-9][0-9]|[0-9]))";
+
+    /**
+     * Kept for backward compatibility reasons. It does not match IPv6 addresses.
+     *
+     * @deprecated Please use {@link android.net.InetAddresses#isNumericAddress(String)} instead.
+     */
+    @Deprecated
     public static final Pattern IP_ADDRESS = Pattern.compile(IP_ADDRESS_STRING);
 
     /**
diff --git a/core/java/android/util/TypedValue.java b/core/java/android/util/TypedValue.java
index 7f1ee30..19de396 100644
--- a/core/java/android/util/TypedValue.java
+++ b/core/java/android/util/TypedValue.java
@@ -17,8 +17,14 @@
 package android.util;
 
 import android.annotation.AnyRes;
+import android.annotation.FloatRange;
+import android.annotation.IntDef;
+import android.annotation.IntRange;
 import android.content.pm.ActivityInfo.Config;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
 /**
  * Container for a dynamically typed data value.  Primarily used with
  * {@link android.content.res.Resources} for holding resource values.
@@ -95,6 +101,18 @@
      *  defined below. */
     public static final int COMPLEX_UNIT_MASK = 0xf;
 
+    /** @hide **/
+    @IntDef(prefix = "COMPLEX_UNIT_", value = {
+            COMPLEX_UNIT_PX,
+            COMPLEX_UNIT_DIP,
+            COMPLEX_UNIT_SP,
+            COMPLEX_UNIT_PT,
+            COMPLEX_UNIT_IN,
+            COMPLEX_UNIT_MM,
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface ComplexDimensionUnit {}
+
     /** {@link #TYPE_DIMENSION} complex unit: Value is raw pixels. */
     public static final int COMPLEX_UNIT_PX = 0;
     /** {@link #TYPE_DIMENSION} complex unit: Value is Device Independent
@@ -381,7 +399,7 @@
      * @return The complex floating point value multiplied by the appropriate 
      * metrics depending on its unit. 
      */
-    public static float applyDimension(int unit, float value,
+    public static float applyDimension(@ComplexDimensionUnit int unit, float value,
                                        DisplayMetrics metrics)
     {
         switch (unit) {
@@ -417,6 +435,130 @@
     }
 
     /**
+     * Construct a complex data integer.  This validates the radix and the magnitude of the
+     * mantissa, and sets the {@link TypedValue#COMPLEX_MANTISSA_MASK} and
+     * {@link TypedValue#COMPLEX_RADIX_MASK} components as provided. The units are not set.
+     **
+     * @param mantissa an integer representing the mantissa.
+     * @param radix a radix option, e.g. {@link TypedValue#COMPLEX_RADIX_23p0}.
+     * @return A complex data integer representing the value.
+     * @hide
+     */
+    private static int createComplex(@IntRange(from = -0x800000, to = 0x7FFFFF) int mantissa,
+            int radix) {
+        if (mantissa < -0x800000 || mantissa >= 0x800000) {
+            throw new IllegalArgumentException("Magnitude of mantissa is too large: " + mantissa);
+        }
+        if (radix < TypedValue.COMPLEX_RADIX_23p0 || radix > TypedValue.COMPLEX_RADIX_0p23) {
+            throw new IllegalArgumentException("Invalid radix: " + radix);
+        }
+        return ((mantissa & TypedValue.COMPLEX_MANTISSA_MASK) << TypedValue.COMPLEX_MANTISSA_SHIFT)
+                | (radix << TypedValue.COMPLEX_RADIX_SHIFT);
+    }
+
+    /**
+     * Convert a base value to a complex data integer.  This sets the {@link
+     * TypedValue#COMPLEX_MANTISSA_MASK} and {@link TypedValue#COMPLEX_RADIX_MASK} fields of the
+     * data to create a floating point representation of the given value. The units are not set.
+     *
+     * <p>This is the inverse of {@link TypedValue#complexToFloat(int)}.
+     *
+     * @param value An integer value.
+     * @return A complex data integer representing the value.
+     * @hide
+     */
+    public static int intToComplex(int value) {
+        if (value < -0x800000 || value >= 0x800000) {
+            throw new IllegalArgumentException("Magnitude of the value is too large: " + value);
+        }
+        return createComplex(value, TypedValue.COMPLEX_RADIX_23p0);
+    }
+
+    /**
+     * Convert a base value to a complex data integer.  This sets the {@link
+     * TypedValue#COMPLEX_MANTISSA_MASK} and {@link TypedValue#COMPLEX_RADIX_MASK} fields of the
+     * data to create a floating point representation of the given value. The units are not set.
+     *
+     * <p>This is the inverse of {@link TypedValue#complexToFloat(int)}.
+     *
+     * @param value A floating point value.
+     * @return A complex data integer representing the value.
+     * @hide
+     */
+    public static int floatToComplex(@FloatRange(from = -0x800000, to = 0x7FFFFF) float value) {
+        // validate that the magnitude fits in this representation
+        if (value < (float) -0x800000 - .5f || value >= (float) 0x800000 - .5f) {
+            throw new IllegalArgumentException("Magnitude of the value is too large: " + value);
+        }
+        try {
+            // If there's no fraction, use integer representation, as that's clearer
+            if (value == (float) (int) value) {
+                return createComplex((int) value, TypedValue.COMPLEX_RADIX_23p0);
+            }
+            float absValue = Math.abs(value);
+            // If the magnitude is 0, we don't need any magnitude digits
+            if (absValue < 1f) {
+                return createComplex(Math.round(value * (1 << 23)), TypedValue.COMPLEX_RADIX_0p23);
+            }
+            // If the magnitude is less than 2^8, use 8 magnitude digits
+            if (absValue < (float) (1 << 8)) {
+                return createComplex(Math.round(value * (1 << 15)), TypedValue.COMPLEX_RADIX_8p15);
+            }
+            // If the magnitude is less than 2^16, use 16 magnitude digits
+            if (absValue < (float) (1 << 16)) {
+                return createComplex(Math.round(value * (1 << 7)), TypedValue.COMPLEX_RADIX_16p7);
+            }
+            // The magnitude requires all 23 digits
+            return createComplex(Math.round(value), TypedValue.COMPLEX_RADIX_23p0);
+        } catch (IllegalArgumentException ex) {
+            // Wrap exception so as to include the value argument in the message.
+            throw new IllegalArgumentException("Unable to convert value to complex: " + value, ex);
+        }
+    }
+
+    /**
+     * <p>Creates a complex data integer that stores a dimension value and units.
+     *
+     * <p>The resulting value can be passed to e.g.
+     * {@link TypedValue#complexToDimensionPixelOffset(int, DisplayMetrics)} to calculate the pixel
+     * value for the dimension.
+     *
+     * @param value the value of the dimension
+     * @param units the units of the dimension, e.g. {@link TypedValue#COMPLEX_UNIT_DIP}
+     * @return A complex data integer representing the value and units of the dimension.
+     * @hide
+     */
+    public static int createComplexDimension(
+            @IntRange(from = -0x800000, to = 0x7FFFFF) int value,
+            @ComplexDimensionUnit int units) {
+        if (units < TypedValue.COMPLEX_UNIT_PX || units > TypedValue.COMPLEX_UNIT_MM) {
+            throw new IllegalArgumentException("Must be a valid COMPLEX_UNIT_*: " + units);
+        }
+        return intToComplex(value) | units;
+    }
+
+    /**
+     * <p>Creates a complex data integer that stores a dimension value and units.
+     *
+     * <p>The resulting value can be passed to e.g.
+     * {@link TypedValue#complexToDimensionPixelOffset(int, DisplayMetrics)} to calculate the pixel
+     * value for the dimension.
+     *
+     * @param value the value of the dimension
+     * @param units the units of the dimension, e.g. {@link TypedValue#COMPLEX_UNIT_DIP}
+     * @return A complex data integer representing the value and units of the dimension.
+     * @hide
+     */
+    public static int createComplexDimension(
+            @FloatRange(from = -0x800000, to = 0x7FFFFF) float value,
+            @ComplexDimensionUnit int units) {
+        if (units < TypedValue.COMPLEX_UNIT_PX || units > TypedValue.COMPLEX_UNIT_MM) {
+            throw new IllegalArgumentException("Must be a valid COMPLEX_UNIT_*: " + units);
+        }
+        return floatToComplex(value) | units;
+    }
+
+    /**
      * Converts a complex data value holding a fraction to its final floating 
      * point value. The given <var>data</var> must be structured as a 
      * {@link #TYPE_FRACTION}.
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index 9991367..a2dab70 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -687,6 +687,19 @@
     }
 
     /**
+     * Gets the default brightness configured for the display.
+     *
+     * @return Default brightness between 0.0-1.0
+     * @hide
+     */
+    public float getBrightnessDefault() {
+        synchronized (this) {
+            updateDisplayInfoLocked();
+            return mDisplayInfo.brightnessDefault;
+        }
+    }
+
+    /**
      * Gets the size of the display, in pixels.
      * Value returned by this method does not necessarily represent the actual raw size
      * (native resolution) of the display.
diff --git a/core/java/android/view/DisplayCutout.java b/core/java/android/view/DisplayCutout.java
index 3f2dd4d..525ac53 100644
--- a/core/java/android/view/DisplayCutout.java
+++ b/core/java/android/view/DisplayCutout.java
@@ -196,6 +196,12 @@
             return rects;
         }
 
+        private void scale(float scale) {
+            for (int i = 0; i < BOUNDS_POSITION_LENGTH; ++i) {
+                mRects[i].scale(scale);
+            }
+        }
+
         @Override
         public int hashCode() {
             int result = 0;
@@ -871,6 +877,16 @@
             mInner = cutout;
         }
 
+        public void scale(float scale) {
+            final Rect safeInsets = mInner.getSafeInsets();
+            safeInsets.scale(scale);
+            final Bounds bounds = new Bounds(mInner.mBounds.mRects, true);
+            bounds.scale(scale);
+            final Rect waterfallInsets = mInner.mWaterfallInsets.toRect();
+            waterfallInsets.scale(scale);
+            mInner = new DisplayCutout(safeInsets, Insets.of(waterfallInsets), bounds);
+        }
+
         @Override
         public int hashCode() {
             return mInner.hashCode();
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index 0ac0305..fc42cd0 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -275,6 +275,27 @@
     // TODO (b/114338689): Remove the flag and use IWindowManager#getRemoveContentMode
     public int removeMode = Display.REMOVE_MODE_MOVE_CONTENT_TO_PRIMARY;
 
+    /**
+     * @hide
+     * The current minimum brightness constraint of the display. Value between 0.0 and 1.0,
+     * derived from the config constraints of the display device of this logical display.
+     */
+    public float brightnessMinimum;
+
+    /**
+     * @hide
+     * The current maximum brightness constraint of the display. Value between 0.0 and 1.0,
+     * derived from the config constraints of the display device of this logical display.
+     */
+    public float brightnessMaximum;
+
+    /**
+     * @hide
+     * The current default brightness of the display. Value between 0.0 and 1.0,
+     * derived from the configuration of the display device of this logical display.
+     */
+    public float brightnessDefault;
+
     public static final @android.annotation.NonNull Creator<DisplayInfo> CREATOR = new Creator<DisplayInfo>() {
         @Override
         public DisplayInfo createFromParcel(Parcel source) {
@@ -339,7 +360,10 @@
                 && ownerUid == other.ownerUid
                 && Objects.equals(ownerPackageName, other.ownerPackageName)
                 && removeMode == other.removeMode
-                && refreshRateOverride == other.refreshRateOverride;
+                && refreshRateOverride == other.refreshRateOverride
+                && brightnessMinimum == other.brightnessMinimum
+                && brightnessMaximum == other.brightnessMaximum
+                && brightnessDefault == other.brightnessDefault;
     }
 
     @Override
@@ -384,6 +408,9 @@
         ownerPackageName = other.ownerPackageName;
         removeMode = other.removeMode;
         refreshRateOverride = other.refreshRateOverride;
+        brightnessMinimum = other.brightnessMinimum;
+        brightnessMaximum = other.brightnessMaximum;
+        brightnessDefault = other.brightnessDefault;
     }
 
     public void readFromParcel(Parcel source) {
@@ -430,6 +457,9 @@
         uniqueId = source.readString8();
         removeMode = source.readInt();
         refreshRateOverride = source.readFloat();
+        brightnessMinimum = source.readFloat();
+        brightnessMaximum = source.readFloat();
+        brightnessDefault = source.readFloat();
     }
 
     @Override
@@ -475,6 +505,9 @@
         dest.writeString8(uniqueId);
         dest.writeInt(removeMode);
         dest.writeFloat(refreshRateOverride);
+        dest.writeFloat(brightnessMinimum);
+        dest.writeFloat(brightnessMaximum);
+        dest.writeFloat(brightnessDefault);
     }
 
     @Override
@@ -698,7 +731,12 @@
         sb.append(removeMode);
         sb.append(", refreshRateOverride ");
         sb.append(refreshRateOverride);
-
+        sb.append(", brightnessMinimum ");
+        sb.append(brightnessMinimum);
+        sb.append(", brightnessMaximum ");
+        sb.append(brightnessMaximum);
+        sb.append(", brightnessDefault ");
+        sb.append(brightnessDefault);
         sb.append("}");
         return sb.toString();
     }
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 025a977..68a6de8 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -728,7 +728,7 @@
      * @return {@code true} if system bars are always comsumed.
      */
     boolean getWindowInsets(in WindowManager.LayoutParams attrs, int displayId,
-            out DisplayCutout.ParcelableWrapper outDisplayCutout, out InsetsState outInsetsState);
+            out InsetsState outInsetsState);
 
     /**
      * Called to show global actions.
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
index cfdaf8c..85498cb 100644
--- a/core/java/android/view/IWindowSession.aidl
+++ b/core/java/android/view/IWindowSession.aidl
@@ -47,13 +47,11 @@
 interface IWindowSession {
     int addToDisplay(IWindow window, in WindowManager.LayoutParams attrs,
             in int viewVisibility, in int layerStackId, in InsetsState requestedVisibility,
-            out Rect outFrame, out DisplayCutout.ParcelableWrapper displayCutout,
-            out InputChannel outInputChannel, out InsetsState insetsState,
+            out Rect outFrame, out InputChannel outInputChannel, out InsetsState insetsState,
             out InsetsSourceControl[] activeControls);
     int addToDisplayAsUser(IWindow window, in WindowManager.LayoutParams attrs,
             in int viewVisibility, in int layerStackId, in int userId,
-            in InsetsState requestedVisibility, out Rect outFrame,
-            out DisplayCutout.ParcelableWrapper displayCutout, out InputChannel outInputChannel,
+            in InsetsState requestedVisibility, out Rect outFrame, out InputChannel outInputChannel,
             out InsetsState insetsState, out InsetsSourceControl[] activeControls);
     int addToDisplayWithoutInputChannel(IWindow window, in WindowManager.LayoutParams attrs,
             in int viewVisibility, in int layerStackId, out InsetsState insetsState);
diff --git a/core/java/android/view/InsetsAnimationControlImpl.java b/core/java/android/view/InsetsAnimationControlImpl.java
index 75dc0c4..a89c540 100644
--- a/core/java/android/view/InsetsAnimationControlImpl.java
+++ b/core/java/android/view/InsetsAnimationControlImpl.java
@@ -334,8 +334,7 @@
     private Insets getInsetsFromState(InsetsState state, Rect frame,
             @Nullable @InternalInsetsSide SparseIntArray typeSideMap) {
         return state.calculateInsets(frame, null /* ignoringVisibilityState */,
-                false /* isScreenRound */,
-                false /* alwaysConsumeSystemBars */, null /* displayCutout */,
+                false /* isScreenRound */, false /* alwaysConsumeSystemBars */,
                 LayoutParams.SOFT_INPUT_ADJUST_RESIZE /* legacySoftInputMode*/,
                 0 /* legacyWindowFlags */, 0 /* legacySystemUiFlags */, TYPE_APPLICATION,
                 WINDOWING_MODE_UNDEFINED, typeSideMap).getInsets(mTypes);
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 49cd3a6..2d26c64 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -522,7 +522,6 @@
     private int mLastLegacyWindowFlags;
     private int mLastLegacySystemUiFlags;
     private int mLastWindowingMode;
-    private DisplayCutout mLastDisplayCutout;
     private boolean mStartingAnimation;
     private int mCaptionInsetsHeight = 0;
     private boolean mAnimationsDisabled;
@@ -589,9 +588,8 @@
 
             WindowInsets insets = state.calculateInsets(mFrame, mState /* ignoringVisibilityState*/,
                     mLastInsets.isRound(), mLastInsets.shouldAlwaysConsumeSystemBars(),
-                    mLastDisplayCutout, mLastLegacySoftInputMode, mLastLegacyWindowFlags,
-                    mLastLegacySystemUiFlags, mWindowType, mLastWindowingMode,
-                    null /* typeSideMap */);
+                    mLastLegacySoftInputMode, mLastLegacyWindowFlags, mLastLegacySystemUiFlags,
+                    mWindowType, mLastWindowingMode, null /* typeSideMap */);
             mHost.dispatchWindowInsetsAnimationProgress(insets, mUnmodifiableTmpRunningAnims);
             if (DEBUG) {
                 for (WindowInsetsAnimation anim : mUnmodifiableTmpRunningAnims) {
@@ -654,6 +652,7 @@
 
     private void updateState(InsetsState newState) {
         mState.setDisplayFrame(newState.getDisplayFrame());
+        mState.setDisplayCutout(newState.getDisplayCutout());
         @InsetsType int disabledUserAnimationTypes = 0;
         @InsetsType int[] cancelledUserAnimationTypes = {0};
         for (@InternalInsetsType int type = 0; type < InsetsState.SIZE; type++) {
@@ -725,18 +724,16 @@
      */
     @VisibleForTesting
     public WindowInsets calculateInsets(boolean isScreenRound, boolean alwaysConsumeSystemBars,
-            DisplayCutout cutout, int windowType, int windowingMode,
-            int legacySoftInputMode, int legacyWindowFlags, int legacySystemUiFlags) {
+            int windowType, int windowingMode, int legacySoftInputMode, int legacyWindowFlags,
+            int legacySystemUiFlags) {
         mWindowType = windowType;
         mLastWindowingMode = windowingMode;
         mLastLegacySoftInputMode = legacySoftInputMode;
         mLastLegacyWindowFlags = legacyWindowFlags;
         mLastLegacySystemUiFlags = legacySystemUiFlags;
-        mLastDisplayCutout = cutout;
         mLastInsets = mState.calculateInsets(mFrame, null /* ignoringVisibilityState*/,
-                isScreenRound, alwaysConsumeSystemBars, cutout,
-                legacySoftInputMode, legacyWindowFlags, legacySystemUiFlags,
-                windowType, windowingMode, null /* typeSideMap */);
+                isScreenRound, alwaysConsumeSystemBars, legacySoftInputMode, legacyWindowFlags,
+                legacySystemUiFlags, windowType, windowingMode, null /* typeSideMap */);
         return mLastInsets;
     }
 
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index b66dd29..bf377b0 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -16,6 +16,7 @@
 
 package android.view;
 
+import static android.view.InsetsStateProto.DISPLAY_CUTOUT;
 import static android.view.InsetsStateProto.DISPLAY_FRAME;
 import static android.view.InsetsStateProto.SOURCES;
 import static android.view.View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
@@ -165,6 +166,10 @@
      */
     private final Rect mDisplayFrame = new Rect();
 
+    /** The area cut from the display. */
+    private final DisplayCutout.ParcelableWrapper mDisplayCutout =
+            new DisplayCutout.ParcelableWrapper();
+
     public InsetsState() {
     }
 
@@ -186,7 +191,7 @@
      * @return The calculated insets.
      */
     public WindowInsets calculateInsets(Rect frame, @Nullable InsetsState ignoringVisibilityState,
-            boolean isScreenRound, boolean alwaysConsumeSystemBars, DisplayCutout cutout,
+            boolean isScreenRound, boolean alwaysConsumeSystemBars,
             int legacySoftInputMode, int legacyWindowFlags, int legacySystemUiFlags,
             int windowType, @WindowConfiguration.WindowingMode int windowingMode,
             @Nullable @InternalInsetsSide SparseIntArray typeSideMap) {
@@ -236,10 +241,31 @@
         }
 
         return new WindowInsets(typeInsetsMap, typeMaxInsetsMap, typeVisibilityMap, isScreenRound,
-                alwaysConsumeSystemBars, cutout, compatInsetsTypes,
+                alwaysConsumeSystemBars, calculateRelativeCutout(frame), compatInsetsTypes,
                 (legacySystemUiFlags & SYSTEM_UI_FLAG_LAYOUT_STABLE) != 0);
     }
 
+    private DisplayCutout calculateRelativeCutout(Rect frame) {
+        final DisplayCutout raw = mDisplayCutout.get();
+        if (mDisplayFrame.equals(frame)) {
+            return raw;
+        }
+        if (frame == null) {
+            return DisplayCutout.NO_CUTOUT;
+        }
+        final int insetLeft = frame.left - mDisplayFrame.left;
+        final int insetTop = frame.top - mDisplayFrame.top;
+        final int insetRight = mDisplayFrame.right - frame.right;
+        final int insetBottom = mDisplayFrame.bottom - frame.bottom;
+        if (insetLeft >= raw.getSafeInsetLeft()
+                && insetTop >= raw.getSafeInsetTop()
+                && insetRight >= raw.getSafeInsetRight()
+                && insetBottom >= raw.getSafeInsetBottom()) {
+            return DisplayCutout.NO_CUTOUT;
+        }
+        return raw.inset(insetLeft, insetTop, insetRight, insetBottom);
+    }
+
     public Rect calculateInsets(Rect frame, @InsetsType int types, boolean ignoreVisibility) {
         Insets insets = Insets.NONE;
         for (int type = FIRST_TYPE; type <= LAST_TYPE; type++) {
@@ -392,15 +418,6 @@
         return mSources[type];
     }
 
-    public boolean hasSources() {
-        for (int i = 0; i < SIZE; i++) {
-            if (mSources[i] != null) {
-                return true;
-            }
-        }
-        return false;
-    }
-
     /**
      * Returns the source visibility or the default visibility if the source doesn't exist. This is
      * useful if when treating this object as a request.
@@ -422,6 +439,14 @@
         return mDisplayFrame;
     }
 
+    public void setDisplayCutout(DisplayCutout cutout) {
+        mDisplayCutout.set(cutout);
+    }
+
+    public DisplayCutout getDisplayCutout() {
+        return mDisplayCutout.get();
+    }
+
     /**
      * Modifies the state of this class to exclude a certain type to make it ready for dispatching
      * to the client.
@@ -452,6 +477,7 @@
      */
     public void scale(float scale) {
         mDisplayFrame.scale(scale);
+        mDisplayCutout.scale(scale);
         for (int i = 0; i < SIZE; i++) {
             final InsetsSource source = mSources[i];
             if (source != null) {
@@ -470,6 +496,7 @@
 
     public void set(InsetsState other, boolean copySources) {
         mDisplayFrame.set(other.mDisplayFrame);
+        mDisplayCutout.set(other.mDisplayCutout);
         if (copySources) {
             for (int i = 0; i < SIZE; i++) {
                 InsetsSource source = other.mSources[i];
@@ -592,6 +619,7 @@
             source.dumpDebug(proto, SOURCES);
         }
         mDisplayFrame.dumpDebug(proto, DISPLAY_FRAME);
+        mDisplayCutout.get().dumpDebug(proto, DISPLAY_CUTOUT);
         proto.end(token);
     }
 
@@ -669,7 +697,8 @@
 
         InsetsState state = (InsetsState) o;
 
-        if (!mDisplayFrame.equals(state.mDisplayFrame)) {
+        if (!mDisplayFrame.equals(state.mDisplayFrame)
+                || !mDisplayCutout.equals(state.mDisplayCutout)) {
             return false;
         }
         for (int i = 0; i < SIZE; i++) {
@@ -681,7 +710,7 @@
             if (source == null && otherSource == null) {
                 continue;
             }
-            if (source != null && otherSource == null || source == null && otherSource != null) {
+            if (source == null || otherSource == null) {
                 return false;
             }
             if (!otherSource.equals(source, excludeInvisibleImeFrames)) {
@@ -693,7 +722,7 @@
 
     @Override
     public int hashCode() {
-        return Objects.hash(mDisplayFrame, Arrays.hashCode(mSources));
+        return Objects.hash(mDisplayFrame, mDisplayCutout, Arrays.hashCode(mSources));
     }
 
     public InsetsState(Parcel in) {
@@ -707,7 +736,8 @@
 
     @Override
     public void writeToParcel(Parcel dest, int flags) {
-        dest.writeParcelable(mDisplayFrame, flags);
+        mDisplayFrame.writeToParcel(dest, flags);
+        mDisplayCutout.writeToParcel(dest, flags);
         dest.writeParcelableArray(mSources, 0);
     }
 
@@ -723,7 +753,8 @@
     };
 
     public void readFromParcel(Parcel in) {
-        mDisplayFrame.set(in.readParcelable(null /* loader */));
+        mDisplayFrame.set(Rect.CREATOR.createFromParcel(in));
+        mDisplayCutout.set(DisplayCutout.ParcelableWrapper.CREATOR.createFromParcel(in));
         mSources = in.readParcelableArray(null, InsetsSource.class);
     }
 
@@ -738,6 +769,7 @@
         }
         return "InsetsState: {"
                 + "mDisplayFrame=" + mDisplayFrame
+                + ", mDisplayCutout=" + mDisplayCutout
                 + ", mSources= { " + joiner
                 + " }";
     }
diff --git a/core/java/android/view/OWNERS b/core/java/android/view/OWNERS
index bae6ee8..e66b17a 100644
--- a/core/java/android/view/OWNERS
+++ b/core/java/android/view/OWNERS
@@ -33,6 +33,9 @@
 per-file InputWindowHandle.java  = file:/services/core/java/com/android/server/input/OWNERS
 per-file InputWindowHandle.java  = file:/services/core/java/com/android/server/wm/OWNERS
 
+# Notifications
+per-file Notification*.java = file:/services/core/java/com/android/server/notification/OWNERS
+
 # Surface
 per-file Surface.java = file:/graphics/java/android/graphics/OWNERS
 per-file Surface.java = file:/services/core/java/com/android/server/wm/OWNERS
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 36124e8..26e3bb2 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -240,7 +240,7 @@
     private static boolean useBlastAdapter(Context context) {
         ContentResolver contentResolver = context.getContentResolver();
         return Settings.Global.getInt(contentResolver,
-                Settings.Global.DEVELOPMENT_USE_BLAST_ADAPTER_SV, 0 /* default */) == 1;
+                Settings.Global.DEVELOPMENT_USE_BLAST_ADAPTER_SV, 1 /* default */) == 1;
     }
 
     private final boolean mUseBlastAdapter;
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 1d1c87d..0f53215 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -1274,7 +1274,6 @@
             AUTOFILL_TYPE_TOGGLE,
             AUTOFILL_TYPE_LIST,
             AUTOFILL_TYPE_DATE,
-            AUTOFILL_TYPE_RICH_CONTENT
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface AutofillType {}
@@ -1338,17 +1337,6 @@
      */
     public static final int AUTOFILL_TYPE_DATE = 4;
 
-    /**
-     * Autofill type for a field that can accept rich content (text, images, etc).
-     *
-     * <p>{@link AutofillValue} instances for autofilling a {@link View} can be obtained through
-     * {@link AutofillValue#forRichContent(ClipData)}, and the values passed to
-     * autofill a {@link View} can be fetched through {@link AutofillValue#getRichContentValue()}.
-     *
-     * @see #getAutofillType()
-     */
-    public static final int AUTOFILL_TYPE_RICH_CONTENT = 5;
-
 
     /** @hide */
     @IntDef(prefix = { "IMPORTANT_FOR_AUTOFILL_" }, value = {
@@ -29071,9 +29059,6 @@
          */
         final Rect mCaptionInsets = new Rect();
 
-        final DisplayCutout.ParcelableWrapper mDisplayCutout =
-                new DisplayCutout.ParcelableWrapper(DisplayCutout.NO_CUTOUT);
-
         /**
          * In multi-window we force show the system bars. Because we don't want that the surface
          * size changes in this mode, we instead have a flag whether the system bars sizes should
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 0fc2d7a..d863ea5 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -42,7 +42,6 @@
 import static android.view.ViewRootImplProto.IS_ANIMATING;
 import static android.view.ViewRootImplProto.IS_DRAWING;
 import static android.view.ViewRootImplProto.LAST_WINDOW_INSETS;
-import static android.view.ViewRootImplProto.PENDING_DISPLAY_CUTOUT;
 import static android.view.ViewRootImplProto.REMOVED;
 import static android.view.ViewRootImplProto.SCROLL_Y;
 import static android.view.ViewRootImplProto.SOFT_INPUT_MODE;
@@ -573,8 +572,6 @@
     final Rect mWinFrame; // frame given by window manager.
 
     final Rect mPendingBackDropFrame = new Rect();
-    final DisplayCutout.ParcelableWrapper mPendingDisplayCutout =
-            new DisplayCutout.ParcelableWrapper(DisplayCutout.NO_CUTOUT);
     boolean mPendingAlwaysConsumeSystemBars;
     private final InsetsState mTempInsets = new InsetsState();
     private final InsetsSourceControl[] mTempControls = new InsetsSourceControl[SIZE];
@@ -698,34 +695,33 @@
         int localChanges;
     }
 
-    // If set, ViewRootImpl will request a callback from HWRender when it's ready to render the next
-    // frame. This will allow VRI to call BLASTBufferQueue::setNextTransaction with
-    // mRtBLASTSyncTransaction, so the next frame submitted will be added to the
-    // mRtBLASTSyncTransaction instead of getting applied.
-    private boolean mNextDrawUseBLASTSyncTransaction;
-
-    // This is used to signal if the mRtBLASTSyncTransaction should be applied/merged. When true,
-    // it indicates mRtBLASTSyncTransaction was sent to BLASTBufferQueue::setNextTransaction.
-    // Therefore, in onFrameComplete, if mRtNextFrameReportConsumeWithBlast is true, that means
-    // mRtBLASTSyncTransaction now contains the next buffer frame to be applied.
-    private boolean mRtNextFrameReportedConsumeWithBlast;
-
-    // Be very careful with the threading here. This is used from a thread pool generated by the
-    // render thread. Therefore, it needs to be locked when updating from the thread pool since
-    // multiple threads can be accessing it. It does not need to be locked when applied or merged
-    // since that can only happen from the frame complete callback after the other callbacks have
-    // been invoked.
+    /**
+     * This is only used when the UI thread is paused due to {@link #mNextDrawUseBlastSync} being
+     * set. Specifically, it's only used when calling
+     * {@link BLASTBufferQueue#setNextTransaction(Transaction)} and then merged with
+     * {@link #mSurfaceChangedTransaction}. It doesn't need to be thread safe since it's only
+     * accessed when the UI thread is paused.
+     */
     private final SurfaceControl.Transaction mRtBLASTSyncTransaction =
             new SurfaceControl.Transaction();
 
-    // Keeps track of whether the WM requested us to use BLAST Sync when calling relayout.
-    //  We use this to make sure we don't send the WM transactions from an internal BLAST sync
-    // (e.g. SurfaceView)
-    private boolean mSendNextFrameToWm = false;
+    /**
+     * Keeps track of whether the WM requested to use BLAST Sync when calling relayout. When set,
+     * we pause the UI thread to ensure we don't get overlapping requests. We then send a
+     * transaction to {@link BLASTBufferQueue#setNextTransaction(Transaction)}, which is then sent
+     * back to WM to synchronize.
+     *
+     * This flag is set to false only after the synchronized transaction that contains the buffer
+     * has been sent to SurfaceFlinger.
+     */
+    private boolean mNextDrawUseBlastSync = false;
 
-    // Keeps track of whether a traverse was triggered while the UI thread was paused. This can
-    // occur when the client is waiting on another process to submit the transaction that contains
-    // the buffer. The UI thread needs to wait on the callback before it can submit another buffer.
+    /**
+     * Keeps track of whether a traverse was triggered while the UI thread was paused. This can
+     * occur when the client is waiting on another process to submit the transaction that
+     * contains the buffer. The UI thread needs to wait on the callback before it can submit
+     * another buffer.
+     */
     private boolean mRequestedTraverseWhilePaused = false;
 
     private HashSet<ScrollCaptureCallback> mRootScrollCaptureCallbacks;
@@ -1062,8 +1058,7 @@
                     res = mWindowSession.addToDisplayAsUser(mWindow, mWindowAttributes,
                             getHostVisibility(), mDisplay.getDisplayId(), userId,
                             mInsetsController.getRequestedVisibility(), mTmpFrames.frame,
-                            mAttachInfo.mDisplayCutout, inputChannel,
-                            mTempInsets, mTempControls);
+                            inputChannel, mTempInsets, mTempControls);
                     if (mTranslator != null) {
                         mTranslator.translateRectInScreenToAppWindow(mTmpFrames.frame);
                         mTranslator.translateInsetsStateInScreenToAppWindow(mTempInsets);
@@ -1084,7 +1079,6 @@
                     }
                 }
 
-                mPendingDisplayCutout.set(mAttachInfo.mDisplayCutout);
                 mAttachInfo.mAlwaysConsumeSystemBars =
                         (res & WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_SYSTEM_BARS) != 0;
                 mPendingAlwaysConsumeSystemBars = mAttachInfo.mAlwaysConsumeSystemBars;
@@ -1395,7 +1389,8 @@
         return mLocation;
     }
 
-    void setLayoutParams(WindowManager.LayoutParams attrs, boolean newView) {
+    @VisibleForTesting
+    public void setLayoutParams(WindowManager.LayoutParams attrs, boolean newView) {
         synchronized (this) {
             final int oldInsetLeft = mWindowAttributes.surfaceInsets.left;
             final int oldInsetTop = mWindowAttributes.surfaceInsets.top;
@@ -1417,13 +1412,15 @@
             final int compatibleWindowFlag = mWindowAttributes.privateFlags
                     & WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
 
-            // Transfer over system UI visibility values as they carry current state.
-            attrs.systemUiVisibility = mWindowAttributes.systemUiVisibility;
-            attrs.subtreeSystemUiVisibility = mWindowAttributes.subtreeSystemUiVisibility;
+            // Preserve system UI visibility.
+            final int systemUiVisibility = mWindowAttributes.systemUiVisibility;
+            final int subtreeSystemUiVisibility = mWindowAttributes.subtreeSystemUiVisibility;
 
-            // Transfer over appearance and behavior values as they carry current state.
-            attrs.insetsFlags.appearance = mWindowAttributes.insetsFlags.appearance;
-            attrs.insetsFlags.behavior = mWindowAttributes.insetsFlags.behavior;
+            // Preserve appearance and behavior.
+            final int appearance = mWindowAttributes.insetsFlags.appearance;
+            final int behavior = mWindowAttributes.insetsFlags.behavior;
+            final int appearanceAndBehaviorPrivateFlags = mWindowAttributes.privateFlags
+                    & (PRIVATE_FLAG_APPEARANCE_CONTROLLED | PRIVATE_FLAG_BEHAVIOR_CONTROLLED);
 
             final int changes = mWindowAttributes.copyFrom(attrs);
             if ((changes & WindowManager.LayoutParams.TRANSLUCENT_FLAGS_CHANGED) != 0) {
@@ -1437,10 +1434,15 @@
             if (mWindowAttributes.packageName == null) {
                 mWindowAttributes.packageName = mBasePackageName;
             }
-            mWindowAttributes.privateFlags |= compatibleWindowFlag;
 
-            mWindowAttributes.privateFlags |=
-                    WindowManager.LayoutParams.PRIVATE_FLAG_USE_BLAST;
+            // Restore preserved flags.
+            mWindowAttributes.systemUiVisibility = systemUiVisibility;
+            mWindowAttributes.subtreeSystemUiVisibility = subtreeSystemUiVisibility;
+            mWindowAttributes.insetsFlags.appearance = appearance;
+            mWindowAttributes.insetsFlags.behavior = behavior;
+            mWindowAttributes.privateFlags |= compatibleWindowFlag
+                    | appearanceAndBehaviorPrivateFlags
+                    | WindowManager.LayoutParams.PRIVATE_FLAG_USE_BLAST;
 
             if (mWindowAttributes.preservePreviousSurfaceInsets) {
                 // Restore old surface insets.
@@ -1505,15 +1507,13 @@
         final boolean forceNextWindowRelayout = args.argi1 != 0;
         final int displayId = args.argi3;
         final Rect backdropFrame = frames.backdropFrame;
-        final DisplayCutout displayCutout = frames.displayCutout.get();
 
         final boolean frameChanged = !mWinFrame.equals(frames.frame);
-        final boolean cutoutChanged = !mPendingDisplayCutout.get().equals(displayCutout);
         final boolean backdropFrameChanged = !mPendingBackDropFrame.equals(backdropFrame);
         final boolean configChanged = !mLastReportedMergedConfiguration.equals(mergedConfiguration);
         final boolean displayChanged = mDisplay.getDisplayId() != displayId;
-        if (msg == MSG_RESIZED && !frameChanged && !cutoutChanged && !backdropFrameChanged
-                && !configChanged && !displayChanged && !forceNextWindowRelayout) {
+        if (msg == MSG_RESIZED && !frameChanged && !backdropFrameChanged && !configChanged
+                && !displayChanged && !forceNextWindowRelayout) {
             return;
         }
 
@@ -1528,16 +1528,15 @@
 
         setFrame(frames.frame);
         mTmpFrames.displayFrame.set(frames.displayFrame);
-        mPendingDisplayCutout.set(displayCutout);
         mPendingBackDropFrame.set(backdropFrame);
         mForceNextWindowRelayout = forceNextWindowRelayout;
         mPendingAlwaysConsumeSystemBars = args.argi2 != 0;
 
-        if (msg == MSG_RESIZED_REPORT && !mSendNextFrameToWm) {
+        if (msg == MSG_RESIZED_REPORT && !mNextDrawUseBlastSync) {
             reportNextDraw();
         }
 
-        if (mView != null && (frameChanged || cutoutChanged || configChanged)) {
+        if (mView != null && (frameChanged || configChanged)) {
             forceLayout(mView);
         }
         requestLayout();
@@ -2337,8 +2336,7 @@
             final Configuration config = mContext.getResources().getConfiguration();
             mLastWindowInsets = mInsetsController.calculateInsets(
                     config.isScreenRound(), mAttachInfo.mAlwaysConsumeSystemBars,
-                    mPendingDisplayCutout.get(), mWindowAttributes.type,
-                    config.windowConfiguration.getWindowingMode(),
+                    mWindowAttributes.type, config.windowConfiguration.getWindowingMode(),
                     mWindowAttributes.softInputMode, mWindowAttributes.flags,
                     (mWindowAttributes.systemUiVisibility
                             | mWindowAttributes.subtreeSystemUiVisibility));
@@ -2424,7 +2422,7 @@
         //
         // When the callback is invoked, it will trigger a traversal request if
         // mRequestedTraverseWhilePaused is set so there's no need to attempt a retry here.
-        if (mSendNextFrameToWm) {
+        if (mNextDrawUseBlastSync) {
             if (DEBUG_BLAST) {
                 Log.w(mTag, "Can't perform draw while waiting for a transaction complete");
             }
@@ -2538,8 +2536,6 @@
         // Execute enqueued actions on every traversal in case a detached view enqueued an action
         getRunQueue().executeActions(mAttachInfo.mHandler);
 
-        boolean cutoutChanged = false;
-
         boolean layoutRequested = mLayoutRequested && (!mStopped || mReportNextDraw);
         if (layoutRequested) {
 
@@ -2551,9 +2547,6 @@
                 mAttachInfo.mInTouchMode = !mAddedTouchMode;
                 ensureTouchModeLocally(mAddedTouchMode);
             } else {
-                if (!mPendingDisplayCutout.equals(mAttachInfo.mDisplayCutout)) {
-                    cutoutChanged = true;
-                }
                 if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT
                         || lp.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
                     windowSizeMayChange = true;
@@ -2679,7 +2672,7 @@
             }
         }
 
-        if (mFirst || windowShouldResize || viewVisibilityChanged || cutoutChanged || params != null
+        if (mFirst || windowShouldResize || viewVisibilityChanged || params != null
                 || mForceNextWindowRelayout) {
             mForceNextWindowRelayout = false;
 
@@ -2719,7 +2712,6 @@
                 relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);
 
                 if (DEBUG_LAYOUT) Log.v(mTag, "relayout: frame=" + frame.toShortString()
-                        + " cutout=" + mPendingDisplayCutout.get().toString()
                         + " surface=" + mSurface);
 
                 // If the pending {@link MergedConfiguration} handed back from
@@ -2735,7 +2727,6 @@
                     updatedConfiguration = true;
                 }
 
-                cutoutChanged = !mPendingDisplayCutout.equals(mAttachInfo.mDisplayCutout);
                 surfaceSizeChanged = false;
                 if (!mLastSurfaceSize.equals(mSurfaceSize)) {
                     surfaceSizeChanged = true;
@@ -2759,14 +2750,6 @@
                     mSurfaceSequenceId++;
                 }
 
-                if (cutoutChanged) {
-                    mAttachInfo.mDisplayCutout.set(mPendingDisplayCutout);
-                    if (DEBUG_LAYOUT) {
-                        Log.v(mTag, "DisplayCutout changing to: " + mAttachInfo.mDisplayCutout);
-                    }
-                    // Need to relayout with content insets.
-                    dispatchApplyInsets = true;
-                }
                 if (alwaysConsumeSystemBarsChanged) {
                     mAttachInfo.mAlwaysConsumeSystemBars = mPendingAlwaysConsumeSystemBars;
                     dispatchApplyInsets = true;
@@ -3176,8 +3159,7 @@
                 Log.d(mTag, "Relayout called with blastSync");
             }
             reportNextDraw();
-            setUseBLASTSyncTransaction();
-            mSendNextFrameToWm = true;
+            mNextDrawUseBlastSync = true;
         }
 
         boolean cancelDraw = mAttachInfo.mTreeObserver.dispatchOnPreDraw() || !isViewVisible;
@@ -3868,19 +3850,19 @@
     private HardwareRenderer.FrameCompleteCallback createFrameCompleteCallback(Handler handler,
             boolean reportNextDraw, ArrayList<Runnable> commitCallbacks) {
         return frameNr -> {
-            mBlurRegionAggregator.dispatchBlurTransactionIfNeeded(frameNr);
-
             if (DEBUG_BLAST) {
                 Log.d(mTag, "Received frameCompleteCallback frameNum=" + frameNr);
             }
-            // Use a new transaction here since mRtBLASTSyncTransaction can only be accessed by
-            // the render thread and mSurfaceChangedTransaction can only be accessed by the UI
-            // thread. The temporary transaction is used so mRtBLASTSyncTransaction can be merged
-            // with mSurfaceChangedTransaction without synchronization issues.
-            final Transaction t = new Transaction();
-            finishBLASTSyncOnRT(!mSendNextFrameToWm, t);
+
             handler.postAtFrontOfQueue(() -> {
-                mSurfaceChangedTransaction.merge(t);
+                if (mNextDrawUseBlastSync) {
+                    // We don't need to synchronize mRtBLASTSyncTransaction here since we're
+                    // guaranteed that this is called after onFrameDraw and mNextDrawUseBlastSync
+                    // is only true when the UI thread is paused. Therefore, no one should be
+                    // modifying this object until the next vsync.
+                    mSurfaceChangedTransaction.merge(mRtBLASTSyncTransaction);
+                }
+
                 if (reportNextDraw) {
                     // TODO: Use the frame number
                     pendingDrawFinished();
@@ -3902,13 +3884,12 @@
         ArrayList<Runnable> commitCallbacks = mAttachInfo.mTreeObserver
                 .captureFrameCommitCallbacks();
         final boolean needFrameCompleteCallback =
-                mNextDrawUseBLASTSyncTransaction || mReportNextDraw
-                        || (commitCallbacks != null && commitCallbacks.size() > 0)
-                        || mBlurRegionAggregator.hasRegions();
+                mNextDrawUseBlastSync || mReportNextDraw
+                        || (commitCallbacks != null && commitCallbacks.size() > 0);
         if (needFrameCompleteCallback) {
             if (DEBUG_BLAST) {
                 Log.d(mTag, "Creating frameCompleteCallback"
-                        + " mNextDrawUseBLASTSyncTransaction=" + mNextDrawUseBLASTSyncTransaction
+                        + " mNextDrawUseBlastSync=" + mNextDrawUseBlastSync
                         + " mReportNextDraw=" + mReportNextDraw
                         + " commitCallbacks size="
                         + (commitCallbacks == null ? 0 : commitCallbacks.size()));
@@ -3921,75 +3902,70 @@
         return false;
     }
 
-    /**
-     * The callback will run on a worker thread pool from the render thread.
-     */
-    private HardwareRenderer.FrameDrawingCallback createFrameDrawingCallback(
-            boolean addTransactionComplete) {
-        return frame -> {
+    private void addFrameCallbackIfNeeded() {
+        boolean nextDrawUseBlastSync = mNextDrawUseBlastSync;
+        boolean hasBlur = mBlurRegionAggregator.hasRegions();
+        boolean reportNextDraw = mReportNextDraw;
+
+        if (!nextDrawUseBlastSync && !reportNextDraw && !hasBlur) {
+            return;
+        }
+
+        if (DEBUG_BLAST) {
+            Log.d(mTag, "Creating frameDrawingCallback"
+                    + " nextDrawUseBlastSync=" + nextDrawUseBlastSync
+                    + " reportNextDraw=" + reportNextDraw
+                    + " hasBlur=" + hasBlur);
+        }
+
+        // The callback will run on a worker thread pool from the render thread.
+        HardwareRenderer.FrameDrawingCallback frameDrawingCallback = frame -> {
             if (DEBUG_BLAST) {
                 Log.d(mTag, "Received frameDrawingCallback frameNum=" + frame + "."
-                        + " Creating transactionCompleteCallback=" + addTransactionComplete);
+                        + " Creating transactionCompleteCallback=" + nextDrawUseBlastSync);
             }
-            mRtNextFrameReportedConsumeWithBlast = true;
+
+            if (hasBlur) {
+                mBlurRegionAggregator.dispatchBlurTransactionIfNeeded(frame);
+            }
+
             if (mBlastBufferQueue == null) {
                 return;
             }
 
-            // We don't need to synchronize mRtBLASTSyncTransaction here since it's not
-            // being modified and only sent to BlastBufferQueue.
-            mBlastBufferQueue.setNextTransaction(mRtBLASTSyncTransaction);
-            if (!addTransactionComplete) {
-                return;
-            }
+            if (nextDrawUseBlastSync) {
+                // Frame callbacks will always occur after submitting draw requests and before
+                // the draw actually occurs. This will ensure that we set the next transaction
+                // for the frame that's about to get drawn and not on a previous frame that.
 
-            mBlastBufferQueue.setTransactionCompleteCallback(frame, frameNumber -> {
-                if (DEBUG_BLAST) {
-                    Log.d(mTag, "Received transactionCompleteCallback frameNum=" + frame);
-                }
-                mHandler.postAtFrontOfQueue(() -> {
-                    mSendNextFrameToWm = false;
+                // We don't need to synchronize mRtBLASTSyncTransaction here since it's not
+                // being modified and only sent to BlastBufferQueue.
+                mBlastBufferQueue.setNextTransaction(mRtBLASTSyncTransaction);
+
+                mBlastBufferQueue.setTransactionCompleteCallback(frame, frameNumber -> {
                     if (DEBUG_BLAST) {
-                        Log.d(mTag, "Scheduling a traversal=" + mRequestedTraverseWhilePaused
-                                + " due to a previous skipped traversal.");
+                        Log.d(mTag, "Received transactionCompleteCallback frameNum=" + frame);
                     }
-                    if (mRequestedTraverseWhilePaused) {
-                        mRequestedTraverseWhilePaused = false;
-                        scheduleTraversals();
-                    }
+                    mHandler.postAtFrontOfQueue(() -> {
+                        mNextDrawUseBlastSync = false;
+                        if (DEBUG_BLAST) {
+                            Log.d(mTag, "Scheduling a traversal=" + mRequestedTraverseWhilePaused
+                                    + " due to a previous skipped traversal.");
+                        }
+                        if (mRequestedTraverseWhilePaused) {
+                            mRequestedTraverseWhilePaused = false;
+                            scheduleTraversals();
+                        }
+                    });
                 });
-            });
-        };
-    }
-
-    private void addFrameCallbackIfNeeded() {
-        if (DEBUG_BLAST) {
-            if (mNextDrawUseBLASTSyncTransaction || mReportNextDraw) {
-                Log.d(mTag, "Creating frameDrawingCallback mNextDrawUseBLASTSyncTransaction="
-                        + mNextDrawUseBLASTSyncTransaction + " mReportNextDraw=" + mReportNextDraw);
+            } else if (reportNextDraw) {
+                // If we need to report next draw, wait for adapter to flush its shadow queue
+                // by processing previously queued buffers so that we can submit the
+                // transaction a timely manner.
+                mBlastBufferQueue.flushShadowQueue();
             }
-        }
-
-        if (mNextDrawUseBLASTSyncTransaction) {
-            // Frame callbacks will always occur after submitting draw requests and before
-            // the draw actually occurs. This will ensure that we set the next transaction
-            // for the frame that's about to get drawn and not on a previous frame that.
-            //
-            // This is thread safe since mRtNextFrameReportConsumeWithBlast will only be
-            // modified in onFrameDraw and then again in onFrameComplete. This is to ensure the
-            // next frame completed should be reported with the blast sync transaction.
-            registerRtFrameCallback(createFrameDrawingCallback(mSendNextFrameToWm));
-            mNextDrawUseBLASTSyncTransaction = false;
-        } else if (mReportNextDraw) {
-            registerRtFrameCallback(frame -> {
-                if (mBlastBufferQueue != null) {
-                    // If we need to report next draw, wait for adapter to flush its shadow queue
-                    // by processing previously queued buffers so that we can submit the
-                    // transaction a timely manner.
-                    mBlastBufferQueue.flushShadowQueue();
-                }
-            });
-        }
+        };
+        registerRtFrameCallback(frameDrawingCallback);
     }
 
     private void performDraw() {
@@ -4000,7 +3976,7 @@
         }
 
         final boolean fullRedrawNeeded =
-                mFullRedrawNeeded || mReportNextDraw || mNextDrawUseBLASTSyncTransaction;
+                mFullRedrawNeeded || mReportNextDraw || mNextDrawUseBlastSync;
         mFullRedrawNeeded = false;
 
         mIsDrawing = true;
@@ -5535,26 +5511,39 @@
             if (mView == null || !mAdded) {
                 Slog.w(mTag, "Dropping event due to root view being removed: " + q.mEvent);
                 return true;
-            } else if ((!mAttachInfo.mHasWindowFocus
-                    && !q.mEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER)
-                    && !isAutofillUiShowing()) || mStopped
-                    || (mIsAmbientMode && !q.mEvent.isFromSource(InputDevice.SOURCE_CLASS_BUTTON))
-                    || (mPausedForTransition && !isBack(q.mEvent))) {
-                // This is a focus event and the window doesn't currently have input focus or
-                // has stopped. This could be an event that came back from the previous stage
-                // but the window has lost focus or stopped in the meantime.
-                if (isTerminalInputEvent(q.mEvent)) {
-                    // Don't drop terminal input events, however mark them as canceled.
-                    q.mEvent.cancel();
-                    Slog.w(mTag, "Cancelling event due to no window focus: " + q.mEvent);
-                    return false;
-                }
-
-                // Drop non-terminal input events.
-                Slog.w(mTag, "Dropping event due to no window focus: " + q.mEvent);
-                return true;
             }
-            return false;
+
+            // Find a reason for dropping or canceling the event.
+            final String reason;
+            if (!mAttachInfo.mHasWindowFocus
+                    && !q.mEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER)
+                    && !isAutofillUiShowing()) {
+                // This is a non-pointer event and the window doesn't currently have input focus
+                // This could be an event that came back from the previous stage
+                // but the window has lost focus or stopped in the meantime.
+                reason = "no window focus";
+            } else if (mStopped) {
+                reason = "window is stopped";
+            } else if (mIsAmbientMode
+                    && !q.mEvent.isFromSource(InputDevice.SOURCE_CLASS_BUTTON)) {
+                reason = "non-button event in ambient mode";
+            } else if (mPausedForTransition && !isBack(q.mEvent)) {
+                reason = "paused for transition";
+            } else {
+                // Most common path: no reason to drop or cancel the event
+                return false;
+            }
+
+            if (isTerminalInputEvent(q.mEvent)) {
+                // Don't drop terminal input events, however mark them as canceled.
+                q.mEvent.cancel();
+                Slog.w(mTag, "Cancelling event (" + reason + "):" + q.mEvent);
+                return false;
+            }
+
+            // Drop non-terminal input events.
+            Slog.w(mTag, "Dropping event (" + reason + "):" + q.mEvent);
+            return true;
         }
 
         void dump(String prefix, PrintWriter writer) {
@@ -7595,7 +7584,6 @@
                 insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, frameNumber,
                 mTmpFrames, mPendingMergedConfiguration, mSurfaceControl, mTempInsets,
                 mTempControls, mSurfaceSize);
-        mPendingDisplayCutout.set(mTmpFrames.displayCutout);
         mPendingBackDropFrame.set(mTmpFrames.backdropFrame);
         if (mSurfaceControl.isValid()) {
             if (!useBLAST()) {
@@ -7756,7 +7744,6 @@
         proto.write(IS_DRAWING, mIsDrawing);
         proto.write(ADDED, mAdded);
         mWinFrame.dumpDebug(proto, WIN_FRAME);
-        mPendingDisplayCutout.get().dumpDebug(proto, PENDING_DISPLAY_CUTOUT);
         proto.write(LAST_WINDOW_INSETS, Objects.toString(mLastWindowInsets));
         proto.write(SOFT_INPUT_MODE, InputMethodDebug.softInputModeToString(mSoftInputMode));
         proto.write(SCROLL_Y, mScrollY);
@@ -10023,42 +10010,6 @@
         }
     }
 
-    void setUseBLASTSyncTransaction() {
-        mNextDrawUseBLASTSyncTransaction = true;
-    }
-
-    /**
-     * This should only be called from the render thread.
-     */
-    private void finishBLASTSyncOnRT(boolean apply, Transaction t) {
-        // This is safe to modify on the render thread since the only other place it's modified
-        // is on the UI thread when the render thread is paused.
-        if (mRtNextFrameReportedConsumeWithBlast) {
-            mRtNextFrameReportedConsumeWithBlast = false;
-
-            // We don't need to synchronize mRtBLASTSyncTransaction here we're guaranteed that this
-            // is called after all onFrameDraw and after callbacks to PositionUpdateListener.
-            // Therefore, no one should be modifying this object until the next vsync.
-            if (apply) {
-                mRtBLASTSyncTransaction.apply();
-            } else {
-                t.merge(mRtBLASTSyncTransaction);
-            }
-
-            // There's potential for the frame callback to get called even if nothing was drawn.
-            // When that occurs, we remove the transaction sent to BBQ since the draw we were
-            // waiting on will not happen. We can apply the transaction here but it will not contain
-            // a buffer since nothing new was drawn.
-            //
-            // This is mainly for the case when the SurfaceView has changed and wants to synchronize
-            // with the main window. If the main window doesn't need to draw anything, we can just
-            // apply the transaction without the new buffer from the main window.
-            if (mBlastBufferQueue != null) {
-                mBlastBufferQueue.setNextTransaction(null);
-            }
-        }
-    }
-
     /**
      * Sends a list of blur regions to SurfaceFlinger, tagged with a frame.
      *
@@ -10070,14 +10021,14 @@
         if (!surfaceControl.isValid()) {
             return;
         }
-        if (useBLAST()) {
-            synchronized (getBlastTransactionLock()) {
-                getBLASTSyncTransaction().setBlurRegions(surfaceControl, regionCopy);
-            }
+
+        SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
+        transaction.setBlurRegions(surfaceControl, regionCopy);
+
+        if (useBLAST() && mBlastBufferQueue != null) {
+            mBlastBufferQueue.mergeWithNextTransaction(transaction, frameNumber);
         } else {
-            SurfaceControl.Transaction transaction = new SurfaceControl.Transaction();
-            transaction.setBlurRegions(surfaceControl, regionCopy);
-            transaction.deferTransactionUntil(surfaceControl, getSurfaceControl(), frameNumber);
+            transaction.deferTransactionUntil(surfaceControl, surfaceControl, frameNumber);
             transaction.apply();
         }
     }
@@ -10089,14 +10040,6 @@
         return mBlurRegionAggregator.createBackgroundBlurDrawable(mContext);
     }
 
-    SurfaceControl.Transaction getBLASTSyncTransaction() {
-        return mRtBLASTSyncTransaction;
-    }
-
-    Object getBlastTransactionLock() {
-        return mRtBLASTSyncTransaction;
-    }
-
     @Override
     public void onDescendantUnbufferedRequested() {
         mUnbufferedInputSource = mView.mUnbufferedInputSource;
diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java
index 3384bbe..391e55a 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -262,18 +262,15 @@
 
     private WindowInsets getWindowInsetsFromServer(WindowManager.LayoutParams attrs, Rect bounds) {
         try {
-            final DisplayCutout.ParcelableWrapper displayCutout =
-                    new DisplayCutout.ParcelableWrapper();
             final InsetsState insetsState = new InsetsState();
             final boolean alwaysConsumeSystemBars = WindowManagerGlobal.getWindowManagerService()
-                    .getWindowInsets(attrs, mContext.getDisplayId(), displayCutout, insetsState);
+                    .getWindowInsets(attrs, mContext.getDisplayId(), insetsState);
             final Configuration config = mContext.getResources().getConfiguration();
             final boolean isScreenRound = config.isScreenRound();
             final int windowingMode = config.windowConfiguration.getWindowingMode();
             return insetsState.calculateInsets(bounds, null /* ignoringVisibilityState*/,
-                    isScreenRound, alwaysConsumeSystemBars, displayCutout.get(),
-                    SOFT_INPUT_ADJUST_NOTHING, attrs.flags, SYSTEM_UI_FLAG_VISIBLE, attrs.type,
-                    windowingMode, null /* typeSideMap */);
+                    isScreenRound, alwaysConsumeSystemBars, SOFT_INPUT_ADJUST_NOTHING, attrs.flags,
+                    SYSTEM_UI_FLAG_VISIBLE, attrs.type, windowingMode, null /* typeSideMap */);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index 149338c..dd56c15 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -136,8 +136,8 @@
     @Override
     public int addToDisplay(IWindow window, WindowManager.LayoutParams attrs,
             int viewVisibility, int displayId, InsetsState requestedVisibility, Rect outFrame,
-            DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
-            InsetsState outInsetsState, InsetsSourceControl[] outActiveControls) {
+            InputChannel outInputChannel, InsetsState outInsetsState,
+            InsetsSourceControl[] outActiveControls) {
         final SurfaceControl.Builder b = new SurfaceControl.Builder(mSurfaceSession)
                 .setFormat(attrs.format)
                 .setBufferSize(getSurfaceWidth(attrs), getSurfaceHeight(attrs))
@@ -171,11 +171,10 @@
     @Override
     public int addToDisplayAsUser(IWindow window, WindowManager.LayoutParams attrs,
             int viewVisibility, int displayId, int userId, InsetsState requestedVisibility,
-            Rect outFrame, DisplayCutout.ParcelableWrapper outDisplayCutout,
-            InputChannel outInputChannel, InsetsState outInsetsState,
+            Rect outFrame, InputChannel outInputChannel, InsetsState outInsetsState,
             InsetsSourceControl[] outActiveControls) {
         return addToDisplay(window, attrs, viewVisibility, displayId, requestedVisibility,
-                outFrame, outDisplayCutout, outInputChannel, outInsetsState, outActiveControls);
+                outFrame, outInputChannel, outInsetsState, outActiveControls);
     }
 
     @Override
diff --git a/core/java/android/view/autofill/AutofillId.java b/core/java/android/view/autofill/AutofillId.java
index 82d52b6..ae145de 100644
--- a/core/java/android/view/autofill/AutofillId.java
+++ b/core/java/android/view/autofill/AutofillId.java
@@ -143,6 +143,7 @@
      *
      * @hide
      */
+    @TestApi
     public boolean isNonVirtual() {
         return !isVirtualInt() && !isVirtualLong();
     }
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 364ae818..794181e 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -32,6 +32,9 @@
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
 import android.annotation.TestApi;
+import android.app.assist.AssistStructure.ViewNode;
+import android.app.assist.AssistStructure.ViewNodeBuilder;
+import android.app.assist.AssistStructure.ViewNodeParcelable;
 import android.content.AutofillOptions;
 import android.content.ClipData;
 import android.content.ComponentName;
@@ -64,6 +67,8 @@
 import android.view.ContentInfo;
 import android.view.KeyEvent;
 import android.view.View;
+import android.view.ViewRootImpl;
+import android.view.WindowManager;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.AccessibilityNodeInfo;
@@ -3581,6 +3586,35 @@
             mAfm = new WeakReference<>(autofillManager);
         }
 
+        @Nullable
+        @Override
+        public ViewNodeParcelable getViewNodeParcelable(@NonNull AutofillId id) {
+            final AutofillManager afm = mAfm.get();
+            if (afm == null) return null;
+
+            final View view = getView(afm, id);
+            if (view == null) {
+                Log.w(TAG, "getViewNodeParcelable(" + id + "): could not find view");
+                return null;
+            }
+            final ViewRootImpl root = view.getViewRootImpl();
+            if (root != null
+                    && (root.getWindowFlags() & WindowManager.LayoutParams.FLAG_SECURE) == 0) {
+                ViewNodeBuilder viewStructure = new ViewNodeBuilder();
+                viewStructure.setAutofillId(view.getAutofillId());
+                view.onProvideAutofillStructure(viewStructure, /* flags= */ 0);
+                // TODO(b/141703532): We don't call View#onProvideAutofillVirtualStructure for
+                //  efficiency reason. But this also means we will return null for virtual views
+                //  for now. We will add a new API to fetch the view node info of the virtual
+                //  child view.
+                ViewNode viewNode = viewStructure.getViewNode();
+                if (viewNode != null && id.equals(viewNode.getAutofillId())) {
+                    return new ViewNodeParcelable(viewNode);
+                }
+            }
+            return null;
+        }
+
         @Override
         public Rect getViewCoordinates(@NonNull AutofillId id) {
             final AutofillManager afm = mAfm.get();
diff --git a/core/java/android/view/autofill/AutofillValue.java b/core/java/android/view/autofill/AutofillValue.java
index e92c30f..f5eef35 100644
--- a/core/java/android/view/autofill/AutofillValue.java
+++ b/core/java/android/view/autofill/AutofillValue.java
@@ -18,7 +18,6 @@
 
 import static android.view.View.AUTOFILL_TYPE_DATE;
 import static android.view.View.AUTOFILL_TYPE_LIST;
-import static android.view.View.AUTOFILL_TYPE_RICH_CONTENT;
 import static android.view.View.AUTOFILL_TYPE_TEXT;
 import static android.view.View.AUTOFILL_TYPE_TOGGLE;
 import static android.view.autofill.Helper.sDebug;
@@ -26,7 +25,6 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
-import android.content.ClipData;
 import android.os.Looper;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -142,28 +140,6 @@
     }
 
     /**
-     * Gets the value to autofill a field that accepts rich content (text, images, etc).
-     *
-     * <p>See {@link View#AUTOFILL_TYPE_RICH_CONTENT} for more info.</p>
-     *
-     * @throws IllegalStateException if the value is not a content value
-     */
-    public @NonNull ClipData getRichContentValue() {
-        Preconditions.checkState(isRichContent(),
-                "value must be a rich content value, not type=%d", mType);
-        return (ClipData) mValue;
-    }
-
-    /**
-     * Checks if this is a rich content value (represented by {@link ClipData}).
-     *
-     * <p>See {@link View#AUTOFILL_TYPE_RICH_CONTENT} for more info.</p>
-     */
-    public boolean isRichContent() {
-        return mType == AUTOFILL_TYPE_RICH_CONTENT;
-    }
-
-    /**
      * Used to define whether a field is empty so it's not sent to service on save.
      *
      * <p>Only applies to some types, like text.
@@ -208,10 +184,6 @@
                 .append(", value=");
         if (isText()) {
             Helper.appendRedacted(string, (CharSequence) mValue);
-        } else if (isRichContent()) {
-            string.append("{");
-            getRichContentValue().getDescription().toShortStringTypesOnly(string);
-            string.append("}");
         } else {
             string.append(mValue);
         }
@@ -244,9 +216,6 @@
             case AUTOFILL_TYPE_DATE:
                 parcel.writeLong((Long) mValue);
                 break;
-            case AUTOFILL_TYPE_RICH_CONTENT:
-                ((ClipData) mValue).writeToParcel(parcel, flags);
-                break;
         }
     }
 
@@ -267,9 +236,6 @@
             case AUTOFILL_TYPE_DATE:
                 mValue = parcel.readLong();
                 break;
-            case AUTOFILL_TYPE_RICH_CONTENT:
-                mValue = ClipData.CREATOR.createFromParcel(parcel);
-                break;
             default:
                 throw new IllegalArgumentException("type=" + mType + " not valid");
         }
@@ -337,15 +303,4 @@
     public static AutofillValue forDate(long value) {
         return new AutofillValue(AUTOFILL_TYPE_DATE, value);
     }
-
-    /**
-     * Creates a new {@link AutofillValue} to autofill a {@link View} that accepts rich content
-     * (text, images, etc).
-     *
-     * <p>See {@link View#AUTOFILL_TYPE_RICH_CONTENT} for more info.
-     */
-    public static @NonNull AutofillValue forRichContent(@NonNull ClipData value) {
-        Objects.requireNonNull(value.getDescription(), "clip description must not be null");
-        return new AutofillValue(AUTOFILL_TYPE_RICH_CONTENT, value);
-    }
 }
diff --git a/core/java/android/view/autofill/IAugmentedAutofillManagerClient.aidl b/core/java/android/view/autofill/IAugmentedAutofillManagerClient.aidl
index 8526c1e..7d08bcf 100644
--- a/core/java/android/view/autofill/IAugmentedAutofillManagerClient.aidl
+++ b/core/java/android/view/autofill/IAugmentedAutofillManagerClient.aidl
@@ -18,6 +18,7 @@
 
 import java.util.List;
 
+import android.app.assist.AssistStructure;
 import android.graphics.Rect;
 import android.view.autofill.AutofillId;
 import android.view.autofill.AutofillValue;
@@ -36,6 +37,11 @@
     Rect getViewCoordinates(in AutofillId id);
 
     /**
+      * Gets the autofill view structure of the input field view.
+      */
+    AssistStructure.ViewNodeParcelable getViewNodeParcelable(in AutofillId id);
+
+    /**
      * Autofills the activity with the contents of the values.
      */
     void autofill(int sessionId, in List<AutofillId> ids, in List<AutofillValue> values,
diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java
index f057c12..415b3a7 100644
--- a/core/java/android/view/inputmethod/BaseInputConnection.java
+++ b/core/java/android/view/inputmethod/BaseInputConnection.java
@@ -163,11 +163,13 @@
     }
 
     /**
-     * Default implementation calls {@link #finishComposingText()}.
+     * Default implementation calls {@link #finishComposingText()} and
+     * {@code setImeTemporarilyConsumesInput(false)}.
      */
     @CallSuper
     public void closeConnection() {
         finishComposingText();
+        setImeTemporarilyConsumesInput(false);
     }
 
     /**
diff --git a/core/java/android/view/inputmethod/EditorInfo.java b/core/java/android/view/inputmethod/EditorInfo.java
index 1cf25a7..2df75f6 100644
--- a/core/java/android/view/inputmethod/EditorInfo.java
+++ b/core/java/android/view/inputmethod/EditorInfo.java
@@ -25,6 +25,7 @@
 import static android.view.inputmethod.EditorInfoProto.TARGET_INPUT_METHOD_USER_ID;
 
 import android.annotation.IntDef;
+import android.annotation.IntRange;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.RequiresPermission;
@@ -36,7 +37,6 @@
 import android.os.Parcelable;
 import android.os.UserHandle;
 import android.text.InputType;
-import android.text.ParcelableSpan;
 import android.text.TextUtils;
 import android.util.Printer;
 import android.util.proto.ProtoOutputStream;
@@ -44,6 +44,7 @@
 import android.view.autofill.AutofillId;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.Preconditions;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -563,8 +564,9 @@
     @VisibleForTesting
     static final int MAX_INITIAL_SELECTION_LENGTH =  MEMORY_EFFICIENT_TEXT_LENGTH / 2;
 
-    @NonNull
-    private InitialSurroundingText mInitialSurroundingText = new InitialSurroundingText();
+    @Nullable
+    private SurroundingText mInitialSurroundingText = null;
+
 
     /**
      * Editors may use this method to provide initial input text to IMEs. As the surrounding text
@@ -607,6 +609,12 @@
     public void setInitialSurroundingSubText(@NonNull CharSequence subText, int subTextStart) {
         Objects.requireNonNull(subText);
 
+        // For privacy protection reason, we don't carry password inputs to IMEs.
+        if (isPasswordInputType(inputType)) {
+            mInitialSurroundingText = null;
+            return;
+        }
+
         // Swap selection start and end if necessary.
         final int subTextSelStart = initialSelStart > initialSelEnd
                 ? initialSelEnd - subTextStart : initialSelStart - subTextStart;
@@ -616,23 +624,17 @@
         final int subTextLength = subText.length();
         // Unknown or invalid selection.
         if (subTextStart < 0 || subTextSelStart < 0 || subTextSelEnd > subTextLength) {
-            mInitialSurroundingText = new InitialSurroundingText();
-            return;
-        }
-
-        // For privacy protection reason, we don't carry password inputs to IMEs.
-        if (isPasswordInputType(inputType)) {
-            mInitialSurroundingText = new InitialSurroundingText();
+            mInitialSurroundingText = null;
             return;
         }
 
         if (subTextLength <= MEMORY_EFFICIENT_TEXT_LENGTH) {
-            mInitialSurroundingText = new InitialSurroundingText(subText, subTextSelStart,
-                    subTextSelEnd);
+            mInitialSurroundingText = new SurroundingText(subText, subTextSelStart,
+                    subTextSelEnd, subTextStart);
             return;
         }
 
-        trimLongSurroundingText(subText, subTextSelStart, subTextSelEnd);
+        trimLongSurroundingText(subText, subTextSelStart, subTextSelEnd, subTextStart);
     }
 
     /**
@@ -651,8 +653,10 @@
      * @param subText The long text that needs to be trimmed.
      * @param selStart The text offset of the start of the selection.
      * @param selEnd The text offset of the end of the selection
+     * @param subTextStart The position that the input text got trimmed.
      */
-    private void trimLongSurroundingText(CharSequence subText, int selStart, int selEnd) {
+    private void trimLongSurroundingText(CharSequence subText, int selStart, int selEnd,
+            int subTextStart) {
         final int sourceSelLength = selEnd - selStart;
         // When the selected text is too long, drop it.
         final int newSelLength = (sourceSelLength > MAX_INITIAL_SELECTION_LENGTH)
@@ -702,10 +706,13 @@
         // obj.
         newBeforeCursorHead = 0;
         final int newSelHead = newBeforeCursorHead + newBeforeCursorLength;
-        mInitialSurroundingText = new InitialSurroundingText(
-                newInitialSurroundingText, newSelHead, newSelHead + newSelLength);
+        final int newOffset = subTextStart + selStart - newSelHead;
+        mInitialSurroundingText = new SurroundingText(
+                newInitialSurroundingText, newSelHead, newSelHead + newSelLength,
+                newOffset);
     }
 
+
     /**
      * Get <var>length</var> characters of text before the current cursor position. May be
      * {@code null} when the protocol is not supported.
@@ -720,7 +727,17 @@
      */
     @Nullable
     public CharSequence getInitialTextBeforeCursor(int length, int flags) {
-        return mInitialSurroundingText.getInitialTextBeforeCursor(length, flags);
+        if (mInitialSurroundingText == null) {
+            return null;
+        }
+
+        int selStart = Math.min(mInitialSurroundingText.getSelectionStart(),
+                mInitialSurroundingText.getSelectionEnd());
+        int n = Math.min(length, selStart);
+        return ((flags & InputConnection.GET_TEXT_WITH_STYLES) != 0)
+                ? mInitialSurroundingText.getText().subSequence(selStart - n, selStart)
+                : TextUtils.substring(mInitialSurroundingText.getText(), selStart - n,
+                        selStart);
     }
 
     /**
@@ -735,6 +752,10 @@
      */
     @Nullable
     public CharSequence getInitialSelectedText(int flags) {
+        if (mInitialSurroundingText == null) {
+            return null;
+        }
+
         // Swap selection start and end if necessary.
         final int correctedTextSelStart = initialSelStart > initialSelEnd
                 ? initialSelEnd : initialSelStart;
@@ -742,11 +763,21 @@
                 ? initialSelStart : initialSelEnd;
 
         final int sourceSelLength = correctedTextSelEnd - correctedTextSelStart;
-        if (initialSelStart < 0 || initialSelEnd < 0
-                || mInitialSurroundingText.getSelectionLength() != sourceSelLength) {
+        int selStart = mInitialSurroundingText.getSelectionStart();
+        int selEnd = mInitialSurroundingText.getSelectionEnd();
+        if (selStart > selEnd) {
+            int tmp = selStart;
+            selStart = selEnd;
+            selEnd = tmp;
+        }
+        final int selLength = selEnd - selStart;
+        if (initialSelStart < 0 || initialSelEnd < 0 || selLength != sourceSelLength) {
             return null;
         }
-        return mInitialSurroundingText.getInitialSelectedText(flags);
+
+        return ((flags & InputConnection.GET_TEXT_WITH_STYLES) != 0)
+                ? mInitialSurroundingText.getText().subSequence(selStart, selEnd)
+                : TextUtils.substring(mInitialSurroundingText.getText(), selStart, selEnd);
     }
 
     /**
@@ -763,7 +794,79 @@
      */
     @Nullable
     public CharSequence getInitialTextAfterCursor(int length, int flags) {
-        return mInitialSurroundingText.getInitialTextAfterCursor(length, flags);
+        if (mInitialSurroundingText == null) {
+            return null;
+        }
+
+        int surroundingTextLength = mInitialSurroundingText.getText().length();
+        int selEnd = Math.max(mInitialSurroundingText.getSelectionStart(),
+                mInitialSurroundingText.getSelectionEnd());
+        int n = Math.min(length, surroundingTextLength - selEnd);
+        return ((flags & InputConnection.GET_TEXT_WITH_STYLES) != 0)
+                ? mInitialSurroundingText.getText().subSequence(selEnd, selEnd + n)
+                : TextUtils.substring(mInitialSurroundingText.getText(), selEnd, selEnd + n);
+    }
+
+    /**
+     * Gets the surrounding text around the current cursor, with <var>beforeLength</var> characters
+     * of text before the cursor (start of the selection), <var>afterLength</var> characters of text
+     * after the cursor (end of the selection), and all of the selected text.
+     *
+     * <p>The initial surrounding text for return could be trimmed if oversize. Fundamental trimming
+     * rules are:</p>
+     * <ul>
+     *     <li>The text before the cursor is the most important information to IMEs.</li>
+     *     <li>The text after the cursor is the second important information to IMEs.</li>
+     *     <li>The selected text is the least important information but it shall NEVER be truncated.
+     *     When it is too long, just drop it.</li>
+     * </ul>
+     *
+     * <p>For example, the subText can be viewed as TextBeforeCursor + Selection + TextAfterCursor.
+     * The result could be:</p>
+     * <ol>
+     *     <li>(maybeTrimmedAtHead)TextBeforeCursor + Selection
+     *     + TextAfterCursor(maybeTrimmedAtTail)</li>
+     *     <li>(maybeTrimmedAtHead)TextBeforeCursor + TextAfterCursor(maybeTrimmedAtTail)</li>
+     * </ol>
+     *
+     * @param beforeLength The expected length of the text before the cursor.
+     * @param afterLength The expected length of the text after the cursor.
+     * @param flags Supplies additional options controlling how the text is returned. May be either
+     * {@code 0} or {@link InputConnection#GET_TEXT_WITH_STYLES}.
+     * @return an {@link android.view.inputmethod.SurroundingText} object describing the surrounding
+     * text and state of selection, or  {@code null} if the editor or system could not support this
+     * protocol.
+     * @throws IllegalArgumentException if {@code beforeLength} or {@code afterLength} is negative.
+     */
+    @Nullable
+    public SurroundingText getInitialSurroundingText(
+            @IntRange(from = 0) int beforeLength, @IntRange(from = 0)  int afterLength,
+            @InputConnection.GetTextType int flags) {
+        Preconditions.checkArgumentNonnegative(beforeLength);
+        Preconditions.checkArgumentNonnegative(afterLength);
+
+        if (mInitialSurroundingText == null) {
+            return null;
+        }
+
+        int length = mInitialSurroundingText.getText().length();
+        int selStart = mInitialSurroundingText.getSelectionStart();
+        int selEnd = mInitialSurroundingText.getSelectionEnd();
+        if (selStart > selEnd) {
+            int tmp = selStart;
+            selStart = selEnd;
+            selEnd = tmp;
+        }
+
+        int before = Math.min(beforeLength, selStart);
+        int after = Math.min(selEnd + afterLength, length);
+        int offset = selStart - before;
+        CharSequence newText = ((flags & InputConnection.GET_TEXT_WITH_STYLES) != 0)
+                ? mInitialSurroundingText.getText().subSequence(offset, after)
+                : TextUtils.substring(mInitialSurroundingText.getText(), offset, after);
+        int newSelEnd = Math.min(selEnd - offset, length);
+        return new SurroundingText(newText, before, newSelEnd,
+                mInitialSurroundingText.getOffset() + offset);
     }
 
     private static boolean isCutOnSurrogate(CharSequence sourceText, int cutPosition,
@@ -893,7 +996,10 @@
         dest.writeInt(fieldId);
         dest.writeString(fieldName);
         dest.writeBundle(extras);
-        mInitialSurroundingText.writeToParcel(dest, flags);
+        dest.writeBoolean(mInitialSurroundingText != null);
+        if (mInitialSurroundingText != null) {
+            mInitialSurroundingText.writeToParcel(dest, flags);
+        }
         if (hintLocales != null) {
             hintLocales.writeToParcel(dest, flags);
         } else {
@@ -925,9 +1031,11 @@
                     res.fieldId = source.readInt();
                     res.fieldName = source.readString();
                     res.extras = source.readBundle();
-                    InitialSurroundingText initialSurroundingText =
-                            InitialSurroundingText.CREATOR.createFromParcel(source);
-                    res.mInitialSurroundingText = initialSurroundingText;
+                    boolean hasInitialSurroundingText = source.readBoolean();
+                    if (hasInitialSurroundingText) {
+                        res.mInitialSurroundingText =
+                                SurroundingText.CREATOR.createFromParcel(source);
+                    }
                     LocaleList hintLocales = LocaleList.CREATOR.createFromParcel(source);
                     res.hintLocales = hintLocales.isEmpty() ? null : hintLocales;
                     res.contentMimeTypes = source.readStringArray();
@@ -943,120 +1051,4 @@
     public int describeContents() {
         return 0;
     }
-
-    static final class InitialSurroundingText implements Parcelable {
-        @Nullable final CharSequence mSurroundingText;
-        final int mSelectionHead;
-        final int mSelectionEnd;
-
-        InitialSurroundingText() {
-            mSurroundingText = null;
-            mSelectionHead = 0;
-            mSelectionEnd = 0;
-        }
-
-        InitialSurroundingText(@Nullable CharSequence surroundingText, int selectionHead,
-                int selectionEnd) {
-            // Copy the original text (without NoCopySpan) in case the original text is updated
-            // later.
-            mSurroundingText = copyWithParcelableSpans(surroundingText);
-            mSelectionHead = selectionHead;
-            mSelectionEnd = selectionEnd;
-        }
-
-        @Nullable
-        private CharSequence getInitialTextBeforeCursor(int n, int flags) {
-            if (mSurroundingText == null) {
-                return null;
-            }
-
-            final int length = Math.min(n, mSelectionHead);
-            return ((flags & InputConnection.GET_TEXT_WITH_STYLES) != 0)
-                    ? mSurroundingText.subSequence(mSelectionHead - length, mSelectionHead)
-                    : TextUtils.substring(mSurroundingText, mSelectionHead - length,
-                            mSelectionHead);
-        }
-
-        @Nullable
-        private CharSequence getInitialSelectedText(int flags) {
-            if (mSurroundingText == null) {
-                return null;
-            }
-
-            return ((flags & InputConnection.GET_TEXT_WITH_STYLES) != 0)
-                    ? mSurroundingText.subSequence(mSelectionHead, mSelectionEnd)
-                    : TextUtils.substring(mSurroundingText, mSelectionHead, mSelectionEnd);
-        }
-
-        @Nullable
-        private CharSequence getInitialTextAfterCursor(int n, int flags) {
-            if (mSurroundingText == null) {
-                return null;
-            }
-
-            final int length = Math.min(n, mSurroundingText.length() - mSelectionEnd);
-            return ((flags & InputConnection.GET_TEXT_WITH_STYLES) != 0)
-                    ? mSurroundingText.subSequence(mSelectionEnd, mSelectionEnd + length)
-                    : TextUtils.substring(mSurroundingText, mSelectionEnd, mSelectionEnd + length);
-        }
-
-        private int getSelectionLength() {
-            return mSelectionEnd - mSelectionHead;
-        }
-
-        @Override
-        public int describeContents() {
-            return 0;
-        }
-
-        @Override
-        public void writeToParcel(Parcel dest, int flags) {
-            TextUtils.writeToParcel(mSurroundingText, dest, flags);
-            dest.writeInt(mSelectionHead);
-            dest.writeInt(mSelectionEnd);
-        }
-
-        public static final @android.annotation.NonNull Parcelable.Creator<InitialSurroundingText>
-                CREATOR = new Parcelable.Creator<InitialSurroundingText>() {
-                    @Override
-                    public InitialSurroundingText createFromParcel(Parcel source) {
-                        final CharSequence initialText =
-                                TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
-                        final int selectionHead = source.readInt();
-                        final int selectionEnd = source.readInt();
-
-                        return new InitialSurroundingText(initialText, selectionHead, selectionEnd);
-                    }
-
-                    @Override
-                    public InitialSurroundingText[] newArray(int size) {
-                        return new InitialSurroundingText[size];
-                    }
-                };
-
-        /**
-         * Create a copy of the given {@link CharSequence} object, with completely copy
-         * {@link ParcelableSpan} instances.
-         *
-         * @param source the original {@link CharSequence} to be copied.
-         * @return the copied {@link CharSequence}. {@code null} if {@code source} is {@code null}.
-         */
-        @Nullable
-        private static CharSequence copyWithParcelableSpans(@Nullable CharSequence source) {
-            if (source == null) {
-                return null;
-            }
-            Parcel parcel = null;
-            try {
-                parcel = Parcel.obtain();
-                TextUtils.writeToParcel(source, parcel, /* parcelableFlags= */ 0);
-                parcel.setDataPosition(0);
-                return TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
-            } finally {
-                if (parcel != null) {
-                    parcel.recycle();
-                }
-            }
-        }
-    }
-}
+}
\ No newline at end of file
diff --git a/core/java/android/view/inputmethod/InputConnection.java b/core/java/android/view/inputmethod/InputConnection.java
index 8c81143..a76d46d1 100644
--- a/core/java/android/view/inputmethod/InputConnection.java
+++ b/core/java/android/view/inputmethod/InputConnection.java
@@ -1002,4 +1002,22 @@
      */
     boolean commitContent(@NonNull InputContentInfo inputContentInfo, int flags,
             @Nullable Bundle opts);
+
+    /**
+     * Called by the input method to indicate that it temporarily consumes all input for itself,
+     * or no longer does so.
+     *
+     * <p>Editors should reflect that they are temporarily not receiving input by hiding the
+     * cursor if {@code imeTemporarilyConsumesInput} is {@code true}, and resume showing the
+     * cursor if it is {@code false}.
+     *
+     * @param imeTemporarilyConsumesInput {@code true} when the IME is temporarily consuming input
+     * and the cursor should be hidden, {@code false} when input to the editor resumes and the
+     * cursor should be shown again.
+     * @return {@code true} on success, {@code false} if the input connection is no longer valid, or
+     * the protocol is not supported.
+     */
+    default boolean setImeTemporarilyConsumesInput(boolean imeTemporarilyConsumesInput) {
+        return false;
+    }
 }
diff --git a/core/java/android/view/inputmethod/InputConnectionWrapper.java b/core/java/android/view/inputmethod/InputConnectionWrapper.java
index ca85348..b29149f 100644
--- a/core/java/android/view/inputmethod/InputConnectionWrapper.java
+++ b/core/java/android/view/inputmethod/InputConnectionWrapper.java
@@ -335,4 +335,13 @@
     public boolean commitContent(InputContentInfo inputContentInfo, int flags, Bundle opts) {
         return mTarget.commitContent(inputContentInfo, flags, opts);
     }
+
+    /**
+     * {@inheritDoc}
+     * @throws NullPointerException if the target is {@code null}.
+     */
+    @Override
+    public boolean setImeTemporarilyConsumesInput(boolean imeTemporarilyConsumesInput) {
+        return mTarget.setImeTemporarilyConsumesInput(imeTemporarilyConsumesInput);
+    }
 }
diff --git a/core/java/android/view/inputmethod/SurroundingText.java b/core/java/android/view/inputmethod/SurroundingText.java
index 94e05a8..c85a18a1 100644
--- a/core/java/android/view/inputmethod/SurroundingText.java
+++ b/core/java/android/view/inputmethod/SurroundingText.java
@@ -18,6 +18,7 @@
 
 import android.annotation.IntRange;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.TextUtils;
@@ -74,7 +75,7 @@
     public SurroundingText(@NonNull final CharSequence text,
             @IntRange(from = 0) int selectionStart, @IntRange(from = 0) int selectionEnd,
             @IntRange(from = -1) int offset) {
-        mText = text;
+        mText = copyWithParcelableSpans(text);
         mSelectionStart = selectionStart;
         mSelectionEnd = selectionEnd;
         mOffset = offset;
@@ -155,4 +156,29 @@
                     return new SurroundingText[size];
                 }
             };
+
+    /**
+     * Create a copy of the given {@link CharSequence} object, with completely copy
+     * {@link ParcelableSpan} instances.
+     *
+     * @param source the original {@link CharSequence} to be copied.
+     * @return the copied {@link CharSequence}. {@code null} if {@code source} is {@code null}.
+     */
+    @Nullable
+    private static CharSequence copyWithParcelableSpans(@Nullable CharSequence source) {
+        if (source == null) {
+            return null;
+        }
+        Parcel parcel = null;
+        try {
+            parcel = Parcel.obtain();
+            TextUtils.writeToParcel(source, parcel, /* parcelableFlags= */ 0);
+            parcel.setDataPosition(0);
+            return TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
+        } finally {
+            if (parcel != null) {
+                parcel.recycle();
+            }
+        }
+    }
 }
diff --git a/core/java/android/webkit/TEST_MAPPING b/core/java/android/webkit/TEST_MAPPING
new file mode 100644
index 0000000..bd25200
--- /dev/null
+++ b/core/java/android/webkit/TEST_MAPPING
@@ -0,0 +1,36 @@
+{
+  "presubmit": [
+    {
+      "name": "CtsWebkitTestCases",
+      "options": [
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        }
+      ]
+    },
+    {
+      "name": "CtsHostsideWebViewTests",
+      "options": [
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        }
+      ]
+    },
+    {
+      "name": "GtsWebViewTestCases",
+      "options": [
+        {
+          "exclude-annotation": "android.test.FlakyTest"
+        }
+      ]
+    },
+    {
+      "name": "GtsWebViewHostTestCases",
+      "options": [
+        {
+          "exclude-annotation": "android.test.FlakyTest"
+        }
+      ]
+    }
+  ]
+}
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index cf0e0d1..5e74381 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -73,7 +73,7 @@
 /**
  * <p>Displays a vertically-scrollable collection of views, where each view is positioned
  * immediatelybelow the previous view in the list.  For a more modern, flexible, and performant
- * approach to displaying lists, use {@link android.support.v7.widget.RecyclerView}.</p>
+ * approach to displaying lists, use {@link androidx.recyclerview.widget.RecyclerView}.</p>
  *
  * <p>To display a list, you can include a list view in your layout XML file:</p>
  *
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 4ba1ca8..4a84851 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -18,6 +18,7 @@
 
 import android.annotation.ColorInt;
 import android.annotation.DimenRes;
+import android.annotation.IdRes;
 import android.annotation.IntDef;
 import android.annotation.LayoutRes;
 import android.annotation.NonNull;
@@ -63,12 +64,15 @@
 import android.util.IntArray;
 import android.util.Log;
 import android.util.Pair;
+import android.util.TypedValue;
+import android.util.TypedValue.ComplexDimensionUnit;
 import android.view.ContextThemeWrapper;
 import android.view.LayoutInflater;
 import android.view.LayoutInflater.Filter;
 import android.view.RemotableViewMethod;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.ViewGroup.MarginLayoutParams;
 import android.view.ViewStub;
 import android.widget.AdapterView.OnItemClickListener;
 
@@ -173,6 +177,48 @@
     private static final int SET_INT_TAG_TAG = 22;
 
     /** @hide **/
+    @IntDef(prefix = "MARGIN_", value = {
+            MARGIN_LEFT,
+            MARGIN_TOP,
+            MARGIN_RIGHT,
+            MARGIN_BOTTOM,
+            MARGIN_START,
+            MARGIN_END
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface MarginType {}
+    /**
+     * The value will apply to the marginLeft.
+     * @hide
+     */
+    public static final int MARGIN_LEFT = 0;
+    /**
+     * The value will apply to the marginTop.
+     * @hide
+     */
+    public static final int MARGIN_TOP = 1;
+    /**
+     * The value will apply to the marginRight.
+     * @hide
+     */
+    public static final int MARGIN_RIGHT = 2;
+    /**
+     * The value will apply to the marginBottom.
+     * @hide
+     */
+    public static final int MARGIN_BOTTOM = 3;
+    /**
+     * The value will apply to the marginStart.
+     * @hide
+     */
+    public static final int MARGIN_START = 4;
+    /**
+     * The value will apply to the marginEnd.
+     * @hide
+     */
+    public static final int MARGIN_END = 5;
+
+    /** @hide **/
     @IntDef(flag = true, value = {
             FLAG_REAPPLY_DISALLOWED,
             FLAG_WIDGET_IS_COLLECTION_CHILD,
@@ -1730,8 +1776,16 @@
 
             final ViewGroup targetVg = (ViewGroup) target.mRoot;
 
-            // Clear all children when nested views omitted
-            target.mChildren = null;
+            if (mViewIdToKeep == REMOVE_ALL_VIEWS_ID) {
+                // Clear all children when there's no excepted view
+                target.mChildren = null;
+            } else {
+                // Remove just the children which don't match the excepted view
+                target.mChildren.removeIf(childTree -> childTree.mRoot.getId() != mViewIdToKeep);
+                if (target.mChildren.isEmpty()) {
+                    target.mChildren = null;
+                }
+            }
             return new RuntimeAction() {
                 @Override
                 public void apply(View root, ViewGroup rootParent, OnClickHandler handler)
@@ -1922,13 +1976,13 @@
      * Helper action to set text size on a TextView in any supported units.
      */
     private class TextViewSizeAction extends Action {
-        public TextViewSizeAction(int viewId, int units, float size) {
+        TextViewSizeAction(@IdRes int viewId, @ComplexDimensionUnit int units, float size) {
             this.viewId = viewId;
             this.units = units;
             this.size = size;
         }
 
-        public TextViewSizeAction(Parcel parcel) {
+        TextViewSizeAction(Parcel parcel) {
             viewId = parcel.readInt();
             units = parcel.readInt();
             size  = parcel.readFloat();
@@ -2004,36 +2058,56 @@
      */
     private static class LayoutParamAction extends Action {
 
-        /** Set marginEnd */
-        public static final int LAYOUT_MARGIN_END_DIMEN = 1;
-        /** Set width */
-        public static final int LAYOUT_WIDTH = 2;
-        public static final int LAYOUT_MARGIN_BOTTOM_DIMEN = 3;
-        public static final int LAYOUT_MARGIN_END = 4;
+        static final int LAYOUT_MARGIN_LEFT = MARGIN_LEFT;
+        static final int LAYOUT_MARGIN_TOP = MARGIN_TOP;
+        static final int LAYOUT_MARGIN_RIGHT = MARGIN_RIGHT;
+        static final int LAYOUT_MARGIN_BOTTOM = MARGIN_BOTTOM;
+        static final int LAYOUT_MARGIN_START = MARGIN_START;
+        static final int LAYOUT_MARGIN_END = MARGIN_END;
+        static final int LAYOUT_WIDTH = 8;
+        static final int LAYOUT_HEIGHT = 9;
 
         final int mProperty;
+        final boolean mIsDimen;
         final int mValue;
 
         /**
          * @param viewId ID of the view alter
          * @param property which layout parameter to alter
          * @param value new value of the layout parameter
+         * @param units the units of the given value
          */
-        public LayoutParamAction(int viewId, int property, int value) {
+        LayoutParamAction(@IdRes int viewId, int property, float value,
+                @ComplexDimensionUnit int units) {
             this.viewId = viewId;
             this.mProperty = property;
-            this.mValue = value;
+            this.mIsDimen = false;
+            this.mValue = TypedValue.createComplexDimension(value, units);
+        }
+
+        /**
+         * @param viewId ID of the view alter
+         * @param property which layout parameter to alter
+         * @param dimen new dimension with the value of the layout parameter
+         */
+        LayoutParamAction(@IdRes int viewId, int property, @DimenRes int dimen) {
+            this.viewId = viewId;
+            this.mProperty = property;
+            this.mIsDimen = true;
+            this.mValue = dimen;
         }
 
         public LayoutParamAction(Parcel parcel) {
             viewId = parcel.readInt();
             mProperty = parcel.readInt();
+            mIsDimen = parcel.readBoolean();
             mValue = parcel.readInt();
         }
 
         public void writeToParcel(Parcel dest, int flags) {
             dest.writeInt(viewId);
             dest.writeInt(mProperty);
+            dest.writeBoolean(mIsDimen);
             dest.writeInt(mValue);
         }
 
@@ -2047,26 +2121,49 @@
             if (layoutParams == null) {
                 return;
             }
-            int value = mValue;
             switch (mProperty) {
-                case LAYOUT_MARGIN_END_DIMEN:
-                    value = resolveDimenPixelOffset(target, mValue);
-                    // fall-through
-                case LAYOUT_MARGIN_END:
-                    if (layoutParams instanceof ViewGroup.MarginLayoutParams) {
-                        ((ViewGroup.MarginLayoutParams) layoutParams).setMarginEnd(value);
+                case LAYOUT_MARGIN_LEFT:
+                    if (layoutParams instanceof MarginLayoutParams) {
+                        ((MarginLayoutParams) layoutParams).leftMargin = getPixelOffset(target);
                         target.setLayoutParams(layoutParams);
                     }
                     break;
-                case LAYOUT_MARGIN_BOTTOM_DIMEN:
-                    if (layoutParams instanceof ViewGroup.MarginLayoutParams) {
-                        int resolved = resolveDimenPixelOffset(target, mValue);
-                        ((ViewGroup.MarginLayoutParams) layoutParams).bottomMargin = resolved;
+                case LAYOUT_MARGIN_TOP:
+                    if (layoutParams instanceof MarginLayoutParams) {
+                        ((MarginLayoutParams) layoutParams).topMargin = getPixelOffset(target);
+                        target.setLayoutParams(layoutParams);
+                    }
+                    break;
+                case LAYOUT_MARGIN_RIGHT:
+                    if (layoutParams instanceof MarginLayoutParams) {
+                        ((MarginLayoutParams) layoutParams).rightMargin = getPixelOffset(target);
+                        target.setLayoutParams(layoutParams);
+                    }
+                    break;
+                case LAYOUT_MARGIN_BOTTOM:
+                    if (layoutParams instanceof MarginLayoutParams) {
+                        ((MarginLayoutParams) layoutParams).bottomMargin = getPixelOffset(target);
+                        target.setLayoutParams(layoutParams);
+                    }
+                    break;
+                case LAYOUT_MARGIN_START:
+                    if (layoutParams instanceof MarginLayoutParams) {
+                        ((MarginLayoutParams) layoutParams).setMarginStart(getPixelOffset(target));
+                        target.setLayoutParams(layoutParams);
+                    }
+                    break;
+                case LAYOUT_MARGIN_END:
+                    if (layoutParams instanceof MarginLayoutParams) {
+                        ((MarginLayoutParams) layoutParams).setMarginEnd(getPixelOffset(target));
                         target.setLayoutParams(layoutParams);
                     }
                     break;
                 case LAYOUT_WIDTH:
-                    layoutParams.width = mValue;
+                    layoutParams.width = getPixelSize(target);
+                    target.setLayoutParams(layoutParams);
+                    break;
+                case LAYOUT_HEIGHT:
+                    layoutParams.height = getPixelSize(target);
                     target.setLayoutParams(layoutParams);
                     break;
                 default:
@@ -2074,11 +2171,26 @@
             }
         }
 
-        private static int resolveDimenPixelOffset(View target, int value) {
-            if (value == 0) {
-                return 0;
+        private int getPixelOffset(View target) {
+            if (mIsDimen) {
+                if (mValue == 0) {
+                    return 0;
+                }
+                return target.getResources().getDimensionPixelOffset(mValue);
             }
-            return target.getContext().getResources().getDimensionPixelOffset(value);
+            return TypedValue.complexToDimensionPixelOffset(mValue,
+                    target.getResources().getDisplayMetrics());
+        }
+
+        private int getPixelSize(View target) {
+            if (mIsDimen) {
+                if (mValue == 0) {
+                    return 0;
+                }
+                return target.getResources().getDimensionPixelSize(mValue);
+            }
+            return TypedValue.complexToDimensionPixelSize(mValue,
+                    target.getResources().getDisplayMetrics());
         }
 
         @Override
@@ -2512,6 +2624,7 @@
      * @param nestedView {@link RemoteViews} that describes the child.
      */
     public void addView(int viewId, RemoteViews nestedView) {
+        // Clear all children when nested views omitted
         addAction(nestedView == null
                 ? new ViewGroupActionRemove(viewId)
                 : new ViewGroupActionAdd(viewId, nestedView));
@@ -3044,57 +3157,94 @@
     }
 
     /**
-     * @hide
-     * Equivalent to calling {@link android.view.ViewGroup.MarginLayoutParams#setMarginEnd(int)}.
+     * Equivalent to calling {@link MarginLayoutParams#setMarginEnd}.
      * Only works if the {@link View#getLayoutParams()} supports margins.
-     * Hidden for now since we don't want to support this for all different layout margins yet.
      *
      * @param viewId The id of the view to change
-     * @param endMarginDimen a dimen resource to read the margin from or 0 to clear the margin.
+     * @param type The margin being set e.g. {@link #MARGIN_END}
+     * @param dimen a dimension resource to apply to the margin, or 0 to clear the margin.
+     * @hide
      */
-    public void setViewLayoutMarginEndDimen(int viewId, @DimenRes int endMarginDimen) {
-        addAction(new LayoutParamAction(viewId, LayoutParamAction.LAYOUT_MARGIN_END_DIMEN,
-                endMarginDimen));
+    public void setViewLayoutMarginDimen(@IdRes int viewId, @MarginType int type,
+            @DimenRes int dimen) {
+        addAction(new LayoutParamAction(viewId, type, dimen));
     }
 
     /**
-     * Equivalent to calling {@link android.view.ViewGroup.MarginLayoutParams#setMarginEnd(int)}.
+     * Equivalent to calling {@link MarginLayoutParams#setMarginEnd}.
      * Only works if the {@link View#getLayoutParams()} supports margins.
-     * Hidden for now since we don't want to support this for all different layout margins yet.
+     *
+     * <p>NOTE: It is recommended to use {@link TypedValue#COMPLEX_UNIT_PX} only for 0.
+     * Setting margins in pixels will behave poorly when the RemoteViews object is used on a
+     * display with a different density.
      *
      * @param viewId The id of the view to change
-     * @param endMargin a value in pixels for the end margin.
+     * @param type The margin being set e.g. {@link #MARGIN_END}
+     * @param value a value for the margin the given units.
+     * @param units The unit type of the value e.g. {@link TypedValue#COMPLEX_UNIT_DIP}
      * @hide
      */
-    public void setViewLayoutMarginEnd(int viewId, @DimenRes int endMargin) {
-        addAction(new LayoutParamAction(viewId, LayoutParamAction.LAYOUT_MARGIN_END,
-                endMargin));
+    public void setViewLayoutMargin(@IdRes int viewId, @MarginType int type, float value,
+            @ComplexDimensionUnit int units) {
+        addAction(new LayoutParamAction(viewId, type, value, units));
     }
 
     /**
-     * Equivalent to setting {@link android.view.ViewGroup.MarginLayoutParams#bottomMargin}.
+     * Equivalent to setting {@link android.view.ViewGroup.LayoutParams#width} except that you may
+     * provide the value in any dimension units.
      *
-     * @param bottomMarginDimen a dimen resource to read the margin from or 0 to clear the margin.
+     * <p>NOTE: It is recommended to use {@link TypedValue#COMPLEX_UNIT_PX} only for 0,
+     * {@link ViewGroup.LayoutParams#WRAP_CONTENT}, or {@link ViewGroup.LayoutParams#MATCH_PARENT}.
+     * Setting actual sizes in pixels will behave poorly when the RemoteViews object is used on a
+     * display with a different density.
+     *
+     * @param width Width of the view in the given units
+     * @param units The unit type of the value e.g. {@link TypedValue#COMPLEX_UNIT_DIP}
      * @hide
      */
-    public void setViewLayoutMarginBottomDimen(int viewId, @DimenRes int bottomMarginDimen) {
-        addAction(new LayoutParamAction(viewId, LayoutParamAction.LAYOUT_MARGIN_BOTTOM_DIMEN,
-                bottomMarginDimen));
+    public void setViewLayoutWidth(@IdRes int viewId, float width,
+            @ComplexDimensionUnit int units) {
+        addAction(new LayoutParamAction(viewId, LayoutParamAction.LAYOUT_WIDTH, width, units));
     }
 
     /**
-     * Equivalent to setting {@link android.view.ViewGroup.LayoutParams#width}.
+     * Equivalent to setting {@link android.view.ViewGroup.LayoutParams#width} with
+     * the result of {@link Resources#getDimensionPixelSize(int)}.
      *
-     * @param layoutWidth one of 0, MATCH_PARENT or WRAP_CONTENT. Other sizes are not allowed
-     *                    because they behave poorly when the density changes.
+     * @param widthDimen the dimension resource for the view's width
      * @hide
      */
-    public void setViewLayoutWidth(int viewId, int layoutWidth) {
-        if (layoutWidth != 0 && layoutWidth != ViewGroup.LayoutParams.MATCH_PARENT
-                && layoutWidth != ViewGroup.LayoutParams.WRAP_CONTENT) {
-            throw new IllegalArgumentException("Only supports 0, WRAP_CONTENT and MATCH_PARENT");
-        }
-        mActions.add(new LayoutParamAction(viewId, LayoutParamAction.LAYOUT_WIDTH, layoutWidth));
+    public void setViewLayoutWidthDimen(@IdRes int viewId, @DimenRes int widthDimen) {
+        addAction(new LayoutParamAction(viewId, LayoutParamAction.LAYOUT_WIDTH, widthDimen));
+    }
+
+    /**
+     * Equivalent to setting {@link android.view.ViewGroup.LayoutParams#height} except that you may
+     * provide the value in any dimension units.
+     *
+     * <p>NOTE: It is recommended to use {@link TypedValue#COMPLEX_UNIT_PX} only for 0,
+     * {@link ViewGroup.LayoutParams#WRAP_CONTENT}, or {@link ViewGroup.LayoutParams#MATCH_PARENT}.
+     * Setting actual sizes in pixels will behave poorly when the RemoteViews object is used on a
+     * display with a different density.
+     *
+     * @param height height of the view in the given units
+     * @param units The unit type of the value e.g. {@link TypedValue#COMPLEX_UNIT_DIP}
+     * @hide
+     */
+    public void setViewLayoutHeight(@IdRes int viewId, float height,
+            @ComplexDimensionUnit int units) {
+        addAction(new LayoutParamAction(viewId, LayoutParamAction.LAYOUT_HEIGHT, height, units));
+    }
+
+    /**
+     * Equivalent to setting {@link android.view.ViewGroup.LayoutParams#height} with
+     * the result of {@link Resources#getDimensionPixelSize(int)}.
+     *
+     * @param heightDimen a dimen resource to read the height from.
+     * @hide
+     */
+    public void setViewLayoutHeightDimen(@IdRes int viewId, @DimenRes int heightDimen) {
+        addAction(new LayoutParamAction(viewId, LayoutParamAction.LAYOUT_HEIGHT, heightDimen));
     }
 
     /**
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 977a0e8..1599f2b 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -494,6 +494,13 @@
 
     private TextUtils.TruncateAt mEllipsize;
 
+    // A flag to indicate the cursor was hidden by IME.
+    private boolean mImeTemporarilyConsumesInput;
+
+    // Whether cursor is visible without regard to {@link mImeTemporarilyConsumesInput}.
+    // {code true} is the default value.
+    private boolean mCursorVisibleFromAttr = true;
+
     static class Drawables {
         static final int LEFT = 0;
         static final int TOP = 1;
@@ -10496,7 +10503,9 @@
 
     /**
      * Set whether the cursor is visible. The default is true. Note that this property only
-     * makes sense for editable TextView.
+     * makes sense for editable TextView. If IME is temporarily consuming the input, the cursor will
+     * be always invisible, visibility will be updated as the last state when IME does not consume
+     * the input anymore.
      *
      * @see #isCursorVisible()
      *
@@ -10504,6 +10513,25 @@
      */
     @android.view.RemotableViewMethod
     public void setCursorVisible(boolean visible) {
+        mCursorVisibleFromAttr = visible;
+        updateCursorVisibleInternal();
+    }
+
+    /**
+     * Sets the IME is temporarily consuming the input and make the cursor invisible if
+     * {@code imeTemporarilyConsumesInput} is {@code true}. Otherwise, make the cursor visible.
+     *
+     * @param imeTemporarilyConsumesInput {@code true} if IME is temporarily consuming the input
+     *
+     * @hide
+     */
+    public void setImeTemporarilyConsumesInput(boolean imeTemporarilyConsumesInput) {
+        mImeTemporarilyConsumesInput = imeTemporarilyConsumesInput;
+        updateCursorVisibleInternal();
+    }
+
+    private void updateCursorVisibleInternal()  {
+        boolean visible = mCursorVisibleFromAttr && !mImeTemporarilyConsumesInput;
         if (visible && mEditor == null) return; // visible is the default value with no edit data
         createEditorIfNeeded();
         if (mEditor.mCursorVisible != visible) {
@@ -10518,7 +10546,10 @@
     }
 
     /**
-     * @return whether or not the cursor is visible (assuming this TextView is editable)
+     * @return whether or not the cursor is visible (assuming this TextView is editable). This
+     * method may return {@code false} when the IME is temporarily consuming the input even if the
+     * {@code mEditor.mCursorVisible} attribute is {@code true} or {@code #setCursorVisible(true)}
+     * is called.
      *
      * @see #setCursorVisible(boolean)
      *
@@ -11862,16 +11893,12 @@
             Log.w(LOG_TAG, "cannot autofill non-editable TextView: " + this);
             return;
         }
-        final ClipData clip;
-        if (value.isRichContent()) {
-            clip = value.getRichContentValue();
-        } else if (value.isText()) {
-            clip = ClipData.newPlainText("", value.getTextValue());
-        } else {
+        if (!value.isText()) {
             Log.w(LOG_TAG, "value of type " + value.describeContents()
                     + " cannot be autofilled into " + this);
             return;
         }
+        final ClipData clip = ClipData.newPlainText("", value.getTextValue());
         final ContentInfo payload = new ContentInfo.Builder(clip, SOURCE_AUTOFILL).build();
         performReceiveContent(payload);
     }
diff --git a/core/java/android/window/ClientWindowFrames.java b/core/java/android/window/ClientWindowFrames.java
index 5d7025b..e22a5eb 100644
--- a/core/java/android/window/ClientWindowFrames.java
+++ b/core/java/android/window/ClientWindowFrames.java
@@ -20,7 +20,6 @@
 import android.graphics.Rect;
 import android.os.Parcel;
 import android.os.Parcelable;
-import android.view.DisplayCutout;
 
 /**
  * The window frame container class used by client side for layout.
@@ -39,28 +38,22 @@
     /** The background area while the window is resizing. */
     public final @NonNull Rect backdropFrame;
 
-    /** The area cut from the display. */
-    public final @NonNull DisplayCutout.ParcelableWrapper displayCutout;
-
     public ClientWindowFrames() {
         frame = new Rect();
         displayFrame = new Rect();
         backdropFrame = new Rect();
-        displayCutout = new DisplayCutout.ParcelableWrapper();
     }
 
     public ClientWindowFrames(ClientWindowFrames other) {
         frame = new Rect(other.frame);
         displayFrame = new Rect(other.displayFrame);
         backdropFrame = new Rect(other.backdropFrame);
-        displayCutout = new DisplayCutout.ParcelableWrapper(other.displayCutout.get());
     }
 
     private ClientWindowFrames(Parcel in) {
         frame = Rect.CREATOR.createFromParcel(in);
         displayFrame = Rect.CREATOR.createFromParcel(in);
         backdropFrame = Rect.CREATOR.createFromParcel(in);
-        displayCutout = DisplayCutout.ParcelableWrapper.CREATOR.createFromParcel(in);
     }
 
     /** Needed for AIDL out parameters. */
@@ -68,7 +61,6 @@
         frame.set(Rect.CREATOR.createFromParcel(in));
         displayFrame.set(Rect.CREATOR.createFromParcel(in));
         backdropFrame.set(Rect.CREATOR.createFromParcel(in));
-        displayCutout.set(DisplayCutout.ParcelableWrapper.CREATOR.createFromParcel(in));
     }
 
     @Override
@@ -76,7 +68,6 @@
         frame.writeToParcel(dest, flags);
         displayFrame.writeToParcel(dest, flags);
         backdropFrame.writeToParcel(dest, flags);
-        displayCutout.writeToParcel(dest, flags);
     }
 
     @Override
@@ -84,8 +75,7 @@
         final StringBuilder sb = new StringBuilder(32);
         return "ClientWindowFrames{frame=" + frame.toShortString(sb)
                 + " display=" + displayFrame.toShortString(sb)
-                + " backdrop=" + backdropFrame.toShortString(sb)
-                + " cutout=" + displayCutout + "}";
+                + " backdrop=" + backdropFrame.toShortString(sb) + "}";
     }
 
     @Override
diff --git a/core/java/android/window/TransitionInfo.java b/core/java/android/window/TransitionInfo.java
index 52ab38f..da291cf 100644
--- a/core/java/android/window/TransitionInfo.java
+++ b/core/java/android/window/TransitionInfo.java
@@ -57,6 +57,32 @@
     })
     public @interface TransitionMode {}
 
+    /** No flags */
+    public static final int FLAG_NONE = 0;
+
+    /** The container shows the wallpaper behind it. */
+    public static final int FLAG_SHOW_WALLPAPER = 1;
+
+    /** The container IS the wallpaper. */
+    public static final int FLAG_IS_WALLPAPER = 1 << 1;
+
+    /** The container is translucent. */
+    public static final int FLAG_TRANSLUCENT = 1 << 2;
+
+    // TODO: remove when starting-window is moved to Task
+    /** The container is the recipient of a transferred starting-window */
+    public static final int FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT = 1 << 3;
+
+    /** @hide */
+    @IntDef(prefix = { "FLAG_" }, value = {
+            FLAG_NONE,
+            FLAG_SHOW_WALLPAPER,
+            FLAG_IS_WALLPAPER,
+            FLAG_TRANSLUCENT,
+            FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT
+    })
+    public @interface ChangeFlags {}
+
     private final @WindowManager.TransitionOldType int mType;
     private final @WindowManager.TransitionFlags int mFlags;
     private final ArrayList<Change> mChanges = new ArrayList<>();
@@ -198,12 +224,33 @@
         }
     }
 
+    /** Converts change flags into a string representation. */
+    @NonNull
+    public static String flagsToString(@ChangeFlags int flags) {
+        if (flags == 0) return "NONE";
+        final StringBuilder sb = new StringBuilder();
+        if ((flags & FLAG_SHOW_WALLPAPER) != 0) {
+            sb.append("SHOW_WALLPAPER");
+        }
+        if ((flags & FLAG_IS_WALLPAPER) != 0) {
+            sb.append("IS_WALLPAPER");
+        }
+        if ((flags & FLAG_TRANSLUCENT) != 0) {
+            sb.append((sb.length() == 0 ? "" : "|") + "TRANSLUCENT");
+        }
+        if ((flags & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) != 0) {
+            sb.append((sb.length() == 0 ? "" : "|") + "STARTING_WINDOW_TRANSFER");
+        }
+        return sb.toString();
+    }
+
     /** Represents the change a WindowContainer undergoes during a transition */
     public static final class Change implements Parcelable {
         private final WindowContainerToken mContainer;
         private WindowContainerToken mParent;
         private final SurfaceControl mLeash;
         private @TransitionMode int mMode = TRANSIT_NONE;
+        private @ChangeFlags int mFlags = FLAG_NONE;
         private final Rect mStartAbsBounds = new Rect();
         private final Rect mEndAbsBounds = new Rect();
         private final Point mEndRelOffset = new Point();
@@ -219,6 +266,7 @@
             mLeash = new SurfaceControl();
             mLeash.readFromParcel(in);
             mMode = in.readInt();
+            mFlags = in.readInt();
             mStartAbsBounds.readFromParcel(in);
             mEndAbsBounds.readFromParcel(in);
             mEndRelOffset.readFromParcel(in);
@@ -234,6 +282,11 @@
             mMode = mode;
         }
 
+        /** Sets the flags for this change */
+        public void setFlags(@ChangeFlags int flags) {
+            mFlags = flags;
+        }
+
         /** Sets the bounds this container occupied before the change in screen space */
         public void setStartAbsBounds(@Nullable Rect rect) {
             mStartAbsBounds.set(rect);
@@ -269,6 +322,11 @@
             return mMode;
         }
 
+        /** @return the flags for this change. */
+        public @ChangeFlags int getFlags() {
+            return mFlags;
+        }
+
         /**
          * @return the bounds of the container before the change. It may be empty if the container
          * is coming into existence.
@@ -308,6 +366,7 @@
             dest.writeTypedObject(mParent, flags);
             mLeash.writeToParcel(dest, flags);
             dest.writeInt(mMode);
+            dest.writeInt(mFlags);
             mStartAbsBounds.writeToParcel(dest, flags);
             mEndAbsBounds.writeToParcel(dest, flags);
             mEndRelOffset.writeToParcel(dest, flags);
@@ -336,8 +395,8 @@
         @Override
         public String toString() {
             return "{" + mContainer + "(" + mParent + ") leash=" + mLeash
-                    + " m=" + modeToString(mMode) + " sb=" + mStartAbsBounds
-                    + " eb=" + mEndAbsBounds + " eo=" + mEndRelOffset + "}";
+                    + " m=" + modeToString(mMode) + " f=" + flagsToString(mFlags) + " sb="
+                    + mStartAbsBounds + " eb=" + mEndAbsBounds + " eo=" + mEndRelOffset + "}";
         }
     }
 }
diff --git a/core/java/com/android/internal/app/ChooserFlags.java b/core/java/com/android/internal/app/ChooserFlags.java
index 3e26679..1a93f1b 100644
--- a/core/java/com/android/internal/app/ChooserFlags.java
+++ b/core/java/com/android/internal/app/ChooserFlags.java
@@ -33,7 +33,7 @@
      */
     public static final boolean USE_SERVICE_TARGETS_FOR_DIRECT_TARGETS =
             DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_SYSTEMUI,
-                SystemUiDeviceConfigFlags.SHARE_USE_SERVICE_TARGETS, true);
+                SystemUiDeviceConfigFlags.SHARE_USE_SERVICE_TARGETS, false);
 
     /**
      * Whether to use {@link AppPredictionManager} to query for direct share targets (as opposed to
diff --git a/core/java/com/android/internal/app/NetInitiatedActivity.java b/core/java/com/android/internal/app/NetInitiatedActivity.java
index 56ec87c..375e503 100644
--- a/core/java/com/android/internal/app/NetInitiatedActivity.java
+++ b/core/java/com/android/internal/app/NetInitiatedActivity.java
@@ -17,18 +17,14 @@
 package com.android.internal.app;
 
 import android.app.AlertDialog;
-import android.compat.annotation.UnsupportedAppUsage;
-import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.DialogInterface;
 import android.content.Intent;
-import android.content.IntentFilter;
 import android.location.LocationManagerInternal;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
 import android.util.Log;
-import android.widget.Toast;
 
 import com.android.internal.R;
 import com.android.internal.location.GpsNetInitiatedHandler;
@@ -43,7 +39,6 @@
     private static final String TAG = "NetInitiatedActivity";
 
     private static final boolean DEBUG = true;
-    private static final boolean VERBOSE = false;
 
     private static final int POSITIVE_BUTTON = AlertDialog.BUTTON_POSITIVE;
     private static final int NEGATIVE_BUTTON = AlertDialog.BUTTON_NEGATIVE;
@@ -55,17 +50,6 @@
     private int default_response = -1;
     private int default_response_timeout = 6;
 
-    /** Used to detect when NI request is received */
-    private BroadcastReceiver mNetInitiatedReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            if (DEBUG) Log.d(TAG, "NetInitiatedReceiver onReceive: " + intent.getAction());
-            if (intent.getAction() == GpsNetInitiatedHandler.ACTION_NI_VERIFY) {
-                handleNIVerify(intent);
-            }
-        }
-    };
-
     private final Handler mHandler = new Handler() {
         public void handleMessage(Message msg) {
             switch (msg.what) {
@@ -109,14 +93,12 @@
     protected void onResume() {
         super.onResume();
         if (DEBUG) Log.d(TAG, "onResume");
-        registerReceiver(mNetInitiatedReceiver, new IntentFilter(GpsNetInitiatedHandler.ACTION_NI_VERIFY));
     }
 
     @Override
     protected void onPause() {
         super.onPause();
         if (DEBUG) Log.d(TAG, "onPause");
-        unregisterReceiver(mNetInitiatedReceiver);
     }
 
     /**
@@ -141,17 +123,4 @@
         LocationManagerInternal lm = LocalServices.getService(LocationManagerInternal.class);
         lm.sendNiResponse(notificationId, response);
     }
-
-    @UnsupportedAppUsage
-    private void handleNIVerify(Intent intent) {
-        int notifId = intent.getIntExtra(GpsNetInitiatedHandler.NI_INTENT_KEY_NOTIF_ID, -1);
-        notificationId = notifId;
-
-        if (DEBUG) Log.d(TAG, "handleNIVerify action: " + intent.getAction());
-    }
-
-    private void showNIError() {
-        Toast.makeText(this, "NI error" /* com.android.internal.R.string.usb_storage_error_message */,
-                Toast.LENGTH_LONG).show();
-    }
 }
diff --git a/core/java/com/android/internal/app/OWNERS b/core/java/com/android/internal/app/OWNERS
index 382b49e..c5a956a 100644
--- a/core/java/com/android/internal/app/OWNERS
+++ b/core/java/com/android/internal/app/OWNERS
@@ -2,3 +2,4 @@
 per-file *Resolver* = file:/packages/SystemUI/OWNERS
 per-file *Chooser* = file:/packages/SystemUI/OWNERS
 per-file SimpleIconFactory.java = file:/packages/SystemUI/OWNERS
+per-file NetInitiatedActivity.java = file:/location/java/android/location/OWNERS
diff --git a/core/java/com/android/internal/compat/OverrideAllowedState.java b/core/java/com/android/internal/compat/OverrideAllowedState.java
index 9a78ad2..c0bbe50 100644
--- a/core/java/com/android/internal/compat/OverrideAllowedState.java
+++ b/core/java/com/android/internal/compat/OverrideAllowedState.java
@@ -33,7 +33,7 @@
             DISABLED_NOT_DEBUGGABLE,
             DISABLED_NON_TARGET_SDK,
             DISABLED_TARGET_SDK_TOO_HIGH,
-            PACKAGE_DOES_NOT_EXIST,
+            DEFERRED_VERIFICATION,
             LOGGING_ONLY_CHANGE
     })
     @Retention(RetentionPolicy.SOURCE)
@@ -57,10 +57,10 @@
      * Change cannot be overridden, due to the app's targetSdk being above the change's targetSdk.
      */
     public static final int DISABLED_TARGET_SDK_TOO_HIGH = 3;
-    /**
-     * Package does not exist.
+     /**
+     * Change override decision is currently being deferred, due to the app not being installed yet.
      */
-    public static final int PACKAGE_DOES_NOT_EXIST = 4;
+    public static final int DEFERRED_VERIFICATION = 4;
     /**
      * Change is marked as logging only, and cannot be toggled.
      */
@@ -106,6 +106,7 @@
             throws SecurityException {
         switch (state) {
             case ALLOWED:
+            case DEFERRED_VERIFICATION:
                 return;
             case DISABLED_NOT_DEBUGGABLE:
                 throw new SecurityException(
@@ -118,11 +119,6 @@
                         "Cannot override %1$d for %2$s because the app's targetSdk (%3$d) is "
                                 + "above the change's targetSdk threshold (%4$d)",
                         changeId, packageName, appTargetSdk, changeIdTargetSdk));
-            case PACKAGE_DOES_NOT_EXIST:
-                throw new SecurityException(String.format(
-                        "Cannot override %1$d for %2$s because the package does not exist, and "
-                                + "the change is targetSdk gated.",
-                        changeId, packageName));
             case LOGGING_ONLY_CHANGE:
                 throw new SecurityException(String.format(
                         "Cannot override %1$d because it is marked as a logging-only change.",
@@ -170,8 +166,8 @@
                 return "DISABLED_NON_TARGET_SDK";
             case DISABLED_TARGET_SDK_TOO_HIGH:
                 return "DISABLED_TARGET_SDK_TOO_HIGH";
-            case PACKAGE_DOES_NOT_EXIST:
-                return "PACKAGE_DOES_NOT_EXIST";
+            case DEFERRED_VERIFICATION:
+                return "DEFERRED_VERIFICATION";
             case LOGGING_ONLY_CHANGE:
                 return "LOGGING_ONLY_CHANGE";
         }
diff --git a/core/java/com/android/internal/infra/AndroidFuture.java b/core/java/com/android/internal/infra/AndroidFuture.java
index 9f15d89..db5d066 100644
--- a/core/java/com/android/internal/infra/AndroidFuture.java
+++ b/core/java/com/android/internal/infra/AndroidFuture.java
@@ -16,23 +16,20 @@
 
 package com.android.internal.infra;
 
-import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
-
 import android.annotation.CallSuper;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.os.Handler;
-import android.os.Message;
+import android.os.Looper;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.RemoteException;
-import android.util.ExceptionUtils;
 import android.util.Log;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.util.Preconditions;
-import com.android.internal.util.function.pooled.PooledLambda;
 
+import java.lang.reflect.Constructor;
 import java.util.concurrent.CancellationException;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.CompletionStage;
@@ -75,14 +72,16 @@
 
     private static final boolean DEBUG = false;
     private static final String LOG_TAG = AndroidFuture.class.getSimpleName();
+    private static final Executor DIRECT_EXECUTOR = Runnable::run;
     private static final StackTraceElement[] EMPTY_STACK_TRACE = new StackTraceElement[0];
+    private static @Nullable Handler sMainHandler;
 
     private final @NonNull Object mLock = new Object();
     @GuardedBy("mLock")
     private @Nullable BiConsumer<? super T, ? super Throwable> mListener;
     @GuardedBy("mLock")
     private @Nullable Executor mListenerExecutor = DIRECT_EXECUTOR;
-    private @NonNull Handler mTimeoutHandler = Handler.getMain();
+    private @NonNull Handler mTimeoutHandler = getMainHandler();
     private final @Nullable IAndroidFuture mRemoteOrigin;
 
     public AndroidFuture() {
@@ -96,7 +95,7 @@
             // Done
             if (in.readBoolean()) {
                 // Failed
-                completeExceptionally(unparcelException(in));
+                completeExceptionally(readThrowable(in));
             } else {
                 // Success
                 complete((T) in.readValue(null));
@@ -108,6 +107,15 @@
         }
     }
 
+    @NonNull
+    private static Handler getMainHandler() {
+        // This isn't thread-safe but we are okay with it.
+        if (sMainHandler == null) {
+            sMainHandler = new Handler(Looper.getMainLooper());
+        }
+        return sMainHandler;
+    }
+
     /**
      * Create a completed future with the given value.
      *
@@ -236,9 +244,7 @@
         if (mListenerExecutor == DIRECT_EXECUTOR) {
             callListener(listener, res, err);
         } else {
-            mListenerExecutor.execute(PooledLambda
-                    .obtainRunnable(AndroidFuture::callListener, listener, res, err)
-                    .recycleOnUse());
+            mListenerExecutor.execute(() -> callListener(listener, res, err));
         }
     }
 
@@ -260,7 +266,8 @@
                 } else {
                     // listener exception-case threw
                     // give up on listener but preserve the original exception when throwing up
-                    throw ExceptionUtils.appendCause(t, err);
+                    t.addSuppressed(err);
+                    throw t;
                 }
             }
         } catch (Throwable t2) {
@@ -272,9 +279,7 @@
     /** @inheritDoc */
     //@Override //TODO uncomment once java 9 APIs are exposed to frameworks
     public AndroidFuture<T> orTimeout(long timeout, @NonNull TimeUnit unit) {
-        Message msg = PooledLambda.obtainMessage(AndroidFuture::triggerTimeout, this);
-        msg.obj = this;
-        mTimeoutHandler.sendMessageDelayed(msg, unit.toMillis(timeout));
+        mTimeoutHandler.postDelayed(this::triggerTimeout, this, unit.toMillis(timeout));
         return this;
     }
 
@@ -507,7 +512,7 @@
                 result = get();
             } catch (Throwable t) {
                 dest.writeBoolean(true);
-                parcelException(dest, unwrapExecutionException(t));
+                writeThrowable(dest, unwrapExecutionException(t));
                 return;
             }
             dest.writeBoolean(false);
@@ -545,45 +550,70 @@
      * Alternative to {@link Parcel#writeException} that stores the stack trace, in a
      * way consistent with the binder IPC exception propagation behavior.
      */
-    private static void parcelException(Parcel p, @Nullable Throwable t) {
-        p.writeBoolean(t == null);
-        if (t == null) {
+    private static void writeThrowable(@NonNull Parcel parcel, @Nullable Throwable throwable) {
+        boolean hasThrowable = throwable != null;
+        parcel.writeBoolean(hasThrowable);
+        if (!hasThrowable) {
             return;
         }
 
-        p.writeInt(Parcel.getExceptionCode(t));
-        p.writeString(t.getClass().getName());
-        p.writeString(t.getMessage());
-        p.writeStackTrace(t);
-        parcelException(p, t.getCause());
+        boolean isFrameworkParcelable = throwable instanceof Parcelable
+                && throwable.getClass().getClassLoader() == Parcelable.class.getClassLoader();
+        parcel.writeBoolean(isFrameworkParcelable);
+        if (isFrameworkParcelable) {
+            parcel.writeParcelable((Parcelable) throwable,
+                    Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
+            return;
+        }
+
+        parcel.writeString(throwable.getClass().getName());
+        parcel.writeString(throwable.getMessage());
+        StackTraceElement[] stackTrace = throwable.getStackTrace();
+        StringBuilder stackTraceBuilder = new StringBuilder();
+        int truncatedStackTraceLength = Math.min(stackTrace != null ? stackTrace.length : 0, 5);
+        for (int i = 0; i < truncatedStackTraceLength; i++) {
+            if (i > 0) {
+                stackTraceBuilder.append('\n');
+            }
+            stackTraceBuilder.append("\tat ").append(stackTrace[i]);
+        }
+        parcel.writeString(stackTraceBuilder.toString());
+        writeThrowable(parcel, throwable.getCause());
     }
 
     /**
-     * @see #parcelException
+     * @see #writeThrowable
      */
-    private static @Nullable Throwable unparcelException(Parcel p) {
-        if (p.readBoolean()) {
+    private static @Nullable Throwable readThrowable(@NonNull Parcel parcel) {
+        final boolean hasThrowable = parcel.readBoolean();
+        if (!hasThrowable) {
             return null;
         }
 
-        int exCode = p.readInt();
-        String cls = p.readString();
-        String msg = p.readString();
-        String stackTrace = p.readInt() > 0 ? p.readString() : "\t<stack trace unavailable>";
-        msg += "\n" + stackTrace;
-
-        Exception ex = p.createExceptionOrNull(exCode, msg);
-        if (ex == null) {
-            ex = new RuntimeException(cls + ": " + msg);
+        boolean isFrameworkParcelable = parcel.readBoolean();
+        if (isFrameworkParcelable) {
+            return parcel.readParcelable(Parcelable.class.getClassLoader());
         }
-        ex.setStackTrace(EMPTY_STACK_TRACE);
 
-        Throwable cause = unparcelException(p);
+        String className = parcel.readString();
+        String message = parcel.readString();
+        String stackTrace = parcel.readString();
+        String messageWithStackTrace = message + '\n' + stackTrace;
+        Throwable throwable;
+        try {
+            Class<?> clazz = Class.forName(className);
+            Constructor<?> constructor = clazz.getConstructor(String.class);
+            throwable = (Throwable) constructor.newInstance(messageWithStackTrace);
+        } catch (Throwable t) {
+            throwable = new RuntimeException(className + ": " + messageWithStackTrace);
+            throwable.addSuppressed(t);
+        }
+        throwable.setStackTrace(EMPTY_STACK_TRACE);
+        Throwable cause = readThrowable(parcel);
         if (cause != null) {
-            ex.initCause(ex);
+            throwable.initCause(cause);
         }
-
-        return ex;
+        return throwable;
     }
 
     @Override
diff --git a/core/java/com/android/internal/infra/ServiceConnector.java b/core/java/com/android/internal/infra/ServiceConnector.java
index 167d128..8986938 100644
--- a/core/java/com/android/internal/infra/ServiceConnector.java
+++ b/core/java/com/android/internal/infra/ServiceConnector.java
@@ -26,14 +26,11 @@
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.IInterface;
+import android.os.Looper;
 import android.os.RemoteException;
 import android.os.UserHandle;
-import android.text.TextUtils;
-import android.util.DebugUtils;
 import android.util.Log;
 
-import com.android.internal.util.function.pooled.PooledLambda;
-
 import java.io.PrintWriter;
 import java.util.ArrayDeque;
 import java.util.ArrayList;
@@ -47,7 +44,6 @@
 import java.util.function.BiConsumer;
 import java.util.function.Function;
 
-
 /**
  * Takes care of managing a {@link ServiceConnection} and auto-disconnecting from the service upon
  * a certain timeout.
@@ -220,6 +216,7 @@
         private final @NonNull Queue<Job<I, ?>> mQueue = this;
         private final @NonNull List<CompletionAwareJob<I, ?>> mUnfinishedJobs = new ArrayList<>();
 
+        private final @NonNull Handler mMainHandler = new Handler(Looper.getMainLooper());
         private final @NonNull ServiceConnection mServiceConnection = this;
         private final @NonNull Runnable mTimeoutDisconnect = this;
 
@@ -250,9 +247,8 @@
          *                          {@link IInterface}.
          *                          Typically this is {@code IMyInterface.Stub::asInterface}
          */
-        public Impl(@NonNull Context context, @NonNull Intent intent,
-                @Context.BindServiceFlags int bindingFlags, @UserIdInt int userId,
-                @Nullable Function<IBinder, I> binderAsInterface) {
+        public Impl(@NonNull Context context, @NonNull Intent intent, int bindingFlags,
+                @UserIdInt int userId, @Nullable Function<IBinder, I> binderAsInterface) {
             mContext = context;
             mIntent = intent;
             mBindingFlags = bindingFlags;
@@ -264,7 +260,7 @@
          * {@link Handler} on which {@link Job}s will be called
          */
         protected Handler getJobHandler() {
-            return Handler.getMain();
+            return mMainHandler;
         }
 
         /**
@@ -391,8 +387,7 @@
 
         private boolean enqueue(@NonNull Job<I, ?> job) {
             cancelTimeout();
-            return getJobHandler().sendMessage(PooledLambda.obtainMessage(
-                    ServiceConnector.Impl::enqueueJobThread, this, job));
+            return getJobHandler().post(() -> enqueueJobThread(job));
         }
 
         void enqueueJobThread(@NonNull Job<I, ?> job) {
@@ -422,7 +417,7 @@
             if (DEBUG) {
                 logTrace();
             }
-            Handler.getMain().removeCallbacks(mTimeoutDisconnect);
+            mMainHandler.removeCallbacks(mTimeoutDisconnect);
         }
 
         void completeExceptionally(@NonNull Job<?, ?> job, @NonNull Throwable ex) {
@@ -486,7 +481,7 @@
             }
             long timeout = getAutoDisconnectTimeoutMs();
             if (timeout > 0) {
-                Handler.getMain().postDelayed(mTimeoutDisconnect, timeout);
+                mMainHandler.postDelayed(mTimeoutDisconnect, timeout);
             } else if (DEBUG) {
                 Log.i(LOG_TAG, "Not scheduling unbind for permanently bound " + this);
             }
@@ -502,7 +497,7 @@
                 logTrace();
             }
             mUnbinding = true;
-            getJobHandler().sendMessage(PooledLambda.obtainMessage(Impl::unbindJobThread, this));
+            getJobHandler().post(this::unbindJobThread);
         }
 
         void unbindJobThread() {
@@ -659,10 +654,7 @@
         }
 
         private void logTrace() {
-            Log.i(LOG_TAG,
-                    TextUtils.join(" -> ",
-                            DebugUtils.callersWithin(ServiceConnector.class, /* offset= */ 1))
-                    + "(" + this + ")");
+            Log.i(LOG_TAG, "See stacktrace", new Throwable());
         }
 
         /**
diff --git a/core/java/com/android/internal/inputmethod/CallbackUtils.java b/core/java/com/android/internal/inputmethod/CallbackUtils.java
index 248feb8..e9e39db 100644
--- a/core/java/com/android/internal/inputmethod/CallbackUtils.java
+++ b/core/java/com/android/internal/inputmethod/CallbackUtils.java
@@ -200,4 +200,29 @@
             callback.onResult(result);
         } catch (RemoteException ignored) { }
     }
+
+    /**
+     * A utility method using given {@link IVoidResultCallback} to callback the result.
+     *
+     * @param callback {@link IVoidResultCallback} to be called back.
+     * @param resultSupplier the supplier from which the result is provided.
+     */
+    public static void onResult(@NonNull IVoidResultCallback callback,
+            @NonNull Supplier<Void> resultSupplier) {
+        Throwable exception = null;
+
+        try {
+            resultSupplier.get();
+        } catch (Throwable throwable) {
+            exception = throwable;
+        }
+
+        try {
+            if (exception != null) {
+                callback.onError(ThrowableHolder.of(exception));
+                return;
+            }
+            callback.onResult();
+        } catch (RemoteException ignored) { }
+    }
 }
diff --git a/core/java/com/android/internal/inputmethod/Completable.java b/core/java/com/android/internal/inputmethod/Completable.java
index 1913fcd..b82ba81 100644
--- a/core/java/com/android/internal/inputmethod/Completable.java
+++ b/core/java/com/android/internal/inputmethod/Completable.java
@@ -286,6 +286,42 @@
     }
 
     /**
+     * Completable object of {@link java.lang.Void}.
+     */
+    public static final class Void extends ValueBase {
+        /**
+         * Notify when this completable object callback.
+         */
+        @AnyThread
+        @Override
+        protected void onComplete() {
+            synchronized (mStateLock) {
+                switch (mState) {
+                    case CompletionState.NOT_COMPLETED:
+                        mState = CompletionState.COMPLETED_WITH_VALUE;
+                        break;
+                    default:
+                        throw new UnsupportedOperationException(
+                                "onComplete() is not allowed on state=" + stateToString(mState));
+                }
+            }
+            super.onComplete();
+        }
+
+        /**
+         * @throws RuntimeException when called while {@link #onError} happened.
+         * @throws UnsupportedOperationException when called while {@link #hasValue()} returns
+         *                                       {@code false}.
+         */
+        @AnyThread
+        public void getValue() {
+            synchronized (mStateLock) {
+                enforceGetValueLocked();
+            }
+        }
+    }
+
+    /**
      * Base class of completable object types.
      *
      * @param <T> type associated with this completable object.
@@ -396,6 +432,13 @@
     }
 
     /**
+     * @return an instance of {@link Completable.Void}.
+     */
+    public static Completable.Void createVoid() {
+        return new Completable.Void();
+    }
+
+    /**
      * Completable object of {@link java.lang.Boolean}.
      */
     public static final class Boolean extends Values<java.lang.Boolean> { }
@@ -465,6 +508,17 @@
     }
 
     /**
+     * Await the result by the {@link Completable.Void}.
+     *
+     * Check the result once {@link ValueBase#onComplete()}
+     */
+    @AnyThread
+    public static void getResult(@NonNull Completable.Void value) {
+        value.await();
+        value.getValue();
+    }
+
+    /**
      * Await the result by the {@link Completable.Int}, and log it if there is no result after
      * given timeout.
      *
diff --git a/core/java/android/app/role/RolePrivileges.aidl b/core/java/com/android/internal/inputmethod/IVoidResultCallback.aidl
similarity index 74%
copy from core/java/android/app/role/RolePrivileges.aidl
copy to core/java/com/android/internal/inputmethod/IVoidResultCallback.aidl
index 1561ad4..0b25a2b 100644
--- a/core/java/android/app/role/RolePrivileges.aidl
+++ b/core/java/com/android/internal/inputmethod/IVoidResultCallback.aidl
@@ -14,7 +14,11 @@
  * limitations under the License.
  */
 
+package com.android.internal.inputmethod;
 
-package android.app.role;
+import com.android.internal.inputmethod.ThrowableHolder;
 
-parcelable RolePrivileges;
+oneway interface IVoidResultCallback {
+    void onResult();
+    void onError(in ThrowableHolder exception);
+}
\ No newline at end of file
diff --git a/core/java/com/android/internal/inputmethod/ResultCallbacks.java b/core/java/com/android/internal/inputmethod/ResultCallbacks.java
index 6ce851b..2a48c1f 100644
--- a/core/java/com/android/internal/inputmethod/ResultCallbacks.java
+++ b/core/java/com/android/internal/inputmethod/ResultCallbacks.java
@@ -352,4 +352,39 @@
             }
         };
     }
+
+    /**
+     * Creates {@link IVoidResultCallback.Stub} that is to set {@link Completable.Void} when
+     * receiving the result.
+     *
+     * @param value {@link Completable.Void} to be set when receiving the result.
+     * @return {@link IVoidResultCallback.Stub} that can be passed as a binder IPC parameter.
+     */
+    @AnyThread
+    public static IVoidResultCallback.Stub of(@NonNull Completable.Void value) {
+        final AtomicReference<WeakReference<Completable.Void>> atomicRef =
+                new AtomicReference<>(new WeakReference<>(value));
+
+        return new IVoidResultCallback.Stub() {
+            @BinderThread
+            @Override
+            public void onResult() {
+                final Completable.Void value = unwrap(atomicRef);
+                if (value == null) {
+                    return;
+                }
+                value.onComplete();
+            }
+
+            @BinderThread
+            @Override
+            public void onError(ThrowableHolder throwableHolder) {
+                final Completable.Void value = unwrap(atomicRef);
+                if (value == null) {
+                    return;
+                }
+                value.onError(throwableHolder);
+            }
+        };
+    }
 }
diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java
index 771a72c..1e2ce28 100644
--- a/core/java/com/android/internal/jank/InteractionJankMonitor.java
+++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java
@@ -463,7 +463,7 @@
         }
 
         public String getName() {
-            return "Cuj<" + getNameOfCuj(mCujType) + ">";
+            return "J<" + getNameOfCuj(mCujType) + ">";
         }
     }
 }
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 0fa0df6..93dff9f 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -8648,6 +8648,15 @@
             }
         }
 
+        @Override
+        public long getScreenOnEnergy() {
+            if (mUidMeasuredEnergyStats == null) {
+                return ENERGY_DATA_UNAVAILABLE;
+            }
+            return mUidMeasuredEnergyStats.getAccumulatedBucketEnergy(
+                    MeasuredEnergyStats.ENERGY_BUCKET_SCREEN_ON);
+        }
+
         void initNetworkActivityLocked() {
             detachIfNotNull(mNetworkByteActivityCounters);
             mNetworkByteActivityCounters = new LongSamplingCounter[NUM_NETWORK_ACTIVITY_TYPES];
diff --git a/core/java/com/android/internal/os/KernelCpuUidTimeReader.java b/core/java/com/android/internal/os/KernelCpuUidTimeReader.java
index f7fad2c..2dd51b4 100644
--- a/core/java/com/android/internal/os/KernelCpuUidTimeReader.java
+++ b/core/java/com/android/internal/os/KernelCpuUidTimeReader.java
@@ -143,6 +143,10 @@
      */
     public void removeUid(int uid) {
         mLastTimes.delete(uid);
+
+        if (mBpfTimesAvailable) {
+            mBpfReader.removeUidsInRange(uid, uid);
+        }
     }
 
     /**
diff --git a/core/java/com/android/internal/util/LatencyTracker.java b/core/java/com/android/internal/util/LatencyTracker.java
index 254c299..1e9801f 100644
--- a/core/java/com/android/internal/util/LatencyTracker.java
+++ b/core/java/com/android/internal/util/LatencyTracker.java
@@ -91,18 +91,6 @@
      */
     public static final int ACTION_START_RECENTS_ANIMATION = 8;
 
-    private static final String[] NAMES = new String[]{
-            "expand panel",
-            "toggle recents",
-            "fingerprint wake-and-unlock",
-            "check credential",
-            "check credential unlocked",
-            "turn on screen",
-            "rotate the screen",
-            "face wake-and-unlock",
-            "start recents-animation",
-    };
-
     private static final int[] STATSD_ACTION = new int[]{
             FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_EXPAND_PANEL,
             FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_TOGGLE_RECENTS,
@@ -185,6 +173,10 @@
         }
     }
 
+    private String getTraceNameOfAcion(int action) {
+        return "L<" + getNameOfAction(action) + ">";
+    }
+
     public static boolean isEnabled(Context ctx) {
         return getInstance(ctx).isEnabled();
     }
@@ -202,7 +194,7 @@
         if (!isEnabled()) {
             return;
         }
-        Trace.asyncTraceBegin(Trace.TRACE_TAG_APP, NAMES[action], 0);
+        Trace.asyncTraceBegin(Trace.TRACE_TAG_APP, getTraceNameOfAcion(action), 0);
         mStartRtc.put(action, SystemClock.elapsedRealtime());
     }
 
@@ -221,7 +213,7 @@
             return;
         }
         mStartRtc.delete(action);
-        Trace.asyncTraceEnd(Trace.TRACE_TAG_APP, NAMES[action], 0);
+        Trace.asyncTraceEnd(Trace.TRACE_TAG_APP, getTraceNameOfAcion(action), 0);
         logAction(action, (int) (endRtc - startRtc));
     }
 
diff --git a/core/java/com/android/internal/view/IInputConnectionWrapper.java b/core/java/com/android/internal/view/IInputConnectionWrapper.java
index 3a7e66c..e7b7bf4 100644
--- a/core/java/com/android/internal/view/IInputConnectionWrapper.java
+++ b/core/java/com/android/internal/view/IInputConnectionWrapper.java
@@ -79,6 +79,7 @@
     private static final int DO_CLOSE_CONNECTION = 150;
     private static final int DO_COMMIT_CONTENT = 160;
     private static final int DO_GET_SURROUNDING_TEXT = 41;
+    private static final int DO_SET_IME_TEMPORARILY_CONSUMES_INPUT = 170;
 
 
     @GuardedBy("mLock")
@@ -266,6 +267,16 @@
         dispatchMessage(mH.obtainMessage(DO_COMMIT_CONTENT, flags, 0 /* unused */, args));
     }
 
+    /**
+     * Dispatches the request for setting ime temporarily consumes input.
+     *
+     * <p>See {@link InputConnection#setImeTemporarilyConsumesInput(boolean)}.
+     */
+    public void setImeTemporarilyConsumesInput(boolean imeTemporarilyConsumesInput) {
+        dispatchMessage(obtainMessageB(DO_SET_IME_TEMPORARILY_CONSUMES_INPUT,
+                imeTemporarilyConsumesInput));
+    }
+
     void dispatchMessage(Message msg) {
         // If we are calling this from the main thread, then we can call
         // right through.  Otherwise, we need to send the message to the
@@ -811,6 +822,22 @@
                 }
                 return;
             }
+            case DO_SET_IME_TEMPORARILY_CONSUMES_INPUT: {
+                Trace.traceBegin(Trace.TRACE_TAG_INPUT,
+                        "InputConnection#setImeTemporarilyConsumesInput");
+                try {
+                    InputConnection ic = getInputConnection();
+                    if (ic == null || !isActive()) {
+                        Log.w(TAG,
+                                "setImeTemporarilyConsumesInput on inactive InputConnection");
+                        return;
+                    }
+                    ic.setImeTemporarilyConsumesInput(msg.arg1 == 1);
+                } finally {
+                    Trace.traceEnd(Trace.TRACE_TAG_INPUT);
+                }
+                return;
+            }
         }
         Log.w(TAG, "Unhandled message code: " + msg.what);
     }
@@ -837,4 +864,8 @@
         args.arg2 = arg2;
         return mH.obtainMessage(what, 0, 0, args);
     }
+
+    Message obtainMessageB(int what, boolean arg1) {
+        return mH.obtainMessage(what, arg1 ? 1 : 0, 0);
+    }
 }
diff --git a/core/java/com/android/internal/view/IInputContext.aidl b/core/java/com/android/internal/view/IInputContext.aidl
index 53cbf961..586404c 100644
--- a/core/java/com/android/internal/view/IInputContext.aidl
+++ b/core/java/com/android/internal/view/IInputContext.aidl
@@ -85,4 +85,6 @@
 
     void getSurroundingText(int beforeLength, int afterLength, int flags,
             ISurroundingTextResultCallback callback);
+
+    void setImeTemporarilyConsumesInput(boolean imeTemporarilyConsumesInput);
 }
diff --git a/core/java/com/android/internal/view/InputConnectionWrapper.java b/core/java/com/android/internal/view/InputConnectionWrapper.java
index af9c012..84c92ca 100644
--- a/core/java/com/android/internal/view/InputConnectionWrapper.java
+++ b/core/java/com/android/internal/view/InputConnectionWrapper.java
@@ -524,6 +524,19 @@
                 value, TAG, "commitContent()", mCancellationGroup, MAX_WAIT_TIME_MILLIS) != 0;
     }
 
+    /**
+     * See {@link InputConnection#setImeTemporarilyConsumesInput(boolean)}.
+     */
+    @AnyThread
+    public boolean setImeTemporarilyConsumesInput(boolean imeTemporarilyConsumesInput) {
+        try {
+            mIInputContext.setImeTemporarilyConsumesInput(imeTemporarilyConsumesInput);
+            return true;
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
     @AnyThread
     private boolean isMethodMissing(@MissingMethodFlags final int methodFlag) {
         return (mMissingMethods & methodFlag) == methodFlag;
diff --git a/core/java/com/android/internal/widget/EditableInputConnection.java b/core/java/com/android/internal/widget/EditableInputConnection.java
index 767ad42..4ccf9ce 100644
--- a/core/java/com/android/internal/widget/EditableInputConnection.java
+++ b/core/java/com/android/internal/widget/EditableInputConnection.java
@@ -245,6 +245,15 @@
     }
 
     @Override
+    public boolean setImeTemporarilyConsumesInput(boolean imeTemporarilyConsumesInput) {
+        if (mTextView == null) {
+            return super.setImeTemporarilyConsumesInput(imeTemporarilyConsumesInput);
+        }
+        mTextView.setImeTemporarilyConsumesInput(imeTemporarilyConsumesInput);
+        return true;
+    }
+
+    @Override
     public void dumpDebug(ProtoOutputStream proto, long fieldId) {
         final long token = proto.start(fieldId);
         CharSequence editableText = mTextView.getText();
diff --git a/core/proto/android/app/OWNERS b/core/proto/android/app/OWNERS
new file mode 100644
index 0000000..296abd1
--- /dev/null
+++ b/core/proto/android/app/OWNERS
@@ -0,0 +1 @@
+per-file location_time_zone_manager.proto = nfuller@google.com, mingaleev@google.com
diff --git a/core/proto/android/app/location_time_zone_manager.proto b/core/proto/android/app/location_time_zone_manager.proto
new file mode 100644
index 0000000..f44d549
--- /dev/null
+++ b/core/proto/android/app/location_time_zone_manager.proto
@@ -0,0 +1,59 @@
+/*
+ * 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.
+ */
+
+syntax = "proto2";
+package android.app.time;
+
+import "frameworks/base/core/proto/android/privacy.proto";
+
+option java_multiple_files = true;
+option java_outer_classname = "LocationTimeZoneManagerProto";
+
+// Represents the state of the LocationTimeZoneManagerService for use in tests.
+message LocationTimeZoneManagerServiceStateProto {
+  option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+  optional GeolocationTimeZoneSuggestionProto last_suggestion = 1;
+  repeated TimeZoneProviderStateProto primary_provider_states = 2;
+  repeated TimeZoneProviderStateProto secondary_provider_states = 3;
+}
+
+// Represents a GeolocationTimeZoneSuggestion that can be / has been passed to the time zone
+// detector.
+message GeolocationTimeZoneSuggestionProto {
+  option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+  repeated string zone_ids = 1;
+  repeated string debug_info = 2;
+}
+
+// The state tracked for a LocationTimeZoneProvider.
+message TimeZoneProviderStateProto {
+  option (android.msg_privacy).dest = DEST_AUTOMATIC;
+
+  optional TimeZoneProviderStateEnum state = 1;
+}
+
+// The state enum for LocationTimeZoneProviders.
+enum TimeZoneProviderStateEnum {
+  TIME_ZONE_PROVIDER_STATE_UNKNOWN = 0;
+  TIME_ZONE_PROVIDER_STATE_INITIALIZING = 1;
+  TIME_ZONE_PROVIDER_STATE_CERTAIN = 2;
+  TIME_ZONE_PROVIDER_STATE_UNCERTAIN = 3;
+  TIME_ZONE_PROVIDER_STATE_DISABLED = 4;
+  TIME_ZONE_PROVIDER_STATE_PERM_FAILED = 5;
+  TIME_ZONE_PROVIDER_STATE_DESTROYED = 6;
+}
diff --git a/core/proto/android/providers/settings/secure.proto b/core/proto/android/providers/settings/secure.proto
index 2ba39cb..391919a 100644
--- a/core/proto/android/providers/settings/secure.proto
+++ b/core/proto/android/providers/settings/secure.proto
@@ -81,6 +81,9 @@
         optional SettingProto accessibility_magnification_capability = 36 [ (android.privacy).dest = DEST_AUTOMATIC ];
         // Settings for accessibility button mode (navigation bar or floating action menu).
         optional SettingProto accessibility_button_mode = 37 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto accessibility_floating_menu_size = 38 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto accessibility_floating_menu_icon_type = 39 [ (android.privacy).dest = DEST_AUTOMATIC ];
+        optional SettingProto accessibility_floating_menu_opacity = 40 [ (android.privacy).dest = DEST_AUTOMATIC ];
     }
     optional Accessibility accessibility = 2;
 
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index c4c007d..610e0e0 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -509,7 +509,7 @@
     optional .android.graphics.RectProto overscan_frame = 7 [deprecated=true];
     optional .android.graphics.RectProto parent_frame = 8;
     optional .android.graphics.RectProto visible_frame = 9 [deprecated=true];
-    optional .android.view.DisplayCutoutProto cutout = 10;
+    optional .android.view.DisplayCutoutProto cutout = 10 [deprecated=true];
     optional .android.graphics.RectProto content_insets = 11 [deprecated=true];
     optional .android.graphics.RectProto overscan_insets = 12 [deprecated=true];
     optional .android.graphics.RectProto visible_insets = 13 [deprecated=true];
diff --git a/core/proto/android/view/insetsstate.proto b/core/proto/android/view/insetsstate.proto
index 9e9933d..1cab982 100644
--- a/core/proto/android/view/insetsstate.proto
+++ b/core/proto/android/view/insetsstate.proto
@@ -16,8 +16,9 @@
 
 syntax = "proto2";
 
-import "frameworks/base/core/proto/android/view/insetssource.proto";
 import "frameworks/base/core/proto/android/graphics/rect.proto";
+import "frameworks/base/core/proto/android/view/displaycutout.proto";
+import "frameworks/base/core/proto/android/view/insetssource.proto";
 
 package android.view;
 
@@ -29,4 +30,5 @@
 message InsetsStateProto {
     repeated InsetsSourceProto sources = 1;
     optional .android.graphics.RectProto display_frame = 2;
-}
\ No newline at end of file
+    optional DisplayCutoutProto display_cutout = 3;
+}
diff --git a/core/proto/android/view/viewrootimpl.proto b/core/proto/android/view/viewrootimpl.proto
index 0abe5e06..181b2bb 100644
--- a/core/proto/android/view/viewrootimpl.proto
+++ b/core/proto/android/view/viewrootimpl.proto
@@ -38,7 +38,7 @@
     optional bool is_drawing = 8;
     optional bool added = 9;
     optional .android.graphics.RectProto win_frame = 10;
-    optional DisplayCutoutProto pending_display_cutout = 11;
+    optional DisplayCutoutProto pending_display_cutout = 11 [deprecated=true];
     optional string last_window_insets = 12;
     optional string soft_input_mode = 13;
     optional int32 scroll_y = 14;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 60e0ae8..8682fea 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1591,13 +1591,31 @@
     <permission android:name="android.permission.INSTALL_LOCATION_PROVIDER"
         android:protectionLevel="signature|privileged" />
 
-    <!-- @SystemApi @hide Allows an application to install a LocationTimeZoneProvider into the
-         LocationTimeZoneProviderManager.
+    <!-- @SystemApi @hide Allows an application to provide location-based time zone suggestions to
+         the system server. This is needed because the system server discovers time zone providers
+         by exposed intent actions and metadata, without it any app could potentially register
+         itself as time zone provider. The system server checks for this permission.
          <p>Not for use by third-party applications.
     -->
-    <permission android:name="android.permission.INSTALL_LOCATION_TIME_ZONE_PROVIDER"
+    <permission android:name="android.permission.INSTALL_LOCATION_TIME_ZONE_PROVIDER_SERVICE"
         android:protectionLevel="signature|privileged" />
 
+    <!-- The system server uses this permission to install a default secondary location time zone
+         provider.
+    -->
+    <uses-permission android:name="android.permission.INSTALL_LOCATION_TIME_ZONE_PROVIDER_SERVICE"/>
+
+    <!-- @SystemApi @hide Allows an application to bind to a android.service.TimeZoneProviderService
+         for the purpose of detecting the device's time zone. This prevents arbitrary clients
+         connecting to the time zone provider service. The system server checks that the provider's
+         intent service explicitly sets this permission via the android:permission attribute of the
+         service.
+         This is only expected to be possessed by the system server outside of tests.
+         <p>Not for use by third-party applications.
+    -->
+    <permission android:name="android.permission.BIND_TIME_ZONE_PROVIDER_SERVICE"
+        android:protectionLevel="signature" />
+
     <!-- @SystemApi @hide Allows HDMI-CEC service to access device and configuration files.
          This should only be used by HDMI-CEC service.
     -->
@@ -5786,6 +5804,7 @@
              data set from the com.android.geotz APEX. -->
         <service android:name="com.android.timezone.geotz.provider.OfflineLocationTimeZoneProviderService"
                  android:enabled="@bool/config_enableSecondaryLocationTimeZoneProvider"
+                 android:permission="android.permission.BIND_TIME_ZONE_PROVIDER_SERVICE"
                  android:exported="false">
             <intent-filter>
                 <action android:name="android.service.timezone.SecondaryLocationTimeZoneProviderService" />
diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml
index 0fea936..8dced4b2 100644
--- a/core/res/res/values-bn/strings.xml
+++ b/core/res/res/values-bn/strings.xml
@@ -2009,14 +2009,10 @@
     <string name="notification_appops_microphone_active" msgid="581333393214739332">"মাইক্রোফোন"</string>
     <string name="notification_appops_overlay_active" msgid="5571732753262836481">"স্ক্রিনে অন্যান্য অ্যাপের উপরে দেখানো হচ্ছে"</string>
     <string name="notification_feedback_indicator" msgid="663476517711323016">"মতামত জানান"</string>
-    <!-- no translation found for notification_feedback_indicator_alerted (6552871804121942099) -->
-    <skip />
-    <!-- no translation found for notification_feedback_indicator_silenced (3799442124723177262) -->
-    <skip />
-    <!-- no translation found for notification_feedback_indicator_promoted (9030204303764698640) -->
-    <skip />
-    <!-- no translation found for notification_feedback_indicator_demoted (8880309924296450875) -->
-    <skip />
+    <string name="notification_feedback_indicator_alerted" msgid="6552871804121942099">"এই বিজ্ঞপ্তির গুরুত্ব বাড়িয়ে ডিফল্ট হিসেবে সেট করা হয়েছে। মতামত জানাতে ট্যাপ করুন।"</string>
+    <string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"এই বিজ্ঞপ্তির গুরুত্ব কমিয়ে মিউট হিসেবে সেট করা হয়েছে। মতামত জানাতে ট্যাপ করুন।"</string>
+    <string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"এই বিজ্ঞপ্তির গুরুত্ব বাড়ানো হয়েছে। মতামত জানাতে ট্যাপ করুন।"</string>
+    <string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"এই বিজ্ঞপ্তির গুরুত্ব কমানো হয়েছে। মতামত জানাতে ট্যাপ করুন।"</string>
     <string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"রুটিন মোডের তথ্য সংক্রান্ত বিজ্ঞপ্তি"</string>
     <string name="dynamic_mode_notification_title" msgid="9205715501274608016">"সাধারণত যখন চার্জ দেন, তার আগে চার্জ শেষ হয়ে যেতে পারে"</string>
     <string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"ডিভাইস বেশিক্ষণ চালু রাখতে ব্যাটারি সেভার চালু করা হয়েছে"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index f23cb1a..366c506 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -2009,14 +2009,10 @@
     <string name="notification_appops_microphone_active" msgid="581333393214739332">"Micrófono"</string>
     <string name="notification_appops_overlay_active" msgid="5571732753262836481">"se muestra sobre otras aplicaciones que haya en la pantalla"</string>
     <string name="notification_feedback_indicator" msgid="663476517711323016">"Enviar comentarios"</string>
-    <!-- no translation found for notification_feedback_indicator_alerted (6552871804121942099) -->
-    <skip />
-    <!-- no translation found for notification_feedback_indicator_silenced (3799442124723177262) -->
-    <skip />
-    <!-- no translation found for notification_feedback_indicator_promoted (9030204303764698640) -->
-    <skip />
-    <!-- no translation found for notification_feedback_indicator_demoted (8880309924296450875) -->
-    <skip />
+    <string name="notification_feedback_indicator_alerted" msgid="6552871804121942099">"La importancia de esta notificación ha aumentado a Predeterminado. Toca para enviar comentarios."</string>
+    <string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"La importancia de esta notificación ha disminuido a Silencio. Toca para enviar comentarios."</string>
+    <string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"Esta notificación aparecerá en una posición más alta. Toca para enviar comentarios."</string>
+    <string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"Esta notificación aparecerá en una posición más baja. Toca para enviar comentarios."</string>
     <string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Notificación sobre el modo rutina"</string>
     <string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Quizás se agote la batería antes de lo habitual"</string>
     <string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Se ha activado el modo Ahorro de batería para aumentar la duración de la batería"</string>
diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml
index 9bc37c1..4ba95bb 100644
--- a/core/res/res/values-eu/strings.xml
+++ b/core/res/res/values-eu/strings.xml
@@ -352,15 +352,15 @@
     <string name="permlab_receiveMms" msgid="4000650116674380275">"jaso testu-mezuak (MMSak)"</string>
     <string name="permdesc_receiveMms" msgid="958102423732219710">"MMS mezuak jasotzeko eta prozesatzeko baimena ematen die aplikazioei. Horrela, aplikazioak gailura bidalitako mezuak kontrola eta ezaba ditzake zuri erakutsi gabe."</string>
     <string name="permlab_bindCellBroadcastService" msgid="586746677002040651">"desbideratu sare mugikor bidezko igorpen-mezuak"</string>
-    <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"Sare mugikor bidezko igorpen-modulura lotzeko baimena ematen dio aplikazioari, sare mugikor bidezko igorpen-mezuak jaso ahala desbideratu ahal izateko. Sare mugikor bidezko igorpen-alertak kokapen batzuetan entregatzen dira larrialdi-egoeren berri emateko. Sare mugikor bidezko larrialdi-igorpenak jasotzean, aplikazio gaiztoek gailuaren errendimenduari edota funtzionamenduari eragin diezaiokete."</string>
+    <string name="permdesc_bindCellBroadcastService" msgid="6540910200973641606">"Sare mugikor bidezko igorpen-modulura lotzeko baimena ematen dio aplikazioari, sare mugikor bidezko igorpen-mezuak jaso ahala desbideratu ahal izateko. Sare mugikor bidezko igorpen-alertak kokapen batzuetan entregatzen dira larrialdi-egoeren berri emateko. Sare mugikor bidezko larrialdi-igorpenak jasotzean, asmo txarreko aplikazioek gailuaren errendimenduari edota funtzionamenduari eragin diezaiokete."</string>
     <string name="permlab_manageOngoingCalls" msgid="281244770664231782">"Kudeatu abian dauden deiak"</string>
     <string name="permdesc_manageOngoingCalls" msgid="7003138133829915265">"Gailuak jasotzen dituen deiei buruzko xehetasunak ikusteko eta dei horiek kontrolatzeko baimena ematen die aplikazioei."</string>
     <string name="permlab_readCellBroadcasts" msgid="5869884450872137693">"irakurri sare mugikor bidezko igorpen-mezuak"</string>
-    <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"Gailuak jasotako sare mugikor bidezko igorpenen mezuak irakurtzeko baimena ematen die aplikazioei. Sare mugikor bidezko igorpen-alertak kokapen batzuetan ematen dira larrialdi-egoeren berri emateko. Aplikazio gaiztoek gailuaren errendimendua edo funtzionamendua oztopa dezakete larrialdi-igorpen horietako bat jasotzen denean."</string>
+    <string name="permdesc_readCellBroadcasts" msgid="672513437331980168">"Gailuak jasotako sare mugikor bidezko igorpenen mezuak irakurtzeko baimena ematen die aplikazioei. Sare mugikor bidezko igorpen-alertak kokapen batzuetan ematen dira larrialdi-egoeren berri emateko. Asmo txarreko aplikazioek gailuaren errendimendua edo funtzionamendua oztopa dezakete larrialdi-igorpen horietako bat jasotzen denean."</string>
     <string name="permlab_subscribedFeedsRead" msgid="217624769238425461">"irakurri harpidetutako jarioak"</string>
     <string name="permdesc_subscribedFeedsRead" msgid="6911349196661811865">"Une horretan sinkronizatutako jarioei buruzko xehetasunak lortzeko baimena ematen die aplikazioei."</string>
     <string name="permlab_sendSms" msgid="7757368721742014252">"bidali eta ikusi SMS mezuak"</string>
-    <string name="permdesc_sendSms" msgid="6757089798435130769">"SMS mezuak bidaltzeko baimena ematen die aplikazioei. Horrela, ustekabeko gastuak eragin daitezke. Aplikazio gaiztoek erabil dezakete zuk berretsi gabeko mezuak bidalita gastuak eragiteko."</string>
+    <string name="permdesc_sendSms" msgid="6757089798435130769">"SMS mezuak bidaltzeko baimena ematen die aplikazioei. Horrela, ustekabeko gastuak eragin daitezke. Asmo txarreko aplikazioek erabil dezakete zuk berretsi gabeko mezuak bidalita gastuak eragiteko."</string>
     <string name="permlab_readSms" msgid="5164176626258800297">"irakurri testu-mezuak (SMSak edo MMSak)"</string>
     <string name="permdesc_readSms" product="tablet" msgid="7912990447198112829">"Aplikazioak tabletan gordetako SMS mezu (testu-mezu) guztiak irakur ditzake."</string>
     <string name="permdesc_readSms" product="tv" msgid="3054753345758011986">"Aplikazioek Android TV gailuan gordetako SMS (testu) mezu guztiak irakur ditzakete."</string>
@@ -392,7 +392,7 @@
     <string name="permlab_getPackageSize" msgid="375391550792886641">"neurtu aplikazioen biltegiratzeko tokia"</string>
     <string name="permdesc_getPackageSize" msgid="742743530909966782">"Bere kodea, datuak eta cache-tamainak eskuratzeko baimena ematen die aplikazioei."</string>
     <string name="permlab_writeSettings" msgid="8057285063719277394">"aldatu sistemaren ezarpenak"</string>
-    <string name="permdesc_writeSettings" msgid="8293047411196067188">"Sistemaren ezarpenen datuak aldatzeko baimena ematen die aplikazioei. Aplikazio gaiztoek sistemaren konfigurazioa hondatzeko erabil dezakete."</string>
+    <string name="permdesc_writeSettings" msgid="8293047411196067188">"Sistemaren ezarpenen datuak aldatzeko baimena ematen die aplikazioei. Asmo txarreko aplikazioek sistemaren konfigurazioa hondatzeko erabil dezakete."</string>
     <string name="permlab_receiveBootCompleted" msgid="6643339400247325379">"exekutatu abiaraztean"</string>
     <string name="permdesc_receiveBootCompleted" product="tablet" msgid="5565659082718177484">"Sistema berrabiarazi bezain laster abiarazteko baimena ematen die aplikazioei. Horrela, agian denbora gehiago beharko du tabletak abiarazteko, eta tabletaren funtzionamendu orokorra mantso daiteke, baimen hori duten aplikazioak beti abian egongo baitira."</string>
     <string name="permdesc_receiveBootCompleted" product="tv" msgid="4900842256047614307">"Sistema abiarazi bezain laster beren burua abiarazteko baimena ematen die aplikazioei. Baliteke denbora gehiago behar izatea Android TV gailua abiarazteko eta aplikazioek gailua orokorrean mantsoago ibilarazteko baimena izatea, beti abian izango baita."</string>
@@ -402,9 +402,9 @@
     <string name="permdesc_broadcastSticky" product="tv" msgid="2338185920171000650">"Igorpen iraunkorrak egiteko baimena ematen die aplikazioei. Igorpena amaitu ondoren ere igortzen jarraitzen dute igorpen iraunkorrek. Gehiegi erabiliz gero, Android TV gailua motel edo ezegonkor ibiliko da, memoria gehiago erabiliko delako."</string>
     <string name="permdesc_broadcastSticky" product="default" msgid="134529339678913453">"Igorpen iraunkorrak emateko baimena ematen die; horiek igorpena amaitu ondoren mantentzen dira. Gehiegi erabiliz gero, telefonoa motel edo ezegonkor ibiliko da, memoria gehiago erabiliko delako."</string>
     <string name="permlab_readContacts" msgid="8776395111787429099">"irakurri kontaktuak"</string>
-    <string name="permdesc_readContacts" product="tablet" msgid="6430093481659992692">"Tabletan gordetako kontaktuei buruzko datuak irakurtzeko baimena ematen dio aplikazioari. Kontaktuak sortu dituzten tabletako kontuak ere atzitu ahalko dituzte aplikazioek. Horrek barnean hartuko ditu instalatutako aplikazioek sortutako kontuak, agian. Baimen horrekin, kontaktuen datuak gorde ditzakete aplikazioek, eta baliteke aplikazio gaiztoek zuk jakin gabe partekatzea datu horiek."</string>
-    <string name="permdesc_readContacts" product="tv" msgid="8400138591135554789">"Android TV gailuan gordetako kontaktuei buruzko datuak irakurtzeko baimena ematen dio aplikazioari. Kontaktuak sortu dituzten Android TV gailuko kontuak ere atzitu ahalko dituzte aplikazioek. Horrek barnean hartuko ditu instalatutako aplikazioek sortutako kontuak, agian. Baimen horrekin, kontaktuen datuak gorde ditzakete aplikazioek, eta baliteke aplikazio gaiztoek zuk jakin gabe partekatzea datu horiek."</string>
-    <string name="permdesc_readContacts" product="default" msgid="4911989776203207644">"Telefonoan gordetako kontaktuei buruzko datuak irakurtzeko baimena ematen dio aplikazioari. Kontaktuak sortu dituzten telefonoko kontuak ere atzitu ahalko dituzte aplikazioek. Horrek barnean hartuko ditu instalatutako aplikazioek sortutako kontuak, agian. Baimen horrekin, kontaktuen datuak gorde ditzakete aplikazioek, eta baliteke aplikazio gaiztoek zuk jakin gabe partekatzea datu horiek."</string>
+    <string name="permdesc_readContacts" product="tablet" msgid="6430093481659992692">"Tabletan gordetako kontaktuei buruzko datuak irakurtzeko baimena ematen dio aplikazioari. Kontaktuak sortu dituzten tabletako kontuak ere atzitu ahalko dituzte aplikazioek. Horrek barnean hartuko ditu instalatutako aplikazioek sortutako kontuak, agian. Baimen horrekin, kontaktuen datuak gorde ditzakete aplikazioek, eta baliteke asmo txarreko aplikazioek zuk jakin gabe partekatzea datu horiek."</string>
+    <string name="permdesc_readContacts" product="tv" msgid="8400138591135554789">"Android TV gailuan gordetako kontaktuei buruzko datuak irakurtzeko baimena ematen dio aplikazioari. Kontaktuak sortu dituzten Android TV gailuko kontuak ere atzitu ahalko dituzte aplikazioek. Horrek barnean hartuko ditu instalatutako aplikazioek sortutako kontuak, agian. Baimen horrekin, kontaktuen datuak gorde ditzakete aplikazioek, eta baliteke asmo txarreko aplikazioek zuk jakin gabe partekatzea datu horiek."</string>
+    <string name="permdesc_readContacts" product="default" msgid="4911989776203207644">"Telefonoan gordetako kontaktuei buruzko datuak irakurtzeko baimena ematen dio aplikazioari. Kontaktuak sortu dituzten telefonoko kontuak ere atzitu ahalko dituzte aplikazioek. Horrek barnean hartuko ditu instalatutako aplikazioek sortutako kontuak, agian. Baimen horrekin, kontaktuen datuak gorde ditzakete aplikazioek, eta baliteke asmo txarreko aplikazioek zuk jakin gabe partekatzea datu horiek."</string>
     <string name="permlab_writeContacts" msgid="8919430536404830430">"aldatu kontaktuak"</string>
     <string name="permdesc_writeContacts" product="tablet" msgid="6422419281427826181">"Tabletan gordetako kontaktuei buruzko datuak aldatzeko baimena ematen dio aplikazioari. Baimen horrekin, aplikazioek kontaktuen datuak ezaba ditzakete."</string>
     <string name="permdesc_writeContacts" product="tv" msgid="6488872735379978935">"Android TV gailuan gordetako kontaktuei buruzko datuak aldatzeko baimena ematen dio aplikazioari. Baimen horrekin, aplikazioek kontaktuen datuak ezaba ditzakete."</string>
@@ -412,9 +412,9 @@
     <string name="permlab_readCallLog" msgid="1739990210293505948">"irakurri deien erregistroa"</string>
     <string name="permdesc_readCallLog" msgid="8964770895425873433">"Aplikazioak deien historia irakur dezake."</string>
     <string name="permlab_writeCallLog" msgid="670292975137658895">"idatzi deien erregistroan"</string>
-    <string name="permdesc_writeCallLog" product="tablet" msgid="2657525794731690397">"Tabletaren deien erregistroa aldatzeko baimena ematen die aplikazioei, sarrerako eta irteerako deiei buruzko datuak barne. Aplikazio gaiztoek deien erregistroa ezabatzeko edo aldatzeko erabil dezakete."</string>
-    <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"Android TV gailuko deien erregistroa aldatzeko baimena ematen die aplikazioei, jasotako eta egindako deiei buruzko datuak barne. Baliteke aplikazio gaiztoek deien erregistroa ezabatzea edo aldatzea."</string>
-    <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"Telefonoaren deien erregistroa aldatzeko baimena ematen die aplikazioei, sarrerako eta irteerako deiei buruzko datuak barne. Aplikazio gaiztoek deien erregistroa ezabatzeko edo aldatzeko erabil dezakete."</string>
+    <string name="permdesc_writeCallLog" product="tablet" msgid="2657525794731690397">"Tabletaren deien erregistroa aldatzeko baimena ematen die aplikazioei, sarrerako eta irteerako deiei buruzko datuak barne. Asmo txarreko aplikazioek deien erregistroa ezabatzeko edo aldatzeko erabil dezakete."</string>
+    <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"Android TV gailuko deien erregistroa aldatzeko baimena ematen die aplikazioei, jasotako eta egindako deiei buruzko datuak barne. Baliteke asmo txarreko aplikazioek deien erregistroa ezabatzea edo aldatzea."</string>
+    <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"Telefonoaren deien erregistroa aldatzeko baimena ematen die aplikazioei, sarrerako eta irteerako deiei buruzko datuak barne. Asmo txarreko aplikazioek deien erregistroa ezabatzeko edo aldatzeko erabil dezakete."</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"Atzitu gorputzaren sentsoreak (adibidez, bihotz-maiztasunarenak)"</string>
     <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"Zure egoera fisikoa kontrolatzen duten sentsoreetako datuak (adibidez, bihotz-maiztasuna) atzitzeko baimena ematen die aplikazioei."</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"irakurri egutegiko gertaerak eta xehetasunak"</string>
@@ -455,7 +455,7 @@
     <string name="permdesc_vibrate" msgid="8733343234582083721">"Bibragailua kontrolatzeko aukera ematen die aplikazioei."</string>
     <string name="permdesc_vibrator_state" msgid="7050024956594170724">"Dardara-egoera atzitzeko baimena ematen dio aplikazioari."</string>
     <string name="permlab_callPhone" msgid="1798582257194643320">"deitu zuzenean telefono-zenbakietara"</string>
-    <string name="permdesc_callPhone" msgid="5439809516131609109">"Telefono-zenbakietara zuk esku hartu gabe deitzeko baimena ematen die aplikazioei. Horrela, ustekabeko gastuak edo deiak eragin daitezke. Aplikazio gaiztoek erabil dezakete zuk berretsi gabeko deiak eginda gastuak eragiteko."</string>
+    <string name="permdesc_callPhone" msgid="5439809516131609109">"Telefono-zenbakietara zuk esku hartu gabe deitzeko baimena ematen die aplikazioei. Horrela, ustekabeko gastuak edo deiak eragin daitezke. Asmo txarreko aplikazioek erabil dezakete zuk berretsi gabeko deiak eginda gastuak eragiteko."</string>
     <string name="permlab_accessImsCallService" msgid="442192920714863782">"atzitu IMS dei-zerbitzua"</string>
     <string name="permdesc_accessImsCallService" msgid="6328551241649687162">"Zuk ezer egin beharrik gabe deiak egiteko IMS zerbitzua erabiltzeko baimena ematen die aplikazioei."</string>
     <string name="permlab_readPhoneState" msgid="8138526903259297969">"irakurri telefonoaren egoera eta identitatea"</string>
@@ -972,7 +972,7 @@
     <string name="permlab_addVoicemail" msgid="4770245808840814471">"gehitu erantzungailua"</string>
     <string name="permdesc_addVoicemail" msgid="5470312139820074324">"Erantzungailuko sarrera-ontzian mezuak gehitzeko baimena ematen die aplikazioei."</string>
     <string name="permlab_writeGeolocationPermissions" msgid="8605631647492879449">"aldatu arakatzailearen geokokapenaren baimenak"</string>
-    <string name="permdesc_writeGeolocationPermissions" msgid="5817346421222227772">"Arakatzailearen geokokapenaren baimenak aldatzeko baimena ematen die aplikazioei. Aplikazio gaiztoek hori erabil dezakete kokapenari buruzko informazioa haiek hautatutako webguneetara bidaltzeko."</string>
+    <string name="permdesc_writeGeolocationPermissions" msgid="5817346421222227772">"Arakatzailearen geokokapenaren baimenak aldatzeko baimena ematen die aplikazioei. Asmo txarreko aplikazioek hori erabil dezakete kokapenari buruzko informazioa haiek hautatutako webguneetara bidaltzeko."</string>
     <string name="save_password_message" msgid="2146409467245462965">"Arakatzaileak pasahitza gogoratzea nahi duzu?"</string>
     <string name="save_password_notnow" msgid="2878327088951240061">"Ez une honetan"</string>
     <string name="save_password_remember" msgid="6490888932657708341">"Gogoratu"</string>
@@ -2009,10 +2009,10 @@
     <string name="notification_appops_microphone_active" msgid="581333393214739332">"Mikrofonoa"</string>
     <string name="notification_appops_overlay_active" msgid="5571732753262836481">"pantailako beste aplikazioen gainean bistaratzen"</string>
     <string name="notification_feedback_indicator" msgid="663476517711323016">"Eman iritzia"</string>
-    <string name="notification_feedback_indicator_alerted" msgid="6552871804121942099">"Lehenetsi gisa ezarri da jakinarazpena. Sakatu hau iritzia emateko."</string>
-    <string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"Isilarazi da jakinarazpena. Sakatu hau iritzia emateko."</string>
-    <string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"Mailaz igo da jakinarazpena. Sakatu hau iritzia emateko."</string>
-    <string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"Mailaz jaitsi da jakinarazpena. Sakatu hau iritzia emateko."</string>
+    <string name="notification_feedback_indicator_alerted" msgid="6552871804121942099">"Lehenetsi gisa ezarri da jakinarazpena. Sakatu hau oharrak bidaltzeko."</string>
+    <string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"Isilarazi da jakinarazpena. Sakatu hau oharrak bidaltzeko."</string>
+    <string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"Mailaz igo da jakinarazpena. Sakatu hau oharrak bidaltzeko."</string>
+    <string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"Mailaz jaitsi da jakinarazpena. Sakatu hau oharrak bidaltzeko."</string>
     <string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Ohitura moduaren informazio-jakinarazpena"</string>
     <string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Baliteke bateria ohi baino lehenago agortzea"</string>
     <string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Bateria-aurrezlea aktibatuta dago bateriaren iraupena luzatzeko"</string>
diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml
index a3c1ec4..967bb92 100644
--- a/core/res/res/values-gu/strings.xml
+++ b/core/res/res/values-gu/strings.xml
@@ -100,7 +100,7 @@
     <string name="peerTtyModeHco" msgid="5626377160840915617">"પીઅરે TTY મોડ HCO ની વિનંતી કરી"</string>
     <string name="peerTtyModeVco" msgid="572208600818270944">"પીઅરે TTY મોડ VCO ની વિનંતી કરી"</string>
     <string name="peerTtyModeOff" msgid="2420380956369226583">"પીઅરે TTY મોડ બંધ કરવાની વિનંતી કરી"</string>
-    <string name="serviceClassVoice" msgid="2065556932043454987">"અવાજ"</string>
+    <string name="serviceClassVoice" msgid="2065556932043454987">"Voice"</string>
     <string name="serviceClassData" msgid="4148080018967300248">"ડેટા"</string>
     <string name="serviceClassFAX" msgid="2561653371698904118">"ફેક્સ"</string>
     <string name="serviceClassSMS" msgid="1547664561704509004">"SMS"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 7fb27f1..0df69d9 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -1166,7 +1166,7 @@
     <string name="clearDefaultHintMsg" msgid="1325866337702524936">"सिस्‍टम सेटिंग और डाउनलोड किए गए ऐप में डिफ़ॉल्‍ट साफ़ करें."</string>
     <string name="chooseActivity" msgid="8563390197659779956">"कोई कार्रवाई चुनें"</string>
     <string name="chooseUsbActivity" msgid="2096269989990986612">"USB डिवाइस के लिए कोई ऐप्स  चुनें"</string>
-    <string name="noApplications" msgid="1186909265235544019">"कोई भी ऐप्स यह कार्यवाही नहीं कर सकता."</string>
+    <string name="noApplications" msgid="1186909265235544019">"कोई भी ऐप्लिकेशन यह कार्रवाई नहीं कर सकता."</string>
     <string name="aerr_application" msgid="4090916809370389109">"<xliff:g id="APPLICATION">%1$s</xliff:g> रुक गया है"</string>
     <string name="aerr_process" msgid="4268018696970966407">"<xliff:g id="PROCESS">%1$s</xliff:g> रुक गई है"</string>
     <string name="aerr_application_repeated" msgid="7804378743218496566">"<xliff:g id="APPLICATION">%1$s</xliff:g> रुक रहा है"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index af282c0..625b3ed 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -1340,7 +1340,7 @@
     <string name="usb_unsupported_audio_accessory_title" msgid="2335775548086533065">"Otkriven je analogni audiododatak"</string>
     <string name="usb_unsupported_audio_accessory_message" msgid="1300168007129796621">"Priključeni uređaj nije kompatibilan s ovim telefonom. Dodirnite da biste saznali više."</string>
     <string name="adb_active_notification_title" msgid="408390247354560331">"Priključen je alat za otklanjanje pogrešaka putem USB-a"</string>
-    <string name="adb_active_notification_message" msgid="5617264033476778211">"Dodirnite da isključite otklanjanje pogrešaka putem USB-a"</string>
+    <string name="adb_active_notification_message" msgid="5617264033476778211">"Dodirnite da isključite otkl. pogrešaka putem USB-a"</string>
     <string name="adb_active_notification_message" product="tv" msgid="6624498401272780855">"Odaberite da biste onemogućili rješavanje programske pogreške na USB-u."</string>
     <string name="adbwifi_active_notification_title" msgid="6147343659168302473">"Bežično otklanjanje pogrešaka povezano"</string>
     <string name="adbwifi_active_notification_message" msgid="930987922852867972">"Dodirnite da biste isključili bežično otklanjanje pogrešaka"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 5672f46..be3e3c75 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -864,7 +864,7 @@
     <string name="lockscreen_transport_prev_description" msgid="2879469521751181478">"הרצועה הקודמת"</string>
     <string name="lockscreen_transport_next_description" msgid="2931509904881099919">"הרצועה הבאה"</string>
     <string name="lockscreen_transport_pause_description" msgid="6705284702135372494">"השהה"</string>
-    <string name="lockscreen_transport_play_description" msgid="106868788691652733">"הפעל"</string>
+    <string name="lockscreen_transport_play_description" msgid="106868788691652733">"הפעלה"</string>
     <string name="lockscreen_transport_stop_description" msgid="1449552232598355348">"הפסק"</string>
     <string name="lockscreen_transport_rew_description" msgid="7680106856221622779">"הרץ אחורה"</string>
     <string name="lockscreen_transport_ffw_description" msgid="4763794746640196772">"הרץ קדימה"</string>
@@ -1852,7 +1852,7 @@
     <string name="battery_saver_description" msgid="6794188153647295212">"‏כדי להאריך את חיי הסוללה, התכונה \'חיסכון בסוללה\':\n\n• מפעילה עיצוב כהה\n• מכבה או מגבילה פעילות ברקע, חלק מהאפקטים החזותיים ותכונות אחרות כמו Ok Google"</string>
     <string name="data_saver_description" msgid="4995164271550590517">"‏כדי לסייע בהפחתת השימוש בנתונים, חוסך הנתונים (Data Saver) מונע מאפליקציות מסוימות שליחה או קבלה של נתונים ברקע. אפליקציה שבה נעשה שימוש כרגע יכולה לגשת לנתונים, אבל בתדירות נמוכה יותר. המשמעות היא, למשל, שתמונות יוצגו רק לאחר שמקישים עליהן."</string>
     <string name="data_saver_enable_title" msgid="7080620065745260137">"להפעיל את חוסך הנתונים?"</string>
-    <string name="data_saver_enable_button" msgid="4399405762586419726">"הפעל"</string>
+    <string name="data_saver_enable_button" msgid="4399405762586419726">"הפעלה"</string>
     <plurals name="zen_mode_duration_minutes_summary" formatted="false" msgid="2877101784123058273">
       <item quantity="two">‏למשך %d דקות (עד <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item>
       <item quantity="many">‏למשך %1$d דקות (עד <xliff:g id="FORMATTEDTIME_1">%2$s</xliff:g>)</item>
@@ -1963,7 +1963,7 @@
     <string name="app_suspended_unsuspend_message" msgid="1665438589450555459">"ביטול ההשהיה של האפליקציה"</string>
     <string name="work_mode_off_title" msgid="5503291976647976560">"להפעיל את פרופיל העבודה?"</string>
     <string name="work_mode_off_message" msgid="8417484421098563803">"אפליקציות העבודה, התראות, נתונים ותכונות נוספות של פרופיל העבודה יופעלו"</string>
-    <string name="work_mode_turn_on" msgid="3662561662475962285">"הפעל"</string>
+    <string name="work_mode_turn_on" msgid="3662561662475962285">"הפעלה"</string>
     <string name="app_blocked_title" msgid="7353262160455028160">"האפליקציה לא זמינה"</string>
     <string name="app_blocked_message" msgid="542972921087873023">"האפליקציה <xliff:g id="APP_NAME">%1$s</xliff:g> לא זמינה בשלב זה."</string>
     <string name="deprecated_target_sdk_message" msgid="5203207875657579953">"‏האפליקציה הזו עוצבה לגרסה ישנה יותר של Android וייתכן שלא תפעל כראוי. ניתן לבדוק אם יש עדכונים או ליצור קשר עם המפתח."</string>
diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml
index 14615fb..b65a316 100644
--- a/core/res/res/values-mr/strings.xml
+++ b/core/res/res/values-mr/strings.xml
@@ -100,7 +100,7 @@
     <string name="peerTtyModeHco" msgid="5626377160840915617">"समवयस्क व्यक्तीने TTY मोड HCO ची विनंती केली"</string>
     <string name="peerTtyModeVco" msgid="572208600818270944">"समवयस्क व्यक्तीने TTY मोड VCO ची विनंती केली"</string>
     <string name="peerTtyModeOff" msgid="2420380956369226583">"समवयस्क व्यक्तीने TTY मोड बंद ची विनंती केली"</string>
-    <string name="serviceClassVoice" msgid="2065556932043454987">"व्हॉइस"</string>
+    <string name="serviceClassVoice" msgid="2065556932043454987">"Voice"</string>
     <string name="serviceClassData" msgid="4148080018967300248">"डेटा"</string>
     <string name="serviceClassFAX" msgid="2561653371698904118">"फॅक्स"</string>
     <string name="serviceClassSMS" msgid="1547664561704509004">"SMS"</string>
@@ -2009,14 +2009,10 @@
     <string name="notification_appops_microphone_active" msgid="581333393214739332">"मायक्रोफोन"</string>
     <string name="notification_appops_overlay_active" msgid="5571732753262836481">"तुमच्‍या स्‍क्रीनवर इतर ॲप्‍सवर डिस्‍प्‍ले करत आहे"</string>
     <string name="notification_feedback_indicator" msgid="663476517711323016">"फीडबॅक द्या"</string>
-    <!-- no translation found for notification_feedback_indicator_alerted (6552871804121942099) -->
-    <skip />
-    <!-- no translation found for notification_feedback_indicator_silenced (3799442124723177262) -->
-    <skip />
-    <!-- no translation found for notification_feedback_indicator_promoted (9030204303764698640) -->
-    <skip />
-    <!-- no translation found for notification_feedback_indicator_demoted (8880309924296450875) -->
-    <skip />
+    <string name="notification_feedback_indicator_alerted" msgid="6552871804121942099">"ही सूचना डीफॉल्ट करण्यात आली आहे. फीडबॅक देण्यासाठी टॅप करा."</string>
+    <string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"ही सूचना सायलंट करण्यात आली आहे. फीडबॅक देण्यासाठी टॅप करा."</string>
+    <string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"हा सूचनेला उच्च रँक करण्यात आले. फीडबॅक देण्यासाठी टॅप करा."</string>
+    <string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"या सूचनेला कमी रँक करण्यात आले. फीडबॅक देण्यासाठी टॅप करा."</string>
     <string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"दिनक्रम मोडची माहिती सूचना"</string>
     <string name="dynamic_mode_notification_title" msgid="9205715501274608016">"चार्जिंगची सामान्य पातळी गाठेपर्यंत कदाचित बॅटरी संपू शकते"</string>
     <string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"बॅटरी लाइफ वाढवण्यासाठी बॅटरी सेव्हर सुरू केला आहे"</string>
diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml
index 90d4d84..e53a519 100644
--- a/core/res/res/values-ne/strings.xml
+++ b/core/res/res/values-ne/strings.xml
@@ -2009,14 +2009,10 @@
     <string name="notification_appops_microphone_active" msgid="581333393214739332">"माइक्रोफोन"</string>
     <string name="notification_appops_overlay_active" msgid="5571732753262836481">"तपाईंको स्क्रिनका अन्य एपहरूमा प्रदर्शन गरिँदै छ"</string>
     <string name="notification_feedback_indicator" msgid="663476517711323016">"प्रतिक्रिया दिनुहोस्"</string>
-    <!-- no translation found for notification_feedback_indicator_alerted (6552871804121942099) -->
-    <skip />
-    <!-- no translation found for notification_feedback_indicator_silenced (3799442124723177262) -->
-    <skip />
-    <!-- no translation found for notification_feedback_indicator_promoted (9030204303764698640) -->
-    <skip />
-    <!-- no translation found for notification_feedback_indicator_demoted (8880309924296450875) -->
-    <skip />
+    <string name="notification_feedback_indicator_alerted" msgid="6552871804121942099">"सिस्टमले स्वतः यस सूचनालाई महत्त्वपूर्ण ठानी यसका लागि पूर्वनिर्धारित मोड सेट गरिदिएको छ। प्रतिक्रिया दिन ट्याप गर्नुहोस्।"</string>
+    <string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"यस सूचनालाई कम महत्त्वपूर्ण ठानी यसका लागि साइलेन्ट मोड सेट गरिएको छ। प्रतिक्रिया दिन ट्याप गर्नुहोस्।"</string>
+    <string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"यस सूचनालाई धेरै महत्त्वपूर्ण सूचनाका रूपमा सेट गरिएको छ। प्रतिक्रिया दिन ट्याप गर्नुहोस्।"</string>
+    <string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"यस सूचनालाई कम महत्त्वपूर्ण सूचनाका रूपमा सेट गरिएको छ। प्रतिक्रिया दिन ट्याप गर्नुहोस्।"</string>
     <string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"दिनचर्या मोडको जानकारीमूलक सूचना"</string>
     <string name="dynamic_mode_notification_title" msgid="9205715501274608016">"प्रायः चार्ज गर्ने समय हुनुभन्दा पहिले नै ब्याट्री सकिन सक्छ"</string>
     <string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"ब्याट्रीको आयु बढाउन ब्याट्री सेभर सक्रिय गरियो"</string>
diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml
index 1f53964..8d5e974 100644
--- a/core/res/res/values-pa/strings.xml
+++ b/core/res/res/values-pa/strings.xml
@@ -2009,14 +2009,10 @@
     <string name="notification_appops_microphone_active" msgid="581333393214739332">"ਮਾਈਕ੍ਰੋਫ਼ੋਨ"</string>
     <string name="notification_appops_overlay_active" msgid="5571732753262836481">"ਤੁਹਾਡੀ ਸਕ੍ਰੀਨ \'ਤੇ ਹੋਰਾਂ ਐਪਾਂ ਉੱਪਰ ਦਿਖਾਇਆ ਜਾ ਰਿਹਾ ਹੈ"</string>
     <string name="notification_feedback_indicator" msgid="663476517711323016">"ਵਿਚਾਰ ਦਿਓ"</string>
-    <!-- no translation found for notification_feedback_indicator_alerted (6552871804121942099) -->
-    <skip />
-    <!-- no translation found for notification_feedback_indicator_silenced (3799442124723177262) -->
-    <skip />
-    <!-- no translation found for notification_feedback_indicator_promoted (9030204303764698640) -->
-    <skip />
-    <!-- no translation found for notification_feedback_indicator_demoted (8880309924296450875) -->
-    <skip />
+    <string name="notification_feedback_indicator_alerted" msgid="6552871804121942099">"ਇਸ ਸੂਚਨਾ ਦਾ ਦਰਜਾ ਵਧਾ ਕੇ ਪੂਰਵ-ਨਿਰਧਾਰਿਤ \'ਤੇ ਸੈੱਟ ਕੀਤਾ ਗਿਆ। ਵਿਚਾਰ ਮੁਹੱਈਆ ਕਰਵਾਉਣ ਲਈ ਟੈਪ ਕਰੋ।"</string>
+    <string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"ਇਸ ਸੂਚਨਾ ਦਾ ਦਰਜਾ ਘਟਾ ਕੇ ਸ਼ਾਂਤ \'ਤੇ ਸੈੱਟ ਕੀਤਾ ਗਿਆ। ਵਿਚਾਰ ਮੁਹੱਈਆ ਕਰਵਾਉਣ ਲਈ ਟੈਪ ਕਰੋ।"</string>
+    <string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"ਇਸ ਸੂਚਨਾ ਦਾ ਦਰਜਾ ਵਧਾ ਦਿੱਤਾ ਗਿਆ। ਵਿਚਾਰ ਮੁਹੱਈਆ ਕਰਵਾਉਣ ਲਈ ਟੈਪ ਕਰੋ।"</string>
+    <string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"ਇਸ ਸੂਚਨਾ ਦਾ ਦਰਜਾ ਘਟਾ ਦਿੱਤਾ ਗਿਆ। ਵਿਚਾਰ ਮੁਹੱਈਆ ਕਰਵਾਉਣ ਲਈ ਟੈਪ ਕਰੋ।"</string>
     <string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"ਨਿਯਮਬੱਧ ਮੋਡ ਦੀ ਜਾਣਕਾਰੀ ਵਾਲੀ ਸੂਚਨਾ"</string>
     <string name="dynamic_mode_notification_title" msgid="9205715501274608016">"ਬੈਟਰੀ ਚਾਰਜ ਕਰਨ ਦੇ ਮਿੱਥੇ ਸਮੇਂ ਤੋਂ ਪਹਿਲਾਂ ਸ਼ਾਇਦ ਬੈਟਰੀ ਖਤਮ ਹੋ ਜਾਵੇ"</string>
     <string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"ਬੈਟਰੀ ਲਾਈਫ਼ ਵਧਾਉਣ ਲਈ ਬੈਟਰੀ ਸੇਵਰ ਚਾਲੂ ਕੀਤਾ ਗਿਆ"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 02d29cb..cfcc2d5 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -1848,8 +1848,8 @@
     <string name="package_updated_device_owner" msgid="7560272363805506941">"Zaktualizowany przez administratora"</string>
     <string name="package_deleted_device_owner" msgid="2292335928930293023">"Usunięty przez administratora"</string>
     <string name="confirm_battery_saver" msgid="5247976246208245754">"OK"</string>
-    <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Aby wydłużyć czas pracy na baterii, funkcja Oszczędzanie baterii:\n\n• włącza tryb ciemny,\n• wyłącza lub ogranicza aktywność w tle, niektóre efekty wizualne oraz inne funkcje, np. „OK Google”.\n\n"<annotation id="url">"Więcej informacji"</annotation></string>
-    <string name="battery_saver_description" msgid="6794188153647295212">"Aby wydłużyć czas pracy na baterii, Oszczędzanie baterii:\n\n• włącza tryb ciemny,\n• wyłącza lub ogranicza aktywność w tle, niektóre efekty wizualne oraz inne funkcje, np. „OK Google”."</string>
+    <string name="battery_saver_description_with_learn_more" msgid="4424488535318105801">"Aby wydłużyć czas pracy na baterii, funkcja Oszczędzanie baterii:\n\n• włącza ciemny motyw,\n• wyłącza lub ogranicza aktywność w tle, niektóre efekty wizualne oraz inne funkcje, np. „OK Google”.\n\n"<annotation id="url">"Więcej informacji"</annotation></string>
+    <string name="battery_saver_description" msgid="6794188153647295212">"Aby wydłużyć czas pracy na baterii, Oszczędzanie baterii:\n\n• włącza ciemny motyw,\n• wyłącza lub ogranicza aktywność w tle, niektóre efekty wizualne oraz inne funkcje, np. „OK Google”."</string>
     <string name="data_saver_description" msgid="4995164271550590517">"Oszczędzanie danych uniemożliwia niektórym aplikacjom wysyłanie i odbieranie danych w tle, zmniejszając w ten sposób ich użycie. Aplikacja, z której w tej chwili korzystasz, może uzyskiwać dostęp do danych, ale rzadziej. Może to powodować, że obrazy będą się wyświetlać dopiero po kliknięciu."</string>
     <string name="data_saver_enable_title" msgid="7080620065745260137">"Włączyć Oszczędzanie danych?"</string>
     <string name="data_saver_enable_button" msgid="4399405762586419726">"Włącz"</string>
diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml
index 4f0407b..218412b 100644
--- a/core/res/res/values-sq/strings.xml
+++ b/core/res/res/values-sq/strings.xml
@@ -1319,8 +1319,8 @@
     <string name="usb_power_notification_message" msgid="7284765627437897702">"Pajisja e lidhur po karikohet. Trokit për opsione të tjera."</string>
     <string name="usb_unsupported_audio_accessory_title" msgid="2335775548086533065">"U zbulua aksesor i audios analoge"</string>
     <string name="usb_unsupported_audio_accessory_message" msgid="1300168007129796621">"Pajisja e bashkuar nuk është e pajtueshme me këtë telefon. Trokit për të mësuar më shumë."</string>
-    <string name="adb_active_notification_title" msgid="408390247354560331">"Korrigjuesi i USB-së është i lidhur"</string>
-    <string name="adb_active_notification_message" msgid="5617264033476778211">"Trokit për të çaktivizuar korrigjimin e USB-së"</string>
+    <string name="adb_active_notification_title" msgid="408390247354560331">"Korrigjimi përmes USB-së është i lidhur"</string>
+    <string name="adb_active_notification_message" msgid="5617264033476778211">"Trokit për të çaktivizuar korrigjimin përmes USB-së"</string>
     <string name="adb_active_notification_message" product="tv" msgid="6624498401272780855">"Përzgjidhe për të çaktivizuar korrigjimin e gabimeve të USB-së"</string>
     <string name="adbwifi_active_notification_title" msgid="6147343659168302473">"Korrigjimi përmes Wi-Fi është lidhur"</string>
     <string name="adbwifi_active_notification_message" msgid="930987922852867972">"Trokit për të çaktivizuar korrigjimin përmes Wi-Fi"</string>
@@ -1931,7 +1931,7 @@
     <string name="app_category_maps" msgid="6395725487922533156">"Harta dhe navigim"</string>
     <string name="app_category_productivity" msgid="1844422703029557883">"Produktivitet"</string>
     <string name="device_storage_monitor_notification_channel" msgid="5164244565844470758">"Hapësira ruajtëse e pajisjes"</string>
-    <string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"Korrigjimi i USB-së"</string>
+    <string name="adb_debugging_notification_channel_tv" msgid="4764046459631031496">"Korrigjimi përmes USB-së"</string>
     <string name="time_picker_hour_label" msgid="4208590187662336864">"orë"</string>
     <string name="time_picker_minute_label" msgid="8307452311269824553">"minutë"</string>
     <string name="time_picker_header_text" msgid="9073802285051516688">"Vendos orën"</string>
diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml
index 1342dbe..ec9c0a7 100644
--- a/core/res/res/values-ta/strings.xml
+++ b/core/res/res/values-ta/strings.xml
@@ -100,7 +100,7 @@
     <string name="peerTtyModeHco" msgid="5626377160840915617">"TTY Mode HCOஐ இணைச் செயல்பாடு கோரியது"</string>
     <string name="peerTtyModeVco" msgid="572208600818270944">"TTY Mode VCOஐ இணைச் செயல்பாடு கோரியது"</string>
     <string name="peerTtyModeOff" msgid="2420380956369226583">"TTY Mode OFFஐ இணைச் செயல்பாடு கோரியது"</string>
-    <string name="serviceClassVoice" msgid="2065556932043454987">"குரல்"</string>
+    <string name="serviceClassVoice" msgid="2065556932043454987">"Voice"</string>
     <string name="serviceClassData" msgid="4148080018967300248">"தரவு"</string>
     <string name="serviceClassFAX" msgid="2561653371698904118">"தொலைநகல்"</string>
     <string name="serviceClassSMS" msgid="1547664561704509004">"SMS"</string>
@@ -270,7 +270,7 @@
     <string name="global_action_lockdown" msgid="2475471405907902963">"பூட்டு"</string>
     <string name="status_bar_notification_info_overflow" msgid="3330152558746563475">"999+"</string>
     <string name="notification_hidden_text" msgid="2835519769868187223">"புதிய அறிவிப்பு"</string>
-    <string name="notification_channel_virtual_keyboard" msgid="6465975799223304567">"விர்ச்சுவல் கீபோர்ட்"</string>
+    <string name="notification_channel_virtual_keyboard" msgid="6465975799223304567">"விர்ச்சுவல் கீபோர்டு"</string>
     <string name="notification_channel_physical_keyboard" msgid="5417306456125988096">"கைமுறை விசைப்பலகை"</string>
     <string name="notification_channel_security" msgid="8516754650348238057">"பாதுகாப்பு"</string>
     <string name="notification_channel_car_mode" msgid="2123919247040988436">"கார் பயன்முறை"</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index abf116c..094b6c8 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -314,7 +314,7 @@
     <string name="permgrouplab_camera" msgid="9090413408963547706">"กล้องถ่ายรูป"</string>
     <string name="permgroupdesc_camera" msgid="7585150538459320326">"ถ่ายภาพและบันทึกวิดีโอ"</string>
     <string name="permgrouplab_calllog" msgid="7926834372073550288">"ประวัติการโทร"</string>
-    <string name="permgroupdesc_calllog" msgid="2026996642917801803">"อ่านและเขียนประวัติการโทรของโทรศัพท์"</string>
+    <string name="permgroupdesc_calllog" msgid="2026996642917801803">"อ่านและเขียนบันทึกการโทรของโทรศัพท์"</string>
     <string name="permgrouplab_phone" msgid="570318944091926620">"โทรศัพท์"</string>
     <string name="permgroupdesc_phone" msgid="270048070781478204">"โทรและจัดการการโทร"</string>
     <string name="permgrouplab_sensors" msgid="9134046949784064495">"เซ็นเซอร์ร่างกาย"</string>
@@ -409,12 +409,12 @@
     <string name="permdesc_writeContacts" product="tablet" msgid="6422419281427826181">"อนุญาตให้แอปแก้ไขข้อมูลเกี่ยวกับรายชื่อติดต่อที่จัดเก็บไว้ในแท็บเล็ต สิทธิ์นี้ทำให้แอปลบข้อมูลรายชื่อติดต่อได้"</string>
     <string name="permdesc_writeContacts" product="tv" msgid="6488872735379978935">"อนุญาตให้แอปแก้ไขข้อมูลเกี่ยวกับรายชื่อติดต่อที่จัดเก็บไว้ในอุปกรณ์ Android TV สิทธิ์นี้ทำให้แอปลบข้อมูลรายชื่อติดต่อได้"</string>
     <string name="permdesc_writeContacts" product="default" msgid="8304795696237065281">"อนุญาตให้แอปแก้ไขข้อมูลเกี่ยวกับรายชื่อติดต่อที่จัดเก็บไว้ในโทรศัพท์ สิทธิ์นี้ทำให้แอปลบข้อมูลรายชื่อติดต่อได้"</string>
-    <string name="permlab_readCallLog" msgid="1739990210293505948">"อ่านประวัติการโทร"</string>
+    <string name="permlab_readCallLog" msgid="1739990210293505948">"อ่านบันทึกการโทร"</string>
     <string name="permdesc_readCallLog" msgid="8964770895425873433">"แอปนี้สามารถอ่านประวัติการโทรของคุณได้"</string>
-    <string name="permlab_writeCallLog" msgid="670292975137658895">"เขียนประวัติการโทร"</string>
-    <string name="permdesc_writeCallLog" product="tablet" msgid="2657525794731690397">"อนุญาตให้แอปแก้ไขประวัติการโทรจากแท็บเล็ตของคุณ รวมถึงข้อมูลเกี่ยวกับสายเรียกเข้าและการโทรออก แอปที่เป็นอันตรายอาจใช้สิ่งนี้เพื่อลบหรือแก้ไขประวัติการโทรของคุณ"</string>
-    <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"อนุญาตให้แอปแก้ไขประวัติการโทรจากอุปกรณ์ Android TV รวมถึงข้อมูลเกี่ยวกับสายเรียกเข้าและสายโทรออก แอปที่เป็นอันตรายอาจใช้สิทธิ์นี้เพื่อลบหรือแก้ไขประวัติการโทรได้"</string>
-    <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"อนุญาตให้แอปแก้ไขประวัติการโทรจากโทรศัพท์ของคุณ รวมถึงข้อมูลเกี่ยวกับสายเรียกเข้าและการโทรออก แอปที่เป็นอันตรายอาจใช้สิ่งนี้เพื่อลบหรือแก้ไขประวัติการโทรของคุณ"</string>
+    <string name="permlab_writeCallLog" msgid="670292975137658895">"เขียนบันทึกการโทร"</string>
+    <string name="permdesc_writeCallLog" product="tablet" msgid="2657525794731690397">"อนุญาตให้แอปแก้ไขบันทึกการโทรจากแท็บเล็ตของคุณ รวมถึงข้อมูลเกี่ยวกับสายเรียกเข้าและการโทรออก แอปที่เป็นอันตรายอาจใช้สิ่งนี้เพื่อลบหรือแก้ไขบันทึกการโทรของคุณ"</string>
+    <string name="permdesc_writeCallLog" product="tv" msgid="3934939195095317432">"อนุญาตให้แอปแก้ไขบันทึกการโทรจากอุปกรณ์ Android TV รวมถึงข้อมูลเกี่ยวกับสายเรียกเข้าและสายโทรออก แอปที่เป็นอันตรายอาจใช้สิทธิ์นี้เพื่อลบหรือแก้ไขบันทึกการโทรได้"</string>
+    <string name="permdesc_writeCallLog" product="default" msgid="5903033505665134802">"อนุญาตให้แอปแก้ไขบันทึกการโทรจากโทรศัพท์ของคุณ รวมถึงข้อมูลเกี่ยวกับสายเรียกเข้าและการโทรออก แอปที่เป็นอันตรายอาจใช้สิ่งนี้เพื่อลบหรือแก้ไขบันทึกการโทรของคุณ"</string>
     <string name="permlab_bodySensors" msgid="3411035315357380862">"เข้าถึงเซ็นเซอร์ร่างกาย (เช่น ตัววัดอัตราการเต้นของหัวใจ)"</string>
     <string name="permdesc_bodySensors" product="default" msgid="2365357960407973997">"อนุญาตให้แอปเข้าถึงข้อมูลจากเซ็นเซอร์ที่ตรวจสอบสภาพทางกายภาพ เช่น อัตราการเต้นของหัวใจ"</string>
     <string name="permlab_readCalendar" msgid="6408654259475396200">"อ่านกิจกรรมในปฏิทินและรายละเอียด"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 258b424..af10694 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -2009,8 +2009,8 @@
     <string name="notification_appops_microphone_active" msgid="581333393214739332">"Mikrofon"</string>
     <string name="notification_appops_overlay_active" msgid="5571732753262836481">"ekranınızdaki diğer uygulamaların üzerinde görüntüleniyor"</string>
     <string name="notification_feedback_indicator" msgid="663476517711323016">"Geri Bildirim Gönder"</string>
-    <string name="notification_feedback_indicator_alerted" msgid="6552871804121942099">"Bu bildirim, Varsayılana yükseltildi. Geri bildirimde bulunmak için dokunun."</string>
-    <string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"Bu bildirim Sessize düşürüldü. Geri bildirimde bulunmak için dokunun."</string>
+    <string name="notification_feedback_indicator_alerted" msgid="6552871804121942099">"Bu bildirimin önem derecesi, \"Varsayılan\" seviyesine yükseltildi. Geri bildirimde bulunmak için dokunun."</string>
+    <string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"Bu bildirimin önem derecesi, \"Sessiz\" seviyesine düşürüldü. Geri bildirimde bulunmak için dokunun."</string>
     <string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"Bu bildirimin önem derecesi yükseltildi. Geri bildirimde bulunmak için dokunun."</string>
     <string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"Bu bildirimin önem derecesi düşürüldü. Geri bildirimde bulunmak için dokunun."</string>
     <string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Rutin Modu bilgi bildirimi"</string>
diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml
index 04498c1..37cb12a 100644
--- a/core/res/res/values-ur/strings.xml
+++ b/core/res/res/values-ur/strings.xml
@@ -829,7 +829,7 @@
     <string name="keyguard_password_enter_puk_code" msgid="3112256684547584093">"‏PUK اور نیا PIN کوڈ ٹائپ کریں"</string>
     <string name="keyguard_password_enter_puk_prompt" msgid="2825313071899938305">"‏PUK کوڈ"</string>
     <string name="keyguard_password_enter_pin_prompt" msgid="5505434724229581207">"‏نیا PIN کوڈ"</string>
-    <string name="keyguard_password_entry_touch_hint" msgid="4032288032993261520"><font size="17">"پاسورڈ ٹائپ کرنے کیلئے تھپتھپائیں"</font></string>
+    <string name="keyguard_password_entry_touch_hint" msgid="4032288032993261520"><font size="17">"پاس ورڈ ٹائپ کرنے کیلئے تھپتھپائیں"</font></string>
     <string name="keyguard_password_enter_password_code" msgid="2751130557661643482">"غیر مقفل کرنے کیلئے پاس ورڈ ٹائپ کریں"</string>
     <string name="keyguard_password_enter_pin_password_code" msgid="7792964196473964340">"‏غیر مقفل کرنے کیلئے PIN ٹائپ کریں"</string>
     <string name="keyguard_password_wrong_pin_code" msgid="8583732939138432793">"‏غلط PIN کوڈ۔"</string>
diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml
index 11e20a9..23fd462 100644
--- a/core/res/res/values-uz/strings.xml
+++ b/core/res/res/values-uz/strings.xml
@@ -1410,7 +1410,7 @@
     <string name="permdesc_requestIgnoreBatteryOptimizations" msgid="634260656917874356">"Ilovaga batareya quvvatidan xohlagancha foydalanish uchun ruxsat so‘rashga imkon beradi."</string>
     <string name="tutorial_double_tap_to_zoom_message_short" msgid="1842872462124648678">"Ko‘lamini o‘zgartirish uchun ikki marta bosing"</string>
     <string name="gadget_host_error_inflating" msgid="2449961590495198720">"Vidjet qo‘shilmadi."</string>
-    <string name="ime_action_go" msgid="5536744546326495436">"O‘tish"</string>
+    <string name="ime_action_go" msgid="5536744546326495436">"Tanlash"</string>
     <string name="ime_action_search" msgid="4501435960587287668">"Qidirish"</string>
     <string name="ime_action_send" msgid="8456843745664334138">"Yuborish"</string>
     <string name="ime_action_next" msgid="4169702997635728543">"Keyingisi"</string>
@@ -2009,10 +2009,10 @@
     <string name="notification_appops_microphone_active" msgid="581333393214739332">"Mikrofon"</string>
     <string name="notification_appops_overlay_active" msgid="5571732753262836481">"ekranda boshqa ilovalar ustidan ochiladi"</string>
     <string name="notification_feedback_indicator" msgid="663476517711323016">"Fikr-mulohaza yuborish"</string>
-    <string name="notification_feedback_indicator_alerted" msgid="6552871804121942099">"Bu bildirishnoma standart sifatida balandlatildi. Fikr-mulohaza bildirish uchun bosing."</string>
-    <string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"Bu bildirishnoma Ovozsiz sifatida pastlatildi Fikr-mulohaza bildirish uchun bosing."</string>
-    <string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"Bu bildirishnomaga baland baho berilgan. Fikr-mulohaza bildirish uchun bosing."</string>
-    <string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"Bu bildirishnomaga past baho berilgan. Fikr-mulohaza bildirish uchun bosing."</string>
+    <string name="notification_feedback_indicator_alerted" msgid="6552871804121942099">"Bu bildirishnoma darajasi Standart darajaga chiqarildi. Fikr-mulohaza bildirish uchun bosing."</string>
+    <string name="notification_feedback_indicator_silenced" msgid="3799442124723177262">"Bu bildirishnoma darajasi Tovushsiz darajaga tushirildi Fikr-mulohaza bildirish uchun bosing."</string>
+    <string name="notification_feedback_indicator_promoted" msgid="9030204303764698640">"Bu bildirishnoma darajasi oshirildi. Fikr-mulohaza bildirish uchun bosing."</string>
+    <string name="notification_feedback_indicator_demoted" msgid="8880309924296450875">"Bu bildirishnoma darajasi pasaytirildi. Fikr-mulohaza bildirish uchun bosing."</string>
     <string name="dynamic_mode_notification_channel_name" msgid="2986926422100223328">"Kun tartibi rejimi haqidagi bildirishnoma"</string>
     <string name="dynamic_mode_notification_title" msgid="9205715501274608016">"Batareya quvvati odatdagidan ertaroq tugashi mumkin"</string>
     <string name="dynamic_mode_notification_summary" msgid="4141614604437372157">"Batareya quvvatini uzoqroq vaqtga yetkazish uchun quvvat tejash rejimi yoqildi"</string>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 4313d4a..0958434 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3064,7 +3064,7 @@
     <!-- dimension definitions go here -->
   </public-group>
 
-  <public-group type="bool" first-id="0x01110006">
+  <public-group type="bool" first-id="0x01110007">
     <!-- boolean definitions go here -->
   </public-group>
 
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerData.java b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerData.java
index 8086ff2..e0d159b 100644
--- a/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerData.java
+++ b/core/tests/batterystatstests/BatteryStatsViewer/src/com/android/frameworks/core/batterystatsviewer/BatteryConsumerData.java
@@ -171,6 +171,7 @@
         mBatteryConsumerInfo = BatteryConsumerInfoHelper.makeBatteryConsumerInfo(
                 context.getPackageManager(), requestedBatterySipper);
         long totalScreenMeasuredEnergyUJ = batteryStats.getScreenOnEnergy();
+        long uidScreenMeasuredEnergyUJ = requestedBatterySipper.uidObj.getScreenOnEnergy();
 
         addEntry("Total power", EntryType.POWER,
                 requestedBatterySipper.totalSmearedPowerMah, totalSmearedPowerMah);
@@ -180,11 +181,12 @@
                 requestedBatterySipper.totalSmearedPowerMah, totalPowerExcludeSystemMah);
         addEntry("Screen, smeared", EntryType.POWER,
                 requestedBatterySipper.screenPowerMah, totalScreenPower);
-        if (totalScreenMeasuredEnergyUJ != BatteryStats.ENERGY_DATA_UNAVAILABLE) {
-            final double measuredCharge = UJ_2_MAH * totalScreenMeasuredEnergyUJ;
-            final double ratio = measuredCharge / totalScreenPower;
-            addEntry("Screen, smeared (PowerStatsHal adjusted)", EntryType.POWER,
-                    requestedBatterySipper.screenPowerMah * ratio, measuredCharge);
+        if (uidScreenMeasuredEnergyUJ != BatteryStats.ENERGY_DATA_UNAVAILABLE
+                && totalScreenMeasuredEnergyUJ != BatteryStats.ENERGY_DATA_UNAVAILABLE) {
+            final double measuredCharge = UJ_2_MAH * uidScreenMeasuredEnergyUJ;
+            final double totalMeasuredCharge = UJ_2_MAH * totalScreenMeasuredEnergyUJ;
+            addEntry("Screen, measured", EntryType.POWER,
+                    measuredCharge, totalMeasuredCharge);
         }
         addEntry("Other, smeared", EntryType.POWER,
                 requestedBatterySipper.proportionalSmearMah, totalProportionalSmearMah);
diff --git a/core/tests/coretests/src/android/app/appsearch/external/app/SearchSpecTest.java b/core/tests/coretests/src/android/app/appsearch/external/app/SearchSpecTest.java
index 9fd480d..b3caecc 100644
--- a/core/tests/coretests/src/android/app/appsearch/external/app/SearchSpecTest.java
+++ b/core/tests/coretests/src/android/app/appsearch/external/app/SearchSpecTest.java
@@ -22,6 +22,9 @@
 
 import org.junit.Test;
 
+import java.util.List;
+import java.util.Map;
+
 public class SearchSpecTest {
     @Test
     public void testGetBundle() {
@@ -53,4 +56,21 @@
         assertThat(bundle.getInt(SearchSpec.RANKING_STRATEGY_FIELD))
                 .isEqualTo(SearchSpec.RANKING_STRATEGY_DOCUMENT_SCORE);
     }
+
+    @Test
+    public void testGetProjectionTypePropertyMasks() {
+        SearchSpec searchSpec =
+                new SearchSpec.Builder()
+                        .setTermMatch(SearchSpec.TERM_MATCH_PREFIX)
+                        .addProjectionTypePropertyPaths("TypeA", "field1", "field2.subfield2")
+                        .addProjectionTypePropertyPaths("TypeB", "field7")
+                        .addProjectionTypePropertyPaths("TypeC")
+                        .build();
+
+        Map<String, List<String>> typePropertyPathMap = searchSpec.getProjectionTypePropertyPaths();
+        assertThat(typePropertyPathMap.keySet()).containsExactly("TypeA", "TypeB", "TypeC");
+        assertThat(typePropertyPathMap.get("TypeA")).containsExactly("field1", "field2.subfield2");
+        assertThat(typePropertyPathMap.get("TypeB")).containsExactly("field7");
+        assertThat(typePropertyPathMap.get("TypeC")).isEmpty();
+    }
 }
diff --git a/core/tests/coretests/src/android/app/assist/AssistStructureTest.java b/core/tests/coretests/src/android/app/assist/AssistStructureTest.java
index da386a6..4609c23 100644
--- a/core/tests/coretests/src/android/app/assist/AssistStructureTest.java
+++ b/core/tests/coretests/src/android/app/assist/AssistStructureTest.java
@@ -23,11 +23,14 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import android.app.assist.AssistStructure.ViewNode;
+import android.app.assist.AssistStructure.ViewNodeBuilder;
+import android.app.assist.AssistStructure.ViewNodeParcelable;
 import android.content.Context;
 import android.os.Parcel;
 import android.os.SystemClock;
 import android.text.InputFilter;
 import android.util.Log;
+import android.view.View;
 import android.view.autofill.AutofillId;
 import android.widget.EditText;
 import android.widget.FrameLayout;
@@ -219,6 +222,28 @@
         }
     }
 
+    @Test
+    public void testViewNodeParcelableForAutofill() {
+        Log.d(TAG, "Adding view with " + BIG_VIEW_SIZE + " chars");
+
+        View view = newBigView();
+        mActivity.addView(view);
+        waitUntilViewsAreLaidOff();
+
+        assertThat(view.getViewRootImpl()).isNotNull();
+        ViewNodeBuilder viewStructure = new ViewNodeBuilder();
+        viewStructure.setAutofillId(view.getAutofillId());
+        view.onProvideAutofillStructure(viewStructure, /* flags= */ 0);
+        ViewNodeParcelable viewNodeParcelable = new ViewNodeParcelable(viewStructure.getViewNode());
+
+        // Check properties on "original" view node.
+        assertBigView(viewNodeParcelable.getViewNode());
+
+        // Check properties on "cloned" view node.
+        ViewNodeParcelable clone = cloneThroughParcel(viewNodeParcelable);
+        assertBigView(clone.getViewNode());
+    }
+
     private EditText newSmallView() {
         EditText view = new EditText(mContext);
         view.setText("I AM GROOT");
@@ -272,6 +297,24 @@
         assertThat(hint.charAt(BIG_VIEW_SIZE - 1)).isEqualTo(BIG_VIEW_CHAR);
     }
 
+    private ViewNodeParcelable cloneThroughParcel(ViewNodeParcelable viewNodeParcelable) {
+        Parcel parcel = Parcel.obtain();
+
+        try {
+            // Write to parcel
+            parcel.setDataPosition(0); // Validity Check
+            viewNodeParcelable.writeToParcel(parcel, NO_FLAGS);
+
+            // Read from parcel
+            parcel.setDataPosition(0);
+            ViewNodeParcelable clone = ViewNodeParcelable.CREATOR.createFromParcel(parcel);
+            assertThat(clone).isNotNull();
+            return clone;
+        } finally {
+            parcel.recycle();
+        }
+    }
+
     private AssistStructure cloneThroughParcel(AssistStructure structure) {
         Parcel parcel = Parcel.obtain();
 
diff --git a/core/tests/coretests/src/android/app/assist/OWNERS b/core/tests/coretests/src/android/app/assist/OWNERS
new file mode 100644
index 0000000..43ad108
--- /dev/null
+++ b/core/tests/coretests/src/android/app/assist/OWNERS
@@ -0,0 +1 @@
+file:/core/java/android/app/assist/OWNERS
diff --git a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
index b2b34d6..50b52eb 100644
--- a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
@@ -23,9 +23,11 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertNotSame;
 import static org.junit.Assert.assertSame;
 
+import android.app.ActivityOptions;
 import android.app.servertransaction.TestUtils.LaunchActivityItemBuilder;
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
@@ -247,6 +249,22 @@
     }
 
     @Test
+    public void testRecycleStartActivityItem() {
+        StartActivityItem emptyItem = StartActivityItem.obtain(null /* activityOptions */);
+        StartActivityItem item = StartActivityItem.obtain(ActivityOptions.makeBasic());
+        assertNotSame(item, emptyItem);
+        assertNotEquals(item, emptyItem);
+
+        item.recycle();
+        assertEquals(item, emptyItem);
+
+        StartActivityItem item2 = StartActivityItem.obtain(
+                ActivityOptions.makeBasic().setLaunchDisplayId(10));
+        assertSame(item, item2);
+        assertNotEquals(item2, emptyItem);
+    }
+
+    @Test
     public void testRecycleStopItem() {
         StopActivityItem emptyItem = StopActivityItem.obtain(0);
         StopActivityItem item = StopActivityItem.obtain(4);
diff --git a/core/tests/coretests/src/android/app/servertransaction/TestUtils.java b/core/tests/coretests/src/android/app/servertransaction/TestUtils.java
index 7e9933c..02e75dd 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TestUtils.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TestUtils.java
@@ -18,6 +18,7 @@
 
 import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
 
+import android.app.ActivityOptions;
 import android.app.ProfilerInfo;
 import android.app.ResultInfo;
 import android.content.Intent;
@@ -104,6 +105,7 @@
         private PersistableBundle mPersistentState;
         private List<ResultInfo> mPendingResults;
         private List<ReferrerIntent> mPendingNewIntents;
+        private ActivityOptions mActivityOptions;
         private boolean mIsForward;
         private ProfilerInfo mProfilerInfo;
         private IBinder mAssistToken;
@@ -174,6 +176,11 @@
             return this;
         }
 
+        LaunchActivityItemBuilder setActivityOptions(ActivityOptions activityOptions) {
+            mActivityOptions = activityOptions;
+            return this;
+        }
+
         LaunchActivityItemBuilder setIsForward(boolean isForward) {
             mIsForward = isForward;
             return this;
@@ -198,8 +205,8 @@
             return LaunchActivityItem.obtain(mIntent, mIdent, mInfo,
                     mCurConfig, mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor,
                     mProcState, mState, mPersistentState, mPendingResults, mPendingNewIntents,
-                    mIsForward, mProfilerInfo, mAssistToken, null /* activityClientController */,
-                    mFixedRotationAdjustments);
+                    mActivityOptions, mIsForward, mProfilerInfo, mAssistToken,
+                    null /* activityClientController */, mFixedRotationAdjustments);
         }
     }
 }
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
index e1c7146..5705dd5 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
@@ -24,6 +24,7 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
+import android.app.ActivityOptions;
 import android.app.ContentProviderHolder;
 import android.app.IApplicationThread;
 import android.app.IInstrumentationWatcher;
@@ -200,9 +201,10 @@
                 .setIntent(intent).setIdent(ident).setInfo(activityInfo).setCurConfig(config())
                 .setOverrideConfig(overrideConfig).setCompatInfo(compat).setReferrer(referrer)
                 .setProcState(procState).setState(bundle).setPersistentState(persistableBundle)
-                .setPendingResults(resultInfoList()).setPendingNewIntents(referrerIntentList())
-                .setIsForward(true).setAssistToken(new Binder())
-                .setFixedRotationAdjustments(fixedRotationAdjustments).build();
+                .setPendingResults(resultInfoList()).setActivityOptions(ActivityOptions.makeBasic())
+                .setPendingNewIntents(referrerIntentList()).setIsForward(true)
+                .setAssistToken(new Binder()).setFixedRotationAdjustments(fixedRotationAdjustments)
+                .build();
 
         writeAndPrepareForReading(item);
 
@@ -273,7 +275,7 @@
     @Test
     public void testStart() {
         // Write to parcel
-        StartActivityItem item = StartActivityItem.obtain();
+        StartActivityItem item = StartActivityItem.obtain(ActivityOptions.makeBasic());
         writeAndPrepareForReading(item);
 
         // Read from parcel and assert
diff --git a/core/tests/coretests/src/android/content/OWNERS b/core/tests/coretests/src/android/content/OWNERS
index 911efb2..912db1e 100644
--- a/core/tests/coretests/src/android/content/OWNERS
+++ b/core/tests/coretests/src/android/content/OWNERS
@@ -1 +1,3 @@
 per-file ContextTest.java = file:/services/core/java/com/android/server/wm/OWNERS
+per-file *Shortcut* = file:/core/java/android/content/pm/SHORTCUT_OWNERS
+per-file *Launcher* = file:/core/java/android/content/pm/LAUNCHER_OWNERS
diff --git a/core/tests/coretests/src/android/service/notification/NotificationListenerFilterTest.java b/core/tests/coretests/src/android/service/notification/NotificationListenerFilterTest.java
new file mode 100644
index 0000000..a43b238
--- /dev/null
+++ b/core/tests/coretests/src/android/service/notification/NotificationListenerFilterTest.java
@@ -0,0 +1,124 @@
+/*
+ * 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.service.notification;
+
+import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
+import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ALERTING;
+import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_CONVERSATIONS;
+import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_SILENT;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.NotificationChannel;
+import android.os.Parcel;
+import android.util.ArraySet;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class NotificationListenerFilterTest {
+
+    @Test
+    public void testEmptyConstructor() {
+        NotificationListenerFilter nlf = new NotificationListenerFilter();
+        assertThat(nlf.isTypeAllowed(FLAG_FILTER_TYPE_CONVERSATIONS)).isTrue();
+        assertThat(nlf.isTypeAllowed(FLAG_FILTER_TYPE_ALERTING)).isTrue();
+        assertThat(nlf.isTypeAllowed(FLAG_FILTER_TYPE_SILENT)).isTrue();
+        assertThat(nlf.getTypes()).isEqualTo(FLAG_FILTER_TYPE_CONVERSATIONS
+                | FLAG_FILTER_TYPE_ALERTING
+                | FLAG_FILTER_TYPE_SILENT);
+
+        assertThat(nlf.getDisallowedPackages()).isEmpty();
+        assertThat(nlf.isPackageAllowed("pkg1")).isTrue();
+    }
+
+
+    @Test
+    public void testConstructor() {
+        ArraySet<String> pkgs = new ArraySet<>(new String[] {"pkg1", "pkg2"});
+        NotificationListenerFilter nlf =
+                new NotificationListenerFilter(FLAG_FILTER_TYPE_ALERTING, pkgs);
+        assertThat(nlf.isTypeAllowed(FLAG_FILTER_TYPE_CONVERSATIONS)).isFalse();
+        assertThat(nlf.isTypeAllowed(FLAG_FILTER_TYPE_ALERTING)).isTrue();
+        assertThat(nlf.isTypeAllowed(FLAG_FILTER_TYPE_SILENT)).isFalse();
+        assertThat(nlf.getTypes()).isEqualTo(FLAG_FILTER_TYPE_ALERTING);
+
+        assertThat(nlf.getDisallowedPackages()).contains("pkg1");
+        assertThat(nlf.getDisallowedPackages()).contains("pkg2");
+        assertThat(nlf.isPackageAllowed("pkg1")).isFalse();
+        assertThat(nlf.isPackageAllowed("pkg2")).isFalse();
+    }
+
+    @Test
+    public void testSetDisallowedPackages() {
+        NotificationListenerFilter nlf = new NotificationListenerFilter();
+
+        ArraySet<String> pkgs = new ArraySet<>(new String[] {"pkg1"});
+        nlf.setDisallowedPackages(pkgs);
+
+        assertThat(nlf.isPackageAllowed("pkg1")).isFalse();
+    }
+
+    @Test
+    public void testSetTypes() {
+        NotificationListenerFilter nlf = new NotificationListenerFilter();
+
+        nlf.setTypes(FLAG_FILTER_TYPE_ALERTING | FLAG_FILTER_TYPE_SILENT);
+
+        assertThat(nlf.isTypeAllowed(FLAG_FILTER_TYPE_CONVERSATIONS)).isFalse();
+        assertThat(nlf.isTypeAllowed(FLAG_FILTER_TYPE_ALERTING)).isTrue();
+        assertThat(nlf.isTypeAllowed(FLAG_FILTER_TYPE_SILENT)).isTrue();
+        assertThat(nlf.getTypes()).isEqualTo(FLAG_FILTER_TYPE_ALERTING
+                | FLAG_FILTER_TYPE_SILENT);
+    }
+
+    @Test
+    public void testDescribeContents() {
+        final int expected = 0;
+        ArraySet<String> pkgs = new ArraySet<>(new String[] {"pkg1", "pkg2"});
+        NotificationListenerFilter nlf =
+                new NotificationListenerFilter(FLAG_FILTER_TYPE_ALERTING, pkgs);
+        assertThat(nlf.describeContents()).isEqualTo(expected);
+    }
+
+    @Test
+    public void testParceling() {
+        ArraySet<String> pkgs = new ArraySet<>(new String[] {"pkg1", "pkg2"});
+        NotificationListenerFilter nlf =
+                new NotificationListenerFilter(FLAG_FILTER_TYPE_ALERTING, pkgs);
+
+        Parcel parcel = Parcel.obtain();
+        nlf.writeToParcel(parcel, 0);
+        parcel.setDataPosition(0);
+        NotificationListenerFilter nlf1 =
+                NotificationListenerFilter.CREATOR.createFromParcel(parcel);
+        assertThat(nlf1.isTypeAllowed(FLAG_FILTER_TYPE_CONVERSATIONS)).isFalse();
+        assertThat(nlf1.isTypeAllowed(FLAG_FILTER_TYPE_ALERTING)).isTrue();
+        assertThat(nlf1.isTypeAllowed(FLAG_FILTER_TYPE_SILENT)).isFalse();
+        assertThat(nlf1.getTypes()).isEqualTo(FLAG_FILTER_TYPE_ALERTING);
+
+        assertThat(nlf1.getDisallowedPackages()).contains("pkg1");
+        assertThat(nlf1.getDisallowedPackages()).contains("pkg2");
+        assertThat(nlf1.isPackageAllowed("pkg1")).isFalse();
+        assertThat(nlf1.isPackageAllowed("pkg2")).isFalse();
+    }
+}
diff --git a/core/tests/coretests/src/android/service/notification/OWNERS b/core/tests/coretests/src/android/service/notification/OWNERS
new file mode 100644
index 0000000..1502b60
--- /dev/null
+++ b/core/tests/coretests/src/android/service/notification/OWNERS
@@ -0,0 +1,2 @@
+include platform/frameworks/base:/services/core/java/com/android/server/notification/OWNERS
+
diff --git a/core/tests/coretests/src/android/util/TypedValueTest.kt b/core/tests/coretests/src/android/util/TypedValueTest.kt
new file mode 100644
index 0000000..7a05d97
--- /dev/null
+++ b/core/tests/coretests/src/android/util/TypedValueTest.kt
@@ -0,0 +1,155 @@
+/*
+ * 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.util
+
+import androidx.test.filters.LargeTest
+import androidx.test.filters.SmallTest
+import androidx.test.runner.AndroidJUnit4
+import org.junit.Assert.assertEquals
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.mock
+import kotlin.math.abs
+import kotlin.math.min
+import kotlin.math.roundToInt
+
+@RunWith(AndroidJUnit4::class)
+class TypedValueTest {
+    @LargeTest
+    @Test
+    fun testFloatToComplex() {
+        fun assertRoundTripEquals(value: Float, expectedRadix: Int? = null) {
+            val complex = TypedValue.floatToComplex(value)
+            // Ensure values are accurate within .5% of the original value and within .5
+            val delta = min(abs(value) / 512f, .5f)
+            assertEquals(value, TypedValue.complexToFloat(complex), delta)
+            // If expectedRadix is provided, validate it
+            if (expectedRadix != null) {
+                val actualRadix = ((complex shr TypedValue.COMPLEX_RADIX_SHIFT)
+                        and TypedValue.COMPLEX_RADIX_MASK)
+                assertEquals("Incorrect radix for $value:", expectedRadix, actualRadix)
+            }
+        }
+
+        assertRoundTripEquals(0f, TypedValue.COMPLEX_RADIX_23p0)
+
+        assertRoundTripEquals(0.5f, TypedValue.COMPLEX_RADIX_0p23)
+        assertRoundTripEquals(0.05f, TypedValue.COMPLEX_RADIX_0p23)
+        assertRoundTripEquals(0.005f, TypedValue.COMPLEX_RADIX_0p23)
+        assertRoundTripEquals(0.0005f, TypedValue.COMPLEX_RADIX_0p23)
+        assertRoundTripEquals(0.00005f, TypedValue.COMPLEX_RADIX_0p23)
+
+        assertRoundTripEquals(1.5f, TypedValue.COMPLEX_RADIX_8p15)
+        assertRoundTripEquals(10.5f, TypedValue.COMPLEX_RADIX_8p15)
+        assertRoundTripEquals(100.5f, TypedValue.COMPLEX_RADIX_8p15)
+        assertRoundTripEquals(255.5f, TypedValue.COMPLEX_RADIX_8p15) // 2^8 - .5
+
+        assertRoundTripEquals(256.5f, TypedValue.COMPLEX_RADIX_16p7) // 2^8 + .5
+        assertRoundTripEquals(1000.5f, TypedValue.COMPLEX_RADIX_16p7)
+        assertRoundTripEquals(10000.5f, TypedValue.COMPLEX_RADIX_16p7)
+        assertRoundTripEquals(65535.5f, TypedValue.COMPLEX_RADIX_16p7) // 2^16 - .5
+
+        assertRoundTripEquals(65536.5f, TypedValue.COMPLEX_RADIX_23p0) // 2^16 + .5
+        assertRoundTripEquals(100000.5f, TypedValue.COMPLEX_RADIX_23p0)
+        assertRoundTripEquals(1000000.5f, TypedValue.COMPLEX_RADIX_23p0)
+        assertRoundTripEquals(8388607.2f, TypedValue.COMPLEX_RADIX_23p0) // 2^23 -.8
+
+        assertRoundTripEquals(-0.5f, TypedValue.COMPLEX_RADIX_0p23)
+        assertRoundTripEquals(-0.05f, TypedValue.COMPLEX_RADIX_0p23)
+        assertRoundTripEquals(-0.005f, TypedValue.COMPLEX_RADIX_0p23)
+        assertRoundTripEquals(-0.0005f, TypedValue.COMPLEX_RADIX_0p23)
+        assertRoundTripEquals(-0.00005f, TypedValue.COMPLEX_RADIX_0p23)
+
+        assertRoundTripEquals(-1.5f, TypedValue.COMPLEX_RADIX_8p15)
+        assertRoundTripEquals(-10.5f, TypedValue.COMPLEX_RADIX_8p15)
+        assertRoundTripEquals(-100.5f, TypedValue.COMPLEX_RADIX_8p15)
+        assertRoundTripEquals(-255.5f, TypedValue.COMPLEX_RADIX_8p15) // -2^8 + .5
+
+        // NOTE: -256.5f fits in COMPLEX_RADIX_8p15 but is stored with COMPLEX_RADIX_16p7 for
+        // simplicity of the algorithm.  However, it's better not to enforce that with a test.
+        assertRoundTripEquals(-257.5f, TypedValue.COMPLEX_RADIX_16p7) // -2^8 - 1.5
+        assertRoundTripEquals(-1000.5f, TypedValue.COMPLEX_RADIX_16p7)
+        assertRoundTripEquals(-10000.5f, TypedValue.COMPLEX_RADIX_16p7)
+        assertRoundTripEquals(-65535.5f, TypedValue.COMPLEX_RADIX_16p7) // -2^16 + .5
+
+        // NOTE: -65536.5f fits in COMPLEX_RADIX_16p7 but is stored with COMPLEX_RADIX_23p0 for
+        // simplicity of the algorithm.  However, it's better not to enforce that with a test.
+        assertRoundTripEquals(-65537.5f, TypedValue.COMPLEX_RADIX_23p0) // -2^16 - 1.5
+        assertRoundTripEquals(-100000.5f, TypedValue.COMPLEX_RADIX_23p0)
+        assertRoundTripEquals(-1000000.5f, TypedValue.COMPLEX_RADIX_23p0)
+        assertRoundTripEquals(-8388607.5f, TypedValue.COMPLEX_RADIX_23p0) // 2^23 -.5
+
+        // Test for every integer value in the range...
+        for (i: Int in -(1 shl 23) until (1 shl 23)) {
+            // ... that true integers are stored as the precise integer
+            assertRoundTripEquals(i.toFloat(), TypedValue.COMPLEX_RADIX_23p0)
+            // ... that values round up when just below an integer
+            assertRoundTripEquals(i - .1f)
+            // ... that values round down when just above an integer
+            assertRoundTripEquals(i + .1f)
+        }
+    }
+
+    @SmallTest
+    @Test(expected = IllegalArgumentException::class)
+    fun testFloatToComplex_failsIfValueTooLarge() {
+        TypedValue.floatToComplex(8388607.5f) // 2^23 - .5
+    }
+
+    @SmallTest
+    @Test(expected = IllegalArgumentException::class)
+    fun testFloatToComplex_failsIfValueTooSmall() {
+        TypedValue.floatToComplex(8388608.5f) // -2^23 - .5
+    }
+
+    @LargeTest
+    @Test
+    fun testIntToComplex() {
+        // Validates every single valid value
+        for (value: Int in -(1 shl 23) until (1 shl 23)) {
+            assertEquals(value.toFloat(), TypedValue.complexToFloat(TypedValue.intToComplex(value)))
+        }
+    }
+
+    @SmallTest
+    @Test(expected = IllegalArgumentException::class)
+    fun testIntToComplex_failsIfValueTooLarge() {
+        TypedValue.intToComplex(0x800000)
+    }
+
+    @SmallTest
+    @Test(expected = IllegalArgumentException::class)
+    fun testIntToComplex_failsIfValueTooSmall() {
+        TypedValue.intToComplex(-0x800001)
+    }
+
+    @SmallTest
+    @Test
+    fun testCreateComplexDimension_appliesUnits() {
+        val metrics: DisplayMetrics = mock(DisplayMetrics::class.java)
+        metrics.density = 3.25f
+
+        val height = 52 * metrics.density
+        val widthFloat = height * 16 / 9
+        val widthDimen = TypedValue.createComplexDimension(
+                widthFloat / metrics.density,
+                TypedValue.COMPLEX_UNIT_DIP
+        )
+        val widthPx = TypedValue.complexToDimensionPixelSize(widthDimen, metrics)
+        assertEquals(widthFloat.roundToInt(), widthPx)
+    }
+}
\ No newline at end of file
diff --git a/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
index 970ab79..7a2e6b7 100644
--- a/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
+++ b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
@@ -78,11 +78,11 @@
             mController = Mockito.spy(new InsetsController(
                     new ViewRootInsetsControllerHost(viewRootImpl)));
             final Rect rect = new Rect(5, 5, 5, 5);
+            mController.getState().setDisplayCutout(new DisplayCutout(
+                    Insets.of(10, 10, 10, 10), rect, rect, rect, rect));
             mController.calculateInsets(
                     false,
                     false,
-                    new DisplayCutout(
-                            Insets.of(10, 10, 10, 10), rect, rect, rect, rect),
                     TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED,
                     SOFT_INPUT_ADJUST_RESIZE, 0, 0);
             mImeConsumer = (ImeInsetsSourceConsumer) mController.getSourceConsumer(ITYPE_IME);
diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java
index 7b84f68c..af13cc0 100644
--- a/core/tests/coretests/src/android/view/InsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java
@@ -151,11 +151,11 @@
                     new Rect(0, 90, 100, 100));
             mController.getState().getSource(ITYPE_IME).setFrame(new Rect(0, 50, 100, 100));
             mController.getState().setDisplayFrame(new Rect(0, 0, 100, 100));
+            mController.getState().setDisplayCutout(new DisplayCutout(
+                    Insets.of(10, 10, 10, 10), rect, rect, rect, rect));
             mController.calculateInsets(
                     false,
                     false,
-                    new DisplayCutout(
-                            Insets.of(10, 10, 10, 10), rect, rect, rect, rect),
                     TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED,
                     SOFT_INPUT_ADJUST_RESIZE, 0, 0);
             mController.onFrameChanged(new Rect(0, 0, 100, 100));
diff --git a/core/tests/coretests/src/android/view/InsetsStateTest.java b/core/tests/coretests/src/android/view/InsetsStateTest.java
index 8a000a0..9705284 100644
--- a/core/tests/coretests/src/android/view/InsetsStateTest.java
+++ b/core/tests/coretests/src/android/view/InsetsStateTest.java
@@ -37,6 +37,7 @@
 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING;
 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN;
 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_UNSPECIFIED;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
 import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
@@ -82,8 +83,8 @@
         mState.getSource(ITYPE_IME).setVisible(true);
         SparseIntArray typeSideMap = new SparseIntArray();
         WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
-                false, DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_RESIZE, 0, 0,
-                TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED, typeSideMap);
+                false, SOFT_INPUT_ADJUST_RESIZE, 0, 0, TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED,
+                typeSideMap);
         assertEquals(Insets.of(0, 100, 0, 100), insets.getSystemWindowInsets());
         assertEquals(Insets.of(0, 100, 0, 100), insets.getInsets(Type.all()));
         assertEquals(ISIDE_TOP, typeSideMap.get(ITYPE_STATUS_BAR));
@@ -99,8 +100,8 @@
         mState.getSource(ITYPE_IME).setFrame(new Rect(0, 100, 100, 300));
         mState.getSource(ITYPE_IME).setVisible(true);
         WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
-                false, DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_RESIZE, 0, 0,
-                TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED, null);
+                false, SOFT_INPUT_ADJUST_RESIZE, 0, 0, TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED,
+                null);
         assertEquals(100, insets.getStableInsetBottom());
         assertEquals(Insets.of(0, 0, 0, 100), insets.getInsetsIgnoringVisibility(systemBars()));
         assertEquals(Insets.of(0, 0, 0, 200), insets.getSystemWindowInsets());
@@ -116,8 +117,7 @@
         mState.getSource(ITYPE_NAVIGATION_BAR).setFrame(new Rect(80, 0, 100, 300));
         mState.getSource(ITYPE_NAVIGATION_BAR).setVisible(true);
         WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
-                false, DisplayCutout.NO_CUTOUT, 0, 0, 0, TYPE_APPLICATION,
-                WINDOWING_MODE_UNDEFINED, null);
+                false, 0, 0, 0, TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED, null);
         assertEquals(Insets.of(0, 100, 20, 0), insets.getSystemWindowInsets());
         assertEquals(Insets.of(0, 100, 0, 0), insets.getInsets(statusBars()));
         assertEquals(Insets.of(0, 0, 20, 0), insets.getInsets(navigationBars()));
@@ -130,8 +130,7 @@
         mState.getSource(ITYPE_EXTRA_NAVIGATION_BAR).setFrame(new Rect(80, 0, 100, 300));
         mState.getSource(ITYPE_EXTRA_NAVIGATION_BAR).setVisible(true);
         WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
-                false, DisplayCutout.NO_CUTOUT, 0, 0, 0, TYPE_APPLICATION,
-                WINDOWING_MODE_UNDEFINED, null);
+                false, 0, 0, 0, TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED, null);
         // ITYPE_CLIMATE_BAR is a type of status bar and ITYPE_EXTRA_NAVIGATION_BAR is a type
         // of navigation bar.
         assertEquals(Insets.of(0, 100, 20, 0), insets.getSystemWindowInsets());
@@ -146,8 +145,8 @@
         mState.getSource(ITYPE_IME).setFrame(new Rect(0, 200, 100, 300));
         mState.getSource(ITYPE_IME).setVisible(true);
         WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
-                false, DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_NOTHING, 0, 0,
-                TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED, null);
+                false, SOFT_INPUT_ADJUST_NOTHING, 0, 0, TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED,
+                null);
         assertEquals(0, insets.getSystemWindowInsetBottom());
         assertEquals(100, insets.getInsets(ime()).bottom);
         assertTrue(insets.isVisible(ime()));
@@ -160,12 +159,12 @@
         mState.getSource(ITYPE_IME).setFrame(new Rect(0, 200, 100, 300));
         mState.getSource(ITYPE_IME).setVisible(true);
         WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
-                false, DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_NOTHING, 0,
-                SYSTEM_UI_FLAG_LAYOUT_STABLE, TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED, null);
+                false, SOFT_INPUT_ADJUST_NOTHING, 0, SYSTEM_UI_FLAG_LAYOUT_STABLE, TYPE_APPLICATION,
+                WINDOWING_MODE_UNDEFINED, null);
         assertEquals(100, insets.getSystemWindowInsetTop());
         insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false, false,
-                DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_NOTHING, 0,
-                0 /* legacySystemUiFlags */, TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED, null);
+                SOFT_INPUT_ADJUST_NOTHING, 0, 0 /* legacySystemUiFlags */, TYPE_APPLICATION,
+                WINDOWING_MODE_UNDEFINED, null);
         assertEquals(0, insets.getSystemWindowInsetTop());
     }
 
@@ -174,12 +173,12 @@
         mState.getSource(ITYPE_STATUS_BAR).setFrame(new Rect(0, 0, 100, 100));
         mState.getSource(ITYPE_STATUS_BAR).setVisible(false);
         WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
-                false, DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_NOTHING, FLAG_FULLSCREEN,
-                SYSTEM_UI_FLAG_LAYOUT_STABLE, TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED, null);
+                false, SOFT_INPUT_ADJUST_NOTHING, FLAG_FULLSCREEN, SYSTEM_UI_FLAG_LAYOUT_STABLE,
+                TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED, null);
         assertEquals(0, insets.getSystemWindowInsetTop());
         insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false, false,
-                DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_NOTHING, 0,
-                0 /* legacySystemUiFlags */, TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED, null);
+                SOFT_INPUT_ADJUST_NOTHING, 0, 0 /* legacySystemUiFlags */, TYPE_APPLICATION,
+                WINDOWING_MODE_UNDEFINED, null);
         assertEquals(0, insets.getSystemWindowInsetTop());
     }
 
@@ -188,19 +187,19 @@
         mState.getSource(ITYPE_STATUS_BAR).setFrame(new Rect(0, 0, 100, 100));
         mState.getSource(ITYPE_STATUS_BAR).setVisible(true);
         WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
-                false, DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_NOTHING, FLAG_LAYOUT_NO_LIMITS,
+                false, SOFT_INPUT_ADJUST_NOTHING, FLAG_LAYOUT_NO_LIMITS,
                 0 /* legacySystemUiFlags */, TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED, null);
         assertEquals(0, insets.getSystemWindowInsetTop());
         insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
-                false, DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_NOTHING, FLAG_LAYOUT_NO_LIMITS,
+                false, SOFT_INPUT_ADJUST_NOTHING, FLAG_LAYOUT_NO_LIMITS,
                 0 /* legacySystemUiFlags */, TYPE_SYSTEM_ERROR, WINDOWING_MODE_UNDEFINED, null);
         assertEquals(100, insets.getSystemWindowInsetTop());
         insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
-                false, DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_NOTHING, FLAG_LAYOUT_NO_LIMITS,
+                false, SOFT_INPUT_ADJUST_NOTHING, FLAG_LAYOUT_NO_LIMITS,
                 0 /* legacySystemUiFlags */, TYPE_WALLPAPER, WINDOWING_MODE_UNDEFINED, null);
         assertEquals(100, insets.getSystemWindowInsetTop());
         insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
-                false, DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_NOTHING, FLAG_LAYOUT_NO_LIMITS,
+                false, SOFT_INPUT_ADJUST_NOTHING, FLAG_LAYOUT_NO_LIMITS,
                 0 /* legacySystemUiFlags */, TYPE_APPLICATION, WINDOWING_MODE_FREEFORM, null);
         assertEquals(100, insets.getSystemWindowInsetTop());
     }
@@ -235,8 +234,7 @@
         mState.getSource(ITYPE_EXTRA_NAVIGATION_BAR).setFrame(new Rect(80, 0, 100, 300));
         mState.getSource(ITYPE_EXTRA_NAVIGATION_BAR).setVisible(true);
         WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
-                false, DisplayCutout.NO_CUTOUT, 0, 0, 0, TYPE_APPLICATION,
-                WINDOWING_MODE_UNDEFINED, null);
+                false, 0, 0, 0, TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED, null);
         assertEquals(Insets.of(0, 100, 20, 0), insets.getSystemWindowInsets());
         assertEquals(Insets.of(0, 100, 0, 0), insets.getInsets(statusBars()));
         assertEquals(Insets.of(0, 0, 20, 0), insets.getInsets(navigationBars()));
@@ -249,8 +247,7 @@
         mState.getSource(ITYPE_NAVIGATION_BAR).setFrame(new Rect(80, 0, 100, 300));
         mState.getSource(ITYPE_NAVIGATION_BAR).setVisible(true);
         WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
-                false, DisplayCutout.NO_CUTOUT, 0, 0, 0, TYPE_APPLICATION,
-                WINDOWING_MODE_UNDEFINED, null);
+                false, 0, 0, 0, TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED, null);
         assertEquals(Insets.of(0, 100, 20, 0), insets.getSystemWindowInsets());
         assertEquals(Insets.of(0, 100, 0, 0), insets.getInsets(statusBars()));
         assertEquals(Insets.of(0, 0, 20, 0), insets.getInsets(navigationBars()));
@@ -264,8 +261,7 @@
         mState.getSource(ITYPE_IME).setVisible(true);
         mState.removeSource(ITYPE_IME);
         WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false, false,
-                DisplayCutout.NO_CUTOUT, SOFT_INPUT_ADJUST_RESIZE, 0, 0, TYPE_APPLICATION,
-                WINDOWING_MODE_UNDEFINED, null);
+                SOFT_INPUT_ADJUST_RESIZE, 0, 0, TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED, null);
         assertEquals(0, insets.getSystemWindowInsetBottom());
     }
 
@@ -406,6 +402,31 @@
                 mState.calculateUncontrollableInsetsFromFrame(new Rect(50, 0, 150, 300)));
     }
 
+    @Test
+    public void testCalculateRelativeCutout() {
+        mState.setDisplayFrame(new Rect(0, 0, 200, 300));
+        mState.setDisplayCutout(new DisplayCutout(Insets.of(1, 2, 3, 4),
+                new Rect(0, 0, 1, 2),
+                new Rect(0, 0, 1, 2),
+                new Rect(197, 296, 200, 300),
+                new Rect(197, 296, 200, 300)));
+        DisplayCutout cutout = mState.calculateInsets(new Rect(1, 1, 199, 300), null, false, false,
+                SOFT_INPUT_ADJUST_UNSPECIFIED, 0, 0, TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED,
+                new SparseIntArray()).getDisplayCutout();
+        assertEquals(0, cutout.getSafeInsetLeft());
+        assertEquals(1, cutout.getSafeInsetTop());
+        assertEquals(2, cutout.getSafeInsetRight());
+        assertEquals(4, cutout.getSafeInsetBottom());
+        assertEquals(new Rect(-1, -1, 0, 1),
+                cutout.getBoundingRectLeft());
+        assertEquals(new Rect(-1, -1, 0, 1),
+                cutout.getBoundingRectTop());
+        assertEquals(new Rect(196, 295, 199, 299),
+                cutout.getBoundingRectRight());
+        assertEquals(new Rect(196, 295, 199, 299),
+                cutout.getBoundingRectBottom());
+    }
+
     private void assertEqualsAndHashCode() {
         assertEquals(mState, mState2);
         assertEquals(mState.hashCode(), mState2.hashCode());
diff --git a/core/tests/coretests/src/android/view/OWNERS b/core/tests/coretests/src/android/view/OWNERS
index a3a3e7c..5031ff9 100644
--- a/core/tests/coretests/src/android/view/OWNERS
+++ b/core/tests/coretests/src/android/view/OWNERS
@@ -2,3 +2,10 @@
 per-file *MotionEventTest.* = michaelwr@google.com, svv@google.com
 per-file *KeyEventTest.* = michaelwr@google.com, svv@google.com
 per-file VelocityTest.java = michaelwr@google.com, svv@google.com
+
+# WindowManager
+per-file *Display* = file:/services/core/java/com/android/server/wm/OWNERS
+per-file *Focus* = file:/services/core/java/com/android/server/wm/OWNERS
+per-file *Insets* = file:/services/core/java/com/android/server/wm/OWNERS
+per-file *View* = file:/services/core/java/com/android/server/wm/OWNERS
+per-file *Visibility* = file:/services/core/java/com/android/server/wm/OWNERS
diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java
index 4cf6715..f371a7f 100644
--- a/core/tests/coretests/src/android/view/ViewRootImplTest.java
+++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java
@@ -22,10 +22,12 @@
 import static android.view.View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
 import static android.view.View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
 import static android.view.View.SYSTEM_UI_FLAG_LOW_PROFILE;
-import static android.view.WindowInsetsController.BEHAVIOR_SHOW_BARS_BY_TOUCH;
+import static android.view.WindowInsetsController.APPEARANCE_OPAQUE_STATUS_BARS;
+import static android.view.WindowInsetsController.BEHAVIOR_SHOW_BARS_BY_SWIPE;
 import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
 import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
 
@@ -35,12 +37,14 @@
 import static org.junit.Assert.assertTrue;
 
 import android.content.Context;
+import android.os.Binder;
 import android.platform.test.annotations.Presubmit;
 import android.view.WindowInsets.Side;
 import android.view.WindowInsets.Type;
 
 import androidx.test.ext.junit.runners.AndroidJUnit4;
 import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -58,13 +62,15 @@
 public class ViewRootImplTest {
 
     private ViewRootImpl mViewRootImpl;
+    private Context mContext;
+    private volatile boolean mKeyReceived = false;
 
     @Before
     public void setUp() throws Exception {
-        final Context context = getInstrumentation().getTargetContext();
+        mContext = getInstrumentation().getTargetContext();
 
         getInstrumentation().runOnMainSync(() ->
-                mViewRootImpl = new ViewRootImpl(context, context.getDisplayNoVerify()));
+                mViewRootImpl = new ViewRootImpl(mContext, mContext.getDisplayNoVerify()));
     }
 
     @Test
@@ -153,7 +159,7 @@
     public void adjustLayoutParamsForCompatibility_noAdjustAppearance() {
         final WindowInsetsController controller = mViewRootImpl.getInsetsController();
         final WindowManager.LayoutParams attrs = mViewRootImpl.mWindowAttributes;
-        final int appearance = 0;
+        final int appearance = APPEARANCE_OPAQUE_STATUS_BARS;
         controller.setSystemBarsAppearance(appearance, 0xffffffff);
         attrs.systemUiVisibility = SYSTEM_UI_FLAG_LOW_PROFILE
                 | SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
@@ -163,13 +169,18 @@
         // Appearance must not be adjusted due to legacy system UI visibility after calling
         // setSystemBarsAppearance.
         assertEquals(appearance, controller.getSystemBarsAppearance());
+
+        mViewRootImpl.setLayoutParams(new WindowManager.LayoutParams(), false);
+
+        // Appearance must not be adjusted due to setting new LayoutParams.
+        assertEquals(appearance, controller.getSystemBarsAppearance());
     }
 
     @Test
     public void adjustLayoutParamsForCompatibility_noAdjustBehavior() {
         final WindowInsetsController controller = mViewRootImpl.getInsetsController();
         final WindowManager.LayoutParams attrs = mViewRootImpl.mWindowAttributes;
-        final int behavior = BEHAVIOR_SHOW_BARS_BY_TOUCH;
+        final int behavior = BEHAVIOR_SHOW_BARS_BY_SWIPE;
         controller.setSystemBarsBehavior(behavior);
         attrs.systemUiVisibility = SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
         ViewRootImpl.adjustLayoutParamsForCompatibility(attrs);
@@ -177,5 +188,92 @@
         // Behavior must not be adjusted due to legacy system UI visibility after calling
         // setSystemBarsBehavior.
         assertEquals(behavior, controller.getSystemBarsBehavior());
+
+        mViewRootImpl.setLayoutParams(new WindowManager.LayoutParams(), false);
+
+        // Behavior must not be adjusted due to setting new LayoutParams.
+        assertEquals(behavior, controller.getSystemBarsBehavior());
+    }
+
+    /**
+     * When window doesn't have focus, keys should be dropped.
+     */
+    @Test
+    public void whenWindowDoesNotHaveFocus_keysAreDropped() {
+        checkKeyEvent(() -> {
+            mViewRootImpl.windowFocusChanged(false /*hasFocus*/, true /*inTouchMode*/);
+        }, false /*shouldReceiveKey*/);
+    }
+
+    /**
+     * When window has focus, keys should be received
+     */
+    @Test
+    public void whenWindowHasFocus_keysAreReceived() {
+        checkKeyEvent(() -> {
+            mViewRootImpl.windowFocusChanged(true /*hasFocus*/, true /*inTouchMode*/);
+        }, true /*shouldReceiveKey*/);
+    }
+
+    /**
+     * When window is in ambient mode, keys should be dropped
+     */
+    @Test
+    public void whenWindowIsInAmbientMode_keysAreDropped() {
+        checkKeyEvent(() -> {
+            mViewRootImpl.setIsAmbientMode(true /*ambient*/);
+        }, false /*shouldReceiveKey*/);
+    }
+
+    /**
+     * When window is paused for transition, keys should be dropped
+     */
+    @Test
+    public void whenWindowIsPausedForTransition_keysAreDropped() {
+        checkKeyEvent(() -> {
+            mViewRootImpl.setPausedForTransition(true /*paused*/);
+        }, false /*shouldReceiveKey*/);
+    }
+
+    class KeyView extends View {
+        KeyView(Context context) {
+            super(context);
+        }
+
+        @Override
+        public boolean dispatchKeyEventPreIme(KeyEvent event) {
+            mKeyReceived = true;
+            return true /*handled*/;
+        }
+    }
+
+    /**
+     * Create a new view, and add it to window manager.
+     * Run the precondition 'setup'.
+     * Next, inject an event into this view, and check whether it is received.
+     */
+    private void checkKeyEvent(Runnable setup, boolean shouldReceiveKey) {
+        final KeyView view = new KeyView(mContext);
+
+        WindowManager.LayoutParams wmlp = new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY);
+        wmlp.token = new Binder(); // Set a fake token to bypass 'is your activity running' check
+
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+            WindowManager wm = mContext.getSystemService(WindowManager.class);
+            wm.addView(view, wmlp);
+        });
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+        mViewRootImpl = view.getViewRootImpl();
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(setup);
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+
+        // Inject a key event, and wait for it to be processed
+        InstrumentationRegistry.getInstrumentation().runOnMainSync(() -> {
+            KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_A);
+            mViewRootImpl.dispatchInputEvent(event);
+        });
+        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
+        assertEquals(mKeyReceived, shouldReceiveKey);
     }
 }
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java
index 75116d8..115c266 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityManagerTest.java
@@ -64,7 +64,8 @@
     private static final String INTENT_ACTION = "TESTACTION";
     private static final String DESCRIPTION = "description";
     private static final PendingIntent TEST_PENDING_INTENT = PendingIntent.getBroadcast(
-            InstrumentationRegistry.getTargetContext(), 0, new Intent(INTENT_ACTION), 0);
+            InstrumentationRegistry.getTargetContext(), 0, new Intent(INTENT_ACTION), 
+            PendingIntent.FLAG_IMMUTABLE);
     private static final RemoteAction TEST_ACTION = new RemoteAction(
             Icon.createWithContentUri("content://test"),
             LABEL,
diff --git a/core/tests/coretests/src/android/view/autofill/AutofillValueTest.java b/core/tests/coretests/src/android/view/autofill/AutofillValueTest.java
index 52373f0..7f24b0d 100644
--- a/core/tests/coretests/src/android/view/autofill/AutofillValueTest.java
+++ b/core/tests/coretests/src/android/view/autofill/AutofillValueTest.java
@@ -18,7 +18,6 @@
 
 import static com.google.common.truth.Truth.assertThat;
 
-import android.content.ClipData;
 import android.os.Parcel;
 
 import org.junit.Test;
@@ -39,20 +38,4 @@
         assertThat(result.isText()).isTrue();
         assertThat(result.getTextValue()).isEqualTo("hello");
     }
-
-    @Test
-    public void testWriteToParcel_richContent() throws Exception {
-        ClipData clip = ClipData.newPlainText("my label", "hello");
-        AutofillValue value = AutofillValue.forRichContent(clip);
-        Parcel parcel = Parcel.obtain();
-        value.writeToParcel(parcel, 0);
-        parcel.setDataPosition(0);
-
-        AutofillValue result = AutofillValue.CREATOR.createFromParcel(parcel);
-        assertThat(result.isRichContent()).isTrue();
-        ClipData resultClip = result.getRichContentValue();
-        assertThat(resultClip.getDescription().getLabel()).isEqualTo("my label");
-        assertThat(resultClip.getItemAt(0).getText()).isEqualTo("hello");
-        assertThat(resultClip.getDescription().getMimeType(0)).isEqualTo("text/plain");
-    }
 }
diff --git a/core/tests/coretests/src/android/view/inputmethod/EditorInfoTest.java b/core/tests/coretests/src/android/view/inputmethod/EditorInfoTest.java
index 92fb528..c636912 100644
--- a/core/tests/coretests/src/android/view/inputmethod/EditorInfoTest.java
+++ b/core/tests/coretests/src/android/view/inputmethod/EditorInfoTest.java
@@ -109,11 +109,15 @@
         editorInfo.initialSelEnd = editorInfo.initialSelStart + selectionLength;
         final int expectedTextBeforeCursorLength = 0;
         final int expectedTextAfterCursorLength = testText.length();
+        final SurroundingText expectedSurroundingText =
+                new SurroundingText(testText, editorInfo.initialSelStart,
+                        editorInfo.initialSelEnd, 0);
+
 
         editorInfo.setInitialSurroundingText(testText);
 
         assertExpectedTextLength(editorInfo, expectedTextBeforeCursorLength, selectionLength,
-                expectedTextAfterCursorLength);
+                expectedTextAfterCursorLength, expectedSurroundingText);
     }
 
     @Test
@@ -125,11 +129,14 @@
         editorInfo.initialSelEnd = testText.length();
         final int expectedTextBeforeCursorLength = testText.length();
         final int expectedTextAfterCursorLength = 0;
+        final SurroundingText expectedSurroundingText =
+                new SurroundingText(testText, editorInfo.initialSelStart,
+                        editorInfo.initialSelEnd, 0);
 
         editorInfo.setInitialSurroundingText(testText);
 
         assertExpectedTextLength(editorInfo, expectedTextBeforeCursorLength, selectionLength,
-                expectedTextAfterCursorLength);
+                expectedTextAfterCursorLength, expectedSurroundingText);
     }
 
     @Test
@@ -141,11 +148,14 @@
         editorInfo.initialSelEnd = editorInfo.initialSelStart + selectionLength;
         final int expectedTextBeforeCursorLength = editorInfo.initialSelStart;
         final int expectedTextAfterCursorLength = testText.length() - editorInfo.initialSelEnd;
+        final SurroundingText expectedSurroundingText =
+                new SurroundingText(testText, editorInfo.initialSelStart,
+                        editorInfo.initialSelEnd, 0);
 
         editorInfo.setInitialSurroundingText(testText);
 
         assertExpectedTextLength(editorInfo, expectedTextBeforeCursorLength, selectionLength,
-                expectedTextAfterCursorLength);
+                expectedTextAfterCursorLength, expectedSurroundingText);
     }
 
     @Test
@@ -158,11 +168,14 @@
         final int expectedTextBeforeCursorLength = testText.length() / 2;
         final int expectedTextAfterCursorLength = testText.length() - testText.length() / 2
                 - selectionLength;
-
+        final SurroundingText expectedSurroundingText =
+                new SurroundingText(testText,
+                        editorInfo.initialSelEnd,
+                        editorInfo.initialSelStart , 0);
         editorInfo.setInitialSurroundingText(testText);
 
         assertExpectedTextLength(editorInfo, expectedTextBeforeCursorLength, selectionLength,
-                expectedTextAfterCursorLength);
+                expectedTextAfterCursorLength, expectedSurroundingText);
     }
 
     @Test
@@ -174,9 +187,10 @@
         editorInfo.setInitialSurroundingText(testText);
 
         assertExpectedTextLength(editorInfo,
-                /* expectBeforeCursorLength= */null,
-                /* expectSelectionLength= */null,
-                /* expectAfterCursorLength= */null);
+                /* expectBeforeCursorLength= */ null,
+                /* expectSelectionLength= */ null,
+                /* expectAfterCursorLength= */   null,
+                /* expectSurroundingText= */ null);
     }
 
     @Test
@@ -190,11 +204,23 @@
                 (int) (0.8 * (EditorInfo.MEMORY_EFFICIENT_TEXT_LENGTH - selectionLength)));
         final int expectedTextAfterCursorLength = EditorInfo.MEMORY_EFFICIENT_TEXT_LENGTH
                 - expectedTextBeforeCursorLength - selectionLength;
+        final int offset = editorInfo.initialSelStart - expectedTextBeforeCursorLength;
+        final CharSequence beforeCursor = testText.subSequence(
+                offset, offset + expectedTextBeforeCursorLength);
+        final CharSequence afterCursor = testText.subSequence(editorInfo.initialSelEnd,
+                editorInfo.initialSelEnd + expectedTextAfterCursorLength);
+        final CharSequence selectedText = testText.subSequence(editorInfo.initialSelStart,
+                editorInfo.initialSelEnd);
+
+        final SurroundingText expectedSurroundingText =
+                new SurroundingText(TextUtils.concat(beforeCursor, selectedText, afterCursor),
+                        expectedTextBeforeCursorLength,
+                        expectedTextBeforeCursorLength + selectionLength, offset);
 
         editorInfo.setInitialSurroundingText(testText);
 
         assertExpectedTextLength(editorInfo, expectedTextBeforeCursorLength, selectionLength,
-                expectedTextAfterCursorLength);
+                expectedTextAfterCursorLength, expectedSurroundingText);
     }
 
     @Test
@@ -207,11 +233,22 @@
         final int expectedTextBeforeCursorLength = Math.min(editorInfo.initialSelStart,
                 (int) (0.8 * EditorInfo.MEMORY_EFFICIENT_TEXT_LENGTH));
         final int expectedTextAfterCursorLength = testText.length() - editorInfo.initialSelEnd;
+        final CharSequence before = testText.subSequence(
+                editorInfo.initialSelStart  - expectedTextBeforeCursorLength,
+                expectedTextBeforeCursorLength);
+        final CharSequence after = testText.subSequence(editorInfo.initialSelEnd,
+                editorInfo.initialSelEnd + expectedTextAfterCursorLength);
+        final SurroundingText expectedSurroundingText =
+                new SurroundingText(TextUtils.concat(before, after),
+                        expectedTextBeforeCursorLength,
+                        expectedTextBeforeCursorLength,
+                        editorInfo.initialSelStart - expectedTextBeforeCursorLength);
 
         editorInfo.setInitialSurroundingText(testText);
 
         assertExpectedTextLength(editorInfo, expectedTextBeforeCursorLength,
-                /* expectSelectionLength= */null, expectedTextAfterCursorLength);
+                /* expectSelectionLength= */null, expectedTextAfterCursorLength,
+                expectedSurroundingText);
     }
 
     @Test
@@ -269,6 +306,19 @@
                         InputConnection.GET_TEXT_WITH_STYLES),
                 targetEditorInfo.getInitialTextAfterCursor(LONG_EXP_TEXT_LENGTH,
                         InputConnection.GET_TEXT_WITH_STYLES)));
+
+        final SurroundingText sourceSurroundingText = sourceEditorInfo.getInitialSurroundingText(
+                LONG_EXP_TEXT_LENGTH, LONG_EXP_TEXT_LENGTH, InputConnection.GET_TEXT_WITH_STYLES);
+        final SurroundingText targetSurroundingText = targetEditorInfo.getInitialSurroundingText(
+                LONG_EXP_TEXT_LENGTH, LONG_EXP_TEXT_LENGTH, InputConnection.GET_TEXT_WITH_STYLES);
+
+        assertTrue(TextUtils.equals(sourceSurroundingText.getText(),
+                targetSurroundingText.getText()));
+        assertEquals(sourceSurroundingText.getSelectionStart(),
+                targetSurroundingText.getSelectionStart());
+        assertEquals(sourceSurroundingText.getSelectionEnd(),
+                targetSurroundingText.getSelectionEnd());
+        assertEquals(sourceSurroundingText.getOffset(), targetSurroundingText.getOffset());
     }
 
     @Test
@@ -338,7 +388,8 @@
 
     private static void assertExpectedTextLength(EditorInfo editorInfo,
             @Nullable Integer expectBeforeCursorLength, @Nullable Integer expectSelectionLength,
-            @Nullable Integer expectAfterCursorLength) {
+            @Nullable Integer expectAfterCursorLength,
+            @Nullable SurroundingText expectSurroundingText) {
         final CharSequence textBeforeCursor =
                 editorInfo.getInitialTextBeforeCursor(LONG_EXP_TEXT_LENGTH,
                         InputConnection.GET_TEXT_WITH_STYLES);
@@ -347,6 +398,10 @@
         final CharSequence textAfterCursor =
                 editorInfo.getInitialTextAfterCursor(LONG_EXP_TEXT_LENGTH,
                         InputConnection.GET_TEXT_WITH_STYLES);
+        final SurroundingText surroundingText = editorInfo.getInitialSurroundingText(
+                LONG_EXP_TEXT_LENGTH,
+                LONG_EXP_TEXT_LENGTH,
+                InputConnection.GET_TEXT_WITH_STYLES);
 
         if (expectBeforeCursorLength == null) {
             assertNull(textBeforeCursor);
@@ -365,6 +420,18 @@
         } else {
             assertEquals(expectAfterCursorLength.intValue(), textAfterCursor.length());
         }
+
+        if (expectSurroundingText == null) {
+            assertNull(surroundingText);
+        } else {
+            assertTrue(TextUtils.equals(
+                    expectSurroundingText.getText(), surroundingText.getText()));
+            assertEquals(expectSurroundingText.getSelectionStart(),
+                    surroundingText.getSelectionStart());
+            assertEquals(expectSurroundingText.getSelectionEnd(),
+                    surroundingText.getSelectionEnd());
+            assertEquals(expectSurroundingText.getOffset(), surroundingText.getOffset());
+        }
     }
 
     private static CharSequence createTestText(int surroundingLength) {
diff --git a/core/tests/coretests/src/android/view/inputmethod/OWNERS b/core/tests/coretests/src/android/view/inputmethod/OWNERS
new file mode 100644
index 0000000..eb06b78
--- /dev/null
+++ b/core/tests/coretests/src/android/view/inputmethod/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 34867
+
+include /core/java/android/view/inputmethod/OWNERS
diff --git a/core/tests/coretests/src/android/widget/TextViewTest.java b/core/tests/coretests/src/android/widget/TextViewTest.java
index 66fdfff..a4284a0 100644
--- a/core/tests/coretests/src/android/widget/TextViewTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewTest.java
@@ -276,6 +276,34 @@
                 0, mTextView.getImeOptions() & EditorInfo.IME_FLAG_NO_FULLSCREEN);
     }
 
+    @Test
+    @UiThreadTest
+    public void setSetImeTemporarilyConsumesInput_recoveryToVisible() {
+        mTextView = new TextView(mActivity);
+        mTextView.setCursorVisible(true);
+        assertTrue(mTextView.isCursorVisible());
+
+        mTextView.setImeTemporarilyConsumesInput(true);
+        assertFalse(mTextView.isCursorVisible());
+
+        mTextView.setImeTemporarilyConsumesInput(false);
+        assertTrue(mTextView.isCursorVisible());
+    }
+
+    @Test
+    @UiThreadTest
+    public void setSetImeTemporarilyConsumesInput_recoveryToInvisible() {
+        mTextView = new TextView(mActivity);
+        mTextView.setCursorVisible(false);
+        assertFalse(mTextView.isCursorVisible());
+
+        mTextView.setImeTemporarilyConsumesInput(true);
+        assertFalse(mTextView.isCursorVisible());
+
+        mTextView.setImeTemporarilyConsumesInput(false);
+        assertFalse(mTextView.isCursorVisible());
+    }
+
     private String createLongText() {
         int size = 600 * 1000;
         final StringBuilder builder = new StringBuilder(size);
diff --git a/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java b/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java
index a74f580..d2b20b4 100644
--- a/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java
+++ b/core/tests/mockingcoretests/src/android/app/activity/ActivityThreadClientTest.java
@@ -242,7 +242,7 @@
         }
 
         private void startActivity(ActivityClientRecord r) {
-            mThread.handleStartActivity(r, null /* pendingActions */);
+            mThread.handleStartActivity(r, null /* pendingActions */, null /* activityOptions */);
         }
 
         private void resumeActivity(ActivityClientRecord r) {
@@ -295,8 +295,9 @@
                     0 /* ident */, info, new Configuration(),
                     CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO, null /* referrer */,
                     null /* voiceInteractor */, null /* state */, null /* persistentState */,
-                    null /* pendingResults */, null /* pendingNewIntents */, true /* isForward */,
-                    null /* profilerInfo */,  mThread /* client */, null /* asssitToken */,
+                    null /* pendingResults */, null /* pendingNewIntents */,
+                    null /* activityOptions */, true /* isForward */, null /* profilerInfo */,
+                    mThread /* client */, null /* asssitToken */,
                     null /* fixedRotationAdjustments */);
         }
 
diff --git a/core/tests/overlaytests/device/Android.bp b/core/tests/overlaytests/device/Android.bp
index 12a2b08..f86ac9c 100644
--- a/core/tests/overlaytests/device/Android.bp
+++ b/core/tests/overlaytests/device/Android.bp
@@ -16,7 +16,11 @@
     name: "OverlayDeviceTests",
     srcs: ["src/**/*.java"],
     platform_apis: true,
-    static_libs: ["androidx.test.rules"],
+    certificate: "platform",
+    static_libs: [
+        "androidx.test.rules",
+        "testng",
+    ],
     test_suites: ["device-tests"],
     data: [
         ":OverlayDeviceTests_AppOverlayOne",
diff --git a/core/tests/overlaytests/device/AndroidManifest.xml b/core/tests/overlaytests/device/AndroidManifest.xml
index 4881636..a69911f 100644
--- a/core/tests/overlaytests/device/AndroidManifest.xml
+++ b/core/tests/overlaytests/device/AndroidManifest.xml
@@ -19,6 +19,8 @@
 
     <uses-sdk android:minSdkVersion="21" />
 
+    <uses-permission android:name="android.permission.CHANGE_OVERLAY_PACKAGES" />
+
     <application>
         <uses-library android:name="android.test.runner"/>
     </application>
diff --git a/core/tests/overlaytests/device/AndroidTest.xml b/core/tests/overlaytests/device/AndroidTest.xml
index 6507839..db45750 100644
--- a/core/tests/overlaytests/device/AndroidTest.xml
+++ b/core/tests/overlaytests/device/AndroidTest.xml
@@ -19,9 +19,20 @@
     <option name="test-suite-tag" value="apct" />
     <option name="test-suite-tag" value="apct-instrumentation" />
 
+    <target_preparer class="com.android.compatibility.common.tradefed.targetprep.FilePusher">
+        <option name="cleanup" value="true" />
+        <option name="remount-system" value="true" />
+        <option name="push" value="OverlayDeviceTests.apk->/system/app/OverlayDeviceTests.apk" />
+    </target_preparer>
+  
+    <!-- Reboot to have the test APK scanned by PM and reboot after to remove the test APK. -->
+    <target_preparer class="com.android.tradefed.targetprep.RebootTargetPreparer">
+      <option name="pre-reboot" value="true" />
+      <option name="post-reboot" value="true" />
+    </target_preparer>
+
     <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
         <option name="cleanup-apks" value="true" />
-        <option name="test-file-name" value="OverlayDeviceTests.apk" />
         <option name="test-file-name" value="OverlayDeviceTests_AppOverlayOne.apk" />
         <option name="test-file-name" value="OverlayDeviceTests_AppOverlayTwo.apk" />
         <option name="test-file-name" value="OverlayDeviceTests_FrameworkOverlay.apk" />
diff --git a/core/tests/overlaytests/device/TEST_MAPPING b/core/tests/overlaytests/device/TEST_MAPPING
new file mode 100644
index 0000000..43ee00f
--- /dev/null
+++ b/core/tests/overlaytests/device/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name" : "OverlayDeviceTests"
+    }
+  ]
+}
diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/LocalOverlayManager.java b/core/tests/overlaytests/device/src/com/android/overlaytest/LocalOverlayManager.java
index 390bb76..76c01a7 100644
--- a/core/tests/overlaytests/device/src/com/android/overlaytest/LocalOverlayManager.java
+++ b/core/tests/overlaytests/device/src/com/android/overlaytest/LocalOverlayManager.java
@@ -18,60 +18,76 @@
 
 import static java.util.concurrent.TimeUnit.SECONDS;
 
-import android.app.UiAutomation;
-import android.content.res.Resources;
-import android.os.ParcelFileDescriptor;
+import android.annotation.NonNull;
+import android.content.Context;
+import android.content.om.OverlayManager;
+import android.content.om.OverlayManagerTransaction;
+import android.os.UserHandle;
 
 import androidx.test.InstrumentationRegistry;
 
-import java.io.BufferedReader;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.nio.charset.StandardCharsets;
-import java.util.Arrays;
 import java.util.concurrent.Executor;
 import java.util.concurrent.FutureTask;
 
 class LocalOverlayManager {
     private static final long TIMEOUT = 30;
 
-    public static void setEnabledAndWait(Executor executor, final String packageName,
-            boolean enable) throws Exception {
-        final String pattern = (enable ? "[x]" : "[ ]") + " " + packageName;
-        if (executeShellCommand("cmd overlay list").contains(pattern)) {
-            // nothing to do, overlay already in the requested state
-            return;
+    public static void toggleOverlaysAndWait(@NonNull final String[] overlaysToEnable,
+            @NonNull final String[] overlaysToDisable) throws Exception {
+        final int userId = UserHandle.myUserId();
+        OverlayManagerTransaction.Builder builder = new OverlayManagerTransaction.Builder();
+        for (String pkg : overlaysToEnable) {
+            builder.setEnabled(pkg, true, userId);
         }
+        for (String pkg : overlaysToDisable) {
+            builder.setEnabled(pkg, false, userId);
+        }
+        OverlayManagerTransaction transaction = builder.build();
 
-        final Resources res = InstrumentationRegistry.getContext().getResources();
-        final String[] oldApkPaths = res.getAssets().getApkPaths();
+        final Context ctx = InstrumentationRegistry.getTargetContext();
         FutureTask<Boolean> task = new FutureTask<>(() -> {
             while (true) {
-                if (!Arrays.equals(oldApkPaths, res.getAssets().getApkPaths())) {
+                final String[] paths = ctx.getResources().getAssets().getApkPaths();
+                if (arrayTailContains(paths, overlaysToEnable)
+                        && arrayDoesNotContain(paths, overlaysToDisable)) {
                     return true;
                 }
                 Thread.sleep(10);
             }
         });
+
+        OverlayManager om = ctx.getSystemService(OverlayManager.class);
+        om.commit(transaction);
+
+        Executor executor = (cmd) -> new Thread(cmd).start();
         executor.execute(task);
-        executeShellCommand("cmd overlay " + (enable ? "enable " : "disable ") + packageName);
         task.get(TIMEOUT, SECONDS);
     }
 
-    private static String executeShellCommand(final String command)
-            throws Exception {
-        final UiAutomation uiAutomation =
-                InstrumentationRegistry.getInstrumentation().getUiAutomation();
-        final ParcelFileDescriptor pfd = uiAutomation.executeShellCommand(command);
-        try (InputStream in = new ParcelFileDescriptor.AutoCloseInputStream(pfd)) {
-            final BufferedReader reader = new BufferedReader(
-                    new InputStreamReader(in, StandardCharsets.UTF_8));
-            StringBuilder str = new StringBuilder();
-            String line;
-            while ((line = reader.readLine()) != null) {
-                str.append(line);
-            }
-            return str.toString();
+    private static boolean arrayTailContains(@NonNull final String[] array,
+            @NonNull final String[] substrings) {
+        if (array.length < substrings.length) {
+            return false;
         }
+        for (int i = 0; i < substrings.length; i++) {
+            String a = array[array.length - substrings.length + i];
+            String s = substrings[i];
+            if (!a.contains(s)) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private static boolean arrayDoesNotContain(@NonNull final String[] array,
+            @NonNull final String[] substrings) {
+        for (String s : substrings) {
+            for (String a : array) {
+                if (a.contains(s)) {
+                    return false;
+                }
+            }
+        }
+        return true;
     }
 }
diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/TransactionTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/TransactionTest.java
new file mode 100644
index 0000000..0b4f5e2
--- /dev/null
+++ b/core/tests/overlaytests/device/src/com/android/overlaytest/TransactionTest.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.overlaytest;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.testng.Assert.assertThrows;
+
+import android.content.Context;
+import android.content.om.OverlayInfo;
+import android.content.om.OverlayManager;
+import android.content.om.OverlayManagerTransaction;
+import android.content.res.Resources;
+import android.os.UserHandle;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.MediumTest;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.util.List;
+
+@RunWith(JUnit4.class)
+@MediumTest
+public class TransactionTest {
+    static final String APP_OVERLAY_ONE_PKG = "com.android.overlaytest.app_overlay_one";
+    static final String APP_OVERLAY_TWO_PKG = "com.android.overlaytest.app_overlay_two";
+
+    private Context mContext;
+    private Resources mResources;
+    private OverlayManager mOverlayManager;
+    private int mUserId;
+    private UserHandle mUserHandle;
+
+    @Before
+    public void setUp() throws Exception {
+        mContext = InstrumentationRegistry.getContext();
+        mResources = mContext.getResources();
+        mOverlayManager = mContext.getSystemService(OverlayManager.class);
+        mUserId = UserHandle.myUserId();
+        mUserHandle = UserHandle.of(mUserId);
+
+        LocalOverlayManager.toggleOverlaysAndWait(
+                new String[]{},
+                new String[]{APP_OVERLAY_ONE_PKG, APP_OVERLAY_TWO_PKG});
+    }
+
+    @Test
+    public void testValidTransaction() throws Exception {
+        assertOverlayIsEnabled(APP_OVERLAY_ONE_PKG, false, mUserId);
+        assertOverlayIsEnabled(APP_OVERLAY_TWO_PKG, false, mUserId);
+
+        OverlayManagerTransaction t = new OverlayManagerTransaction.Builder()
+                .setEnabled(APP_OVERLAY_ONE_PKG, true)
+                .setEnabled(APP_OVERLAY_TWO_PKG, true)
+                .build();
+        mOverlayManager.commit(t);
+
+        assertOverlayIsEnabled(APP_OVERLAY_ONE_PKG, true, mUserId);
+        assertOverlayIsEnabled(APP_OVERLAY_TWO_PKG, true, mUserId);
+        List<OverlayInfo> ois =
+                mOverlayManager.getOverlayInfosForTarget("com.android.overlaytest", mUserHandle);
+        assertEquals(ois.size(), 2);
+        assertEquals(ois.get(0).packageName, APP_OVERLAY_ONE_PKG);
+        assertEquals(ois.get(1).packageName, APP_OVERLAY_TWO_PKG);
+
+        OverlayManagerTransaction t2 = new OverlayManagerTransaction.Builder()
+                .setEnabled(APP_OVERLAY_TWO_PKG, true)
+                .setEnabled(APP_OVERLAY_ONE_PKG, true)
+                .build();
+        mOverlayManager.commit(t2);
+
+        assertOverlayIsEnabled(APP_OVERLAY_ONE_PKG, true, mUserId);
+        assertOverlayIsEnabled(APP_OVERLAY_TWO_PKG, true, mUserId);
+        List<OverlayInfo> ois2 =
+                mOverlayManager.getOverlayInfosForTarget("com.android.overlaytest", mUserHandle);
+        assertEquals(ois2.size(), 2);
+        assertEquals(ois2.get(0).packageName, APP_OVERLAY_TWO_PKG);
+        assertEquals(ois2.get(1).packageName, APP_OVERLAY_ONE_PKG);
+
+        OverlayManagerTransaction t3 = new OverlayManagerTransaction.Builder()
+                .setEnabled(APP_OVERLAY_TWO_PKG, false)
+                .build();
+        mOverlayManager.commit(t3);
+
+        assertOverlayIsEnabled(APP_OVERLAY_ONE_PKG, true, mUserId);
+        assertOverlayIsEnabled(APP_OVERLAY_TWO_PKG, false, mUserId);
+        List<OverlayInfo> ois3 =
+                mOverlayManager.getOverlayInfosForTarget("com.android.overlaytest", mUserHandle);
+        assertEquals(ois3.size(), 2);
+        assertEquals(ois3.get(0).packageName, APP_OVERLAY_TWO_PKG);
+        assertEquals(ois3.get(1).packageName, APP_OVERLAY_ONE_PKG);
+    }
+
+    @Test
+    public void testInvalidRequestHasNoEffect() {
+        assertOverlayIsEnabled(APP_OVERLAY_ONE_PKG, false, mUserId);
+        assertOverlayIsEnabled(APP_OVERLAY_TWO_PKG, false, mUserId);
+
+        OverlayManagerTransaction t = new OverlayManagerTransaction.Builder()
+                .setEnabled(APP_OVERLAY_ONE_PKG, true)
+                .setEnabled("does-not-exist", true)
+                .setEnabled(APP_OVERLAY_TWO_PKG, true)
+                .build();
+        assertThrows(SecurityException.class, () -> mOverlayManager.commit(t));
+
+        assertOverlayIsEnabled(APP_OVERLAY_ONE_PKG, false, mUserId);
+        assertOverlayIsEnabled(APP_OVERLAY_TWO_PKG, false, mUserId);
+    }
+
+    private void assertOverlayIsEnabled(final String packageName, boolean enabled, int userId) {
+        final OverlayInfo oi = mOverlayManager.getOverlayInfo(packageName, UserHandle.of(userId));
+        assertNotNull(oi);
+        assertEquals(oi.isEnabled(), enabled);
+    }
+}
diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/WithMultipleOverlaysTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/WithMultipleOverlaysTest.java
index d28c47d..420f755 100644
--- a/core/tests/overlaytests/device/src/com/android/overlaytest/WithMultipleOverlaysTest.java
+++ b/core/tests/overlaytests/device/src/com/android/overlaytest/WithMultipleOverlaysTest.java
@@ -22,8 +22,6 @@
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
-import java.util.concurrent.Executor;
-
 @RunWith(JUnit4.class)
 @MediumTest
 public class WithMultipleOverlaysTest extends OverlayBaseTest {
@@ -33,9 +31,8 @@
 
     @BeforeClass
     public static void enableOverlay() throws Exception {
-        Executor executor = (cmd) -> new Thread(cmd).start();
-        LocalOverlayManager.setEnabledAndWait(executor, APP_OVERLAY_ONE_PKG, true);
-        LocalOverlayManager.setEnabledAndWait(executor, APP_OVERLAY_TWO_PKG, true);
-        LocalOverlayManager.setEnabledAndWait(executor, FRAMEWORK_OVERLAY_PKG, true);
+        LocalOverlayManager.toggleOverlaysAndWait(
+                new String[]{FRAMEWORK_OVERLAY_PKG, APP_OVERLAY_ONE_PKG, APP_OVERLAY_TWO_PKG},
+                new String[]{});
     }
 }
diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/WithOverlayTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/WithOverlayTest.java
index 6566ad3..a86255e 100644
--- a/core/tests/overlaytests/device/src/com/android/overlaytest/WithOverlayTest.java
+++ b/core/tests/overlaytests/device/src/com/android/overlaytest/WithOverlayTest.java
@@ -22,8 +22,6 @@
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
-import java.util.concurrent.Executor;
-
 @RunWith(JUnit4.class)
 @MediumTest
 public class WithOverlayTest extends OverlayBaseTest {
@@ -32,10 +30,9 @@
     }
 
     @BeforeClass
-    public static void enableOverlay() throws Exception {
-        Executor executor = (cmd) -> new Thread(cmd).start();
-        LocalOverlayManager.setEnabledAndWait(executor, APP_OVERLAY_ONE_PKG, true);
-        LocalOverlayManager.setEnabledAndWait(executor, APP_OVERLAY_TWO_PKG, false);
-        LocalOverlayManager.setEnabledAndWait(executor, FRAMEWORK_OVERLAY_PKG, true);
+    public static void enableOverlays() throws Exception {
+        LocalOverlayManager.toggleOverlaysAndWait(
+                new String[]{FRAMEWORK_OVERLAY_PKG, APP_OVERLAY_ONE_PKG},
+                new String[]{APP_OVERLAY_TWO_PKG});
     }
 }
diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/WithoutOverlayTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/WithoutOverlayTest.java
index 48cfeab..51c4118 100644
--- a/core/tests/overlaytests/device/src/com/android/overlaytest/WithoutOverlayTest.java
+++ b/core/tests/overlaytests/device/src/com/android/overlaytest/WithoutOverlayTest.java
@@ -22,8 +22,6 @@
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
-import java.util.concurrent.Executor;
-
 @RunWith(JUnit4.class)
 @MediumTest
 public class WithoutOverlayTest extends OverlayBaseTest {
@@ -33,9 +31,8 @@
 
     @BeforeClass
     public static void disableOverlays() throws Exception {
-        Executor executor = (cmd) -> new Thread(cmd).start();
-        LocalOverlayManager.setEnabledAndWait(executor, APP_OVERLAY_ONE_PKG, false);
-        LocalOverlayManager.setEnabledAndWait(executor, APP_OVERLAY_TWO_PKG, false);
-        LocalOverlayManager.setEnabledAndWait(executor, FRAMEWORK_OVERLAY_PKG, false);
+        LocalOverlayManager.toggleOverlaysAndWait(
+                new String[]{},
+                new String[]{FRAMEWORK_OVERLAY_PKG, APP_OVERLAY_ONE_PKG, APP_OVERLAY_TWO_PKG});
     }
 }
diff --git a/core/tests/overlaytests/device/test-apps/AppOverlayOne/Android.bp b/core/tests/overlaytests/device/test-apps/AppOverlayOne/Android.bp
index da3aa00..847b491 100644
--- a/core/tests/overlaytests/device/test-apps/AppOverlayOne/Android.bp
+++ b/core/tests/overlaytests/device/test-apps/AppOverlayOne/Android.bp
@@ -15,6 +15,6 @@
 android_test {
     name: "OverlayDeviceTests_AppOverlayOne",
     sdk_version: "current",
-
+    certificate: "platform",
     aaptflags: ["--no-resource-removal"],
 }
diff --git a/core/tests/overlaytests/device/test-apps/AppOverlayTwo/Android.bp b/core/tests/overlaytests/device/test-apps/AppOverlayTwo/Android.bp
index 215b66da3..7d5f82a 100644
--- a/core/tests/overlaytests/device/test-apps/AppOverlayTwo/Android.bp
+++ b/core/tests/overlaytests/device/test-apps/AppOverlayTwo/Android.bp
@@ -15,6 +15,6 @@
 android_test {
     name: "OverlayDeviceTests_AppOverlayTwo",
     sdk_version: "current",
-
+    certificate: "platform",
     aaptflags: ["--no-resource-removal"],
 }
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 2e12795..eb5e0a2 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -350,6 +350,8 @@
         <permission name="android.permission.MOUNT_FORMAT_FILESYSTEMS"/>
         <permission name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
         <permission name="android.permission.MOVE_PACKAGE"/>
+        <!-- Needed for test only -->
+        <permission name="android.permission.NETWORK_AIRPLANE_MODE"/>
         <permission name="android.permission.OBSERVE_APP_USAGE"/>
         <permission name="android.permission.NETWORK_SCAN"/>
         <permission name="android.permission.PACKAGE_USAGE_STATS" />
@@ -455,6 +457,11 @@
         <permission name="android.contacts.permission.MANAGE_SIM_ACCOUNTS" />
         <!-- Permissions required for CTS test - CtsHdmiCecHostTestCases -->
         <permission name="android.permission.HDMI_CEC"/>
+        <!-- Permission needed for CTS test - WifiManagerTest -->
+        <permission name="android.permission.WIFI_ACCESS_COEX_UNSAFE_CHANNELS" />
+        <permission name="android.permission.WIFI_UPDATE_COEX_UNSAFE_CHANNELS" />
+        <!-- Permission required for CTS test CarrierMessagingServiceWrapperTest -->
+        <permission name="android.permission.BIND_CARRIER_SERVICES"/>
     </privapp-permissions>
 
     <privapp-permissions package="com.android.statementservice">
@@ -464,6 +471,8 @@
     <privapp-permissions package="com.android.traceur">
         <!-- Permissions required to receive BUGREPORT_STARTED intent -->
         <permission name="android.permission.DUMP"/>
+        <!-- Permissions required to start/stop tracing -->
+        <permission name="android.permission.START_FOREGROUND_SERVICES_FROM_BACKGROUND"/>
         <!-- Permissions required for quick settings tile -->
         <permission name="android.permission.STATUS_BAR"/>
     </privapp-permissions>
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index faf4973..2d9aafb 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -49,12 +49,6 @@
       "group": "WM_ERROR",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
-    "-2062338592": {
-      "message": "Looking for task of %s",
-      "level": "DEBUG",
-      "group": "WM_DEBUG_TASKS",
-      "at": "com\/android\/server\/wm\/RootWindowContainer.java"
-    },
     "-2054442123": {
       "message": "Setting Intent of %s to %s",
       "level": "VERBOSE",
@@ -73,6 +67,12 @@
       "group": "WM_ERROR",
       "at": "com\/android\/server\/wm\/WindowManagerService.java"
     },
+    "-2036671725": {
+      "message": "      SKIP: is wallpaper",
+      "level": "VERBOSE",
+      "group": "WM_DEBUG_WINDOW_TRANSITIONS",
+      "at": "com\/android\/server\/wm\/Transition.java"
+    },
     "-2029985709": {
       "message": "setFocusedTask: taskId=%d",
       "level": "DEBUG",
@@ -475,6 +475,12 @@
       "group": "WM_DEBUG_WINDOW_TRANSITIONS",
       "at": "com\/android\/server\/wm\/Transition.java"
     },
+    "-1559645910": {
+      "message": "Looking for task of type=%s, taskAffinity=%s, intent=%s, info=%s, preferredTDA=%s",
+      "level": "DEBUG",
+      "group": "WM_DEBUG_TASKS",
+      "at": "com\/android\/server\/wm\/RootWindowContainer.java"
+    },
     "-1558137010": {
       "message": "Config is relaunching invisible activity %s called by %s",
       "level": "VERBOSE",
@@ -1531,6 +1537,12 @@
       "group": "WM_DEBUG_APP_TRANSITIONS",
       "at": "com\/android\/server\/wm\/ActivityRecord.java"
     },
+    "-373110070": {
+      "message": "Skipping task: (mismatch activity\/task) %s",
+      "level": "DEBUG",
+      "group": "WM_DEBUG_TASKS",
+      "at": "com\/android\/server\/wm\/RootWindowContainer.java"
+    },
     "-354571697": {
       "message": "Existence Changed in transition %d: %s",
       "level": "VERBOSE",
diff --git a/data/keyboards/Vendor_0957_Product_0001.idc b/data/keyboards/Vendor_0957_Product_0001.idc
new file mode 100644
index 0000000..e1f4346
--- /dev/null
+++ b/data/keyboards/Vendor_0957_Product_0001.idc
@@ -0,0 +1,23 @@
+# Copyright (C) 2021 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.
+
+#
+# Input Device Configuration file for Google Reference RCU Remote.
+#
+#
+
+# Basic Parameters
+keyboard.layout = Vendor_0957_Product_0001
+keyboard.characterMap = Vendor_0957_Product_0001
+audio.mic = 1
\ No newline at end of file
diff --git a/data/keyboards/Vendor_0957_Product_0001.kl b/data/keyboards/Vendor_0957_Product_0001.kl
new file mode 100644
index 0000000..e9f4f28
--- /dev/null
+++ b/data/keyboards/Vendor_0957_Product_0001.kl
@@ -0,0 +1,72 @@
+# Copyright (C) 2021 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.
+
+#
+# Key Layout file for Google Reference RCU Remote.
+#
+
+key 116   POWER         WAKE
+key 217   ASSIST        WAKE
+
+key 103   DPAD_UP
+key 108   DPAD_DOWN
+key 105   DPAD_LEFT
+key 106   DPAD_RIGHT
+key 353   DPAD_CENTER
+
+key 158   BACK
+key 172   HOME          WAKE
+
+key 113   VOLUME_MUTE
+key 114   VOLUME_DOWN
+key 115   VOLUME_UP
+
+key 2     1
+key 3     2
+key 4     3
+key 5     4
+key 6     5
+key 7     6
+key 8     7
+key 9     8
+key 10    9
+key 11    0
+
+# custom keys
+key usage 0x000c01BB    TV_INPUT
+key usage 0x000c022A    BOOKMARK
+key usage 0x000c0096    SETTINGS
+key usage 0x000c0097    NOTIFICATION
+key usage 0x000c008D    GUIDE
+key usage 0x000c0089    TV
+key usage 0x000c009C    CHANNEL_UP
+key usage 0x000c009D    CHANNEL_DOWN
+key usage 0x000c00CD    MEDIA_PLAY_PAUSE
+key usage 0x000c00B4    MEDIA_SKIP_BACKWARD
+key usage 0x000c00B3    MEDIA_SKIP_FORWARD
+key usage 0x000c0226    MEDIA_STOP
+
+key usage 0x000c0077    BUTTON_3     WAKE #YouTube
+key usage 0x000c0078    BUTTON_4     WAKE #Netflix
+key usage 0x000c0079    BUTTON_6     WAKE #Disney+
+key usage 0x000c007A    BUTTON_7     WAKE #HBOmax
+
+key usage 0x000c01BD    INFO
+key usage 0x000c0061    CAPTIONS
+key usage 0x000c0185    TV_TELETEXT
+
+key usage 0x000c0069    PROG_RED
+key usage 0x000c006A    PROG_GREEN
+key usage 0x000c006B    PROG_BLUE
+key usage 0x000c006C    PROG_YELLOW
\ No newline at end of file
diff --git a/framework-jarjar-rules.txt b/framework-jarjar-rules.txt
index d8af726..52ee63a 100644
--- a/framework-jarjar-rules.txt
+++ b/framework-jarjar-rules.txt
@@ -1,2 +1,3 @@
 rule android.hidl.** android.internal.hidl.@1
 rule android.net.wifi.WifiAnnotations* android.internal.wifi.WifiAnnotations@1
+rule com.android.server.vcn.util.** com.android.server.vcn.repackaged.util.@1
diff --git a/graphics/OWNERS b/graphics/OWNERS
index a6d1bc3..5851cbb 100644
--- a/graphics/OWNERS
+++ b/graphics/OWNERS
@@ -1 +1 @@
-include /core/java/android/graphics/OWNERS
+include /graphics/java/android/graphics/OWNERS
\ No newline at end of file
diff --git a/graphics/java/android/graphics/RecordingCanvas.java b/graphics/java/android/graphics/RecordingCanvas.java
index 9f46ceb..49888fd 100644
--- a/graphics/java/android/graphics/RecordingCanvas.java
+++ b/graphics/java/android/graphics/RecordingCanvas.java
@@ -204,6 +204,26 @@
     }
 
     /**
+     * Draws a ripple
+     *
+     * @param cx
+     * @param cy
+     * @param radius
+     * @param paint
+     * @param progress
+     * @param shader
+     *
+     * @hide
+     */
+    public void drawRipple(CanvasProperty<Float> cx, CanvasProperty<Float> cy,
+            CanvasProperty<Float> radius, CanvasProperty<Paint> paint,
+            CanvasProperty<Float> progress, RuntimeShader shader) {
+        nDrawRipple(mNativeCanvasWrapper, cx.getNativeContainer(), cy.getNativeContainer(),
+                radius.getNativeContainer(), paint.getNativeContainer(),
+                progress.getNativeContainer(), shader.getNativeShaderFactory());
+    }
+
+    /**
      * Draws a round rect
      *
      * @param left
@@ -260,6 +280,9 @@
     private static native void nDrawCircle(long renderer, long propCx,
             long propCy, long propRadius, long propPaint);
     @CriticalNative
+    private static native void nDrawRipple(long renderer, long propCx, long propCy, long propRadius,
+            long propPaint, long propProgress, long runtimeEffect);
+    @CriticalNative
     private static native void nDrawRoundRect(long renderer, long propLeft, long propTop,
             long propRight, long propBottom, long propRx, long propRy, long propPaint);
     @CriticalNative
diff --git a/graphics/java/android/graphics/RenderEffect.java b/graphics/java/android/graphics/RenderEffect.java
index 8b9e36a..496e470 100644
--- a/graphics/java/android/graphics/RenderEffect.java
+++ b/graphics/java/android/graphics/RenderEffect.java
@@ -24,7 +24,11 @@
 
 /**
  * Intermediate rendering step used to render drawing commands with a corresponding
- * visual effect
+ * visual effect. A {@link RenderEffect} can be configured on a {@link RenderNode} through
+ * {@link RenderNode#setRenderEffect(RenderEffect)} and will be applied when drawn through
+ * {@link Canvas#drawRenderNode(RenderNode)}.
+ * Additionally a {@link RenderEffect} can be applied to a View's backing RenderNode through
+ * {@link android.view.View#setRenderEffect(RenderEffect)}
  */
 public final class RenderEffect {
 
@@ -156,7 +160,8 @@
      * @param src Optional subset of the bitmap to be part of the rendered output. If null
      *            is provided, the entire bitmap bounds are used.
      * @param dst Bounds of the destination which the bitmap is translated and scaled to be
-     *            drawn into
+     *            drawn into within the bounds of the {@link RenderNode} this RenderEffect is
+     *            installed on
      */
     @NonNull
     public static RenderEffect createBitmapEffect(
@@ -222,8 +227,8 @@
      * {@link RenderEffect} that is a composition of 2 other {@link RenderEffect} instances
      * combined by the specified {@link BlendMode}
      *
-     * @param dst The Dst pixels used in blending, if null the source bitmap is used.
-     * @param src The Src pixels used in blending, if null the source bitmap is use
+     * @param dst The Dst pixels used in blending
+     * @param src The Src pixels used in blending
      * @param blendMode The {@link BlendMode} to be used to combine colors from the two
      *                  {@link RenderEffect}s
      */
@@ -246,7 +251,11 @@
      * Create a filter that composes 'inner' with 'outer', such that the results of 'inner' are
      * treated as the source bitmap passed to 'outer', i.e.
      *
-     * result = outer(inner(source)).
+     * <pre>
+     * {@code
+     * result = outer(inner(source))
+     * }
+     * </pre>
      *
      * Consumers should favor explicit chaining of {@link RenderEffect} instances at creation time
      * rather than using chain effect. Chain effects are useful for situations where the input or
diff --git a/graphics/java/android/graphics/RuntimeShader.java b/graphics/java/android/graphics/RuntimeShader.java
index fb0983a..7f2e503 100644
--- a/graphics/java/android/graphics/RuntimeShader.java
+++ b/graphics/java/android/graphics/RuntimeShader.java
@@ -115,6 +115,10 @@
                 nativeShaders, colorSpace().getNativeInstance(), mIsOpaque);
     }
 
+    public long getNativeShaderFactory() {
+        return mNativeInstanceRuntimeShaderFactory;
+    }
+
     private static native long nativeCreate(long shaderFactory, long matrix, byte[] inputs,
             long[] shaderInputs, long colorSpaceHandle, boolean isOpaque);
 
diff --git a/graphics/java/android/graphics/SurfaceTexture.java b/graphics/java/android/graphics/SurfaceTexture.java
index 7a2e584..9b2effc 100644
--- a/graphics/java/android/graphics/SurfaceTexture.java
+++ b/graphics/java/android/graphics/SurfaceTexture.java
@@ -76,9 +76,9 @@
     /**
      * These fields are used by native code, do not access or modify.
      */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    @UnsupportedAppUsage(trackingBug = 176388660)
     private long mSurfaceTexture;
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    @UnsupportedAppUsage(trackingBug = 176388660)
     private long mProducer;
     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
     private long mFrameAvailableListener;
diff --git a/graphics/java/android/graphics/drawable/Icon.java b/graphics/java/android/graphics/drawable/Icon.java
index dcd4f33..8da8056 100644
--- a/graphics/java/android/graphics/drawable/Icon.java
+++ b/graphics/java/android/graphics/drawable/Icon.java
@@ -21,7 +21,6 @@
 
 import android.annotation.ColorInt;
 import android.annotation.DrawableRes;
-import android.annotation.IdRes;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.compat.annotation.UnsupportedAppUsage;
@@ -248,7 +247,7 @@
      * Note: This resource may not be available if the application changes at all, and it is
      * up to the caller to ensure safety if this resource is re-used and/or persisted.
      */
-    @IdRes
+    @DrawableRes
     public int getResId() {
         if (mType != TYPE_RESOURCE) {
             throw new IllegalStateException("called getResId() on " + this);
diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java
index 8677fb1..bab80ce 100644
--- a/graphics/java/android/graphics/drawable/RippleDrawable.java
+++ b/graphics/java/android/graphics/drawable/RippleDrawable.java
@@ -122,7 +122,7 @@
     private final Rect mDirtyBounds = new Rect();
 
     /** Mirrors mLayerState with some extra information. */
-    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+    @UnsupportedAppUsage(trackingBug = 175939224)
     private RippleState mState;
 
     /** The masking layer, e.g. the layer with id R.id.mask. */
diff --git a/keystore/java/android/security/AndroidProtectedConfirmation.java b/keystore/java/android/security/AndroidProtectedConfirmation.java
new file mode 100644
index 0000000..dfe485a
--- /dev/null
+++ b/keystore/java/android/security/AndroidProtectedConfirmation.java
@@ -0,0 +1,118 @@
+/*
+ * 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.security;
+
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.ServiceSpecificException;
+import android.security.apc.IConfirmationCallback;
+import android.security.apc.IProtectedConfirmation;
+import android.security.apc.ResponseCode;
+import android.util.Log;
+
+/**
+ * @hide
+ */
+public class AndroidProtectedConfirmation {
+    private static final String TAG = "AndroidProtectedConfirmation";
+
+    public static final int ERROR_OK = ResponseCode.OK;
+    public static final int ERROR_CANCELED = ResponseCode.CANCELLED;
+    public static final int ERROR_ABORTED = ResponseCode.ABORTED;
+    public static final int ERROR_OPERATION_PENDING = ResponseCode.OPERATION_PENDING;
+    public static final int ERROR_IGNORED = ResponseCode.IGNORED;
+    public static final int ERROR_SYSTEM_ERROR = ResponseCode.SYSTEM_ERROR;
+    public static final int ERROR_UNIMPLEMENTED = ResponseCode.UNIMPLEMENTED;
+
+    public static final int FLAG_UI_OPTION_INVERTED =
+            IProtectedConfirmation.FLAG_UI_OPTION_INVERTED;
+    public static final int FLAG_UI_OPTION_MAGNIFIED =
+            IProtectedConfirmation.FLAG_UI_OPTION_MAGNIFIED;
+
+    private IProtectedConfirmation mProtectedConfirmation;
+
+    public AndroidProtectedConfirmation() {
+        mProtectedConfirmation = null;
+    }
+
+    private synchronized IProtectedConfirmation getService() {
+        if (mProtectedConfirmation == null) {
+            mProtectedConfirmation = IProtectedConfirmation.Stub.asInterface(ServiceManager
+                    .getService("android.security.apc"));
+        }
+        return mProtectedConfirmation;
+    }
+
+    /**
+     * Requests keystore call into the confirmationui HAL to display a prompt.
+     *
+     * @param listener the binder to use for callbacks.
+     * @param promptText the prompt to display.
+     * @param extraData extra data / nonce from application.
+     * @param locale the locale as a BCP 47 language tag.
+     * @param uiOptionsAsFlags the UI options to use, as flags.
+     * @return one of the {@code CONFIRMATIONUI_*} constants, for
+     * example {@code KeyStore.CONFIRMATIONUI_OK}.
+     */
+    public int presentConfirmationPrompt(IConfirmationCallback listener, String promptText,
+                                         byte[] extraData, String locale, int uiOptionsAsFlags) {
+        try {
+            getService().presentPrompt(listener, promptText, extraData, locale,
+                                                     uiOptionsAsFlags);
+            return ERROR_OK;
+        } catch (RemoteException e) {
+            Log.w(TAG, "Cannot connect to keystore", e);
+            return ERROR_SYSTEM_ERROR;
+        } catch (ServiceSpecificException e) {
+            return e.errorCode;
+        }
+    }
+
+    /**
+     * Requests keystore call into the confirmationui HAL to cancel displaying a prompt.
+     *
+     * @param listener the binder passed to the {@link #presentConfirmationPrompt} method.
+     * @return one of the {@code CONFIRMATIONUI_*} constants, for
+     * example {@code KeyStore.CONFIRMATIONUI_OK}.
+     */
+    public int cancelConfirmationPrompt(IConfirmationCallback listener) {
+        try {
+            getService().cancelPrompt(listener);
+            return ERROR_OK;
+        } catch (RemoteException e) {
+            Log.w(TAG, "Cannot connect to keystore", e);
+            return ERROR_SYSTEM_ERROR;
+        } catch (ServiceSpecificException e) {
+            return e.errorCode;
+        }
+    }
+
+    /**
+     * Requests keystore to check if the confirmationui HAL is available.
+     *
+     * @return whether the confirmationUI HAL is available.
+     */
+    public boolean isConfirmationPromptSupported() {
+        try {
+            return getService().isSupported();
+        } catch (RemoteException e) {
+            Log.w(TAG, "Cannot connect to keystore", e);
+            return false;
+        }
+    }
+
+}
diff --git a/keystore/java/android/security/IKeyChainService.aidl b/keystore/java/android/security/IKeyChainService.aidl
index a9d4094..f708298 100644
--- a/keystore/java/android/security/IKeyChainService.aidl
+++ b/keystore/java/android/security/IKeyChainService.aidl
@@ -60,7 +60,6 @@
     byte[] getEncodedCaCertificate(String alias, boolean includeDeletedSystem);
     List<String> getCaCertificateChainAliases(String rootAlias, boolean includeDeletedSystem);
     void setCredentialManagementApp(String packageName, in AppUriAuthenticationPolicy policy);
-    void updateCredentialManagementAppPolicy(in AppUriAuthenticationPolicy policy);
     boolean hasCredentialManagementApp();
     String getCredentialManagementAppPackageName();
     AppUriAuthenticationPolicy getCredentialManagementAppPolicy();
diff --git a/keystore/java/android/security/KeyChain.java b/keystore/java/android/security/KeyChain.java
index c6e72b0..2f444b3 100644
--- a/keystore/java/android/security/KeyChain.java
+++ b/keystore/java/android/security/KeyChain.java
@@ -31,6 +31,7 @@
 import android.content.ServiceConnection;
 import android.net.Uri;
 import android.os.Binder;
+import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Process;
@@ -854,10 +855,26 @@
     @WorkerThread
     public static KeyChainConnection bindAsUser(@NonNull Context context, UserHandle user)
             throws InterruptedException {
+        return bindAsUser(context, null, user);
+    }
+
+    /**
+     * Bind to KeyChainService in the target user.
+     * Caller should call unbindService on the result when finished.
+     *
+     * @throws InterruptedException if interrupted during binding.
+     * @throws AssertionError if unable to bind to KeyChainService.
+     * @hide
+     */
+    public static KeyChainConnection bindAsUser(@NonNull Context context, @Nullable Handler handler,
+            UserHandle user) throws InterruptedException {
+
         if (context == null) {
             throw new NullPointerException("context == null");
         }
-        ensureNotOnMainThread(context);
+        if (handler == null) {
+            ensureNotOnMainThread(context);
+        }
         if (!UserManager.get(context).isUserUnlocked(user)) {
             throw new IllegalStateException("User must be unlocked");
         }
@@ -884,9 +901,19 @@
         };
         Intent intent = new Intent(IKeyChainService.class.getName());
         ComponentName comp = intent.resolveSystemService(context.getPackageManager(), 0);
+        if (comp == null) {
+            throw new AssertionError("could not resolve KeyChainService");
+        }
         intent.setComponent(comp);
-        if (comp == null || !context.bindServiceAsUser(
-                intent, keyChainServiceConnection, Context.BIND_AUTO_CREATE, user)) {
+        final boolean bindSucceed;
+        if (handler != null) {
+            bindSucceed = context.bindServiceAsUser(
+                    intent, keyChainServiceConnection, Context.BIND_AUTO_CREATE, handler, user);
+        } else {
+            bindSucceed = context.bindServiceAsUser(
+                    intent, keyChainServiceConnection, Context.BIND_AUTO_CREATE, user);
+        }
+        if (!bindSucceed) {
             throw new AssertionError("could not bind to KeyChainService");
         }
         countDownLatch.await();
diff --git a/keystore/java/android/security/keystore2/KeyStoreCryptoOperationUtils.java b/keystore/java/android/security/keystore2/KeyStoreCryptoOperationUtils.java
index f87a3d2..9924542 100644
--- a/keystore/java/android/security/keystore2/KeyStoreCryptoOperationUtils.java
+++ b/keystore/java/android/security/keystore2/KeyStoreCryptoOperationUtils.java
@@ -120,6 +120,7 @@
                 return new KeyPermanentlyInvalidatedException();
             case ResponseCode.LOCKED:
             case ResponseCode.UNINITIALIZED:
+            case KeymasterDefs.KM_ERROR_KEY_USER_NOT_AUTHENTICATED:
                 // TODO b/173111727 remove response codes LOCKED and UNINITIALIZED
                 return new UserNotAuthenticatedException();
             default:
diff --git a/libs/WindowManager/Shell/res/layout/tv_pip_controls.xml b/libs/WindowManager/Shell/res/layout/tv_pip_controls.xml
deleted file mode 100644
index 9157f63..0000000
--- a/libs/WindowManager/Shell/res/layout/tv_pip_controls.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-    Copyright (C) 2020 The Android Open Source Project
-
-    Licensed under the Apache License, Version 2.0 (the "License");
-    you may not use this file except in compliance with the License.
-    You may obtain a copy of the License at
-
-         http://www.apache.org/licenses/LICENSE-2.0
-
-    Unless required by applicable law or agreed to in writing, software
-    distributed under the License is distributed on an "AS IS" BASIS,
-    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-    See the License for the specific language governing permissions and
-    limitations under the License.
--->
-<!-- Layout for {@link com.android.wm.shell.pip.tv.PipControlsView}. -->
-<merge xmlns:android="http://schemas.android.com/apk/res/android">
-    <com.android.wm.shell.pip.tv.PipControlButtonView
-        android:id="@+id/full_button"
-        android:layout_width="@dimen/picture_in_picture_button_width"
-        android:layout_height="wrap_content"
-        android:src="@drawable/pip_ic_fullscreen_white"
-        android:text="@string/pip_fullscreen" />
-
-    <com.android.wm.shell.pip.tv.PipControlButtonView
-        android:id="@+id/close_button"
-        android:layout_width="@dimen/picture_in_picture_button_width"
-        android:layout_height="wrap_content"
-        android:layout_marginStart="@dimen/picture_in_picture_button_start_margin"
-        android:src="@drawable/pip_ic_close_white"
-        android:text="@string/pip_close" />
-</merge>
diff --git a/libs/WindowManager/Shell/res/layout/tv_pip_menu.xml b/libs/WindowManager/Shell/res/layout/tv_pip_menu.xml
index 0d684e8..49e2379 100644
--- a/libs/WindowManager/Shell/res/layout/tv_pip_menu.xml
+++ b/libs/WindowManager/Shell/res/layout/tv_pip_menu.xml
@@ -14,19 +14,39 @@
     See the License for the specific language governing permissions and
     limitations under the License.
 -->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-              android:id="@+id/tv_pip_menu"
-              android:layout_width="match_parent"
-              android:layout_height="match_parent"
-              android:orientation="horizontal"
-              android:paddingTop="350dp"
-              android:background="#CC000000"
-              android:gravity="top|center_horizontal"
-              android:clipChildren="false">
+<!-- Layout for TvPipMenuView -->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+             android:id="@+id/tv_pip_menu"
+             android:layout_width="match_parent"
+             android:layout_height="match_parent"
+             android:background="#CC000000">
 
-    <com.android.wm.shell.pip.tv.PipControlsView
-        android:id="@+id/pip_controls"
+    <LinearLayout
+        android:id="@+id/tv_pip_menu_action_buttons"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        android:alpha="0" />
-</LinearLayout>
+        android:layout_gravity="center_horizontal"
+        android:layout_marginTop="350dp"
+        android:orientation="horizontal"
+        android:alpha="0">
+
+        <com.android.wm.shell.pip.tv.TvPipMenuActionButton
+            android:id="@+id/tv_pip_menu_fullscreen_button"
+            android:layout_width="@dimen/picture_in_picture_button_width"
+            android:layout_height="wrap_content"
+            android:src="@drawable/pip_ic_fullscreen_white"
+            android:text="@string/pip_fullscreen" />
+
+        <com.android.wm.shell.pip.tv.TvPipMenuActionButton
+            android:id="@+id/tv_pip_menu_close_button"
+            android:layout_width="@dimen/picture_in_picture_button_width"
+            android:layout_height="wrap_content"
+            android:layout_marginStart="@dimen/picture_in_picture_button_start_margin"
+            android:src="@drawable/pip_ic_close_white"
+            android:text="@string/pip_close" />
+
+        <!-- More TvPipMenuActionButtons may be added here at runtime. -->
+
+    </LinearLayout>
+
+</FrameLayout>
diff --git a/libs/WindowManager/Shell/res/layout/tv_pip_control_button.xml b/libs/WindowManager/Shell/res/layout/tv_pip_menu_action_button.xml
similarity index 95%
rename from libs/WindowManager/Shell/res/layout/tv_pip_control_button.xml
rename to libs/WindowManager/Shell/res/layout/tv_pip_menu_action_button.xml
index 727ac34..5925008 100644
--- a/libs/WindowManager/Shell/res/layout/tv_pip_control_button.xml
+++ b/libs/WindowManager/Shell/res/layout/tv_pip_menu_action_button.xml
@@ -14,7 +14,7 @@
     See the License for the specific language governing permissions and
     limitations under the License.
 -->
-<!-- Layout for {@link com.android.wm.shell.pip.tv.PipControlButtonView}. -->
+<!-- Layout for TvPipMenuActionButton -->
 <merge xmlns:android="http://schemas.android.com/apk/res/android">
 
     <ImageView android:id="@+id/button"
diff --git a/libs/WindowManager/Shell/res/layout/tv_pip_custom_control.xml b/libs/WindowManager/Shell/res/layout/tv_pip_menu_additional_action_button.xml
similarity index 94%
rename from libs/WindowManager/Shell/res/layout/tv_pip_custom_control.xml
rename to libs/WindowManager/Shell/res/layout/tv_pip_menu_additional_action_button.xml
index 452f2cd..bf4eb26 100644
--- a/libs/WindowManager/Shell/res/layout/tv_pip_custom_control.xml
+++ b/libs/WindowManager/Shell/res/layout/tv_pip_menu_additional_action_button.xml
@@ -14,7 +14,7 @@
     See the License for the specific language governing permissions and
     limitations under the License.
 -->
-<com.android.wm.shell.pip.tv.PipControlButtonView
+<com.android.wm.shell.pip.tv.TvPipMenuActionButton
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="@dimen/picture_in_picture_button_width"
     android:layout_height="wrap_content"
diff --git a/libs/WindowManager/Shell/res/values-night/colors.xml b/libs/WindowManager/Shell/res/values-night/colors.xml
index 24b3640..83c4d93 100644
--- a/libs/WindowManager/Shell/res/values-night/colors.xml
+++ b/libs/WindowManager/Shell/res/values-night/colors.xml
@@ -17,4 +17,6 @@
 <resources>
     <!-- Bubbles -->
     <color name="bubbles_icon_tint">@color/GM2_grey_200</color>
+    <!-- Splash screen-->
+    <color name="splash_window_background_default">@color/splash_screen_bg_dark</color>
 </resources>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/values/colors.xml b/libs/WindowManager/Shell/res/values/colors.xml
index 1674d0b..350beaf 100644
--- a/libs/WindowManager/Shell/res/values/colors.xml
+++ b/libs/WindowManager/Shell/res/values/colors.xml
@@ -35,4 +35,9 @@
     <color name="GM2_grey_200">#E8EAED</color>
     <color name="GM2_grey_700">#5F6368</color>
     <color name="GM2_grey_800">#3C4043</color>
+
+    <!-- Splash screen -->
+    <color name="splash_screen_bg_light">#FFFFFF</color>
+    <color name="splash_screen_bg_dark">#000000</color>
+    <color name="splash_window_background_default">@color/splash_screen_bg_light</color>
 </resources>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index 25d034c..e25a05c 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -167,4 +167,7 @@
     <!-- Size of padding for the user education cling, this should at minimum be larger than
         individual_bubble_size + some padding. -->
     <dimen name="bubble_stack_user_education_side_inset">72dp</dimen>
+
+    <!-- The width/height of the icon view on staring surface. -->
+    <dimen name="starting_surface_icon_size">108dp</dimen>
 </resources>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java
index 4a8b450..4ef489f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/RootTaskDisplayAreaOrganizer.java
@@ -16,13 +16,23 @@
 
 package com.android.wm.shell;
 
+import android.annotation.UiContext;
+import android.app.ResourcesManager;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.res.Configuration;
+import android.hardware.display.DisplayManager;
+import android.os.Binder;
+import android.os.IBinder;
 import android.util.SparseArray;
+import android.view.Display;
 import android.view.SurfaceControl;
 import android.window.DisplayAreaAppearedInfo;
 import android.window.DisplayAreaInfo;
 import android.window.DisplayAreaOrganizer;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -42,8 +52,13 @@
     private final SparseArray<ArrayList<RootTaskDisplayAreaListener>> mListeners =
             new SparseArray<>();
 
-    public RootTaskDisplayAreaOrganizer(Executor executor) {
+    private final SparseArray<DisplayAreaContext> mDisplayAreaContexts = new SparseArray<>();
+
+    private final Context mContext;
+
+    public RootTaskDisplayAreaOrganizer(Executor executor, Context context) {
         super(executor);
+        mContext = context;
         List<DisplayAreaAppearedInfo> infos = registerOrganizer(FEATURE_DEFAULT_TASK_CONTAINER);
         for (int i = infos.size() - 1; i >= 0; --i) {
             onDisplayAreaAppeared(infos.get(i).getDisplayAreaInfo(), infos.get(i).getLeash());
@@ -103,6 +118,7 @@
                 listeners.get(i).onDisplayAreaAppeared(displayAreaInfo);
             }
         }
+        applyConfigChangesToContext(displayId, displayAreaInfo.configuration);
     }
 
     @Override
@@ -123,6 +139,7 @@
                 listeners.get(i).onDisplayAreaVanished(displayAreaInfo);
             }
         }
+        mDisplayAreaContexts.remove(displayId);
     }
 
     @Override
@@ -143,6 +160,33 @@
                 listeners.get(i).onDisplayAreaInfoChanged(displayAreaInfo);
             }
         }
+        applyConfigChangesToContext(displayId, displayAreaInfo.configuration);
+    }
+
+    /**
+     * Applies the {@link Configuration} to the {@link DisplayAreaContext} specified by
+     * {@code displayId}.
+     *
+     * @param displayId The ID of the {@link Display} which the {@link DisplayAreaContext} is
+     *                  associated with
+     * @param newConfig The propagated configuration
+     */
+    private void applyConfigChangesToContext(int displayId, @NonNull Configuration newConfig) {
+        DisplayAreaContext daContext = mDisplayAreaContexts.get(displayId);
+        if (daContext == null) {
+            daContext = new DisplayAreaContext(mContext, displayId);
+            mDisplayAreaContexts.put(displayId, daContext);
+        }
+        daContext.updateConfigurationChanges(newConfig);
+    }
+
+    /**
+     * Returns the UI context associated with RootTaskDisplayArea specified by {@code displayId}.
+     */
+    @Nullable
+    @UiContext
+    public Context getContext(int displayId) {
+        return mDisplayAreaContexts.get(displayId);
     }
 
     public void dump(@NonNull PrintWriter pw, String prefix) {
@@ -170,4 +214,32 @@
         default void dump(@NonNull PrintWriter pw, String prefix) {
         }
     }
+
+    /**
+     * A UI context to associate with a {@link com.android.server.wm.DisplayArea}.
+     *
+     * This context receives configuration changes through {@link DisplayAreaOrganizer} callbacks
+     * and the core implementation is {@link Context#createTokenContext(IBinder, Display)} to apply
+     * the configuration updates to the {@link android.content.res.Resources}.
+     */
+    @UiContext
+    public static class DisplayAreaContext extends ContextWrapper {
+        private final IBinder mToken = new Binder();
+        private final ResourcesManager mResourcesManager = ResourcesManager.getInstance();
+
+        public DisplayAreaContext(@NonNull Context context, int displayId) {
+            super(null);
+            final Display display = context.getSystemService(DisplayManager.class)
+                    .getDisplay(displayId);
+            attachBaseContext(context.createTokenContext(mToken, display));
+        }
+
+        private void updateConfigurationChanges(@NonNull Configuration newConfig) {
+            final Configuration config = getResources().getConfiguration();
+            final boolean configChanged = config.diff(newConfig) != 0;
+            if (configChanged) {
+                mResourcesManager.updateResourcesForActivity(mToken, newConfig, getDisplayId());
+            }
+        }
+    }
 }
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/Transitions.java
index a779531..54863d2 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/Transitions.java
@@ -20,6 +20,7 @@
 import static android.view.WindowManager.TRANSIT_OPEN;
 import static android.view.WindowManager.TRANSIT_TO_BACK;
 import static android.view.WindowManager.TRANSIT_TO_FRONT;
+import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT;
 
 import android.animation.Animator;
 import android.animation.ValueAnimator;
@@ -166,8 +167,14 @@
                 if (isOpening) {
                     // put on top and fade in
                     t.setLayer(leash, info.getChanges().size() - i);
-                    t.setAlpha(leash, 0.f);
-                    startExampleAnimation(transitionToken, leash, true /* show */);
+                    if ((change.getFlags() & FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT) != 0) {
+                        // This received a transferred starting window, so make it immediately
+                        // visible.
+                        t.setAlpha(leash, 1.f);
+                    } else {
+                        t.setAlpha(leash, 0.f);
+                        startExampleAnimation(transitionToken, leash, true /* show */);
+                    }
                 } else {
                     // put on bottom and leave it visible without fade
                     t.setLayer(leash, -i);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
index 4bf01f7..7f33895 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
@@ -474,8 +474,10 @@
         Bundle extras = new Bundle();
         extras.putBinder(EXTRA_BUBBLE_CONTROLLER, ObjectWrapper.wrap(mController));
         target.putExtras(extras);
+        // TODO(b/175352055) Please replace FLAG_MUTABLE_UNAUDITED below
+        // with either FLAG_IMMUTABLE (recommended) or FLAG_MUTABLE.
         mPendingIntent = PendingIntent.getActivity(mContext, 0 /* requestCode */,
-                target, PendingIntent.FLAG_UPDATE_CURRENT);
+                target, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE_UNAUDITED);
         mSettingsIcon.setVisibility(GONE);
     }
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenController.java
index d88696d..6b79a36 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/legacysplitscreen/LegacySplitScreenController.java
@@ -503,31 +503,31 @@
                     || isSplitActive()) {
                 return false;
             }
-
-            // Try fetching the top running task.
-            final List<RunningTaskInfo> runningTasks =
-                    ActivityTaskManager.getInstance().getTasks(1 /* maxNum */);
-            if (runningTasks == null || runningTasks.isEmpty()) {
-                return false;
-            }
-            // Note: The set of running tasks from the system is ordered by recency.
-            final RunningTaskInfo topRunningTask = runningTasks.get(0);
-            final int activityType = topRunningTask.getActivityType();
-            if (activityType == ACTIVITY_TYPE_HOME || activityType == ACTIVITY_TYPE_RECENTS) {
-                return false;
-            }
-
-            if (!topRunningTask.supportsSplitScreenMultiWindow) {
-                Toast.makeText(mContext, R.string.dock_non_resizeble_failed_to_dock_text,
-                        Toast.LENGTH_SHORT).show();
-                return false;
-            }
-
-            return ActivityTaskManager.getService().setTaskWindowingModeSplitScreenPrimary(
-                    topRunningTask.taskId, true /* onTop */);
         } catch (RemoteException e) {
             return false;
         }
+
+        // Try fetching the top running task.
+        final List<RunningTaskInfo> runningTasks =
+                ActivityTaskManager.getInstance().getTasks(1 /* maxNum */);
+        if (runningTasks == null || runningTasks.isEmpty()) {
+            return false;
+        }
+        // Note: The set of running tasks from the system is ordered by recency.
+        final RunningTaskInfo topRunningTask = runningTasks.get(0);
+        final int activityType = topRunningTask.getActivityType();
+        if (activityType == ACTIVITY_TYPE_HOME || activityType == ACTIVITY_TYPE_RECENTS) {
+            return false;
+        }
+
+        if (!topRunningTask.supportsSplitScreenMultiWindow) {
+            Toast.makeText(mContext, R.string.dock_non_resizeble_failed_to_dock_text,
+                    Toast.LENGTH_SHORT).show();
+            return false;
+        }
+
+        return ActivityTaskManager.getInstance().setTaskWindowingModeSplitScreenPrimary(
+                topRunningTask.taskId, true /* onTop */);
     }
 
     @Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipController.java
index 0955056..ffa6c99 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipController.java
@@ -60,7 +60,8 @@
 /**
  * Manages the picture-in-picture (PIP) UI and states.
  */
-public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallback {
+public class PipController implements Pip, PipTaskOrganizer.PipTransitionCallback,
+        TvPipMenuController.Delegate {
     private static final String TAG = "TvPipController";
     static final boolean DEBUG = false;
 
@@ -198,7 +199,7 @@
         mPipBoundsAlgorithm = pipBoundsAlgorithm;
         mPipMediaController = pipMediaController;
         mTvPipMenuController = tvPipMenuController;
-        mTvPipMenuController.attachPipController(this);
+        mTvPipMenuController.setDelegate(this);
         // Ensure that we have the display info in case we get calls to update the bounds
         // before the listener calls back
         final DisplayInfo displayInfo = new DisplayInfo();
@@ -289,6 +290,7 @@
     /**
      * Closes PIP (PIPed activity and PIP system UI).
      */
+    @Override
     public void closePip() {
         if (DEBUG) Log.d(TAG, "closePip(), current state=" + getStateDescription());
 
@@ -318,9 +320,15 @@
         mHandler.removeCallbacks(mClosePipRunnable);
     }
 
+    @Override
+    public void movePipToNormalPosition() {
+        resizePinnedStack(PipController.STATE_PIP);
+    }
+
     /**
      * Moves the PIPed activity to the fullscreen and closes PIP system UI.
      */
+    @Override
     public void movePipToFullscreen() {
         if (DEBUG) Log.d(TAG, "movePipToFullscreen(), current state=" + getStateDescription());
 
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipControlsView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipControlsView.java
deleted file mode 100644
index 95d9b77..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipControlsView.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * 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.wm.shell.pip.tv;
-
-import android.content.Context;
-import android.util.AttributeSet;
-import android.view.Gravity;
-import android.view.LayoutInflater;
-import android.widget.LinearLayout;
-
-import com.android.wm.shell.R;
-
-
-/**
- * A view containing PIP controls including fullscreen, close, and media controls.
- */
-public class PipControlsView extends LinearLayout {
-
-    public PipControlsView(Context context) {
-        this(context, null);
-    }
-
-    public PipControlsView(Context context, AttributeSet attrs) {
-        this(context, attrs, 0);
-    }
-
-    public PipControlsView(Context context, AttributeSet attrs, int defStyleAttr) {
-        this(context, attrs, defStyleAttr, 0);
-    }
-
-    public PipControlsView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
-        super(context, attrs, defStyleAttr, defStyleRes);
-        LayoutInflater layoutInflater = (LayoutInflater) getContext().getSystemService(
-                Context.LAYOUT_INFLATER_SERVICE);
-        layoutInflater.inflate(R.layout.tv_pip_controls, this);
-        setOrientation(LinearLayout.HORIZONTAL);
-        setGravity(Gravity.TOP | Gravity.CENTER_HORIZONTAL);
-    }
-
-    PipControlButtonView getFullscreenButton() {
-        return findViewById(R.id.full_button);
-    }
-
-    PipControlButtonView getCloseButton() {
-        return findViewById(R.id.close_button);
-    }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipControlsViewController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipControlsViewController.java
deleted file mode 100644
index 5265e7705..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipControlsViewController.java
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
- * 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.wm.shell.pip.tv;
-
-import android.app.PendingIntent;
-import android.app.RemoteAction;
-import android.content.Context;
-import android.graphics.Color;
-import android.os.Handler;
-import android.os.Looper;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-
-import com.android.wm.shell.R;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-
-/**
- * Controller for {@link PipControlsView}.
- */
-public class PipControlsViewController {
-    private static final String TAG = PipControlsViewController.class.getSimpleName();
-
-    private static final float DISABLED_ACTION_ALPHA = 0.54f;
-
-    private final PipController mPipController;
-
-    private final Context mContext;
-    private final Handler mUiThreadHandler;
-    private final PipControlsView mView;
-    private final List<PipControlButtonView> mAdditionalButtons = new ArrayList<>();
-
-    private final List<RemoteAction> mCustomActions = new ArrayList<>();
-    private final List<RemoteAction> mMediaActions = new ArrayList<>();
-
-    public PipControlsViewController(PipControlsView view, PipController pipController) {
-        mContext = view.getContext();
-        mUiThreadHandler = new Handler(Looper.getMainLooper());
-        mPipController = pipController;
-        mView = view;
-
-        mView.getFullscreenButton().setOnClickListener(v -> mPipController.movePipToFullscreen());
-        mView.getCloseButton().setOnClickListener(v -> mPipController.closePip());
-
-        mPipController.getPipMediaController().addActionListener(this::onMediaActionsChanged);
-    }
-
-    PipControlsView getView() {
-        return mView;
-    }
-
-    /**
-     * Updates the set of activity-defined actions.
-     */
-    void setCustomActions(List<? extends RemoteAction> actions) {
-        if (mCustomActions.isEmpty() && actions.isEmpty()) {
-            // Nothing changed - return early.
-            return;
-        }
-        mCustomActions.clear();
-        mCustomActions.addAll(actions);
-        updateAdditionalActions();
-    }
-
-    private void onMediaActionsChanged(List<RemoteAction> actions) {
-        if (mMediaActions.isEmpty() && actions.isEmpty()) {
-            // Nothing changed - return early.
-            return;
-        }
-        mMediaActions.clear();
-        mMediaActions.addAll(actions);
-
-        // Update the view only if there are no custom actions (media actions are only shown when
-        // there no custom actions).
-        if (mCustomActions.isEmpty()) {
-            updateAdditionalActions();
-        }
-    }
-
-    private void updateAdditionalActions() {
-        final List<RemoteAction> actionsToDisplay;
-        if (!mCustomActions.isEmpty()) {
-            // If there are custom actions: show them.
-            actionsToDisplay = mCustomActions;
-        } else if (!mMediaActions.isEmpty()) {
-            // If there are no custom actions, but there media actions: show them.
-            actionsToDisplay = mMediaActions;
-        } else {
-            // If there no custom actions and no media actions: clean up all the additional buttons.
-            actionsToDisplay = Collections.emptyList();
-        }
-
-        // Make sure we exactly as many additional buttons as we have actions to display.
-        final int actionsNumber = actionsToDisplay.size();
-        int buttonsNumber = mAdditionalButtons.size();
-        if (actionsNumber > buttonsNumber) {
-            final LayoutInflater layoutInflater = LayoutInflater.from(mContext);
-            // Add buttons until we have enough to display all of the actions.
-            while (actionsNumber > buttonsNumber) {
-                final PipControlButtonView button = (PipControlButtonView) layoutInflater.inflate(
-                        R.layout.tv_pip_custom_control, mView, false);
-                mView.addView(button);
-                mAdditionalButtons.add(button);
-
-                buttonsNumber++;
-            }
-        } else if (actionsNumber < buttonsNumber) {
-            // Hide buttons until we as many as the actions.
-            while (actionsNumber < buttonsNumber) {
-                final View button = mAdditionalButtons.get(buttonsNumber - 1);
-                button.setVisibility(View.GONE);
-                button.setOnClickListener(null);
-
-                buttonsNumber--;
-            }
-        }
-
-        // "Assign" actions to the buttons.
-        for (int index = 0; index < actionsNumber; index++) {
-            final RemoteAction action = actionsToDisplay.get(index);
-            final PipControlButtonView button = mAdditionalButtons.get(index);
-            button.setVisibility(View.VISIBLE); // Ensure the button is visible.
-            button.setText(action.getContentDescription());
-            button.setEnabled(action.isEnabled());
-            button.setAlpha(action.isEnabled() ? 1f : DISABLED_ACTION_ALPHA);
-            button.setOnClickListener(v -> {
-                try {
-                    action.getActionIntent().send();
-                } catch (PendingIntent.CanceledException e) {
-                    Log.w(TAG, "Failed to send action", e);
-                }
-            });
-
-            action.getIcon().loadDrawableAsync(mContext, drawable -> {
-                drawable.setTint(Color.WHITE);
-                button.setImageDrawable(drawable);
-            }, mUiThreadHandler);
-        }
-    }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipMenuView.java
deleted file mode 100644
index 83cb7ce..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipMenuView.java
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * 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.wm.shell.pip.tv;
-
-import static android.view.KeyEvent.ACTION_UP;
-import static android.view.KeyEvent.KEYCODE_BACK;
-
-import android.animation.Animator;
-import android.animation.AnimatorInflater;
-import android.annotation.Nullable;
-import android.app.RemoteAction;
-import android.content.Context;
-import android.content.pm.ParceledListSlice;
-import android.util.Log;
-import android.view.KeyEvent;
-import android.view.SurfaceControl;
-import android.view.ViewRootImpl;
-import android.view.WindowManagerGlobal;
-import android.widget.FrameLayout;
-
-import com.android.wm.shell.R;
-
-import java.util.Collections;
-
-/**
- * The Menu View that shows controls of the PiP. Always fullscreen.
- */
-public class PipMenuView extends FrameLayout {
-    private static final String TAG = "PipMenuView";
-    private static final boolean DEBUG = PipController.DEBUG;
-
-    private final Animator mFadeInAnimation;
-    private final Animator mFadeOutAnimation;
-    private final PipControlsViewController mPipControlsViewController;
-    @Nullable
-    private OnBackPressListener mOnBackPressListener;
-
-    public PipMenuView(Context context, PipController pipController) {
-        super(context, null, 0);
-        inflate(context, R.layout.tv_pip_menu, this);
-
-        mPipControlsViewController = new PipControlsViewController(
-                findViewById(R.id.pip_controls), pipController);
-        mFadeInAnimation = AnimatorInflater.loadAnimator(
-                mContext, R.anim.tv_pip_menu_fade_in_animation);
-        mFadeInAnimation.setTarget(mPipControlsViewController.getView());
-        mFadeOutAnimation = AnimatorInflater.loadAnimator(
-                mContext, R.anim.tv_pip_menu_fade_out_animation);
-        mFadeOutAnimation.setTarget(mPipControlsViewController.getView());
-    }
-
-    @Nullable
-    SurfaceControl getWindowSurfaceControl() {
-        final ViewRootImpl root = getViewRootImpl();
-        if (root == null) {
-            return null;
-        }
-        final SurfaceControl out = root.getSurfaceControl();
-        if (out != null && out.isValid()) {
-            return out;
-        }
-        return null;
-    }
-
-    void showMenu() {
-        mFadeInAnimation.start();
-        setAlpha(1.0f);
-        grantWindowFocus(true);
-    }
-
-    void hideMenu() {
-        mFadeOutAnimation.start();
-        setAlpha(0.0f);
-        grantWindowFocus(false);
-    }
-
-    private void grantWindowFocus(boolean grantFocus) {
-        try {
-            WindowManagerGlobal.getWindowSession().grantEmbeddedWindowFocus(null /* window */,
-                    getViewRootImpl().getInputToken(), grantFocus);
-        } catch (Exception e) {
-            Log.e(TAG, "Unable to update focus as menu disappears", e);
-        }
-    }
-
-    void setOnBackPressListener(OnBackPressListener onBackPressListener) {
-        mOnBackPressListener = onBackPressListener;
-    }
-
-    @Override
-    public boolean dispatchKeyEvent(KeyEvent event) {
-        if (event.getKeyCode() == KEYCODE_BACK && event.getAction() == ACTION_UP
-                && mOnBackPressListener != null) {
-            mOnBackPressListener.onBackPress();
-            return true;
-        } else {
-            return super.dispatchKeyEvent(event);
-        }
-    }
-
-    void setAppActions(ParceledListSlice<RemoteAction> actions) {
-        if (DEBUG) Log.d(TAG, "onPipMenuActionsChanged()");
-
-        boolean hasCustomActions = actions != null && !actions.getList().isEmpty();
-        mPipControlsViewController.setCustomActions(
-                hasCustomActions ? actions.getList() : Collections.emptyList());
-    }
-
-    interface OnBackPressListener {
-        void onBackPress();
-    }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipNotification.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipNotification.java
index 4e0ab66..5716c7f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipNotification.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipNotification.java
@@ -175,7 +175,7 @@
     }
 
     private static PendingIntent createPendingIntent(Context context, String action) {
-        return PendingIntent.getBroadcast(context, 0,
-                new Intent(action), PendingIntent.FLAG_CANCEL_CURRENT);
+        return PendingIntent.getBroadcast(context, 0, new Intent(action),
+                PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE);
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipControlButtonView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuActionButton.java
similarity index 68%
rename from libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipControlButtonView.java
rename to libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuActionButton.java
index 4e82bb5..6f7cd82 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/PipControlButtonView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuActionButton.java
@@ -31,64 +31,51 @@
 import com.android.wm.shell.R;
 
 /**
- * A view containing PIP controls including fullscreen, close, and media controls.
+ * A View that represents Pip Menu action button, such as "Fullscreen" and "Close" as well custom
+ * (provided by the application in Pip) and media buttons.
  */
-public class PipControlButtonView extends RelativeLayout {
-
-    private OnFocusChangeListener mFocusChangeListener;
-    private ImageView mIconImageView;
-    ImageView mButtonImageView;
-    private TextView mDescriptionTextView;
+public class TvPipMenuActionButton extends RelativeLayout implements View.OnClickListener {
+    private final ImageView mIconImageView;
+    private final ImageView mButtonImageView;
+    private final TextView mDescriptionTextView;
     private Animator mTextFocusGainAnimator;
     private Animator mButtonFocusGainAnimator;
     private Animator mTextFocusLossAnimator;
     private Animator mButtonFocusLossAnimator;
+    private OnClickListener mOnClickListener;
 
-    private final OnFocusChangeListener mInternalFocusChangeListener =
-            new OnFocusChangeListener() {
-                @Override
-                public void onFocusChange(View v, boolean hasFocus) {
-                    if (hasFocus) {
-                        startFocusGainAnimation();
-                    } else {
-                        startFocusLossAnimation();
-                    }
-
-                    if (mFocusChangeListener != null) {
-                        mFocusChangeListener.onFocusChange(PipControlButtonView.this, hasFocus);
-                    }
-                }
-            };
-
-    public PipControlButtonView(Context context) {
+    public TvPipMenuActionButton(Context context) {
         this(context, null, 0, 0);
     }
 
-    public PipControlButtonView(Context context, AttributeSet attrs) {
+    public TvPipMenuActionButton(Context context, AttributeSet attrs) {
         this(context, attrs, 0, 0);
     }
 
-    public PipControlButtonView(Context context, AttributeSet attrs, int defStyleAttr) {
+    public TvPipMenuActionButton(Context context, AttributeSet attrs, int defStyleAttr) {
         this(context, attrs, defStyleAttr, 0);
     }
 
-    public PipControlButtonView(
+    public TvPipMenuActionButton(
             Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
         super(context, attrs, defStyleAttr, defStyleRes);
-        LayoutInflater inflater = (LayoutInflater) getContext()
+        final LayoutInflater inflater = (LayoutInflater) getContext()
                 .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-        inflater.inflate(R.layout.tv_pip_control_button, this);
+        inflater.inflate(R.layout.tv_pip_menu_action_button, this);
 
         mIconImageView = findViewById(R.id.icon);
         mButtonImageView = findViewById(R.id.button);
         mDescriptionTextView = findViewById(R.id.desc);
 
-        int[] values = new int[]{android.R.attr.src, android.R.attr.text};
-        TypedArray typedArray = context.obtainStyledAttributes(attrs, values, defStyleAttr,
+        final int[] values = new int[]{android.R.attr.src, android.R.attr.text};
+        final TypedArray typedArray = context.obtainStyledAttributes(attrs, values, defStyleAttr,
                 defStyleRes);
 
         setImageResource(typedArray.getResourceId(0, 0));
-        setText(typedArray.getResourceId(1, 0));
+        final int textResId = typedArray.getResourceId(1, 0);
+        if (textResId != 0) {
+            setTextAndDescription(getContext().getString(textResId));
+        }
 
         typedArray.recycle();
     }
@@ -96,7 +83,13 @@
     @Override
     public void onFinishInflate() {
         super.onFinishInflate();
-        mButtonImageView.setOnFocusChangeListener(mInternalFocusChangeListener);
+        mButtonImageView.setOnFocusChangeListener((v, hasFocus) -> {
+            if (hasFocus) {
+                startFocusGainAnimation();
+            } else {
+                startFocusLossAnimation();
+            }
+        });
 
         mTextFocusGainAnimator = AnimatorInflater.loadAnimator(getContext(),
                 R.anim.tv_pip_controls_focus_gain_animation);
@@ -115,12 +108,19 @@
 
     @Override
     public void setOnClickListener(OnClickListener listener) {
-        mButtonImageView.setOnClickListener(listener);
+        // We do not want to set an OnClickListener to the TvPipMenuActionButton itself, but only to
+        // the ImageView. So let's "cash" the listener we've been passed here and set a "proxy"
+        // listener to the ImageView.
+        mOnClickListener = listener;
+        mButtonImageView.setOnClickListener(listener != null ? this : null);
     }
 
     @Override
-    public void setOnFocusChangeListener(OnFocusChangeListener listener) {
-        mFocusChangeListener = listener;
+    public void onClick(View v) {
+        if (mOnClickListener != null) {
+            // Pass the correct view - this.
+            mOnClickListener.onClick(this);
+        }
     }
 
     /**
@@ -142,21 +142,11 @@
     /**
      * Sets the text for description the with the given string.
      */
-    public void setText(CharSequence text) {
+    public void setTextAndDescription(CharSequence text) {
         mButtonImageView.setContentDescription(text);
         mDescriptionTextView.setText(text);
     }
 
-    /**
-     * Sets the text for description the with the given resource id.
-     */
-    public void setText(int resId) {
-        if (resId != 0) {
-            mButtonImageView.setContentDescription(getContext().getString(resId));
-            mDescriptionTextView.setText(resId);
-        }
-    }
-
     private static void cancelAnimator(Animator animator) {
         if (animator.isStarted()) {
             animator.cancel();
@@ -187,8 +177,8 @@
         mTextFocusLossAnimator.start();
         if (mButtonImageView.hasFocus()) {
             // Button uses ripple that has the default animation for the focus changes.
-            // Howevever, it doesn't expose the API to fade out while it is focused,
-            // so we should manually run the fade out animation when PIP controls row loses focus.
+            // However, it doesn't expose the API to fade out while it is focused, so we should
+            // manually run the fade out animation when PIP controls row loses focus.
             mButtonFocusLossAnimator.start();
         }
     }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java
index 5d0d761..9192cf1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java
@@ -19,38 +19,97 @@
 import static android.view.WindowManager.SHELL_ROOT_LAYER_PIP;
 
 import android.app.RemoteAction;
+import android.content.BroadcastReceiver;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.pm.ParceledListSlice;
 import android.util.Log;
 import android.view.SurfaceControl;
 
+import androidx.annotation.Nullable;
+
 import com.android.wm.shell.common.SystemWindows;
 import com.android.wm.shell.pip.PipBoundsState;
+import com.android.wm.shell.pip.PipMediaController;
 import com.android.wm.shell.pip.PipMenuController;
 
+import java.util.ArrayList;
+import java.util.List;
+
 /**
  * Manages the visibility of the PiP Menu as user interacts with PiP.
  */
-public class TvPipMenuController implements PipMenuController {
+public class TvPipMenuController implements PipMenuController, TvPipMenuView.Listener {
     private static final String TAG = "TvPipMenuController";
     private static final boolean DEBUG = PipController.DEBUG;
 
     private final Context mContext;
     private final SystemWindows mSystemWindows;
     private final PipBoundsState mPipBoundsState;
-    private PipMenuView mMenuView;
-    private PipController mPipController;
+
+    private Delegate mDelegate;
     private SurfaceControl mLeash;
+    private TvPipMenuView mMenuView;
+
+    private final List<RemoteAction> mMediaActions = new ArrayList<>();
+    private final List<RemoteAction> mAppActions = new ArrayList<>();
 
     public TvPipMenuController(Context context, PipBoundsState pipBoundsState,
-            SystemWindows systemWindows) {
+            SystemWindows systemWindows, PipMediaController pipMediaController) {
         mContext = context;
         mPipBoundsState = pipBoundsState;
         mSystemWindows = systemWindows;
+
+        // We need to "close" the menu the platform call for all the system dialogs to close (for
+        // example, on the Home button press).
+        final BroadcastReceiver closeSystemDialogsBroadcastReceiver = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                hideMenu();
+            }
+        };
+        context.registerReceiver(closeSystemDialogsBroadcastReceiver,
+                new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
+
+        pipMediaController.addActionListener(this::onMediaActionsChanged);
     }
 
-    void attachPipController(PipController pipController) {
-        mPipController = pipController;
+    void setDelegate(Delegate delegate) {
+        if (DEBUG) Log.d(TAG, "setDelegate(), delegate=" + delegate);
+        if (mDelegate != null) {
+            throw new IllegalStateException(
+                    "The delegate has already been set and should not change.");
+        }
+        if (delegate == null) {
+            throw new IllegalArgumentException("The delegate must not be null.");
+        }
+
+        mDelegate = delegate;
+    }
+
+    @Override
+    public void attach(SurfaceControl leash) {
+        if (mDelegate == null) {
+            throw new IllegalStateException("Delegate is not set.");
+        }
+
+        mLeash = leash;
+        attachPipMenuView();
+    }
+
+    private void attachPipMenuView() {
+        if (DEBUG) Log.d(TAG, "attachPipMenuView()");
+
+        if (mMenuView != null) {
+            detachPipMenuView();
+        }
+
+        mMenuView = new TvPipMenuView(mContext);
+        mMenuView.setListener(this);
+        mSystemWindows.addView(mMenuView,
+                getPipMenuLayoutParams(MENU_WINDOW_TITLE, 0 /* width */, 0 /* height */),
+                0, SHELL_ROOT_LAYER_PIP);
     }
 
     @Override
@@ -61,7 +120,8 @@
             mSystemWindows.updateViewLayout(mMenuView, getPipMenuLayoutParams(MENU_WINDOW_TITLE,
                     mPipBoundsState.getDisplayBounds().width(),
                     mPipBoundsState.getDisplayBounds().height()));
-            mMenuView.showMenu();
+            maybeUpdateMenuViewActions();
+            mMenuView.show();
 
             // By default, SystemWindows views are above everything else.
             // Set the relative z-order so the menu is below PiP.
@@ -77,38 +137,18 @@
         if (DEBUG) Log.d(TAG, "hideMenu()");
 
         if (isMenuVisible()) {
-            mMenuView.hideMenu();
-            mPipController.resizePinnedStack(PipController.STATE_PIP);
+            mMenuView.hide();
+            mDelegate.movePipToNormalPosition();
         }
     }
 
     @Override
-    public void attach(SurfaceControl leash) {
-        mLeash = leash;
-        attachPipMenuView();
-    }
-
-    @Override
     public void detach() {
         hideMenu();
         detachPipMenuView();
         mLeash = null;
     }
 
-    private void attachPipMenuView() {
-        if (DEBUG) Log.d(TAG, "attachPipMenuView()");
-
-        if (mMenuView != null) {
-            detachPipMenuView();
-        }
-
-        mMenuView = new PipMenuView(mContext, mPipController);
-        mMenuView.setOnBackPressListener(this::hideMenu);
-        mSystemWindows.addView(mMenuView,
-                getPipMenuLayoutParams(MENU_WINDOW_TITLE, 0 /* width */, 0 /* height */),
-                0, SHELL_ROOT_LAYER_PIP);
-    }
-
     private void detachPipMenuView() {
         if (DEBUG) Log.d(TAG, "detachPipMenuView()");
 
@@ -121,18 +161,65 @@
     }
 
     @Override
-    public void setAppActions(ParceledListSlice<RemoteAction> appActions) {
-        if (DEBUG) Log.d(TAG, "setAppActions(), actions=" + appActions);
+    public void setAppActions(ParceledListSlice<RemoteAction> actions) {
+        if (DEBUG) Log.d(TAG, "setAppActions()");
+        updateAdditionalActionsList(mAppActions, actions.getList());
+    }
 
-        if (mMenuView != null) {
-            mMenuView.setAppActions(appActions);
+    private void onMediaActionsChanged(List<RemoteAction> actions) {
+        if (DEBUG) Log.d(TAG, "onMediaActionsChanged()");
+        updateAdditionalActionsList(mMediaActions, actions);
+    }
+
+    private void updateAdditionalActionsList(
+            List<RemoteAction> destination, @Nullable List<RemoteAction> source) {
+        final int number = source != null ? source.size() : 0;
+        if (number == 0 && destination.isEmpty()) {
+            // Nothing changed.
+            return;
+        }
+
+        destination.clear();
+        if (number > 0) {
+            destination.addAll(source);
+        }
+        maybeUpdateMenuViewActions();
+    }
+
+    private void maybeUpdateMenuViewActions() {
+        if (mMenuView == null) {
+            return;
+        }
+        if (!mAppActions.isEmpty()) {
+            mMenuView.setAdditionalActions(mAppActions);
         } else {
-            Log.w(TAG, "Cannot set remote actions, there is no View");
+            mMenuView.setAdditionalActions(mMediaActions);
         }
     }
 
     @Override
     public boolean isMenuVisible() {
-        return mMenuView != null && mMenuView.getAlpha() == 1.0f;
+        return mMenuView != null && mMenuView.isVisible();
+    }
+
+    @Override
+    public void onBackPress() {
+        hideMenu();
+    }
+
+    @Override
+    public void onCloseButtonClick() {
+        mDelegate.closePip();
+    }
+
+    @Override
+    public void onFullscreenButtonClick() {
+        mDelegate.movePipToFullscreen();
+    }
+
+    interface Delegate {
+        void movePipToNormalPosition();
+        void movePipToFullscreen();
+        void closePip();
     }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java
new file mode 100644
index 0000000..f7b76c1
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuView.java
@@ -0,0 +1,235 @@
+/*
+ * 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.wm.shell.pip.tv;
+
+import static android.animation.AnimatorInflater.loadAnimator;
+import static android.view.KeyEvent.ACTION_UP;
+import static android.view.KeyEvent.KEYCODE_BACK;
+
+import android.animation.Animator;
+import android.app.PendingIntent;
+import android.app.RemoteAction;
+import android.content.Context;
+import android.graphics.Color;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.SurfaceControl;
+import android.view.View;
+import android.view.ViewRootImpl;
+import android.view.WindowManagerGlobal;
+import android.widget.FrameLayout;
+import android.widget.LinearLayout;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.wm.shell.R;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A View that represents Pip Menu on TV. It's responsible for displaying 2 ever-present Pip Menu
+ * actions: Fullscreen and Close, but could also display "additional" actions, that may be set via
+ * a {@link #setAdditionalActions(List)} call.
+ */
+public class TvPipMenuView extends FrameLayout implements View.OnClickListener {
+    private static final String TAG = "TvPipMenuView";
+    private static final boolean DEBUG = PipController.DEBUG;
+
+    private static final float DISABLED_ACTION_ALPHA = 0.54f;
+
+    private final Handler mUiThreadHandler;
+    private final Animator mFadeInAnimation;
+    private final Animator mFadeOutAnimation;
+    @Nullable private Listener mListener;
+
+    private final LinearLayout mActionButtonsContainer;
+    private final List<TvPipMenuActionButton> mAdditionalButtons = new ArrayList<>();
+
+    public TvPipMenuView(@NonNull Context context) {
+        this(context, null);
+    }
+
+    public TvPipMenuView(@NonNull Context context, @Nullable AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public TvPipMenuView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public TvPipMenuView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr,
+            int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+        mUiThreadHandler = new Handler(Looper.getMainLooper());
+
+        inflate(context, R.layout.tv_pip_menu, this);
+
+        mActionButtonsContainer = findViewById(R.id.tv_pip_menu_action_buttons);
+        mActionButtonsContainer.findViewById(R.id.tv_pip_menu_fullscreen_button)
+                .setOnClickListener(this);
+        mActionButtonsContainer.findViewById(R.id.tv_pip_menu_close_button)
+                .setOnClickListener(this);
+
+        mFadeInAnimation = loadAnimator(mContext, R.anim.tv_pip_menu_fade_in_animation);
+        mFadeInAnimation.setTarget(mActionButtonsContainer);
+
+        mFadeOutAnimation = loadAnimator(mContext, R.anim.tv_pip_menu_fade_out_animation);
+        mFadeOutAnimation.setTarget(mActionButtonsContainer);
+    }
+
+    void setListener(@Nullable Listener listener) {
+        mListener = listener;
+    }
+
+    void show() {
+        if (DEBUG) Log.d(TAG, "show()");
+
+        mFadeInAnimation.start();
+        setAlpha(1.0f);
+        grantWindowFocus(true);
+    }
+
+    void hide() {
+        if (DEBUG) Log.d(TAG, "hide()");
+
+        mFadeOutAnimation.start();
+        setAlpha(0.0f);
+        grantWindowFocus(false);
+    }
+
+    boolean isVisible() {
+        return getAlpha() == 1.0f;
+    }
+
+    private void grantWindowFocus(boolean grantFocus) {
+        if (DEBUG) Log.d(TAG, "grantWindowFocus(" + grantFocus + ")");
+
+        try {
+            WindowManagerGlobal.getWindowSession().grantEmbeddedWindowFocus(null /* window */,
+                    getViewRootImpl().getInputToken(), grantFocus);
+        } catch (Exception e) {
+            Log.e(TAG, "Unable to update focus", e);
+        }
+    }
+
+    void setAdditionalActions(List<RemoteAction> actions) {
+        if (DEBUG) Log.d(TAG, "setAdditionalActions()");
+
+        // Make sure we exactly as many additional buttons as we have actions to display.
+        final int actionsNumber = actions.size();
+        int buttonsNumber = mAdditionalButtons.size();
+        if (actionsNumber > buttonsNumber) {
+            final LayoutInflater layoutInflater = LayoutInflater.from(mContext);
+            // Add buttons until we have enough to display all of the actions.
+            while (actionsNumber > buttonsNumber) {
+                final TvPipMenuActionButton button = (TvPipMenuActionButton) layoutInflater.inflate(
+                        R.layout.tv_pip_menu_additional_action_button, mActionButtonsContainer,
+                        false);
+                button.setOnClickListener(this);
+
+                mActionButtonsContainer.addView(button);
+                mAdditionalButtons.add(button);
+
+                buttonsNumber++;
+            }
+        } else if (actionsNumber < buttonsNumber) {
+            // Hide buttons until we as many as the actions.
+            while (actionsNumber < buttonsNumber) {
+                final View button = mAdditionalButtons.get(buttonsNumber - 1);
+                button.setVisibility(View.GONE);
+                button.setTag(null);
+
+                buttonsNumber--;
+            }
+        }
+
+        // "Assign" actions to the buttons.
+        for (int index = 0; index < actionsNumber; index++) {
+            final RemoteAction action = actions.get(index);
+            final TvPipMenuActionButton button = mAdditionalButtons.get(index);
+            button.setVisibility(View.VISIBLE); // Ensure the button is visible.
+            button.setTextAndDescription(action.getContentDescription());
+            button.setEnabled(action.isEnabled());
+            button.setAlpha(action.isEnabled() ? 1f : DISABLED_ACTION_ALPHA);
+            button.setTag(action);
+
+            action.getIcon().loadDrawableAsync(mContext, drawable -> {
+                drawable.setTint(Color.WHITE);
+                button.setImageDrawable(drawable);
+            }, mUiThreadHandler);
+        }
+    }
+
+    @Nullable
+    SurfaceControl getWindowSurfaceControl() {
+        final ViewRootImpl root = getViewRootImpl();
+        if (root == null) {
+            return null;
+        }
+        final SurfaceControl out = root.getSurfaceControl();
+        if (out != null && out.isValid()) {
+            return out;
+        }
+        return null;
+    }
+
+    @Override
+    public void onClick(View v) {
+        if (mListener == null) return;
+
+        final int id = v.getId();
+        if (id == R.id.tv_pip_menu_fullscreen_button) {
+            mListener.onFullscreenButtonClick();
+        } else if (id == R.id.tv_pip_menu_close_button) {
+            mListener.onCloseButtonClick();
+        } else {
+            // This should be an "additional action"
+            final RemoteAction action = (RemoteAction) v.getTag();
+            if (action != null) {
+                try {
+                    action.getActionIntent().send();
+                } catch (PendingIntent.CanceledException e) {
+                    Log.w(TAG, "Failed to send action", e);
+                }
+            } else {
+                Log.w(TAG, "RemoteAction is null");
+            }
+        }
+    }
+
+    @Override
+    public boolean dispatchKeyEvent(KeyEvent event) {
+        if (event.getAction() == ACTION_UP && event.getKeyCode() == KEYCODE_BACK
+                && mListener != null) {
+            mListener.onBackPress();
+            return true;
+        }
+        return super.dispatchKeyEvent(event);
+    }
+
+    interface Listener {
+        void onBackPress();
+        void onCloseButtonClick();
+        void onFullscreenButtonClick();
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
new file mode 100644
index 0000000..f3f2fc3
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/SplashscreenContentDrawer.java
@@ -0,0 +1,565 @@
+/*
+ * 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.wm.shell.startingsurface;
+
+import android.annotation.NonNull;
+import android.app.ActivityThread;
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Rect;
+import android.graphics.drawable.AdaptiveIconDrawable;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.LayerDrawable;
+import android.os.Build;
+import android.util.Slog;
+import android.view.Gravity;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.FrameLayout;
+
+import com.android.internal.R;
+import com.android.internal.graphics.palette.Palette;
+import com.android.internal.graphics.palette.Quantizer;
+import com.android.internal.graphics.palette.VariationalKMeansQuantizer;
+import com.android.internal.policy.PhoneWindow;
+
+import java.util.List;
+
+/**
+ * Util class to create the view for a splash screen content.
+ */
+class SplashscreenContentDrawer {
+    private static final String TAG = StartingSurfaceDrawer.TAG;
+    private static final boolean DEBUG = StartingSurfaceDrawer.DEBUG_SPLASH_SCREEN;
+
+    // The acceptable area ratio of foreground_icon_area/background_icon_area, if there is an
+    // icon which it's non-transparent foreground area is similar to it's background area, then
+    // do not enlarge the foreground drawable.
+    // For example, an icon with the foreground 108*108 opaque pixels and it's background
+    // also 108*108 pixels, then do not enlarge this icon if only need to show foreground icon.
+    private static final float ENLARGE_FOREGROUND_ICON_THRESHOLD = (72f * 72f) / (108f * 108f);
+    private final Context mContext;
+    private int mIconSize;
+
+    SplashscreenContentDrawer(Context context) {
+        mContext = context;
+    }
+
+    private void updateDensity() {
+        mIconSize = mContext.getResources().getDimensionPixelSize(
+                com.android.wm.shell.R.dimen.starting_surface_icon_size);
+    }
+
+    private int getSystemBGColor() {
+        final Context systemContext = ActivityThread.currentApplication();
+        if (systemContext == null) {
+            Slog.e(TAG, "System context does not exist!");
+            return Color.BLACK;
+        }
+        final Resources res = systemContext.getResources();
+        return res.getColor(com.android.wm.shell.R.color.splash_window_background_default);
+    }
+
+    private Drawable createDefaultBackgroundDrawable() {
+        return new ColorDrawable(getSystemBGColor());
+    }
+
+    View makeSplashScreenContentView(PhoneWindow win, Context context, int iconRes,
+            int splashscreenContentResId) {
+        updateDensity();
+        win.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
+        // splash screen content will be deprecated after S.
+        final View ssc = makeSplashscreenContentDrawable(win, context, splashscreenContentResId);
+        if (ssc != null) {
+            return ssc;
+        }
+
+        final TypedArray typedArray = context.obtainStyledAttributes(
+                com.android.internal.R.styleable.Window);
+        final int resId = typedArray.getResourceId(R.styleable.Window_windowBackground, 0);
+        typedArray.recycle();
+        final Drawable themeBGDrawable;
+        if (resId == 0) {
+            Slog.w(TAG, "Window background not exist!");
+            themeBGDrawable = createDefaultBackgroundDrawable();
+        } else {
+            themeBGDrawable = context.getDrawable(resId);
+        }
+        final Drawable iconDrawable = iconRes != 0 ? context.getDrawable(iconRes)
+                : context.getPackageManager().getDefaultActivityIcon();
+        // TODO (b/173975965) Tracking the performance on improved splash screen.
+        final StartingWindowViewBuilder builder = new StartingWindowViewBuilder();
+        return builder
+                .setPhoneWindow(win)
+                .setContext(context)
+                .setThemeDrawable(themeBGDrawable)
+                .setIconDrawable(iconDrawable).build();
+    }
+
+    private class StartingWindowViewBuilder {
+        // materials
+        private Drawable mThemeBGDrawable;
+        private Drawable mIconDrawable;
+        private PhoneWindow mPhoneWindow;
+        private Context mContext;
+
+        // result
+        private boolean mBuildComplete = false;
+        private View mCachedResult;
+        private int mThemeColor;
+        private Drawable mFinalIconDrawable;
+        private float mScale = 1f;
+
+        StartingWindowViewBuilder setThemeDrawable(Drawable background) {
+            mThemeBGDrawable = background;
+            mBuildComplete = false;
+            return this;
+        }
+
+        StartingWindowViewBuilder setIconDrawable(Drawable iconDrawable) {
+            mIconDrawable = iconDrawable;
+            mBuildComplete = false;
+            return this;
+        }
+
+        StartingWindowViewBuilder setPhoneWindow(PhoneWindow window) {
+            mPhoneWindow = window;
+            mBuildComplete = false;
+            return this;
+        }
+
+        StartingWindowViewBuilder setContext(Context context) {
+            mContext = context;
+            mBuildComplete = false;
+            return this;
+        }
+
+        View build() {
+            if (mBuildComplete) {
+                return mCachedResult;
+            }
+            if (mPhoneWindow == null || mContext == null) {
+                Slog.e(TAG, "Unable to create StartingWindowView, lack of materials!");
+                return null;
+            }
+            if (mThemeBGDrawable == null) {
+                Slog.w(TAG, "Theme Background Drawable is null, forget to set Theme Drawable?");
+                mThemeBGDrawable = createDefaultBackgroundDrawable();
+            }
+            processThemeColor();
+            if (!processAdaptiveIcon() && mIconDrawable != null) {
+                if (DEBUG) {
+                    Slog.d(TAG, "The icon is not an AdaptiveIconDrawable");
+                }
+                mFinalIconDrawable = mIconDrawable;
+            }
+            final int iconSize = mFinalIconDrawable != null ? (int) (mIconSize * mScale) : 0;
+            mCachedResult = fillViewWithIcon(mPhoneWindow, mContext, iconSize, mFinalIconDrawable);
+            mBuildComplete = true;
+            return mCachedResult;
+        }
+
+        private void processThemeColor() {
+            final DrawableColorTester themeBGTester =
+                    new DrawableColorTester(mThemeBGDrawable, true /* filterTransparent */);
+            if (themeBGTester.nonTransparentRatio() == 0) {
+                // the window background is transparent, unable to draw
+                Slog.w(TAG, "Window background is transparent, fill background with black color");
+                mThemeColor = getSystemBGColor();
+            } else {
+                mThemeColor = themeBGTester.getDominateColor();
+            }
+        }
+
+        private boolean processAdaptiveIcon() {
+            if (!(mIconDrawable instanceof AdaptiveIconDrawable)) {
+                return false;
+            }
+
+            final AdaptiveIconDrawable adaptiveIconDrawable = (AdaptiveIconDrawable) mIconDrawable;
+            final DrawableColorTester backIconTester =
+                    new DrawableColorTester(adaptiveIconDrawable.getBackground());
+
+            final Drawable iconForeground = adaptiveIconDrawable.getForeground();
+            final DrawableColorTester foreIconTester =
+                    new DrawableColorTester(iconForeground, true /* filterTransparent */);
+
+            final boolean foreComplex = foreIconTester.isComplexColor();
+            final int foreMainColor = foreIconTester.getDominateColor();
+
+            if (DEBUG) {
+                Slog.d(TAG, "foreground complex color? " + foreComplex + " main color: "
+                        + Integer.toHexString(foreMainColor));
+            }
+            final boolean backComplex = backIconTester.isComplexColor();
+            final int backMainColor = backIconTester.getDominateColor();
+            if (DEBUG) {
+                Slog.d(TAG, "background complex color? " + backComplex + " main color: "
+                        + Integer.toHexString(backMainColor));
+                Slog.d(TAG, "theme color? " + Integer.toHexString(mThemeColor));
+            }
+
+            // Only draw the foreground of AdaptiveIcon to the splash screen if below condition
+            // meet:
+            // A. The background of the adaptive icon is not complicated. If it is complicated,
+            // it may contain some information, and
+            // B. The background of the adaptive icon is similar to the theme color, or
+            // C. The background of the adaptive icon is grayscale, and the foreground of the
+            // adaptive icon forms a certain contrast with the theme color.
+            if (!backComplex && (isRgbSimilarInHsv(mThemeColor, backMainColor)
+                    || (backIconTester.isGrayscale()
+                    && !isRgbSimilarInHsv(mThemeColor, foreMainColor)))) {
+                if (DEBUG) {
+                    Slog.d(TAG, "makeSplashScreenContentView: choose fg icon");
+                }
+                // Using AdaptiveIconDrawable here can help keep the shape consistent with the
+                // current settings.
+                mFinalIconDrawable = new AdaptiveIconDrawable(
+                        new ColorDrawable(mThemeColor), iconForeground);
+                // Reference AdaptiveIcon description, outer is 108 and inner is 72, so we
+                // should enlarge the size 108/72 if we only draw adaptiveIcon's foreground.
+                if (foreIconTester.nonTransparentRatio() < ENLARGE_FOREGROUND_ICON_THRESHOLD) {
+                    mScale = 1.5f;
+                }
+            } else {
+                if (DEBUG) {
+                    Slog.d(TAG, "makeSplashScreenContentView: draw whole icon");
+                }
+                mFinalIconDrawable = adaptiveIconDrawable;
+            }
+            return true;
+        }
+
+        private View fillViewWithIcon(PhoneWindow win, Context context,
+                int iconSize, Drawable iconDrawable) {
+            final StartingSurfaceWindowView surfaceWindowView =
+                    new StartingSurfaceWindowView(context, iconSize);
+            surfaceWindowView.setBackground(new ColorDrawable(mThemeColor));
+            if (iconDrawable != null) {
+                surfaceWindowView.setIconDrawable(iconDrawable);
+            }
+            if (DEBUG) {
+                Slog.d(TAG, "fillViewWithIcon surfaceWindowView " + surfaceWindowView);
+            }
+            win.setContentView(surfaceWindowView);
+            makeSystemUIColorsTransparent(win);
+            return surfaceWindowView;
+        }
+
+        private void makeSystemUIColorsTransparent(PhoneWindow win) {
+            win.setStatusBarColor(Color.TRANSPARENT);
+            win.setNavigationBarColor(Color.TRANSPARENT);
+        }
+    }
+
+    private static boolean isRgbSimilarInHsv(int a, int b) {
+        if (a == b) {
+            return true;
+        }
+        final float[] aHsv = new float[3];
+        final float[] bHsv = new float[3];
+        Color.colorToHSV(a, aHsv);
+        Color.colorToHSV(b, bHsv);
+        // Minimum degree of the hue between two colors, the result range is 0-180.
+        int minAngle = (int) Math.abs(aHsv[0] - bHsv[0]);
+        minAngle = (minAngle + 180) % 360 - 180;
+
+        // Calculate the difference between two colors based on the HSV dimensions.
+        final float normalizeH = minAngle / 180f;
+        final double square =  Math.pow(normalizeH, 2)
+                + Math.pow(aHsv[1] - bHsv[1], 2)
+                + Math.pow(aHsv[2] - bHsv[2], 2);
+        final double mean = square / 3;
+        final double root = Math.sqrt(mean);
+        if (DEBUG) {
+            Slog.d(TAG, "hsvDiff " + minAngle + " a: " + Integer.toHexString(a)
+                    + " b " + Integer.toHexString(b) + " ah " + aHsv[0] + " bh " + bHsv[0]
+                    + " root " + root);
+        }
+        return root < 0.1;
+    }
+
+    private static View makeSplashscreenContentDrawable(PhoneWindow win, Context ctx,
+            int splashscreenContentResId) {
+        // doesn't support windowSplashscreenContent after S
+        // TODO add an allowlist to skip some packages if needed
+        final int targetSdkVersion = ctx.getApplicationInfo().targetSdkVersion;
+        if (DEBUG) {
+            Slog.d(TAG, "target sdk for package: " + targetSdkVersion);
+        }
+        if (targetSdkVersion >= Build.VERSION_CODES.S) {
+            return null;
+        }
+        if (splashscreenContentResId == 0) {
+            return null;
+        }
+        final Drawable drawable = ctx.getDrawable(splashscreenContentResId);
+        if (drawable == null) {
+            return null;
+        }
+        View view = new View(ctx);
+        view.setBackground(drawable);
+        win.setContentView(view);
+        return view;
+    }
+
+    private static class DrawableColorTester {
+        private final ColorTester mColorChecker;
+
+        DrawableColorTester(Drawable drawable) {
+            this(drawable, false /* filterTransparent */);
+        }
+
+        DrawableColorTester(Drawable drawable, boolean filterTransparent) {
+            // Some applications use LayerDrawable for their windowBackground. To ensure that we
+            // only get the real background, so that the color is not affected by the alpha of the
+            // upper layer, try to get the lower layer here. This can also speed up the calculation.
+            if (drawable instanceof LayerDrawable) {
+                LayerDrawable layerDrawable = (LayerDrawable) drawable;
+                if (layerDrawable.getNumberOfLayers() > 0) {
+                    if (DEBUG) {
+                        Slog.d(TAG, "replace drawable with bottom layer drawable");
+                    }
+                    drawable = layerDrawable.getDrawable(0);
+                }
+            }
+            mColorChecker = drawable instanceof ColorDrawable
+                    ? new SingleColorTester((ColorDrawable) drawable)
+                    : new ComplexDrawableTester(drawable, filterTransparent);
+        }
+
+        public float nonTransparentRatio() {
+            return mColorChecker.nonTransparentRatio();
+        }
+
+        public boolean isComplexColor() {
+            return mColorChecker.isComplexColor();
+        }
+
+        public int getDominateColor() {
+            return mColorChecker.getDominantColor();
+        }
+
+        public boolean isGrayscale() {
+            return mColorChecker.isGrayscale();
+        }
+
+        /**
+         * A help class to check the color information from a Drawable.
+         */
+        private interface ColorTester {
+            float nonTransparentRatio();
+            boolean isComplexColor();
+            int getDominantColor();
+            boolean isGrayscale();
+        }
+
+        private static boolean isGrayscaleColor(int color) {
+            final int red = Color.red(color);
+            final int green = Color.green(color);
+            final int blue = Color.blue(color);
+            return red == green && green == blue;
+        }
+
+        /**
+         * For ColorDrawable only.
+         * There will be only one color so don't spend too much resource for it.
+         */
+        private static class SingleColorTester implements ColorTester {
+            private final ColorDrawable mColorDrawable;
+
+            SingleColorTester(@NonNull ColorDrawable drawable) {
+                mColorDrawable = drawable;
+            }
+
+            @Override
+            public float nonTransparentRatio() {
+                final int alpha = mColorDrawable.getAlpha();
+                return (float) (alpha / 255);
+            }
+
+            @Override
+            public boolean isComplexColor() {
+                return false;
+            }
+
+            @Override
+            public int getDominantColor() {
+                return mColorDrawable.getColor();
+            }
+
+            @Override
+            public boolean isGrayscale() {
+                return isGrayscaleColor(mColorDrawable.getColor());
+            }
+        }
+
+        /**
+         * For any other Drawable except ColorDrawable.
+         * This will use the Palette API to check the color information and use a quantizer to
+         * filter out transparent colors when needed.
+         */
+        private static class ComplexDrawableTester implements ColorTester {
+            private static final int MAX_BITMAP_SIZE = 40;
+            private final Palette mPalette;
+            private final boolean mFilterTransparent;
+            private static final TransparentFilterQuantizer TRANSPARENT_FILTER_QUANTIZER =
+                    new TransparentFilterQuantizer();
+
+            ComplexDrawableTester(Drawable drawable, boolean filterTransparent) {
+                final Rect initialBounds = drawable.copyBounds();
+                int width = drawable.getIntrinsicWidth();
+                int height = drawable.getIntrinsicHeight();
+
+                // Some drawables do not have intrinsic dimensions
+                if (width <= 0 || height <= 0) {
+                    width = MAX_BITMAP_SIZE;
+                    height = MAX_BITMAP_SIZE;
+                } else {
+                    width = Math.min(width, MAX_BITMAP_SIZE);
+                    height = Math.min(height, MAX_BITMAP_SIZE);
+                }
+
+                final Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+                final Canvas bmpCanvas = new Canvas(bitmap);
+                drawable.setBounds(0, 0, bitmap.getWidth(), bitmap.getHeight());
+                drawable.draw(bmpCanvas);
+                // restore to original bounds
+                drawable.setBounds(initialBounds);
+
+                final Palette.Builder builder = new Palette.Builder(bitmap)
+                        .maximumColorCount(5).clearFilters();
+                // The Palette API will ignore Alpha, so it cannot handle transparent pixels, but
+                // sometimes we will need this information to know if this Drawable object is
+                // transparent.
+                mFilterTransparent = filterTransparent;
+                if (mFilterTransparent) {
+                    builder.setQuantizer(TRANSPARENT_FILTER_QUANTIZER);
+                }
+                mPalette = builder.generate();
+                bitmap.recycle();
+            }
+
+            @Override
+            public float nonTransparentRatio() {
+                return mFilterTransparent ? TRANSPARENT_FILTER_QUANTIZER.mNonTransparentRatio : 1;
+            }
+
+            @Override
+            public boolean isComplexColor() {
+                return mPalette.getSwatches().size() > 1;
+            }
+
+            @Override
+            public int getDominantColor() {
+                final Palette.Swatch mainSwatch = mPalette.getDominantSwatch();
+                if (mainSwatch != null) {
+                    return mainSwatch.getRgb();
+                }
+                return Color.BLACK;
+            }
+
+            @Override
+            public boolean isGrayscale() {
+                final List<Palette.Swatch> swatches = mPalette.getSwatches();
+                if (swatches != null) {
+                    for (int i = swatches.size() - 1; i >= 0; i--) {
+                        Palette.Swatch swatch = swatches.get(i);
+                        if (!isGrayscaleColor(swatch.getRgb())) {
+                            return false;
+                        }
+                    }
+                }
+                return true;
+            }
+
+            private static class TransparentFilterQuantizer implements Quantizer {
+                private static final int NON_TRANSPARENT = 0xFF000000;
+                private final Quantizer mInnerQuantizer = new VariationalKMeansQuantizer();
+                private float mNonTransparentRatio;
+                @Override
+                public void quantize(final int[] pixels, final int maxColors,
+                        final Palette.Filter[] filters) {
+                    mNonTransparentRatio = 0;
+                    int realSize = 0;
+                    for (int i = pixels.length - 1; i > 0; i--) {
+                        if ((pixels[i] & NON_TRANSPARENT) != 0) {
+                            realSize++;
+                        }
+                    }
+                    if (realSize == 0) {
+                        if (DEBUG) {
+                            Slog.d(TAG, "quantize: this is pure transparent image");
+                        }
+                        mInnerQuantizer.quantize(pixels, maxColors, filters);
+                        return;
+                    }
+                    mNonTransparentRatio = (float) realSize / pixels.length;
+                    final int[] samplePixels = new int[realSize];
+                    int rowIndex = 0;
+                    for (int i = pixels.length - 1; i > 0; i--) {
+                        if ((pixels[i] & NON_TRANSPARENT) == NON_TRANSPARENT) {
+                            samplePixels[rowIndex] = pixels[i];
+                            rowIndex++;
+                        }
+                    }
+                    mInnerQuantizer.quantize(samplePixels, maxColors, filters);
+                }
+
+                @Override
+                public List<Palette.Swatch> getQuantizedColors() {
+                    return mInnerQuantizer.getQuantizedColors();
+                }
+            }
+        }
+    }
+
+    private static class StartingSurfaceWindowView extends FrameLayout {
+        // TODO animate the icon view
+        private final View mIconView;
+
+        StartingSurfaceWindowView(Context context, int iconSize) {
+            super(context);
+
+            final boolean emptyIcon = iconSize == 0;
+            if (emptyIcon) {
+                mIconView = null;
+            } else {
+                mIconView = new View(context);
+                FrameLayout.LayoutParams params =
+                        new FrameLayout.LayoutParams(iconSize, iconSize);
+                params.gravity = Gravity.CENTER;
+                addView(mIconView, params);
+            }
+            setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE
+                    | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
+                    | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
+        }
+
+        // TODO support animatable icon
+        void setIconDrawable(Drawable icon) {
+            if (mIconView != null) {
+                mIconView.setBackground(icon);
+            }
+        }
+    }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
index e144ca3..786a1fd 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java
@@ -35,7 +35,6 @@
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
-import android.graphics.drawable.Drawable;
 import android.hardware.display.DisplayManager;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -64,6 +63,7 @@
  * class to remove the starting window of the Task.
  * @hide
  */
+
 public class StartingSurfaceDrawer {
     static final String TAG = StartingSurfaceDrawer.class.getSimpleName();
     static final boolean DEBUG_SPLASH_SCREEN = false;
@@ -72,6 +72,7 @@
     private final Context mContext;
     private final DisplayManager mDisplayManager;
     final ShellExecutor mMainExecutor;
+    private final SplashscreenContentDrawer mSplashscreenContentDrawer;
 
     // TODO(b/131727939) remove this when clearing ActivityRecord
     private static final int REMOVE_WHEN_TIMEOUT = 2000;
@@ -80,6 +81,7 @@
         mContext = context;
         mDisplayManager = mContext.getSystemService(DisplayManager.class);
         mMainExecutor = mainExecutor;
+        mSplashscreenContentDrawer = new SplashscreenContentDrawer(context);
     }
 
     private final SparseArray<StartingWindowRecord> mStartingWindowRecords = new SparseArray<>();
@@ -234,7 +236,6 @@
             return;
         }
         context = displayContext;
-
         if (theme != context.getThemeResId() || labelRes != 0) {
             try {
                 context = context.createPackageContext(
@@ -347,7 +348,12 @@
         }
 
         params.setTitle("Splash Screen " + activityInfo.packageName);
-        addSplashscreenContent(win, context, splashscreenContentResId[0]);
+        final View contentView = mSplashscreenContentDrawer.makeSplashScreenContentView(win,
+                context, iconRes, splashscreenContentResId[0]);
+        if (contentView == null) {
+            Slog.w(TAG, "Adding splash screen window for " + activityInfo.packageName + " failed!");
+            return;
+        }
 
         final View view = win.getDecorView();
 
@@ -459,20 +465,4 @@
             mTaskSnapshotWindow = taskSnapshotWindow;
         }
     }
-
-    private void addSplashscreenContent(PhoneWindow win, Context ctx,
-            int splashscreenContentResId) {
-        if (splashscreenContentResId == 0) {
-            return;
-        }
-        final Drawable drawable = ctx.getDrawable(splashscreenContentResId);
-        if (drawable == null) {
-            return;
-        }
-
-        // We wrap this into a view so the system insets get applied to the drawable.
-        final View v = new View(ctx);
-        v.setBackground(drawable);
-        win.setContentView(v);
-    }
 }
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
index e66d85f..3198725 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
@@ -221,8 +221,7 @@
         mainExecutor.execute(() -> {
             try {
                 final int res = session.addToDisplay(window, layoutParams, View.GONE,
-                        displayId, mTmpInsetsState, tmpFrames.frame,
-                        tmpFrames.displayCutout, tmpInputChannel/* outInputChannel */,
+                        displayId, mTmpInsetsState, tmpFrames.frame, tmpInputChannel,
                         mTmpInsetsState, mTempControls);
                 if (res < 0) {
                     Slog.w(TAG, "Failed to add snapshot starting window res=" + res);
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipMenuTests.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipMenuTests.kt
index 66efb5a..6105f50 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipMenuTests.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvPipMenuTests.kt
@@ -208,8 +208,8 @@
     @Test
     fun pipMenu_customActions_override_mediaControls() {
         // Start media session before entering PiP with custom actions.
-        testApp.clickStartMediaSessionButton()
         testApp.checkWithCustomActionsCheckbox()
+        testApp.clickStartMediaSessionButton()
         enterPip_openMenu_assertShown()
 
         // PiP menu should contain "No-Op", "Off" and "Clear" buttons for the custom actions...
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvUtils.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvUtils.kt
index 587b551..1b73920 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvUtils.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/tv/TvUtils.kt
@@ -26,78 +26,109 @@
 
 /** Id of the root view in the com.android.wm.shell.pip.tv.PipMenuActivity */
 private const val TV_PIP_MENU_ROOT_ID = "tv_pip_menu"
-private const val TV_PIP_MENU_CONTROLS_ID = "pip_controls"
-private const val TV_PIP_MENU_CLOSE_BUTTON_ID = "close_button"
-private const val TV_PIP_MENU_FULLSCREEN_BUTTON_ID = "full_button"
+private const val TV_PIP_MENU_BUTTONS_CONTAINER_ID = "tv_pip_menu_action_buttons"
+private const val TV_PIP_MENU_CLOSE_BUTTON_ID = "tv_pip_menu_close_button"
+private const val TV_PIP_MENU_FULLSCREEN_BUTTON_ID = "tv_pip_menu_fullscreen_button"
 
 private const val FOCUS_ATTEMPTS = 10
 private const val WAIT_TIME_MS = 3_000L
 
+private val TV_PIP_MENU_SELECTOR =
+        By.res(SYSTEM_UI_PACKAGE_NAME, TV_PIP_MENU_ROOT_ID)
+private val TV_PIP_MENU_BUTTONS_CONTAINER_SELECTOR =
+        By.res(SYSTEM_UI_PACKAGE_NAME, TV_PIP_MENU_BUTTONS_CONTAINER_ID)
 private val TV_PIP_MENU_CLOSE_BUTTON_SELECTOR =
         By.res(SYSTEM_UI_PACKAGE_NAME, TV_PIP_MENU_CLOSE_BUTTON_ID)
 private val TV_PIP_MENU_FULLSCREEN_BUTTON_SELECTOR =
         By.res(SYSTEM_UI_PACKAGE_NAME, TV_PIP_MENU_FULLSCREEN_BUTTON_ID)
 
-private val tvPipMenuSelector = By.res(SYSTEM_UI_PACKAGE_NAME, TV_PIP_MENU_ROOT_ID)
-
-fun UiDevice.pressWindowKey() = pressKeyCode(KeyEvent.KEYCODE_WINDOW)
-
 fun UiDevice.waitForTvPipMenu(): UiObject2? =
-        wait(Until.findObject(tvPipMenuSelector), WAIT_TIME_MS)
+        wait(Until.findObject(TV_PIP_MENU_SELECTOR), WAIT_TIME_MS)
 
-fun UiDevice.waitForTvPipMenuToClose(): Boolean = wait(Until.gone(tvPipMenuSelector), WAIT_TIME_MS)
+fun UiDevice.waitForTvPipMenuToClose(): Boolean =
+        wait(Until.gone(TV_PIP_MENU_SELECTOR), WAIT_TIME_MS)
 
 fun UiDevice.findTvPipMenuControls(): UiObject2? =
-        findObject(tvPipMenuSelector)
-                ?.findObject(By.res(SYSTEM_UI_PACKAGE_NAME, TV_PIP_MENU_CONTROLS_ID))
+        findTvPipMenuElement(TV_PIP_MENU_BUTTONS_CONTAINER_SELECTOR)
 
 fun UiDevice.findTvPipMenuCloseButton(): UiObject2? =
-        findObject(tvPipMenuSelector)?.findObject(TV_PIP_MENU_CLOSE_BUTTON_SELECTOR)
-
-fun UiDevice.clickTvPipMenuCloseButton() {
-    focusOnObjectInTvPipMenu(TV_PIP_MENU_CLOSE_BUTTON_SELECTOR) ||
-            error("Could not focus on the Close button")
-    pressDPadCenter()
-}
+        findTvPipMenuElement(TV_PIP_MENU_CLOSE_BUTTON_SELECTOR)
 
 fun UiDevice.findTvPipMenuFullscreenButton(): UiObject2? =
-        findObject(tvPipMenuSelector)?.findObject(TV_PIP_MENU_FULLSCREEN_BUTTON_SELECTOR)
-
-fun UiDevice.clickTvPipMenuFullscreenButton() {
-    focusOnObjectInTvPipMenu(TV_PIP_MENU_FULLSCREEN_BUTTON_SELECTOR) ||
-            error("Could not focus on the Fullscreen button")
-    pressDPadCenter()
-}
+        findTvPipMenuElement(TV_PIP_MENU_FULLSCREEN_BUTTON_SELECTOR)
 
 fun UiDevice.findTvPipMenuElementWithDescription(desc: String): UiObject2? =
-        findObject(tvPipMenuSelector)?.findObject(By.desc(desc).pkg(SYSTEM_UI_PACKAGE_NAME))
+        findTvPipMenuElement(By.desc(desc))
 
-fun UiDevice.clickTvPipMenuElementWithDescription(desc: String) {
-    focusOnObjectInTvPipMenu(By.desc(desc).pkg(SYSTEM_UI_PACKAGE_NAME)) ||
-            error("Could not focus on the Pip menu object with \"$desc\" description")
-    pressDPadCenter()
-}
+private fun UiDevice.findTvPipMenuElement(selector: BySelector): UiObject2? =
+    findObject(TV_PIP_MENU_SELECTOR)?.findObject(selector)
 
 fun UiDevice.waitForTvPipMenuElementWithDescription(desc: String): UiObject2? {
-    val buttonSelector = By.desc(desc)
-    val menuWithButtonSelector = By.copy(tvPipMenuSelector).hasDescendant(buttonSelector)
-    return wait(Until.findObject(menuWithButtonSelector), WAIT_TIME_MS)
-            ?.findObject(buttonSelector)
+    // Ideally, we'd want to wait for an element with the given description that has the Pip Menu as
+    // its parent, but the API does not allow us to construct a query exactly that way.
+    // So instead we'll wait for a Pip Menu that has the element, which we are looking for, as a
+    // descendant and then retrieve the element from the menu and return to the caller of this
+    // method.
+    val elementSelector = By.desc(desc)
+    val menuContainingElementSelector = By.copy(TV_PIP_MENU_SELECTOR).hasDescendant(elementSelector)
+
+    return wait(Until.findObject(menuContainingElementSelector), WAIT_TIME_MS)
+            ?.findObject(elementSelector)
 }
 
-fun UiDevice.waitUntilTvPipMenuElementWithDescriptionIsGone(desc: String): Boolean? =
-    wait(Until.gone(By.copy(tvPipMenuSelector).hasDescendant(By.desc(desc))), WAIT_TIME_MS)
+fun UiDevice.waitUntilTvPipMenuElementWithDescriptionIsGone(desc: String): Boolean? {
+    val elementSelector = By.desc(desc)
+    val menuContainingElementSelector = By.copy(TV_PIP_MENU_SELECTOR).hasDescendant(elementSelector)
 
-fun UiObject2.isFullscreen(uiDevice: UiDevice): Boolean = visibleBounds.run {
-    height() == uiDevice.displayHeight && width() == uiDevice.displayWidth
+    return wait(Until.gone(menuContainingElementSelector), WAIT_TIME_MS)
 }
 
-val UiObject2.isFocusedOrHasFocusedChild: Boolean
-    get() = isFocused || findObject(By.focused(true)) != null
+fun UiDevice.clickTvPipMenuCloseButton() {
+    focusOnAndClickTvPipMenuElement(TV_PIP_MENU_CLOSE_BUTTON_SELECTOR) ||
+            error("Could not focus on the Close button")
+}
+
+fun UiDevice.clickTvPipMenuFullscreenButton() {
+    focusOnAndClickTvPipMenuElement(TV_PIP_MENU_FULLSCREEN_BUTTON_SELECTOR) ||
+            error("Could not focus on the Fullscreen button")
+}
+
+fun UiDevice.clickTvPipMenuElementWithDescription(desc: String) {
+    focusOnAndClickTvPipMenuElement(By.desc(desc).pkg(SYSTEM_UI_PACKAGE_NAME)) ||
+            error("Could not focus on the Pip menu object with \"$desc\" description")
+    // So apparently Accessibility framework on TV is not very reliable and sometimes the state of
+    // the tree of accessibility nodes as seen by the accessibility clients kind of lags behind of
+    // the "real" state of the "UI tree". It seems, however, that moving focus around the tree
+    // forces the AccessibilityNodeInfo tree to get properly updated.
+    // So since we suspect that clicking on a Pip Menu element may cause some UI changes and we want
+    // those changes to be seen by the UiAutomator, which is using Accessibility framework under the
+    // hood for inspecting UI, we'll move the focus around a little.
+    moveFocus()
+}
+
+private fun UiDevice.focusOnAndClickTvPipMenuElement(selector: BySelector): Boolean {
+    repeat(FOCUS_ATTEMPTS) {
+        val element = findTvPipMenuElement(selector)
+                ?: error("The Pip Menu element we try to focus on is gone.")
+
+        if (element.isFocusedOrHasFocusedChild) {
+            pressDPadCenter()
+            return true
+        }
+
+        findTvPipMenuElement(By.focused(true))?.let { focused ->
+            if (element.visibleCenter.x < focused.visibleCenter.x)
+                pressDPadLeft() else pressDPadRight()
+            waitForIdle()
+        } ?: error("Pip menu does not contain a focused element")
+    }
+
+    return false
+}
 
 fun UiDevice.closeTvPipWindow() {
     // Check if Pip menu is Open. If it's not, open it.
-    if (findObject(tvPipMenuSelector) == null) {
+    if (findObject(TV_PIP_MENU_SELECTOR) == null) {
         pressWindowKey()
         waitForTvPipMenu() ?: error("Could not open Pip menu")
     }
@@ -106,17 +137,25 @@
     waitForTvPipMenuToClose()
 }
 
-private fun UiDevice.focusOnObjectInTvPipMenu(objectSelector: BySelector): Boolean {
-    repeat(FOCUS_ATTEMPTS) {
-        val menu = findObject(tvPipMenuSelector) ?: error("Pip Menu is now shown")
-        val objectToFocus = menu.findObject(objectSelector)
-                .apply { if (isFocusedOrHasFocusedChild) return true }
-                ?: error("The object we try to focus on is gone.")
-        val currentlyFocused = menu.findObject(By.focused(true))
-                ?: error("Pip menu does not contain a focused element")
-        if (objectToFocus.visibleCenter.x < currentlyFocused.visibleCenter.x)
-            pressDPadLeft() else pressDPadRight()
-        waitForIdle()
-    }
-    return false
-}
\ No newline at end of file
+/**
+ * Simply presses the D-Pad Left and Right buttons once, which should move the focus on the screen,
+ * which should cause Accessibility events to be fired, which should, hopefully, properly update
+ * AccessibilityNodeInfo tree dispatched by the platform to the Accessibility services, one of which
+ * is the UiAutomator.
+ */
+private fun UiDevice.moveFocus() {
+    waitForIdle()
+    pressDPadLeft()
+    waitForIdle()
+    pressDPadRight()
+    waitForIdle()
+}
+
+fun UiDevice.pressWindowKey() = pressKeyCode(KeyEvent.KEYCODE_WINDOW)
+
+fun UiObject2.isFullscreen(uiDevice: UiDevice): Boolean = visibleBounds.run {
+    height() == uiDevice.displayHeight && width() == uiDevice.displayWidth
+}
+
+val UiObject2.isFocusedOrHasFocusedChild: Boolean
+    get() = isFocused || findObject(By.focused(true)) != null
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index a2028ca..6bf2e99 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -306,25 +306,29 @@
 
 struct DrawImage final : Op {
     static const auto kType = Type::DrawImage;
-    DrawImage(sk_sp<const SkImage>&& image, SkScalar x, SkScalar y, const SkPaint* paint,
-              BitmapPalette palette)
-            : image(std::move(image)), x(x), y(y), palette(palette) {
+    DrawImage(sk_sp<const SkImage>&& image, SkScalar x, SkScalar y,
+              const SkSamplingOptions& sampling, const SkPaint* paint, BitmapPalette palette)
+            : image(std::move(image)), x(x), y(y), sampling(sampling), palette(palette) {
         if (paint) {
             this->paint = *paint;
         }
     }
     sk_sp<const SkImage> image;
     SkScalar x, y;
+    SkSamplingOptions sampling;
     SkPaint paint;
     BitmapPalette palette;
-    void draw(SkCanvas* c, const SkMatrix&) const { c->drawImage(image.get(), x, y, &paint); }
+    void draw(SkCanvas* c, const SkMatrix&) const {
+        c->drawImage(image.get(), x, y, sampling, &paint);
+    }
 };
 struct DrawImageRect final : Op {
     static const auto kType = Type::DrawImageRect;
     DrawImageRect(sk_sp<const SkImage>&& image, const SkRect* src, const SkRect& dst,
-                  const SkPaint* paint, SkCanvas::SrcRectConstraint constraint,
-                  BitmapPalette palette)
-            : image(std::move(image)), dst(dst), constraint(constraint), palette(palette) {
+                  const SkSamplingOptions& sampling, const SkPaint* paint,
+                  SkCanvas::SrcRectConstraint constraint, BitmapPalette palette)
+            : image(std::move(image)), dst(dst), sampling(sampling), constraint(constraint)
+            , palette(palette) {
         this->src = src ? *src : SkRect::MakeIWH(this->image->width(), this->image->height());
         if (paint) {
             this->paint = *paint;
@@ -332,23 +336,26 @@
     }
     sk_sp<const SkImage> image;
     SkRect src, dst;
+    SkSamplingOptions sampling;
     SkPaint paint;
     SkCanvas::SrcRectConstraint constraint;
     BitmapPalette palette;
     void draw(SkCanvas* c, const SkMatrix&) const {
-        c->drawImageRect(image.get(), src, dst, &paint, constraint);
+        c->drawImageRect(image.get(), src, dst, sampling, &paint, constraint);
     }
 };
 struct DrawImageLattice final : Op {
     static const auto kType = Type::DrawImageLattice;
     DrawImageLattice(sk_sp<const SkImage>&& image, int xs, int ys, int fs, const SkIRect& src,
-                     const SkRect& dst, const SkPaint* paint, BitmapPalette palette)
+                     const SkRect& dst, SkFilterMode filter, const SkPaint* paint,
+                     BitmapPalette palette)
             : image(std::move(image))
             , xs(xs)
             , ys(ys)
             , fs(fs)
             , src(src)
             , dst(dst)
+            , filter(filter)
             , palette(palette) {
         if (paint) {
             this->paint = *paint;
@@ -358,6 +365,7 @@
     int xs, ys, fs;
     SkIRect src;
     SkRect dst;
+    SkFilterMode filter;
     SkPaint paint;
     BitmapPalette palette;
     void draw(SkCanvas* c, const SkMatrix&) const {
@@ -366,7 +374,8 @@
         auto flags =
                 (0 == fs) ? nullptr : pod<SkCanvas::Lattice::RectType>(
                                               this, (xs + ys) * sizeof(int) + fs * sizeof(SkColor));
-        c->drawImageLattice(image.get(), {xdivs, ydivs, flags, xs, ys, &src, colors}, dst, &paint);
+        c->drawImageLattice(image.get(), {xdivs, ydivs, flags, xs, ys, &src, colors}, dst,
+                            filter, &paint);
     }
 };
 
@@ -431,9 +440,10 @@
 };
 struct DrawAtlas final : Op {
     static const auto kType = Type::DrawAtlas;
-    DrawAtlas(const SkImage* atlas, int count, SkBlendMode xfermode, const SkRect* cull,
-              const SkPaint* paint, bool has_colors)
-            : atlas(sk_ref_sp(atlas)), count(count), xfermode(xfermode), has_colors(has_colors) {
+    DrawAtlas(const SkImage* atlas, int count, SkBlendMode mode, const SkSamplingOptions& sampling,
+              const SkRect* cull, const SkPaint* paint, bool has_colors)
+            : atlas(sk_ref_sp(atlas)), count(count), mode(mode), sampling(sampling)
+            , has_colors(has_colors) {
         if (cull) {
             this->cull = *cull;
         }
@@ -443,7 +453,8 @@
     }
     sk_sp<const SkImage> atlas;
     int count;
-    SkBlendMode xfermode;
+    SkBlendMode mode;
+    SkSamplingOptions sampling;
     SkRect cull = kUnset;
     SkPaint paint;
     bool has_colors;
@@ -452,7 +463,8 @@
         auto texs = pod<SkRect>(this, count * sizeof(SkRSXform));
         auto colors = has_colors ? pod<SkColor>(this, count * (sizeof(SkRSXform) + sizeof(SkRect)))
                                  : nullptr;
-        c->drawAtlas(atlas.get(), xforms, texs, colors, count, xfermode, maybe_unset(cull), &paint);
+        c->drawAtlas(atlas.get(), xforms, texs, colors, count, mode, sampling, maybe_unset(cull),
+                     &paint);
     }
 };
 struct DrawShadowRec final : Op {
@@ -613,16 +625,18 @@
     this->push<DrawPicture>(0, picture, matrix, paint);
 }
 void DisplayListData::drawImage(sk_sp<const SkImage> image, SkScalar x, SkScalar y,
-                                const SkPaint* paint, BitmapPalette palette) {
-    this->push<DrawImage>(0, std::move(image), x, y, paint, palette);
+                                const SkSamplingOptions& sampling, const SkPaint* paint,
+                                BitmapPalette palette) {
+    this->push<DrawImage>(0, std::move(image), x, y, sampling, paint, palette);
 }
 void DisplayListData::drawImageRect(sk_sp<const SkImage> image, const SkRect* src,
-                                    const SkRect& dst, const SkPaint* paint,
-                                    SkCanvas::SrcRectConstraint constraint, BitmapPalette palette) {
-    this->push<DrawImageRect>(0, std::move(image), src, dst, paint, constraint, palette);
+                                    const SkRect& dst, const SkSamplingOptions& sampling,
+                                    const SkPaint* paint, SkCanvas::SrcRectConstraint constraint,
+                                    BitmapPalette palette) {
+    this->push<DrawImageRect>(0, std::move(image), src, dst, sampling, paint, constraint, palette);
 }
 void DisplayListData::drawImageLattice(sk_sp<const SkImage> image, const SkCanvas::Lattice& lattice,
-                                       const SkRect& dst, const SkPaint* paint,
+                                       const SkRect& dst, SkFilterMode filter, const SkPaint* paint,
                                        BitmapPalette palette) {
     int xs = lattice.fXCount, ys = lattice.fYCount;
     int fs = lattice.fRectTypes ? (xs + 1) * (ys + 1) : 0;
@@ -630,7 +644,7 @@
                    fs * sizeof(SkColor);
     SkASSERT(lattice.fBounds);
     void* pod = this->push<DrawImageLattice>(bytes, std::move(image), xs, ys, fs, *lattice.fBounds,
-                                             dst, paint, palette);
+                                             dst, filter, paint, palette);
     copy_v(pod, lattice.fXDivs, xs, lattice.fYDivs, ys, lattice.fColors, fs, lattice.fRectTypes,
            fs);
 }
@@ -650,18 +664,19 @@
     void* pod = this->push<DrawPoints>(count * sizeof(SkPoint), mode, count, paint);
     copy_v(pod, points, count);
 }
-void DisplayListData::drawVertices(const SkVertices* vertices, SkBlendMode mode, const SkPaint& paint) {
-    this->push<DrawVertices>(0, vertices, mode, paint);
+void DisplayListData::drawVertices(const SkVertices* vert, SkBlendMode mode, const SkPaint& paint) {
+    this->push<DrawVertices>(0, vert, mode, paint);
 }
 void DisplayListData::drawAtlas(const SkImage* atlas, const SkRSXform xforms[], const SkRect texs[],
                                 const SkColor colors[], int count, SkBlendMode xfermode,
-                                const SkRect* cull, const SkPaint* paint) {
+                                const SkSamplingOptions& sampling, const SkRect* cull,
+                                const SkPaint* paint) {
     size_t bytes = count * (sizeof(SkRSXform) + sizeof(SkRect));
     if (colors) {
         bytes += count * sizeof(SkColor);
     }
-    void* pod =
-            this->push<DrawAtlas>(bytes, atlas, count, xfermode, cull, paint, colors != nullptr);
+    void* pod = this->push<DrawAtlas>(bytes, atlas, count, xfermode, sampling, cull, paint,
+                                      colors != nullptr);
     copy_v(pod, xforms, count, texs, count, colors, colors ? count : 0);
 }
 void DisplayListData::drawShadowRec(const SkPath& path, const SkDrawShadowRec& rec) {
@@ -887,18 +902,20 @@
 }
 
 void RecordingCanvas::drawImage(const sk_sp<SkImage>& image, SkScalar x, SkScalar y,
-                                const SkPaint* paint, BitmapPalette palette) {
-    fDL->drawImage(image, x, y, paint, palette);
+                                const SkSamplingOptions& sampling, const SkPaint* paint,
+                                BitmapPalette palette) {
+    fDL->drawImage(image, x, y, sampling, paint, palette);
 }
 
 void RecordingCanvas::drawImageRect(const sk_sp<SkImage>& image, const SkRect& src,
-                                    const SkRect& dst, const SkPaint* paint,
-                                    SrcRectConstraint constraint, BitmapPalette palette) {
-    fDL->drawImageRect(image, &src, dst, paint, constraint, palette);
+                                    const SkRect& dst, const SkSamplingOptions& sampling,
+                                    const SkPaint* paint, SrcRectConstraint constraint,
+                                    BitmapPalette palette) {
+    fDL->drawImageRect(image, &src, dst, sampling, paint, constraint, palette);
 }
 
 void RecordingCanvas::drawImageLattice(const sk_sp<SkImage>& image, const Lattice& lattice,
-                                       const SkRect& dst, const SkPaint* paint,
+                                       const SkRect& dst, SkFilterMode filter, const SkPaint* paint,
                                        BitmapPalette palette) {
     if (!image || dst.isEmpty()) {
         return;
@@ -912,24 +929,29 @@
     }
 
     if (SkLatticeIter::Valid(image->width(), image->height(), latticePlusBounds)) {
-        fDL->drawImageLattice(image, latticePlusBounds, dst, paint, palette);
+        fDL->drawImageLattice(image, latticePlusBounds, dst, filter, paint, palette);
     } else {
-        fDL->drawImageRect(image, nullptr, dst, paint, SrcRectConstraint::kFast_SrcRectConstraint,
-                           palette);
+        SkSamplingOptions sampling(filter, SkMipmapMode::kNone);
+        fDL->drawImageRect(image, nullptr, dst, sampling, paint, kFast_SrcRectConstraint, palette);
     }
 }
 
-void RecordingCanvas::onDrawImage(const SkImage* img, SkScalar x, SkScalar y,
-                                  const SkPaint* paint) {
-    fDL->drawImage(sk_ref_sp(img), x, y, paint, BitmapPalette::Unknown);
+void RecordingCanvas::onDrawImage2(const SkImage* img, SkScalar x, SkScalar y,
+                                   const SkSamplingOptions& sampling, const SkPaint* paint) {
+    fDL->drawImage(sk_ref_sp(img), x, y, sampling, paint, BitmapPalette::Unknown);
 }
-void RecordingCanvas::onDrawImageRect(const SkImage* img, const SkRect* src, const SkRect& dst,
-                                      const SkPaint* paint, SrcRectConstraint constraint) {
-    fDL->drawImageRect(sk_ref_sp(img), src, dst, paint, constraint, BitmapPalette::Unknown);
+
+void RecordingCanvas::onDrawImageRect2(const SkImage* img, const SkRect& src, const SkRect& dst,
+                                       const SkSamplingOptions& sampling, const SkPaint* paint,
+                                       SrcRectConstraint constraint) {
+    fDL->drawImageRect(sk_ref_sp(img), &src, dst, sampling, paint, constraint,
+                       BitmapPalette::Unknown);
 }
-void RecordingCanvas::onDrawImageLattice(const SkImage* img, const SkCanvas::Lattice& lattice,
-                                         const SkRect& dst, const SkPaint* paint) {
-    fDL->drawImageLattice(sk_ref_sp(img), lattice, dst, paint, BitmapPalette::Unknown);
+
+void RecordingCanvas::onDrawImageLattice2(const SkImage* img, const SkCanvas::Lattice& lattice,
+                                          const SkRect& dst, SkFilterMode filter,
+                                          const SkPaint* paint) {
+    fDL->drawImageLattice(sk_ref_sp(img), lattice, dst, filter, paint, BitmapPalette::Unknown);
 }
 
 void RecordingCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
@@ -945,10 +967,11 @@
                                            SkBlendMode mode, const SkPaint& paint) {
     fDL->drawVertices(vertices, mode, paint);
 }
-void RecordingCanvas::onDrawAtlas(const SkImage* atlas, const SkRSXform xforms[],
-                                  const SkRect texs[], const SkColor colors[], int count,
-                                  SkBlendMode bmode, const SkRect* cull, const SkPaint* paint) {
-    fDL->drawAtlas(atlas, xforms, texs, colors, count, bmode, cull, paint);
+void RecordingCanvas::onDrawAtlas2(const SkImage* atlas, const SkRSXform xforms[],
+                                   const SkRect texs[], const SkColor colors[], int count,
+                                   SkBlendMode bmode, const SkSamplingOptions& sampling,
+                                   const SkRect* cull, const SkPaint* paint) {
+    fDL->drawAtlas(atlas, xforms, texs, colors, count, bmode, sampling, cull, paint);
 }
 void RecordingCanvas::onDrawShadowRec(const SkPath& path, const SkDrawShadowRec& rec) {
     fDL->drawShadowRec(path, rec);
diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h
index 9e2b0a9..89e3df7 100644
--- a/libs/hwui/RecordingCanvas.h
+++ b/libs/hwui/RecordingCanvas.h
@@ -108,19 +108,20 @@
 
     void drawTextBlob(const SkTextBlob*, SkScalar, SkScalar, const SkPaint&);
 
-    void drawImage(sk_sp<const SkImage>, SkScalar, SkScalar, const SkPaint*, BitmapPalette palette);
+    void drawImage(sk_sp<const SkImage>, SkScalar, SkScalar, const SkSamplingOptions&,
+                   const SkPaint*, BitmapPalette palette);
     void drawImageNine(sk_sp<const SkImage>, const SkIRect&, const SkRect&, const SkPaint*);
-    void drawImageRect(sk_sp<const SkImage>, const SkRect*, const SkRect&, const SkPaint*,
-                       SkCanvas::SrcRectConstraint, BitmapPalette palette);
+    void drawImageRect(sk_sp<const SkImage>, const SkRect*, const SkRect&, const SkSamplingOptions&,
+                       const SkPaint*, SkCanvas::SrcRectConstraint, BitmapPalette palette);
     void drawImageLattice(sk_sp<const SkImage>, const SkCanvas::Lattice&, const SkRect&,
-                          const SkPaint*, BitmapPalette);
+                          SkFilterMode, const SkPaint*, BitmapPalette);
 
     void drawPatch(const SkPoint[12], const SkColor[4], const SkPoint[4], SkBlendMode,
                    const SkPaint&);
     void drawPoints(SkCanvas::PointMode, size_t, const SkPoint[], const SkPaint&);
     void drawVertices(const SkVertices*, SkBlendMode, const SkPaint&);
     void drawAtlas(const SkImage*, const SkRSXform[], const SkRect[], const SkColor[], int,
-                   SkBlendMode, const SkRect*, const SkPaint*);
+                   SkBlendMode, const SkSamplingOptions&, const SkRect*, const SkPaint*);
     void drawShadowRec(const SkPath&, const SkDrawShadowRec&);
     void drawVectorDrawable(VectorDrawableRoot* tree);
     void drawWebView(skiapipeline::FunctorDrawable*);
@@ -178,25 +179,27 @@
 
     void onDrawTextBlob(const SkTextBlob*, SkScalar, SkScalar, const SkPaint&) override;
 
-    void drawImage(const sk_sp<SkImage>& image, SkScalar left, SkScalar top, const SkPaint* paint,
-                   BitmapPalette pallete);
+    void drawImage(const sk_sp<SkImage>&, SkScalar left, SkScalar top, const SkSamplingOptions&,
+                   const SkPaint* paint, BitmapPalette pallete);
 
     void drawImageRect(const sk_sp<SkImage>& image, const SkRect& src, const SkRect& dst,
-                       const SkPaint* paint, SrcRectConstraint constraint, BitmapPalette palette);
+                       const SkSamplingOptions&, const SkPaint*, SrcRectConstraint, BitmapPalette);
     void drawImageLattice(const sk_sp<SkImage>& image, const Lattice& lattice, const SkRect& dst,
-                          const SkPaint* paint, BitmapPalette palette);
+                          SkFilterMode, const SkPaint* paint, BitmapPalette palette);
 
-    void onDrawImage(const SkImage*, SkScalar, SkScalar, const SkPaint*) override;
-    void onDrawImageLattice(const SkImage*, const Lattice&, const SkRect&, const SkPaint*) override;
-    void onDrawImageRect(const SkImage*, const SkRect*, const SkRect&, const SkPaint*,
-                         SrcRectConstraint) override;
+    void onDrawImage2(const SkImage*, SkScalar, SkScalar, const SkSamplingOptions&,
+                      const SkPaint*) override;
+    void onDrawImageLattice2(const SkImage*, const Lattice&, const SkRect&, SkFilterMode,
+                             const SkPaint*) override;
+    void onDrawImageRect2(const SkImage*, const SkRect&, const SkRect&, const SkSamplingOptions&,
+                          const SkPaint*, SrcRectConstraint) override;
 
     void onDrawPatch(const SkPoint[12], const SkColor[4], const SkPoint[4], SkBlendMode,
                      const SkPaint&) override;
     void onDrawPoints(PointMode, size_t count, const SkPoint pts[], const SkPaint&) override;
     void onDrawVerticesObject(const SkVertices*, SkBlendMode, const SkPaint&) override;
-    void onDrawAtlas(const SkImage*, const SkRSXform[], const SkRect[], const SkColor[], int,
-                     SkBlendMode, const SkRect*, const SkPaint*) override;
+    void onDrawAtlas2(const SkImage*, const SkRSXform[], const SkRect[], const SkColor[], int,
+                     SkBlendMode, const SkSamplingOptions&, const SkRect*, const SkPaint*) override;
     void onDrawShadowRec(const SkPath&, const SkDrawShadowRec&) override;
 
     void drawVectorDrawable(VectorDrawableRoot* tree);
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index acb74f4..815ffde 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -814,6 +814,18 @@
     mCanvas->drawDrawable(drawable.get());
 }
 
+void SkiaCanvas::drawRipple(uirenderer::CanvasPropertyPrimitive* x,
+                            uirenderer::CanvasPropertyPrimitive* y,
+                            uirenderer::CanvasPropertyPrimitive* radius,
+                            uirenderer::CanvasPropertyPaint* paint,
+                            uirenderer::CanvasPropertyPrimitive* progress,
+                            sk_sp<SkRuntimeEffect> runtimeEffect) {
+    sk_sp<uirenderer::skiapipeline::AnimatedRipple> drawable(
+            new uirenderer::skiapipeline::AnimatedRipple(x, y, radius, paint, progress,
+                                                         runtimeEffect));
+    mCanvas->drawDrawable(drawable.get());
+}
+
 void SkiaCanvas::drawPicture(const SkPicture& picture) {
     // TODO: Change to mCanvas->drawPicture()? SkCanvas::drawPicture seems to be
     // where the logic is for playback vs. ref picture. Using picture.playback here
diff --git a/libs/hwui/SkiaCanvas.h b/libs/hwui/SkiaCanvas.h
index 591ae5c..fa7d373 100644
--- a/libs/hwui/SkiaCanvas.h
+++ b/libs/hwui/SkiaCanvas.h
@@ -147,6 +147,12 @@
                             uirenderer::CanvasPropertyPrimitive* y,
                             uirenderer::CanvasPropertyPrimitive* radius,
                             uirenderer::CanvasPropertyPaint* paint) override;
+    virtual void drawRipple(uirenderer::CanvasPropertyPrimitive* x,
+                            uirenderer::CanvasPropertyPrimitive* y,
+                            uirenderer::CanvasPropertyPrimitive* radius,
+                            uirenderer::CanvasPropertyPaint* paint,
+                            uirenderer::CanvasPropertyPrimitive* progress,
+                            sk_sp<SkRuntimeEffect> runtimeEffect) override;
 
     virtual void drawLayer(uirenderer::DeferredLayerUpdater* layerHandle) override;
     virtual void drawRenderNode(uirenderer::RenderNode* renderNode) override;
@@ -190,7 +196,6 @@
         }
         operator const SkPaint*() const { return mPtr; }
         const SkPaint* operator->() const { assert(mPtr); return mPtr; }
-        const SkPaint& operator*() const { assert(mPtr); return *mPtr; }
         explicit operator bool() { return mPtr != nullptr; }
     private:
         const SkPaint* mPtr;
diff --git a/libs/hwui/canvas/CanvasOpTypes.h b/libs/hwui/canvas/CanvasOpTypes.h
index f0aa777..cde50bd 100644
--- a/libs/hwui/canvas/CanvasOpTypes.h
+++ b/libs/hwui/canvas/CanvasOpTypes.h
@@ -42,6 +42,7 @@
     DrawRoundRectProperty,
     DrawDoubleRoundRect,
     DrawCircleProperty,
+    DrawRippleProperty,
     DrawCircle,
     DrawOval,
     DrawArc,
diff --git a/libs/hwui/canvas/CanvasOps.h b/libs/hwui/canvas/CanvasOps.h
index 62c26c7..ea9fea97 100644
--- a/libs/hwui/canvas/CanvasOps.h
+++ b/libs/hwui/canvas/CanvasOps.h
@@ -23,6 +23,7 @@
 #include <SkVertices.h>
 #include <SkImage.h>
 #include <SkPicture.h>
+#include <SkRuntimeEffect.h>
 #include <hwui/Bitmap.h>
 #include <log/log.h>
 #include "CanvasProperty.h"
@@ -142,6 +143,42 @@
     ASSERT_DRAWABLE()
 };
 
+template<>
+struct CanvasOp<CanvasOpType::DrawRippleProperty> {
+    sp<uirenderer::CanvasPropertyPrimitive> x;
+    sp<uirenderer::CanvasPropertyPrimitive> y;
+    sp<uirenderer::CanvasPropertyPrimitive> radius;
+    sp<uirenderer::CanvasPropertyPaint> paint;
+    sp<uirenderer::CanvasPropertyPrimitive> progress;
+    sk_sp<SkRuntimeEffect> effect;
+
+    void draw(SkCanvas* canvas) const {
+        SkRuntimeShaderBuilder runtimeEffectBuilder(effect);
+
+        SkRuntimeShaderBuilder::BuilderUniform center = runtimeEffectBuilder.uniform("in_origin");
+        if (center.fVar != nullptr) {
+            center = SkV2{x->value, y->value};
+        }
+
+        SkRuntimeShaderBuilder::BuilderUniform radiusU =
+                runtimeEffectBuilder.uniform("in_maxRadius");
+        if (radiusU.fVar != nullptr) {
+            radiusU = radius->value;
+        }
+
+        SkRuntimeShaderBuilder::BuilderUniform progressU =
+                runtimeEffectBuilder.uniform("in_progress");
+        if (progressU.fVar != nullptr) {
+            progressU = progress->value;
+        }
+
+        SkPaint paintMod = paint->value;
+        paintMod.setShader(runtimeEffectBuilder.makeShader(nullptr, false));
+        canvas->drawCircle(x->value, y->value, radius->value, paintMod);
+    }
+    ASSERT_DRAWABLE()
+};
+
 template <>
 struct CanvasOp<CanvasOpType::DrawColor> {
     SkColor4f color;
diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h
index 11fa322..d0c996b 100644
--- a/libs/hwui/hwui/Canvas.h
+++ b/libs/hwui/hwui/Canvas.h
@@ -31,6 +31,7 @@
 
 class SkAnimatedImage;
 class SkCanvasState;
+class SkRuntimeEffect;
 class SkVertices;
 
 namespace minikin {
@@ -133,6 +134,12 @@
                             uirenderer::CanvasPropertyPrimitive* y,
                             uirenderer::CanvasPropertyPrimitive* radius,
                             uirenderer::CanvasPropertyPaint* paint) = 0;
+    virtual void drawRipple(uirenderer::CanvasPropertyPrimitive* x,
+                            uirenderer::CanvasPropertyPrimitive* y,
+                            uirenderer::CanvasPropertyPrimitive* radius,
+                            uirenderer::CanvasPropertyPaint* paint,
+                            uirenderer::CanvasPropertyPrimitive* progress,
+                            sk_sp<SkRuntimeEffect> runtimeEffect) = 0;
 
     virtual void drawLayer(uirenderer::DeferredLayerUpdater* layerHandle) = 0;
     virtual void drawRenderNode(uirenderer::RenderNode* renderNode) = 0;
diff --git a/libs/hwui/jni/android_graphics_DisplayListCanvas.cpp b/libs/hwui/jni/android_graphics_DisplayListCanvas.cpp
index 7c1422d..f4877f4 100644
--- a/libs/hwui/jni/android_graphics_DisplayListCanvas.cpp
+++ b/libs/hwui/jni/android_graphics_DisplayListCanvas.cpp
@@ -20,8 +20,8 @@
 #include <utils/Looper.h>
 #endif
 
-#include <SkBitmap.h>
 #include <SkRegion.h>
+#include <SkRuntimeEffect.h>
 
 #include <Rect.h>
 #include <RenderNode.h>
@@ -139,6 +139,21 @@
     canvas->drawCircle(xProp, yProp, radiusProp, paintProp);
 }
 
+static void android_view_DisplayListCanvas_drawRippleProps(CRITICAL_JNI_PARAMS_COMMA jlong canvasPtr,
+                                                           jlong xPropPtr, jlong yPropPtr,
+                                                           jlong radiusPropPtr, jlong paintPropPtr,
+                                                           jlong progressPropPtr, jlong effectPtr) {
+    Canvas* canvas = reinterpret_cast<Canvas*>(canvasPtr);
+    CanvasPropertyPrimitive* xProp = reinterpret_cast<CanvasPropertyPrimitive*>(xPropPtr);
+    CanvasPropertyPrimitive* yProp = reinterpret_cast<CanvasPropertyPrimitive*>(yPropPtr);
+    CanvasPropertyPrimitive* radiusProp = reinterpret_cast<CanvasPropertyPrimitive*>(radiusPropPtr);
+    CanvasPropertyPaint* paintProp = reinterpret_cast<CanvasPropertyPaint*>(paintPropPtr);
+    CanvasPropertyPrimitive* progressProp =
+            reinterpret_cast<CanvasPropertyPrimitive*>(progressPropPtr);
+    SkRuntimeEffect* effect = reinterpret_cast<SkRuntimeEffect*>(effectPtr);
+    canvas->drawRipple(xProp, yProp, radiusProp, paintProp, progressProp, sk_ref_sp(effect));
+}
+
 static void android_view_DisplayListCanvas_drawWebViewFunctor(CRITICAL_JNI_PARAMS_COMMA jlong canvasPtr, jint functor) {
     Canvas* canvas = reinterpret_cast<Canvas*>(canvasPtr);
     canvas->drawWebViewFunctor(functor);
@@ -163,6 +178,7 @@
     { "nDrawCircle",              "(JJJJJ)V",   (void*) android_view_DisplayListCanvas_drawCircleProps },
     { "nDrawRoundRect",           "(JJJJJJJJ)V",(void*) android_view_DisplayListCanvas_drawRoundRectProps },
     { "nDrawWebViewFunctor",      "(JI)V",      (void*) android_view_DisplayListCanvas_drawWebViewFunctor },
+    { "nDrawRipple",              "(JJJJJJJ)V", (void*) android_view_DisplayListCanvas_drawRippleProps },
 };
 
 int register_android_view_DisplayListCanvas(JNIEnv* env) {
diff --git a/libs/hwui/pipeline/skia/AnimatedDrawables.h b/libs/hwui/pipeline/skia/AnimatedDrawables.h
index bf19655..3142d92 100644
--- a/libs/hwui/pipeline/skia/AnimatedDrawables.h
+++ b/libs/hwui/pipeline/skia/AnimatedDrawables.h
@@ -18,6 +18,7 @@
 
 #include <SkCanvas.h>
 #include <SkDrawable.h>
+#include <SkRuntimeEffect.h>
 #include <utils/RefBase.h>
 #include "CanvasProperty.h"
 
@@ -54,6 +55,59 @@
     sp<uirenderer::CanvasPropertyPaint> mPaint;
 };
 
+class AnimatedRipple : public SkDrawable {
+public:
+    AnimatedRipple(uirenderer::CanvasPropertyPrimitive* x, uirenderer::CanvasPropertyPrimitive* y,
+                   uirenderer::CanvasPropertyPrimitive* radius,
+                   uirenderer::CanvasPropertyPaint* paint,
+                   uirenderer::CanvasPropertyPrimitive* progress,
+                   sk_sp<SkRuntimeEffect> runtimeEffect)
+            : mX(x)
+            , mY(y)
+            , mRadius(radius)
+            , mPaint(paint)
+            , mProgress(progress)
+            , mRuntimeEffectBuilder(std::move(runtimeEffect)) {}
+
+protected:
+    virtual SkRect onGetBounds() override {
+        const float x = mX->value;
+        const float y = mY->value;
+        const float radius = mRadius->value;
+        return SkRect::MakeLTRB(x - radius, y - radius, x + radius, y + radius);
+    }
+    virtual void onDraw(SkCanvas* canvas) override {
+        SkRuntimeShaderBuilder::BuilderUniform center = mRuntimeEffectBuilder.uniform("in_origin");
+        if (center.fVar != nullptr) {
+            center = SkV2{mX->value, mY->value};
+        }
+
+        SkRuntimeShaderBuilder::BuilderUniform radiusU =
+                mRuntimeEffectBuilder.uniform("in_maxRadius");
+        if (radiusU.fVar != nullptr) {
+            radiusU = mRadius->value;
+        }
+
+        SkRuntimeShaderBuilder::BuilderUniform progressU =
+                mRuntimeEffectBuilder.uniform("in_progress");
+        if (progressU.fVar != nullptr) {
+            progressU = mProgress->value;
+        }
+
+        SkPaint paint = mPaint->value;
+        paint.setShader(mRuntimeEffectBuilder.makeShader(nullptr, false));
+        canvas->drawCircle(mX->value, mY->value, mRadius->value, paint);
+    }
+
+private:
+    sp<uirenderer::CanvasPropertyPrimitive> mX;
+    sp<uirenderer::CanvasPropertyPrimitive> mY;
+    sp<uirenderer::CanvasPropertyPrimitive> mRadius;
+    sp<uirenderer::CanvasPropertyPaint> mPaint;
+    sp<uirenderer::CanvasPropertyPrimitive> mProgress;
+    SkRuntimeShaderBuilder mRuntimeEffectBuilder;
+};
+
 class AnimatedCircle : public SkDrawable {
 public:
     AnimatedCircle(uirenderer::CanvasPropertyPrimitive* x, uirenderer::CanvasPropertyPrimitive* y,
diff --git a/libs/hwui/pipeline/skia/DumpOpsCanvas.h b/libs/hwui/pipeline/skia/DumpOpsCanvas.h
index d5b46d5..26ff8bf 100644
--- a/libs/hwui/pipeline/skia/DumpOpsCanvas.h
+++ b/libs/hwui/pipeline/skia/DumpOpsCanvas.h
@@ -86,17 +86,18 @@
         mOutput << mIdent << "drawTextBlob" << std::endl;
     }
 
-    void onDrawImage(const SkImage*, SkScalar dx, SkScalar dy, const SkPaint*) override {
+    void onDrawImage2(const SkImage*, SkScalar dx, SkScalar dy, const SkSamplingOptions&,
+                      const SkPaint*) override {
         mOutput << mIdent << "drawImage" << std::endl;
     }
 
-    void onDrawImageRect(const SkImage*, const SkRect*, const SkRect&, const SkPaint*,
-                         SrcRectConstraint) override {
+    void onDrawImageRect2(const SkImage*, const SkRect&, const SkRect&, const SkSamplingOptions&,
+                          const SkPaint*, SrcRectConstraint) override {
         mOutput << mIdent << "drawImageRect" << std::endl;
     }
 
-    void onDrawImageLattice(const SkImage*, const Lattice& lattice, const SkRect& dst,
-                            const SkPaint*) override {
+    void onDrawImageLattice2(const SkImage*, const Lattice& lattice, const SkRect& dst,
+                             SkFilterMode, const SkPaint*) override {
         mOutput << mIdent << "drawImageLattice" << std::endl;
     }
 
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
index e292cbd..7faebda 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -85,6 +85,16 @@
     drawDrawable(mDisplayList->allocateDrawable<AnimatedCircle>(x, y, radius, paint));
 }
 
+void SkiaRecordingCanvas::drawRipple(uirenderer::CanvasPropertyPrimitive* x,
+                                     uirenderer::CanvasPropertyPrimitive* y,
+                                     uirenderer::CanvasPropertyPrimitive* radius,
+                                     uirenderer::CanvasPropertyPaint* paint,
+                                     uirenderer::CanvasPropertyPrimitive* progress,
+                                     sk_sp<SkRuntimeEffect> runtimeEffect) {
+    drawDrawable(mDisplayList->allocateDrawable<AnimatedRipple>(x, y, radius, paint, progress,
+                                                                runtimeEffect));
+}
+
 void SkiaRecordingCanvas::enableZ(bool enableZ) {
     if (mCurrentBarrier && enableZ) {
         // Already in a re-order section, nothing to do
@@ -185,18 +195,21 @@
 }
 
 template <typename Proc>
-void applyLooper(SkDrawLooper* looper, const SkPaint& paint, Proc proc) {
+void applyLooper(SkDrawLooper* looper, const SkPaint* paint, Proc proc) {
     if (looper) {
         SkSTArenaAlloc<256> alloc;
         SkDrawLooper::Context* ctx = looper->makeContext(&alloc);
         if (ctx) {
             SkDrawLooper::Context::Info info;
             for (;;) {
-                SkPaint p = paint;
+                SkPaint p;
+                if (paint) {
+                    p = *paint;
+                }
                 if (!ctx->next(&info, &p)) {
                     break;
                 }
-                proc(info.fTranslate.fX, info.fTranslate.fY, p);
+                proc(info.fTranslate.fX, info.fTranslate.fY, &p);
             }
         }
     } else {
@@ -204,11 +217,22 @@
     }
 }
 
+static SkFilterMode Paint_to_filter(const SkPaint* paint) {
+    return paint && paint->getFilterQuality() != kNone_SkFilterQuality ? SkFilterMode::kLinear
+                                                                       : SkFilterMode::kNearest;
+}
+
+static SkSamplingOptions Paint_to_sampling(const SkPaint* paint) {
+    // Android only has 1-bit for "filter", so we don't try to cons-up mipmaps or cubics
+    return SkSamplingOptions(Paint_to_filter(paint), SkMipmapMode::kNone);
+}
+
 void SkiaRecordingCanvas::drawBitmap(Bitmap& bitmap, float left, float top, const Paint* paint) {
     sk_sp<SkImage> image = bitmap.makeImage();
 
-    applyLooper(get_looper(paint), *filterBitmap(paint), [&](SkScalar x, SkScalar y, const SkPaint& p) {
-        mRecorder.drawImage(image, left + x, top + y, &p, bitmap.palette());
+    applyLooper(get_looper(paint), filterBitmap(paint), [&](SkScalar x, SkScalar y,
+                const SkPaint* p) {
+        mRecorder.drawImage(image, left + x, top + y, Paint_to_sampling(p), p, bitmap.palette());
     });
 
     // if image->unique() is true, then mRecorder.drawImage failed for some reason. It also means
@@ -225,8 +249,9 @@
 
     sk_sp<SkImage> image = bitmap.makeImage();
 
-    applyLooper(get_looper(paint), *filterBitmap(paint), [&](SkScalar x, SkScalar y, const SkPaint& p) {
-        mRecorder.drawImage(image, x, y, &p, bitmap.palette());
+    applyLooper(get_looper(paint), filterBitmap(paint), [&](SkScalar x, SkScalar y,
+                const SkPaint* p) {
+        mRecorder.drawImage(image, x, y, Paint_to_sampling(p), p, bitmap.palette());
     });
 
     if (!bitmap.isImmutable() && image.get() && !image->unique()) {
@@ -242,9 +267,10 @@
 
     sk_sp<SkImage> image = bitmap.makeImage();
 
-    applyLooper(get_looper(paint), *filterBitmap(paint), [&](SkScalar x, SkScalar y, const SkPaint& p) {
-        mRecorder.drawImageRect(image, srcRect, dstRect.makeOffset(x, y), &p,
-                                SkCanvas::kFast_SrcRectConstraint, bitmap.palette());
+    applyLooper(get_looper(paint), filterBitmap(paint), [&](SkScalar x, SkScalar y,
+                const SkPaint* p) {
+        mRecorder.drawImageRect(image, srcRect, dstRect.makeOffset(x, y), Paint_to_sampling(p),
+                                p, SkCanvas::kFast_SrcRectConstraint, bitmap.palette());
     });
 
     if (!bitmap.isImmutable() && image.get() && !image->unique() && !srcRect.isEmpty() &&
@@ -276,16 +302,12 @@
 
     lattice.fBounds = nullptr;
     SkRect dst = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom);
-
-    PaintCoW filteredPaint(paint);
-    // HWUI always draws 9-patches with bilinear filtering, regardless of what is set in the Paint.
-    if (!filteredPaint || filteredPaint->getFilterQuality() != kLow_SkFilterQuality) {
-        filteredPaint.writeable().setFilterQuality(kLow_SkFilterQuality);
-    }
     sk_sp<SkImage> image = bitmap.makeImage();
 
-    applyLooper(get_looper(paint), *filterBitmap(paint), [&](SkScalar x, SkScalar y, const SkPaint& p) {
-        mRecorder.drawImageLattice(image, lattice, dst.makeOffset(x, y), &p, bitmap.palette());
+    applyLooper(get_looper(paint), filterBitmap(paint), [&](SkScalar x, SkScalar y,
+                const SkPaint* p) {
+        mRecorder.drawImageLattice(image, lattice, dst.makeOffset(x, y), Paint_to_filter(p),
+                                   p, bitmap.palette());
     });
 
     if (!bitmap.isImmutable() && image.get() && !image->unique() && !dst.isEmpty()) {
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
index 83e9349..622df43 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
@@ -66,6 +66,12 @@
                             uirenderer::CanvasPropertyPrimitive* y,
                             uirenderer::CanvasPropertyPrimitive* radius,
                             uirenderer::CanvasPropertyPaint* paint) override;
+    virtual void drawRipple(uirenderer::CanvasPropertyPrimitive* x,
+                            uirenderer::CanvasPropertyPrimitive* y,
+                            uirenderer::CanvasPropertyPrimitive* radius,
+                            uirenderer::CanvasPropertyPaint* paint,
+                            uirenderer::CanvasPropertyPrimitive* progress,
+                            sk_sp<SkRuntimeEffect> runtimeEffect) override;
 
     virtual void drawVectorDrawable(VectorDrawableRoot* vectorDrawable) override;
 
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index a101d46..7750a31 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -199,7 +199,10 @@
 
 void RenderThread::requireVkContext() {
     // the getter creates the context in the event it had been destroyed by destroyRenderingContext
-    if (vulkanManager().hasVkContext()) {
+    // Also check if we have a GrContext before returning fast. VulkanManager may be shared with
+    // the HardwareBitmapUploader which initializes the Vk context without persisting the GrContext
+    // in the rendering thread.
+    if (vulkanManager().hasVkContext() && mGrContext) {
         return;
     }
     mVkManager->initialize();
diff --git a/libs/hwui/tests/common/CallCountingCanvas.h b/libs/hwui/tests/common/CallCountingCanvas.h
index 40b5747..d3c41191 100644
--- a/libs/hwui/tests/common/CallCountingCanvas.h
+++ b/libs/hwui/tests/common/CallCountingCanvas.h
@@ -108,27 +108,27 @@
     }
 
     int drawImageCount = 0;
-    void onDrawImage(const SkImage* image, SkScalar dx, SkScalar dy,
+    void onDrawImage2(const SkImage* image, SkScalar dx, SkScalar dy, const SkSamplingOptions&,
                      const SkPaint* paint) override {
         drawImageCount++;
     }
 
     int drawImageRectCount = 0;
-    void onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
-                         const SkPaint* paint, SkCanvas::SrcRectConstraint constraint) override {
+    void onDrawImageRect2(const SkImage*, const SkRect&, const SkRect&, const SkSamplingOptions&,
+                          const SkPaint*, SkCanvas::SrcRectConstraint) override {
         drawImageRectCount++;
     }
 
     int drawImageLatticeCount = 0;
-    void onDrawImageLattice(const SkImage* image, const SkCanvas::Lattice& lattice,
-                            const SkRect& dst, const SkPaint* paint) override {
+    void onDrawImageLattice2(const SkImage* image, const SkCanvas::Lattice& lattice,
+                             const SkRect& dst, SkFilterMode, const SkPaint* paint) override {
         drawImageLatticeCount++;
     }
 
     int drawAtlasCount = 0;
-    void onDrawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect rect[],
-                     const SkColor colors[], int count, SkBlendMode mode, const SkRect* cull,
-                     const SkPaint* paint) override {
+    void onDrawAtlas2(const SkImage* atlas, const SkRSXform xform[], const SkRect rect[],
+                      const SkColor colors[], int count, SkBlendMode mode, const SkSamplingOptions&,
+                      const SkRect* cull, const SkPaint* paint) override {
         drawAtlasCount++;
     }
 
diff --git a/libs/hwui/tests/unit/FatalTestCanvas.h b/libs/hwui/tests/unit/FatalTestCanvas.h
index 8467be9..2a74afc 100644
--- a/libs/hwui/tests/unit/FatalTestCanvas.h
+++ b/libs/hwui/tests/unit/FatalTestCanvas.h
@@ -60,22 +60,23 @@
     void onDrawVerticesObject(const SkVertices*, SkBlendMode, const SkPaint&) {
         ADD_FAILURE() << "onDrawVertices not expected in this test";
     }
-    void onDrawAtlas(const SkImage*, const SkRSXform[], const SkRect[], const SkColor[], int count,
-                     SkBlendMode, const SkRect* cull, const SkPaint*) {
+    void onDrawAtlas2(const SkImage*, const SkRSXform[], const SkRect[], const SkColor[], int count,
+                      SkBlendMode, const SkSamplingOptions&, const SkRect* cull, const SkPaint*) {
         ADD_FAILURE() << "onDrawAtlas not expected in this test";
     }
     void onDrawPath(const SkPath&, const SkPaint&) {
         ADD_FAILURE() << "onDrawPath not expected in this test";
     }
-    void onDrawImage(const SkImage*, SkScalar dx, SkScalar dy, const SkPaint*) {
+    void onDrawImage2(const SkImage*, SkScalar dx, SkScalar dy, const SkSamplingOptions&,
+                      const SkPaint*) {
         ADD_FAILURE() << "onDrawImage not expected in this test";
     }
-    void onDrawImageRect(const SkImage*, const SkRect*, const SkRect&, const SkPaint*,
-                         SrcRectConstraint) {
+    void onDrawImageRect2(const SkImage*, const SkRect&, const SkRect&, const SkSamplingOptions&,
+                          const SkPaint*, SrcRectConstraint) {
         ADD_FAILURE() << "onDrawImageRect not expected in this test";
     }
-    void onDrawImageLattice(const SkImage*, const Lattice& lattice, const SkRect& dst,
-                            const SkPaint*) {
+    void onDrawImageLattice2(const SkImage*, const Lattice& lattice, const SkRect& dst,
+                             SkFilterMode, const SkPaint*) {
         ADD_FAILURE() << "onDrawImageLattice not expected in this test";
     }
     void onClipRRect(const SkRRect& rrect, SkClipOp, ClipEdgeStyle) {
diff --git a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
index 26bc659..423400e 100644
--- a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
+++ b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
@@ -938,7 +938,8 @@
         void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
             EXPECT_EQ(0, mDrawCounter++);
         }
-        void onDrawImage(const SkImage*, SkScalar dx, SkScalar dy, const SkPaint*) override {
+        void onDrawImage2(const SkImage*, SkScalar dx, SkScalar dy, const SkSamplingOptions&,
+                          const SkPaint*) override {
             EXPECT_EQ(1, mDrawCounter++);
         }
     };
@@ -1047,7 +1048,7 @@
     EXPECT_EQ(2, canvas.mDrawCounter);
 }
 
-// Verify that layers are composed with kLow_SkFilterQuality filter quality.
+// Verify that layers are composed with linear filtering.
 RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, layerComposeQuality) {
     static const int CANVAS_WIDTH = 1;
     static const int CANVAS_HEIGHT = 1;
@@ -1056,10 +1057,12 @@
     class FrameTestCanvas : public TestCanvasBase {
     public:
         FrameTestCanvas() : TestCanvasBase(CANVAS_WIDTH, CANVAS_HEIGHT) {}
-        void onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
-                             const SkPaint* paint, SrcRectConstraint constraint) override {
+        void onDrawImageRect2(const SkImage* image, const SkRect& src, const SkRect& dst,
+                              const SkSamplingOptions& sampling, const SkPaint* paint,
+                              SrcRectConstraint constraint) override {
             mDrawCounter++;
-            EXPECT_EQ(kLow_SkFilterQuality, paint->getFilterQuality());
+            EXPECT_FALSE(sampling.useCubic);
+            EXPECT_EQ(SkFilterMode::kLinear, sampling.filter);
         }
     };
 
@@ -1169,8 +1172,9 @@
     class VectorDrawableTestCanvas : public TestCanvasBase {
     public:
         VectorDrawableTestCanvas() : TestCanvasBase(CANVAS_WIDTH, CANVAS_HEIGHT) {}
-        void onDrawImageRect(const SkImage*, const SkRect* src, const SkRect& dst,
-                              const SkPaint* paint, SrcRectConstraint constraint) override {
+        void onDrawImageRect2(const SkImage*, const SkRect& src, const SkRect& dst,
+                              const SkSamplingOptions&, const SkPaint* paint,
+                              SrcRectConstraint constraint) override {
             const int index = mDrawCounter++;
             switch (index) {
                 case 0:
diff --git a/libs/hwui/tests/unit/SkiaPipelineTests.cpp b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
index e7a889d..6dd57b1 100644
--- a/libs/hwui/tests/unit/SkiaPipelineTests.cpp
+++ b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
@@ -302,7 +302,8 @@
     class ClippedTestCanvas : public SkCanvas {
     public:
         ClippedTestCanvas() : SkCanvas(CANVAS_WIDTH, CANVAS_HEIGHT) {}
-        void onDrawImage(const SkImage*, SkScalar dx, SkScalar dy, const SkPaint*) override {
+        void onDrawImage2(const SkImage*, SkScalar dx, SkScalar dy, const SkSamplingOptions&,
+                          const SkPaint*) override {
             EXPECT_EQ(0, mDrawCounter++);
             EXPECT_EQ(SkRect::MakeLTRB(10, 20, 30, 40), TestUtils::getClipBounds(this));
             EXPECT_TRUE(getTotalMatrix().isIdentity());
@@ -336,7 +337,8 @@
     class ClippedTestCanvas : public SkCanvas {
     public:
         ClippedTestCanvas() : SkCanvas(CANVAS_WIDTH, CANVAS_HEIGHT) {}
-        void onDrawImage(const SkImage*, SkScalar dx, SkScalar dy, const SkPaint*) override {
+        void onDrawImage2(const SkImage*, SkScalar dx, SkScalar dy, const SkSamplingOptions&,
+                          const SkPaint*) override {
             EXPECT_EQ(0, mDrawCounter++);
             // Expect clip to be rotated.
             EXPECT_EQ(SkRect::MakeLTRB(CANVAS_HEIGHT - dirty.fTop - dirty.height(), dirty.fLeft,
diff --git a/location/java/android/location/GnssAntennaInfo.java b/location/java/android/location/GnssAntennaInfo.java
index 23977f1..f1eb8fc 100644
--- a/location/java/android/location/GnssAntennaInfo.java
+++ b/location/java/android/location/GnssAntennaInfo.java
@@ -34,17 +34,19 @@
 public final class GnssAntennaInfo implements Parcelable {
     private final double mCarrierFrequencyMHz;
     private final PhaseCenterOffset mPhaseCenterOffset;
-    private final SphericalCorrections mPhaseCenterVariationCorrections;
-    private final SphericalCorrections mSignalGainCorrections;
+    private final @Nullable SphericalCorrections mPhaseCenterVariationCorrections;
+    private final @Nullable SphericalCorrections mSignalGainCorrections;
 
     /**
-     * Used for receiving GNSS antenna info from the GNSS engine. You can implement this interface
-     * and call {@link LocationManager#registerAntennaInfoListener};
+     * Used for receiving GNSS antenna info from the GNSS engine.
+     *
+     * @deprecated Prefer to use a broadcast receiver for
+     * {@link LocationManager#ACTION_GNSS_ANTENNA_INFOS_CHANGED}.
      */
+    @Deprecated
     public interface Listener {
         /**
-         * Returns the latest GNSS antenna info. This event is triggered when a listener is
-         * registered, and whenever the antenna info changes (due to a device configuration change).
+         * Invoked on a change to GNSS antenna info.
          */
         void onGnssAntennaInfoReceived(@NonNull List<GnssAntennaInfo> gnssAntennaInfos);
     }
@@ -172,6 +174,28 @@
                     + ", OffsetZMm=" + mOffsetZMm + " +/-" + mOffsetZUncertaintyMm
                     + '}';
         }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (!(o instanceof PhaseCenterOffset)) {
+                return false;
+            }
+            PhaseCenterOffset that = (PhaseCenterOffset) o;
+            return Double.compare(that.mOffsetXMm, mOffsetXMm) == 0
+                    && Double.compare(that.mOffsetXUncertaintyMm, mOffsetXUncertaintyMm) == 0
+                    && Double.compare(that.mOffsetYMm, mOffsetYMm) == 0
+                    && Double.compare(that.mOffsetYUncertaintyMm, mOffsetYUncertaintyMm) == 0
+                    && Double.compare(that.mOffsetZMm, mOffsetZMm) == 0
+                    && Double.compare(that.mOffsetZUncertaintyMm, mOffsetZUncertaintyMm) == 0;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(mOffsetXMm, mOffsetYMm, mOffsetZMm);
+        }
     }
 
     /**
@@ -190,38 +214,34 @@
      * i.e., deltaPhi = 180 / (number of columns - 1).
      */
     public static final class SphericalCorrections implements Parcelable {
-        private final double[][] mCorrections;
-        private final double[][] mCorrectionUncertainties;
-        private final double mDeltaTheta;
-        private final double mDeltaPhi;
+
         private final int mNumRows;
         private final int mNumColumns;
+        private final double[][] mCorrections;
+        private final double[][] mCorrectionUncertainties;
 
         public SphericalCorrections(@NonNull double[][] corrections,
                 @NonNull double[][] correctionUncertainties) {
-            if (corrections.length != correctionUncertainties.length
-                    || corrections[0].length != correctionUncertainties[0].length) {
-                throw new IllegalArgumentException("Correction and correction uncertainty arrays "
-                        + "must have the same dimensions.");
+            if (corrections.length != correctionUncertainties.length || corrections.length < 1) {
+                throw new IllegalArgumentException("correction and uncertainty arrays must have "
+                        + "the same (non-zero) dimensions");
             }
 
             mNumRows = corrections.length;
-            if (mNumRows < 1) {
-                throw new IllegalArgumentException("Arrays must have at least one row.");
-            }
-
             mNumColumns = corrections[0].length;
-            if (mNumColumns < 2) {
-                throw new IllegalArgumentException("Arrays must have at least two columns.");
+            for (int i = 0; i < corrections.length; i++) {
+                if (corrections[i].length != mNumColumns
+                        || correctionUncertainties[i].length != mNumColumns || mNumColumns < 2) {
+                    throw new IllegalArgumentException("correction and uncertainty arrays must all "
+                            + " have the same (greater than 2) number of columns");
+                }
             }
 
             mCorrections = corrections;
             mCorrectionUncertainties = correctionUncertainties;
-            mDeltaTheta = 360.0d / mNumRows;
-            mDeltaPhi = 180.0d / (mNumColumns - 1);
         }
 
-        SphericalCorrections(Parcel in) {
+        private SphericalCorrections(Parcel in) {
             int numRows = in.readInt();
             int numColumns = in.readInt();
 
@@ -231,19 +251,16 @@
                     new double[numRows][numColumns];
 
             for (int row = 0; row < numRows; row++) {
-                in.readDoubleArray(corrections[row]);
-            }
-
-            for (int row = 0; row < numRows; row++) {
-                in.readDoubleArray(correctionUncertainties[row]);
+                for (int col = 0; col < numColumns; col++) {
+                    corrections[row][col] = in.readDouble();
+                    correctionUncertainties[row][col] = in.readDouble();
+                }
             }
 
             mNumRows = numRows;
             mNumColumns = numColumns;
             mCorrections = corrections;
             mCorrectionUncertainties = correctionUncertainties;
-            mDeltaTheta = 360.0d / mNumRows;
-            mDeltaPhi = 180.0d / (mNumColumns - 1);
         }
 
         /**
@@ -286,7 +303,7 @@
          */
         @FloatRange(from = 0.0f, to = 360.0f)
         public double getDeltaTheta() {
-            return mDeltaTheta;
+            return 360.0D / mNumRows;
         }
 
         /**
@@ -294,7 +311,7 @@
          */
         @FloatRange(from = 0.0f, to = 180.0f)
         public double getDeltaPhi() {
-            return mDeltaPhi;
+            return 180.0D / (mNumColumns - 1);
         }
 
 
@@ -320,11 +337,11 @@
         public void writeToParcel(@NonNull Parcel dest, int flags) {
             dest.writeInt(mNumRows);
             dest.writeInt(mNumColumns);
-            for (double[] row : mCorrections) {
-                dest.writeDoubleArray(row);
-            }
-            for (double[] row : mCorrectionUncertainties) {
-                dest.writeDoubleArray(row);
+            for (int row = 0; row < mNumRows; row++) {
+                for (int col = 0; col < mNumColumns; col++) {
+                    dest.writeDouble(mCorrections[row][col]);
+                    dest.writeDouble(mCorrectionUncertainties[row][col]);
+                }
             }
         }
 
@@ -333,22 +350,41 @@
             return "SphericalCorrections{"
                     + "Corrections=" + Arrays.toString(mCorrections)
                     + ", CorrectionUncertainties=" + Arrays.toString(mCorrectionUncertainties)
-                    + ", DeltaTheta=" + mDeltaTheta
-                    + ", DeltaPhi=" + mDeltaPhi
+                    + ", DeltaTheta=" + getDeltaTheta()
+                    + ", DeltaPhi=" + getDeltaPhi()
                     + '}';
         }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (!(o instanceof SphericalCorrections)) {
+                return false;
+            }
+            SphericalCorrections that = (SphericalCorrections) o;
+            return mNumRows == that.mNumRows
+                    && mNumColumns == that.mNumColumns
+                    && Arrays.deepEquals(mCorrections, that.mCorrections)
+                    && Arrays.deepEquals(mCorrectionUncertainties, that.mCorrectionUncertainties);
+        }
+
+        @Override
+        public int hashCode() {
+            int result = Arrays.deepHashCode(mCorrections);
+            result = 31 * result + Arrays.deepHashCode(mCorrectionUncertainties);
+            return result;
+        }
     }
 
     private GnssAntennaInfo(
             double carrierFrequencyMHz,
-            @NonNull PhaseCenterOffset phaseCenterOffset,
+            PhaseCenterOffset phaseCenterOffset,
             @Nullable SphericalCorrections phaseCenterVariationCorrections,
             @Nullable SphericalCorrections signalGainCorrectionDbi) {
-        if (phaseCenterOffset == null) {
-            throw new IllegalArgumentException("Phase Center Offset Coordinates cannot be null.");
-        }
         mCarrierFrequencyMHz = carrierFrequencyMHz;
-        mPhaseCenterOffset = phaseCenterOffset;
+        mPhaseCenterOffset = Objects.requireNonNull(phaseCenterOffset);
         mPhaseCenterVariationCorrections = phaseCenterVariationCorrections;
         mSignalGainCorrections = signalGainCorrectionDbi;
     }
@@ -359,8 +395,28 @@
     public static class Builder {
         private double mCarrierFrequencyMHz;
         private PhaseCenterOffset mPhaseCenterOffset;
-        private SphericalCorrections mPhaseCenterVariationCorrections;
-        private SphericalCorrections mSignalGainCorrections;
+        private @Nullable SphericalCorrections mPhaseCenterVariationCorrections;
+        private @Nullable SphericalCorrections mSignalGainCorrections;
+
+        /**
+         * @deprecated Prefer {@link #Builder(double, PhaseCenterOffset)}.
+         */
+        @Deprecated
+        public Builder() {
+            this(0, new PhaseCenterOffset(0, 0, 0, 0, 0, 0));
+        }
+
+        public Builder(double carrierFrequencyMHz, @NonNull PhaseCenterOffset phaseCenterOffset) {
+            mCarrierFrequencyMHz = carrierFrequencyMHz;
+            mPhaseCenterOffset = Objects.requireNonNull(phaseCenterOffset);
+        }
+
+        public Builder(@NonNull GnssAntennaInfo antennaInfo) {
+            mCarrierFrequencyMHz = antennaInfo.mCarrierFrequencyMHz;
+            mPhaseCenterOffset = antennaInfo.mPhaseCenterOffset;
+            mPhaseCenterVariationCorrections = antennaInfo.mPhaseCenterVariationCorrections;
+            mSignalGainCorrections = antennaInfo.mSignalGainCorrections;
+        }
 
         /**
          * Set antenna carrier frequency (MHz).
@@ -462,32 +518,29 @@
         return mSignalGainCorrections;
     }
 
-    public static final @android.annotation.NonNull Creator<GnssAntennaInfo> CREATOR =
-            new Creator<GnssAntennaInfo>() {
-                @Override
-                public GnssAntennaInfo createFromParcel(Parcel in) {
-                    double carrierFrequencyMHz = in.readDouble();
+    public static final @NonNull Creator<GnssAntennaInfo> CREATOR = new Creator<GnssAntennaInfo>() {
+        @Override
+        public GnssAntennaInfo createFromParcel(Parcel in) {
+            double carrierFrequencyMHz = in.readDouble();
+            PhaseCenterOffset phaseCenterOffset =
+                    in.readTypedObject(PhaseCenterOffset.CREATOR);
+            SphericalCorrections phaseCenterVariationCorrections =
+                    in.readTypedObject(SphericalCorrections.CREATOR);
+            SphericalCorrections signalGainCorrections =
+                    in.readTypedObject(SphericalCorrections.CREATOR);
 
-                    ClassLoader classLoader = getClass().getClassLoader();
-                    PhaseCenterOffset phaseCenterOffset =
-                            in.readParcelable(classLoader);
-                    SphericalCorrections phaseCenterVariationCorrections =
-                            in.readParcelable(classLoader);
-                    SphericalCorrections signalGainCorrections =
-                            in.readParcelable(classLoader);
+            return new GnssAntennaInfo(
+                    carrierFrequencyMHz,
+                    phaseCenterOffset,
+                    phaseCenterVariationCorrections,
+                    signalGainCorrections);
+        }
 
-                    return new GnssAntennaInfo(
-                            carrierFrequencyMHz,
-                            phaseCenterOffset,
-                            phaseCenterVariationCorrections,
-                            signalGainCorrections);
-                }
-
-                @Override
-                public GnssAntennaInfo[] newArray(int size) {
-                    return new GnssAntennaInfo[size];
-                }
-            };
+        @Override
+        public GnssAntennaInfo[] newArray(int size) {
+            return new GnssAntennaInfo[size];
+        }
+    };
 
     @Override
     public int describeContents() {
@@ -497,9 +550,9 @@
     @Override
     public void writeToParcel(@NonNull Parcel parcel, int flags) {
         parcel.writeDouble(mCarrierFrequencyMHz);
-        parcel.writeParcelable(mPhaseCenterOffset, flags);
-        parcel.writeParcelable(mPhaseCenterVariationCorrections, flags);
-        parcel.writeParcelable(mSignalGainCorrections, flags);
+        parcel.writeTypedObject(mPhaseCenterOffset, flags);
+        parcel.writeTypedObject(mPhaseCenterVariationCorrections, flags);
+        parcel.writeTypedObject(mSignalGainCorrections, flags);
     }
 
     @Override
@@ -511,4 +564,26 @@
                 + ", SignalGainCorrections=" + mSignalGainCorrections
                 + '}';
     }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (!(o instanceof GnssAntennaInfo)) {
+            return false;
+        }
+        GnssAntennaInfo that = (GnssAntennaInfo) o;
+        return Double.compare(that.mCarrierFrequencyMHz, mCarrierFrequencyMHz) == 0
+                && mPhaseCenterOffset.equals(that.mPhaseCenterOffset)
+                && Objects.equals(mPhaseCenterVariationCorrections,
+                    that.mPhaseCenterVariationCorrections)
+                && Objects.equals(mSignalGainCorrections, that.mSignalGainCorrections);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mCarrierFrequencyMHz, mPhaseCenterOffset,
+                mPhaseCenterVariationCorrections, mSignalGainCorrections);
+    }
 }
diff --git a/location/java/com/android/internal/location/timezone/LocationTimeZoneEvent.aidl b/location/java/android/location/GnssCapabilities.aidl
similarity index 87%
rename from location/java/com/android/internal/location/timezone/LocationTimeZoneEvent.aidl
rename to location/java/android/location/GnssCapabilities.aidl
index 199e067..bdf3014 100644
--- a/location/java/com/android/internal/location/timezone/LocationTimeZoneEvent.aidl
+++ b/location/java/android/location/GnssCapabilities.aidl
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 
-package com.android.internal.location.timezone;
+package android.location;
 
-parcelable LocationTimeZoneEvent;
+parcelable GnssCapabilities;
\ No newline at end of file
diff --git a/location/java/android/location/GnssCapabilities.java b/location/java/android/location/GnssCapabilities.java
index bbb5bb8..89a3bd5 100644
--- a/location/java/android/location/GnssCapabilities.java
+++ b/location/java/android/location/GnssCapabilities.java
@@ -16,121 +16,207 @@
 
 package android.location;
 
+import android.annotation.IntDef;
+import android.annotation.NonNull;
 import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+import java.util.concurrent.Executor;
 
 /**
- * A container of supported GNSS chipset capabilities.
+ * GNSS chipset capabilities.
  */
-public final class GnssCapabilities {
-    /**
-     * Bit mask indicating GNSS chipset supports low power mode.
-     * @hide
-     */
-    public static final long LOW_POWER_MODE                                     = 1L << 0;
+public final class GnssCapabilities implements Parcelable {
 
-    /**
-     * Bit mask indicating GNSS chipset supports blocklisting satellites.
-     * @hide
-     */
-    public static final long SATELLITE_BLOCKLIST                                = 1L << 1;
-
-    /**
-     * Bit mask indicating GNSS chipset supports geofencing.
-     * @hide
-     */
-    public static final long GEOFENCING                                         = 1L << 2;
-
-    /**
-     * Bit mask indicating GNSS chipset supports measurements.
-     * @hide
-     */
-    public static final long MEASUREMENTS                                       = 1L << 3;
-
-    /**
-     * Bit mask indicating GNSS chipset supports navigation messages.
-     * @hide
-     */
-    public static final long NAV_MESSAGES                                       = 1L << 4;
-
-    /**
-     * Bit mask indicating GNSS chipset supports measurement corrections.
-     * @hide
-     */
-    public static final long MEASUREMENT_CORRECTIONS                            = 1L << 5;
-
-    /**
-     * Bit mask indicating GNSS chipset supports line-of-sight satellite identification
-     * measurement corrections.
-     * @hide
-     */
-    public static final long MEASUREMENT_CORRECTIONS_LOS_SATS                   = 1L << 6;
-
-    /**
-     * Bit mask indicating GNSS chipset supports per satellite excess-path-length
-     * measurement corrections.
-     * @hide
-     */
-    public static final long MEASUREMENT_CORRECTIONS_EXCESS_PATH_LENGTH         = 1L << 7;
-
-    /**
-     * Bit mask indicating GNSS chipset supports reflecting planes measurement corrections.
-     * @hide
-     */
-    public static final long MEASUREMENT_CORRECTIONS_REFLECTING_PLANE           = 1L << 8;
-
-    /**
-     * Bit mask indicating GNSS chipset supports GNSS antenna info.
-     * @hide
-     */
-    public static final long ANTENNA_INFO                                       = 1L << 9;
+    // IMPORTANT - must match the Capabilities enum in IGnssCallback.hal
+    /** @hide */
+    public static final int TOP_HAL_CAPABILITY_SCHEDULING = 1;
+    /** @hide */
+    public static final int TOP_HAL_CAPABILITY_MSB = 2;
+    /** @hide */
+    public static final int TOP_HAL_CAPABILITY_MSA = 4;
+    /** @hide */
+    public static final int TOP_HAL_CAPABILITY_SINGLE_SHOT = 8;
+    /** @hide */
+    public static final int TOP_HAL_CAPABILITY_ON_DEMAND_TIME = 16;
+    /** @hide */
+    public static final int TOP_HAL_CAPABILITY_GEOFENCING = 32;
+    /** @hide */
+    public static final int TOP_HAL_CAPABILITY_MEASUREMENTS = 64;
+    /** @hide */
+    public static final int TOP_HAL_CAPABILITY_NAV_MESSAGES = 128;
+    /** @hide */
+    public static final int TOP_HAL_CAPABILITY_LOW_POWER_MODE = 256;
+    /** @hide */
+    public static final int TOP_HAL_CAPABILITY_SATELLITE_BLOCKLIST = 512;
+    /** @hide */
+    public static final int TOP_HAL_CAPABILITY_MEASUREMENT_CORRECTIONS = 1024;
+    /** @hide */
+    public static final int TOP_HAL_CAPABILITY_ANTENNA_INFO = 2048;
 
     /** @hide */
-    public static final long INVALID_CAPABILITIES = -1;
+    @IntDef(flag = true, prefix = {"TOP_HAL_CAPABILITY_"}, value = {TOP_HAL_CAPABILITY_SCHEDULING,
+            TOP_HAL_CAPABILITY_MSB, TOP_HAL_CAPABILITY_MSA, TOP_HAL_CAPABILITY_SINGLE_SHOT,
+            TOP_HAL_CAPABILITY_ON_DEMAND_TIME, TOP_HAL_CAPABILITY_GEOFENCING,
+            TOP_HAL_CAPABILITY_MEASUREMENTS, TOP_HAL_CAPABILITY_NAV_MESSAGES,
+            TOP_HAL_CAPABILITY_LOW_POWER_MODE, TOP_HAL_CAPABILITY_SATELLITE_BLOCKLIST,
+            TOP_HAL_CAPABILITY_MEASUREMENT_CORRECTIONS, TOP_HAL_CAPABILITY_ANTENNA_INFO})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface TopHalCapabilityFlags {}
 
-    /** A bitmask of supported GNSS capabilities. */
-    private final long mGnssCapabilities;
+    // IMPORTANT - must match the Capabilities enum in IMeasurementCorrectionsCallback.hal
+    /** @hide */
+    public static final int SUB_HAL_MEASUREMENT_CORRECTIONS_CAPABILITY_LOS_SATS = 1;
+    /** @hide */
+    public static final int SUB_HAL_MEASUREMENT_CORRECTIONS_CAPABILITY_EXCESS_PATH_LENGTH = 2;
+    /** @hide */
+    public static final int SUB_HAL_MEASUREMENT_CORRECTIONS_CAPABILITY_REFLECTING_PLANE = 4;
 
     /** @hide */
-    public static GnssCapabilities of(long gnssCapabilities) {
-        return new GnssCapabilities(gnssCapabilities);
-    }
+    @IntDef(flag = true, prefix = {"SUB_HAL_MEASUREMENT_CORRECTIONS_CAPABILITY_"}, value = {
+            SUB_HAL_MEASUREMENT_CORRECTIONS_CAPABILITY_LOS_SATS,
+            SUB_HAL_MEASUREMENT_CORRECTIONS_CAPABILITY_EXCESS_PATH_LENGTH,
+            SUB_HAL_MEASUREMENT_CORRECTIONS_CAPABILITY_REFLECTING_PLANE})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface SubHalMeasurementCorrectionsCapabilityFlags {}
 
-    private GnssCapabilities(long gnssCapabilities) {
-        mGnssCapabilities = gnssCapabilities;
-    }
+    // IMPORATANT - must match values in IGnssPowerIndicationCallback.aidl
+    /** @hide */
+    public static final int SUB_HAL_POWER_CAPABILITY_TOTAL = 1;
+    /** @hide */
+    public static final int SUB_HAL_POWER_CAPABILITY_SINGLEBAND_TRACKING = 2;
+    /** @hide */
+    public static final int SUB_HAL_POWER_CAPABILITY_MULTIBAND_TRACKING = 4;
+    /** @hide */
+    public static final int SUB_HAL_POWER_CAPABILITY_SINGLEBAND_ACQUISITION = 8;
+    /** @hide */
+    public static final int SUB_HAL_POWER_CAPABILITY_MULTIBAND_ACQUISITION = 16;
+    /** @hide */
+    public static final int SUB_HAL_POWER_CAPABILITY_OTHER_MODES = 32;
+
+    /** @hide */
+    @IntDef(flag = true, prefix = {"SUB_HAL_POWER_CAPABILITY_"}, value = {
+            SUB_HAL_POWER_CAPABILITY_TOTAL, SUB_HAL_POWER_CAPABILITY_SINGLEBAND_TRACKING,
+            SUB_HAL_POWER_CAPABILITY_MULTIBAND_TRACKING,
+            SUB_HAL_POWER_CAPABILITY_SINGLEBAND_ACQUISITION,
+            SUB_HAL_POWER_CAPABILITY_MULTIBAND_ACQUISITION,
+            SUB_HAL_POWER_CAPABILITY_OTHER_MODES})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface SubHalPowerCapabilityFlags {}
 
     /**
-     * Returns {@code true} if GNSS chipset supports low power mode, {@code false} otherwise.
+     * Returns an empty GnssCapabilities object.
      *
      * @hide
      */
-    @SystemApi
-    public boolean hasLowPowerMode() {
-        return hasCapability(LOW_POWER_MODE);
+    public static GnssCapabilities empty() {
+        return new GnssCapabilities(0, 0, 0);
+    }
+
+    private final @TopHalCapabilityFlags int mTopFlags;
+    private final @SubHalMeasurementCorrectionsCapabilityFlags int mMeasurementCorrectionsFlags;
+    private final @SubHalPowerCapabilityFlags int mPowerFlags;
+
+    private GnssCapabilities(
+            @TopHalCapabilityFlags int topFlags,
+            @SubHalMeasurementCorrectionsCapabilityFlags int measurementCorrectionsFlags,
+            @SubHalPowerCapabilityFlags int powerFlags) {
+        mTopFlags = topFlags;
+        mMeasurementCorrectionsFlags = measurementCorrectionsFlags;
+        mPowerFlags = powerFlags;
     }
 
     /**
-     * Returns {@code true} if GNSS chipset supports blocklisting satellites, {@code false}
-     * otherwise.
+     * Returns a new GnssCapabilities object with top hal values set from the given flags.
      *
      * @hide
-     * @deprecated use {@link #hasSatelliteBlocklist} instead.
      */
-    @SystemApi
-    @Deprecated
-    public boolean hasSatelliteBlacklist() {
-        return hasCapability(SATELLITE_BLOCKLIST);
+    public GnssCapabilities withTopHalFlags(@TopHalCapabilityFlags int flags) {
+        if (mTopFlags == flags) {
+            return this;
+        } else {
+            return new GnssCapabilities(flags, mMeasurementCorrectionsFlags, mPowerFlags);
+        }
     }
 
     /**
-     * Returns {@code true} if GNSS chipset supports blocklisting satellites, {@code false}
+     * Returns a new GnssCapabilities object with gnss measurement corrections sub hal values set
+     * from the given flags.
+     *
+     * @hide
+     */
+    public GnssCapabilities withSubHalMeasurementCorrectionsFlags(
+            @SubHalMeasurementCorrectionsCapabilityFlags int flags) {
+        if (mMeasurementCorrectionsFlags == flags) {
+            return this;
+        } else {
+            return new GnssCapabilities(mTopFlags, flags, mPowerFlags);
+        }
+    }
+
+    /**
+     * Returns a new GnssCapabilities object with gnss measurement corrections sub hal values set
+     * from the given flags.
+     *
+     * @hide
+     */
+    public GnssCapabilities withSubHalPowerFlags(@SubHalPowerCapabilityFlags int flags) {
+        if (mPowerFlags == flags) {
+            return this;
+        } else {
+            return new GnssCapabilities(mTopFlags, mMeasurementCorrectionsFlags, flags);
+        }
+    }
+
+    /**
+     * Returns {@code true} if GNSS chipset supports scheduling, {@code false} otherwise.
+     *
+     * @hide
+     */
+    public boolean hasScheduling() {
+        return (mTopFlags & TOP_HAL_CAPABILITY_SCHEDULING) != 0;
+    }
+
+    /**
+     * Returns {@code true} if GNSS chipset supports Mobile Station Based assistance, {@code false}
      * otherwise.
      *
      * @hide
      */
-    @SystemApi
-    public boolean hasSatelliteBlocklist() {
-        return hasCapability(SATELLITE_BLOCKLIST);
+    public boolean hasMsb() {
+        return (mTopFlags & TOP_HAL_CAPABILITY_MSB) != 0;
+    }
+
+    /**
+     * Returns {@code true} if GNSS chipset supports Mobile Station Assisted assitance,
+     * {@code false} otherwise.
+     *
+     * @hide
+     */
+    public boolean hasMsa() {
+        return (mTopFlags & TOP_HAL_CAPABILITY_MSA) != 0;
+    }
+
+    /**
+     * Returns {@code true} if GNSS chipset supports single shot locating, {@code false} otherwise.
+     *
+     * @hide
+     */
+    public boolean hasSingleShot() {
+        return (mTopFlags & TOP_HAL_CAPABILITY_SINGLE_SHOT) != 0;
+    }
+
+    /**
+     * Returns {@code true} if GNSS chipset supports on demand time, {@code false} otherwise.
+     *
+     * @hide
+     */
+    public boolean hasOnDemandTime() {
+        return (mTopFlags & TOP_HAL_CAPABILITY_ON_DEMAND_TIME) != 0;
     }
 
     /**
@@ -140,27 +226,71 @@
      */
     @SystemApi
     public boolean hasGeofencing() {
-        return hasCapability(GEOFENCING);
+        return (mTopFlags & TOP_HAL_CAPABILITY_GEOFENCING) != 0;
     }
 
     /**
      * Returns {@code true} if GNSS chipset supports measurements, {@code false} otherwise.
      *
-     * @hide
+     * @see LocationManager#registerGnssMeasurementsCallback(Executor, GnssMeasurementsEvent.Callback)
      */
-    @SystemApi
     public boolean hasMeasurements() {
-        return hasCapability(MEASUREMENTS);
+        return (mTopFlags & TOP_HAL_CAPABILITY_MEASUREMENTS) != 0;
     }
 
     /**
      * Returns {@code true} if GNSS chipset supports navigation messages, {@code false} otherwise.
      *
+     * @deprecated Use {@link #hasNavigationMessages()} instead.
+     *
+     * @hide
+     */
+    @Deprecated
+    @SystemApi
+    public boolean hasNavMessages() {
+        return hasNavigationMessages();
+    }
+
+    /**
+     * Returns {@code true} if GNSS chipset supports navigation messages, {@code false} otherwise.
+     *
+     * @see LocationManager#registerGnssNavigationMessageCallback(Executor, GnssNavigationMessage.Callback)
+     */
+    public boolean hasNavigationMessages() {
+        return (mTopFlags & TOP_HAL_CAPABILITY_NAV_MESSAGES) != 0;
+    }
+
+    /**
+     * Returns {@code true} if GNSS chipset supports low power mode, {@code false} otherwise.
+     *
      * @hide
      */
     @SystemApi
-    public boolean hasNavMessages() {
-        return hasCapability(NAV_MESSAGES);
+    public boolean hasLowPowerMode() {
+        return (mTopFlags & TOP_HAL_CAPABILITY_LOW_POWER_MODE) != 0;
+    }
+
+    /**
+     * Returns {@code true} if GNSS chipset supports satellite blocklists, {@code false} otherwise.
+     *
+     * @deprecated Use {@link #hasSatelliteBlocklist} instead.
+     *
+     * @hide
+     */
+    @SystemApi
+    @Deprecated
+    public boolean hasSatelliteBlacklist() {
+        return (mTopFlags & TOP_HAL_CAPABILITY_SATELLITE_BLOCKLIST) != 0;
+    }
+
+    /**
+     * Returns {@code true} if GNSS chipset supports satellite blocklists, {@code false} otherwise.
+     *
+     * @hide
+     */
+    @SystemApi
+    public boolean hasSatelliteBlocklist() {
+        return (mTopFlags & TOP_HAL_CAPABILITY_SATELLITE_BLOCKLIST) != 0;
     }
 
     /**
@@ -171,7 +301,26 @@
      */
     @SystemApi
     public boolean hasMeasurementCorrections() {
-        return hasCapability(MEASUREMENT_CORRECTIONS);
+        return (mTopFlags & TOP_HAL_CAPABILITY_MEASUREMENT_CORRECTIONS) != 0;
+    }
+
+    /**
+     * Returns {@code true} if GNSS chipset supports antenna info, {@code false} otherwise.
+     *
+     * @deprecated Use {@link #hasAntennaInfo()} instead.
+     */
+    @Deprecated
+    public boolean hasGnssAntennaInfo() {
+        return hasAntennaInfo();
+    }
+
+    /**
+     * Returns {@code true} if GNSS chipset supports antenna info, {@code false} otherwise.
+     *
+     * @see LocationManager#registerAntennaInfoListener(Executor, GnssAntennaInfo.Listener)
+     */
+    public boolean hasAntennaInfo() {
+        return (mTopFlags & TOP_HAL_CAPABILITY_ANTENNA_INFO) != 0;
     }
 
     /**
@@ -182,7 +331,8 @@
      */
     @SystemApi
     public boolean hasMeasurementCorrectionsLosSats() {
-        return hasCapability(MEASUREMENT_CORRECTIONS_LOS_SATS);
+        return (mMeasurementCorrectionsFlags & SUB_HAL_MEASUREMENT_CORRECTIONS_CAPABILITY_LOS_SATS)
+                != 0;
     }
 
     /**
@@ -193,28 +343,468 @@
      */
     @SystemApi
     public boolean hasMeasurementCorrectionsExcessPathLength() {
-        return hasCapability(MEASUREMENT_CORRECTIONS_EXCESS_PATH_LENGTH);
+        return (mMeasurementCorrectionsFlags
+                & SUB_HAL_MEASUREMENT_CORRECTIONS_CAPABILITY_EXCESS_PATH_LENGTH) != 0;
     }
 
     /**
-     * Returns {@code true} if GNSS chipset supports reflecting planes measurement corrections,
+     * Returns {@code true} if GNSS chipset supports reflecting plane measurement corrections,
      * {@code false} otherwise.
      *
+     * @deprecated Use {@link #hasMeasurementCorrectionsReflectingPlane()} instead.
+     *
      * @hide
      */
     @SystemApi
     public boolean hasMeasurementCorrectionsReflectingPane() {
-        return hasCapability(MEASUREMENT_CORRECTIONS_REFLECTING_PLANE);
+        return hasMeasurementCorrectionsReflectingPlane();
     }
 
     /**
-     * Returns {@code true} if GNSS chipset supports antenna info, {@code false} otherwise.
+     * Returns {@code true} if GNSS chipset supports reflecting plane measurement corrections,
+     * {@code false} otherwise.
+     *
+     * @hide
      */
-    public boolean hasGnssAntennaInfo() {
-        return hasCapability(ANTENNA_INFO);
+    @SystemApi
+    public boolean hasMeasurementCorrectionsReflectingPlane() {
+        return (mMeasurementCorrectionsFlags
+                & SUB_HAL_MEASUREMENT_CORRECTIONS_CAPABILITY_REFLECTING_PLANE) != 0;
     }
 
-    private boolean hasCapability(long capability) {
-        return (mGnssCapabilities & capability) == capability;
+    /**
+     * Returns {@code true} if GNSS chipset supports measuring power totals, {@code false}
+     * otherwise.
+     *
+     * @hide
+     */
+    public boolean hasPowerTotal() {
+        return (mPowerFlags & SUB_HAL_POWER_CAPABILITY_TOTAL) != 0;
+    }
+
+    /**
+     * Returns {@code true} if GNSS chipset supports measuring single-band tracking power,
+     * {@code false} otherwise.
+     *
+     * @hide
+     */
+    public boolean hasPowerSinglebandTracking() {
+        return (mPowerFlags & SUB_HAL_POWER_CAPABILITY_SINGLEBAND_TRACKING) != 0;
+    }
+
+    /**
+     * Returns {@code true} if GNSS chipset supports measuring multi-band tracking power,
+     * {@code false} otherwise.
+     *
+     * @hide
+     */
+    public boolean hasPowerMultibandTracking() {
+        return (mPowerFlags & SUB_HAL_POWER_CAPABILITY_MULTIBAND_TRACKING) != 0;
+    }
+
+    /**
+     * Returns {@code true} if GNSS chipset supports measuring single-band acquisition power,
+     * {@code false} otherwise.
+     *
+     * @hide
+     */
+    public boolean hasPowerSinglebandAcquisition() {
+        return (mPowerFlags & SUB_HAL_POWER_CAPABILITY_SINGLEBAND_ACQUISITION) != 0;
+    }
+
+    /**
+     * Returns {@code true} if GNSS chipset supports measuring multi-band acquisition power,
+     * {@code false} otherwise.
+     *
+     * @hide
+     */
+    public boolean hasPowerMultibandAcquisition() {
+        return (mPowerFlags & SUB_HAL_POWER_CAPABILITY_MULTIBAND_ACQUISITION) != 0;
+    }
+
+    /**
+     * Returns {@code true} if GNSS chipset supports measuring OEM defined mode power, {@code false}
+     * otherwise.
+     *
+     * @hide
+     */
+    public boolean hasPowerOtherModes() {
+        return (mPowerFlags & SUB_HAL_POWER_CAPABILITY_OTHER_MODES) != 0;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (!(o instanceof GnssCapabilities)) {
+            return false;
+        }
+
+        GnssCapabilities that = (GnssCapabilities) o;
+        return mTopFlags == that.mTopFlags
+                && mMeasurementCorrectionsFlags == that.mMeasurementCorrectionsFlags
+                && mPowerFlags == that.mPowerFlags;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mTopFlags, mMeasurementCorrectionsFlags, mPowerFlags);
+    }
+
+    public static final @NonNull Creator<GnssCapabilities> CREATOR =
+            new Creator<GnssCapabilities>() {
+                @Override
+                public GnssCapabilities createFromParcel(Parcel in) {
+                    return new GnssCapabilities(in.readInt(), in.readInt(), in.readInt());
+                }
+
+                @Override
+                public GnssCapabilities[] newArray(int size) {
+                    return new GnssCapabilities[size];
+                }
+            };
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(@NonNull Parcel parcel, int flags) {
+        parcel.writeInt(mTopFlags);
+        parcel.writeInt(mMeasurementCorrectionsFlags);
+        parcel.writeInt(mPowerFlags);
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder builder = new StringBuilder();
+        builder.append("[");
+        if (hasScheduling()) {
+            builder.append("SCHEDULING ");
+        }
+        if (hasMsb()) {
+            builder.append("MSB ");
+        }
+        if (hasMsa()) {
+            builder.append("MSA ");
+        }
+        if (hasSingleShot()) {
+            builder.append("SINGLE_SHOT ");
+        }
+        if (hasOnDemandTime()) {
+            builder.append("ON_DEMAND_TIME ");
+        }
+        if (hasGeofencing()) {
+            builder.append("GEOFENCING ");
+        }
+        if (hasMeasurementCorrections()) {
+            builder.append("MEASUREMENTS ");
+        }
+        if (hasNavigationMessages()) {
+            builder.append("NAVIGATION_MESSAGES ");
+        }
+        if (hasLowPowerMode()) {
+            builder.append("LOW_POWER_MODE ");
+        }
+        if (hasSatelliteBlocklist()) {
+            builder.append("SATELLITE_BLOCKLIST ");
+        }
+        if (hasMeasurementCorrections()) {
+            builder.append("MEASUREMENT_CORRECTIONS ");
+        }
+        if (hasAntennaInfo()) {
+            builder.append("ANTENNA_INFO ");
+        }
+        if (hasMeasurementCorrectionsLosSats()) {
+            builder.append("LOS_SATS ");
+        }
+        if (hasMeasurementCorrectionsExcessPathLength()) {
+            builder.append("EXCESS_PATH_LENGTH ");
+        }
+        if (hasMeasurementCorrectionsReflectingPlane()) {
+            builder.append("REFLECTING_PLANE ");
+        }
+        if (hasPowerTotal()) {
+            builder.append("TOTAL_POWER ");
+        }
+        if (hasPowerSinglebandTracking()) {
+            builder.append("SINGLEBAND_TRACKING_POWER ");
+        }
+        if (hasPowerMultibandTracking()) {
+            builder.append("MULTIBAND_TRACKING_POWER ");
+        }
+        if (hasPowerSinglebandAcquisition()) {
+            builder.append("SINGLEBAND_ACQUISITION_POWER ");
+        }
+        if (hasPowerMultibandAcquisition()) {
+            builder.append("MULTIBAND_ACQUISITION_POWER ");
+        }
+        if (hasPowerOtherModes()) {
+            builder.append("OTHER_MODES_POWER ");
+        }
+        if (builder.length() > 1) {
+            builder.setLength(builder.length() - 1);
+        } else {
+            builder.append("NONE");
+        }
+        builder.append("]");
+        return builder.toString();
+    }
+
+    /**
+     * Builder for GnssCapabilities.
+     */
+    public static final class Builder {
+
+        private @TopHalCapabilityFlags int mTopFlags;
+        private @SubHalMeasurementCorrectionsCapabilityFlags int mMeasurementCorrectionsFlags;
+        private @SubHalPowerCapabilityFlags int mPowerFlags;
+
+        public Builder() {
+            mTopFlags = 0;
+            mMeasurementCorrectionsFlags = 0;
+            mPowerFlags = 0;
+        }
+
+        public Builder(@NonNull GnssCapabilities capabilities) {
+            mTopFlags = capabilities.mTopFlags;
+            mMeasurementCorrectionsFlags = capabilities.mMeasurementCorrectionsFlags;
+            mPowerFlags = capabilities.mPowerFlags;
+        }
+
+        /**
+         * Sets scheduling capability.
+         *
+         * @hide
+         */
+        public @NonNull Builder setHasScheduling(boolean capable) {
+            mTopFlags = setFlag(mTopFlags, TOP_HAL_CAPABILITY_SCHEDULING, capable);
+            return this;
+        }
+
+        /**
+         * Sets Mobile Station Based capability.
+         *
+         * @hide
+         */
+        public @NonNull Builder setHasMsb(boolean capable) {
+            mTopFlags = setFlag(mTopFlags, TOP_HAL_CAPABILITY_MSB, capable);
+            return this;
+        }
+
+        /**
+         * Sets Mobile Station Assisted capability.
+         *
+         * @hide
+         */
+        public @NonNull Builder setHasMsa(boolean capable) {
+            mTopFlags = setFlag(mTopFlags, TOP_HAL_CAPABILITY_MSA, capable);
+            return this;
+        }
+
+        /**
+         * Sets single shot locating capability.
+         *
+         * @hide
+         */
+        public @NonNull Builder setHasSingleShot(boolean capable) {
+            mTopFlags = setFlag(mTopFlags, TOP_HAL_CAPABILITY_SINGLE_SHOT, capable);
+            return this;
+        }
+
+        /**
+         * Sets on demand time capability.
+         *
+         * @hide
+         */
+        public @NonNull Builder setHasOnDemandTime(boolean capable) {
+            mTopFlags = setFlag(mTopFlags, TOP_HAL_CAPABILITY_ON_DEMAND_TIME, capable);
+            return this;
+        }
+
+        /**
+         * Sets geofencing capability.
+         *
+         * @hide
+         */
+        @SystemApi
+        public @NonNull Builder setHasGeofencing(boolean capable) {
+            mTopFlags = setFlag(mTopFlags, TOP_HAL_CAPABILITY_GEOFENCING, capable);
+            return this;
+        }
+
+        /**
+         * Sets measurements capability.
+         */
+        public @NonNull Builder setHasMeasurements(boolean capable) {
+            mTopFlags = setFlag(mTopFlags, TOP_HAL_CAPABILITY_MEASUREMENTS, capable);
+            return this;
+        }
+
+        /**
+         * Sets navigation messages capability.
+         */
+        public @NonNull Builder setHasNavigationMessages(boolean capable) {
+            mTopFlags = setFlag(mTopFlags, TOP_HAL_CAPABILITY_NAV_MESSAGES, capable);
+            return this;
+        }
+
+        /**
+         * Sets low power mode capability.
+         *
+         * @hide
+         */
+        @SystemApi
+        public @NonNull Builder setHasLowPowerMode(boolean capable) {
+            mTopFlags = setFlag(mTopFlags, TOP_HAL_CAPABILITY_LOW_POWER_MODE, capable);
+            return this;
+        }
+
+        /**
+         * Sets satellite blocklist capability.
+         *
+         * @hide
+         */
+        @SystemApi
+        public @NonNull Builder setHasSatelliteBlocklist(boolean capable) {
+            mTopFlags = setFlag(mTopFlags, TOP_HAL_CAPABILITY_SATELLITE_BLOCKLIST, capable);
+            return this;
+        }
+
+        /**
+         * Sets measurement corrections capability.
+         *
+         * @hide
+         */
+        @SystemApi
+        public @NonNull Builder setHasMeasurementCorrections(boolean capable) {
+            mTopFlags = setFlag(mTopFlags, TOP_HAL_CAPABILITY_MEASUREMENT_CORRECTIONS, capable);
+            return this;
+        }
+
+        /**
+         * Sets antenna info capability.
+         */
+        public @NonNull Builder setHasAntennaInfo(boolean capable) {
+            mTopFlags = setFlag(mTopFlags, TOP_HAL_CAPABILITY_ANTENNA_INFO, capable);
+            return this;
+        }
+
+        /**
+         * Sets measurement corrections line-of-sight satellites capabilitity.
+         *
+         * @hide
+         */
+        @SystemApi
+        public @NonNull Builder setHasMeasurementCorrectionsLosSats(boolean capable) {
+            mMeasurementCorrectionsFlags = setFlag(mMeasurementCorrectionsFlags,
+                    SUB_HAL_MEASUREMENT_CORRECTIONS_CAPABILITY_LOS_SATS, capable);
+            return this;
+        }
+
+        /**
+         * Sets measurement corrections excess path length capabilitity.
+         *
+         * @hide
+         */
+        @SystemApi
+        public @NonNull Builder setHasMeasurementCorrectionsExcessPathLength(boolean capable) {
+            mMeasurementCorrectionsFlags = setFlag(mMeasurementCorrectionsFlags,
+                    SUB_HAL_MEASUREMENT_CORRECTIONS_CAPABILITY_EXCESS_PATH_LENGTH, capable);
+            return this;
+        }
+
+        /**
+         * Sets measurement corrections reflecting plane capabilitity.
+         *
+         * @hide
+         */
+        @SystemApi
+        public @NonNull Builder setHasMeasurementCorrectionsReflectingPlane(boolean capable) {
+            mMeasurementCorrectionsFlags = setFlag(mMeasurementCorrectionsFlags,
+                    SUB_HAL_MEASUREMENT_CORRECTIONS_CAPABILITY_REFLECTING_PLANE, capable);
+            return this;
+        }
+
+        /**
+         * Sets power totals capabilitity.
+         *
+         * @hide
+         */
+        public @NonNull Builder setHasPowerTotal(boolean capable) {
+            mPowerFlags = setFlag(mPowerFlags, SUB_HAL_POWER_CAPABILITY_TOTAL, capable);
+            return this;
+        }
+
+        /**
+         * Sets power single-band tracking capabilitity.
+         *
+         * @hide
+         */
+        public @NonNull Builder setHasPowerSinglebandTracking(boolean capable) {
+            mPowerFlags = setFlag(mPowerFlags, SUB_HAL_POWER_CAPABILITY_SINGLEBAND_TRACKING,
+                    capable);
+            return this;
+        }
+
+        /**
+         * Sets power multi-band tracking capabilitity.
+         *
+         * @hide
+         */
+        public @NonNull Builder setHasPowerMultibandTracking(boolean capable) {
+            mPowerFlags = setFlag(mPowerFlags, SUB_HAL_POWER_CAPABILITY_MULTIBAND_TRACKING,
+                    capable);
+            return this;
+        }
+
+        /**
+         * Sets power single-band acquisition capabilitity.
+         *
+         * @hide
+         */
+        public @NonNull Builder setHasPowerSinglebandAcquisition(boolean capable) {
+            mPowerFlags = setFlag(mPowerFlags, SUB_HAL_POWER_CAPABILITY_SINGLEBAND_ACQUISITION,
+                    capable);
+            return this;
+        }
+
+        /**
+         * Sets power multi-band acquisition capabilitity.
+         *
+         * @hide
+         */
+        public @NonNull Builder setHasPowerMultibandAcquisition(boolean capable) {
+            mPowerFlags = setFlag(mPowerFlags, SUB_HAL_POWER_CAPABILITY_MULTIBAND_ACQUISITION,
+                    capable);
+            return this;
+        }
+
+        /**
+         * Sets power other modes capabilitity.
+         *
+         * @hide
+         */
+        public @NonNull Builder setHasPowerOtherModes(boolean capable) {
+            mPowerFlags = setFlag(mPowerFlags, SUB_HAL_POWER_CAPABILITY_OTHER_MODES, capable);
+            return this;
+        }
+
+        /**
+         * Builds a new GnssCapabilities.
+         */
+        public @NonNull GnssCapabilities build() {
+            return new GnssCapabilities(mTopFlags, mMeasurementCorrectionsFlags, mPowerFlags);
+        }
+
+        private static int setFlag(int value, int flag, boolean set) {
+            if (set) {
+                return value | flag;
+            } else {
+                return value & ~flag;
+            }
+        }
     }
 }
diff --git a/location/java/android/location/GnssMeasurementsEvent.java b/location/java/android/location/GnssMeasurementsEvent.java
index 98ef2d4..a07a64a 100644
--- a/location/java/android/location/GnssMeasurementsEvent.java
+++ b/location/java/android/location/GnssMeasurementsEvent.java
@@ -16,9 +16,9 @@
 
 package android.location;
 
-import android.annotation.TestApi;
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.TestApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -46,8 +46,10 @@
     public static abstract class Callback {
         /**
          * The status of the GNSS measurements event.
+         * @deprecated Do not use.
          * @hide
          */
+        @Deprecated
         @Retention(RetentionPolicy.SOURCE)
         @IntDef({STATUS_NOT_SUPPORTED, STATUS_READY, STATUS_LOCATION_DISABLED, STATUS_NOT_ALLOWED})
         public @interface GnssMeasurementsStatus {}
@@ -56,19 +58,28 @@
          * The system does not support tracking of GNSS Measurements.
          *
          * <p>This status will not change in the future.
+         *
+         * @deprecated Do not use.
          */
+        @Deprecated
         public static final int STATUS_NOT_SUPPORTED = 0;
 
         /**
          * GNSS Measurements are successfully being tracked, it will receive updates once they are
          * available.
+         *
+         * @deprecated Do not use.
          */
+        @Deprecated
         public static final int STATUS_READY = 1;
 
         /**
          * GPS provider or Location is disabled, updates will not be received until they are
          * enabled.
+         *
+         * @deprecated Do not use.
          */
+        @Deprecated
         public static final int STATUS_LOCATION_DISABLED = 2;
 
         /**
@@ -81,7 +92,10 @@
          *
          * <p>If such a status is received, one would try again at a later time point where no
          * other client is having a conflicting request.
+         *
+         * @deprecated Do not use.
          */
+        @Deprecated
         public static final int STATUS_NOT_ALLOWED = 3;
 
         /**
@@ -91,7 +105,13 @@
 
         /**
          * Reports the latest status of the GNSS Measurements sub-system.
+         *
+         * @deprecated Do not rely on this callback. From Android S onwards this callback will be
+         * invoked once with {@link #STATUS_READY} in all cases for backwards compatibility, and
+         * then never invoked again. Use LocationManager APIs if you need to determine if
+         * GNSS measurements are supported or if location is off, etc...
          */
+        @Deprecated
         public void onStatusChanged(@GnssMeasurementsStatus int status) {}
     }
 
diff --git a/location/java/android/location/GnssNavigationMessage.java b/location/java/android/location/GnssNavigationMessage.java
index aade5ac..84a363d 100644
--- a/location/java/android/location/GnssNavigationMessage.java
+++ b/location/java/android/location/GnssNavigationMessage.java
@@ -110,6 +110,7 @@
     public static abstract class Callback {
         /**
          * The status of GNSS Navigation Message event.
+         * @deprecated Do not use.
          * @hide
          */
         @Retention(RetentionPolicy.SOURCE)
@@ -120,19 +121,28 @@
          * The system does not support tracking of GNSS Navigation Messages.
          *
          * This status will not change in the future.
+         *
+         * @deprecated Do not use.
          */
+        @Deprecated
         public static final int STATUS_NOT_SUPPORTED = 0;
 
         /**
          * GNSS Navigation Messages are successfully being tracked, it will receive updates once
          * they are available.
+         *
+         * @deprecated Do not use.
          */
+        @Deprecated
         public static final int STATUS_READY = 1;
 
         /**
          * GNSS provider or Location is disabled, updated will not be received until they are
          * enabled.
+         *
+         * @deprecated Do not use.
          */
+        @Deprecated
         public static final int STATUS_LOCATION_DISABLED = 2;
 
         /**
@@ -142,7 +152,13 @@
 
         /**
          * Returns the latest status of the GNSS Navigation Messages sub-system.
+         *
+         * @deprecated Do not rely on this callback. From Android S onwards this callback will be
+         * invoked once with {@link #STATUS_READY} in all cases for backwards compatibility, and
+         * then never invoked again. Use LocationManager APIs if you need to determine if
+         * GNSS navigation messages are supported or if location is off, etc...
          */
+        @Deprecated
         public void onStatusChanged(@GnssNavigationMessageStatus int status) {}
     }
 
diff --git a/location/java/android/location/GnssStatus.java b/location/java/android/location/GnssStatus.java
index c5b4c16..b46e8ce 100644
--- a/location/java/android/location/GnssStatus.java
+++ b/location/java/android/location/GnssStatus.java
@@ -391,20 +391,10 @@
             float[] basebandCn0DbHzs = new float[svCount];
             for (int i = 0; i < svCount; i++) {
                 svidWithFlags[i] = in.readInt();
-            }
-            for (int i = 0; i < svCount; i++) {
                 cn0DbHzs[i] = in.readFloat();
-            }
-            for (int i = 0; i < svCount; i++) {
                 elevations[i] = in.readFloat();
-            }
-            for (int i = 0; i < svCount; i++) {
                 azimuths[i] = in.readFloat();
-            }
-            for (int i = 0; i < svCount; i++) {
                 carrierFrequencies[i] = in.readFloat();
-            }
-            for (int i = 0; i < svCount; i++) {
                 basebandCn0DbHzs[i] = in.readFloat();
             }
 
@@ -428,20 +418,10 @@
         parcel.writeInt(mSvCount);
         for (int i = 0; i < mSvCount; i++) {
             parcel.writeInt(mSvidWithFlags[i]);
-        }
-        for (int i = 0; i < mSvCount; i++) {
             parcel.writeFloat(mCn0DbHzs[i]);
-        }
-        for (int i = 0; i < mSvCount; i++) {
             parcel.writeFloat(mElevations[i]);
-        }
-        for (int i = 0; i < mSvCount; i++) {
             parcel.writeFloat(mAzimuths[i]);
-        }
-        for (int i = 0; i < mSvCount; i++) {
             parcel.writeFloat(mCarrierFrequencies[i]);
-        }
-        for (int i = 0; i < mSvCount; i++) {
             parcel.writeFloat(mBasebandCn0DbHzs[i]);
         }
     }
diff --git a/location/java/android/location/IGnssAntennaInfoListener.aidl b/location/java/android/location/IGnssAntennaInfoListener.aidl
deleted file mode 100644
index 603ed6a..0000000
--- a/location/java/android/location/IGnssAntennaInfoListener.aidl
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * 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.location;
-
-import android.location.GnssAntennaInfo;
-
-/**
- * {@hide}
- */
-oneway interface IGnssAntennaInfoListener {
-    void onGnssAntennaInfoReceived(in List<GnssAntennaInfo> gnssAntennaInfo);
-}
\ No newline at end of file
diff --git a/location/java/com/android/internal/location/timezone/LocationTimeZoneEvent.aidl b/location/java/android/location/IGnssNmeaListener.aidl
similarity index 81%
copy from location/java/com/android/internal/location/timezone/LocationTimeZoneEvent.aidl
copy to location/java/android/location/IGnssNmeaListener.aidl
index 199e067..c67cc89 100644
--- a/location/java/com/android/internal/location/timezone/LocationTimeZoneEvent.aidl
+++ b/location/java/android/location/IGnssNmeaListener.aidl
@@ -14,6 +14,12 @@
  * limitations under the License.
  */
 
-package com.android.internal.location.timezone;
+package android.location;
 
-parcelable LocationTimeZoneEvent;
+/**
+ * {@hide}
+ */
+oneway interface IGnssNmeaListener
+{
+    void onNmeaReceived(long timestamp, String nmea);
+}
\ No newline at end of file
diff --git a/location/java/android/location/IGnssStatusListener.aidl b/location/java/android/location/IGnssStatusListener.aidl
index 57b1268..c25046e 100644
--- a/location/java/android/location/IGnssStatusListener.aidl
+++ b/location/java/android/location/IGnssStatusListener.aidl
@@ -27,5 +27,4 @@
     void onGnssStopped();
     void onFirstFix(int ttff);
     void onSvStatusChanged(in GnssStatus gnssStatus);
-    void onNmeaReceived(long timestamp, String nmea);
 }
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index a666eb4..c50904c 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -21,24 +21,25 @@
 import android.location.Criteria;
 import android.location.GeocoderParams;
 import android.location.Geofence;
+import android.location.GnssAntennaInfo;
+import android.location.GnssCapabilities;
 import android.location.GnssMeasurementCorrections;
 import android.location.GnssMeasurementRequest;
 import android.location.IGeocodeListener;
-import android.location.IGnssAntennaInfoListener;
 import android.location.IGnssMeasurementsListener;
 import android.location.IGnssStatusListener;
 import android.location.IGnssNavigationMessageListener;
+import android.location.IGnssNmeaListener;
 import android.location.ILocationCallback;
 import android.location.ILocationListener;
 import android.location.LastLocationRequest;
 import android.location.Location;
 import android.location.LocationRequest;
 import android.location.LocationTime;
+import android.location.ProviderProperties;
 import android.os.Bundle;
 import android.os.ICancellationSignal;
 
-import com.android.internal.location.ProviderProperties;
-
 /**
  * System private API for talking with the location service.
  *
@@ -71,20 +72,22 @@
         double upperRightLatitude, double upperRightLongitude, int maxResults,
         in GeocoderParams params, in IGeocodeListener listener);
 
-    long getGnssCapabilities();
+    GnssCapabilities getGnssCapabilities();
     int getGnssYearOfHardware();
     String getGnssHardwareModelName();
 
+    @nullable List<GnssAntennaInfo> getGnssAntennaInfos();
+
     void registerGnssStatusCallback(in IGnssStatusListener callback, String packageName, String attributionTag);
     void unregisterGnssStatusCallback(in IGnssStatusListener callback);
 
+    void registerGnssNmeaCallback(in IGnssNmeaListener callback, String packageName, String attributionTag);
+    void unregisterGnssNmeaCallback(in IGnssNmeaListener callback);
+
     void addGnssMeasurementsListener(in GnssMeasurementRequest request, in IGnssMeasurementsListener listener, String packageName, String attributionTag);
     void removeGnssMeasurementsListener(in IGnssMeasurementsListener listener);
     void injectGnssMeasurementCorrections(in GnssMeasurementCorrections corrections);
 
-    void addGnssAntennaInfoListener(in IGnssAntennaInfoListener listener, String packageName, String attributionTag);
-    void removeGnssAntennaInfoListener(in IGnssAntennaInfoListener listener);
-
     void addGnssNavigationMessageListener(in IGnssNavigationMessageListener listener, String packageName, String attributionTag);
     void removeGnssNavigationMessageListener(in IGnssNavigationMessageListener listener);
 
@@ -93,6 +96,7 @@
     void flushGnssBatch();
     void stopGnssBatch();
 
+    boolean hasProvider(String provider);
     List<String> getAllProviders();
     List<String> getProviders(in Criteria criteria, boolean enabledOnly);
     String getBestProvider(in Criteria criteria, boolean enabledOnly);
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index ecdba1f..1d3e8eb 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -44,7 +44,10 @@
 import android.compat.annotation.ChangeId;
 import android.compat.annotation.EnabledAfter;
 import android.compat.annotation.UnsupportedAppUsage;
+import android.content.BroadcastReceiver;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.pm.PackageManager;
 import android.os.Build;
 import android.os.Bundle;
@@ -61,10 +64,10 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.listeners.ListenerExecutor;
 import com.android.internal.listeners.ListenerTransportMultiplexer;
-import com.android.internal.location.ProviderProperties;
 import com.android.internal.util.Preconditions;
 
 import java.lang.ref.WeakReference;
+import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
@@ -313,6 +316,48 @@
             "android.location.HIGH_POWER_REQUEST_CHANGE";
 
     /**
+     * Broadcast intent action when GNSS capabilities change. This is most common at boot time as
+     * GNSS capabilities are queried from the chipset. Includes an intent extra,
+     * {@link #EXTRA_GNSS_CAPABILITIES}, with the new {@link GnssCapabilities}.
+     *
+     * @see #EXTRA_GNSS_CAPABILITIES
+     * @see #getGnssCapabilities()
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_GNSS_CAPABILITIES_CHANGED =
+            "android.location.action.GNSS_CAPABILITIES_CHANGED";
+
+    /**
+     * Intent extra included with {@link #ACTION_GNSS_CAPABILITIES_CHANGED} broadcasts, containing
+     * the new {@link GnssCapabilities}.
+     *
+     * @see #ACTION_GNSS_CAPABILITIES_CHANGED
+     */
+    public static final String EXTRA_GNSS_CAPABILITIES = "android.location.extra.GNSS_CAPABILITIES";
+
+    /**
+     * Broadcast intent action when GNSS antenna infos change. Includes an intent extra,
+     * {@link #EXTRA_GNSS_ANTENNA_INFOS}, with an ArrayList of the new {@link GnssAntennaInfo}. This
+     * may be read via {@link android.content.Intent#getParcelableArrayListExtra(String)}.
+     *
+     * @see #EXTRA_GNSS_ANTENNA_INFOS
+     * @see #getGnssAntennaInfos()
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_GNSS_ANTENNA_INFOS_CHANGED =
+            "android.location.action.GNSS_ANTENNA_INFOS_CHANGED";
+
+    /**
+     * Intent extra included with {@link #ACTION_GNSS_ANTENNA_INFOS_CHANGED} broadcasts, containing
+     * the new ArrayList of {@link GnssAntennaInfo}. This may be read via
+     * {@link android.content.Intent#getParcelableArrayListExtra(String)}.
+     *
+     * @see #ACTION_GNSS_ANTENNA_INFOS_CHANGED
+     */
+    public static final String EXTRA_GNSS_ANTENNA_INFOS =
+            "android.location.extra.GNSS_ANTENNA_INFOS";
+
+    /**
      * Broadcast intent action for Settings app to inject a footer at the bottom of location
      * settings. This is for use only by apps that are included in the system image.
      *
@@ -381,6 +426,8 @@
     @GuardedBy("mLock")
     @Nullable private GnssStatusTransportMultiplexer mGnssStatusTransportMultiplexer;
     @GuardedBy("mLock")
+    @Nullable private GnssNmeaTransportMultiplexer mGnssNmeaTransportMultiplexer;
+    @GuardedBy("mLock")
     @Nullable private GnssMeasurementsTransportMultiplexer mGnssMeasurementsTransportMultiplexer;
     @GuardedBy("mLock")
     @Nullable private GnssNavigationTransportMultiplexer mGnssNavigationTransportMultiplexer;
@@ -404,6 +451,15 @@
         }
     }
 
+    private GnssNmeaTransportMultiplexer getGnssNmeaTransportMultiplexer() {
+        synchronized (mLock) {
+            if (mGnssNmeaTransportMultiplexer == null) {
+                mGnssNmeaTransportMultiplexer = new GnssNmeaTransportMultiplexer();
+            }
+            return mGnssNmeaTransportMultiplexer;
+        }
+    }
+
     private GnssMeasurementsTransportMultiplexer getGnssMeasurementsTransportMultiplexer() {
         synchronized (mLock) {
             if (mGnssMeasurementsTransportMultiplexer == null) {
@@ -1616,6 +1672,25 @@
     }
 
     /**
+     * Returns true if the given location provider exists on this device, irrespective of whether
+     * it is currently enabled or not.
+     *
+     * @param provider a potential location provider
+     * @return true if the location provider exists, false otherwise
+     *
+     * @throws IllegalArgumentException if provider is null
+     */
+    public boolean hasProvider(@NonNull String provider) {
+        Preconditions.checkArgument(provider != null, "invalid null provider");
+
+        try {
+            return mService.hasProvider(provider);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Returns a list of the names of all available location providers. All providers are returned,
      * including those that are currently disabled.
      *
@@ -1703,7 +1778,12 @@
      * @return location provider information, or null if provider does not exist
      *
      * @throws IllegalArgumentException if provider is null
+     *
+     * @deprecated This method has no way to indicate that a provider's properties are unknown, and
+     * so may return incorrect results on rare occasions. Use {@link #getProviderProperties(String)}
+     * instead.
      */
+    @Deprecated
     public @Nullable LocationProvider getProvider(@NonNull String provider) {
         Preconditions.checkArgument(provider != null, "invalid null provider");
 
@@ -1723,11 +1803,33 @@
         }
 
         try {
+
             ProviderProperties properties = mService.getProviderProperties(provider);
-            if (properties == null) {
-                return null;
-            }
             return new LocationProvider(provider, properties);
+        } catch (IllegalArgumentException e) {
+            // provider does not exist
+            return null;
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Returns the properties of the given provider, or null if the properties are currently
+     * unknown. Provider properties may change over time, although this is discouraged, and should
+     * be rare. The most common transition is when provider properties go from being unknown to
+     * known, which is most common near boot time.
+     *
+     * @param provider a provider listed by {@link #getAllProviders()}
+     * @return location provider properties, or null if properties are currently unknown
+     *
+     * @throws IllegalArgumentException if provider is null or does not exist
+     */
+    public @Nullable ProviderProperties getProviderProperties(@NonNull String provider) {
+        Preconditions.checkArgument(provider != null, "invalid null provider");
+
+        try {
+            return mService.getProviderProperties(provider);
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
@@ -1826,12 +1928,14 @@
     public void addTestProvider(
             @NonNull String provider, boolean requiresNetwork, boolean requiresSatellite,
             boolean requiresCell, boolean hasMonetaryCost, boolean supportsAltitude,
-            boolean supportsSpeed, boolean supportsBearing, int powerRequirement, int accuracy) {
+            boolean supportsSpeed, boolean supportsBearing,
+            @ProviderProperties.PowerUsage int powerUsage,
+            @ProviderProperties.Accuracy int accuracy) {
         Preconditions.checkArgument(provider != null, "invalid null provider");
 
         ProviderProperties properties = new ProviderProperties(requiresNetwork,
                 requiresSatellite, requiresCell, hasMonetaryCost, supportsAltitude, supportsSpeed,
-                supportsBearing, powerRequirement, accuracy);
+                supportsBearing, powerUsage, accuracy);
         try {
             mService.addTestProvider(provider, properties, mContext.getOpPackageName(),
                     mContext.getFeatureId());
@@ -2045,20 +2149,15 @@
      */
     public @NonNull GnssCapabilities getGnssCapabilities() {
         try {
-            long gnssCapabilities = mService.getGnssCapabilities();
-            if (gnssCapabilities == GnssCapabilities.INVALID_CAPABILITIES) {
-                gnssCapabilities = 0L;
-            }
-            return GnssCapabilities.of(gnssCapabilities);
+            return mService.getGnssCapabilities();
         } catch (RemoteException e) {
             throw e.rethrowFromSystemServer();
         }
     }
 
     /**
-     * Returns the model year of the GNSS hardware and software build. More details, such as build
-     * date, may be available in {@link #getGnssHardwareModelName()}. May return 0 if the model year
-     * is less than 2016.
+     * Returns the model year of the GNSS hardware and software build, or 0 if the model year
+     * is before 2016.
      */
     public int getGnssYearOfHardware() {
         try {
@@ -2069,13 +2168,10 @@
     }
 
     /**
-     * Returns the Model Name (including Vendor and Hardware/Software Version) of the GNSS hardware
-     * driver.
+     * Returns the model name (including vendor and hardware/software version) of the GNSS hardware
+     * driver, or null if this information is not available.
      *
-     * <p> No device-specific serial number or ID is returned from this API.
-     *
-     * <p> Will return null when the GNSS hardware abstraction layer does not support providing
-     * this value.
+     * <p>No device-specific serial number or ID is returned from this API.
      */
     @Nullable
     public String getGnssHardwareModelName() {
@@ -2087,6 +2183,20 @@
     }
 
     /**
+     * Returns the current list of GNSS antenna infos, or null if unknown or unsupported.
+     *
+     * @see #getGnssCapabilities()
+     */
+    @Nullable
+    public List<GnssAntennaInfo> getGnssAntennaInfos() {
+        try {
+            return mService.getGnssAntennaInfos();
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * Retrieves information about the current status of the GPS engine. This should only be called
      * from within the {@link GpsStatus.Listener#onGpsStatusChanged} callback to ensure that the
      * data is copied atomically.
@@ -2170,8 +2280,11 @@
      * Registers a GNSS status callback. This method must be called from a {@link Looper} thread,
      * and callbacks will occur on that looper.
      *
-     * @param callback GNSS status callback object to register
-     * @return true if the listener was successfully added
+     * <p>See {@link #registerGnssStatusCallback(Executor, GnssStatus.Callback)} for more detail on
+     * how this method works.
+     *
+     * @param callback the callback to register
+     * @return {@code true} always
      *
      * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
      *
@@ -2187,9 +2300,12 @@
     /**
      * Registers a GNSS status callback.
      *
-     * @param callback GNSS status callback object to register
-     * @param handler  a handler with a looper that the callback runs on
-     * @return true if the listener was successfully added
+     * <p>See {@link #registerGnssStatusCallback(Executor, GnssStatus.Callback)} for more detail on
+     * how this method works.
+     *
+     * @param callback the callback to register
+     * @param handler  the handler the callback runs on
+     * @return {@code true} always
      *
      * @throws IllegalArgumentException if callback is null
      * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
@@ -2205,11 +2321,12 @@
     }
 
     /**
-     * Registers a GNSS status callback.
+     * Registers a GNSS status callback. GNSS status information will only be received while the
+     * {@link #GPS_PROVIDER} is enabled, and while the client app is in the foreground.
      *
      * @param executor the executor that the callback runs on
-     * @param callback GNSS status callback object to register
-     * @return true if the listener was successfully added
+     * @param callback the callback to register
+     * @return {@code true} always
      *
      * @throws IllegalArgumentException if executor is null
      * @throws IllegalArgumentException if callback is null
@@ -2254,8 +2371,12 @@
     /**
      * Adds an NMEA listener.
      *
-     * @param listener a {@link OnNmeaMessageListener} object to register
-     * @return true if the listener was successfully added
+     * <p>See {@link #addNmeaListener(Executor, OnNmeaMessageListener)} for more detail on how this
+     * method works.
+     *
+     * @param listener the listener to register
+     * @return {@code true} always
+     *
      * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
      * @deprecated Use {@link #addNmeaListener(OnNmeaMessageListener, Handler)} or {@link
      * #addNmeaListener(Executor, OnNmeaMessageListener)} instead.
@@ -2269,9 +2390,12 @@
     /**
      * Adds an NMEA listener.
      *
-     * @param listener a {@link OnNmeaMessageListener} object to register
-     * @param handler  a handler with the looper that the listener runs on.
-     * @return true if the listener was successfully added
+     * <p>See {@link #addNmeaListener(Executor, OnNmeaMessageListener)} for more detail on how this
+     * method works.
+     *
+     * @param listener the listener to register
+     * @param handler  the handler that the listener runs on
+     * @return {@code true} always
      *
      * @throws IllegalArgumentException if listener is null
      * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
@@ -2287,11 +2411,12 @@
     }
 
     /**
-     * Adds an NMEA listener.
+     * Adds an NMEA listener. GNSS NMEA information will only be received while the
+     * {@link #GPS_PROVIDER} is enabled, and while the client app is in the foreground.
      *
-     * @param listener a {@link OnNmeaMessageListener} object to register
-     * @param executor the {@link Executor} that the listener runs on.
-     * @return true if the listener was successfully added
+     * @param listener the listener to register
+     * @param executor the executor that the listener runs on
+     * @return {@code true} always
      *
      * @throws IllegalArgumentException if executor is null
      * @throws IllegalArgumentException if listener is null
@@ -2301,7 +2426,7 @@
     public boolean addNmeaListener(
             @NonNull @CallbackExecutor Executor executor,
             @NonNull OnNmeaMessageListener listener) {
-        getGnssStatusTransportMultiplexer().addListener(listener, executor);
+        getGnssNmeaTransportMultiplexer().addListener(listener, executor);
         return true;
     }
 
@@ -2311,7 +2436,7 @@
      * @param listener a {@link OnNmeaMessageListener} object to remove
      */
     public void removeNmeaListener(@NonNull OnNmeaMessageListener listener) {
-        getGnssStatusTransportMultiplexer().removeListener(listener);
+        getGnssNmeaTransportMultiplexer().removeListener(listener);
     }
 
     /**
@@ -2339,10 +2464,14 @@
     public void removeGpsMeasurementListener(GpsMeasurementsEvent.Listener listener) {}
 
     /**
-     * Registers a GPS Measurement callback which will run on a binder thread.
+     * Registers a GNSS measurements callback which will run on a binder thread.
      *
-     * @param callback a {@link GnssMeasurementsEvent.Callback} object to register.
-     * @return {@code true} if the callback was added successfully, {@code false} otherwise.
+     * <p>See {@link #registerGnssMeasurementsCallback(Executor, GnssMeasurementsEvent.Callback)
+     * for more detail on how this method works.
+     *
+     * @param callback a {@link GnssMeasurementsEvent.Callback} object to register
+     * @return {@code true} always
+     *
      * @deprecated Use {@link
      * #registerGnssMeasurementsCallback(GnssMeasurementsEvent.Callback, Handler)} or {@link
      * #registerGnssMeasurementsCallback(Executor, GnssMeasurementsEvent.Callback)} instead.
@@ -2355,11 +2484,14 @@
     }
 
     /**
-     * Registers a GPS Measurement callback.
+     * Registers a GNSS measurements callback.
      *
-     * @param callback a {@link GnssMeasurementsEvent.Callback} object to register.
-     * @param handler  the handler that the callback runs on.
-     * @return {@code true} if the callback was added successfully, {@code false} otherwise.
+     * <p>See {@link #registerGnssMeasurementsCallback(Executor, GnssMeasurementsEvent.Callback)
+     * for more detail on how this method works.
+     *
+     * @param callback a {@link GnssMeasurementsEvent.Callback} object to register
+     * @param handler  the handler that the callback runs on
+     * @return {@code true} always
      *
      * @throws IllegalArgumentException if callback is null
      * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
@@ -2376,11 +2508,14 @@
     }
 
     /**
-     * Registers a GPS Measurement callback.
+     * Registers a GNSS measurements callback. GNSS measurements information will only be received
+     * while the {@link #GPS_PROVIDER} is enabled, and while the client app is in the foreground.
      *
-     * @param callback a {@link GnssMeasurementsEvent.Callback} object to register.
-     * @param executor the executor that the callback runs on.
-     * @return {@code true} if the callback was added successfully, {@code false} otherwise.
+     * <p>Not all GNSS chipsets support measurements updates, see {@link #getGnssCapabilities()}.
+     *
+     * @param executor the executor that the callback runs on
+     * @param callback the callback to register
+     * @return {@code true} always
      *
      * @throws IllegalArgumentException if executor is null
      * @throws IllegalArgumentException if callback is null
@@ -2397,12 +2532,11 @@
     /**
      * Registers a GNSS Measurement callback.
      *
-     * @param request  extra parameters to pass to GNSS measurement provider. For example, if {@link
-     *                 GnssRequest#isFullTracking()} is true, GNSS chipset switches off duty
-     *                 cycling.
-     * @param executor the executor that the callback runs on.
-     * @param callback a {@link GnssMeasurementsEvent.Callback} object to register.
-     * @return {@code true} if the callback was added successfully, {@code false} otherwise.
+     * @param request  the gnss measurement request containgin measurement parameters
+     * @param executor the executor that the callback runs on
+     * @param callback the callack to register
+     * @return {@code true} always
+     *
      * @throws IllegalArgumentException if request is null
      * @throws IllegalArgumentException if executor is null
      * @throws IllegalArgumentException if callback is null
@@ -2451,8 +2585,7 @@
     /**
      * Injects GNSS measurement corrections into the GNSS chipset.
      *
-     * @param measurementCorrections a {@link GnssMeasurementCorrections} object with the GNSS
-     *     measurement corrections to be injected into the GNSS chipset.
+     * @param measurementCorrections measurement corrections to be injected into the chipset
      *
      * @throws IllegalArgumentException if measurementCorrections is null
      * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
@@ -2481,18 +2614,24 @@
     }
 
     /**
-     * Registers a Gnss Antenna Info listener. Only expect results if
-     * {@link GnssCapabilities#hasGnssAntennaInfo()} shows that antenna info is supported.
+     * Registers a GNSS antenna info listener. GNSS antenna info updates will only be received while
+     * the {@link #GPS_PROVIDER} is enabled, and while the client app is in the foreground.
      *
-     * @param executor the executor that the listener runs on.
-     * @param listener a {@link GnssAntennaInfo.Listener} object to register.
-     * @return {@code true} if the listener was added successfully, {@code false} otherwise.
+     * <p>Not all GNSS chipsets support antenna info updates, see {@link #getGnssCapabilities()}.
+     *
+     * <p>Prior to Android S, this requires the {@link Manifest.permission#ACCESS_FINE_LOCATION}
+     * permission.
+     *
+     * @param executor the executor that the listener runs on
+     * @param listener the listener to register
+     * @return {@code true} always
      *
      * @throws IllegalArgumentException if executor is null
      * @throws IllegalArgumentException if listener is null
-     * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
+     *
+     * @deprecated Prefer to use a receiver for {@link #ACTION_GNSS_ANTENNA_INFOS_CHANGED}.
      */
-    @RequiresPermission(ACCESS_FINE_LOCATION)
+    @Deprecated
     public boolean registerAntennaInfoListener(
             @NonNull @CallbackExecutor Executor executor,
             @NonNull GnssAntennaInfo.Listener listener) {
@@ -2503,8 +2642,11 @@
     /**
      * Unregisters a GNSS Antenna Info listener.
      *
-     * @param listener a {@link GnssAntennaInfo.Listener} object to remove.
+     * @param listener a {@link GnssAntennaInfo.Listener} object to remove
+     *
+     * @deprecated Prefer to use a receiver for {@link #ACTION_GNSS_ANTENNA_INFOS_CHANGED}.
      */
+    @Deprecated
     public void unregisterAntennaInfoListener(@NonNull GnssAntennaInfo.Listener listener) {
         getGnssAntennaInfoTransportMultiplexer().removeListener(listener);
     }
@@ -2534,10 +2676,15 @@
     public void removeGpsNavigationMessageListener(GpsNavigationMessageEvent.Listener listener) {}
 
     /**
-     * Registers a GNSS Navigation Message callback which will run on a binder thread.
+     * Registers a GNSS navigation message callback which will run on a binder thread.
      *
-     * @param callback a {@link GnssNavigationMessage.Callback} object to register.
-     * @return {@code true} if the callback was added successfully, {@code false} otherwise.
+     * <p>See
+     * {@link #registerGnssNavigationMessageCallback(Executor, GnssNavigationMessage.Callback)} for
+     * more detail on how this method works.
+     *
+     * @param callback the callback to register
+     * @return {@code true} always
+     *
      * @deprecated Use {@link
      * #registerGnssNavigationMessageCallback(GnssNavigationMessage.Callback, Handler)} or {@link
      * #registerGnssNavigationMessageCallback(Executor, GnssNavigationMessage.Callback)} instead.
@@ -2549,11 +2696,15 @@
     }
 
     /**
-     * Registers a GNSS Navigation Message callback.
+     * Registers a GNSS navigation message callback.
      *
-     * @param callback a {@link GnssNavigationMessage.Callback} object to register.
-     * @param handler  the handler that the callback runs on.
-     * @return {@code true} if the callback was added successfully, {@code false} otherwise.
+     * <p>See
+     * {@link #registerGnssNavigationMessageCallback(Executor, GnssNavigationMessage.Callback)} for
+     * more detail on how this method works.
+     *
+     * @param callback the callback to register
+     * @param handler  the handler that the callback runs on
+     * @return {@code true} always
      *
      * @throws IllegalArgumentException if callback is null
      * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
@@ -2569,11 +2720,15 @@
     }
 
     /**
-     * Registers a GNSS Navigation Message callback.
+     * Registers a GNSS navigation message callback. GNSS navigation messages will only be received
+     * while the {@link #GPS_PROVIDER} is enabled, and while the client app is in the foreground.
      *
-     * @param callback a {@link GnssNavigationMessage.Callback} object to register.
-     * @param executor the looper that the callback runs on.
-     * @return {@code true} if the callback was added successfully, {@code false} otherwise.
+     * <p>Not all GNSS chipsets support navigation message updates, see
+     * {@link #getGnssCapabilities()}.
+     *
+     * @param executor the executor that the callback runs on
+     * @param callback the callback to register
+     * @return {@code true} always
      *
      * @throws IllegalArgumentException if executor is null
      * @throws IllegalArgumentException if callback is null
@@ -2601,6 +2756,9 @@
      * Returns the batch size (in number of Location objects) that are supported by the batching
      * interface.
      *
+     * Prior to Android S this call requires the {@link Manifest.permission#LOCATION_HARDWARE}
+     * permission.
+     *
      * @return Maximum number of location objects that can be returned
      * @deprecated Do not use
      * @hide
@@ -2804,20 +2962,6 @@
         }
     }
 
-    private static class NmeaAdapter extends GnssStatus.Callback implements OnNmeaMessageListener {
-
-        private final OnNmeaMessageListener mListener;
-
-        NmeaAdapter(OnNmeaMessageListener listener) {
-            mListener = listener;
-        }
-
-        @Override
-        public void onNmeaMessage(String message, long timestamp) {
-            mListener.onNmeaMessage(message, timestamp);
-        }
-    }
-
     private static class GpsAdapter extends GnssStatus.Callback {
 
         private final GpsStatus.Listener mGpsListener;
@@ -2865,11 +3009,6 @@
             return mTtff;
         }
 
-        public void addListener(@NonNull OnNmeaMessageListener listener,
-                @NonNull Executor executor) {
-            addListener(listener, null, new NmeaAdapter(listener), executor);
-        }
-
         public void addListener(@NonNull GpsStatus.Listener listener, @NonNull Executor executor) {
             addListener(listener, null, new GpsAdapter(listener), executor);
         }
@@ -2922,14 +3061,51 @@
                 mGnssStatus = gnssStatus;
                 deliverToListeners(callback -> callback.onSatelliteStatusChanged(gnssStatus));
             }
+        }
+    }
+
+    private class GnssNmeaTransportMultiplexer extends
+            ListenerTransportMultiplexer<Void, OnNmeaMessageListener> {
+
+        private @Nullable IGnssNmeaListener mListenerTransport;
+
+        GnssNmeaTransportMultiplexer() {}
+
+        public void addListener(@NonNull OnNmeaMessageListener listener,
+                @NonNull Executor executor) {
+            addListener(listener, null, listener, executor);
+        }
+
+        @Override
+        protected void registerWithServer(Void ignored) throws RemoteException {
+            IGnssNmeaListener transport = mListenerTransport;
+            if (transport == null) {
+                transport = new GnssNmeaListener();
+            }
+
+            // if a remote exception is thrown the transport should not be set
+            mListenerTransport = null;
+            mService.registerGnssNmeaCallback(transport, mContext.getPackageName(),
+                    mContext.getAttributionTag());
+            mListenerTransport = transport;
+        }
+
+        @Override
+        protected void unregisterWithServer() throws RemoteException {
+            if (mListenerTransport != null) {
+                IGnssNmeaListener transport = mListenerTransport;
+                mListenerTransport = null;
+                mService.unregisterGnssNmeaCallback(transport);
+            }
+        }
+
+        private class GnssNmeaListener extends IGnssNmeaListener.Stub {
+
+            GnssNmeaListener() {}
 
             @Override
             public void onNmeaReceived(long timestamp, String nmea) {
-                deliverToListeners((callback) -> {
-                    if (callback instanceof NmeaAdapter) {
-                        ((NmeaAdapter) callback).onNmeaMessage(nmea, timestamp);
-                    }
-                });
+                deliverToListeners(callback -> callback.onNmeaMessage(nmea, timestamp));
             }
         }
     }
@@ -3044,40 +3220,41 @@
     private class GnssAntennaInfoTransportMultiplexer extends
             ListenerTransportMultiplexer<Void, GnssAntennaInfo.Listener> {
 
-        private @Nullable IGnssAntennaInfoListener mListenerTransport;
+        private @Nullable BroadcastReceiver mListenerTransport;
 
         GnssAntennaInfoTransportMultiplexer() {}
 
         @Override
-        protected void registerWithServer(Void ignored) throws RemoteException {
-            IGnssAntennaInfoListener transport = mListenerTransport;
-            if (transport == null) {
-                transport = new GnssAntennaInfoListener();
+        protected void registerWithServer(Void ignored) {
+            if (mListenerTransport == null) {
+                // if an exception is thrown the transport should not be set
+                BroadcastReceiver transport = new GnssAntennaInfoReceiver();
+                mContext.registerReceiver(transport,
+                        new IntentFilter(ACTION_GNSS_ANTENNA_INFOS_CHANGED));
+                mListenerTransport = transport;
             }
-
-            // if a remote exception is thrown the transport should not be set
-            mListenerTransport = null;
-            mService.addGnssAntennaInfoListener(transport, mContext.getPackageName(),
-                    mContext.getAttributionTag());
-            mListenerTransport = transport;
         }
 
         @Override
-        protected void unregisterWithServer() throws RemoteException {
+        protected void unregisterWithServer() {
             if (mListenerTransport != null) {
-                IGnssAntennaInfoListener transport = mListenerTransport;
+                BroadcastReceiver transport = mListenerTransport;
                 mListenerTransport = null;
-                mService.removeGnssAntennaInfoListener(transport);
+                mContext.unregisterReceiver(transport);
             }
         }
 
-        private class GnssAntennaInfoListener extends IGnssAntennaInfoListener.Stub {
+        private class GnssAntennaInfoReceiver extends BroadcastReceiver {
 
-            GnssAntennaInfoListener() {}
+            GnssAntennaInfoReceiver() {}
 
             @Override
-            public void onGnssAntennaInfoReceived(List<GnssAntennaInfo> infos) {
-                deliverToListeners(callback -> callback.onGnssAntennaInfoReceived(infos));
+            public void onReceive(Context context, Intent intent) {
+                ArrayList<GnssAntennaInfo> infos = intent.getParcelableArrayListExtra(
+                        EXTRA_GNSS_ANTENNA_INFOS);
+                if (infos != null) {
+                    deliverToListeners(callback -> callback.onGnssAntennaInfoReceived(infos));
+                }
             }
         }
     }
diff --git a/location/java/android/location/LocationProvider.java b/location/java/android/location/LocationProvider.java
index b950604..6d2bfed 100644
--- a/location/java/android/location/LocationProvider.java
+++ b/location/java/android/location/LocationProvider.java
@@ -16,23 +16,15 @@
 
 package android.location;
 
-
-import com.android.internal.location.ProviderProperties;
+import android.annotation.Nullable;
 
 /**
- * An abstract superclass for location providers.  A location provider
- * provides periodic reports on the geographical location of the
- * device.
+ * Information about the properties of a location provider.
  *
- * <p> Each provider has a set of criteria under which it may be used;
- * for example, some providers require GPS hardware and visibility to
- * a number of satellites; others require the use of the cellular
- * radio, or access to a specific carrier's network, or to the
- * internet.  They may also have different battery consumption
- * characteristics or monetary costs to the user.  The {@link
- * Criteria} class allows providers to be selected based on
- * user-specified criteria.
+ * @deprecated This class is incapable of representing unknown provider properties and may return
+ * incorrect results when the properties are unknown.
  */
+@Deprecated
 public class LocationProvider {
 
     /**
@@ -54,9 +46,9 @@
     public static final int AVAILABLE = 2;
 
     private final String mName;
-    private final ProviderProperties mProperties;
+    private final @Nullable ProviderProperties mProperties;
 
-    LocationProvider(String name, ProviderProperties properties) {
+    LocationProvider(String name, @Nullable ProviderProperties properties) {
         mName = name;
         mProperties = properties;
     }
@@ -96,7 +88,7 @@
             return false;
         }
         if (criteria.getPowerRequirement() != Criteria.NO_REQUIREMENT &&
-                criteria.getPowerRequirement() < properties.getPowerRequirement()) {
+                criteria.getPowerRequirement() < properties.getPowerUsage()) {
             return false;
         }
         if (criteria.isAltitudeRequired() && !properties.hasAltitudeSupport()) {
@@ -119,7 +111,11 @@
      * data network (e.g., the Internet), false otherwise.
      */
     public boolean requiresNetwork() {
-        return mProperties.hasNetworkRequirement();
+        if (mProperties == null) {
+            return false;
+        } else {
+            return mProperties.hasNetworkRequirement();
+        }
     }
 
     /**
@@ -128,7 +124,11 @@
      * otherwise.
      */
     public boolean requiresSatellite() {
-        return mProperties.hasSatelliteRequirement();
+        if (mProperties == null) {
+            return false;
+        } else {
+            return mProperties.hasSatelliteRequirement();
+        }
     }
 
     /**
@@ -137,7 +137,11 @@
      * otherwise.
      */
     public boolean requiresCell() {
-        return mProperties.hasCellRequirement();
+        if (mProperties == null) {
+            return false;
+        } else {
+            return mProperties.hasCellRequirement();
+        }
     }
 
     /**
@@ -146,7 +150,11 @@
      * each provider to give accurate information.
      */
     public boolean hasMonetaryCost() {
-        return mProperties.hasMonetaryCost();
+        if (mProperties == null) {
+            return false;
+        } else {
+            return mProperties.hasMonetaryCost();
+        }
     }
 
     /**
@@ -156,7 +164,11 @@
      * should return true.
      */
     public boolean supportsAltitude() {
-        return mProperties.hasAltitudeSupport();
+        if (mProperties == null) {
+            return false;
+        } else {
+            return mProperties.hasAltitudeSupport();
+        }
     }
 
     /**
@@ -166,7 +178,11 @@
      * should return true.
      */
     public boolean supportsSpeed() {
-        return mProperties.hasSpeedSupport();
+        if (mProperties == null) {
+            return false;
+        } else {
+            return mProperties.hasSpeedSupport();
+        }
     }
 
     /**
@@ -176,27 +192,39 @@
      * should return true.
      */
     public boolean supportsBearing() {
-        return mProperties.hasBearingSupport();
+        if (mProperties == null) {
+            return false;
+        } else {
+            return mProperties.hasBearingSupport();
+        }
     }
 
     /**
      * Returns the power requirement for this provider.
      *
      * @return the power requirement for this provider, as one of the
-     * constants Criteria.POWER_REQUIREMENT_*.
+     * constants ProviderProperties.POWER_USAGE_*.
      */
     public int getPowerRequirement() {
-        return mProperties.getPowerRequirement();
+        if (mProperties == null) {
+            return ProviderProperties.POWER_USAGE_HIGH;
+        } else {
+            return mProperties.getPowerUsage();
+        }
     }
 
     /**
      * Returns a constant describing horizontal accuracy of this provider.
      * If the provider returns finer grain or exact location,
-     * {@link Criteria#ACCURACY_FINE} is returned, otherwise if the
-     * location is only approximate then {@link Criteria#ACCURACY_COARSE}
+     * {@link ProviderProperties#ACCURACY_FINE} is returned, otherwise if the
+     * location is only approximate then {@link ProviderProperties#ACCURACY_COARSE}
      * is returned.
      */
     public int getAccuracy() {
-        return mProperties.getAccuracy();
+        if (mProperties == null) {
+            return ProviderProperties.ACCURACY_COARSE;
+        } else {
+            return mProperties.getAccuracy();
+        }
     }
 }
diff --git a/location/java/com/android/internal/location/ProviderProperties.aidl b/location/java/android/location/ProviderProperties.aidl
similarity index 94%
rename from location/java/com/android/internal/location/ProviderProperties.aidl
rename to location/java/android/location/ProviderProperties.aidl
index b901444..8b1d79f 100644
--- a/location/java/com/android/internal/location/ProviderProperties.aidl
+++ b/location/java/android/location/ProviderProperties.aidl
@@ -14,6 +14,6 @@
  * limitations under the License.
  */
 
-package com.android.internal.location;
+package android.location;
 
 parcelable ProviderProperties;
diff --git a/location/java/com/android/internal/location/ProviderProperties.java b/location/java/android/location/ProviderProperties.java
similarity index 75%
rename from location/java/com/android/internal/location/ProviderProperties.java
rename to location/java/android/location/ProviderProperties.java
index dbb61d2..8fa8c98 100644
--- a/location/java/com/android/internal/location/ProviderProperties.java
+++ b/location/java/android/location/ProviderProperties.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012 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.
@@ -14,10 +14,10 @@
  * limitations under the License.
  */
 
-package com.android.internal.location;
+package android.location;
 
 import android.annotation.IntDef;
-import android.location.Criteria;
+import android.annotation.NonNull;
 import android.os.Parcel;
 import android.os.Parcelable;
 
@@ -29,18 +29,43 @@
 
 /**
  * Location provider properties.
- * @hide
  */
 public final class ProviderProperties implements Parcelable {
 
-    /** @hide */
-    @Retention(RetentionPolicy.SOURCE)
-    @IntDef({Criteria.POWER_LOW, Criteria.POWER_MEDIUM, Criteria.POWER_HIGH})
-    public @interface PowerRequirement {}
+    /**
+     * A constant indicating low power usage.
+     */
+    public static final int POWER_USAGE_LOW = 1;
+
+    /**
+     * A constant indicating a medium power usage.
+     */
+    public static final int POWER_USAGE_MEDIUM = 2;
+
+    /**
+     * A constant indicating high power usage.
+     */
+    public static final int POWER_USAGE_HIGH = 3;
 
     /** @hide */
     @Retention(RetentionPolicy.SOURCE)
-    @IntDef({Criteria.ACCURACY_FINE, Criteria.ACCURACY_COARSE})
+    @IntDef(prefix = "POWER_USAGE_", value = {POWER_USAGE_LOW, POWER_USAGE_MEDIUM,
+            POWER_USAGE_HIGH})
+    public @interface PowerUsage {}
+
+    /**
+     * A constant indicating a finer location accuracy.
+     */
+    public static final int ACCURACY_FINE = 1;
+
+    /**
+     * A constant indicating a coarser location accuracy.
+     */
+    public static final int ACCURACY_COARSE = 2;
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(prefix = "ACCURACY_", value = {ACCURACY_FINE, ACCURACY_COARSE})
     public @interface Accuracy {}
 
     private final boolean mHasNetworkRequirement;
@@ -50,13 +75,16 @@
     private final boolean mHasAltitudeSupport;
     private final boolean mHasSpeedSupport;
     private final boolean mHasBearingSupport;
-    private final @PowerRequirement int mPowerRequirement;
+    private final @PowerUsage int mPowerUsage;
     private final @Accuracy int mAccuracy;
 
+    /**
+     * @hide
+     */
     public ProviderProperties(boolean hasNetworkRequirement, boolean hasSatelliteRequirement,
             boolean hasCellRequirement, boolean hasMonetaryCost, boolean hasAltitudeSupport,
             boolean hasSpeedSupport, boolean hasBearingSupport,
-            @PowerRequirement int powerRequirement, @Accuracy int accuracy) {
+            @PowerUsage int powerUsage, @Accuracy int accuracy) {
         mHasNetworkRequirement = hasNetworkRequirement;
         mHasSatelliteRequirement = hasSatelliteRequirement;
         mHasCellRequirement = hasCellRequirement;
@@ -64,10 +92,10 @@
         mHasAltitudeSupport = hasAltitudeSupport;
         mHasSpeedSupport = hasSpeedSupport;
         mHasBearingSupport = hasBearingSupport;
-        mPowerRequirement = Preconditions.checkArgumentInRange(powerRequirement, Criteria.POWER_LOW,
-                Criteria.POWER_HIGH, "powerRequirement");
-        mAccuracy = Preconditions.checkArgumentInRange(accuracy, Criteria.ACCURACY_FINE,
-                Criteria.ACCURACY_COARSE, "accuracy");
+        mPowerUsage = Preconditions.checkArgumentInRange(powerUsage, POWER_USAGE_LOW,
+                POWER_USAGE_HIGH, "powerUsage");
+        mAccuracy = Preconditions.checkArgumentInRange(accuracy, ACCURACY_FINE,
+                ACCURACY_COARSE, "locationAccuracy");
     }
 
     /**
@@ -121,22 +149,22 @@
     }
 
     /**
-     * Power requirement for this provider.
+     * Power usage for this provider.
      */
-    public @PowerRequirement int getPowerRequirement() {
-        return mPowerRequirement;
+    public @PowerUsage int getPowerUsage() {
+        return mPowerUsage;
     }
 
     /**
-     * Constant describing the horizontal accuracy returned
-     * by this provider.
+     * Rough location accuracy for this provider, primarily with respect to horizontal location
+     * accuracy.
      */
     public @Accuracy int getAccuracy() {
         return mAccuracy;
     }
 
-    public static final Parcelable.Creator<ProviderProperties> CREATOR =
-            new Parcelable.Creator<ProviderProperties>() {
+    public static final @NonNull Creator<ProviderProperties> CREATOR =
+            new Creator<ProviderProperties>() {
                 @Override
                 public ProviderProperties createFromParcel(Parcel in) {
                     return new ProviderProperties(
@@ -147,7 +175,7 @@
                             /* hasAltitudeSupport= */ in.readBoolean(),
                             /* hasSpeedSupport= */ in.readBoolean(),
                             /* hasBearingSupport= */ in.readBoolean(),
-                            /* powerRequirement= */ in.readInt(),
+                            /* powerUsage= */ in.readInt(),
                             /* accuracy= */ in.readInt());
                 }
 
@@ -163,7 +191,7 @@
     }
 
     @Override
-    public void writeToParcel(Parcel parcel, int flags) {
+    public void writeToParcel(@NonNull Parcel parcel, int flags) {
         parcel.writeBoolean(mHasNetworkRequirement);
         parcel.writeBoolean(mHasSatelliteRequirement);
         parcel.writeBoolean(mHasCellRequirement);
@@ -171,7 +199,7 @@
         parcel.writeBoolean(mHasAltitudeSupport);
         parcel.writeBoolean(mHasSpeedSupport);
         parcel.writeBoolean(mHasBearingSupport);
-        parcel.writeInt(mPowerRequirement);
+        parcel.writeInt(mPowerUsage);
         parcel.writeInt(mAccuracy);
     }
 
@@ -191,7 +219,7 @@
                 && mHasAltitudeSupport == that.mHasAltitudeSupport
                 && mHasSpeedSupport == that.mHasSpeedSupport
                 && mHasBearingSupport == that.mHasBearingSupport
-                && mPowerRequirement == that.mPowerRequirement
+                && mPowerUsage == that.mPowerUsage
                 && mAccuracy == that.mAccuracy;
     }
 
@@ -199,13 +227,13 @@
     public int hashCode() {
         return Objects.hash(mHasNetworkRequirement, mHasSatelliteRequirement, mHasCellRequirement,
                 mHasMonetaryCost, mHasAltitudeSupport, mHasSpeedSupport, mHasBearingSupport,
-                mPowerRequirement, mAccuracy);
+                mPowerUsage, mAccuracy);
     }
 
     @Override
     public String toString() {
         StringBuilder b = new StringBuilder("ProviderProperties[");
-        b.append("power=").append(powerToString(mPowerRequirement)).append(", ");
+        b.append("power=").append(powerToString(mPowerUsage)).append(", ");
         b.append("accuracy=").append(accuracyToString(mAccuracy));
         if (mHasNetworkRequirement || mHasSatelliteRequirement || mHasCellRequirement) {
             b.append(", requires=");
@@ -226,42 +254,42 @@
         if (mHasBearingSupport || mHasSpeedSupport || mHasAltitudeSupport) {
             b.append(", supports=[");
             if (mHasBearingSupport) {
-                b.append("bearing, ");
+                b.append("bearing,");
             }
             if (mHasSpeedSupport) {
-                b.append("speed, ");
+                b.append("speed,");
             }
             if (mHasAltitudeSupport) {
-                b.append("altitude, ");
+                b.append("altitude,");
             }
-            b.setLength(b.length() - 2);
+            b.setLength(b.length() - 1);
             b.append("]");
         }
         b.append("]");
         return b.toString();
     }
 
-    private static String powerToString(@PowerRequirement int power) {
+    private static String powerToString(@PowerUsage int power) {
         switch (power) {
-            case Criteria.POWER_LOW:
+            case POWER_USAGE_LOW:
                 return "Low";
-            case Criteria.POWER_MEDIUM:
+            case POWER_USAGE_MEDIUM:
                 return "Medium";
-            case Criteria.POWER_HIGH:
+            case POWER_USAGE_HIGH:
                 return "High";
             default:
-                return "???";
+                throw new AssertionError();
         }
     }
 
     private static String accuracyToString(@Accuracy int accuracy) {
         switch (accuracy) {
-            case Criteria.ACCURACY_COARSE:
+            case ACCURACY_COARSE:
                 return "Coarse";
-            case Criteria.ACCURACY_FINE:
+            case ACCURACY_FINE:
                 return "Fine";
             default:
-                return "???";
+                throw new AssertionError();
         }
     }
 }
diff --git a/location/java/com/android/internal/location/GpsNetInitiatedHandler.java b/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
index 4a095c9..4ce9a320 100644
--- a/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
+++ b/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
@@ -50,9 +50,6 @@
 
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
 
-    // NI verify activity for bringing up UI (not used yet)
-    public static final String ACTION_NI_VERIFY = "android.intent.action.NETWORK_INITIATED_VERIFY";
-
     // string constants for defining data fields in NI Intent
     public static final String NI_INTENT_KEY_NOTIF_ID = "notif_id";
     public static final String NI_INTENT_KEY_TITLE = "title";
diff --git a/location/java/com/android/internal/location/ILocationProviderManager.aidl b/location/java/com/android/internal/location/ILocationProviderManager.aidl
index a74538b..a5b22b2 100644
--- a/location/java/com/android/internal/location/ILocationProviderManager.aidl
+++ b/location/java/com/android/internal/location/ILocationProviderManager.aidl
@@ -17,17 +17,17 @@
 package com.android.internal.location;
 
 import android.location.LocationResult;
-
-import com.android.internal.location.ProviderProperties;
+import android.location.ProviderProperties;
 
 /**
  * Binder interface for manager of all location providers.
  * @hide
  */
 interface ILocationProviderManager {
-    void onSetIdentity(@nullable String packageName, @nullable String attributionTag);
+    void onInitialize(boolean allowed, in ProviderProperties properties, @nullable String packageName, @nullable String attributionTag);
     void onSetAllowed(boolean allowed);
     void onSetProperties(in ProviderProperties properties);
+
     void onReportLocation(in LocationResult locationResult);
     void onFlushComplete();
 }
diff --git a/location/java/com/android/internal/location/timezone/ILocationTimeZoneProvider.aidl b/location/java/com/android/internal/location/timezone/ILocationTimeZoneProvider.aidl
deleted file mode 100644
index 16aa848..0000000
--- a/location/java/com/android/internal/location/timezone/ILocationTimeZoneProvider.aidl
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * 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.internal.location.timezone;
-
-import com.android.internal.location.timezone.ILocationTimeZoneProviderManager;
-import com.android.internal.location.timezone.LocationTimeZoneProviderRequest;
-
-/**
- * Binder interface for location time zone provider implementations. Do not implement this
- * directly, extend {@link com.android.location.timezone.provider.LocationTimeZoneProviderBase}
- * instead.
- * @hide
- */
-interface ILocationTimeZoneProvider {
-
-    oneway void setLocationTimeZoneProviderManager(in ILocationTimeZoneProviderManager manager);
-
-    oneway void setRequest(in LocationTimeZoneProviderRequest request);
-}
diff --git a/location/java/com/android/internal/location/timezone/LocationTimeZoneEvent.java b/location/java/com/android/internal/location/timezone/LocationTimeZoneEvent.java
deleted file mode 100644
index 31c27d1..0000000
--- a/location/java/com/android/internal/location/timezone/LocationTimeZoneEvent.java
+++ /dev/null
@@ -1,244 +0,0 @@
-/*
- * 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.internal.location.timezone;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import com.android.internal.util.Preconditions;
-
-import java.time.Duration;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Objects;
-
-/**
- * An event containing location time zone information.
- *
- * @hide
- */
-public final class LocationTimeZoneEvent implements Parcelable {
-
-    @IntDef({ EVENT_TYPE_UNKNOWN, EVENT_TYPE_PERMANENT_FAILURE, EVENT_TYPE_SUCCESS,
-            EVENT_TYPE_UNCERTAIN })
-    public @interface EventType {}
-
-    /** Uninitialized value for {@link #mEventType} - must not be used for real events. */
-    private static final int EVENT_TYPE_UNKNOWN = 0;
-
-    /**
-     * Indicates there was a permanent failure. This is not generally expected, and probably means a
-     * required backend service has been turned down, or the client is unreasonably old.
-     */
-    public static final int EVENT_TYPE_PERMANENT_FAILURE = 1;
-
-    /**
-     * Indicates a successful geolocation time zone detection event. {@link #getTimeZoneIds()} will
-     * be non-null but can legitimately be empty, e.g. for disputed areas, oceans.
-     */
-    public static final int EVENT_TYPE_SUCCESS = 2;
-
-    /**
-     * Indicates the time zone is not known because of an expected runtime state or error, e.g. when
-     * the provider is unable to detect location, or there was a problem when resolving the location
-     * to a time zone.
-     */
-    public static final int EVENT_TYPE_UNCERTAIN = 3;
-
-    private static final int EVENT_TYPE_MAX = EVENT_TYPE_UNCERTAIN;
-
-    @EventType
-    private final int mEventType;
-
-    @NonNull
-    private final List<String> mTimeZoneIds;
-
-    private final long mElapsedRealtimeMillis;
-
-    private LocationTimeZoneEvent(@EventType int eventType, @NonNull List<String> timeZoneIds,
-            long elapsedRealtimeMillis) {
-        mEventType = checkValidEventType(eventType);
-        mTimeZoneIds = immutableList(timeZoneIds);
-
-        boolean emptyTimeZoneIdListExpected = eventType != EVENT_TYPE_SUCCESS;
-        Preconditions.checkState(!emptyTimeZoneIdListExpected || timeZoneIds.isEmpty());
-
-        mElapsedRealtimeMillis = elapsedRealtimeMillis;
-    }
-
-    /**
-     * Returns the time of this fix, in elapsed real-time since system boot.
-     *
-     * <p>This value can be reliably compared to {@link
-     * android.os.SystemClock#elapsedRealtime()}, to calculate the age of a fix and to compare
-     * {@link LocationTimeZoneEvent} instances.
-     *
-     * @return elapsed real-time of fix, in milliseconds
-     */
-    public long getElapsedRealtimeMillis() {
-        return mElapsedRealtimeMillis;
-    }
-
-    /**
-     * Returns the event type.
-     */
-    @Nullable
-    public @EventType int getEventType() {
-        return mEventType;
-    }
-
-    /**
-     * Gets the time zone IDs of this event. Contains zero or more IDs for a successful lookup.
-     * The value is undefined for an unsuccessful lookup. See also {@link #getEventType()}.
-     */
-    @NonNull
-    public List<String> getTimeZoneIds() {
-        return mTimeZoneIds;
-    }
-
-    @Override
-    public String toString() {
-        return "LocationTimeZoneEvent{"
-                + "mEventType=" + mEventType
-                + ", mTimeZoneIds=" + mTimeZoneIds
-                + ", mElapsedRealtimeMillis=" + mElapsedRealtimeMillis
-                + "(" + Duration.ofMillis(mElapsedRealtimeMillis) + ")"
-                + '}';
-    }
-
-    public static final @NonNull Parcelable.Creator<LocationTimeZoneEvent> CREATOR =
-            new Parcelable.Creator<LocationTimeZoneEvent>() {
-                @Override
-                public LocationTimeZoneEvent createFromParcel(Parcel in) {
-                    int eventType = in.readInt();
-                    @SuppressWarnings("unchecked")
-                    ArrayList<String> timeZoneIds =
-                            (ArrayList<String>) in.readArrayList(null /* classLoader */);
-                    long elapsedRealtimeMillis = in.readLong();
-                    return new LocationTimeZoneEvent(eventType, timeZoneIds, elapsedRealtimeMillis);
-                }
-
-                @Override
-                public LocationTimeZoneEvent[] newArray(int size) {
-                    return new LocationTimeZoneEvent[size];
-                }
-            };
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel parcel, int flags) {
-        parcel.writeInt(mEventType);
-        parcel.writeList(mTimeZoneIds);
-        parcel.writeLong(mElapsedRealtimeMillis);
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) {
-            return true;
-        }
-        if (o == null || getClass() != o.getClass()) {
-            return false;
-        }
-        LocationTimeZoneEvent that = (LocationTimeZoneEvent) o;
-        return mEventType == that.mEventType
-                && mElapsedRealtimeMillis == that.mElapsedRealtimeMillis
-                && mTimeZoneIds.equals(that.mTimeZoneIds);
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(mEventType, mTimeZoneIds, mElapsedRealtimeMillis);
-    }
-
-    /** @hide */
-    public static final class Builder {
-
-        private @EventType int mEventType = EVENT_TYPE_UNKNOWN;
-        private @NonNull List<String> mTimeZoneIds = Collections.emptyList();
-        private long mElapsedRealtimeMillis;
-
-        public Builder() {
-        }
-
-        /**
-         * Sets the contents of this from the supplied instance.
-         */
-        public Builder(@NonNull LocationTimeZoneEvent ltz) {
-            mEventType = ltz.mEventType;
-            mTimeZoneIds = ltz.mTimeZoneIds;
-            mElapsedRealtimeMillis = ltz.mElapsedRealtimeMillis;
-        }
-
-        /**
-         * Set the time zone ID of this event.
-         */
-        public Builder setEventType(@EventType int eventType) {
-            checkValidEventType(eventType);
-            mEventType = eventType;
-            return this;
-        }
-
-        /**
-         * Sets the time zone IDs of this event.
-         */
-        public Builder setTimeZoneIds(@NonNull List<String> timeZoneIds) {
-            mTimeZoneIds = Objects.requireNonNull(timeZoneIds);
-            return this;
-        }
-
-        /**
-         * Sets the time of this event, in elapsed real-time since system boot.
-         */
-        public Builder setElapsedRealtimeMillis(long time) {
-            mElapsedRealtimeMillis = time;
-            return this;
-        }
-
-        /**
-         * Builds a {@link LocationTimeZoneEvent} instance.
-         */
-        public LocationTimeZoneEvent build() {
-            return new LocationTimeZoneEvent(mEventType, mTimeZoneIds, mElapsedRealtimeMillis);
-        }
-    }
-
-    private static int checkValidEventType(int eventType) {
-        if (eventType <= EVENT_TYPE_UNKNOWN || eventType > EVENT_TYPE_MAX) {
-            throw new IllegalStateException("eventType " + eventType + " unknown");
-        }
-        return eventType;
-    }
-
-    @NonNull
-    private static List<String> immutableList(@NonNull List<String> list) {
-        Objects.requireNonNull(list);
-        if (list.isEmpty()) {
-            return Collections.emptyList();
-        } else {
-            return Collections.unmodifiableList(new ArrayList<>(list));
-        }
-    }
-}
diff --git a/location/java/com/android/internal/location/timezone/LocationTimeZoneProviderRequest.aidl b/location/java/com/android/internal/location/timezone/LocationTimeZoneProviderRequest.aidl
deleted file mode 100644
index bb59457..0000000
--- a/location/java/com/android/internal/location/timezone/LocationTimeZoneProviderRequest.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * 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.internal.location.timezone;
-
-parcelable LocationTimeZoneProviderRequest;
diff --git a/location/java/com/android/internal/location/timezone/LocationTimeZoneProviderRequest.java b/location/java/com/android/internal/location/timezone/LocationTimeZoneProviderRequest.java
deleted file mode 100644
index 5c9d290..0000000
--- a/location/java/com/android/internal/location/timezone/LocationTimeZoneProviderRequest.java
+++ /dev/null
@@ -1,155 +0,0 @@
-/*
- * 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.internal.location.timezone;
-
-import android.annotation.NonNull;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.util.Objects;
-
-/**
- * A request passed to a location time zone provider to configure it.
- *
- * @hide
- */
-public final class LocationTimeZoneProviderRequest implements Parcelable {
-
-    public static final LocationTimeZoneProviderRequest EMPTY_REQUEST =
-            new LocationTimeZoneProviderRequest(
-                    false /* reportLocationTimeZone */,
-                    0 /* initializationTimeoutMillis */);
-
-    public static final Creator<LocationTimeZoneProviderRequest> CREATOR =
-            new Creator<LocationTimeZoneProviderRequest>() {
-                @Override
-                public LocationTimeZoneProviderRequest createFromParcel(Parcel in) {
-                    return LocationTimeZoneProviderRequest.createFromParcel(in);
-                }
-
-                @Override
-                public LocationTimeZoneProviderRequest[] newArray(int size) {
-                    return new LocationTimeZoneProviderRequest[size];
-                }
-            };
-
-    private final boolean mReportLocationTimeZone;
-
-    private final long mInitializationTimeoutMillis;
-
-    private LocationTimeZoneProviderRequest(
-            boolean reportLocationTimeZone, long initializationTimeoutMillis) {
-        mReportLocationTimeZone = reportLocationTimeZone;
-        mInitializationTimeoutMillis = initializationTimeoutMillis;
-    }
-
-    /**
-     * Returns {@code true} if the provider should report events related to the device's current
-     * time zone, {@code false} otherwise.
-     */
-    public boolean getReportLocationTimeZone() {
-        return mReportLocationTimeZone;
-    }
-
-    // TODO(b/152744911) - once there are a couple of implementations, decide whether this needs to
-    //  be passed to the LocationTimeZoneProvider and remove if it is not useful.
-    /**
-     * Returns the maximum time that the provider is allowed to initialize before it is expected to
-     * send an event of any sort. Only valid when {@link #getReportLocationTimeZone()} is {@code
-     * true}. Failure to send an event in this time (with some fuzz) may be interpreted as if the
-     * provider is uncertain of the time zone, and/or it could lead to the provider being disabled.
-     */
-    public long getInitializationTimeoutMillis() {
-        return mInitializationTimeoutMillis;
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel parcel, int flags) {
-        parcel.writeBoolean(mReportLocationTimeZone);
-        parcel.writeLong(mInitializationTimeoutMillis);
-    }
-
-    static LocationTimeZoneProviderRequest createFromParcel(Parcel in) {
-        boolean reportLocationTimeZone = in.readBoolean();
-        long initializationTimeoutMillis = in.readLong();
-        return new LocationTimeZoneProviderRequest(
-                reportLocationTimeZone, initializationTimeoutMillis);
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) {
-            return true;
-        }
-        if (o == null || getClass() != o.getClass()) {
-            return false;
-        }
-        LocationTimeZoneProviderRequest that = (LocationTimeZoneProviderRequest) o;
-        return mReportLocationTimeZone == that.mReportLocationTimeZone
-            && mInitializationTimeoutMillis == that.mInitializationTimeoutMillis;
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(mReportLocationTimeZone, mInitializationTimeoutMillis);
-    }
-
-    @Override
-    public String toString() {
-        return "LocationTimeZoneProviderRequest{"
-                + "mReportLocationTimeZone=" + mReportLocationTimeZone
-                + ", mInitializationTimeoutMillis=" + mInitializationTimeoutMillis
-                + "}";
-    }
-
-    /** @hide */
-    public static final class Builder {
-
-        private boolean mReportLocationTimeZone;
-        private long mInitializationTimeoutMillis;
-
-        /**
-         * Sets the property that enables / disables the provider. This is set to {@code false} by
-         * default.
-         */
-        public Builder setReportLocationTimeZone(boolean reportLocationTimeZone) {
-            mReportLocationTimeZone = reportLocationTimeZone;
-            return this;
-        }
-
-        /**
-         * Sets the initialization timeout. See {@link
-         * LocationTimeZoneProviderRequest#getInitializationTimeoutMillis()} for details.
-         */
-        public Builder setInitializationTimeoutMillis(long timeoutMillis) {
-            mInitializationTimeoutMillis = timeoutMillis;
-            return this;
-        }
-
-        /** Builds the {@link LocationTimeZoneProviderRequest} instance. */
-        @NonNull
-        public LocationTimeZoneProviderRequest build() {
-            return new LocationTimeZoneProviderRequest(
-                    mReportLocationTimeZone, mInitializationTimeoutMillis);
-        }
-    }
-}
diff --git a/location/lib/api/current.txt b/location/lib/api/current.txt
index d4f7558..4e13487 100644
--- a/location/lib/api/current.txt
+++ b/location/lib/api/current.txt
@@ -67,35 +67,3 @@
 
 }
 
-package com.android.location.timezone.provider {
-
-  public final class LocationTimeZoneEventUnbundled {
-    method public int getEventType();
-    method @NonNull public java.util.List<java.lang.String> getTimeZoneIds();
-    field public static final int EVENT_TYPE_PERMANENT_FAILURE = 1; // 0x1
-    field public static final int EVENT_TYPE_SUCCESS = 2; // 0x2
-    field public static final int EVENT_TYPE_UNCERTAIN = 3; // 0x3
-  }
-
-  public static final class LocationTimeZoneEventUnbundled.Builder {
-    ctor public LocationTimeZoneEventUnbundled.Builder();
-    method @NonNull public com.android.location.timezone.provider.LocationTimeZoneEventUnbundled build();
-    method @NonNull public com.android.location.timezone.provider.LocationTimeZoneEventUnbundled.Builder setEventType(int);
-    method @NonNull public com.android.location.timezone.provider.LocationTimeZoneEventUnbundled.Builder setTimeZoneIds(@NonNull java.util.List<java.lang.String>);
-  }
-
-  public abstract class LocationTimeZoneProviderBase {
-    ctor public LocationTimeZoneProviderBase(android.content.Context, String);
-    method public final android.os.IBinder getBinder();
-    method protected final android.content.Context getContext();
-    method protected abstract void onSetRequest(@NonNull com.android.location.timezone.provider.LocationTimeZoneProviderRequestUnbundled);
-    method protected final void reportLocationTimeZoneEvent(@NonNull com.android.location.timezone.provider.LocationTimeZoneEventUnbundled);
-  }
-
-  public final class LocationTimeZoneProviderRequestUnbundled {
-    method @IntRange(from=0) public long getInitializationTimeoutMillis();
-    method public boolean getReportLocationTimeZone();
-  }
-
-}
-
diff --git a/location/lib/java/com/android/location/provider/LocationProviderBase.java b/location/lib/java/com/android/location/provider/LocationProviderBase.java
index 54d8066..47e4256 100644
--- a/location/lib/java/com/android/location/provider/LocationProviderBase.java
+++ b/location/lib/java/com/android/location/provider/LocationProviderBase.java
@@ -23,6 +23,7 @@
 import android.location.LocationManager;
 import android.location.LocationProvider;
 import android.location.LocationResult;
+import android.location.ProviderProperties;
 import android.os.Build.VERSION_CODES;
 import android.os.Bundle;
 import android.os.IBinder;
@@ -35,7 +36,6 @@
 
 import com.android.internal.location.ILocationProvider;
 import com.android.internal.location.ILocationProviderManager;
-import com.android.internal.location.ProviderProperties;
 import com.android.internal.location.ProviderRequest;
 
 import java.io.FileDescriptor;
@@ -372,11 +372,7 @@
         public void setLocationProviderManager(ILocationProviderManager manager) {
             synchronized (mBinder) {
                 try {
-                    if (mPackageName != null || mAttributionTag != null) {
-                        manager.onSetIdentity(mPackageName, mAttributionTag);
-                    }
-                    manager.onSetProperties(mProperties);
-                    manager.onSetAllowed(mAllowed);
+                    manager.onInitialize(mAllowed, mProperties, mPackageName, mAttributionTag);
                 } catch (RemoteException e) {
                     throw e.rethrowFromSystemServer();
                 } catch (RuntimeException e) {
diff --git a/location/lib/java/com/android/location/provider/ProviderPropertiesUnbundled.java b/location/lib/java/com/android/location/provider/ProviderPropertiesUnbundled.java
index 21ee5f4..9d8ccdf 100644
--- a/location/lib/java/com/android/location/provider/ProviderPropertiesUnbundled.java
+++ b/location/lib/java/com/android/location/provider/ProviderPropertiesUnbundled.java
@@ -17,8 +17,7 @@
 package com.android.location.provider;
 
 import android.annotation.NonNull;
-
-import com.android.internal.location.ProviderProperties;
+import android.location.ProviderProperties;
 
 import java.util.Objects;
 
diff --git a/location/lib/java/com/android/location/timezone/provider/LocationTimeZoneEventUnbundled.java b/location/lib/java/com/android/location/timezone/provider/LocationTimeZoneEventUnbundled.java
deleted file mode 100644
index 55f5545..0000000
--- a/location/lib/java/com/android/location/timezone/provider/LocationTimeZoneEventUnbundled.java
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * 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.location.timezone.provider;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.os.SystemClock;
-
-import com.android.internal.location.timezone.LocationTimeZoneEvent;
-
-import java.util.Collections;
-import java.util.List;
-import java.util.Objects;
-
-/**
- * An event from a {@link LocationTimeZoneProviderBase} sent while determining a device's time zone
- * using its location.
- */
-public final class LocationTimeZoneEventUnbundled {
-
-    @IntDef({ EVENT_TYPE_PERMANENT_FAILURE, EVENT_TYPE_SUCCESS, EVENT_TYPE_UNCERTAIN })
-    @interface EventType {}
-
-    /**
-     * Indicates there was a permanent failure. This is not generally expected, and probably means a
-     * required backend service has been turned down, or the client is unreasonably old.
-     */
-    public static final int EVENT_TYPE_PERMANENT_FAILURE =
-            LocationTimeZoneEvent.EVENT_TYPE_PERMANENT_FAILURE;
-
-    /**
-     * Indicates a successful geolocation time zone detection event. {@link #getTimeZoneIds()} will
-     * be non-null but can legitimately be empty, e.g. for disputed areas, oceans.
-     */
-    public static final int EVENT_TYPE_SUCCESS = LocationTimeZoneEvent.EVENT_TYPE_SUCCESS;
-
-    /**
-     * Indicates the time zone is not known because of an expected runtime state or error, e.g. when
-     * the provider is unable to detect location, or there was a problem when resolving the location
-     * to a time zone.
-     */
-    public static final int EVENT_TYPE_UNCERTAIN = LocationTimeZoneEvent.EVENT_TYPE_UNCERTAIN;
-
-    @NonNull
-    private final LocationTimeZoneEvent mDelegate;
-
-    private LocationTimeZoneEventUnbundled(@NonNull LocationTimeZoneEvent delegate) {
-        mDelegate = Objects.requireNonNull(delegate);
-    }
-
-    /**
-     * Returns the event type.
-     */
-    public @EventType int getEventType() {
-        return mDelegate.getEventType();
-    }
-
-    /**
-     * Gets the time zone IDs of this event. Contains zero or more IDs for a successful lookup.
-     * The value is undefined for an unsuccessful lookup. See also {@link #getEventType()}.
-     */
-    @NonNull
-    public List<String> getTimeZoneIds() {
-        return mDelegate.getTimeZoneIds();
-    }
-
-    /**
-     * Returns the information from this as a {@link LocationTimeZoneEvent}.
-     * @hide
-     */
-    @NonNull
-    public LocationTimeZoneEvent getInternalLocationTimeZoneEvent() {
-        return mDelegate;
-    }
-
-    @Override
-    public String toString() {
-        return "LocationTimeZoneEventUnbundled{"
-                + "mDelegate=" + mDelegate
-                + '}';
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) {
-            return true;
-        }
-        if (o == null || getClass() != o.getClass()) {
-            return false;
-        }
-        LocationTimeZoneEventUnbundled that = (LocationTimeZoneEventUnbundled) o;
-        return mDelegate.equals(that.mDelegate);
-    }
-
-    @Override
-    public int hashCode() {
-        return mDelegate.hashCode();
-    }
-
-    /**
-     * A builder of {@link LocationTimeZoneEventUnbundled} instances.
-     */
-    public static final class Builder {
-
-        private @EventType int mEventType;
-        private @NonNull List<String> mTimeZoneIds = Collections.emptyList();
-
-        /**
-         * Set the time zone ID of this event.
-         */
-        @NonNull
-        public Builder setEventType(@EventType int eventType) {
-            checkValidEventType(eventType);
-            mEventType = eventType;
-            return this;
-        }
-
-        /**
-         * Sets the time zone IDs of this event.
-         */
-        @NonNull
-        public Builder setTimeZoneIds(@NonNull List<String> timeZoneIds) {
-            mTimeZoneIds = Objects.requireNonNull(timeZoneIds);
-            return this;
-        }
-
-        /**
-         * Builds a {@link LocationTimeZoneEventUnbundled} instance.
-         */
-        @NonNull
-        public LocationTimeZoneEventUnbundled build() {
-            final int internalEventType = this.mEventType;
-            LocationTimeZoneEvent event = new LocationTimeZoneEvent.Builder()
-                    .setEventType(internalEventType)
-                    .setTimeZoneIds(mTimeZoneIds)
-                    .setElapsedRealtimeMillis(SystemClock.elapsedRealtime())
-                    .build();
-            return new LocationTimeZoneEventUnbundled(event);
-        }
-    }
-
-    private static int checkValidEventType(int eventType) {
-        if (eventType != EVENT_TYPE_SUCCESS
-                && eventType != EVENT_TYPE_UNCERTAIN
-                && eventType != EVENT_TYPE_PERMANENT_FAILURE) {
-            throw new IllegalStateException("eventType=" + eventType);
-        }
-        return eventType;
-    }
-}
diff --git a/location/lib/java/com/android/location/timezone/provider/LocationTimeZoneProviderBase.java b/location/lib/java/com/android/location/timezone/provider/LocationTimeZoneProviderBase.java
deleted file mode 100644
index 68ae722..0000000
--- a/location/lib/java/com/android/location/timezone/provider/LocationTimeZoneProviderBase.java
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * 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.location.timezone.provider;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.Context;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.Log;
-
-import com.android.internal.location.timezone.ILocationTimeZoneProvider;
-import com.android.internal.location.timezone.ILocationTimeZoneProviderManager;
-import com.android.internal.location.timezone.LocationTimeZoneProviderRequest;
-
-import java.util.Objects;
-
-/**
- * A base class for location time zone providers implemented as unbundled services.
- *
- * <p>Provider implementations are enabled / disabled via a call to {@link
- * #onSetRequest(LocationTimeZoneProviderRequestUnbundled)}.
- *
- * <p>Once enabled, providers are expected to detect the time zone if possible, and report the
- * result via {@link #reportLocationTimeZoneEvent(LocationTimeZoneEventUnbundled)}  with a type of
- * either {@link LocationTimeZoneEventUnbundled#EVENT_TYPE_UNCERTAIN} or {@link
- * LocationTimeZoneEventUnbundled#EVENT_TYPE_SUCCESS}. Providers may also report that they have
- * permanently failed by sending an event of type {@link
- * LocationTimeZoneEventUnbundled#EVENT_TYPE_PERMANENT_FAILURE}. See the javadocs for each event
- * type for details.
- *
- * <p>Providers are expected to issue their first event within {@link
- * LocationTimeZoneProviderRequest#getInitializationTimeoutMillis()}.
- *
- * <p>Once disabled or have failed, providers are required to stop producing events.
- *
- * <p>Threading:
- *
- * <p>Calls to {@link #reportLocationTimeZoneEvent(LocationTimeZoneEventUnbundled)} can be made on
- * on any thread, but may be processed asynchronously by the system server. Similarly, calls to
- * {@link #onSetRequest(LocationTimeZoneProviderRequestUnbundled)} may occur on any thread.
- *
- * <p>IMPORTANT: This class is effectively a public API for unbundled applications, and must remain
- * API stable.
- */
-public abstract class LocationTimeZoneProviderBase {
-
-    private final Context mContext;
-    private final String mTag;
-    private final IBinder mBinder;
-
-    // write locked on mBinder, read lock is optional depending on atomicity requirements
-    @Nullable private volatile ILocationTimeZoneProviderManager mManager;
-
-    public LocationTimeZoneProviderBase(Context context, String tag) {
-        mContext = context;
-        mTag = tag;
-        mBinder = new Service();
-        mManager = null;
-    }
-
-    protected final Context getContext() {
-        return mContext;
-    }
-
-    public final IBinder getBinder() {
-        return mBinder;
-    }
-
-    /**
-     * Reports a new location time zone event from this provider.
-     */
-    protected final void reportLocationTimeZoneEvent(
-            @NonNull LocationTimeZoneEventUnbundled event) {
-        ILocationTimeZoneProviderManager manager = mManager;
-        if (manager != null) {
-            try {
-                manager.onLocationTimeZoneEvent(event.getInternalLocationTimeZoneEvent());
-            } catch (RemoteException | RuntimeException e) {
-                Log.w(mTag, e);
-            }
-        }
-    }
-
-    /**
-     * Set the {@link LocationTimeZoneProviderRequestUnbundled} requirements for this provider. Each
-     * call to this method overrides all previous requests. This method might trigger the provider
-     * to start returning location time zones, or to stop returning location time zones, depending
-     * on the parameters in the request.
-     */
-    protected abstract void onSetRequest(@NonNull LocationTimeZoneProviderRequestUnbundled request);
-
-    private final class Service extends ILocationTimeZoneProvider.Stub {
-
-        @Override
-        public void setLocationTimeZoneProviderManager(ILocationTimeZoneProviderManager manager) {
-            mManager = Objects.requireNonNull(manager);
-        }
-
-        @Override
-        public void setRequest(LocationTimeZoneProviderRequest request) {
-            onSetRequest(new LocationTimeZoneProviderRequestUnbundled(request));
-        }
-    }
-}
diff --git a/location/lib/java/com/android/location/timezone/provider/LocationTimeZoneProviderRequestUnbundled.java b/location/lib/java/com/android/location/timezone/provider/LocationTimeZoneProviderRequestUnbundled.java
deleted file mode 100644
index 10d1038..0000000
--- a/location/lib/java/com/android/location/timezone/provider/LocationTimeZoneProviderRequestUnbundled.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * 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.location.timezone.provider;
-
-import android.annotation.IntRange;
-import android.annotation.NonNull;
-
-import com.android.internal.location.timezone.LocationTimeZoneProviderRequest;
-
-import java.util.Objects;
-
-/**
- * This class is an interface to LocationTimeZoneProviderRequest for provider implementations.
- *
- * <p>IMPORTANT: This class is effectively a public API for unbundled code, and must remain API
- * stable.
- */
-public final class LocationTimeZoneProviderRequestUnbundled {
-
-    private final LocationTimeZoneProviderRequest mRequest;
-
-    /** @hide */
-    public LocationTimeZoneProviderRequestUnbundled(
-            @NonNull LocationTimeZoneProviderRequest request) {
-        mRequest = Objects.requireNonNull(request);
-    }
-
-    /**
-     * Returns {@code true} if the provider should report events related to the device's current
-     * time zone, {@code false} otherwise.
-     */
-    public boolean getReportLocationTimeZone() {
-        return mRequest.getReportLocationTimeZone();
-    }
-
-    /**
-     * Returns the maximum time that the provider is allowed to initialize before it is expected to
-     * send an event of any sort. Only valid when {@link #getReportLocationTimeZone()} is {@code
-     * true}. Failure to send an event in this time (with some fuzz) may be interpreted as if the
-     * provider is uncertain of the time zone, and/or it could lead to the provider being disabled.
-     */
-    @IntRange(from = 0)
-    public long getInitializationTimeoutMillis() {
-        return mRequest.getInitializationTimeoutMillis();
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) {
-            return true;
-        }
-        if (o == null || getClass() != o.getClass()) {
-            return false;
-        }
-        LocationTimeZoneProviderRequestUnbundled that =
-                (LocationTimeZoneProviderRequestUnbundled) o;
-        return mRequest.equals(that.mRequest);
-    }
-
-    @Override
-    public int hashCode() {
-        return Objects.hash(mRequest);
-    }
-
-    @Override
-    public String toString() {
-        return mRequest.toString();
-    }
-}
diff --git a/media/java/android/media/AudioPlaybackConfiguration.java b/media/java/android/media/AudioPlaybackConfiguration.java
index 17305a5..812dac9 100644
--- a/media/java/android/media/AudioPlaybackConfiguration.java
+++ b/media/java/android/media/AudioPlaybackConfiguration.java
@@ -21,6 +21,7 @@
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.annotation.SystemApi;
 import android.os.Binder;
 import android.os.IBinder;
@@ -47,6 +48,8 @@
     public static final int PLAYER_PIID_INVALID = -1;
     /** @hide */
     public static final int PLAYER_UPID_INVALID = -1;
+    /** @hide */
+    public static final int PLAYER_DEVICEID_INVALID = 0;
 
     // information about the implementation
     /**
@@ -158,6 +161,11 @@
      */
     @SystemApi
     public static final int PLAYER_STATE_STOPPED = 4;
+    /**
+     * @hide
+     * The state used to update device id, does not actually change the state of the player
+     */
+    public static final int PLAYER_UPDATE_DEVICE_ID = 5;
 
     /** @hide */
     @IntDef({
@@ -166,7 +174,8 @@
         PLAYER_STATE_IDLE,
         PLAYER_STATE_STARTED,
         PLAYER_STATE_PAUSED,
-        PLAYER_STATE_STOPPED
+        PLAYER_STATE_STOPPED,
+        PLAYER_UPDATE_DEVICE_ID
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface PlayerState {}
@@ -184,6 +193,8 @@
     private int mPlayerState;
     private AudioAttributes mPlayerAttr; // never null
 
+    private int mDeviceId;
+
     /**
      * Never use without initializing parameters afterwards
      */
@@ -201,6 +212,7 @@
         mPlayerType = pic.mPlayerType;
         mClientUid = uid;
         mClientPid = pid;
+        mDeviceId = PLAYER_DEVICEID_INVALID;
         mPlayerState = PLAYER_STATE_IDLE;
         mPlayerAttr = pic.mAttributes;
         if ((sPlayerDeathMonitor != null) && (pic.mIPlayer != null)) {
@@ -241,6 +253,7 @@
                         in.mPlayerAttr.getAllowedCapturePolicy() == ALLOW_CAPTURE_BY_ALL
                         ? ALLOW_CAPTURE_BY_ALL : ALLOW_CAPTURE_BY_NONE)
                 .build();
+        anonymCopy.mDeviceId = in.mDeviceId;
         // anonymized data
         anonymCopy.mPlayerType = PLAYER_TYPE_UNKNOWN;
         anonymCopy.mClientUid = PLAYER_UPID_INVALID;
@@ -278,6 +291,17 @@
     }
 
     /**
+     * Returns information about the {@link AudioDeviceInfo} used for this playback.
+     * @return the audio playback device or null if the device is not available at the time of query
+     */
+    public @Nullable AudioDeviceInfo getAudioDeviceInfo() {
+        if (mDeviceId == PLAYER_DEVICEID_INVALID) {
+            return null;
+        }
+        return AudioManager.getDeviceForPortId(mDeviceId, AudioManager.GET_DEVICES_OUTPUTS);
+    }
+
+    /**
      * @hide
      * Return the type of player linked to this configuration.
      * <br>Note that player types not exposed in the system API will be represented as
@@ -359,13 +383,29 @@
      * @hide
      * Handle a player state change
      * @param event
+     * @param deviceId active device id or {@Code PLAYER_DEVICEID_INVALID}
+     * <br>Note device id is valid for {@code PLAYER_UPDATE_DEVICE_ID} or
+     * <br>{@code PLAYER_STATE_STARTED} events, as the device id will be reset to none when
+     * <br>pausing or stopping playback. It will be set to active device when playback starts or
+     * <br>it will be changed when PLAYER_UPDATE_DEVICE_ID is sent. The latter can happen if the
+     * <br>device changes in the middle of playback.
      * @return true if the state changed, false otherwise
      */
-    public boolean handleStateEvent(int event) {
-        final boolean changed;
+    public boolean handleStateEvent(int event, int deviceId) {
+        boolean changed = false;
         synchronized (this) {
-            changed = (mPlayerState != event);
-            mPlayerState = event;
+
+            // Do not update if it is only device id update
+            if (event != PLAYER_UPDATE_DEVICE_ID) {
+                changed = (mPlayerState != event);
+                mPlayerState = event;
+            }
+
+            if (event == PLAYER_STATE_STARTED || event == PLAYER_UPDATE_DEVICE_ID) {
+                changed = changed || (mDeviceId != deviceId);
+                mDeviceId = deviceId;
+            }
+
             if (changed && (event == PLAYER_STATE_RELEASED) && (mIPlayerShell != null)) {
                 mIPlayerShell.release();
                 mIPlayerShell = null;
@@ -436,7 +476,7 @@
 
     @Override
     public int hashCode() {
-        return Objects.hash(mPlayerIId, mPlayerType, mClientUid, mClientPid);
+        return Objects.hash(mPlayerIId, mDeviceId, mPlayerType, mClientUid, mClientPid);
     }
 
     @Override
@@ -447,6 +487,7 @@
     @Override
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeInt(mPlayerIId);
+        dest.writeInt(mDeviceId);
         dest.writeInt(mPlayerType);
         dest.writeInt(mClientUid);
         dest.writeInt(mClientPid);
@@ -461,6 +502,7 @@
 
     private AudioPlaybackConfiguration(Parcel in) {
         mPlayerIId = in.readInt();
+        mDeviceId = in.readInt();
         mPlayerType = in.readInt();
         mClientUid = in.readInt();
         mClientPid = in.readInt();
@@ -478,6 +520,7 @@
         AudioPlaybackConfiguration that = (AudioPlaybackConfiguration) o;
 
         return ((mPlayerIId == that.mPlayerIId)
+                && (mDeviceId == that.mDeviceId)
                 && (mPlayerType == that.mPlayerType)
                 && (mClientUid == that.mClientUid)
                 && (mClientPid == that.mClientPid));
@@ -486,6 +529,7 @@
     @Override
     public String toString() {
         return "AudioPlaybackConfiguration piid:" + mPlayerIId
+                + " deviceId:" + mDeviceId
                 + " type:" + toLogFriendlyPlayerType(mPlayerType)
                 + " u/pid:" + mClientUid + "/" + mClientPid
                 + " state:" + toLogFriendlyPlayerState(mPlayerState)
@@ -571,6 +615,7 @@
             case PLAYER_STATE_STARTED: return "started";
             case PLAYER_STATE_PAUSED: return "paused";
             case PLAYER_STATE_STOPPED: return "stopped";
+            case PLAYER_UPDATE_DEVICE_ID: return "device";
             default:
                 return "unknown player state - FIXME";
         }
diff --git a/media/java/android/media/AudioRecord.java b/media/java/android/media/AudioRecord.java
index ea691a7..c2ce7d3 100644
--- a/media/java/android/media/AudioRecord.java
+++ b/media/java/android/media/AudioRecord.java
@@ -1578,14 +1578,7 @@
         if (deviceId == 0) {
             return null;
         }
-        AudioDeviceInfo[] devices =
-                AudioManager.getDevicesStatic(AudioManager.GET_DEVICES_INPUTS);
-        for (int i = 0; i < devices.length; i++) {
-            if (devices[i].getId() == deviceId) {
-                return devices[i];
-            }
-        }
-        return null;
+        return AudioManager.getDeviceForPortId(deviceId, AudioManager.GET_DEVICES_INPUTS);
     }
 
     /*
diff --git a/media/java/android/media/AudioRecordingConfiguration.java b/media/java/android/media/AudioRecordingConfiguration.java
index 6febabe..7908c96 100644
--- a/media/java/android/media/AudioRecordingConfiguration.java
+++ b/media/java/android/media/AudioRecordingConfiguration.java
@@ -262,14 +262,8 @@
                 final AudioPortConfig[] sources = patch.sources();
                 if ((sources != null) && (sources.length > 0)) {
                     // not supporting multiple sources, so just look at the first source
-                    final int devId = sources[0].port().id();
-                    final AudioDeviceInfo[] devices =
-                            AudioManager.getDevicesStatic(AudioManager.GET_DEVICES_INPUTS);
-                    for (int j = 0; j < devices.length; j++) {
-                        if (devices[j].getId() == devId) {
-                            return devices[j];
-                        }
-                    }
+                    int devId = sources[0].port().id();
+                    return AudioManager.getDeviceForPortId(devId, AudioManager.GET_DEVICES_INPUTS);
                 }
                 // patch handle is unique, there won't be another with the same handle
                 break;
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 81ef064..f1f6cb1 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -741,8 +741,6 @@
         synchronized (AudioSystem.class) {
             cb = sRoutingUpdateCallback;
         }
-        //###
-        Log.i(TAG, "#################### update");
         if (cb == null) {
             Log.e(TAG, "routing update from APM was not captured");
             return;
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index e3b6fba..f1a7546 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -1824,6 +1824,7 @@
 
     @Override
     protected void finalize() {
+        tryToDisableNativeRoutingCallback();
         baseRelease();
         native_finalize();
     }
@@ -2717,9 +2718,14 @@
     }
 
     private void startImpl() {
+        synchronized (mRoutingChangeListeners) {
+            if (!mEnableSelfRoutingMonitor) {
+                mEnableSelfRoutingMonitor = testEnableNativeRoutingCallbacksLocked();
+            }
+        }
         synchronized(mPlayStateLock) {
-            baseStart();
             native_start();
+            baseStart(native_getRoutedDeviceId());
             if (mPlayState == PLAYSTATE_PAUSED_STOPPING) {
                 mPlayState = PLAYSTATE_STOPPING;
             } else {
@@ -2757,6 +2763,7 @@
                 mPlayStateLock.notify();
             }
         }
+        tryToDisableNativeRoutingCallback();
     }
 
     /**
@@ -3486,33 +3493,48 @@
         if (deviceId == 0) {
             return null;
         }
-        AudioDeviceInfo[] devices =
-                AudioManager.getDevicesStatic(AudioManager.GET_DEVICES_OUTPUTS);
-        for (int i = 0; i < devices.length; i++) {
-            if (devices[i].getId() == deviceId) {
-                return devices[i];
+        return AudioManager.getDeviceForPortId(deviceId, AudioManager.GET_DEVICES_OUTPUTS);
+    }
+
+    private void tryToDisableNativeRoutingCallback() {
+        synchronized (mRoutingChangeListeners) {
+            if (mEnableSelfRoutingMonitor) {
+                mEnableSelfRoutingMonitor = false;
+                testDisableNativeRoutingCallbacksLocked();
             }
         }
-        return null;
     }
 
-    /*
-     * Call BEFORE adding a routing callback handler.
+    /**
+     * Call BEFORE adding a routing callback handler and when enabling self routing listener
+     * @return returns true for success, false otherwise.
      */
     @GuardedBy("mRoutingChangeListeners")
-    private void testEnableNativeRoutingCallbacksLocked() {
-        if (mRoutingChangeListeners.size() == 0) {
-            native_enableDeviceCallback();
+    private boolean testEnableNativeRoutingCallbacksLocked() {
+        if (mRoutingChangeListeners.size() == 0 && !mEnableSelfRoutingMonitor) {
+            try {
+                native_enableDeviceCallback();
+                return true;
+            } catch (IllegalStateException e) {
+                // Fail silently as track state could have changed in between start
+                // and enabling routing callback, return false to indicate not enabled
+            }
         }
+        return false;
     }
 
     /*
-     * Call AFTER removing a routing callback handler.
+     * Call AFTER removing a routing callback handler and when disabling self routing listener.
      */
     @GuardedBy("mRoutingChangeListeners")
     private void testDisableNativeRoutingCallbacksLocked() {
-        if (mRoutingChangeListeners.size() == 0) {
-            native_disableDeviceCallback();
+        if (mRoutingChangeListeners.size() == 0 && !mEnableSelfRoutingMonitor) {
+            try {
+                native_disableDeviceCallback();
+            } catch (IllegalStateException e) {
+                // Fail silently as track state could have changed in between stop
+                // and disabling routing callback
+            }
         }
     }
 
@@ -3528,6 +3550,9 @@
     private ArrayMap<AudioRouting.OnRoutingChangedListener,
             NativeRoutingEventHandlerDelegate> mRoutingChangeListeners = new ArrayMap<>();
 
+    @GuardedBy("mRoutingChangeListeners")
+    private boolean mEnableSelfRoutingMonitor;
+
    /**
     * Adds an {@link AudioRouting.OnRoutingChangedListener} to receive notifications of routing
     * changes on this AudioTrack.
@@ -3627,6 +3652,7 @@
      */
     private void broadcastRoutingChange() {
         AudioManager.resetAudioPortGeneration();
+        baseUpdateDeviceId(getRoutedDevice());
         synchronized (mRoutingChangeListeners) {
             for (NativeRoutingEventHandlerDelegate delegate : mRoutingChangeListeners.values()) {
                 delegate.notifyClient();
diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java
index 8ea380a..21f8623 100644
--- a/media/java/android/media/ExifInterface.java
+++ b/media/java/android/media/ExifInterface.java
@@ -5019,13 +5019,18 @@
 
         @Override
         public int skipBytes(int byteCount) throws IOException {
-            int totalSkip = Math.min(byteCount, mLength - mPosition);
-            int skipped = 0;
-            while (skipped < totalSkip) {
-                skipped += mDataInputStream.skipBytes(totalSkip - skipped);
+            int totalBytesToSkip = Math.min(byteCount, mLength - mPosition);
+            int totalSkipped = 0;
+            while (totalSkipped < totalBytesToSkip) {
+                int skipped = mDataInputStream.skipBytes(totalBytesToSkip - totalSkipped);
+                if (skipped > 0) {
+                    totalSkipped += skipped;
+                } else {
+                    break;
+                }
             }
-            mPosition += skipped;
-            return skipped;
+            mPosition += totalSkipped;
+            return totalSkipped;
         }
 
         public int readUnsignedShort() throws IOException {
diff --git a/media/java/android/media/HwAudioSource.java b/media/java/android/media/HwAudioSource.java
index 01a02f1..bbf632a 100644
--- a/media/java/android/media/HwAudioSource.java
+++ b/media/java/android/media/HwAudioSource.java
@@ -22,6 +22,8 @@
 
 import com.android.internal.util.Preconditions;
 
+import java.util.ArrayList;
+
 /**
  * The HwAudioSource represents the audio playback directly from a source audio device.
  * It currently supports {@link HwAudioSource#start()} and {@link HwAudioSource#stop()} only
@@ -130,10 +132,32 @@
      */
     public void start() {
         Preconditions.checkState(!isPlaying(), "HwAudioSource is currently playing");
-        baseStart();
         mNativeHandle = AudioSystem.startAudioSource(
                 mAudioDeviceInfo.getPort().activeConfig(),
                 mAudioAttributes);
+        // FIXME: b/174876389 clean up device id reporting
+        baseStart(getDeviceId());
+    }
+
+    private int getDeviceId() {
+        ArrayList<AudioPatch> patches = new ArrayList<AudioPatch>();
+        if (AudioManager.listAudioPatches(patches) != AudioManager.SUCCESS) {
+            return 0;
+        }
+
+        for (int i = 0; i < patches.size(); i++) {
+            AudioPatch patch = patches.get(i);
+            AudioPortConfig[] sources = patch.sources();
+            AudioPortConfig[] sinks = patch.sinks();
+            if ((sources != null) && (sources.length > 0)) {
+                for (int c = 0;  c < sources.length; c++) {
+                    if (sources[c].port().id() == mAudioDeviceInfo.getId()) {
+                        return sinks[c].port().id();
+                    }
+                }
+            }
+        }
+        return 0;
     }
 
     /**
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index bad0d19..b6bb3a3 100755
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -63,7 +63,7 @@
 
     oneway void playerAttributes(in int piid, in AudioAttributes attr);
 
-    oneway void playerEvent(in int piid, in int event);
+    oneway void playerEvent(in int piid, in int event, in int deviceId);
 
     oneway void releasePlayer(in int piid);
 
diff --git a/media/java/android/media/MediaMetadataRetriever.java b/media/java/android/media/MediaMetadataRetriever.java
index 32a9168..c13f610 100644
--- a/media/java/android/media/MediaMetadataRetriever.java
+++ b/media/java/android/media/MediaMetadataRetriever.java
@@ -1374,17 +1374,13 @@
     /**
      * If the media contains XMP data, this key retrieves the offset (in bytes)
      * of the data.
-     * @hide
      */
-    @SystemApi(client = MODULE_LIBRARIES)
     public static final int METADATA_KEY_XMP_OFFSET = 41;
 
     /**
      * If the media contains XMP data, this key retrieves the length (in bytes)
      * of the data.
-     * @hide
      */
-    @SystemApi(client = MODULE_LIBRARIES)
     public static final int METADATA_KEY_XMP_LENGTH = 42;
 
     // Add more here...
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index f8311cd..dd0bc61 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -1352,7 +1352,6 @@
     }
 
     private void startImpl() {
-        baseStart();
         stayAwake(true);
         _start();
     }
@@ -1378,7 +1377,6 @@
     public void stop() throws IllegalStateException {
         stayAwake(false);
         _stop();
-        baseStop();
     }
 
     private native void _stop() throws IllegalStateException;
@@ -1392,7 +1390,6 @@
     public void pause() throws IllegalStateException {
         stayAwake(false);
         _pause();
-        basePause();
     }
 
     private native void _pause() throws IllegalStateException;
@@ -1487,23 +1484,75 @@
         if (deviceId == 0) {
             return null;
         }
-        AudioDeviceInfo[] devices =
-                AudioManager.getDevicesStatic(AudioManager.GET_DEVICES_OUTPUTS);
-        for (int i = 0; i < devices.length; i++) {
-            if (devices[i].getId() == deviceId) {
-                return devices[i];
+        return AudioManager.getDeviceForPortId(deviceId, AudioManager.GET_DEVICES_OUTPUTS);
+    }
+
+
+    /**
+     * Sends device list change notification to all listeners.
+     */
+    private void broadcastRoutingChange() {
+        AudioManager.resetAudioPortGeneration();
+        synchronized (mRoutingChangeListeners) {
+            // Prevent the case where an event is triggered by registering a routing change
+            // listener via the media player.
+            if (mEnableSelfRoutingMonitor) {
+                baseUpdateDeviceId(getRoutedDevice());
+            }
+            for (NativeRoutingEventHandlerDelegate delegate
+                    : mRoutingChangeListeners.values()) {
+                delegate.notifyClient();
             }
         }
-        return null;
+    }
+
+    /**
+     * Call BEFORE adding a routing callback handler and when enabling self routing listener
+     * @return returns true for success, false otherwise.
+     */
+    @GuardedBy("mRoutingChangeListeners")
+    private boolean testEnableNativeRoutingCallbacksLocked() {
+        if (mRoutingChangeListeners.size() == 0 && !mEnableSelfRoutingMonitor) {
+            try {
+                native_enableDeviceCallback(true);
+                return true;
+            } catch (IllegalStateException e) {
+                // Fail silently as media player state could have changed in between start
+                // and enabling routing callback, return false to indicate not enabled
+            }
+        }
+        return false;
+    }
+
+    private void  tryToEnableNativeRoutingCallback() {
+        synchronized (mRoutingChangeListeners) {
+            if (!mEnableSelfRoutingMonitor) {
+                mEnableSelfRoutingMonitor = testEnableNativeRoutingCallbacksLocked();
+            }
+        }
+    }
+
+    private void tryToDisableNativeRoutingCallback() {
+        synchronized (mRoutingChangeListeners) {
+            if (mEnableSelfRoutingMonitor) {
+                mEnableSelfRoutingMonitor = false;
+                testDisableNativeRoutingCallbacksLocked();
+            }
+        }
     }
 
     /*
-     * Call BEFORE adding a routing callback handler or AFTER removing a routing callback handler.
+     * Call AFTER removing a routing callback handler and when disabling self routing listener
      */
     @GuardedBy("mRoutingChangeListeners")
-    private void enableNativeRoutingCallbacksLocked(boolean enabled) {
-        if (mRoutingChangeListeners.size() == 0) {
-            native_enableDeviceCallback(enabled);
+    private void testDisableNativeRoutingCallbacksLocked() {
+        if (mRoutingChangeListeners.size() == 0 && !mEnableSelfRoutingMonitor) {
+            try {
+                native_enableDeviceCallback(false);
+            } catch (IllegalStateException e) {
+                // Fail silently as media player state could have changed in between stop
+                // and disabling routing callback
+            }
         }
     }
 
@@ -1516,6 +1565,9 @@
     private ArrayMap<AudioRouting.OnRoutingChangedListener,
             NativeRoutingEventHandlerDelegate> mRoutingChangeListeners = new ArrayMap<>();
 
+    @GuardedBy("mRoutingChangeListeners")
+    private boolean mEnableSelfRoutingMonitor;
+
     /**
      * Adds an {@link AudioRouting.OnRoutingChangedListener} to receive notifications of routing
      * changes on this MediaPlayer.
@@ -1529,7 +1581,7 @@
             Handler handler) {
         synchronized (mRoutingChangeListeners) {
             if (listener != null && !mRoutingChangeListeners.containsKey(listener)) {
-                enableNativeRoutingCallbacksLocked(true);
+                testEnableNativeRoutingCallbacksLocked();
                 mRoutingChangeListeners.put(
                         listener, new NativeRoutingEventHandlerDelegate(this, listener,
                                 handler != null ? handler : mEventHandler));
@@ -1548,8 +1600,8 @@
         synchronized (mRoutingChangeListeners) {
             if (mRoutingChangeListeners.containsKey(listener)) {
                 mRoutingChangeListeners.remove(listener);
-                enableNativeRoutingCallbacksLocked(false);
             }
+            testDisableNativeRoutingCallbacksLocked();
         }
     }
 
@@ -3301,6 +3353,7 @@
 
     @Override
     protected void finalize() {
+        tryToDisableNativeRoutingCallback();
         baseRelease();
         native_finalize();
     }
@@ -3415,6 +3468,8 @@
 
             case MEDIA_STOPPED:
                 {
+                    tryToDisableNativeRoutingCallback();
+                    baseStop();
                     TimeProvider timeProvider = mTimeProvider;
                     if (timeProvider != null) {
                         timeProvider.onStopped();
@@ -3423,8 +3478,16 @@
                 break;
 
             case MEDIA_STARTED:
+                {
+                    baseStart(native_getRoutedDeviceId());
+                    tryToEnableNativeRoutingCallback();
+                }
+                // fall through
             case MEDIA_PAUSED:
                 {
+                    if (msg.what == MEDIA_PAUSED) {
+                        basePause();
+                    }
                     TimeProvider timeProvider = mTimeProvider;
                     if (timeProvider != null) {
                         timeProvider.onPaused(msg.what == MEDIA_PAUSED);
@@ -3590,14 +3653,8 @@
                 break;
 
             case MEDIA_AUDIO_ROUTING_CHANGED:
-                AudioManager.resetAudioPortGeneration();
-                synchronized (mRoutingChangeListeners) {
-                    for (NativeRoutingEventHandlerDelegate delegate
-                            : mRoutingChangeListeners.values()) {
-                        delegate.notifyClient();
-                    }
-                }
-                return;
+                    broadcastRoutingChange();
+                    return;
 
             case MEDIA_TIME_DISCONTINUITY:
                 final OnMediaTimeDiscontinuityListener mediaTimeListener;
@@ -3802,6 +3859,7 @@
     private final OnCompletionListener mOnCompletionInternalListener = new OnCompletionListener() {
         @Override
         public void onCompletion(MediaPlayer mp) {
+            tryToDisableNativeRoutingCallback();
             baseStop();
         }
     };
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index c61a2eb..49a4cc6 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -1507,14 +1507,7 @@
         if (deviceId == 0) {
             return null;
         }
-        AudioDeviceInfo[] devices =
-                AudioManager.getDevicesStatic(AudioManager.GET_DEVICES_INPUTS);
-        for (int i = 0; i < devices.length; i++) {
-            if (devices[i].getId() == deviceId) {
-                return devices[i];
-            }
-        }
-        return null;
+        return AudioManager.getDeviceForPortId(deviceId, AudioManager.GET_DEVICES_INPUTS);
     }
 
     /*
diff --git a/media/java/android/media/PlayerBase.java b/media/java/android/media/PlayerBase.java
index da69c6c..58ae279 100644
--- a/media/java/android/media/PlayerBase.java
+++ b/media/java/android/media/PlayerBase.java
@@ -90,6 +90,8 @@
     private float mPanMultiplierR = 1.0f;
     @GuardedBy("mLock")
     private float mVolMultiplier = 1.0f;
+    @GuardedBy("mLock")
+    private int mDeviceId;
 
     /**
      * Constructor. Must be given audio attributes, as they are required for AppOps.
@@ -152,14 +154,35 @@
         }
     }
 
-    private void updateState(int state) {
+    void baseUpdateDeviceId(@Nullable AudioDeviceInfo deviceInfo) {
+        int deviceId = 0;
+        if (deviceInfo != null) {
+            deviceId = deviceInfo.getId();
+        }
+        int piid;
+        synchronized (mLock) {
+            piid = mPlayerIId;
+            mDeviceId = deviceId;
+        }
+        try {
+            getService().playerEvent(piid,
+                    AudioPlaybackConfiguration.PLAYER_UPDATE_DEVICE_ID, deviceId);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error talking to audio service, "
+                    + deviceId
+                    + " device id will not be tracked for piid=" + piid, e);
+        }
+    }
+
+    private void updateState(int state, int deviceId) {
         final int piid;
         synchronized (mLock) {
             mState = state;
             piid = mPlayerIId;
+            mDeviceId = deviceId;
         }
         try {
-            getService().playerEvent(piid, state);
+            getService().playerEvent(piid, state, deviceId);
         } catch (RemoteException e) {
             Log.e(TAG, "Error talking to audio service, "
                     + AudioPlaybackConfiguration.toLogFriendlyPlayerState(state)
@@ -167,9 +190,11 @@
         }
     }
 
-    void baseStart() {
-        if (DEBUG) { Log.v(TAG, "baseStart() piid=" + mPlayerIId); }
-        updateState(AudioPlaybackConfiguration.PLAYER_STATE_STARTED);
+    void baseStart(int deviceId) {
+        if (DEBUG) {
+            Log.v(TAG, "baseStart() piid=" + mPlayerIId + " deviceId=" + deviceId);
+        }
+        updateState(AudioPlaybackConfiguration.PLAYER_STATE_STARTED, deviceId);
         synchronized (mLock) {
             if (isRestricted_sync()) {
                 playerSetVolume(true/*muting*/,0, 0);
@@ -191,12 +216,12 @@
 
     void basePause() {
         if (DEBUG) { Log.v(TAG, "basePause() piid=" + mPlayerIId); }
-        updateState(AudioPlaybackConfiguration.PLAYER_STATE_PAUSED);
+        updateState(AudioPlaybackConfiguration.PLAYER_STATE_PAUSED, 0);
     }
 
     void baseStop() {
         if (DEBUG) { Log.v(TAG, "baseStop() piid=" + mPlayerIId); }
-        updateState(AudioPlaybackConfiguration.PLAYER_STATE_STOPPED);
+        updateState(AudioPlaybackConfiguration.PLAYER_STATE_STOPPED, 0);
     }
 
     void baseSetPan(float pan) {
diff --git a/media/java/android/media/SoundPool.java b/media/java/android/media/SoundPool.java
index 3f685a4..797caf3 100644
--- a/media/java/android/media/SoundPool.java
+++ b/media/java/android/media/SoundPool.java
@@ -303,7 +303,8 @@
      */
     public final int play(int soundID, float leftVolume, float rightVolume,
             int priority, int loop, float rate) {
-        baseStart();
+        // FIXME: b/174876164 implement device id for soundpool
+        baseStart(0);
         return _play(soundID, leftVolume, rightVolume, priority, loop, rate);
     }
 
diff --git a/media/java/android/media/musicrecognition/OWNERS b/media/java/android/media/musicrecognition/OWNERS
new file mode 100644
index 0000000..58f5d40
--- /dev/null
+++ b/media/java/android/media/musicrecognition/OWNERS
@@ -0,0 +1,6 @@
+# Bug component: 830636
+
+joannechung@google.com
+oni@google.com
+volnov@google.com
+
diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java
index f22bcd8..1fd132d 100644
--- a/media/java/android/media/session/MediaSessionManager.java
+++ b/media/java/android/media/session/MediaSessionManager.java
@@ -547,6 +547,7 @@
      * @param keyEvent the key event to send
      * @hide
      */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
     public void dispatchMediaKeyEvent(@NonNull KeyEvent keyEvent) {
         dispatchMediaKeyEventInternal(keyEvent, /*asSystemService=*/false, /*needWakeLock=*/false);
     }
@@ -558,6 +559,7 @@
      * @param needWakeLock true if a wake lock should be held while sending the key
      * @hide
      */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
     public void dispatchMediaKeyEvent(@NonNull KeyEvent keyEvent, boolean needWakeLock) {
         dispatchMediaKeyEventInternal(keyEvent, /*asSystemService=*/false, needWakeLock);
     }
@@ -630,6 +632,7 @@
      * @param musicOnly true if key event should only be sent to music stream
      * @hide
      */
+    @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
     public void dispatchVolumeKeyEvent(@NonNull KeyEvent keyEvent, int streamType,
             boolean musicOnly) {
         dispatchVolumeKeyEventInternal(keyEvent, streamType, musicOnly, /*asSystemService=*/false);
diff --git a/media/java/android/media/tv/TvContract.java b/media/java/android/media/tv/TvContract.java
index 433c622..30a14c8 100644
--- a/media/java/android/media/tv/TvContract.java
+++ b/media/java/android/media/tv/TvContract.java
@@ -2450,6 +2450,71 @@
          */
         public static final String COLUMN_GLOBAL_CONTENT_ID = "global_content_id";
 
+        /**
+         * The remote control key preset number that is assigned to this channel.
+         *
+         * <p> This can be used for one-touch-tuning, tuning to the channel with
+         * pressing the preset button.
+         *
+         * <p> Type: INTEGER (remote control key preset number)
+         */
+        public static final String COLUMN_REMOTE_CONTROL_KEY_PRESET_NUMBER =
+                "remote_control_key_preset_number";
+
+        /**
+         * The flag indicating whether this TV channel is scrambled or not.
+         *
+         * <p>Use the same coding for scrambled in the underlying broadcast standard
+         * if {@code free_ca_mode} in SDT is defined there (e.g. ETSI EN 300 468).
+         *
+         * <p>Type: INTEGER (boolean)
+         */
+        public static final String COLUMN_SCRAMBLED = "scrambled";
+
+        /**
+         * The typical video resolution.
+         *
+         * <p>This is primarily used to filter out channels based on video resolution
+         * by applications. The value is from SDT if defined there. (e.g. ETSI EN 300 468)
+         * The value should match one of the followings: {@link #VIDEO_RESOLUTION_SD},
+         * {@link #VIDEO_RESOLUTION_HD}, {@link #VIDEO_RESOLUTION_UHD}.
+         *
+         * <p>Type: TEXT
+         *
+         */
+        public static final String COLUMN_VIDEO_RESOLUTION = "video_resolution";
+
+        /**
+         * The channel list ID of this TV channel.
+         *
+         * <p>It is used to identify the channel list constructed from broadcast SI based on the
+         * underlying broadcast standard or country/operator profile, if applicable. Otherwise,
+         * leave empty.
+         *
+         * <p>The ID can be defined by individual TV input services. For example, one may assign a
+         * service operator name for the service operator channel list constructed from broadcast
+         * SI or one may assign the {@code profile_name} of the operator_info() APDU defined in CI
+         * Plus 1.3 for the dedicated CICAM operator profile channel list constructed
+         * from CICAM NIT.
+         *
+         * <p>Type: TEXT
+         */
+        public static final String COLUMN_CHANNEL_LIST_ID = "channel_list_id";
+
+        /**
+         * The comma-separated genre string of this TV channel.
+         *
+         * <p>Use the same language appeared in the underlying broadcast standard, if applicable.
+         * Otherwise, leave empty. Use
+         * {@link Genres#encode Genres.encode()} to create a text that can be stored in this column.
+         * Use {@link Genres#decode Genres.decode()} to get the broadcast genre strings from the
+         * text stored in the column.
+         *
+         * <p>Type: TEXT
+         * @see Programs#COLUMN_BROADCAST_GENRE
+         */
+        public static final String COLUMN_BROADCAST_GENRE = Programs.COLUMN_BROADCAST_GENRE;
+
         private Channels() {}
 
         /**
diff --git a/media/java/android/media/tv/tuner/frontend/FrontendInfo.java b/media/java/android/media/tv/tuner/frontend/FrontendInfo.java
index 67d6e9d..e96cae6 100644
--- a/media/java/android/media/tv/tuner/frontend/FrontendInfo.java
+++ b/media/java/android/media/tv/tuner/frontend/FrontendInfo.java
@@ -46,6 +46,10 @@
             FrontendCapabilities frontendCap) {
         mId = id;
         mType = type;
+        // if max Frequency is negative, we set it as max value of the Integer.
+        if (maxFrequency < 0) {
+            maxFrequency = Integer.MAX_VALUE;
+        }
         mFrequencyRange = new Range<>(minFrequency, maxFrequency);
         mSymbolRateRange = new Range<>(minSymbolRate, maxSymbolRate);
         mAcquireRange = acquireRange;
diff --git a/media/java/android/media/tv/tunerresourcemanager/TunerFrontendInfo.java b/media/java/android/media/tv/tunerresourcemanager/TunerFrontendInfo.java
index 8957c37..ef50aac 100644
--- a/media/java/android/media/tv/tunerresourcemanager/TunerFrontendInfo.java
+++ b/media/java/android/media/tv/tunerresourcemanager/TunerFrontendInfo.java
@@ -54,7 +54,7 @@
                 }
             };
 
-    private final int mId;
+    private final int mHandle;
 
     @Type
     private final int mFrontendType;
@@ -66,7 +66,7 @@
     private final int mExclusiveGroupId;
 
     private TunerFrontendInfo(@NonNull Parcel source) {
-        mId = source.readInt();
+        mHandle = source.readInt();
         mFrontendType = source.readInt();
         mExclusiveGroupId = source.readInt();
     }
@@ -74,25 +74,26 @@
     /**
      * Constructs a new {@link TunerFrontendInfo} with the given parameters.
      *
+     * @param handle frontend handle
      * @param frontendType the type of the frontend.
      * @param exclusiveGroupId the group id of the frontend. FE with the same
                                group id can't function at the same time.
      */
-    public TunerFrontendInfo(int id,
+    public TunerFrontendInfo(int handle,
                              @Type int frontendType,
                              int exclusiveGroupId) {
-        mId = id;
+        mHandle = handle;
         mFrontendType = frontendType;
         mExclusiveGroupId = exclusiveGroupId;
     }
 
     /**
-     * Returns the frontend id.
+     * Returns the frontend handle.
      *
-     * @return the value of the frontend id.
+     * @return the value of the frontend handle.
      */
-    public int getId() {
-        return mId;
+    public int getHandle() {
+        return mHandle;
     }
 
     /**
@@ -125,7 +126,7 @@
     @Override
     public String toString() {
         StringBuilder b = new StringBuilder(128);
-        b.append("TunerFrontendInfo {id=").append(mId);
+        b.append("TunerFrontendInfo {handle=").append(mHandle);
         b.append(", frontendType=").append(mFrontendType);
         b.append(", exclusiveGroupId=").append(mExclusiveGroupId);
         b.append("}");
@@ -134,7 +135,7 @@
 
     @Override
     public void writeToParcel(@NonNull Parcel dest, int flags) {
-        dest.writeInt(mId);
+        dest.writeInt(mHandle);
         dest.writeInt(mFrontendType);
         dest.writeInt(mExclusiveGroupId);
     }
diff --git a/media/java/android/mtp/MtpStorageManager.java b/media/java/android/mtp/MtpStorageManager.java
index c0eb5e8..0bede0d 100644
--- a/media/java/android/mtp/MtpStorageManager.java
+++ b/media/java/android/mtp/MtpStorageManager.java
@@ -958,7 +958,7 @@
         MtpObject parent = obj.getParent();
         MtpObject oldObj = parent.getChild(oldName);
         if (!success) {
-            // If the rename failed, we want oldObj to be the original and obj to be the dummy.
+            // If the rename failed, we want oldObj to be the original and obj to be the stand-in.
             // Switch the objects, except for their name and state.
             MtpObject temp = oldObj;
             MtpObjectState oldState = oldObj.getState();
@@ -1034,7 +1034,7 @@
             return generalBeginRemoveObject(obj, MtpOperation.RENAME)
                     && generalBeginCopyObject(newObj, false);
         }
-        // Move obj to new parent, create a dummy object in the old parent.
+        // Move obj to new parent, create a fake object in the old parent.
         MtpObject oldObj = obj.copy(false);
         obj.setParent(newParent);
         oldObj.getParent().addChild(oldObj);
@@ -1063,7 +1063,7 @@
             return generalEndCopyObject(newObj, success, true) && ret;
         }
         if (!success) {
-            // If the rename failed, we want oldObj to be the original and obj to be the dummy.
+            // If the rename failed, we want oldObj to be the original and obj to be the stand-in.
             // Switch the objects, except for their parent and state.
             MtpObject temp = oldObj;
             MtpObjectState oldState = oldObj.getState();
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index 79f6cbf..25b1b40 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -140,6 +140,7 @@
     srcs: [
         "android_media_tv_Tuner.cpp",
         "tuner/DemuxClient.cpp",
+        "tuner/DvrClient.cpp",
         "tuner/FilterClient.cpp",
         "tuner/FrontendClient.cpp",
         "tuner/TunerClient.cpp",
diff --git a/media/jni/android_media_tv_Tuner.cpp b/media/jni/android_media_tv_Tuner.cpp
index 6fa94f1..602364e 100644
--- a/media/jni/android_media_tv_Tuner.cpp
+++ b/media/jni/android_media_tv_Tuner.cpp
@@ -261,70 +261,38 @@
     return mLnbSp;
 }
 
-/////////////// DvrCallback ///////////////////////
-Return<void> DvrCallback::onRecordStatus(RecordStatus status) {
-    ALOGD("DvrCallback::onRecordStatus");
+/////////////// DvrClientCallbackImpl ///////////////////////
+void DvrClientCallbackImpl::onRecordStatus(RecordStatus status) {
+    ALOGD("DvrClientCallbackImpl::onRecordStatus");
     JNIEnv *env = AndroidRuntime::getJNIEnv();
     env->CallVoidMethod(
-            mDvr,
+            mDvrObj,
             gFields.onDvrRecordStatusID,
             (jint) status);
-    return Void();
 }
 
-Return<void> DvrCallback::onPlaybackStatus(PlaybackStatus status) {
-    ALOGD("DvrCallback::onPlaybackStatus");
+void DvrClientCallbackImpl::onPlaybackStatus(PlaybackStatus status) {
+    ALOGD("DvrClientCallbackImpl::onPlaybackStatus");
     JNIEnv *env = AndroidRuntime::getJNIEnv();
     env->CallVoidMethod(
-            mDvr,
+            mDvrObj,
             gFields.onDvrPlaybackStatusID,
             (jint) status);
-    return Void();
 }
 
-void DvrCallback::setDvr(const jobject dvr) {
-    ALOGD("DvrCallback::setDvr");
-    JNIEnv *env = AndroidRuntime::getJNIEnv();
-    mDvr = env->NewWeakGlobalRef(dvr);
+void DvrClientCallbackImpl::setDvr(jweak dvrObj) {
+    ALOGD("DvrClientCallbackImpl::setDvr");
+    mDvrObj = dvrObj;
 }
 
-DvrCallback::~DvrCallback() {
+DvrClientCallbackImpl::~DvrClientCallbackImpl() {
     JNIEnv *env = AndroidRuntime::getJNIEnv();
-    if (mDvr != NULL) {
-        env->DeleteWeakGlobalRef(mDvr);
-        mDvr = NULL;
+    if (mDvrObj != NULL) {
+        env->DeleteWeakGlobalRef(mDvrObj);
+        mDvrObj = NULL;
     }
 }
 
-/////////////// Dvr ///////////////////////
-
-Dvr::Dvr(sp<IDvr> sp, jobject obj) : mDvrSp(sp), mDvrMQEventFlag(nullptr) {
-    JNIEnv *env = AndroidRuntime::getJNIEnv();
-    mDvrObj = env->NewWeakGlobalRef(obj);
-}
-
-Dvr::~Dvr() {
-    JNIEnv *env = AndroidRuntime::getJNIEnv();
-    env->DeleteWeakGlobalRef(mDvrObj);
-    mDvrObj = NULL;
-}
-
-jint Dvr::close() {
-    Result r = mDvrSp->close();
-    if (r == Result::SUCCESS) {
-        EventFlag::deleteEventFlag(&mDvrMQEventFlag);
-    }
-    return (jint) r;
-}
-
-sp<IDvr> Dvr::getIDvr() {
-    return mDvrSp;
-}
-
-MQ& Dvr::getDvrMQ() {
-    return *mDvrMQ;
-}
-
 /////////////// C2DataIdInfo ///////////////////////
 
 C2DataIdInfo::C2DataIdInfo(uint32_t index, uint64_t value) : C2Param(kParamSize, index) {
@@ -1840,9 +1808,7 @@
 
 jobject JTuner::openFilter(DemuxFilterType type, int bufferSize) {
     if (mDemux == NULL || mDemuxClient == NULL) {
-        if (openDemux() != Result::SUCCESS) {
-            return NULL;
-        }
+        return NULL;
     }
 
     sp<FilterClient> filterClient;
@@ -1906,21 +1872,14 @@
 
 jobject JTuner::openDvr(DvrType type, jlong bufferSize) {
     ALOGD("JTuner::openDvr");
-    if (mDemux == NULL) {
-        if (openDemux() != Result::SUCCESS) {
-            return NULL;
-        }
+    if (mDemuxClient == NULL) {
+        return NULL;
     }
-    sp<IDvr> iDvrSp;
-    sp<DvrCallback> callback = new DvrCallback();
-    Result res;
-    mDemux->openDvr(type, (uint32_t) bufferSize, callback,
-            [&](Result r, const sp<IDvr>& dvr) {
-                res = r;
-                iDvrSp = dvr;
-            });
+    sp<DvrClient> dvrClient;
+    sp<DvrClientCallbackImpl> callback = new DvrClientCallbackImpl();
+    dvrClient = mDemuxClient->openDvr(type, (int) bufferSize, callback);
 
-    if (res != Result::SUCCESS || iDvrSp == NULL) {
+    if (dvrClient == NULL) {
         return NULL;
     }
 
@@ -1932,21 +1891,19 @@
                         env->FindClass("android/media/tv/tuner/dvr/DvrRecorder"),
                         gFields.dvrRecorderInitID,
                         mObject);
-        sp<Dvr> dvrSp = new Dvr(iDvrSp, dvrObj);
-        dvrSp->incStrong(dvrObj);
-        env->SetLongField(dvrObj, gFields.dvrRecorderContext, (jlong)dvrSp.get());
+        dvrClient->incStrong(dvrObj);
+        env->SetLongField(dvrObj, gFields.dvrRecorderContext, (jlong)dvrClient.get());
     } else {
         dvrObj =
                 env->NewObject(
                         env->FindClass("android/media/tv/tuner/dvr/DvrPlayback"),
                         gFields.dvrPlaybackInitID,
                         mObject);
-        sp<Dvr> dvrSp = new Dvr(iDvrSp, dvrObj);
-        dvrSp->incStrong(dvrObj);
-        env->SetLongField(dvrObj, gFields.dvrPlaybackContext, (jlong)dvrSp.get());
+        dvrClient->incStrong(dvrObj);
+        env->SetLongField(dvrObj, gFields.dvrPlaybackContext, (jlong)dvrClient.get());
     }
 
-    callback->setDvr(dvrObj);
+    callback->setDvr(env->NewWeakGlobalRef(dvrObj));
 
     return dvrObj;
 }
@@ -3304,12 +3261,12 @@
     return dvrSettings;
 }
 
-static sp<Dvr> getDvr(JNIEnv *env, jobject dvr) {
+static sp<DvrClient> getDvrClient(JNIEnv *env, jobject dvr) {
     bool isRecorder =
             env->IsInstanceOf(dvr, env->FindClass("android/media/tv/tuner/dvr/DvrRecorder"));
     jfieldID fieldId =
             isRecorder ? gFields.dvrRecorderContext : gFields.dvrPlaybackContext;
-    return (Dvr *)env->GetLongField(dvr, fieldId);
+    return (DvrClient *)env->GetLongField(dvr, fieldId);
 }
 
 static void android_media_tv_Tuner_native_init(JNIEnv *env) {
@@ -3910,33 +3867,6 @@
     return filterClient->configureIpFilterContextId(cid);
 }
 
-static jint copyData(JNIEnv *env, std::unique_ptr<MQ>& mq, EventFlag* flag, jbyteArray buffer,
-        jlong offset, jlong size) {
-    ALOGD("copyData, size=%ld, offset=%ld", (long) size, (long) offset);
-
-    jlong available = mq->availableToRead();
-    ALOGD("copyData, available=%ld", (long) available);
-    size = std::min(size, available);
-
-    jboolean isCopy;
-    jbyte *dst = env->GetByteArrayElements(buffer, &isCopy);
-    ALOGD("copyData, isCopy=%d", isCopy);
-    if (dst == nullptr) {
-        jniThrowRuntimeException(env, "Failed to GetByteArrayElements");
-        return 0;
-    }
-
-    if (mq->read(reinterpret_cast<unsigned char*>(dst) + offset, size)) {
-        env->ReleaseByteArrayElements(buffer, dst, 0);
-        flag->wake(static_cast<uint32_t>(DemuxQueueNotifyBits::DATA_CONSUMED));
-    } else {
-        jniThrowRuntimeException(env, "Failed to read FMQ");
-        env->ReleaseByteArrayElements(buffer, dst, 0);
-        return 0;
-    }
-    return size;
-}
-
 static bool isAvFilterSettings(DemuxFilterSettings filterSettings) {
     return (filterSettings.getDiscriminator() == DemuxFilterSettings::hidl_discriminator::ts
             && filterSettings.ts().filterSettings.getDiscriminator()
@@ -4068,7 +3998,7 @@
     if (filterClient == NULL) {
         jniThrowException(env, "java/lang/IllegalStateException",
                 "Failed to read filter FMQ: filter client not found");
-        return 0;
+        return -1;
     }
 
     jboolean isCopy;
@@ -4076,14 +4006,11 @@
     ALOGD("copyData, isCopy=%d", isCopy);
     if (dst == nullptr) {
         jniThrowRuntimeException(env, "Failed to GetByteArrayElements");
-        return 0;
+        return -1;
     }
     int realReadSize = filterClient->read(reinterpret_cast<uint8_t*>(dst) + offset, size);
     env->ReleaseByteArrayElements(buffer, dst, 0);
-    if (realReadSize < 0) {
-        return (jint) Result::UNKNOWN_ERROR;
-    }
-    return (jint) Result::SUCCESS;
+    return (jint) realReadSize;
 }
 
 static jint android_media_tv_Tuner_close_filter(JNIEnv *env, jobject filter) {
@@ -4283,106 +4210,80 @@
 }
 
 static jint android_media_tv_Tuner_attach_filter(JNIEnv *env, jobject dvr, jobject filter) {
-    sp<Dvr> dvrSp = getDvr(env, dvr);
-    if (dvrSp == NULL) {
-        return (jint) Result::NOT_INITIALIZED;
-    }
     sp<FilterClient> filterClient = getFilterClient(env, filter);
     if (filterClient == NULL) {
         return (jint) Result::INVALID_ARGUMENT;
     }
-    sp<IDvr> iDvrSp = dvrSp->getIDvr();
-    // TODO: use filter client once dvrClient is ready
-    sp<IFilter> iFilterSp = filterClient->getHalFilter();
-    Result result = iDvrSp->attachFilter(iFilterSp);
+    sp<DvrClient> dvrClient = getDvrClient(env, dvr);
+    if (dvrClient == NULL) {
+        return (jint) Result::NOT_INITIALIZED;
+    }
+    Result result = dvrClient->attachFilter(filterClient);
     return (jint) result;
 }
 
 static jint android_media_tv_Tuner_detach_filter(JNIEnv *env, jobject dvr, jobject filter) {
-    sp<Dvr> dvrSp = getDvr(env, dvr);
-    if (dvrSp == NULL) {
-        return (jint) Result::NOT_INITIALIZED;
-    }
     sp<FilterClient> filterClient = getFilterClient(env, filter);
     if (filterClient == NULL) {
         return (jint) Result::INVALID_ARGUMENT;
     }
-    sp<IDvr> iDvrSp = dvrSp->getIDvr();
-    // TODO: use filter client once dvrClient is ready
-    sp<IFilter> iFilterSp = filterClient->getHalFilter();
-    Result result = iDvrSp->detachFilter(iFilterSp);
+    sp<DvrClient> dvrClient = getDvrClient(env, dvr);
+    if (dvrClient == NULL) {
+        return (jint) Result::NOT_INITIALIZED;
+    }
+    Result result = dvrClient->detachFilter(filterClient);
     return (jint) result;
 }
 
 static jint android_media_tv_Tuner_configure_dvr(JNIEnv *env, jobject dvr, jobject settings) {
-    sp<Dvr> dvrSp = getDvr(env, dvr);
-    if (dvrSp == NULL) {
-        ALOGD("Failed to configure dvr: dvr not found");
+    sp<DvrClient> dvrClient = getDvrClient(env, dvr);
+    if (dvrClient == NULL) {
+        ALOGD("Failed to configure dvr: dvr client not found");
         return (int)Result::NOT_INITIALIZED;
     }
-    sp<IDvr> iDvrSp = dvrSp->getIDvr();
     bool isRecorder =
             env->IsInstanceOf(dvr, env->FindClass("android/media/tv/tuner/dvr/DvrRecorder"));
-    Result result = iDvrSp->configure(getDvrSettings(env, settings, isRecorder));
-    if (result != Result::SUCCESS) {
-        return (jint) result;
-    }
-    MQDescriptorSync<uint8_t> dvrMQDesc;
-    Result getQueueDescResult = Result::UNKNOWN_ERROR;
-    iDvrSp->getQueueDesc(
-            [&](Result r, const MQDescriptorSync<uint8_t>& desc) {
-                dvrMQDesc = desc;
-                getQueueDescResult = r;
-                ALOGD("getDvrQueueDesc");
-            });
-    if (getQueueDescResult == Result::SUCCESS) {
-        dvrSp->mDvrMQ = std::make_unique<MQ>(dvrMQDesc, true);
-        EventFlag::createEventFlag(
-                dvrSp->mDvrMQ->getEventFlagWord(), &(dvrSp->mDvrMQEventFlag));
-    }
-    return (jint) getQueueDescResult;
+    Result result = dvrClient->configure(getDvrSettings(env, settings, isRecorder));
+    return (jint) result;
 }
 
 static jint android_media_tv_Tuner_start_dvr(JNIEnv *env, jobject dvr) {
-    sp<Dvr> dvrSp = getDvr(env, dvr);
-    if (dvrSp == NULL) {
-        ALOGD("Failed to start dvr: dvr not found");
+    sp<DvrClient> dvrClient = getDvrClient(env, dvr);
+    if (dvrClient == NULL) {
+        ALOGD("Failed to start dvr: dvr client not found");
         return (jint) Result::NOT_INITIALIZED;
     }
-    sp<IDvr> iDvrSp = dvrSp->getIDvr();
-    Result result = iDvrSp->start();
+    Result result = dvrClient->start();
     return (jint) result;
 }
 
 static jint android_media_tv_Tuner_stop_dvr(JNIEnv *env, jobject dvr) {
-    sp<Dvr> dvrSp = getDvr(env, dvr);
-    if (dvrSp == NULL) {
-        ALOGD("Failed to stop dvr: dvr not found");
+    sp<DvrClient> dvrClient = getDvrClient(env, dvr);
+    if (dvrClient == NULL) {
+        ALOGD("Failed to stop dvr: dvr client not found");
         return (jint) Result::NOT_INITIALIZED;
     }
-    sp<IDvr> iDvrSp = dvrSp->getIDvr();
-    Result result = iDvrSp->stop();
+    Result result = dvrClient->stop();
     return (jint) result;
 }
 
 static jint android_media_tv_Tuner_flush_dvr(JNIEnv *env, jobject dvr) {
-    sp<Dvr> dvrSp = getDvr(env, dvr);
-    if (dvrSp == NULL) {
-        ALOGD("Failed to flush dvr: dvr not found");
+    sp<DvrClient> dvrClient = getDvrClient(env, dvr);
+    if (dvrClient == NULL) {
+        ALOGD("Failed to flush dvr: dvr client not found");
         return (jint) Result::NOT_INITIALIZED;
     }
-    sp<IDvr> iDvrSp = dvrSp->getIDvr();
-    Result result = iDvrSp->flush();
+    Result result = dvrClient->flush();
     return (jint) result;
 }
 
 static jint android_media_tv_Tuner_close_dvr(JNIEnv* env, jobject dvr) {
-    sp<Dvr> dvrSp = getDvr(env, dvr);
-    if (dvrSp == NULL) {
-        ALOGD("Failed to close dvr: dvr not found");
+    sp<DvrClient> dvrClient = getDvrClient(env, dvr);
+    if (dvrClient == NULL) {
+        ALOGD("Failed to close dvr: dvr client not found");
         return (jint) Result::NOT_INITIALIZED;
     }
-    return dvrSp->close();
+    return (jint) dvrClient->close();
 }
 
 static sp<Lnb> getLnb(JNIEnv *env, jobject lnb) {
@@ -4427,170 +4328,76 @@
 }
 
 static void android_media_tv_Tuner_dvr_set_fd(JNIEnv *env, jobject dvr, jint fd) {
-    sp<Dvr> dvrSp = getDvr(env, dvr);
-    if (dvrSp == NULL) {
-        ALOGD("Failed to set FD for dvr: dvr not found");
+    sp<DvrClient> dvrClient = getDvrClient(env, dvr);
+    if (dvrClient == NULL) {
+        ALOGD("Failed to set FD for dvr: dvr client not found");
+        return;
     }
-    dvrSp->mFd = (int) fd;
-    ALOGD("set fd = %d", dvrSp->mFd);
+    dvrClient->setFd((int)fd);
+    ALOGD("set fd = %d", fd);
 }
 
 static jlong android_media_tv_Tuner_read_dvr(JNIEnv *env, jobject dvr, jlong size) {
-    sp<Dvr> dvrSp = getDvr(env, dvr);
-    if (dvrSp == NULL) {
+    sp<DvrClient> dvrClient = getDvrClient(env, dvr);
+    if (dvrClient == NULL) {
         jniThrowException(env, "java/lang/IllegalStateException",
-                "Failed to read dvr: dvr not found");
-        return 0;
+                "Failed to read dvr: dvr client not found");
+        return -1;
     }
 
-    long available = dvrSp->mDvrMQ->availableToWrite();
-    long write = std::min((long) size, available);
-
-    MQ::MemTransaction tx;
-    long ret = 0;
-    if (dvrSp->mDvrMQ->beginWrite(write, &tx)) {
-        auto first = tx.getFirstRegion();
-        auto data = first.getAddress();
-        long length = first.getLength();
-        long firstToWrite = std::min(length, write);
-        ret = read(dvrSp->mFd, data, firstToWrite);
-
-        if (ret < 0) {
-            ALOGE("[DVR] Failed to read from FD: %s", strerror(errno));
-            jniThrowRuntimeException(env, strerror(errno));
-            return 0;
-        }
-        if (ret < firstToWrite) {
-            ALOGW("[DVR] file to MQ, first region: %ld bytes to write, but %ld bytes written",
-                    firstToWrite, ret);
-        } else if (firstToWrite < write) {
-            ALOGD("[DVR] write second region: %ld bytes written, %ld bytes in total", ret, write);
-            auto second = tx.getSecondRegion();
-            data = second.getAddress();
-            length = second.getLength();
-            int secondToWrite = std::min(length, write - firstToWrite);
-            ret += read(dvrSp->mFd, data, secondToWrite);
-        }
-        ALOGD("[DVR] file to MQ: %ld bytes need to be written, %ld bytes written", write, ret);
-        if (!dvrSp->mDvrMQ->commitWrite(ret)) {
-            ALOGE("[DVR] Error: failed to commit write!");
-            return 0;
-        }
-
-    } else {
-        ALOGE("dvrMq.beginWrite failed");
-    }
-
-    if (ret > 0) {
-        dvrSp->mDvrMQEventFlag->wake(static_cast<uint32_t>(DemuxQueueNotifyBits::DATA_READY));
-    }
-    return (jlong) ret;
+    return (jlong) dvrClient->readFromFile(size);
 }
 
 static jlong android_media_tv_Tuner_read_dvr_from_array(
         JNIEnv* env, jobject dvr, jbyteArray buffer, jlong offset, jlong size) {
-    sp<Dvr> dvrSp = getDvr(env, dvr);
-    if (dvrSp == NULL) {
-        ALOGW("Failed to read dvr: dvr not found");
-        return 0;
+    sp<DvrClient> dvrClient = getDvrClient(env, dvr);
+    if (dvrClient == NULL) {
+        ALOGW("Failed to read dvr: dvr client not found");
+        return -1;
     }
-    if (dvrSp->mDvrMQ == NULL) {
-        ALOGW("Failed to read dvr: dvr not configured");
-        return 0;
-    }
-
-    jlong available = dvrSp->mDvrMQ->availableToWrite();
-    size = std::min(size, available);
 
     jboolean isCopy;
     jbyte *src = env->GetByteArrayElements(buffer, &isCopy);
     if (src == nullptr) {
         ALOGD("Failed to GetByteArrayElements");
-        return 0;
+        return -1;
     }
+    long realSize = dvrClient->readFromBuffer(reinterpret_cast<unsigned char*>(src) + offset, size);
+    env->ReleaseByteArrayElements(buffer, src, 0);
+    return (jlong) realSize;
 
-    if (dvrSp->mDvrMQ->write(reinterpret_cast<unsigned char*>(src) + offset, size)) {
-        env->ReleaseByteArrayElements(buffer, src, 0);
-        dvrSp->mDvrMQEventFlag->wake(static_cast<uint32_t>(DemuxQueueNotifyBits::DATA_READY));
-    } else {
-        ALOGD("Failed to write FMQ");
-        env->ReleaseByteArrayElements(buffer, src, 0);
-        return 0;
-    }
-    return size;
 }
 
 static jlong android_media_tv_Tuner_write_dvr(JNIEnv *env, jobject dvr, jlong size) {
-    sp<Dvr> dvrSp = getDvr(env, dvr);
-    if (dvrSp == NULL) {
+    sp<DvrClient> dvrClient = getDvrClient(env, dvr);
+    if (dvrClient == NULL) {
         jniThrowException(env, "java/lang/IllegalStateException",
-                "Failed to write dvr: dvr not found");
-        return 0;
+                "Failed to write dvr: dvr client not found");
+        return -1;
     }
 
-    if (dvrSp->mDvrMQ == NULL) {
-        jniThrowException(env, "java/lang/IllegalStateException",
-                "Failed to write dvr: dvr not configured");
-        return 0;
-    }
-
-    MQ& dvrMq = dvrSp->getDvrMQ();
-
-    long available = dvrMq.availableToRead();
-    long toRead = std::min((long) size, available);
-
-    long ret = 0;
-    MQ::MemTransaction tx;
-    if (dvrMq.beginRead(toRead, &tx)) {
-        auto first = tx.getFirstRegion();
-        auto data = first.getAddress();
-        long length = first.getLength();
-        long firstToRead = std::min(length, toRead);
-        ret = write(dvrSp->mFd, data, firstToRead);
-
-        if (ret < 0) {
-            ALOGE("[DVR] Failed to write to FD: %s", strerror(errno));
-            jniThrowRuntimeException(env, strerror(errno));
-            return 0;
-        }
-        if (ret < firstToRead) {
-            ALOGW("[DVR] MQ to file: %ld bytes read, but %ld bytes written", firstToRead, ret);
-        } else if (firstToRead < toRead) {
-            ALOGD("[DVR] read second region: %ld bytes read, %ld bytes in total", ret, toRead);
-            auto second = tx.getSecondRegion();
-            data = second.getAddress();
-            length = second.getLength();
-            int secondToRead = toRead - firstToRead;
-            ret += write(dvrSp->mFd, data, secondToRead);
-        }
-        ALOGD("[DVR] MQ to file: %ld bytes to be read, %ld bytes written", toRead, ret);
-        if (!dvrMq.commitRead(ret)) {
-            ALOGE("[DVR] Error: failed to commit read!");
-            return 0;
-        }
-
-    } else {
-        ALOGE("dvrMq.beginRead failed");
-    }
-    if (ret > 0) {
-        dvrSp->mDvrMQEventFlag->wake(static_cast<uint32_t>(DemuxQueueNotifyBits::DATA_CONSUMED));
-    }
-
-    return (jlong) ret;
+    return (jlong) dvrClient->writeToFile(size);
 }
 
 static jlong android_media_tv_Tuner_write_dvr_to_array(
         JNIEnv *env, jobject dvr, jbyteArray buffer, jlong offset, jlong size) {
-    sp<Dvr> dvrSp = getDvr(env, dvr);
-    if (dvrSp == NULL) {
-        ALOGW("Failed to write dvr: dvr not found");
-        return 0;
+    sp<DvrClient> dvrClient = getDvrClient(env, dvr);
+    if (dvrClient == NULL) {
+        ALOGW("Failed to read dvr: dvr client not found");
+        return -1;
     }
-    if (dvrSp->mDvrMQ == NULL) {
-        ALOGW("Failed to write dvr: dvr not configured");
-        return 0;
+
+    jboolean isCopy;
+    jbyte *dst = env->GetByteArrayElements(buffer, &isCopy);
+    ALOGD("copyData, isCopy=%d", isCopy);
+    if (dst == nullptr) {
+        jniThrowRuntimeException(env, "Failed to GetByteArrayElements");
+        return -1;
     }
-    return copyData(env, dvrSp->mDvrMQ, dvrSp->mDvrMQEventFlag, buffer, offset, size);
+
+    long realSize = dvrClient->writeToBuffer(reinterpret_cast<unsigned char*>(dst) + offset, size);
+    env->ReleaseByteArrayElements(buffer, dst, 0);
+    return (jlong) realSize;
 }
 
 static sp<MediaEvent> getMediaEventSp(JNIEnv *env, jobject mediaEventObj) {
diff --git a/media/jni/android_media_tv_Tuner.h b/media/jni/android_media_tv_Tuner.h
index 4149228..9dc4ddf 100644
--- a/media/jni/android_media_tv_Tuner.h
+++ b/media/jni/android_media_tv_Tuner.h
@@ -105,29 +105,14 @@
     jweak mLnbObj;
 };
 
-struct DvrCallback : public IDvrCallback {
-    ~DvrCallback();
-    virtual Return<void> onRecordStatus(RecordStatus status);
-    virtual Return<void> onPlaybackStatus(PlaybackStatus status);
+struct DvrClientCallbackImpl : public DvrClientCallback {
+    ~DvrClientCallbackImpl();
+    virtual void onRecordStatus(RecordStatus status);
+    virtual void onPlaybackStatus(PlaybackStatus status);
 
-    void setDvr(const jobject dvr);
+    void setDvr(jweak dvrObj);
 private:
-    jweak mDvr;
-};
-
-struct Dvr : public RefBase {
-    Dvr(sp<IDvr> sp, jweak obj);
-    ~Dvr();
-    jint close();
-    MQ& getDvrMQ();
-    sp<IDvr> getIDvr();
-    // TODO: remove after migrate to client lib
-    sp<IDvr> mDvrSp;
     jweak mDvrObj;
-    std::unique_ptr<MQ> mDvrMQ;
-    EventFlag* mDvrMQEventFlag;
-    std::string mFilePath;
-    int mFd;
 };
 
 struct MediaEvent : public RefBase {
diff --git a/media/jni/tuner/DemuxClient.cpp b/media/jni/tuner/DemuxClient.cpp
index b237a24..59dfd70 100644
--- a/media/jni/tuner/DemuxClient.cpp
+++ b/media/jni/tuner/DemuxClient.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "FrontendClient"
+#define LOG_TAG "DemuxClient"
 
 #include <android-base/logging.h>
 #include <utils/Log.h>
@@ -116,7 +116,21 @@
     return -1;
 }
 
-//DvrClient openDvr(int dvbType, int bufferSize, DvrClientCallback cb);
+sp<DvrClient> DemuxClient::openDvr(DvrType dvbType, int bufferSize, sp<DvrClientCallback> cb) {
+    // TODO: pending aidl interface
+
+    if (mDemux != NULL) {
+        sp<HidlDvrCallback> callback = new HidlDvrCallback(cb);
+        sp<IDvr> hidlDvr = openHidlDvr(dvbType, bufferSize, callback);
+        if (hidlDvr != NULL) {
+            sp<DvrClient> dvrClient = new DvrClient();
+            dvrClient->setHidlDvr(hidlDvr);
+            return dvrClient;
+        }
+    }
+
+    return NULL;
+}
 
 Result DemuxClient::connectCiCam(int ciCamId) {
     // pending aidl interface
@@ -173,4 +187,24 @@
 
     return hidlFilter;
 }
+
+sp<IDvr> DemuxClient::openHidlDvr(DvrType dvrType, int bufferSize,
+        sp<HidlDvrCallback> callback) {
+    if (mDemux == NULL) {
+        return NULL;
+    }
+
+    sp<IDvr> hidlDvr;
+    Result res;
+    mDemux->openDvr(dvrType, bufferSize, callback,
+            [&](Result r, const sp<IDvr>& dvr) {
+                hidlDvr = dvr;
+                res = r;
+            });
+    if (res != Result::SUCCESS || hidlDvr == NULL) {
+        return NULL;
+    }
+
+    return hidlDvr;
+}
 }  // namespace android
diff --git a/media/jni/tuner/DemuxClient.h b/media/jni/tuner/DemuxClient.h
index a0671a5..f11f2c6 100644
--- a/media/jni/tuner/DemuxClient.h
+++ b/media/jni/tuner/DemuxClient.h
@@ -21,6 +21,8 @@
 #include <android/hardware/tv/tuner/1.0/IDemux.h>
 #include <android/hardware/tv/tuner/1.1/types.h>
 
+#include "DvrClient.h"
+#include "DvrClientCallback.h"
 #include "FilterClient.h"
 #include "FilterClientCallback.h"
 #include "FrontendClient.h"
@@ -28,6 +30,7 @@
 //using ::aidl::android::media::tv::tuner::ITunerDemux;
 
 using ::android::hardware::tv::tuner::V1_0::DemuxFilterType;
+using ::android::hardware::tv::tuner::V1_0::DvrType;
 using ::android::hardware::tv::tuner::V1_0::IDemux;
 
 using namespace std;
@@ -68,8 +71,7 @@
     /**
      * Open a DVR (Digital Video Record) client.
      */
-    // TODO: handle DvrClient and callback
-    //DvrClient openDvr(int dvbType, int bufferSize, DvrClientCallback cb);  
+    sp<DvrClient> openDvr(DvrType dvbType, int bufferSize, sp<DvrClientCallback> cb);
 
     /**
      * Connect Conditional Access Modules (CAM) through Common Interface (CI).
@@ -88,6 +90,7 @@
 
 private:
     sp<IFilter> openHidlFilter(DemuxFilterType type, int bufferSize, sp<HidlFilterCallback> cb);
+    sp<IDvr> openHidlDvr(DvrType type, int bufferSize, sp<HidlDvrCallback> cb);
 
     /**
      * An AIDL Tuner Demux Singleton assigned at the first time the Tuner Client
diff --git a/media/jni/tuner/DvrClient.cpp b/media/jni/tuner/DvrClient.cpp
new file mode 100644
index 0000000..dd08491
--- /dev/null
+++ b/media/jni/tuner/DvrClient.cpp
@@ -0,0 +1,329 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "DvrClient"
+
+#include <android-base/logging.h>
+#include <utils/Log.h>
+
+#include "DvrClient.h"
+
+using ::android::hardware::tv::tuner::V1_0::DemuxQueueNotifyBits;
+using ::android::hardware::tv::tuner::V1_0::Result;
+
+namespace android {
+
+/////////////// DvrClient ///////////////////////
+
+// TODO: pending aidl interface
+DvrClient::DvrClient() {
+    //mTunerDvr = tunerDvr;
+    mFd = -1;
+    mDvrMQ = NULL;
+    mDvrMQEventFlag = NULL;
+}
+
+DvrClient::~DvrClient() {
+    //mTunerDvr = NULL;
+    mDvr = NULL;
+    mFd = -1;
+    mDvrMQ = NULL;
+    mDvrMQEventFlag = NULL;
+}
+
+// TODO: remove after migration to Tuner Service is done.
+void DvrClient::setHidlDvr(sp<IDvr> dvr) {
+    mDvr = dvr;
+}
+
+void DvrClient::setFd(int fd) {
+    mFd = fd;
+}
+
+long DvrClient::readFromFile(long size) {
+    if (mDvrMQ == NULL || mDvrMQEventFlag == NULL) {
+        ALOGE("Failed to readFromFile. DVR mq is not configured");
+        return -1;
+    }
+    if (mFd < 0) {
+        ALOGE("Failed to readFromFile. File is not configured");
+        return -1;
+    }
+
+    long available = mDvrMQ->availableToWrite();
+    long write = min(size, available);
+
+    MQ::MemTransaction tx;
+    long ret = 0;
+    if (mDvrMQ->beginWrite(write, &tx)) {
+        auto first = tx.getFirstRegion();
+        auto data = first.getAddress();
+        long length = first.getLength();
+        long firstToWrite = min(length, write);
+        ret = read(mFd, data, firstToWrite);
+
+        if (ret < 0) {
+            ALOGE("Failed to read from FD: %s", strerror(errno));
+            return -1;
+        }
+        if (ret < firstToWrite) {
+            ALOGW("file to MQ, first region: %ld bytes to write, but %ld bytes written",
+                    firstToWrite, ret);
+        } else if (firstToWrite < write) {
+            ALOGD("write second region: %ld bytes written, %ld bytes in total", ret, write);
+            auto second = tx.getSecondRegion();
+            data = second.getAddress();
+            length = second.getLength();
+            int secondToWrite = std::min(length, write - firstToWrite);
+            ret += read(mFd, data, secondToWrite);
+        }
+        ALOGD("file to MQ: %ld bytes need to be written, %ld bytes written", write, ret);
+        if (!mDvrMQ->commitWrite(ret)) {
+            ALOGE("Error: failed to commit write!");
+            return -1;
+        }
+    } else {
+        ALOGE("dvrMq.beginWrite failed");
+    }
+
+    if (ret > 0) {
+        mDvrMQEventFlag->wake(static_cast<uint32_t>(DemuxQueueNotifyBits::DATA_READY));
+    }
+    return ret;
+}
+
+long DvrClient::readFromBuffer(uint8_t* buffer, long size) {
+    if (mDvrMQ == NULL || mDvrMQEventFlag == NULL) {
+        ALOGE("Failed to readFromBuffer. DVR mq is not configured");
+        return -1;
+    }
+    if (buffer == nullptr) {
+        ALOGE("Failed to readFromBuffer. Buffer can't be null");
+        return -1;
+    }
+
+    long available = mDvrMQ->availableToWrite();
+    size = min(size, available);
+
+    if (mDvrMQ->write(buffer, size)) {
+        mDvrMQEventFlag->wake(static_cast<uint32_t>(DemuxQueueNotifyBits::DATA_READY));
+    } else {
+        ALOGD("Failed to write FMQ");
+        return -1;
+    }
+    return size;
+}
+
+long DvrClient::writeToFile(long size) {
+    if (mDvrMQ == NULL || mDvrMQEventFlag == NULL) {
+        ALOGE("Failed to writeToFile. DVR mq is not configured");
+        return -1;
+    }
+    if (mFd < 0) {
+        ALOGE("Failed to writeToFile. File is not configured");
+        return -1;
+    }
+
+    long available = mDvrMQ->availableToRead();
+    long toRead = min(size, available);
+
+    long ret = 0;
+    MQ::MemTransaction tx;
+    if (mDvrMQ->beginRead(toRead, &tx)) {
+        auto first = tx.getFirstRegion();
+        auto data = first.getAddress();
+        long length = first.getLength();
+        long firstToRead = std::min(length, toRead);
+        ret = write(mFd, data, firstToRead);
+
+        if (ret < 0) {
+            ALOGE("Failed to write to FD: %s", strerror(errno));
+            return -1;
+        }
+        if (ret < firstToRead) {
+            ALOGW("MQ to file: %ld bytes read, but %ld bytes written", firstToRead, ret);
+        } else if (firstToRead < toRead) {
+            ALOGD("read second region: %ld bytes read, %ld bytes in total", ret, toRead);
+            auto second = tx.getSecondRegion();
+            data = second.getAddress();
+            length = second.getLength();
+            int secondToRead = toRead - firstToRead;
+            ret += write(mFd, data, secondToRead);
+        }
+        ALOGD("MQ to file: %ld bytes to be read, %ld bytes written", toRead, ret);
+        if (!mDvrMQ->commitRead(ret)) {
+            ALOGE("Error: failed to commit read!");
+            return 0;
+        }
+    } else {
+        ALOGE("dvrMq.beginRead failed");
+    }
+    if (ret > 0) {
+        mDvrMQEventFlag->wake(static_cast<uint32_t>(DemuxQueueNotifyBits::DATA_CONSUMED));
+    }
+
+    return ret;
+}
+
+long DvrClient::writeToBuffer(uint8_t* buffer, long size) {
+    if (mDvrMQ == NULL || mDvrMQEventFlag == NULL) {
+        ALOGE("Failed to writetoBuffer. DVR mq is not configured");
+        return -1;
+    }
+    if (buffer == nullptr) {
+        ALOGE("Failed to writetoBuffer. Buffer can't be null");
+        return -1;
+    }
+
+    long available = mDvrMQ->availableToRead();
+    size = min(size, available);
+
+    if (mDvrMQ->read(buffer, size)) {
+        mDvrMQEventFlag->wake(static_cast<uint32_t>(DemuxQueueNotifyBits::DATA_CONSUMED));
+    } else {
+        ALOGD("Failed to write FMQ");
+        return -1;
+    }
+    return size;
+}
+
+Result DvrClient::configure(DvrSettings settings) {
+    // pending aidl interface
+
+    if (mDvr != NULL) {
+        Result res = mDvr->configure(settings);
+        if (res == Result::SUCCESS) {
+            MQDescriptorSync<uint8_t> dvrMQDesc;
+            res = getQueueDesc(dvrMQDesc);
+            if (res == Result::SUCCESS) {
+                mDvrMQ = make_unique<MQ>(dvrMQDesc, true);
+                EventFlag::createEventFlag(mDvrMQ->getEventFlagWord(), &mDvrMQEventFlag);
+            }
+        }
+        return res;
+    }
+
+    return Result::INVALID_STATE;
+}
+
+Result DvrClient::attachFilter(sp<FilterClient> filterClient) {
+    // pending aidl interface
+
+    if (mDvr != NULL) {
+        sp<IFilter> hidlFilter = filterClient->getHalFilter();
+        if (hidlFilter == NULL) {
+            return Result::INVALID_ARGUMENT;
+        }
+        return mDvr->attachFilter(hidlFilter);
+    }
+
+    return Result::INVALID_STATE;
+}
+
+Result DvrClient::detachFilter(sp<FilterClient> filterClient) {
+    // pending aidl interface
+
+    if (mDvr != NULL) {
+        sp<IFilter> hidlFilter = filterClient->getHalFilter();
+        if (hidlFilter == NULL) {
+            return Result::INVALID_ARGUMENT;
+        }
+        return mDvr->detachFilter(hidlFilter);
+    }
+
+    return Result::INVALID_STATE;
+}
+
+Result DvrClient::start() {
+    // pending aidl interface
+
+    if (mDvr != NULL) {
+        return mDvr->start();
+    }
+
+    return Result::INVALID_STATE;
+}
+
+Result DvrClient::stop() {
+    // pending aidl interface
+
+    if (mDvr != NULL) {
+        return mDvr->stop();
+    }
+
+    return Result::INVALID_STATE;
+}
+
+Result DvrClient::flush() {
+    // pending aidl interface
+
+    if (mDvr != NULL) {
+        return mDvr->flush();
+    }
+
+    return Result::INVALID_STATE;
+}
+
+Result DvrClient::close() {
+    // pending aidl interface
+
+    if (mDvr != NULL) {
+        Result res = mDvr->close();
+        if (res == Result::SUCCESS) {
+            mDvr = NULL;
+        }
+        return res;
+    }
+
+    return Result::INVALID_STATE;
+}
+
+/////////////// IDvrCallback ///////////////////////
+
+HidlDvrCallback::HidlDvrCallback(sp<DvrClientCallback> dvrClientCallback)
+        : mDvrClientCallback(dvrClientCallback) {}
+
+Return<void> HidlDvrCallback::onRecordStatus(const RecordStatus status) {
+    if (mDvrClientCallback != NULL) {
+        mDvrClientCallback->onRecordStatus(status);
+    }
+    return Void();
+}
+
+Return<void> HidlDvrCallback::onPlaybackStatus(const PlaybackStatus status) {
+    if (mDvrClientCallback != NULL) {
+        mDvrClientCallback->onPlaybackStatus(status);
+    }
+    return Void();
+}
+
+/////////////// DvrClient Helper Methods ///////////////////////
+
+Result DvrClient::getQueueDesc(MQDesc& dvrMQDesc) {
+    // pending aidl interface
+
+    if (mDvr != NULL) {
+        Result res = Result::UNKNOWN_ERROR;
+        mDvr->getQueueDesc([&](Result r, const MQDesc& desc) {
+            dvrMQDesc = desc;
+            res = r;
+        });
+        return res;
+    }
+
+    return Result::INVALID_STATE;
+}
+}  // namespace android
diff --git a/media/jni/tuner/DvrClient.h b/media/jni/tuner/DvrClient.h
new file mode 100644
index 0000000..2aba5e0
--- /dev/null
+++ b/media/jni/tuner/DvrClient.h
@@ -0,0 +1,162 @@
+/*
+ * 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.
+ */
+
+#ifndef _ANDROID_MEDIA_TV_DVR_CLIENT_H_
+#define _ANDROID_MEDIA_TV_DVR_CLIENT_H_
+
+//#include <aidl/android/media/tv/tuner/ITunerDvr.h>
+#include <android/hardware/tv/tuner/1.0/IDvr.h>
+#include <android/hardware/tv/tuner/1.0/IDvrCallback.h>
+#include <android/hardware/tv/tuner/1.1/types.h>
+#include <fmq/MessageQueue.h>
+
+#include "DvrClientCallback.h"
+#include "FilterClient.h"
+
+//using ::aidl::android::media::tv::tuner::ITunerDvr;
+
+using ::android::hardware::EventFlag;
+using ::android::hardware::MQDescriptorSync;
+using ::android::hardware::MessageQueue;
+using ::android::hardware::tv::tuner::V1_0::DvrSettings;
+using ::android::hardware::tv::tuner::V1_0::IDvr;
+using ::android::hardware::tv::tuner::V1_0::IDvrCallback;
+
+using namespace std;
+
+using MQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
+using MQDesc = MQDescriptorSync<uint8_t>;
+
+namespace android {
+
+// TODO: pending aidl interface
+/*class TunerDvrCallback : public BnTunerDvrCallback {
+
+public:
+    TunerDvrCallback(sp<DvrClientCallback> dvrClientCallback);
+
+    Status onRecordStatus(int status);
+    Status onPlaybackStatus(int status);
+
+private:
+    sp<DvrClientCallback> mDvrClientCallback;
+};*/
+
+struct HidlDvrCallback : public IDvrCallback {
+
+public:
+    HidlDvrCallback(sp<DvrClientCallback> dvrClientCallback);
+    virtual Return<void> onRecordStatus(const RecordStatus status);
+    virtual Return<void> onPlaybackStatus(const PlaybackStatus status);
+
+private:
+    sp<DvrClientCallback> mDvrClientCallback;
+};
+
+struct DvrClient : public RefBase {
+
+public:
+    DvrClient();
+    ~DvrClient();
+
+    // TODO: remove after migration to Tuner Service is done.
+    void setHidlDvr(sp<IDvr> dvr);
+
+    /**
+     * Set the DVR file descriptor.
+     */
+    void setFd(int fd);
+
+    /**
+     * Read data from file with given size. Return the actual read size.
+     */
+    long readFromFile(long size);
+
+    /**
+     * Read data from the given buffer with given size. Return the actual read size.
+     */
+    long readFromBuffer(uint8_t* buffer, long size);
+
+    /**
+     * Write data to file with given size. Return the actual write size.
+     */
+    long writeToFile(long size);
+
+    /**
+     * Write data to the given buffer with given size. Return the actual write size.
+     */
+    long writeToBuffer(uint8_t* buffer, long size);
+
+    /**
+     * Configure the DVR.
+     */
+    Result configure(DvrSettings settings);
+
+    /**
+     * Attach one filter to DVR interface for recording.
+     */
+    Result attachFilter(sp<FilterClient> filterClient);
+
+    /**
+     * Detach one filter from the DVR's recording.
+     */
+    Result detachFilter(sp<FilterClient> filterClient);
+
+    /**
+     * Start DVR.
+     */
+    Result start();
+
+    /**
+     * Stop DVR.
+     */
+    Result stop();
+
+    /**
+     * Flush DVR data.
+     */
+    Result flush();
+
+    /**
+     * close the DVR instance to release resource for DVR.
+     */
+    Result close();
+
+private:
+    Result getQueueDesc(MQDesc& dvrMQDesc);
+
+    /**
+     * An AIDL Tuner Dvr Singleton assigned at the first time the Tuner Client
+     * opens a dvr. Default null when dvr is not opened.
+     */
+    // TODO: pending on aidl interface
+    //shared_ptr<ITunerDvr> mTunerDvr;
+
+    /**
+     * A Dvr HAL interface that is ready before migrating to the TunerDvr.
+     * This is a temprary interface before Tuner Framework migrates to use TunerService.
+     * Default null when the HAL service does not exist.
+     */
+    sp<IDvr> mDvr;
+
+    unique_ptr<MQ> mDvrMQ;
+    EventFlag* mDvrMQEventFlag;
+    string mFilePath;
+    int mFd;
+};
+}  // namespace android
+
+#endif  // _ANDROID_MEDIA_TV_DVR_CLIENT_H_
diff --git a/media/jni/tuner/DvrClientCallback.h b/media/jni/tuner/DvrClientCallback.h
new file mode 100644
index 0000000..6684424
--- /dev/null
+++ b/media/jni/tuner/DvrClientCallback.h
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+
+#ifndef _ANDROID_MEDIA_TV_DVR_CLIENT_CALLBACK_H_
+#define _ANDROID_MEDIA_TV_DVR_CLIENT_CALLBACK_H_
+
+using ::android::hardware::tv::tuner::V1_0::PlaybackStatus;
+using ::android::hardware::tv::tuner::V1_0::RecordStatus;
+
+using namespace std;
+
+namespace android {
+
+struct DvrClientCallback : public RefBase {
+    virtual void onRecordStatus(const RecordStatus status);
+    virtual void onPlaybackStatus(const PlaybackStatus status);
+};
+}  // namespace android
+
+#endif  // _ANDROID_MEDIA_TV_DVR_CLIENT_CALLBACK_H_
\ No newline at end of file
diff --git a/media/jni/tuner/TunerClient.cpp b/media/jni/tuner/TunerClient.cpp
index 2c1735f..bd18c707 100644
--- a/media/jni/tuner/TunerClient.cpp
+++ b/media/jni/tuner/TunerClient.cpp
@@ -40,6 +40,8 @@
     // Connect with Tuner Service.
     ::ndk::SpAIBinder binder(AServiceManager_getService("media.tuner"));
     mTunerService = ITunerService::fromBinder(binder);
+    // TODO: Remove after JNI migration is done.
+    mTunerService = NULL;
     if (mTunerService == NULL) {
         ALOGE("Failed to get tuner service");
     }
diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt
index 8fa3acf..314bf29 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -295,6 +295,7 @@
     AThermal_getCurrentThermalStatus; # introduced=30
     AThermal_registerThermalStatusListener; # introduced=30
     AThermal_unregisterThermalStatusListener; # introduced=30
+    AThermal_getThermalHeadroom; # introduced=31
   local:
     *;
 };
diff --git a/native/android/thermal.cpp b/native/android/thermal.cpp
index 545c423..1f6ef47 100644
--- a/native/android/thermal.cpp
+++ b/native/android/thermal.cpp
@@ -18,6 +18,7 @@
 
 #include <cerrno>
 #include <thread>
+#include <limits>
 
 #include <android/thermal.h>
 #include <android/os/BnThermalStatusListener.h>
@@ -52,6 +53,7 @@
         status_t getCurrentThermalStatus(int32_t *status);
         status_t addListener(AThermal_StatusCallback, void *data);
         status_t removeListener(AThermal_StatusCallback, void *data);
+        status_t getThermalHeadroom(int32_t forecastSeconds, float *result);
    private:
        AThermalManager(sp<IThermalService> service);
        sp<IThermalService> mThermalSvc;
@@ -184,6 +186,18 @@
     return OK;
 }
 
+status_t AThermalManager::getThermalHeadroom(int32_t forecastSeconds, float *result) {
+    binder::Status ret = mThermalSvc->getThermalHeadroom(forecastSeconds, result);
+
+    if (!ret.isOk()) {
+        if (ret.exceptionCode() == binder::Status::EX_SECURITY) {
+            return EPERM;
+        }
+        return EPIPE;
+    }
+    return OK;
+}
+
 /**
   * Acquire an instance of the thermal manager. This must be freed using
   * {@link AThermal_releaseManager}.
@@ -259,3 +273,32 @@
         AThermal_StatusCallback callback, void *data) {
     return manager->removeListener(callback, data);
 }
+
+/**
+ * Provides an estimate of how much thermal headroom the device currently has
+ * before hitting severe throttling.
+ *
+ * Note that this only attempts to track the headroom of slow-moving sensors,
+ * such as the skin temperature sensor. This means that there is no benefit to
+ * calling this function more frequently than about once per second, and attempts
+ * to call significantly more frequently may result in the function returning {@code NaN}.
+ *
+ * See also PowerManager#getThermalHeadroom.
+ *
+ * @param manager The manager instance to use
+ * @param forecastSeconds how many seconds in the future to forecast
+ * @return a value greater than or equal to 0.0 where 1.0 indicates the SEVERE throttling
+ *  	   threshold. Returns NaN if the device does not support this functionality or if
+ * 	       this function is called significantly faster than once per second.
+ */
+float AThermal_getThermalHeadroom(AThermalManager *manager,
+        int forecastSeconds) {
+    float result = 0.0f;
+    status_t ret = manager->getThermalHeadroom(forecastSeconds, &result);
+
+    if (ret != OK) {
+        result = std::numeric_limits<float>::quiet_NaN();
+    }
+
+    return result;
+}
diff --git a/native/graphics/OWNERS b/native/graphics/OWNERS
index a6d1bc3..d81ea2c 100644
--- a/native/graphics/OWNERS
+++ b/native/graphics/OWNERS
@@ -1 +1 @@
-include /core/java/android/graphics/OWNERS
+include /graphics/java/android/graphics/OWNERS
diff --git a/native/webview/TEST_MAPPING b/native/webview/TEST_MAPPING
new file mode 100644
index 0000000..bd25200
--- /dev/null
+++ b/native/webview/TEST_MAPPING
@@ -0,0 +1,36 @@
+{
+  "presubmit": [
+    {
+      "name": "CtsWebkitTestCases",
+      "options": [
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        }
+      ]
+    },
+    {
+      "name": "CtsHostsideWebViewTests",
+      "options": [
+        {
+          "exclude-annotation": "androidx.test.filters.FlakyTest"
+        }
+      ]
+    },
+    {
+      "name": "GtsWebViewTestCases",
+      "options": [
+        {
+          "exclude-annotation": "android.test.FlakyTest"
+        }
+      ]
+    },
+    {
+      "name": "GtsWebViewHostTestCases",
+      "options": [
+        {
+          "exclude-annotation": "android.test.FlakyTest"
+        }
+      ]
+    }
+  ]
+}
diff --git a/packages/CompanionDeviceManager/res/values-af/strings.xml b/packages/CompanionDeviceManager/res/values-af/strings.xml
index b83c914..8fc3531 100644
--- a/packages/CompanionDeviceManager/res/values-af/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-af/strings.xml
@@ -17,16 +17,10 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="4470785958457506021">"Metgeseltoestel-bestuurder"</string>
-    <!-- no translation found for chooser_title (2262294130493605839) -->
-    <skip />
-    <!-- no translation found for profile_name_generic (6851028682723034988) -->
-    <skip />
-    <!-- no translation found for confirmation_title (4751119145078041732) -->
-    <skip />
-    <!-- no translation found for profile_summary (3167701603666642104) -->
-    <skip />
-    <!-- no translation found for consent_yes (4055438216605487056) -->
-    <skip />
-    <!-- no translation found for consent_no (1335543792857823917) -->
-    <skip />
+    <string name="chooser_title" msgid="2262294130493605839">"Kies \'n <xliff:g id="PROFILE_NAME">%1$s</xliff:g> om deur &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; bestuur te word"</string>
+    <string name="profile_name_generic" msgid="6851028682723034988">"toestel"</string>
+    <string name="confirmation_title" msgid="4751119145078041732">"Stel &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; om jou <xliff:g id="PROFILE_NAME">%2$s</xliff:g> te bestuur – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="3167701603666642104">"Jy het <xliff:g id="APP_NAME_0">%1$s</xliff:g> nodig om jou <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g> te bestuur. <xliff:g id="APP_NAME2">%3$s</xliff:g> sal toegang hê tot <xliff:g id="PERMISSIONS">%4$s</xliff:g> terwyl die <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> gekoppel is."</string>
+    <string name="consent_yes" msgid="4055438216605487056">"Ja"</string>
+    <string name="consent_no" msgid="1335543792857823917">"Nee, dankie"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-am/strings.xml b/packages/CompanionDeviceManager/res/values-am/strings.xml
index bad5a9f..ff31454 100644
--- a/packages/CompanionDeviceManager/res/values-am/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-am/strings.xml
@@ -17,16 +17,10 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="4470785958457506021">"አጃቢ የመሣሪያ አስተዳዳሪ"</string>
-    <!-- no translation found for chooser_title (2262294130493605839) -->
-    <skip />
-    <!-- no translation found for profile_name_generic (6851028682723034988) -->
-    <skip />
-    <!-- no translation found for confirmation_title (4751119145078041732) -->
-    <skip />
-    <!-- no translation found for profile_summary (3167701603666642104) -->
-    <skip />
-    <!-- no translation found for consent_yes (4055438216605487056) -->
-    <skip />
-    <!-- no translation found for consent_no (1335543792857823917) -->
-    <skip />
+    <string name="chooser_title" msgid="2262294130493605839">"በ&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; የሚተዳደር <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ይምረጡ"</string>
+    <string name="profile_name_generic" msgid="6851028682723034988">"መሣሪያ"</string>
+    <string name="confirmation_title" msgid="4751119145078041732">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; የእርስዎን <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; እንዲያስተዳድር ያቀናብሩት"</string>
+    <string name="profile_summary" msgid="3167701603666642104">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> የእርስዎን <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g> ለማስተዳደር ያስፈልጋል። <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> ተገናኝቶ ሳለ <xliff:g id="APP_NAME2">%3$s</xliff:g> የ<xliff:g id="PERMISSIONS">%4$s</xliff:g> መዳረሻን ያገኛል።"</string>
+    <string name="consent_yes" msgid="4055438216605487056">"አዎ"</string>
+    <string name="consent_no" msgid="1335543792857823917">"አይ፣ አመሰግናለሁ"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-ar/strings.xml b/packages/CompanionDeviceManager/res/values-ar/strings.xml
index a9a202e..aedf0f3 100644
--- a/packages/CompanionDeviceManager/res/values-ar/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ar/strings.xml
@@ -17,16 +17,10 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="4470785958457506021">"تطبيق \"مدير الجهاز المصاحب\""</string>
-    <!-- no translation found for chooser_title (2262294130493605839) -->
-    <skip />
-    <!-- no translation found for profile_name_generic (6851028682723034988) -->
-    <skip />
-    <!-- no translation found for confirmation_title (4751119145078041732) -->
-    <skip />
-    <!-- no translation found for profile_summary (3167701603666642104) -->
-    <skip />
-    <!-- no translation found for consent_yes (4055438216605487056) -->
-    <skip />
-    <!-- no translation found for consent_no (1335543792857823917) -->
-    <skip />
+    <string name="chooser_title" msgid="2262294130493605839">"‏اختَر <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ليديره تطبيق &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_name_generic" msgid="6851028682723034988">"جهاز"</string>
+    <string name="confirmation_title" msgid="4751119145078041732">"‏اضبط &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; لإدارة <xliff:g id="PROFILE_NAME">%2$s</xliff:g> على &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="3167701603666642104">"يجب توفّر تطبيق <xliff:g id="APP_NAME_0">%1$s</xliff:g> لإدارة <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>. سيتمكن تطبيق <xliff:g id="APP_NAME2">%3$s</xliff:g> من الوصول إلى <xliff:g id="PERMISSIONS">%4$s</xliff:g> عندما يكون <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> مرتبطًا."</string>
+    <string name="consent_yes" msgid="4055438216605487056">"نعم"</string>
+    <string name="consent_no" msgid="1335543792857823917">"لا، شكرًا"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-as/strings.xml b/packages/CompanionDeviceManager/res/values-as/strings.xml
index 31db4732..f0c3ee8 100644
--- a/packages/CompanionDeviceManager/res/values-as/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-as/strings.xml
@@ -17,16 +17,10 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="4470785958457506021">"কম্পেনিয়ন ডিভাইচ মেনেজাৰ"</string>
-    <!-- no translation found for chooser_title (2262294130493605839) -->
-    <skip />
-    <!-- no translation found for profile_name_generic (6851028682723034988) -->
-    <skip />
-    <!-- no translation found for confirmation_title (4751119145078041732) -->
-    <skip />
-    <!-- no translation found for profile_summary (3167701603666642104) -->
-    <skip />
-    <!-- no translation found for consent_yes (4055438216605487056) -->
-    <skip />
-    <!-- no translation found for consent_no (1335543792857823917) -->
-    <skip />
+    <string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;এ পৰিচালনা কৰিব লগা এটা <xliff:g id="PROFILE_NAME">%1$s</xliff:g> বাছনি কৰক"</string>
+    <string name="profile_name_generic" msgid="6851028682723034988">"ডিভাইচ"</string>
+    <string name="confirmation_title" msgid="4751119145078041732">"আপোনাৰ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> পৰিচালনা কৰিবলৈ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ছেট কৰক - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="3167701603666642104">"আপোনাৰ <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g> পৰিচালনা কৰিবলৈ <xliff:g id="APP_NAME_0">%1$s</xliff:g>ৰ আৱশ্যক। <xliff:g id="PROFILE_NAME2">%5$s</xliff:g>ৰ সৈতে সংযোগ কৰিলে <xliff:g id="APP_NAME2">%3$s</xliff:g>এ <xliff:g id="PERMISSIONS">%4$s</xliff:g>লৈ এক্সেছ পাব।"</string>
+    <string name="consent_yes" msgid="4055438216605487056">"হয়"</string>
+    <string name="consent_no" msgid="1335543792857823917">"নালাগে, ধন্যবাদ"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-az/strings.xml b/packages/CompanionDeviceManager/res/values-az/strings.xml
index ee794c3..64bea4d 100644
--- a/packages/CompanionDeviceManager/res/values-az/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-az/strings.xml
@@ -17,16 +17,10 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="4470785958457506021">"Kompanyon Cihaz Meneceri"</string>
-    <!-- no translation found for chooser_title (2262294130493605839) -->
-    <skip />
-    <!-- no translation found for profile_name_generic (6851028682723034988) -->
-    <skip />
-    <!-- no translation found for confirmation_title (4751119145078041732) -->
-    <skip />
-    <!-- no translation found for profile_summary (3167701603666642104) -->
-    <skip />
-    <!-- no translation found for consent_yes (4055438216605487056) -->
-    <skip />
-    <!-- no translation found for consent_no (1335543792857823917) -->
-    <skip />
+    <string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; tərəfindən idarə ediləcək <xliff:g id="PROFILE_NAME">%1$s</xliff:g> seçin"</string>
+    <string name="profile_name_generic" msgid="6851028682723034988">"cihaz"</string>
+    <string name="confirmation_title" msgid="4751119145078041732">"<xliff:g id="PROFILE_NAME">%2$s</xliff:g> profilinizin &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; tərəfindən idarə olunmasını ayarlayın - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="3167701603666642104">"<xliff:g id="PROFILE_NAME_1">%2$s</xliff:g> profilinizi idarə etmək üçün <xliff:g id="APP_NAME_0">%1$s</xliff:g> tələb olunur. <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> qoşulu olduqda <xliff:g id="APP_NAME2">%3$s</xliff:g> <xliff:g id="PERMISSIONS">%4$s</xliff:g> bölməsinə giriş əldə edəcək."</string>
+    <string name="consent_yes" msgid="4055438216605487056">"Bəli"</string>
+    <string name="consent_no" msgid="1335543792857823917">"Xeyr, çox sağolun"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml b/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml
index 708e49a..3f06722 100644
--- a/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-b+sr+Latn/strings.xml
@@ -17,16 +17,10 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="4470785958457506021">"Menadžer pridruženog uređaja"</string>
-    <!-- no translation found for chooser_title (2262294130493605839) -->
-    <skip />
-    <!-- no translation found for profile_name_generic (6851028682723034988) -->
-    <skip />
-    <!-- no translation found for confirmation_title (4751119145078041732) -->
-    <skip />
-    <!-- no translation found for profile_summary (3167701603666642104) -->
-    <skip />
-    <!-- no translation found for consent_yes (4055438216605487056) -->
-    <skip />
-    <!-- no translation found for consent_no (1335543792857823917) -->
-    <skip />
+    <string name="chooser_title" msgid="2262294130493605839">"Odaberite profil <xliff:g id="PROFILE_NAME">%1$s</xliff:g> kojim će upravljati aplikacija &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_name_generic" msgid="6851028682723034988">"uređaj"</string>
+    <string name="confirmation_title" msgid="4751119145078041732">"Podesite aplikaciju &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; da upravlja profilom <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="3167701603666642104">"Aplikacija <xliff:g id="APP_NAME_0">%1$s</xliff:g> je neophodna za upravljanje profilom <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>. <xliff:g id="APP_NAME2">%3$s</xliff:g> će dobiti pristup dozvolama za <xliff:g id="PERMISSIONS">%4$s</xliff:g> dok je <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> povezan."</string>
+    <string name="consent_yes" msgid="4055438216605487056">"Da"</string>
+    <string name="consent_no" msgid="1335543792857823917">"Ne, hvala"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-be/strings.xml b/packages/CompanionDeviceManager/res/values-be/strings.xml
index a1a04e6..25e235c 100644
--- a/packages/CompanionDeviceManager/res/values-be/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-be/strings.xml
@@ -17,16 +17,10 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="4470785958457506021">"Менеджар спадарожнай прылады"</string>
-    <!-- no translation found for chooser_title (2262294130493605839) -->
-    <skip />
-    <!-- no translation found for profile_name_generic (6851028682723034988) -->
-    <skip />
-    <!-- no translation found for confirmation_title (4751119145078041732) -->
-    <skip />
-    <!-- no translation found for profile_summary (3167701603666642104) -->
-    <skip />
-    <!-- no translation found for consent_yes (4055438216605487056) -->
-    <skip />
-    <!-- no translation found for consent_no (1335543792857823917) -->
-    <skip />
+    <string name="chooser_title" msgid="2262294130493605839">"Выберыце прыладу (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>), якой будзе кіраваць праграма &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_name_generic" msgid="6851028682723034988">"прылада"</string>
+    <string name="confirmation_title" msgid="4751119145078041732">"Дазвольце праграме &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; кіраваць прыладай \"<xliff:g id="PROFILE_NAME">%2$s</xliff:g>\" – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="3167701603666642104">"Для кіравання прыладай \"<xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>\" патрабуецца праграма \"<xliff:g id="APP_NAME_0">%1$s</xliff:g>\". Калі прылада \"<xliff:g id="PROFILE_NAME2">%5$s</xliff:g>\" будзе падключана, <xliff:g id="APP_NAME2">%3$s</xliff:g> атрымае наступныя дазволы: <xliff:g id="PERMISSIONS">%4$s</xliff:g>."</string>
+    <string name="consent_yes" msgid="4055438216605487056">"Так"</string>
+    <string name="consent_no" msgid="1335543792857823917">"Не, дзякуй"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-bg/strings.xml b/packages/CompanionDeviceManager/res/values-bg/strings.xml
index eb9204a..264ce27 100644
--- a/packages/CompanionDeviceManager/res/values-bg/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-bg/strings.xml
@@ -17,16 +17,10 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string>
-    <!-- no translation found for chooser_title (2262294130493605839) -->
-    <skip />
-    <!-- no translation found for profile_name_generic (6851028682723034988) -->
-    <skip />
-    <!-- no translation found for confirmation_title (4751119145078041732) -->
-    <skip />
-    <!-- no translation found for profile_summary (3167701603666642104) -->
-    <skip />
-    <!-- no translation found for consent_yes (4055438216605487056) -->
-    <skip />
-    <!-- no translation found for consent_no (1335543792857823917) -->
-    <skip />
+    <string name="chooser_title" msgid="2262294130493605839">"Изберете устройство (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>), което да се управлява от &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_name_generic" msgid="6851028682723034988">"устройство"</string>
+    <string name="confirmation_title" msgid="4751119145078041732">"Задайте &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; да управлява устройството ви (<xliff:g id="PROFILE_NAME">%2$s</xliff:g>) – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="3167701603666642104">"За управление на <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g> се изисква <xliff:g id="APP_NAME_0">%1$s</xliff:g>. <xliff:g id="APP_NAME2">%3$s</xliff:g> ще получи достъп до <xliff:g id="PERMISSIONS">%4$s</xliff:g>, докато устройството (<xliff:g id="PROFILE_NAME2">%5$s</xliff:g>) е свързано."</string>
+    <string name="consent_yes" msgid="4055438216605487056">"Да"</string>
+    <string name="consent_no" msgid="1335543792857823917">"Не, благодаря"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-bn/strings.xml b/packages/CompanionDeviceManager/res/values-bn/strings.xml
index eb9204a..65f92c9 100644
--- a/packages/CompanionDeviceManager/res/values-bn/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-bn/strings.xml
@@ -17,16 +17,10 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string>
-    <!-- no translation found for chooser_title (2262294130493605839) -->
-    <skip />
-    <!-- no translation found for profile_name_generic (6851028682723034988) -->
-    <skip />
-    <!-- no translation found for confirmation_title (4751119145078041732) -->
-    <skip />
-    <!-- no translation found for profile_summary (3167701603666642104) -->
-    <skip />
-    <!-- no translation found for consent_yes (4055438216605487056) -->
-    <skip />
-    <!-- no translation found for consent_no (1335543792857823917) -->
-    <skip />
+    <string name="chooser_title" msgid="2262294130493605839">"<xliff:g id="PROFILE_NAME">%1$s</xliff:g> বেছে নিন যেটি &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; ম্যানেজ করবে"</string>
+    <string name="profile_name_generic" msgid="6851028682723034988">"ডিভাইস"</string>
+    <string name="confirmation_title" msgid="4751119145078041732">"আপনার <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; ম্যানেজ করার জন্য &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; সেট করুন"</string>
+    <string name="profile_summary" msgid="3167701603666642104">"<xliff:g id="APP_NAME_0">%1$s</xliff:g>-কে আপনার <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>.ম্যানেজ করতে দিতে হবে। <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> কানেক্ট করা হলে <xliff:g id="APP_NAME2">%3$s</xliff:g> <xliff:g id="PERMISSIONS">%4$s</xliff:g> অ্যাক্সেস করতে পারবে।"</string>
+    <string name="consent_yes" msgid="4055438216605487056">"হ্যাঁ"</string>
+    <string name="consent_no" msgid="1335543792857823917">"না থাক"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-bs/strings.xml b/packages/CompanionDeviceManager/res/values-bs/strings.xml
index 6515981fa..f8e24b7 100644
--- a/packages/CompanionDeviceManager/res/values-bs/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-bs/strings.xml
@@ -17,10 +17,10 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="4470785958457506021">"Prateći upravitelj uređaja"</string>
-    <string name="chooser_title" msgid="2262294130493605839">"Odaberite profil <xliff:g id="PROFILE_NAME">%1$s</xliff:g> kojim će upravljati aplikacija &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="chooser_title" msgid="2262294130493605839">"Odaberite uređaj <xliff:g id="PROFILE_NAME">%1$s</xliff:g> kojim će upravljati aplikacija &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
     <string name="profile_name_generic" msgid="6851028682723034988">"uređaj"</string>
-    <string name="confirmation_title" msgid="4751119145078041732">"Postavite aplikaciju &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; da upravlja vašim profilom <xliff:g id="PROFILE_NAME">%2$s</xliff:g> –- &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
-    <string name="profile_summary" msgid="3167701603666642104">"Potrebna je <xliff:g id="APP_NAME_0">%1$s</xliff:g> za upravljanje vašim profilom <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>. Aplikacija <xliff:g id="APP_NAME2">%3$s</xliff:g> dobit će pristup dopuštenju za <xliff:g id="PERMISSIONS">%4$s</xliff:g> dok je povezan profil <xliff:g id="PROFILE_NAME2">%5$s</xliff:g>."</string>
+    <string name="confirmation_title" msgid="4751119145078041732">"Postavite aplikaciju &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; da upravlja vašim uređajem <xliff:g id="PROFILE_NAME">%2$s</xliff:g> — &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="3167701603666642104">"Potrebna je aplikacija <xliff:g id="APP_NAME_0">%1$s</xliff:g> za upravljanje uređajem <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>. Aplikacija <xliff:g id="APP_NAME2">%3$s</xliff:g> će dobiti pristup uslugama <xliff:g id="PERMISSIONS">%4$s</xliff:g> dok je uređaj <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> povezan."</string>
     <string name="consent_yes" msgid="4055438216605487056">"Da"</string>
     <string name="consent_no" msgid="1335543792857823917">"Ne, hvala"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-ca/strings.xml b/packages/CompanionDeviceManager/res/values-ca/strings.xml
index a51c967..ae8ca9f 100644
--- a/packages/CompanionDeviceManager/res/values-ca/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ca/strings.xml
@@ -17,16 +17,10 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="4470785958457506021">"Gestor de dispositius complementaris"</string>
-    <!-- no translation found for chooser_title (2262294130493605839) -->
-    <skip />
-    <!-- no translation found for profile_name_generic (6851028682723034988) -->
-    <skip />
-    <!-- no translation found for confirmation_title (4751119145078041732) -->
-    <skip />
-    <!-- no translation found for profile_summary (3167701603666642104) -->
-    <skip />
-    <!-- no translation found for consent_yes (4055438216605487056) -->
-    <skip />
-    <!-- no translation found for consent_no (1335543792857823917) -->
-    <skip />
+    <string name="chooser_title" msgid="2262294130493605839">"Tria un <xliff:g id="PROFILE_NAME">%1$s</xliff:g> perquè el gestioni &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_name_generic" msgid="6851028682723034988">"dispositiu"</string>
+    <string name="confirmation_title" msgid="4751119145078041732">"Defineix que &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; gestioni el teu <xliff:g id="PROFILE_NAME">%2$s</xliff:g> (&lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;)"</string>
+    <string name="profile_summary" msgid="3167701603666642104">"Cal <xliff:g id="APP_NAME_0">%1$s</xliff:g> per gestionar el teu <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>. <xliff:g id="APP_NAME2">%3$s</xliff:g> tindrà accés als permisos <xliff:g id="PERMISSIONS">%4$s</xliff:g> mentre el <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> estigui connectat."</string>
+    <string name="consent_yes" msgid="4055438216605487056">"Sí"</string>
+    <string name="consent_no" msgid="1335543792857823917">"No, gràcies"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-cs/strings.xml b/packages/CompanionDeviceManager/res/values-cs/strings.xml
index faed4dd..675bd29 100644
--- a/packages/CompanionDeviceManager/res/values-cs/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-cs/strings.xml
@@ -17,16 +17,10 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="4470785958457506021">"Správce doprovodných zařízení"</string>
-    <!-- no translation found for chooser_title (2262294130493605839) -->
-    <skip />
-    <!-- no translation found for profile_name_generic (6851028682723034988) -->
-    <skip />
-    <!-- no translation found for confirmation_title (4751119145078041732) -->
-    <skip />
-    <!-- no translation found for profile_summary (3167701603666642104) -->
-    <skip />
-    <!-- no translation found for consent_yes (4055438216605487056) -->
-    <skip />
-    <!-- no translation found for consent_no (1335543792857823917) -->
-    <skip />
+    <string name="chooser_title" msgid="2262294130493605839">"Vyberte zařízení <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, které chcete spravovat pomocí aplikace &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_name_generic" msgid="6851028682723034988">"zařízení"</string>
+    <string name="confirmation_title" msgid="4751119145078041732">"Nastavit aplikaci &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ke správě tohoto zařízení: <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="3167701603666642104">"Ke správě zařízení <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g> je potřeba aplikace <xliff:g id="APP_NAME_0">%1$s</xliff:g>. Dokud bude zařízení <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> připojeno, bude mít aplikace <xliff:g id="APP_NAME2">%3$s</xliff:g> přístup k těmto oprávněním: <xliff:g id="PERMISSIONS">%4$s</xliff:g>."</string>
+    <string name="consent_yes" msgid="4055438216605487056">"Ano"</string>
+    <string name="consent_no" msgid="1335543792857823917">"Ne, díky"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-da/strings.xml b/packages/CompanionDeviceManager/res/values-da/strings.xml
index c87181a..a6720fc 100644
--- a/packages/CompanionDeviceManager/res/values-da/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-da/strings.xml
@@ -17,16 +17,10 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="4470785958457506021">"Medfølgende enhedshåndtering"</string>
-    <!-- no translation found for chooser_title (2262294130493605839) -->
-    <skip />
-    <!-- no translation found for profile_name_generic (6851028682723034988) -->
-    <skip />
-    <!-- no translation found for confirmation_title (4751119145078041732) -->
-    <skip />
-    <!-- no translation found for profile_summary (3167701603666642104) -->
-    <skip />
-    <!-- no translation found for consent_yes (4055438216605487056) -->
-    <skip />
-    <!-- no translation found for consent_no (1335543792857823917) -->
-    <skip />
+    <string name="chooser_title" msgid="2262294130493605839">"Vælg den enhed (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>), som skal administreres af &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_name_generic" msgid="6851028682723034988">"enhed"</string>
+    <string name="confirmation_title" msgid="4751119145078041732">"Angiv &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; til administration af: <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="3167701603666642104">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> er nødvendig for at administrere: <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>. <xliff:g id="APP_NAME2">%3$s</xliff:g> får adgang til <xliff:g id="PERMISSIONS">%4$s</xliff:g>, mens <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> er forbundet."</string>
+    <string name="consent_yes" msgid="4055438216605487056">"Ja"</string>
+    <string name="consent_no" msgid="1335543792857823917">"Nej tak"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-de/strings.xml b/packages/CompanionDeviceManager/res/values-de/strings.xml
index 239243a..eb2631f 100644
--- a/packages/CompanionDeviceManager/res/values-de/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-de/strings.xml
@@ -17,16 +17,10 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="4470785958457506021">"Begleitgerät-Manager"</string>
-    <!-- no translation found for chooser_title (2262294130493605839) -->
-    <skip />
-    <!-- no translation found for profile_name_generic (6851028682723034988) -->
-    <skip />
-    <!-- no translation found for confirmation_title (4751119145078041732) -->
-    <skip />
-    <!-- no translation found for profile_summary (3167701603666642104) -->
-    <skip />
-    <!-- no translation found for consent_yes (4055438216605487056) -->
-    <skip />
-    <!-- no translation found for consent_no (1335543792857823917) -->
-    <skip />
+    <string name="chooser_title" msgid="2262294130493605839">"Gerät (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>) auswählen, das von &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; verwaltet werden soll"</string>
+    <string name="profile_name_generic" msgid="6851028682723034988">"Gerät"</string>
+    <string name="confirmation_title" msgid="4751119145078041732">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; zum Verwalten deines Geräts (<xliff:g id="PROFILE_NAME">%2$s</xliff:g>) festlegen – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="3167701603666642104">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> ist erforderlich, um dein Gerät (<xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>) zu verwalten. <xliff:g id="APP_NAME2">%3$s</xliff:g> erhält Zugriff auf <xliff:g id="PERMISSIONS">%4$s</xliff:g>, während eine Verbindung mit <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> besteht."</string>
+    <string name="consent_yes" msgid="4055438216605487056">"Ja"</string>
+    <string name="consent_no" msgid="1335543792857823917">"Nein danke"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-el/strings.xml b/packages/CompanionDeviceManager/res/values-el/strings.xml
index 0738161..cb31866 100644
--- a/packages/CompanionDeviceManager/res/values-el/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-el/strings.xml
@@ -17,16 +17,10 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="4470785958457506021">"Διαχείριση συνοδευτικής εφαρμογής"</string>
-    <!-- no translation found for chooser_title (2262294130493605839) -->
-    <skip />
-    <!-- no translation found for profile_name_generic (6851028682723034988) -->
-    <skip />
-    <!-- no translation found for confirmation_title (4751119145078041732) -->
-    <skip />
-    <!-- no translation found for profile_summary (3167701603666642104) -->
-    <skip />
-    <!-- no translation found for consent_yes (4055438216605487056) -->
-    <skip />
-    <!-- no translation found for consent_no (1335543792857823917) -->
-    <skip />
+    <string name="chooser_title" msgid="2262294130493605839">"Επιλέξτε ένα προφίλ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> για διαχείριση από την εφαρμογή &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_name_generic" msgid="6851028682723034988">"συσκευή"</string>
+    <string name="confirmation_title" msgid="4751119145078041732">"Ορίστε μια εφαρμογή &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; για διαχείριση του προφίλ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="3167701603666642104">"Η εφαρμογή <xliff:g id="APP_NAME_0">%1$s</xliff:g> απαιτείται για τη διαχείριση του προφίλ <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>. Η εφαρμογή <xliff:g id="APP_NAME2">%3$s</xliff:g> θα έχει πρόσβαση στις άδειες <xliff:g id="PERMISSIONS">%4$s</xliff:g> ενώ είναι συνδεδεμένο το προφίλ <xliff:g id="PROFILE_NAME2">%5$s</xliff:g>."</string>
+    <string name="consent_yes" msgid="4055438216605487056">"Ναι"</string>
+    <string name="consent_no" msgid="1335543792857823917">"Όχι, ευχαριστώ"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml b/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml
index 579639f..63097dc 100644
--- a/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-es-rUS/strings.xml
@@ -17,16 +17,10 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="4470785958457506021">"Administrador de dispositivo complementario"</string>
-    <!-- no translation found for chooser_title (2262294130493605839) -->
-    <skip />
-    <!-- no translation found for profile_name_generic (6851028682723034988) -->
-    <skip />
-    <!-- no translation found for confirmation_title (4751119145078041732) -->
-    <skip />
-    <!-- no translation found for profile_summary (3167701603666642104) -->
-    <skip />
-    <!-- no translation found for consent_yes (4055438216605487056) -->
-    <skip />
-    <!-- no translation found for consent_no (1335543792857823917) -->
-    <skip />
+    <string name="chooser_title" msgid="2262294130493605839">"Elige un <xliff:g id="PROFILE_NAME">%1$s</xliff:g> para que &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; lo administre"</string>
+    <string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string>
+    <string name="confirmation_title" msgid="4751119145078041732">"Configura &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; para administrar <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="3167701603666642104">"Se requiere <xliff:g id="APP_NAME_0">%1$s</xliff:g> para administrar tu <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>. <xliff:g id="APP_NAME2">%3$s</xliff:g> accederá a <xliff:g id="PERMISSIONS">%4$s</xliff:g> mientras tu <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> esté conectado."</string>
+    <string name="consent_yes" msgid="4055438216605487056">"Sí"</string>
+    <string name="consent_no" msgid="1335543792857823917">"No, gracias"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-es/strings.xml b/packages/CompanionDeviceManager/res/values-es/strings.xml
index 4192f5e..784c30d 100644
--- a/packages/CompanionDeviceManager/res/values-es/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-es/strings.xml
@@ -17,16 +17,10 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="4470785958457506021">"Gestor de dispositivos complementario"</string>
-    <!-- no translation found for chooser_title (2262294130493605839) -->
-    <skip />
-    <!-- no translation found for profile_name_generic (6851028682723034988) -->
-    <skip />
-    <!-- no translation found for confirmation_title (4751119145078041732) -->
-    <skip />
-    <!-- no translation found for profile_summary (3167701603666642104) -->
-    <skip />
-    <!-- no translation found for consent_yes (4055438216605487056) -->
-    <skip />
-    <!-- no translation found for consent_no (1335543792857823917) -->
-    <skip />
+    <string name="chooser_title" msgid="2262294130493605839">"Elige un <xliff:g id="PROFILE_NAME">%1$s</xliff:g> para gestionarlo con &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string>
+    <string name="confirmation_title" msgid="4751119145078041732">"Haz que &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; gestione tu <xliff:g id="PROFILE_NAME">%2$s</xliff:g> &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="3167701603666642104">"Se necesita <xliff:g id="APP_NAME_0">%1$s</xliff:g> para gestionar tu <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>. Mientras tu <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> esté conectado, <xliff:g id="APP_NAME2">%3$s</xliff:g> tendrá acceso a lo siguiente: <xliff:g id="PERMISSIONS">%4$s</xliff:g>."</string>
+    <string name="consent_yes" msgid="4055438216605487056">"Sí"</string>
+    <string name="consent_no" msgid="1335543792857823917">"No, gracias"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-et/strings.xml b/packages/CompanionDeviceManager/res/values-et/strings.xml
index 5432ab3..b505870 100644
--- a/packages/CompanionDeviceManager/res/values-et/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-et/strings.xml
@@ -17,16 +17,10 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="4470785958457506021">"Kaasseadme haldur"</string>
-    <!-- no translation found for chooser_title (2262294130493605839) -->
-    <skip />
-    <!-- no translation found for profile_name_generic (6851028682723034988) -->
-    <skip />
-    <!-- no translation found for confirmation_title (4751119145078041732) -->
-    <skip />
-    <!-- no translation found for profile_summary (3167701603666642104) -->
-    <skip />
-    <!-- no translation found for consent_yes (4055438216605487056) -->
-    <skip />
-    <!-- no translation found for consent_no (1335543792857823917) -->
-    <skip />
+    <string name="chooser_title" msgid="2262294130493605839">"Valige seade <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, mida haldab rakendus &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_name_generic" msgid="6851028682723034988">"seade"</string>
+    <string name="confirmation_title" msgid="4751119145078041732">"Määrake rakendus &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; haldama teie seadet <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="3167701603666642104">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> on vajalik teie seadme <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g> haldamiseks. <xliff:g id="APP_NAME2">%3$s</xliff:g> saab juurdepääsuload (<xliff:g id="PERMISSIONS">%4$s</xliff:g>), kui seade <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> on ühendatud."</string>
+    <string name="consent_yes" msgid="4055438216605487056">"Jah"</string>
+    <string name="consent_no" msgid="1335543792857823917">"Tänan, ei"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-eu/strings.xml b/packages/CompanionDeviceManager/res/values-eu/strings.xml
index 6e9729b..826556d 100644
--- a/packages/CompanionDeviceManager/res/values-eu/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-eu/strings.xml
@@ -17,16 +17,10 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="4470785958457506021">"Gailu osagarriaren kudeatzailea"</string>
-    <!-- no translation found for chooser_title (2262294130493605839) -->
-    <skip />
-    <!-- no translation found for profile_name_generic (6851028682723034988) -->
-    <skip />
-    <!-- no translation found for confirmation_title (4751119145078041732) -->
-    <skip />
-    <!-- no translation found for profile_summary (3167701603666642104) -->
-    <skip />
-    <!-- no translation found for consent_yes (4055438216605487056) -->
-    <skip />
-    <!-- no translation found for consent_no (1335543792857823917) -->
-    <skip />
+    <string name="chooser_title" msgid="2262294130493605839">"Aukeratu &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; aplikazioak kudeatu beharreko <xliff:g id="PROFILE_NAME">%1$s</xliff:g>"</string>
+    <string name="profile_name_generic" msgid="6851028682723034988">"gailua"</string>
+    <string name="confirmation_title" msgid="4751119145078041732">"Konfiguratu &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; <xliff:g id="PROFILE_NAME">%2$s</xliff:g> (&lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;) kudea dezan"</string>
+    <string name="profile_summary" msgid="3167701603666642104">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> erabili behar duzu <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g> kudeatzeko. <xliff:g id="APP_NAME2">%3$s</xliff:g> aplikazioak <xliff:g id="PERMISSIONS">%4$s</xliff:g> atzitu ahalko ditu <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> konektatuta dagoen bitartean."</string>
+    <string name="consent_yes" msgid="4055438216605487056">"Bai"</string>
+    <string name="consent_no" msgid="1335543792857823917">"Ez"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-fa/strings.xml b/packages/CompanionDeviceManager/res/values-fa/strings.xml
index c1d951e..66eafc3 100644
--- a/packages/CompanionDeviceManager/res/values-fa/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-fa/strings.xml
@@ -17,16 +17,10 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="4470785958457506021">"مدیر دستگاه مرتبط"</string>
-    <!-- no translation found for chooser_title (2262294130493605839) -->
-    <skip />
-    <!-- no translation found for profile_name_generic (6851028682723034988) -->
-    <skip />
-    <!-- no translation found for confirmation_title (4751119145078041732) -->
-    <skip />
-    <!-- no translation found for profile_summary (3167701603666642104) -->
-    <skip />
-    <!-- no translation found for consent_yes (4055438216605487056) -->
-    <skip />
-    <!-- no translation found for consent_no (1335543792857823917) -->
-    <skip />
+    <string name="chooser_title" msgid="2262294130493605839">"‏انتخاب <xliff:g id="PROFILE_NAME">%1$s</xliff:g> برای مدیریت کردن با &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>‏&lt;/strong&gt;"</string>
+    <string name="profile_name_generic" msgid="6851028682723034988">"دستگاه"</string>
+    <string name="confirmation_title" msgid="4751119145078041732">"‏تنظیم &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; برای مدیریت کردن <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>‏&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="3167701603666642104">"برای مدیریت کردن <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g> به <xliff:g id="APP_NAME_0">%1$s</xliff:g> نیاز دارید. وقتی <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> متصل باشد، <xliff:g id="APP_NAME2">%3$s</xliff:g> به <xliff:g id="PERMISSIONS">%4$s</xliff:g> دسترسی پیدا خواهد کرد."</string>
+    <string name="consent_yes" msgid="4055438216605487056">"بله"</string>
+    <string name="consent_no" msgid="1335543792857823917">"نه متشکرم"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-fi/strings.xml b/packages/CompanionDeviceManager/res/values-fi/strings.xml
index eb9204a..d4a20d9 100644
--- a/packages/CompanionDeviceManager/res/values-fi/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-fi/strings.xml
@@ -17,16 +17,10 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string>
-    <!-- no translation found for chooser_title (2262294130493605839) -->
-    <skip />
-    <!-- no translation found for profile_name_generic (6851028682723034988) -->
-    <skip />
-    <!-- no translation found for confirmation_title (4751119145078041732) -->
-    <skip />
-    <!-- no translation found for profile_summary (3167701603666642104) -->
-    <skip />
-    <!-- no translation found for consent_yes (4055438216605487056) -->
-    <skip />
-    <!-- no translation found for consent_no (1335543792857823917) -->
-    <skip />
+    <string name="chooser_title" msgid="2262294130493605839">"Valitse <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, jota &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; hallinnoi"</string>
+    <string name="profile_name_generic" msgid="6851028682723034988">"laite"</string>
+    <string name="confirmation_title" msgid="4751119145078041732">"Aseta &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; profiilin (<xliff:g id="PROFILE_NAME">%2$s</xliff:g>) hallinnoijaksi – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="3167701603666642104">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> tarvitaan profiilin (<xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>) hallinnointiin. <xliff:g id="APP_NAME2">%3$s</xliff:g> saa käyttöluvat (<xliff:g id="PERMISSIONS">%4$s</xliff:g>), kun <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> on yhdistetty."</string>
+    <string name="consent_yes" msgid="4055438216605487056">"Kyllä"</string>
+    <string name="consent_no" msgid="1335543792857823917">"Ei kiitos"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml b/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml
index 3c8fdd1..e91ccf4 100644
--- a/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-fr-rCA/strings.xml
@@ -17,16 +17,10 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="4470785958457506021">"Gestionnaire d\'appareil compagnon"</string>
-    <!-- no translation found for chooser_title (2262294130493605839) -->
-    <skip />
-    <!-- no translation found for profile_name_generic (6851028682723034988) -->
-    <skip />
-    <!-- no translation found for confirmation_title (4751119145078041732) -->
-    <skip />
-    <!-- no translation found for profile_summary (3167701603666642104) -->
-    <skip />
-    <!-- no translation found for consent_yes (4055438216605487056) -->
-    <skip />
-    <!-- no translation found for consent_no (1335543792857823917) -->
-    <skip />
+    <string name="chooser_title" msgid="2262294130493605839">"Choisissez un <xliff:g id="PROFILE_NAME">%1$s</xliff:g> qui sera géré par &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_name_generic" msgid="6851028682723034988">"appareil"</string>
+    <string name="confirmation_title" msgid="4751119145078041732">"Utiliser &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; pour gérer votre <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="3167701603666642104">"L\'application <xliff:g id="APP_NAME_0">%1$s</xliff:g> est requise pour gérer votre <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>. <xliff:g id="APP_NAME2">%3$s</xliff:g> aura accès à <xliff:g id="PERMISSIONS">%4$s</xliff:g> lorsque <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> est connecté."</string>
+    <string name="consent_yes" msgid="4055438216605487056">"Oui"</string>
+    <string name="consent_no" msgid="1335543792857823917">"Non merci"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-fr/strings.xml b/packages/CompanionDeviceManager/res/values-fr/strings.xml
index fc0e4da..756727a 100644
--- a/packages/CompanionDeviceManager/res/values-fr/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-fr/strings.xml
@@ -17,16 +17,10 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="4470785958457506021">"Gestionnaire d\'appareils associés"</string>
-    <!-- no translation found for chooser_title (2262294130493605839) -->
-    <skip />
-    <!-- no translation found for profile_name_generic (6851028682723034988) -->
-    <skip />
-    <!-- no translation found for confirmation_title (4751119145078041732) -->
-    <skip />
-    <!-- no translation found for profile_summary (3167701603666642104) -->
-    <skip />
-    <!-- no translation found for consent_yes (4055438216605487056) -->
-    <skip />
-    <!-- no translation found for consent_no (1335543792857823917) -->
-    <skip />
+    <string name="chooser_title" msgid="2262294130493605839">"Sélectionner le/la <xliff:g id="PROFILE_NAME">%1$s</xliff:g> qui sera géré(e) par &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_name_generic" msgid="6851028682723034988">"appareil"</string>
+    <string name="confirmation_title" msgid="4751119145078041732">"Définir &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; pour la gestion de votre <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="3167701603666642104">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> est requis pour gérer votre <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>. <xliff:g id="APP_NAME2">%3$s</xliff:g> aura accès aux <xliff:g id="PERMISSIONS">%4$s</xliff:g> lorsque le/la <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> sera connecté(e)."</string>
+    <string name="consent_yes" msgid="4055438216605487056">"Oui"</string>
+    <string name="consent_no" msgid="1335543792857823917">"Non, merci"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-gl/strings.xml b/packages/CompanionDeviceManager/res/values-gl/strings.xml
index 0a4e7fe..7849e42 100644
--- a/packages/CompanionDeviceManager/res/values-gl/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-gl/strings.xml
@@ -17,16 +17,10 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="4470785958457506021">"Xestor de dispositivos complementarios"</string>
-    <!-- no translation found for chooser_title (2262294130493605839) -->
-    <skip />
-    <!-- no translation found for profile_name_generic (6851028682723034988) -->
-    <skip />
-    <!-- no translation found for confirmation_title (4751119145078041732) -->
-    <skip />
-    <!-- no translation found for profile_summary (3167701603666642104) -->
-    <skip />
-    <!-- no translation found for consent_yes (4055438216605487056) -->
-    <skip />
-    <!-- no translation found for consent_no (1335543792857823917) -->
-    <skip />
+    <string name="chooser_title" msgid="2262294130493605839">"Escolle un perfil (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>) para que o xestione a aplicación &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string>
+    <string name="confirmation_title" msgid="4751119145078041732">"Define a aplicación &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; para a xestión do teu perfil (<xliff:g id="PROFILE_NAME">%2$s</xliff:g>): &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="3167701603666642104">"Necesítase a aplicación <xliff:g id="APP_NAME_0">%1$s</xliff:g> para xestionar o teu perfil (<xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>). <xliff:g id="APP_NAME2">%3$s</xliff:g> terá acceso a varios permisos (<xliff:g id="PERMISSIONS">%4$s</xliff:g>) mentres o perfil (<xliff:g id="PROFILE_NAME2">%5$s</xliff:g>) estea conectado."</string>
+    <string name="consent_yes" msgid="4055438216605487056">"Si"</string>
+    <string name="consent_no" msgid="1335543792857823917">"Non, grazas"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-gu/strings.xml b/packages/CompanionDeviceManager/res/values-gu/strings.xml
index bee2e77..1f1edd1 100644
--- a/packages/CompanionDeviceManager/res/values-gu/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-gu/strings.xml
@@ -17,16 +17,10 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="4470785958457506021">"કમ્પેનિયન ડિવાઇસ મેનેજર"</string>
-    <!-- no translation found for chooser_title (2262294130493605839) -->
-    <skip />
-    <!-- no translation found for profile_name_generic (6851028682723034988) -->
-    <skip />
-    <!-- no translation found for confirmation_title (4751119145078041732) -->
-    <skip />
-    <!-- no translation found for profile_summary (3167701603666642104) -->
-    <skip />
-    <!-- no translation found for consent_yes (4055438216605487056) -->
-    <skip />
-    <!-- no translation found for consent_no (1335543792857823917) -->
-    <skip />
+    <string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; દ્વારા મેનેજ કરવા માટે કોઈ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> પસંદ કરો"</string>
+    <string name="profile_name_generic" msgid="6851028682723034988">"ડિવાઇસ"</string>
+    <string name="confirmation_title" msgid="4751119145078041732">"તમારા <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;ને મેનેજ કરવા માટે &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; સેટ કરો"</string>
+    <string name="profile_summary" msgid="3167701603666642104">"તમારા <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>ને મેનેજ કરવા માટે <xliff:g id="APP_NAME_0">%1$s</xliff:g> જરૂરી છે. <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> જ્યારે કનેક્ટેડ હોય ત્યારે <xliff:g id="APP_NAME2">%3$s</xliff:g>ને <xliff:g id="PERMISSIONS">%4$s</xliff:g>નો ઍક્સેસ મળશે."</string>
+    <string name="consent_yes" msgid="4055438216605487056">"હા"</string>
+    <string name="consent_no" msgid="1335543792857823917">"ના, આભાર"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-hi/strings.xml b/packages/CompanionDeviceManager/res/values-hi/strings.xml
index 46bb4ce..6e280b5 100644
--- a/packages/CompanionDeviceManager/res/values-hi/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-hi/strings.xml
@@ -17,16 +17,10 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="4470785958457506021">"सहयोगी डिवाइस मैनेजर"</string>
-    <!-- no translation found for chooser_title (2262294130493605839) -->
-    <skip />
-    <!-- no translation found for profile_name_generic (6851028682723034988) -->
-    <skip />
-    <!-- no translation found for confirmation_title (4751119145078041732) -->
-    <skip />
-    <!-- no translation found for profile_summary (3167701603666642104) -->
-    <skip />
-    <!-- no translation found for consent_yes (4055438216605487056) -->
-    <skip />
-    <!-- no translation found for consent_no (1335543792857823917) -->
-    <skip />
+    <string name="chooser_title" msgid="2262294130493605839">"कोई <xliff:g id="PROFILE_NAME">%1$s</xliff:g> चुनें, ताकि उसे &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; की मदद से प्रबंधित किया जा सके"</string>
+    <string name="profile_name_generic" msgid="6851028682723034988">"डिवाइस"</string>
+    <string name="confirmation_title" msgid="4751119145078041732">"अपने <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; को प्रबंधित करने के लिए, &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; को सेट करें"</string>
+    <string name="profile_summary" msgid="3167701603666642104">"आपके <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g> को प्रबंधित करने के लिए, <xliff:g id="APP_NAME_0">%1$s</xliff:g> की ज़रूरत है. <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> के कनेक्ट होने पर, <xliff:g id="APP_NAME2">%3$s</xliff:g> को <xliff:g id="PERMISSIONS">%4$s</xliff:g> का ऐक्सेस मिल जाएगा."</string>
+    <string name="consent_yes" msgid="4055438216605487056">"हां"</string>
+    <string name="consent_no" msgid="1335543792857823917">"नहीं, रहने दें"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-hy/strings.xml b/packages/CompanionDeviceManager/res/values-hy/strings.xml
index eb9204a..b0037aa 100644
--- a/packages/CompanionDeviceManager/res/values-hy/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-hy/strings.xml
@@ -17,16 +17,10 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string>
-    <!-- no translation found for chooser_title (2262294130493605839) -->
-    <skip />
-    <!-- no translation found for profile_name_generic (6851028682723034988) -->
-    <skip />
-    <!-- no translation found for confirmation_title (4751119145078041732) -->
-    <skip />
-    <!-- no translation found for profile_summary (3167701603666642104) -->
-    <skip />
-    <!-- no translation found for consent_yes (4055438216605487056) -->
-    <skip />
-    <!-- no translation found for consent_no (1335543792857823917) -->
-    <skip />
+    <string name="chooser_title" msgid="2262294130493605839">"Ընտրեք <xliff:g id="PROFILE_NAME">%1$s</xliff:g>ը, որը պետք է կառավարվի &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; հավելվածի կողմից"</string>
+    <string name="profile_name_generic" msgid="6851028682723034988">"սարք"</string>
+    <string name="confirmation_title" msgid="4751119145078041732">"Ընտրեք &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; հավելվածը որպես <xliff:g id="PROFILE_NAME">%2$s</xliff:g>ի կառավարիչ․ &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="3167701603666642104">"Ձեր <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>ը կառավարելու համար անհրաժեշտ է <xliff:g id="APP_NAME_0">%1$s</xliff:g> հավելվածը։ Երբ <xliff:g id="PROFILE_NAME2">%5$s</xliff:g>ը միացված լինի, <xliff:g id="APP_NAME2">%3$s</xliff:g> հավելվածին հասանելի կլինեն՝ <xliff:g id="PERMISSIONS">%4$s</xliff:g>։"</string>
+    <string name="consent_yes" msgid="4055438216605487056">"Այո"</string>
+    <string name="consent_no" msgid="1335543792857823917">"Ոչ, շնորհակալություն"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-in/strings.xml b/packages/CompanionDeviceManager/res/values-in/strings.xml
index 7115961..cc05490 100644
--- a/packages/CompanionDeviceManager/res/values-in/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-in/strings.xml
@@ -17,16 +17,10 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="4470785958457506021">"Pengelola Perangkat Pendamping"</string>
-    <!-- no translation found for chooser_title (2262294130493605839) -->
-    <skip />
-    <!-- no translation found for profile_name_generic (6851028682723034988) -->
-    <skip />
-    <!-- no translation found for confirmation_title (4751119145078041732) -->
-    <skip />
-    <!-- no translation found for profile_summary (3167701603666642104) -->
-    <skip />
-    <!-- no translation found for consent_yes (4055438216605487056) -->
-    <skip />
-    <!-- no translation found for consent_no (1335543792857823917) -->
-    <skip />
+    <string name="chooser_title" msgid="2262294130493605839">"Pilih <xliff:g id="PROFILE_NAME">%1$s</xliff:g> untuk dikelola oleh &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_name_generic" msgid="6851028682723034988">"perangkat"</string>
+    <string name="confirmation_title" msgid="4751119145078041732">"Tetapkan &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; untuk mengelola <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="3167701603666642104">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> diperlukan untuk mengelola <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>. <xliff:g id="APP_NAME2">%3$s</xliff:g> akan mendapatkan akses ke <xliff:g id="PERMISSIONS">%4$s</xliff:g> saat <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> terhubung."</string>
+    <string name="consent_yes" msgid="4055438216605487056">"Ya"</string>
+    <string name="consent_no" msgid="1335543792857823917">"Tidak"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-is/strings.xml b/packages/CompanionDeviceManager/res/values-is/strings.xml
index 615ce83..d3ada74 100644
--- a/packages/CompanionDeviceManager/res/values-is/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-is/strings.xml
@@ -17,16 +17,10 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="4470785958457506021">"Stjórnun fylgdartækja"</string>
-    <!-- no translation found for chooser_title (2262294130493605839) -->
-    <skip />
-    <!-- no translation found for profile_name_generic (6851028682723034988) -->
-    <skip />
-    <!-- no translation found for confirmation_title (4751119145078041732) -->
-    <skip />
-    <!-- no translation found for profile_summary (3167701603666642104) -->
-    <skip />
-    <!-- no translation found for consent_yes (4055438216605487056) -->
-    <skip />
-    <!-- no translation found for consent_no (1335543792857823917) -->
-    <skip />
+    <string name="chooser_title" msgid="2262294130493605839">"Velja <xliff:g id="PROFILE_NAME">%1$s</xliff:g> sem &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; á að stjórna"</string>
+    <string name="profile_name_generic" msgid="6851028682723034988">"tæki"</string>
+    <string name="confirmation_title" msgid="4751119145078041732">"Veita &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; stjórn á <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="3167701603666642104">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> er krafist til að stjórna <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>. <xliff:g id="APP_NAME2">%3$s</xliff:g> fær aðgang að <xliff:g id="PERMISSIONS">%4$s</xliff:g> þegar <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> er tengt."</string>
+    <string name="consent_yes" msgid="4055438216605487056">"Já"</string>
+    <string name="consent_no" msgid="1335543792857823917">"Nei, takk"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-iw/strings.xml b/packages/CompanionDeviceManager/res/values-iw/strings.xml
index 05154ee..ae9d6f4 100644
--- a/packages/CompanionDeviceManager/res/values-iw/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-iw/strings.xml
@@ -17,16 +17,10 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="4470785958457506021">"ניהול מכשיר מותאם"</string>
-    <!-- no translation found for chooser_title (2262294130493605839) -->
-    <skip />
-    <!-- no translation found for profile_name_generic (6851028682723034988) -->
-    <skip />
-    <!-- no translation found for confirmation_title (4751119145078041732) -->
-    <skip />
-    <!-- no translation found for profile_summary (3167701603666642104) -->
-    <skip />
-    <!-- no translation found for consent_yes (4055438216605487056) -->
-    <skip />
-    <!-- no translation found for consent_no (1335543792857823917) -->
-    <skip />
+    <string name="chooser_title" msgid="2262294130493605839">"‏בחירה של <xliff:g id="PROFILE_NAME">%1$s</xliff:g> לניהול באמצעות &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_name_generic" msgid="6851028682723034988">"מכשיר"</string>
+    <string name="confirmation_title" msgid="4751119145078041732">"‏הגדרה של &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; לניהול <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="3167701603666642104">"האפליקציה <xliff:g id="APP_NAME_0">%1$s</xliff:g> נדרשת לניהול של <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>. האפליקציה <xliff:g id="APP_NAME2">%3$s</xliff:g> תקבל גישה אל <xliff:g id="PERMISSIONS">%4$s</xliff:g> כאשר <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> מחובר."</string>
+    <string name="consent_yes" msgid="4055438216605487056">"כן"</string>
+    <string name="consent_no" msgid="1335543792857823917">"לא תודה"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-ja/strings.xml b/packages/CompanionDeviceManager/res/values-ja/strings.xml
index 76fd4b6..3fb0140 100644
--- a/packages/CompanionDeviceManager/res/values-ja/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ja/strings.xml
@@ -17,16 +17,10 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="4470785958457506021">"コンパニオン デバイス マネージャ"</string>
-    <!-- no translation found for chooser_title (2262294130493605839) -->
-    <skip />
-    <!-- no translation found for profile_name_generic (6851028682723034988) -->
-    <skip />
-    <!-- no translation found for confirmation_title (4751119145078041732) -->
-    <skip />
-    <!-- no translation found for profile_summary (3167701603666642104) -->
-    <skip />
-    <!-- no translation found for consent_yes (4055438216605487056) -->
-    <skip />
-    <!-- no translation found for consent_no (1335543792857823917) -->
-    <skip />
+    <string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; の管理対象となる <xliff:g id="PROFILE_NAME">%1$s</xliff:g> の選択"</string>
+    <string name="profile_name_generic" msgid="6851028682723034988">"デバイス"</string>
+    <string name="confirmation_title" msgid="4751119145078041732">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; で <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; を管理するよう設定する"</string>
+    <string name="profile_summary" msgid="3167701603666642104">"<xliff:g id="PROFILE_NAME_1">%2$s</xliff:g> を管理するために <xliff:g id="APP_NAME_0">%1$s</xliff:g> が必要です。<xliff:g id="PROFILE_NAME2">%5$s</xliff:g> の接続中に、<xliff:g id="APP_NAME2">%3$s</xliff:g> が <xliff:g id="PERMISSIONS">%4$s</xliff:g> にアクセスします。"</string>
+    <string name="consent_yes" msgid="4055438216605487056">"はい"</string>
+    <string name="consent_no" msgid="1335543792857823917">"いいえ"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-ka/strings.xml b/packages/CompanionDeviceManager/res/values-ka/strings.xml
index df70f08..5b36106 100644
--- a/packages/CompanionDeviceManager/res/values-ka/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ka/strings.xml
@@ -17,16 +17,10 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="4470785958457506021">"კომპანიონი მოწყობილობების მენეჯერი"</string>
-    <!-- no translation found for chooser_title (2262294130493605839) -->
-    <skip />
-    <!-- no translation found for profile_name_generic (6851028682723034988) -->
-    <skip />
-    <!-- no translation found for confirmation_title (4751119145078041732) -->
-    <skip />
-    <!-- no translation found for profile_summary (3167701603666642104) -->
-    <skip />
-    <!-- no translation found for consent_yes (4055438216605487056) -->
-    <skip />
-    <!-- no translation found for consent_no (1335543792857823917) -->
-    <skip />
+    <string name="chooser_title" msgid="2262294130493605839">"აირჩიეთ <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, რომელიც უნდა მართოს &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;-მა"</string>
+    <string name="profile_name_generic" msgid="6851028682723034988">"მოწყობილობა"</string>
+    <string name="confirmation_title" msgid="4751119145078041732">"დააყენეთ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;, რომ მართოს თქვენი <xliff:g id="PROFILE_NAME">%2$s</xliff:g> — &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="3167701603666642104">"თქვენი <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>-ის სამართავად საჭიროა <xliff:g id="APP_NAME_0">%1$s</xliff:g>. <xliff:g id="APP_NAME2">%3$s</xliff:g> მიიღებს წვდომას <xliff:g id="PERMISSIONS">%4$s</xliff:g>-ზე, სანამ <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> დაკავშირებული იქნება."</string>
+    <string name="consent_yes" msgid="4055438216605487056">"დიახ"</string>
+    <string name="consent_no" msgid="1335543792857823917">"არა, გმადლობთ"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-kk/strings.xml b/packages/CompanionDeviceManager/res/values-kk/strings.xml
index eb9204a..6ff3f83 100644
--- a/packages/CompanionDeviceManager/res/values-kk/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-kk/strings.xml
@@ -17,16 +17,10 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string>
-    <!-- no translation found for chooser_title (2262294130493605839) -->
-    <skip />
-    <!-- no translation found for profile_name_generic (6851028682723034988) -->
-    <skip />
-    <!-- no translation found for confirmation_title (4751119145078041732) -->
-    <skip />
-    <!-- no translation found for profile_summary (3167701603666642104) -->
-    <skip />
-    <!-- no translation found for consent_yes (4055438216605487056) -->
-    <skip />
-    <!-- no translation found for consent_no (1335543792857823917) -->
-    <skip />
+    <string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; арқылы басқарылатын <xliff:g id="PROFILE_NAME">%1$s</xliff:g> құрылғысын таңдаңыз"</string>
+    <string name="profile_name_generic" msgid="6851028682723034988">"құрылғы"</string>
+    <string name="confirmation_title" msgid="4751119145078041732">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; қолданбасына &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; (<xliff:g id="PROFILE_NAME">%2$s</xliff:g>) құрылғысын басқаруға рұқсат беру"</string>
+    <string name="profile_summary" msgid="3167701603666642104">"<xliff:g id="PROFILE_NAME_1">%2$s</xliff:g> құрылғысын басқару үшін <xliff:g id="APP_NAME_0">%1$s</xliff:g> қолданбасы керек. <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> құрылғысы жалғанған кезде<xliff:g id="APP_NAME2">%3$s</xliff:g> қолданбасы мына параметрлерді пайдалана алады: <xliff:g id="PERMISSIONS">%4$s</xliff:g>."</string>
+    <string name="consent_yes" msgid="4055438216605487056">"Иә"</string>
+    <string name="consent_no" msgid="1335543792857823917">"Жоқ, рақмет"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-km/strings.xml b/packages/CompanionDeviceManager/res/values-km/strings.xml
index 8c1de45..cdcebad 100644
--- a/packages/CompanionDeviceManager/res/values-km/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-km/strings.xml
@@ -17,16 +17,10 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="4470785958457506021">"កម្មវិធី​គ្រប់​គ្រង​ឧបករណ៍ដៃគូ"</string>
-    <!-- no translation found for chooser_title (2262294130493605839) -->
-    <skip />
-    <!-- no translation found for profile_name_generic (6851028682723034988) -->
-    <skip />
-    <!-- no translation found for confirmation_title (4751119145078041732) -->
-    <skip />
-    <!-- no translation found for profile_summary (3167701603666642104) -->
-    <skip />
-    <!-- no translation found for consent_yes (4055438216605487056) -->
-    <skip />
-    <!-- no translation found for consent_no (1335543792857823917) -->
-    <skip />
+    <string name="chooser_title" msgid="2262294130493605839">"ជ្រើសរើស <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ដើម្បីឱ្យស្ថិតក្រោម​ការគ្រប់គ្រងរបស់ &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_name_generic" msgid="6851028682723034988">"ឧបករណ៍"</string>
+    <string name="confirmation_title" msgid="4751119145078041732">"កំណត់ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ដើម្បីគ្រប់គ្រង <xliff:g id="PROFILE_NAME">%2$s</xliff:g> របស់អ្នក - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="3167701603666642104">"ចាំបាច់ត្រូវមាន <xliff:g id="APP_NAME_0">%1$s</xliff:g> ដើម្បីគ្រប់គ្រង <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g> របស់អ្នក។ <xliff:g id="APP_NAME2">%3$s</xliff:g> នឹងអាចចូលប្រើ <xliff:g id="PERMISSIONS">%4$s</xliff:g> នៅពេលភ្ជាប់ <xliff:g id="PROFILE_NAME2">%5$s</xliff:g>។"</string>
+    <string name="consent_yes" msgid="4055438216605487056">"បាទ/ចាស"</string>
+    <string name="consent_no" msgid="1335543792857823917">"ទេ អរគុណ"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-kn/strings.xml b/packages/CompanionDeviceManager/res/values-kn/strings.xml
index 1c3d07b..47cf76c 100644
--- a/packages/CompanionDeviceManager/res/values-kn/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-kn/strings.xml
@@ -17,16 +17,10 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="4470785958457506021">"ಕಂಪ್ಯಾನಿಯನ್ ಸಾಧನ ನಿರ್ವಾಹಕರು"</string>
-    <!-- no translation found for chooser_title (2262294130493605839) -->
-    <skip />
-    <!-- no translation found for profile_name_generic (6851028682723034988) -->
-    <skip />
-    <!-- no translation found for confirmation_title (4751119145078041732) -->
-    <skip />
-    <!-- no translation found for profile_summary (3167701603666642104) -->
-    <skip />
-    <!-- no translation found for consent_yes (4055438216605487056) -->
-    <skip />
-    <!-- no translation found for consent_no (1335543792857823917) -->
-    <skip />
+    <string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; ಮೂಲಕ ನಿರ್ವಹಿಸಬೇಕಾದ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ಅನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string>
+    <string name="profile_name_generic" msgid="6851028682723034988">"ಸಾಧನ"</string>
+    <string name="confirmation_title" msgid="4751119145078041732">"ನಿಮ್ಮ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; ಅನ್ನು ನಿರ್ವಹಿಸಲು, &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ಅನ್ನು ನಿರ್ವಹಿಸಿ"</string>
+    <string name="profile_summary" msgid="3167701603666642104">"<xliff:g id="PROFILE_NAME_1">%2$s</xliff:g> ಅನ್ನು ನಿರ್ವಹಿಸಲು, <xliff:g id="APP_NAME_0">%1$s</xliff:g> ಅಗತ್ಯವಿದೆ. <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> ಕನೆಕ್ಟ್ ಆದಾಗ, <xliff:g id="PERMISSIONS">%4$s</xliff:g> ಗೆ <xliff:g id="APP_NAME2">%3$s</xliff:g> ಪ್ರವೇಶವನ್ನು ಪಡೆಯುತ್ತದೆ."</string>
+    <string name="consent_yes" msgid="4055438216605487056">"ಹೌದು"</string>
+    <string name="consent_no" msgid="1335543792857823917">"ಬೇಡ, ಧನ್ಯವಾದಗಳು"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-ko/strings.xml b/packages/CompanionDeviceManager/res/values-ko/strings.xml
index 7cfe8b6..f2904138 100644
--- a/packages/CompanionDeviceManager/res/values-ko/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ko/strings.xml
@@ -17,16 +17,10 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="4470785958457506021">"부속 기기 관리자"</string>
-    <!-- no translation found for chooser_title (2262294130493605839) -->
-    <skip />
-    <!-- no translation found for profile_name_generic (6851028682723034988) -->
-    <skip />
-    <!-- no translation found for confirmation_title (4751119145078041732) -->
-    <skip />
-    <!-- no translation found for profile_summary (3167701603666642104) -->
-    <skip />
-    <!-- no translation found for consent_yes (4055438216605487056) -->
-    <skip />
-    <!-- no translation found for consent_no (1335543792857823917) -->
-    <skip />
+    <string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;에서 관리할 <xliff:g id="PROFILE_NAME">%1$s</xliff:g>을(를) 선택"</string>
+    <string name="profile_name_generic" msgid="6851028682723034988">"기기"</string>
+    <string name="confirmation_title" msgid="4751119145078041732">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;에서 <xliff:g id="PROFILE_NAME">%2$s</xliff:g>(&lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;)을(를) 관리하도록 설정"</string>
+    <string name="profile_summary" msgid="3167701603666642104">"<xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>을(를) 관리하려면 <xliff:g id="APP_NAME_0">%1$s</xliff:g> 앱이 필요합니다. <xliff:g id="PROFILE_NAME2">%5$s</xliff:g>이(가) 연결되어 있는 동안 <xliff:g id="APP_NAME2">%3$s</xliff:g> 앱이 <xliff:g id="PERMISSIONS">%4$s</xliff:g>에 액세스할 수 있습니다."</string>
+    <string name="consent_yes" msgid="4055438216605487056">"예"</string>
+    <string name="consent_no" msgid="1335543792857823917">"취소"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-ky/strings.xml b/packages/CompanionDeviceManager/res/values-ky/strings.xml
index eb9204a..9cce298 100644
--- a/packages/CompanionDeviceManager/res/values-ky/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ky/strings.xml
@@ -17,16 +17,10 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string>
-    <!-- no translation found for chooser_title (2262294130493605839) -->
-    <skip />
-    <!-- no translation found for profile_name_generic (6851028682723034988) -->
-    <skip />
-    <!-- no translation found for confirmation_title (4751119145078041732) -->
-    <skip />
-    <!-- no translation found for profile_summary (3167701603666642104) -->
-    <skip />
-    <!-- no translation found for consent_yes (4055438216605487056) -->
-    <skip />
-    <!-- no translation found for consent_no (1335543792857823917) -->
-    <skip />
+    <string name="chooser_title" msgid="2262294130493605839">"<xliff:g id="PROFILE_NAME">%1$s</xliff:g> &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; тарабынан башкарылсын"</string>
+    <string name="profile_name_generic" msgid="6851028682723034988">"түзмөк"</string>
+    <string name="confirmation_title" msgid="4751119145078041732">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; түзмөгүңүздү башкарсын"</string>
+    <string name="profile_summary" msgid="3167701603666642104">"<xliff:g id="PROFILE_NAME_1">%2$s</xliff:g> профилиңизди башкаруу үчүн <xliff:g id="APP_NAME_0">%1$s</xliff:g> керек. <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> туташып турганда <xliff:g id="APP_NAME2">%3$s</xliff:g> колдонмосунун төмөнкүлөргө уруксаты болот: <xliff:g id="PERMISSIONS">%4$s</xliff:g>."</string>
+    <string name="consent_yes" msgid="4055438216605487056">"Ооба"</string>
+    <string name="consent_no" msgid="1335543792857823917">"Жок, рахмат"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-lo/strings.xml b/packages/CompanionDeviceManager/res/values-lo/strings.xml
index 97d4c33..5fcbf7df 100644
--- a/packages/CompanionDeviceManager/res/values-lo/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-lo/strings.xml
@@ -17,16 +17,10 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="4470785958457506021">"ຕົວຈັດການອຸປະກອນປະກອບ"</string>
-    <!-- no translation found for chooser_title (2262294130493605839) -->
-    <skip />
-    <!-- no translation found for profile_name_generic (6851028682723034988) -->
-    <skip />
-    <!-- no translation found for confirmation_title (4751119145078041732) -->
-    <skip />
-    <!-- no translation found for profile_summary (3167701603666642104) -->
-    <skip />
-    <!-- no translation found for consent_yes (4055438216605487056) -->
-    <skip />
-    <!-- no translation found for consent_no (1335543792857823917) -->
-    <skip />
+    <string name="chooser_title" msgid="2262294130493605839">"ເລືອກ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ເພື່ອໃຫ້ຖືກຈັດການໂດຍ &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_name_generic" msgid="6851028682723034988">"ອຸປະກອນ"</string>
+    <string name="confirmation_title" msgid="4751119145078041732">"ຕັ້ງ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ເພື່ອຈັດການ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; ຂອງທ່ານ"</string>
+    <string name="profile_summary" msgid="3167701603666642104">"ຕ້ອງໃຊ້ <xliff:g id="APP_NAME_0">%1$s</xliff:g> ເພື່ອຈັດການ <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g> ຂອງທ່ານ. <xliff:g id="APP_NAME2">%3$s</xliff:g> ຈະໄດ້ຮັບສິດເຂົ້າເຖິງ <xliff:g id="PERMISSIONS">%4$s</xliff:g> ໃນເວລາເຊື່ອມຕໍ່ <xliff:g id="PROFILE_NAME2">%5$s</xliff:g>."</string>
+    <string name="consent_yes" msgid="4055438216605487056">"ແມ່ນແລ້ວ"</string>
+    <string name="consent_no" msgid="1335543792857823917">"ບໍ່, ຂອບໃຈ"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-lt/strings.xml b/packages/CompanionDeviceManager/res/values-lt/strings.xml
index eb9204a..56930d3 100644
--- a/packages/CompanionDeviceManager/res/values-lt/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-lt/strings.xml
@@ -17,16 +17,10 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string>
-    <!-- no translation found for chooser_title (2262294130493605839) -->
-    <skip />
-    <!-- no translation found for profile_name_generic (6851028682723034988) -->
-    <skip />
-    <!-- no translation found for confirmation_title (4751119145078041732) -->
-    <skip />
-    <!-- no translation found for profile_summary (3167701603666642104) -->
-    <skip />
-    <!-- no translation found for consent_yes (4055438216605487056) -->
-    <skip />
-    <!-- no translation found for consent_no (1335543792857823917) -->
-    <skip />
+    <string name="chooser_title" msgid="2262294130493605839">"Jūsų <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, kurį valdys &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; (pasirinkite)"</string>
+    <string name="profile_name_generic" msgid="6851028682723034988">"įrenginys"</string>
+    <string name="confirmation_title" msgid="4751119145078041732">"Nustatyti, kad <xliff:g id="PROFILE_NAME">%2$s</xliff:g> &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; būtų valdomas programos &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="3167701603666642104">"Tam, kad būtų valdomas jūsų <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>, reikalinga programa „<xliff:g id="APP_NAME_0">%1$s</xliff:g>“. Kol prijungtas <xliff:g id="PROFILE_NAME2">%5$s</xliff:g>, „<xliff:g id="APP_NAME2">%3$s</xliff:g>“ gaus prieigą prie šių elementų: <xliff:g id="PERMISSIONS">%4$s</xliff:g>."</string>
+    <string name="consent_yes" msgid="4055438216605487056">"Taip"</string>
+    <string name="consent_no" msgid="1335543792857823917">"Ne, ačiū"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-lv/strings.xml b/packages/CompanionDeviceManager/res/values-lv/strings.xml
index 39eafe1..a9d2151 100644
--- a/packages/CompanionDeviceManager/res/values-lv/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-lv/strings.xml
@@ -17,16 +17,10 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="4470785958457506021">"Palīgierīču pārzinis"</string>
-    <!-- no translation found for chooser_title (2262294130493605839) -->
-    <skip />
-    <!-- no translation found for profile_name_generic (6851028682723034988) -->
-    <skip />
-    <!-- no translation found for confirmation_title (4751119145078041732) -->
-    <skip />
-    <!-- no translation found for profile_summary (3167701603666642104) -->
-    <skip />
-    <!-- no translation found for consent_yes (4055438216605487056) -->
-    <skip />
-    <!-- no translation found for consent_no (1335543792857823917) -->
-    <skip />
+    <string name="chooser_title" msgid="2262294130493605839">"Profila (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>) izvēle, ko pārvaldīt lietotnē &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_name_generic" msgid="6851028682723034988">"ierīce"</string>
+    <string name="confirmation_title" msgid="4751119145078041732">"Lietotnes &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; iestatīšana profila (<xliff:g id="PROFILE_NAME">%2$s</xliff:g> — &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;) pārvaldībai"</string>
+    <string name="profile_summary" msgid="3167701603666642104">"Lai pārvaldītu profilu (<xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>), nepieciešama lietotne <xliff:g id="APP_NAME_0">%1$s</xliff:g>. Kamēr profils (<xliff:g id="PROFILE_NAME2">%5$s</xliff:g>) būs pievienots, lietotnei <xliff:g id="APP_NAME2">%3$s</xliff:g> tiks piešķirta piekļuve šādām atļaujām: <xliff:g id="PERMISSIONS">%4$s</xliff:g>."</string>
+    <string name="consent_yes" msgid="4055438216605487056">"Jā"</string>
+    <string name="consent_no" msgid="1335543792857823917">"Nē, paldies"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-mk/strings.xml b/packages/CompanionDeviceManager/res/values-mk/strings.xml
index eb9204a..5cc18c5 100644
--- a/packages/CompanionDeviceManager/res/values-mk/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-mk/strings.xml
@@ -17,16 +17,10 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string>
-    <!-- no translation found for chooser_title (2262294130493605839) -->
-    <skip />
-    <!-- no translation found for profile_name_generic (6851028682723034988) -->
-    <skip />
-    <!-- no translation found for confirmation_title (4751119145078041732) -->
-    <skip />
-    <!-- no translation found for profile_summary (3167701603666642104) -->
-    <skip />
-    <!-- no translation found for consent_yes (4055438216605487056) -->
-    <skip />
-    <!-- no translation found for consent_no (1335543792857823917) -->
-    <skip />
+    <string name="chooser_title" msgid="2262294130493605839">"Изберете <xliff:g id="PROFILE_NAME">%1$s</xliff:g> со којшто ќе управува &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_name_generic" msgid="6851028682723034988">"уред"</string>
+    <string name="confirmation_title" msgid="4751119145078041732">"Поставете ја &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; да управува со <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="3167701603666642104">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> е потребна за да управува со <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>. <xliff:g id="APP_NAME2">%3$s</xliff:g> ќе добие пристап до <xliff:g id="PERMISSIONS">%4$s</xliff:g> додека <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> е поврзан."</string>
+    <string name="consent_yes" msgid="4055438216605487056">"Да"</string>
+    <string name="consent_no" msgid="1335543792857823917">"Не, фала"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-ml/strings.xml b/packages/CompanionDeviceManager/res/values-ml/strings.xml
index 8e750de..b6734e8 100644
--- a/packages/CompanionDeviceManager/res/values-ml/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ml/strings.xml
@@ -17,16 +17,10 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="4470785958457506021">"കമ്പാനിയൻ ഉപകരണ മാനേജർ"</string>
-    <!-- no translation found for chooser_title (2262294130493605839) -->
-    <skip />
-    <!-- no translation found for profile_name_generic (6851028682723034988) -->
-    <skip />
-    <!-- no translation found for confirmation_title (4751119145078041732) -->
-    <skip />
-    <!-- no translation found for profile_summary (3167701603666642104) -->
-    <skip />
-    <!-- no translation found for consent_yes (4055438216605487056) -->
-    <skip />
-    <!-- no translation found for consent_no (1335543792857823917) -->
-    <skip />
+    <string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; ഉപയോഗിച്ച് മാനേജ് ചെയ്യുന്നതിന് ഒരു <xliff:g id="PROFILE_NAME">%1$s</xliff:g> തിരഞ്ഞെടുക്കുക"</string>
+    <string name="profile_name_generic" msgid="6851028682723034988">"ഉപകരണം"</string>
+    <string name="confirmation_title" msgid="4751119145078041732">"നിങ്ങളുടെ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> മാനേജ് ചെയ്യുന്നതിന് &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; സജ്ജീകരിക്കുക - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="3167701603666642104">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> എന്ന ആപ്പിന് നിങ്ങളുടെ <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g> മാനേജ് ചെയ്യേണ്ടതുണ്ട്. <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> കണക്റ്റ് ചെയ്‌തിരിക്കുമ്പോൾ <xliff:g id="APP_NAME2">%3$s</xliff:g> എന്ന ആപ്പിന് <xliff:g id="PERMISSIONS">%4$s</xliff:g> എന്നിവയിലേക്ക് ആക്‌സസ് ലഭിക്കും."</string>
+    <string name="consent_yes" msgid="4055438216605487056">"വേണം"</string>
+    <string name="consent_no" msgid="1335543792857823917">"വേണ്ട"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-mn/strings.xml b/packages/CompanionDeviceManager/res/values-mn/strings.xml
index eb9204a..cd4fdbf 100644
--- a/packages/CompanionDeviceManager/res/values-mn/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-mn/strings.xml
@@ -17,16 +17,10 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string>
-    <!-- no translation found for chooser_title (2262294130493605839) -->
-    <skip />
-    <!-- no translation found for profile_name_generic (6851028682723034988) -->
-    <skip />
-    <!-- no translation found for confirmation_title (4751119145078041732) -->
-    <skip />
-    <!-- no translation found for profile_summary (3167701603666642104) -->
-    <skip />
-    <!-- no translation found for consent_yes (4055438216605487056) -->
-    <skip />
-    <!-- no translation found for consent_no (1335543792857823917) -->
-    <skip />
+    <string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;-н удирдах<xliff:g id="PROFILE_NAME">%1$s</xliff:g>-г сонгоно уу"</string>
+    <string name="profile_name_generic" msgid="6851028682723034988">"төхөөрөмж"</string>
+    <string name="confirmation_title" msgid="4751119145078041732">"<xliff:g id="PROFILE_NAME">%2$s</xliff:g>-аа удирдахын тулд &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;-г тохируулна уу - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="3167701603666642104">"Таны <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>-г удирдахын тулд <xliff:g id="APP_NAME_0">%1$s</xliff:g> шаардлагатай. <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> холбогдсон үед <xliff:g id="APP_NAME2">%3$s</xliff:g> нь <xliff:g id="PERMISSIONS">%4$s</xliff:g>-д хандах эрхтэй болно."</string>
+    <string name="consent_yes" msgid="4055438216605487056">"Тийм"</string>
+    <string name="consent_no" msgid="1335543792857823917">"Үгүй, баярлалаа"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-mr/strings.xml b/packages/CompanionDeviceManager/res/values-mr/strings.xml
index 4f8f4ee..65bf262 100644
--- a/packages/CompanionDeviceManager/res/values-mr/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-mr/strings.xml
@@ -17,16 +17,10 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="4470785958457506021">"सहयोगी डिव्हाइस व्यवस्थापक"</string>
-    <!-- no translation found for chooser_title (2262294130493605839) -->
-    <skip />
-    <!-- no translation found for profile_name_generic (6851028682723034988) -->
-    <skip />
-    <!-- no translation found for confirmation_title (4751119145078041732) -->
-    <skip />
-    <!-- no translation found for profile_summary (3167701603666642104) -->
-    <skip />
-    <!-- no translation found for consent_yes (4055438216605487056) -->
-    <skip />
-    <!-- no translation found for consent_no (1335543792857823917) -->
-    <skip />
+    <string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; द्वारे व्यवस्थापित करण्यासाठी <xliff:g id="PROFILE_NAME">%1$s</xliff:g> निवडा"</string>
+    <string name="profile_name_generic" msgid="6851028682723034988">"डिव्हाइस"</string>
+    <string name="confirmation_title" msgid="4751119145078041732">"तुमची <xliff:g id="PROFILE_NAME">%2$s</xliff:g> व्यवस्थापित करण्यासाठी &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; सेट करा - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="3167701603666642104">"तुमची <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g> व्यवस्थापित करण्यासाठी <xliff:g id="APP_NAME_0">%1$s</xliff:g> आवश्यक आहे. <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> कनेक्ट केलेली असताना <xliff:g id="APP_NAME2">%3$s</xliff:g> ला <xliff:g id="PERMISSIONS">%4$s</xliff:g> चा अ‍ॅक्सेस मिळेल."</string>
+    <string name="consent_yes" msgid="4055438216605487056">"होय"</string>
+    <string name="consent_no" msgid="1335543792857823917">"नाही, नको"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-ms/strings.xml b/packages/CompanionDeviceManager/res/values-ms/strings.xml
index c170afa..d17041f 100644
--- a/packages/CompanionDeviceManager/res/values-ms/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ms/strings.xml
@@ -17,16 +17,10 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="4470785958457506021">"Pengurus Peranti Rakan"</string>
-    <!-- no translation found for chooser_title (2262294130493605839) -->
-    <skip />
-    <!-- no translation found for profile_name_generic (6851028682723034988) -->
-    <skip />
-    <!-- no translation found for confirmation_title (4751119145078041732) -->
-    <skip />
-    <!-- no translation found for profile_summary (3167701603666642104) -->
-    <skip />
-    <!-- no translation found for consent_yes (4055438216605487056) -->
-    <skip />
-    <!-- no translation found for consent_no (1335543792857823917) -->
-    <skip />
+    <string name="chooser_title" msgid="2262294130493605839">"Pilih <xliff:g id="PROFILE_NAME">%1$s</xliff:g> untuk diurus oleh &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_name_generic" msgid="6851028682723034988">"peranti"</string>
+    <string name="confirmation_title" msgid="4751119145078041732">"Tetapkan &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; untuk mengurus <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; anda"</string>
+    <string name="profile_summary" msgid="3167701603666642104">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> diperlukan untuk mengurus <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g> anda. <xliff:g id="APP_NAME2">%3$s</xliff:g> akan mendapat akses kepada <xliff:g id="PERMISSIONS">%4$s</xliff:g> semasa <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> disambungkan."</string>
+    <string name="consent_yes" msgid="4055438216605487056">"Ya"</string>
+    <string name="consent_no" msgid="1335543792857823917">"Tidak perlu"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-my/strings.xml b/packages/CompanionDeviceManager/res/values-my/strings.xml
index 6d0274f..23f165a 100644
--- a/packages/CompanionDeviceManager/res/values-my/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-my/strings.xml
@@ -17,16 +17,10 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="4470785958457506021">"တွဲဖက်ကိရိယာ မန်နေဂျာ"</string>
-    <!-- no translation found for chooser_title (2262294130493605839) -->
-    <skip />
-    <!-- no translation found for profile_name_generic (6851028682723034988) -->
-    <skip />
-    <!-- no translation found for confirmation_title (4751119145078041732) -->
-    <skip />
-    <!-- no translation found for profile_summary (3167701603666642104) -->
-    <skip />
-    <!-- no translation found for consent_yes (4055438216605487056) -->
-    <skip />
-    <!-- no translation found for consent_no (1335543792857823917) -->
-    <skip />
+    <string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; က စီမံခန့်ခွဲရန် <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ကို ရွေးချယ်ပါ"</string>
+    <string name="profile_name_generic" msgid="6851028682723034988">"စက်"</string>
+    <string name="confirmation_title" msgid="4751119145078041732">"သင်၏ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; ကို စီမံခန့်ခွဲရန် &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ကို သတ်မှတ်ပါ"</string>
+    <string name="profile_summary" msgid="3167701603666642104">"သင်၏ <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g> ကို စီမံခန့်ခွဲရန် <xliff:g id="APP_NAME_0">%1$s</xliff:g> ကို လိုအပ်ပါသည်။ <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> ကို ချိတ်ဆက်ထားစဉ် <xliff:g id="APP_NAME2">%3$s</xliff:g> သည် <xliff:g id="PERMISSIONS">%4$s</xliff:g> ကို ဝင်သုံးခွင့်ရပါမည်။"</string>
+    <string name="consent_yes" msgid="4055438216605487056">"Yes"</string>
+    <string name="consent_no" msgid="1335543792857823917">"မလိုပါ"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-nb/strings.xml b/packages/CompanionDeviceManager/res/values-nb/strings.xml
index eb9204a..090f2a2 100644
--- a/packages/CompanionDeviceManager/res/values-nb/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-nb/strings.xml
@@ -17,16 +17,10 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string>
-    <!-- no translation found for chooser_title (2262294130493605839) -->
-    <skip />
-    <!-- no translation found for profile_name_generic (6851028682723034988) -->
-    <skip />
-    <!-- no translation found for confirmation_title (4751119145078041732) -->
-    <skip />
-    <!-- no translation found for profile_summary (3167701603666642104) -->
-    <skip />
-    <!-- no translation found for consent_yes (4055438216605487056) -->
-    <skip />
-    <!-- no translation found for consent_no (1335543792857823917) -->
-    <skip />
+    <string name="chooser_title" msgid="2262294130493605839">"Velg <xliff:g id="PROFILE_NAME">%1$s</xliff:g> som skal administreres av &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_name_generic" msgid="6851028682723034988">"enhet"</string>
+    <string name="confirmation_title" msgid="4751119145078041732">"Angi &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; for å administrere <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="3167701603666642104">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> kreves for å administrere <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>. <xliff:g id="APP_NAME2">%3$s</xliff:g> får tilgang til <xliff:g id="PERMISSIONS">%4$s</xliff:g> når <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> er tilkoblet."</string>
+    <string name="consent_yes" msgid="4055438216605487056">"Ja"</string>
+    <string name="consent_no" msgid="1335543792857823917">"Nei takk"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-ne/strings.xml b/packages/CompanionDeviceManager/res/values-ne/strings.xml
index 99ba62b..e885674 100644
--- a/packages/CompanionDeviceManager/res/values-ne/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ne/strings.xml
@@ -17,16 +17,10 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="4470785958457506021">"सहयोगी यन्त्रको प्रबन्धक"</string>
-    <!-- no translation found for chooser_title (2262294130493605839) -->
-    <skip />
-    <!-- no translation found for profile_name_generic (6851028682723034988) -->
-    <skip />
-    <!-- no translation found for confirmation_title (4751119145078041732) -->
-    <skip />
-    <!-- no translation found for profile_summary (3167701603666642104) -->
-    <skip />
-    <!-- no translation found for consent_yes (4055438216605487056) -->
-    <skip />
-    <!-- no translation found for consent_no (1335543792857823917) -->
-    <skip />
+    <string name="chooser_title" msgid="2262294130493605839">"आफूले &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; प्रयोग गरी व्यवस्थापन गर्न चाहेको <xliff:g id="PROFILE_NAME">%1$s</xliff:g> चयन गर्नुहोस्"</string>
+    <string name="profile_name_generic" msgid="6851028682723034988">"यन्त्र"</string>
+    <string name="confirmation_title" msgid="4751119145078041732">"आफ्नो <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; व्यवस्थापन गर्न &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; तोक्नुहोस्"</string>
+    <string name="profile_summary" msgid="3167701603666642104">"<xliff:g id="PROFILE_NAME_1">%2$s</xliff:g> व्यवस्थापन गर्न <xliff:g id="APP_NAME_0">%1$s</xliff:g> इन्स्टल गर्नु पर्ने हुन्छ। <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> कनेक्ट भएका बेला <xliff:g id="APP_NAME2">%3$s</xliff:g> ले <xliff:g id="PERMISSIONS">%4$s</xliff:g> प्रयोग गर्ने अनुमति प्राप्त गर्ने छ।"</string>
+    <string name="consent_yes" msgid="4055438216605487056">"अँ"</string>
+    <string name="consent_no" msgid="1335543792857823917">"सहमत छुइनँ"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-or/strings.xml b/packages/CompanionDeviceManager/res/values-or/strings.xml
index 732017a..03fae5c 100644
--- a/packages/CompanionDeviceManager/res/values-or/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-or/strings.xml
@@ -17,16 +17,10 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="4470785958457506021">"ସହଯୋଗୀ ଡିଭାଇସ୍ ପରିଚାଳକ"</string>
-    <!-- no translation found for chooser_title (2262294130493605839) -->
-    <skip />
-    <!-- no translation found for profile_name_generic (6851028682723034988) -->
-    <skip />
-    <!-- no translation found for confirmation_title (4751119145078041732) -->
-    <skip />
-    <!-- no translation found for profile_summary (3167701603666642104) -->
-    <skip />
-    <!-- no translation found for consent_yes (4055438216605487056) -->
-    <skip />
-    <!-- no translation found for consent_no (1335543792857823917) -->
-    <skip />
+    <string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; ଦ୍ୱାରା ପରିଚାଳିତ ହେବା ପାଇଁ ଏକ <xliff:g id="PROFILE_NAME">%1$s</xliff:g>କୁ ବାଛନ୍ତୁ"</string>
+    <string name="profile_name_generic" msgid="6851028682723034988">"ଡିଭାଇସ୍"</string>
+    <string name="confirmation_title" msgid="4751119145078041732">"ଆପଣଙ୍କ <xliff:g id="PROFILE_NAME">%2$s</xliff:g>କୁ ପରିଚାଳନା କରିବା ପାଇଁ &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;କୁ ସେଟ୍ କରନ୍ତୁ - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="3167701603666642104">"ଆପଣଙ୍କ <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>କୁ ପରିଚାଳନା କରିବା ପାଇଁ <xliff:g id="APP_NAME_0">%1$s</xliff:g>ର ଆବଶ୍ୟକତା ଅଛି। <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> ସଂଯୁକ୍ତ ହୋଇଥିବା ସମୟରେ <xliff:g id="APP_NAME2">%3$s</xliff:g> <xliff:g id="PERMISSIONS">%4$s</xliff:g>କୁ ଆକ୍ସେସ୍ ପାଇବ।"</string>
+    <string name="consent_yes" msgid="4055438216605487056">"ହଁ"</string>
+    <string name="consent_no" msgid="1335543792857823917">"ନା, ଧନ୍ୟବାଦ"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-pa/strings.xml b/packages/CompanionDeviceManager/res/values-pa/strings.xml
index a61c759..33135b2 100644
--- a/packages/CompanionDeviceManager/res/values-pa/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-pa/strings.xml
@@ -17,16 +17,10 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="4470785958457506021">"ਸੰਬੰਧੀ ਡੀਵਾਈਸ ਪ੍ਰਬੰਧਕ"</string>
-    <!-- no translation found for chooser_title (2262294130493605839) -->
-    <skip />
-    <!-- no translation found for profile_name_generic (6851028682723034988) -->
-    <skip />
-    <!-- no translation found for confirmation_title (4751119145078041732) -->
-    <skip />
-    <!-- no translation found for profile_summary (3167701603666642104) -->
-    <skip />
-    <!-- no translation found for consent_yes (4055438216605487056) -->
-    <skip />
-    <!-- no translation found for consent_no (1335543792857823917) -->
-    <skip />
+    <string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; ਵੱਲੋਂ ਪ੍ਰਬੰਧਿਤ ਕੀਤੇ ਜਾਣ ਲਈ <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ਚੁਣੋ"</string>
+    <string name="profile_name_generic" msgid="6851028682723034988">"ਡੀਵਾਈਸ"</string>
+    <string name="confirmation_title" msgid="4751119145078041732">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ਨੂੰ ਤੁਹਾਡਾ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; ਦਾ ਪ੍ਰਬੰਧਨ ਕਰਨ ਲਈ ਸੈੱਟ ਕਰੋ"</string>
+    <string name="profile_summary" msgid="3167701603666642104">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> ਨੂੰ ਤੁਹਾਡਾ <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g> ਦਾ ਪ੍ਰਬੰਧਨ ਕਰਨ ਦੀ ਲੋੜ ਹੈ। <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> ਕਨੈਕਟ ਕੀਤੇ ਹੋਣ \'ਤੇ <xliff:g id="APP_NAME2">%3$s</xliff:g> ਨੂੰ <xliff:g id="PERMISSIONS">%4$s</xliff:g> ਤੱਕ ਪਹੁੰਚ ਪ੍ਰਾਪਤ ਹੋ ਜਾਵੇਗੀ।"</string>
+    <string name="consent_yes" msgid="4055438216605487056">"ਹਾਂ"</string>
+    <string name="consent_no" msgid="1335543792857823917">"ਨਹੀਂ ਧੰਨਵਾਦ"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml b/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml
index 8d96226..4258e70 100644
--- a/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-pt-rBR/strings.xml
@@ -17,16 +17,10 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="4470785958457506021">"Gerenciador de dispositivos complementar"</string>
-    <!-- no translation found for chooser_title (2262294130493605839) -->
-    <skip />
-    <!-- no translation found for profile_name_generic (6851028682723034988) -->
-    <skip />
-    <!-- no translation found for confirmation_title (4751119145078041732) -->
-    <skip />
-    <!-- no translation found for profile_summary (3167701603666642104) -->
-    <skip />
-    <!-- no translation found for consent_yes (4055438216605487056) -->
-    <skip />
-    <!-- no translation found for consent_no (1335543792857823917) -->
-    <skip />
+    <string name="chooser_title" msgid="2262294130493605839">"Escolha um <xliff:g id="PROFILE_NAME">%1$s</xliff:g> para ser gerenciado pelo app &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string>
+    <string name="confirmation_title" msgid="4751119145078041732">"Defina o app &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; como gerenciador do seu <xliff:g id="PROFILE_NAME">%2$s</xliff:g> (&lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;)"</string>
+    <string name="profile_summary" msgid="3167701603666642104">"O app <xliff:g id="APP_NAME_0">%1$s</xliff:g> é necessário para gerenciar seu <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>. <xliff:g id="APP_NAME2">%3$s</xliff:g> terá acesso a <xliff:g id="PERMISSIONS">%4$s</xliff:g> enquanto o <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> estiver conectado."</string>
+    <string name="consent_yes" msgid="4055438216605487056">"Sim"</string>
+    <string name="consent_no" msgid="1335543792857823917">"Agora não"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-pt-rPT/strings.xml b/packages/CompanionDeviceManager/res/values-pt-rPT/strings.xml
index bb84540..45b03d6 100644
--- a/packages/CompanionDeviceManager/res/values-pt-rPT/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-pt-rPT/strings.xml
@@ -17,16 +17,10 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="4470785958457506021">"Gestor de dispositivos associados"</string>
-    <!-- no translation found for chooser_title (2262294130493605839) -->
-    <skip />
-    <!-- no translation found for profile_name_generic (6851028682723034988) -->
-    <skip />
-    <!-- no translation found for confirmation_title (4751119145078041732) -->
-    <skip />
-    <!-- no translation found for profile_summary (3167701603666642104) -->
-    <skip />
-    <!-- no translation found for consent_yes (4055438216605487056) -->
-    <skip />
-    <!-- no translation found for consent_no (1335543792857823917) -->
-    <skip />
+    <string name="chooser_title" msgid="2262294130493605839">"Escolha um <xliff:g id="PROFILE_NAME">%1$s</xliff:g> para ser gerido pela app &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string>
+    <string name="confirmation_title" msgid="4751119145078041732">"Defina a app &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; para gerir o seu <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="3167701603666642104">"A app <xliff:g id="APP_NAME_0">%1$s</xliff:g> é necessária para gerir o seu <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>. A app <xliff:g id="APP_NAME2">%3$s</xliff:g> terá acesso a <xliff:g id="PERMISSIONS">%4$s</xliff:g> enquanto o <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> estiver associado."</string>
+    <string name="consent_yes" msgid="4055438216605487056">"Sim"</string>
+    <string name="consent_no" msgid="1335543792857823917">"Não, obrigado"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-pt/strings.xml b/packages/CompanionDeviceManager/res/values-pt/strings.xml
index 8d96226..4258e70 100644
--- a/packages/CompanionDeviceManager/res/values-pt/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-pt/strings.xml
@@ -17,16 +17,10 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="4470785958457506021">"Gerenciador de dispositivos complementar"</string>
-    <!-- no translation found for chooser_title (2262294130493605839) -->
-    <skip />
-    <!-- no translation found for profile_name_generic (6851028682723034988) -->
-    <skip />
-    <!-- no translation found for confirmation_title (4751119145078041732) -->
-    <skip />
-    <!-- no translation found for profile_summary (3167701603666642104) -->
-    <skip />
-    <!-- no translation found for consent_yes (4055438216605487056) -->
-    <skip />
-    <!-- no translation found for consent_no (1335543792857823917) -->
-    <skip />
+    <string name="chooser_title" msgid="2262294130493605839">"Escolha um <xliff:g id="PROFILE_NAME">%1$s</xliff:g> para ser gerenciado pelo app &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_name_generic" msgid="6851028682723034988">"dispositivo"</string>
+    <string name="confirmation_title" msgid="4751119145078041732">"Defina o app &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; como gerenciador do seu <xliff:g id="PROFILE_NAME">%2$s</xliff:g> (&lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;)"</string>
+    <string name="profile_summary" msgid="3167701603666642104">"O app <xliff:g id="APP_NAME_0">%1$s</xliff:g> é necessário para gerenciar seu <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>. <xliff:g id="APP_NAME2">%3$s</xliff:g> terá acesso a <xliff:g id="PERMISSIONS">%4$s</xliff:g> enquanto o <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> estiver conectado."</string>
+    <string name="consent_yes" msgid="4055438216605487056">"Sim"</string>
+    <string name="consent_no" msgid="1335543792857823917">"Agora não"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-ro/strings.xml b/packages/CompanionDeviceManager/res/values-ro/strings.xml
index 6446ce0..060e996 100644
--- a/packages/CompanionDeviceManager/res/values-ro/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ro/strings.xml
@@ -17,16 +17,10 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="4470785958457506021">"Manager de dispozitiv Companion"</string>
-    <!-- no translation found for chooser_title (2262294130493605839) -->
-    <skip />
-    <!-- no translation found for profile_name_generic (6851028682723034988) -->
-    <skip />
-    <!-- no translation found for confirmation_title (4751119145078041732) -->
-    <skip />
-    <!-- no translation found for profile_summary (3167701603666642104) -->
-    <skip />
-    <!-- no translation found for consent_yes (4055438216605487056) -->
-    <skip />
-    <!-- no translation found for consent_no (1335543792857823917) -->
-    <skip />
+    <string name="chooser_title" msgid="2262294130493605839">"Alegeți un profil <xliff:g id="PROFILE_NAME">%1$s</xliff:g> pe care să îl gestioneze &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_name_generic" msgid="6851028682723034988">"dispozitiv"</string>
+    <string name="confirmation_title" msgid="4751119145078041732">"Setați &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; pentru a vă gestiona profilul <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="3167701603666642104">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> este necesară pentru a vă gestiona profilul <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>. <xliff:g id="APP_NAME2">%3$s</xliff:g> va primi acces la <xliff:g id="PERMISSIONS">%4$s</xliff:g> în timp ce profilul <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> este conectat."</string>
+    <string name="consent_yes" msgid="4055438216605487056">"Da"</string>
+    <string name="consent_no" msgid="1335543792857823917">"Nu, mulțumesc"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-ru/strings.xml b/packages/CompanionDeviceManager/res/values-ru/strings.xml
index 4923f1f..7982507 100644
--- a/packages/CompanionDeviceManager/res/values-ru/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ru/strings.xml
@@ -17,16 +17,10 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="4470785958457506021">"Управление подключенными устройствами"</string>
-    <!-- no translation found for chooser_title (2262294130493605839) -->
-    <skip />
-    <!-- no translation found for profile_name_generic (6851028682723034988) -->
-    <skip />
-    <!-- no translation found for confirmation_title (4751119145078041732) -->
-    <skip />
-    <!-- no translation found for profile_summary (3167701603666642104) -->
-    <skip />
-    <!-- no translation found for consent_yes (4055438216605487056) -->
-    <skip />
-    <!-- no translation found for consent_no (1335543792857823917) -->
-    <skip />
+    <string name="chooser_title" msgid="2262294130493605839">"Выберите устройство (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>), которым будет управлять приложение &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_name_generic" msgid="6851028682723034988">"устройство"</string>
+    <string name="confirmation_title" msgid="4751119145078041732">"Разрешите приложению &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; управлять устройством &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; (<xliff:g id="PROFILE_NAME">%2$s</xliff:g>)"</string>
+    <string name="profile_summary" msgid="3167701603666642104">"Приложение \"<xliff:g id="APP_NAME_0">%1$s</xliff:g>\" необходимо для управления устройством (<xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>). При подключении к устройству (<xliff:g id="PROFILE_NAME2">%5$s</xliff:g>) приложение \"<xliff:g id="APP_NAME2">%3$s</xliff:g>\" получит доступ к следующему: <xliff:g id="PERMISSIONS">%4$s</xliff:g>."</string>
+    <string name="consent_yes" msgid="4055438216605487056">"Да"</string>
+    <string name="consent_no" msgid="1335543792857823917">"Нет"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-si/strings.xml b/packages/CompanionDeviceManager/res/values-si/strings.xml
index eb8d872..8bbc1a6 100644
--- a/packages/CompanionDeviceManager/res/values-si/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-si/strings.xml
@@ -17,16 +17,10 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="4470785958457506021">"සහායක උපාංග කළමනාකරු"</string>
-    <!-- no translation found for chooser_title (2262294130493605839) -->
-    <skip />
-    <!-- no translation found for profile_name_generic (6851028682723034988) -->
-    <skip />
-    <!-- no translation found for confirmation_title (4751119145078041732) -->
-    <skip />
-    <!-- no translation found for profile_summary (3167701603666642104) -->
-    <skip />
-    <!-- no translation found for consent_yes (4055438216605487056) -->
-    <skip />
-    <!-- no translation found for consent_no (1335543792857823917) -->
-    <skip />
+    <string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; මගින් කළමනාකරණය කරනු ලැබීමට <xliff:g id="PROFILE_NAME">%1$s</xliff:g>ක් තෝරන්න"</string>
+    <string name="profile_name_generic" msgid="6851028682723034988">"උපාංගය"</string>
+    <string name="confirmation_title" msgid="4751119145078041732">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ඔබගේ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> කළමනාකරණය කිරීමට සකසන්න - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="3167701603666642104">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> ඔබගේ <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g> කළමනාකරණය කිරීමට අවශ්‍යයි. <xliff:g id="APP_NAME2">%3$s</xliff:g> හට <xliff:g id="PERMISSIONS">%4$s</xliff:g> වෙත ප්‍රවේශය <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> සම්බන්ධිත අතරතුර ලැබෙනු ඇත."</string>
+    <string name="consent_yes" msgid="4055438216605487056">"ඔව්"</string>
+    <string name="consent_no" msgid="1335543792857823917">"එපා, ස්තුතියි"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-sk/strings.xml b/packages/CompanionDeviceManager/res/values-sk/strings.xml
index bf66ced..1037a96 100644
--- a/packages/CompanionDeviceManager/res/values-sk/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sk/strings.xml
@@ -17,16 +17,10 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="4470785958457506021">"Správca sprievodných zariadení"</string>
-    <!-- no translation found for chooser_title (2262294130493605839) -->
-    <skip />
-    <!-- no translation found for profile_name_generic (6851028682723034988) -->
-    <skip />
-    <!-- no translation found for confirmation_title (4751119145078041732) -->
-    <skip />
-    <!-- no translation found for profile_summary (3167701603666642104) -->
-    <skip />
-    <!-- no translation found for consent_yes (4055438216605487056) -->
-    <skip />
-    <!-- no translation found for consent_no (1335543792857823917) -->
-    <skip />
+    <string name="chooser_title" msgid="2262294130493605839">"Vyberte profil <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, ktorý bude spravovať aplikácia &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_name_generic" msgid="6851028682723034988">"zariadenie"</string>
+    <string name="confirmation_title" msgid="4751119145078041732">"Nastavte aplikáciu &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;, aby spravovala profil <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="3167701603666642104">"Na správu profilu <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g> je potrebná aplikácia <xliff:g id="APP_NAME_0">%1$s</xliff:g>. Kým bude profil <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> pripojený, <xliff:g id="APP_NAME2">%3$s</xliff:g> získa prístup k povoleniam <xliff:g id="PERMISSIONS">%4$s</xliff:g>."</string>
+    <string name="consent_yes" msgid="4055438216605487056">"Áno"</string>
+    <string name="consent_no" msgid="1335543792857823917">"Nie, vďaka"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-sl/strings.xml b/packages/CompanionDeviceManager/res/values-sl/strings.xml
index 644b960..f2d4c6f 100644
--- a/packages/CompanionDeviceManager/res/values-sl/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sl/strings.xml
@@ -17,16 +17,10 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="4470785958457506021">"Upravitelj spremljevalnih naprav"</string>
-    <!-- no translation found for chooser_title (2262294130493605839) -->
-    <skip />
-    <!-- no translation found for profile_name_generic (6851028682723034988) -->
-    <skip />
-    <!-- no translation found for confirmation_title (4751119145078041732) -->
-    <skip />
-    <!-- no translation found for profile_summary (3167701603666642104) -->
-    <skip />
-    <!-- no translation found for consent_yes (4055438216605487056) -->
-    <skip />
-    <!-- no translation found for consent_no (1335543792857823917) -->
-    <skip />
+    <string name="chooser_title" msgid="2262294130493605839">"Izbira naprave <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, ki jo bo upravljala aplikacija &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_name_generic" msgid="6851028682723034988">"naprava"</string>
+    <string name="confirmation_title" msgid="4751119145078041732">"Nastavitev aplikacije &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;, ki bo upravljala napravo <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="3167701603666642104">"Za upravljanje naprave <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g> potrebujete aplikacijo <xliff:g id="APP_NAME_0">%1$s</xliff:g>. Ko je naprava <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> povezana, bo aplikaciji <xliff:g id="APP_NAME2">%3$s</xliff:g> omogočen dostop do teh dovoljenj: <xliff:g id="PERMISSIONS">%4$s</xliff:g>."</string>
+    <string name="consent_yes" msgid="4055438216605487056">"Da"</string>
+    <string name="consent_no" msgid="1335543792857823917">"Ne, hvala"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-sq/strings.xml b/packages/CompanionDeviceManager/res/values-sq/strings.xml
index 84812dc..c9336b3 100644
--- a/packages/CompanionDeviceManager/res/values-sq/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sq/strings.xml
@@ -17,16 +17,10 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="4470785958457506021">"Menaxheri i pajisjes shoqëruese"</string>
-    <!-- no translation found for chooser_title (2262294130493605839) -->
-    <skip />
-    <!-- no translation found for profile_name_generic (6851028682723034988) -->
-    <skip />
-    <!-- no translation found for confirmation_title (4751119145078041732) -->
-    <skip />
-    <!-- no translation found for profile_summary (3167701603666642104) -->
-    <skip />
-    <!-- no translation found for consent_yes (4055438216605487056) -->
-    <skip />
-    <!-- no translation found for consent_no (1335543792857823917) -->
-    <skip />
+    <string name="chooser_title" msgid="2262294130493605839">"Zgjidh një profil <xliff:g id="PROFILE_NAME">%1$s</xliff:g> që do të menaxhohet nga &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_name_generic" msgid="6851028682723034988">"pajisja"</string>
+    <string name="confirmation_title" msgid="4751119145078041732">"Cakto &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; që të menaxhojë profilin tënd <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="3167701603666642104">"Nevojitet <xliff:g id="APP_NAME_0">%1$s</xliff:g> për të menaxhuar profilin tënd <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>. <xliff:g id="APP_NAME2">%3$s</xliff:g> do të marrë qasje në <xliff:g id="PERMISSIONS">%4$s</xliff:g> ndërkohë që është lidhur profili <xliff:g id="PROFILE_NAME2">%5$s</xliff:g>."</string>
+    <string name="consent_yes" msgid="4055438216605487056">"Po"</string>
+    <string name="consent_no" msgid="1335543792857823917">"Jo, faleminderit"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-sr/strings.xml b/packages/CompanionDeviceManager/res/values-sr/strings.xml
index 3507af3..5298194 100644
--- a/packages/CompanionDeviceManager/res/values-sr/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sr/strings.xml
@@ -17,16 +17,10 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="4470785958457506021">"Менаџер придруженог уређаја"</string>
-    <!-- no translation found for chooser_title (2262294130493605839) -->
-    <skip />
-    <!-- no translation found for profile_name_generic (6851028682723034988) -->
-    <skip />
-    <!-- no translation found for confirmation_title (4751119145078041732) -->
-    <skip />
-    <!-- no translation found for profile_summary (3167701603666642104) -->
-    <skip />
-    <!-- no translation found for consent_yes (4055438216605487056) -->
-    <skip />
-    <!-- no translation found for consent_no (1335543792857823917) -->
-    <skip />
+    <string name="chooser_title" msgid="2262294130493605839">"Одаберите профил <xliff:g id="PROFILE_NAME">%1$s</xliff:g> којим ће управљати апликација &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_name_generic" msgid="6851028682723034988">"уређај"</string>
+    <string name="confirmation_title" msgid="4751119145078041732">"Подесите апликацију &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; да управља профилом <xliff:g id="PROFILE_NAME">%2$s</xliff:g> – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="3167701603666642104">"Апликација <xliff:g id="APP_NAME_0">%1$s</xliff:g> је неопходна за управљање профилом <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>. <xliff:g id="APP_NAME2">%3$s</xliff:g> ће добити приступ дозволама за <xliff:g id="PERMISSIONS">%4$s</xliff:g> док је <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> повезан."</string>
+    <string name="consent_yes" msgid="4055438216605487056">"Да"</string>
+    <string name="consent_no" msgid="1335543792857823917">"Не, хвала"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-sw/strings.xml b/packages/CompanionDeviceManager/res/values-sw/strings.xml
index 13e7879..ae8ade7 100644
--- a/packages/CompanionDeviceManager/res/values-sw/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-sw/strings.xml
@@ -17,16 +17,10 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="4470785958457506021">"Kidhibiti cha Vifaa Visaidizi"</string>
-    <!-- no translation found for chooser_title (2262294130493605839) -->
-    <skip />
-    <!-- no translation found for profile_name_generic (6851028682723034988) -->
-    <skip />
-    <!-- no translation found for confirmation_title (4751119145078041732) -->
-    <skip />
-    <!-- no translation found for profile_summary (3167701603666642104) -->
-    <skip />
-    <!-- no translation found for consent_yes (4055438216605487056) -->
-    <skip />
-    <!-- no translation found for consent_no (1335543792857823917) -->
-    <skip />
+    <string name="chooser_title" msgid="2262294130493605839">"Chagua <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ili idhibitiwe na &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_name_generic" msgid="6851028682723034988">"kifaa"</string>
+    <string name="confirmation_title" msgid="4751119145078041732">"Weka &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ili udhibiti <xliff:g id="PROFILE_NAME">%2$s</xliff:g> yako - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="3167701603666642104">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> linahitajika ili kudhibiti <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g> wako. <xliff:g id="APP_NAME2">%3$s</xliff:g> itapata uwezo wa kufikia <xliff:g id="PERMISSIONS">%4$s</xliff:g> wakati <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> imeunganishwa."</string>
+    <string name="consent_yes" msgid="4055438216605487056">"Ndiyo"</string>
+    <string name="consent_no" msgid="1335543792857823917">"Hapana"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-ta/strings.xml b/packages/CompanionDeviceManager/res/values-ta/strings.xml
index 9cc9aa7..373ed45 100644
--- a/packages/CompanionDeviceManager/res/values-ta/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ta/strings.xml
@@ -17,16 +17,10 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="4470785958457506021">"கம்பேனியன் சாதன நிர்வாகி"</string>
-    <!-- no translation found for chooser_title (2262294130493605839) -->
-    <skip />
-    <!-- no translation found for profile_name_generic (6851028682723034988) -->
-    <skip />
-    <!-- no translation found for confirmation_title (4751119145078041732) -->
-    <skip />
-    <!-- no translation found for profile_summary (3167701603666642104) -->
-    <skip />
-    <!-- no translation found for consent_yes (4055438216605487056) -->
-    <skip />
-    <!-- no translation found for consent_no (1335543792857823917) -->
-    <skip />
+    <string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; ஆப்ஸ் நிர்வகிக்கக்கூடிய <xliff:g id="PROFILE_NAME">%1$s</xliff:g> ஐத் தேர்ந்தெடுங்கள்"</string>
+    <string name="profile_name_generic" msgid="6851028682723034988">"சாதனம்"</string>
+    <string name="confirmation_title" msgid="4751119145078041732">"<xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; ஐ நிர்வகிக்க &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ஆப்ஸை அமையுங்கள்"</string>
+    <string name="profile_summary" msgid="3167701603666642104">"<xliff:g id="PROFILE_NAME_1">%2$s</xliff:g> ஐ நிர்வகிக்க <xliff:g id="APP_NAME_0">%1$s</xliff:g> ஆப்ஸ் வேண்டும். <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> இணைக்கப்பட்டதும் <xliff:g id="PERMISSIONS">%4$s</xliff:g> ஆகியவற்றுக்கான அணுகலை <xliff:g id="APP_NAME2">%3$s</xliff:g> ஆப்ஸ் பெறும்."</string>
+    <string name="consent_yes" msgid="4055438216605487056">"ஆம்"</string>
+    <string name="consent_no" msgid="1335543792857823917">"வேண்டாம்"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-te/strings.xml b/packages/CompanionDeviceManager/res/values-te/strings.xml
index a4dcba6..f73e713 100644
--- a/packages/CompanionDeviceManager/res/values-te/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-te/strings.xml
@@ -17,16 +17,10 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="4470785958457506021">"సహచర పరికర మేనేజర్"</string>
-    <!-- no translation found for chooser_title (2262294130493605839) -->
-    <skip />
-    <!-- no translation found for profile_name_generic (6851028682723034988) -->
-    <skip />
-    <!-- no translation found for confirmation_title (4751119145078041732) -->
-    <skip />
-    <!-- no translation found for profile_summary (3167701603666642104) -->
-    <skip />
-    <!-- no translation found for consent_yes (4055438216605487056) -->
-    <skip />
-    <!-- no translation found for consent_no (1335543792857823917) -->
-    <skip />
+    <string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; ద్వారా మేనేజ్ చేయబడటానికి ఒక <xliff:g id="PROFILE_NAME">%1$s</xliff:g>ను ఎంచుకోండి"</string>
+    <string name="profile_name_generic" msgid="6851028682723034988">"పరికరం"</string>
+    <string name="confirmation_title" msgid="4751119145078041732">"మీ <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;ను మేనేజ్ చేయడానికి &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;ను సెటప్ చేయండి"</string>
+    <string name="profile_summary" msgid="3167701603666642104">"మీ <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>ను మేనేజ్ చేయడానికి <xliff:g id="APP_NAME_0">%1$s</xliff:g> అవసరం ఉంది. <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> కనెక్ట్ అయినప్పుడు <xliff:g id="APP_NAME2">%3$s</xliff:g>, <xliff:g id="PERMISSIONS">%4$s</xliff:g>కు యాక్సెస్‌ను పొందుతుంది."</string>
+    <string name="consent_yes" msgid="4055438216605487056">"అవును"</string>
+    <string name="consent_no" msgid="1335543792857823917">"వద్దు, ధన్యవాదాలు"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-th/strings.xml b/packages/CompanionDeviceManager/res/values-th/strings.xml
index eb9204a..8c1848a 100644
--- a/packages/CompanionDeviceManager/res/values-th/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-th/strings.xml
@@ -17,16 +17,10 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string>
-    <!-- no translation found for chooser_title (2262294130493605839) -->
-    <skip />
-    <!-- no translation found for profile_name_generic (6851028682723034988) -->
-    <skip />
-    <!-- no translation found for confirmation_title (4751119145078041732) -->
-    <skip />
-    <!-- no translation found for profile_summary (3167701603666642104) -->
-    <skip />
-    <!-- no translation found for consent_yes (4055438216605487056) -->
-    <skip />
-    <!-- no translation found for consent_no (1335543792857823917) -->
-    <skip />
+    <string name="chooser_title" msgid="2262294130493605839">"เลือก<xliff:g id="PROFILE_NAME">%1$s</xliff:g>ที่จะให้มีการจัดการโดย &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_name_generic" msgid="6851028682723034988">"อุปกรณ์"</string>
+    <string name="confirmation_title" msgid="4751119145078041732">"ตั้งค่า &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ให้จัดการ<xliff:g id="PROFILE_NAME">%2$s</xliff:g>ของคุณ - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="3167701603666642104">"ต้องใช้ <xliff:g id="APP_NAME_0">%1$s</xliff:g> ในการจัดการ<xliff:g id="PROFILE_NAME_1">%2$s</xliff:g> <xliff:g id="APP_NAME2">%3$s</xliff:g> จะได้รับสิทธิ์เข้าถึง<xliff:g id="PERMISSIONS">%4$s</xliff:g>ในขณะที่มีการเชื่อมต่อ<xliff:g id="PROFILE_NAME2">%5$s</xliff:g>"</string>
+    <string name="consent_yes" msgid="4055438216605487056">"ใช่"</string>
+    <string name="consent_no" msgid="1335543792857823917">"ไม่เป็นไร"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-tl/strings.xml b/packages/CompanionDeviceManager/res/values-tl/strings.xml
index 1f0d78e..8fcc3d2 100644
--- a/packages/CompanionDeviceManager/res/values-tl/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-tl/strings.xml
@@ -17,16 +17,10 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="4470785958457506021">"Kasamang Device Manager"</string>
-    <!-- no translation found for chooser_title (2262294130493605839) -->
-    <skip />
-    <!-- no translation found for profile_name_generic (6851028682723034988) -->
-    <skip />
-    <!-- no translation found for confirmation_title (4751119145078041732) -->
-    <skip />
-    <!-- no translation found for profile_summary (3167701603666642104) -->
-    <skip />
-    <!-- no translation found for consent_yes (4055438216605487056) -->
-    <skip />
-    <!-- no translation found for consent_no (1335543792857823917) -->
-    <skip />
+    <string name="chooser_title" msgid="2262294130493605839">"Pumili ng <xliff:g id="PROFILE_NAME">%1$s</xliff:g> para pamahalaan ng &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_name_generic" msgid="6851028682723034988">"device"</string>
+    <string name="confirmation_title" msgid="4751119145078041732">"Itakda ang &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; para pamahalaan ang iyong <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="3167701603666642104">"Kailangan ang <xliff:g id="APP_NAME_0">%1$s</xliff:g> para pamahalaan ang iyong <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>. Magkakaroon ng access ang <xliff:g id="APP_NAME2">%3$s</xliff:g> sa <xliff:g id="PERMISSIONS">%4$s</xliff:g> habang nakakonekta ang <xliff:g id="PROFILE_NAME2">%5$s</xliff:g>."</string>
+    <string name="consent_yes" msgid="4055438216605487056">"Oo"</string>
+    <string name="consent_no" msgid="1335543792857823917">"Huwag na lang"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-tr/strings.xml b/packages/CompanionDeviceManager/res/values-tr/strings.xml
index eb9204a..255eca5 100644
--- a/packages/CompanionDeviceManager/res/values-tr/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-tr/strings.xml
@@ -17,16 +17,10 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string>
-    <!-- no translation found for chooser_title (2262294130493605839) -->
-    <skip />
-    <!-- no translation found for profile_name_generic (6851028682723034988) -->
-    <skip />
-    <!-- no translation found for confirmation_title (4751119145078041732) -->
-    <skip />
-    <!-- no translation found for profile_summary (3167701603666642104) -->
-    <skip />
-    <!-- no translation found for consent_yes (4055438216605487056) -->
-    <skip />
-    <!-- no translation found for consent_no (1335543792857823917) -->
-    <skip />
+    <string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; tarafından yönetilecek bir <xliff:g id="PROFILE_NAME">%1$s</xliff:g> seçin"</string>
+    <string name="profile_name_generic" msgid="6851028682723034988">"cihaz"</string>
+    <string name="confirmation_title" msgid="4751119145078041732">"&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; uygulamasını, <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; cihazınızı yönetecek şekilde ayarlayın"</string>
+    <string name="profile_summary" msgid="3167701603666642104">"<xliff:g id="APP_NAME_0">%1$s</xliff:g>, <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g> yönetimi için gereklidir. <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> bağlıyken <xliff:g id="APP_NAME2">%3$s</xliff:g>, şunlara erişebilecek: <xliff:g id="PERMISSIONS">%4$s</xliff:g>."</string>
+    <string name="consent_yes" msgid="4055438216605487056">"Evet"</string>
+    <string name="consent_no" msgid="1335543792857823917">"Hayır, teşekkürler"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-uk/strings.xml b/packages/CompanionDeviceManager/res/values-uk/strings.xml
index 46de2cdc..b5827f2 100644
--- a/packages/CompanionDeviceManager/res/values-uk/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-uk/strings.xml
@@ -17,16 +17,10 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="4470785958457506021">"Диспетчер супутніх пристроїв"</string>
-    <!-- no translation found for chooser_title (2262294130493605839) -->
-    <skip />
-    <!-- no translation found for profile_name_generic (6851028682723034988) -->
-    <skip />
-    <!-- no translation found for confirmation_title (4751119145078041732) -->
-    <skip />
-    <!-- no translation found for profile_summary (3167701603666642104) -->
-    <skip />
-    <!-- no translation found for consent_yes (4055438216605487056) -->
-    <skip />
-    <!-- no translation found for consent_no (1335543792857823917) -->
-    <skip />
+    <string name="chooser_title" msgid="2262294130493605839">"Виберіть <xliff:g id="PROFILE_NAME">%1$s</xliff:g>, яким керуватиме додаток &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_name_generic" msgid="6851028682723034988">"пристрій"</string>
+    <string name="confirmation_title" msgid="4751119145078041732">"Налаштуйте додаток &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;, щоб керувати своїм пристроєм &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; (<xliff:g id="PROFILE_NAME">%2$s</xliff:g>)"</string>
+    <string name="profile_summary" msgid="3167701603666642104">"Щоб керувати своїм пристроєм (<xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>), вам потрібен додаток <xliff:g id="APP_NAME_0">%1$s</xliff:g>. Коли <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> буде підключено, додаток <xliff:g id="APP_NAME2">%3$s</xliff:g> отримає такі дозволи на доступ: <xliff:g id="PERMISSIONS">%4$s</xliff:g>."</string>
+    <string name="consent_yes" msgid="4055438216605487056">"Так"</string>
+    <string name="consent_no" msgid="1335543792857823917">"Ні, дякую"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-ur/strings.xml b/packages/CompanionDeviceManager/res/values-ur/strings.xml
index d2ce1fb..2bbffdc 100644
--- a/packages/CompanionDeviceManager/res/values-ur/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-ur/strings.xml
@@ -17,16 +17,10 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="4470785958457506021">"ساتھی آلہ مینیجر"</string>
-    <!-- no translation found for chooser_title (2262294130493605839) -->
-    <skip />
-    <!-- no translation found for profile_name_generic (6851028682723034988) -->
-    <skip />
-    <!-- no translation found for confirmation_title (4751119145078041732) -->
-    <skip />
-    <!-- no translation found for profile_summary (3167701603666642104) -->
-    <skip />
-    <!-- no translation found for consent_yes (4055438216605487056) -->
-    <skip />
-    <!-- no translation found for consent_no (1335543792857823917) -->
-    <skip />
+    <string name="chooser_title" msgid="2262294130493605839">"‏&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; کے ذریعے نظم کئے جانے کیلئے <xliff:g id="PROFILE_NAME">%1$s</xliff:g> کو منتخب کریں"</string>
+    <string name="profile_name_generic" msgid="6851028682723034988">"آلہ"</string>
+    <string name="confirmation_title" msgid="4751119145078041732">"‏اپنے <xliff:g id="PROFILE_NAME">%2$s</xliff:g> کا نظم کرنے کے لیے &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; کو سیٹ کریں - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="3167701603666642104">"آپ کے <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g> کا نظم کرنے کے لیے <xliff:g id="APP_NAME_0">%1$s</xliff:g> کی ضرورت ہے۔ <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> کے منسلک ہونے پر <xliff:g id="APP_NAME2">%3$s</xliff:g> <xliff:g id="PERMISSIONS">%4$s</xliff:g> تک رسائی حاصل کرے گا۔"</string>
+    <string name="consent_yes" msgid="4055438216605487056">"ہاں"</string>
+    <string name="consent_no" msgid="1335543792857823917">"نہیں شکریہ"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-uz/strings.xml b/packages/CompanionDeviceManager/res/values-uz/strings.xml
index eb9204a..96c49f2 100644
--- a/packages/CompanionDeviceManager/res/values-uz/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-uz/strings.xml
@@ -17,16 +17,10 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string>
-    <!-- no translation found for chooser_title (2262294130493605839) -->
-    <skip />
-    <!-- no translation found for profile_name_generic (6851028682723034988) -->
-    <skip />
-    <!-- no translation found for confirmation_title (4751119145078041732) -->
-    <skip />
-    <!-- no translation found for profile_summary (3167701603666642104) -->
-    <skip />
-    <!-- no translation found for consent_yes (4055438216605487056) -->
-    <skip />
-    <!-- no translation found for consent_no (1335543792857823917) -->
-    <skip />
+    <string name="chooser_title" msgid="2262294130493605839">"&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; boshqaradigan <xliff:g id="PROFILE_NAME">%1$s</xliff:g> qurilmasini tanlang"</string>
+    <string name="profile_name_generic" msgid="6851028682723034988">"qurilma"</string>
+    <string name="confirmation_title" msgid="4751119145078041732">"<xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt; qurilmalarini boshqarish uchun &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ilovasini sozlang"</string>
+    <string name="profile_summary" msgid="3167701603666642104">"<xliff:g id="APP_NAME_0">%1$s</xliff:g> ilovasi <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g> qurilmasini boshqarish uchun kerak. <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> qurilmasiga <xliff:g id="APP_NAME2">%3$s</xliff:g> ilovasi ulansa, u quyidagi ruxsatlarni oladi: <xliff:g id="PERMISSIONS">%4$s</xliff:g>"</string>
+    <string name="consent_yes" msgid="4055438216605487056">"Ha"</string>
+    <string name="consent_no" msgid="1335543792857823917">"Kerak emas"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-vi/strings.xml b/packages/CompanionDeviceManager/res/values-vi/strings.xml
index eb9204a..d67db41 100644
--- a/packages/CompanionDeviceManager/res/values-vi/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-vi/strings.xml
@@ -17,16 +17,10 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="4470785958457506021">"Companion Device Manager"</string>
-    <!-- no translation found for chooser_title (2262294130493605839) -->
-    <skip />
-    <!-- no translation found for profile_name_generic (6851028682723034988) -->
-    <skip />
-    <!-- no translation found for confirmation_title (4751119145078041732) -->
-    <skip />
-    <!-- no translation found for profile_summary (3167701603666642104) -->
-    <skip />
-    <!-- no translation found for consent_yes (4055438216605487056) -->
-    <skip />
-    <!-- no translation found for consent_no (1335543792857823917) -->
-    <skip />
+    <string name="chooser_title" msgid="2262294130493605839">"Chọn một <xliff:g id="PROFILE_NAME">%1$s</xliff:g> sẽ do &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; quản lý"</string>
+    <string name="profile_name_generic" msgid="6851028682723034988">"thiết bị"</string>
+    <string name="confirmation_title" msgid="4751119145078041732">"Đặt &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; để quản lý <xliff:g id="PROFILE_NAME">%2$s</xliff:g> của bạn – &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="3167701603666642104">"Cần có <xliff:g id="APP_NAME_0">%1$s</xliff:g> để quản lý <xliff:g id="PROFILE_NAME_1">%2$s</xliff:g> của bạn. <xliff:g id="APP_NAME2">%3$s</xliff:g> sẽ có quyền truy cập vào <xliff:g id="PERMISSIONS">%4$s</xliff:g> trong khi <xliff:g id="PROFILE_NAME2">%5$s</xliff:g> được kết nối."</string>
+    <string name="consent_yes" msgid="4055438216605487056">"Có"</string>
+    <string name="consent_no" msgid="1335543792857823917">"Không, cảm ơn"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml b/packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml
index 64efeac..a1abd98 100644
--- a/packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-zh-rCN/strings.xml
@@ -17,16 +17,10 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="4470785958457506021">"配套设备管理器"</string>
-    <!-- no translation found for chooser_title (2262294130493605839) -->
-    <skip />
-    <!-- no translation found for profile_name_generic (6851028682723034988) -->
-    <skip />
-    <!-- no translation found for confirmation_title (4751119145078041732) -->
-    <skip />
-    <!-- no translation found for profile_summary (3167701603666642104) -->
-    <skip />
-    <!-- no translation found for consent_yes (4055438216605487056) -->
-    <skip />
-    <!-- no translation found for consent_no (1335543792857823917) -->
-    <skip />
+    <string name="chooser_title" msgid="2262294130493605839">"选择要由&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;管理的<xliff:g id="PROFILE_NAME">%1$s</xliff:g>"</string>
+    <string name="profile_name_generic" msgid="6851028682723034988">"设备"</string>
+    <string name="confirmation_title" msgid="4751119145078041732">"设为由&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt;管理您的<xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="3167701603666642104">"若要管理<xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>,您需要使用<xliff:g id="APP_NAME_0">%1$s</xliff:g>。在已连接<xliff:g id="PROFILE_NAME2">%5$s</xliff:g>的情况下,<xliff:g id="APP_NAME2">%3$s</xliff:g>将能够访问<xliff:g id="PERMISSIONS">%4$s</xliff:g>。"</string>
+    <string name="consent_yes" msgid="4055438216605487056">"好"</string>
+    <string name="consent_no" msgid="1335543792857823917">"不用了"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml b/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml
index ecacf3a..57d2173 100644
--- a/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-zh-rHK/strings.xml
@@ -17,16 +17,10 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="4470785958457506021">"隨附裝置管理員"</string>
-    <!-- no translation found for chooser_title (2262294130493605839) -->
-    <skip />
-    <!-- no translation found for profile_name_generic (6851028682723034988) -->
-    <skip />
-    <!-- no translation found for confirmation_title (4751119145078041732) -->
-    <skip />
-    <!-- no translation found for profile_summary (3167701603666642104) -->
-    <skip />
-    <!-- no translation found for consent_yes (4055438216605487056) -->
-    <skip />
-    <!-- no translation found for consent_no (1335543792857823917) -->
-    <skip />
+    <string name="chooser_title" msgid="2262294130493605839">"選擇由 &lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt; 管理的<xliff:g id="PROFILE_NAME">%1$s</xliff:g>"</string>
+    <string name="profile_name_generic" msgid="6851028682723034988">"裝置"</string>
+    <string name="confirmation_title" msgid="4751119145078041732">"設定 &lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; 來管理您的 <xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="3167701603666642104">"必須使用 <xliff:g id="APP_NAME_0">%1$s</xliff:g> 來管理您的<xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>。連結<xliff:g id="PROFILE_NAME2">%5$s</xliff:g>後,<xliff:g id="APP_NAME2">%3$s</xliff:g> 將可以存取<xliff:g id="PERMISSIONS">%4$s</xliff:g>。"</string>
+    <string name="consent_yes" msgid="4055438216605487056">"是"</string>
+    <string name="consent_no" msgid="1335543792857823917">"不用了,謝謝"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml b/packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml
index ecacf3a..c9a2fd8 100644
--- a/packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-zh-rTW/strings.xml
@@ -17,16 +17,10 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="4470785958457506021">"隨附裝置管理員"</string>
-    <!-- no translation found for chooser_title (2262294130493605839) -->
-    <skip />
-    <!-- no translation found for profile_name_generic (6851028682723034988) -->
-    <skip />
-    <!-- no translation found for confirmation_title (4751119145078041732) -->
-    <skip />
-    <!-- no translation found for profile_summary (3167701603666642104) -->
-    <skip />
-    <!-- no translation found for consent_yes (4055438216605487056) -->
-    <skip />
-    <!-- no translation found for consent_no (1335543792857823917) -->
-    <skip />
+    <string name="chooser_title" msgid="2262294130493605839">"選擇要讓「<xliff:g id="APP_NAME">%2$s</xliff:g>」&lt;strong&gt;&lt;/strong&gt;管理的<xliff:g id="PROFILE_NAME">%1$s</xliff:g>"</string>
+    <string name="profile_name_generic" msgid="6851028682723034988">"裝置"</string>
+    <string name="confirmation_title" msgid="4751119145078041732">"授權讓「<xliff:g id="APP_NAME">%1$s</xliff:g>」&lt;strong&gt;&lt;/strong&gt;管理你的<xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="3167701603666642104">"如要管理你的<xliff:g id="PROFILE_NAME_1">%2$s</xliff:g>,必須使用「<xliff:g id="APP_NAME_0">%1$s</xliff:g>」。與<xliff:g id="PROFILE_NAME2">%5$s</xliff:g>連線時,「<xliff:g id="APP_NAME2">%3$s</xliff:g>」將有權存取你的<xliff:g id="PERMISSIONS">%4$s</xliff:g>。"</string>
+    <string name="consent_yes" msgid="4055438216605487056">"是"</string>
+    <string name="consent_no" msgid="1335543792857823917">"不用了,謝謝"</string>
 </resources>
diff --git a/packages/CompanionDeviceManager/res/values-zu/strings.xml b/packages/CompanionDeviceManager/res/values-zu/strings.xml
index 74f7a0e..c811037 100644
--- a/packages/CompanionDeviceManager/res/values-zu/strings.xml
+++ b/packages/CompanionDeviceManager/res/values-zu/strings.xml
@@ -17,16 +17,10 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label" msgid="4470785958457506021">"Isiphathi sedivayisi esihambisanayo"</string>
-    <!-- no translation found for chooser_title (2262294130493605839) -->
-    <skip />
-    <!-- no translation found for profile_name_generic (6851028682723034988) -->
-    <skip />
-    <!-- no translation found for confirmation_title (4751119145078041732) -->
-    <skip />
-    <!-- no translation found for profile_summary (3167701603666642104) -->
-    <skip />
-    <!-- no translation found for consent_yes (4055438216605487056) -->
-    <skip />
-    <!-- no translation found for consent_no (1335543792857823917) -->
-    <skip />
+    <string name="chooser_title" msgid="2262294130493605839">"Khetha i-<xliff:g id="PROFILE_NAME">%1$s</xliff:g> ezophathwa yi-&lt;strong&gt;<xliff:g id="APP_NAME">%2$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_name_generic" msgid="6851028682723034988">"idivayisi"</string>
+    <string name="confirmation_title" msgid="4751119145078041732">"Setha i-&lt;strong&gt;<xliff:g id="APP_NAME">%1$s</xliff:g>&lt;/strong&gt; ukuba iphathe i-<xliff:g id="PROFILE_NAME">%2$s</xliff:g> - &lt;strong&gt;<xliff:g id="DEVICE_NAME">%3$s</xliff:g>&lt;/strong&gt;"</string>
+    <string name="profile_summary" msgid="3167701603666642104">"I-<xliff:g id="APP_NAME_0">%1$s</xliff:g> iyadingeka ukuphatha i-<xliff:g id="PROFILE_NAME_1">%2$s</xliff:g> yakho. I-<xliff:g id="APP_NAME2">%3$s</xliff:g> izothola ukufinyelela ku-<xliff:g id="PERMISSIONS">%4$s</xliff:g> kuyilapho i-<xliff:g id="PROFILE_NAME2">%5$s</xliff:g> ixhunyiwe."</string>
+    <string name="consent_yes" msgid="4055438216605487056">"Yebo"</string>
+    <string name="consent_no" msgid="1335543792857823917">"Cha ngiyabonga"</string>
 </resources>
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
index a4896cb2..7f19662 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
@@ -74,7 +74,7 @@
 public class DynamicSystemInstallationService extends Service
         implements InstallationAsyncTask.ProgressListener {
 
-    private static final String TAG = "DynSystemInstallationService";
+    private static final String TAG = "DynamicSystemInstallationService";
 
     // TODO (b/131866826): This is currently for test only. Will move this to System API.
     static final String KEY_ENABLE_WHEN_COMPLETED = "KEY_ENABLE_WHEN_COMPLETED";
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
index ac73f35..4ef5e2b 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
@@ -20,6 +20,7 @@
 import android.gsi.AvbPublicKey;
 import android.net.Uri;
 import android.os.AsyncTask;
+import android.os.Build;
 import android.os.MemoryFile;
 import android.os.ParcelFileDescriptor;
 import android.os.image.DynamicSystemManager;
@@ -51,7 +52,8 @@
     private static final long MIN_PROGRESS_TO_PUBLISH = 1 << 27;
 
     private static final List<String> UNSUPPORTED_PARTITIONS =
-            Arrays.asList("vbmeta", "boot", "userdata", "dtbo", "super_empty", "system_other");
+            Arrays.asList(
+                    "vbmeta", "boot", "userdata", "dtbo", "super_empty", "system_other", "scratch");
 
     private class UnsupportedUrlException extends Exception {
         private UnsupportedUrlException(String message) {
@@ -196,6 +198,22 @@
                 return null;
             }
 
+            if (Build.IS_DEBUGGABLE) {
+                // If host is debuggable, then install a scratch partition so that we can do
+                // adb remount in the guest system.
+                try {
+                    installScratch();
+                } catch (IOException e) {
+                    // Failing to install overlayFS scratch shouldn't be fatal.
+                    // Just ignore the error and skip installing the scratch partition.
+                    Log.w(TAG, e.toString(), e);
+                }
+                if (isCancelled()) {
+                    mDynSystem.remove();
+                    return null;
+                }
+            }
+
             mDynSystem.finishInstallation();
         } catch (Exception e) {
             Log.e(TAG, e.toString(), e);
@@ -302,12 +320,53 @@
         }
     }
 
-    private void installUserdata() throws Exception {
+    private void installScratch() throws IOException, InterruptedException {
+        final long scratchSize = mDynSystem.suggestScratchSize();
+        Thread thread = new Thread() {
+            @Override
+            public void run() {
+                mInstallationSession =
+                        mDynSystem.createPartition("scratch", scratchSize, /* readOnly= */ false);
+            }
+        };
+
+        Log.d(TAG, "Creating partition: scratch, size = " + scratchSize);
+        thread.start();
+
+        Progress progress = new Progress("scratch", scratchSize, mNumInstalledPartitions++);
+
+        while (thread.isAlive()) {
+            if (isCancelled()) {
+                return;
+            }
+
+            final long installedSize = mDynSystem.getInstallationProgress().bytes_processed;
+
+            if (installedSize > progress.installedSize + MIN_PROGRESS_TO_PUBLISH) {
+                progress.installedSize = installedSize;
+                publishProgress(progress);
+            }
+
+            Thread.sleep(100);
+        }
+
+        if (mInstallationSession == null) {
+            throw new IOException(
+                    "Failed to start installation with requested size: " + scratchSize);
+        }
+        // Reset installation session and verify that installation completes successfully.
+        mInstallationSession = null;
+        if (!mDynSystem.closePartition()) {
+            throw new IOException("Failed to complete partition installation: scratch");
+        }
+    }
+
+    private void installUserdata() throws IOException, InterruptedException {
         Thread thread = new Thread(() -> {
             mInstallationSession = mDynSystem.createPartition("userdata", mUserdataSize, false);
         });
 
-        Log.d(TAG, "Creating partition: userdata");
+        Log.d(TAG, "Creating partition: userdata, size = " + mUserdataSize);
         thread.start();
 
         Progress progress = new Progress("userdata", mUserdataSize, mNumInstalledPartitions++);
@@ -324,7 +383,7 @@
                 publishProgress(progress);
             }
 
-            Thread.sleep(10);
+            Thread.sleep(100);
         }
 
         if (mInstallationSession == null) {
@@ -445,7 +504,7 @@
                 return;
             }
 
-            Thread.sleep(10);
+            Thread.sleep(100);
         }
 
         if (mInstallationSession == null) {
diff --git a/packages/FusedLocation/test/src/com/android/location/fused/tests/FusedLocationServiceTest.java b/packages/FusedLocation/test/src/com/android/location/fused/tests/FusedLocationServiceTest.java
index f9f1607..2bda530 100644
--- a/packages/FusedLocation/test/src/com/android/location/fused/tests/FusedLocationServiceTest.java
+++ b/packages/FusedLocation/test/src/com/android/location/fused/tests/FusedLocationServiceTest.java
@@ -27,6 +27,7 @@
 import android.location.LocationManager;
 import android.location.LocationRequest;
 import android.location.LocationResult;
+import android.location.ProviderProperties;
 import android.os.ParcelFileDescriptor;
 import android.os.SystemClock;
 import android.os.WorkSource;
@@ -37,7 +38,6 @@
 
 import com.android.internal.location.ILocationProvider;
 import com.android.internal.location.ILocationProviderManager;
-import com.android.internal.location.ProviderProperties;
 import com.android.internal.location.ProviderRequest;
 import com.android.location.fused.FusedLocationProvider;
 
@@ -153,7 +153,8 @@
         }
 
         @Override
-        public void onSetIdentity(String packageName, String attributionTag) {
+        public void onInitialize(boolean allowed, ProviderProperties properties, String packageName,
+                String attributionTag) {
 
         }
 
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
index 1f3ee6d..b0c3169 100644
--- a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
@@ -347,17 +347,19 @@
         if (!wasSetUp) {
             return;
         }
-
-        // load dummy layout with OK button disabled until we override this layout in
-        // startInstallConfirm
-        bindUi();
-        checkIfAllowedAndInitiateInstall();
     }
 
     @Override
     protected void onResume() {
         super.onResume();
 
+        if (mAppSnippet != null) {
+            // load dummy layout with OK button disabled until we override this layout in
+            // startInstallConfirm
+            bindUi();
+            checkIfAllowedAndInitiateInstall();
+        }
+
         if (mOk != null) {
             mOk.setEnabled(mEnableOk);
         }
diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp
index 3834162..0d4e746 100644
--- a/packages/SettingsLib/Android.bp
+++ b/packages/SettingsLib/Android.bp
@@ -56,6 +56,7 @@
         "SettingsLibTopIntroPreference",
         "SettingsLibBannerMessagePreference",
         "SettingsLibFooterPreference",
+        "SettingsLibUsageProgressBarPreference",
     ],
 }
 
diff --git a/packages/SettingsLib/EmergencyNumber/OWNERS b/packages/SettingsLib/EmergencyNumber/OWNERS
new file mode 100644
index 0000000..5707f87
--- /dev/null
+++ b/packages/SettingsLib/EmergencyNumber/OWNERS
@@ -0,0 +1 @@
+zhfan@google.com
\ No newline at end of file
diff --git a/packages/SettingsLib/EmergencyNumber/src/com/android/settingslib/emergencynumber/EmergencyNumberUtils.java b/packages/SettingsLib/EmergencyNumber/src/com/android/settingslib/emergencynumber/EmergencyNumberUtils.java
index 1c0e718..4d45494 100644
--- a/packages/SettingsLib/EmergencyNumber/src/com/android/settingslib/emergencynumber/EmergencyNumberUtils.java
+++ b/packages/SettingsLib/EmergencyNumber/src/com/android/settingslib/emergencynumber/EmergencyNumberUtils.java
@@ -19,9 +19,11 @@
 import static android.telephony.emergency.EmergencyNumber.EMERGENCY_NUMBER_SOURCE_DATABASE;
 import static android.telephony.emergency.EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_POLICE;
 
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.pm.PackageManager;
-import android.provider.Settings;
+import android.net.Uri;
+import android.os.Bundle;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.telephony.emergency.EmergencyNumber;
@@ -40,7 +42,16 @@
  */
 public class EmergencyNumberUtils {
     private static final String TAG = "EmergencyNumberUtils";
-    private static final String EMERGENCY_GESTURE_CALL_NUMBER = "emergency_gesture_call_number";
+
+    public static final Uri EMERGENCY_NUMBER_OVERRIDE_AUTHORITY = new Uri.Builder().scheme(
+            ContentResolver.SCHEME_CONTENT)
+            .authority("com.android.emergency.numbers")
+            .build();
+    public static final String METHOD_NAME_GET_EMERGENCY_NUMBER_OVERRIDE =
+            "GET_EMERGENCY_NUMBER_OVERRIDE";
+    public static final String METHOD_NAME_SET_EMERGENCY_NUMBER_OVERRIDE =
+            "SET_EMERGENCY_NUMBER_OVERRIDE";
+    public static final String EMERGENCY_GESTURE_CALL_NUMBER = "emergency_gesture_call_number";
     @VisibleForTesting
     static final String FALL_BACK_NUMBER = "112";
 
@@ -77,12 +88,28 @@
      * #getDefaultPoliceNumber()}).
      */
     public String getPoliceNumber() {
-        final String userProvidedNumber = Settings.Secure.getString(mContext.getContentResolver(),
-                EMERGENCY_GESTURE_CALL_NUMBER);
+        final String userProvidedNumber = getEmergencyNumberOverride();
         return TextUtils.isEmpty(userProvidedNumber)
                 ? getDefaultPoliceNumber() : userProvidedNumber;
     }
 
+    /**
+     * Sets device-local emergency number override
+     */
+    public void setEmergencyNumberOverride(String number) {
+        final Bundle bundle = new Bundle();
+        bundle.putString(EMERGENCY_GESTURE_CALL_NUMBER, number);
+        mContext.getContentResolver().call(EMERGENCY_NUMBER_OVERRIDE_AUTHORITY,
+                METHOD_NAME_SET_EMERGENCY_NUMBER_OVERRIDE, null /* args */, bundle);
+    }
+
+    private String getEmergencyNumberOverride() {
+        final Bundle bundle = mContext.getContentResolver().call(
+                EMERGENCY_NUMBER_OVERRIDE_AUTHORITY,
+                METHOD_NAME_GET_EMERGENCY_NUMBER_OVERRIDE, null /* args */, null /* bundle */);
+        return bundle == null ? null : bundle.getString(EMERGENCY_GESTURE_CALL_NUMBER);
+    }
+
     private List<EmergencyNumber> getPromotedEmergencyNumbers(int categories) {
         // TODO(b/171542607): Use platform API when its bug is fixed.
         Map<Integer, List<EmergencyNumber>> allLists = filterEmergencyNumbersByCategories(
diff --git a/packages/SettingsLib/RadioButtonPreference/res/drawable/ic_settings_accent.xml b/packages/SettingsLib/RadioButtonPreference/res/drawable/ic_settings_accent.xml
new file mode 100644
index 0000000..6521bc9
--- /dev/null
+++ b/packages/SettingsLib/RadioButtonPreference/res/drawable/ic_settings_accent.xml
@@ -0,0 +1,29 @@
+<!--
+  Copyright (C) 2021 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.
+  -->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24.0"
+        android:viewportHeight="24.0"
+        android:tint="?android:attr/colorAccent">
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M13.85,22.25h-3.7c-0.74,0 -1.36,-0.54 -1.45,-1.27l-0.27,-1.89c-0.27,-0.14 -0.53,-0.29 -0.79,-0.46l-1.8,0.72c-0.7,0.26 -1.47,-0.03 -1.81,-0.65L2.2,15.53c-0.35,-0.66 -0.2,-1.44 0.36,-1.88l1.53,-1.19c-0.01,-0.15 -0.02,-0.3 -0.02,-0.46c0,-0.15 0.01,-0.31 0.02,-0.46l-1.52,-1.19C1.98,9.9 1.83,9.09 2.2,8.47l1.85,-3.19c0.34,-0.62 1.11,-0.9 1.79,-0.63l1.81,0.73c0.26,-0.17 0.52,-0.32 0.78,-0.46l0.27,-1.91c0.09,-0.7 0.71,-1.25 1.44,-1.25h3.7c0.74,0 1.36,0.54 1.45,1.27l0.27,1.89c0.27,0.14 0.53,0.29 0.79,0.46l1.8,-0.72c0.71,-0.26 1.48,0.03 1.82,0.65l1.84,3.18c0.36,0.66 0.2,1.44 -0.36,1.88l-1.52,1.19c0.01,0.15 0.02,0.3 0.02,0.46s-0.01,0.31 -0.02,0.46l1.52,1.19c0.56,0.45 0.72,1.23 0.37,1.86l-1.86,3.22c-0.34,0.62 -1.11,0.9 -1.8,0.63l-1.8,-0.72c-0.26,0.17 -0.52,0.32 -0.78,0.46l-0.27,1.91C15.21,21.71 14.59,22.25 13.85,22.25zM13.32,20.72c0,0.01 0,0.01 0,0.02L13.32,20.72zM10.68,20.7l0,0.02C10.69,20.72 10.69,20.71 10.68,20.7zM10.62,20.25h2.76l0.37,-2.55l0.53,-0.22c0.44,-0.18 0.88,-0.44 1.34,-0.78l0.45,-0.34l2.38,0.96l1.38,-2.4l-2.03,-1.58l0.07,-0.56c0.03,-0.26 0.06,-0.51 0.06,-0.78c0,-0.27 -0.03,-0.53 -0.06,-0.78l-0.07,-0.56l2.03,-1.58l-1.39,-2.4l-2.39,0.96l-0.45,-0.35c-0.42,-0.32 -0.87,-0.58 -1.33,-0.77L13.75,6.3l-0.37,-2.55h-2.76L10.25,6.3L9.72,6.51C9.28,6.7 8.84,6.95 8.38,7.3L7.93,7.63L5.55,6.68L4.16,9.07l2.03,1.58l-0.07,0.56C6.09,11.47 6.06,11.74 6.06,12c0,0.26 0.02,0.53 0.06,0.78l0.07,0.56l-2.03,1.58l1.38,2.4l2.39,-0.96l0.45,0.35c0.43,0.33 0.86,0.58 1.33,0.77l0.53,0.22L10.62,20.25zM18.22,17.72c0,0.01 -0.01,0.02 -0.01,0.03L18.22,17.72zM5.77,17.71l0.01,0.02C5.78,17.72 5.77,17.71 5.77,17.71zM3.93,9.47L3.93,9.47C3.93,9.47 3.93,9.47 3.93,9.47zM18.22,6.27c0,0.01 0.01,0.02 0.01,0.02L18.22,6.27zM5.79,6.25L5.78,6.27C5.78,6.27 5.79,6.26 5.79,6.25zM13.31,3.28c0,0.01 0,0.01 0,0.02L13.31,3.28zM10.69,3.26l0,0.02C10.69,3.27 10.69,3.27 10.69,3.26z"/>
+    <path
+        android:fillColor="#FFFFFFFF"
+        android:pathData="M12,12m-3.5,0a3.5,3.5 0,1 1,7 0a3.5,3.5 0,1 1,-7 0"/>
+</vector>
\ No newline at end of file
diff --git a/packages/SettingsLib/RadioButtonPreference/res/layout/preference_radio.xml b/packages/SettingsLib/RadioButtonPreference/res/layout/preference_radio.xml
index b4b4c63..5ff0dc7 100644
--- a/packages/SettingsLib/RadioButtonPreference/res/layout/preference_radio.xml
+++ b/packages/SettingsLib/RadioButtonPreference/res/layout/preference_radio.xml
@@ -92,16 +92,32 @@
                 android:textAlignment="viewEnd"
                 android:textColor="?android:attr/textColorSecondary"
                 android:maxLines="1"
+                android:visibility="gone"
                 android:ellipsize="end"/>
         </LinearLayout>
-        <ProgressBar
-            android:id="@android:id/progress"
-            style="?android:attr/progressBarStyleHorizontal"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_marginTop="4dp"
-            android:max="100"
-            android:visibility="gone"/>
     </LinearLayout>
 
+    <LinearLayout
+        android:id="@+id/radio_extra_widget_container"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:orientation="horizontal"
+        android:gravity="center_vertical">
+        <View
+            android:layout_width=".75dp"
+            android:layout_height="match_parent"
+            android:layout_marginTop="16dp"
+            android:layout_marginBottom="16dp"
+            android:background="?android:attr/dividerVertical" />
+        <ImageView
+            android:id="@+id/radio_extra_widget"
+            android:layout_width="wrap_content"
+            android:layout_height="fill_parent"
+            android:src="@drawable/ic_settings_accent"
+            android:contentDescription="@string/settings_label"
+            android:paddingStart="16dp"
+            android:paddingEnd="16dp"
+            android:layout_gravity="center"
+            android:background="?android:attr/selectableItemBackground" />
+    </LinearLayout>
 </LinearLayout>
diff --git a/packages/SettingsLib/RadioButtonPreference/res/values/strings.xml b/packages/SettingsLib/RadioButtonPreference/res/values/strings.xml
new file mode 100644
index 0000000..ff3f90c
--- /dev/null
+++ b/packages/SettingsLib/RadioButtonPreference/res/values/strings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2021 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.
+  -->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+    <!-- Content description for RadioButton with extra gear icon [CHAR LIMIT=NONE] -->
+    <string name="settings_label">Settings</string>
+
+</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/RadioButtonPreference/src/com/android/settingslib/widget/RadioButtonPreference.java b/packages/SettingsLib/RadioButtonPreference/src/com/android/settingslib/widget/RadioButtonPreference.java
index 05e008c..f50127f 100644
--- a/packages/SettingsLib/RadioButtonPreference/src/com/android/settingslib/widget/RadioButtonPreference.java
+++ b/packages/SettingsLib/RadioButtonPreference/src/com/android/settingslib/widget/RadioButtonPreference.java
@@ -20,7 +20,7 @@
 import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.view.View;
-import android.widget.TextView;
+import android.widget.ImageView;
 
 import androidx.preference.CheckBoxPreference;
 import androidx.preference.PreferenceViewHolder;
@@ -34,6 +34,9 @@
  * In other words, there's no "RadioButtonPreferenceGroup" in this
  * implementation. When you check one RadioButtonPreference, if you want to
  * uncheck all the other preferences, you should do that by code yourself.
+ *
+ * RadioButtonPreference can assign a extraWidgetListener to show a gear icon
+ * on the right side that can open another page.
  */
 public class RadioButtonPreference extends CheckBoxPreference {
 
@@ -53,6 +56,10 @@
     private View mAppendix;
     private int mAppendixVisibility = -1;
 
+    private View mExtraWidgetContainer;
+    private ImageView mExtraWidget;
+
+    private View.OnClickListener mExtraWidgetOnClickListener;
 
     /**
      * Perform inflation from XML and apply a class-specific base style.
@@ -69,7 +76,6 @@
         init();
     }
 
-
     /**
      * Perform inflation from XML and apply a class-specific base style.
      *
@@ -136,11 +142,10 @@
             }
         }
 
-        TextView title = (TextView) holder.findViewById(android.R.id.title);
-        if (title != null) {
-            title.setSingleLine(false);
-            title.setMaxLines(3);
-        }
+        mExtraWidget = (ImageView) holder.findViewById(R.id.radio_extra_widget);
+        mExtraWidgetContainer = holder.findViewById(R.id.radio_extra_widget_container);
+
+        setExtraWidgetOnClickListener(mExtraWidgetOnClickListener);
     }
 
     /**
@@ -155,6 +160,24 @@
         mAppendixVisibility = visibility;
     }
 
+    /**
+     * Sets the callback to be invoked when extra widget is clicked by the user.
+     *
+     * @param listener The callback to be invoked
+     */
+    public void setExtraWidgetOnClickListener(View.OnClickListener listener) {
+        mExtraWidgetOnClickListener = listener;
+
+        if (mExtraWidget == null || mExtraWidgetContainer == null) {
+            return;
+        }
+
+        mExtraWidget.setOnClickListener(mExtraWidgetOnClickListener);
+
+        mExtraWidgetContainer.setVisibility((mExtraWidgetOnClickListener != null)
+                ? View.VISIBLE : View.GONE);
+    }
+
     private void init() {
         setWidgetLayoutResource(R.layout.preference_widget_radiobutton);
         setLayoutResource(R.layout.preference_radio);
diff --git a/packages/SettingsLib/SettingsTheme/Android.bp b/packages/SettingsLib/SettingsTheme/Android.bp
index 6d505bf..6579fd9 100644
--- a/packages/SettingsLib/SettingsTheme/Android.bp
+++ b/packages/SettingsLib/SettingsTheme/Android.bp
@@ -3,6 +3,10 @@
 
     resource_dirs: ["res"],
 
+    static_libs: [
+            "androidx.preference_preference",
+        ],
+
     sdk_version: "system_current",
     min_sdk_version: "21",
 }
diff --git a/packages/SettingsLib/SettingsTheme/res/layout/preference_category_settings.xml b/packages/SettingsLib/SettingsTheme/res/layout/preference_category_settings.xml
new file mode 100644
index 0000000..4b1b255
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/layout/preference_category_settings.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2021 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.
+  -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:paddingLeft="24dp"
+    android:paddingStart="24dp"
+    android:paddingRight="?android:attr/listPreferredItemPaddingRight"
+    android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+    android:background="?android:attr/selectableItemBackground"
+    android:baselineAligned="false"
+    android:layout_marginTop="16dp"
+    android:gravity="center_vertical">
+
+    <RelativeLayout
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"
+        android:paddingTop="8dp"
+        android:paddingBottom="8dp">
+
+        <TextView
+            android:id="@android:id/title"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_gravity="start"
+            android:textAlignment="viewStart"
+            style="@style/PreferenceCategoryTitleTextStyle"/>
+
+        <TextView
+            android:id="@android:id/summary"
+            android:ellipsize="end"
+            android:singleLine="true"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_below="@android:id/title"
+            android:layout_alignLeft="@android:id/title"
+            android:layout_alignStart="@android:id/title"
+            android:layout_gravity="start"
+            android:textAlignment="viewStart"
+            android:textColor="?android:attr/textColorSecondary"
+            android:maxLines="10"
+            style="@style/PreferenceSummaryTextStyle"/>
+
+    </RelativeLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SettingsLib/SettingsTheme/res/values/styles.xml b/packages/SettingsLib/SettingsTheme/res/values/styles.xml
new file mode 100644
index 0000000..6b285d5
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/values/styles.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2021 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.
+  -->
+<resources>
+    <style name="TextAppearance.CategoryTitle"
+           parent="@*android:style/TextAppearance.DeviceDefault.Body2">
+        <item name="android:textAllCaps">true</item>
+        <item name="android:textSize">11sp</item>
+        <!-- 0.8 Spacing, 0.8/11 = 0.072727273 -->
+        <item name="android:letterSpacing">0.072727273</item>
+    </style>
+</resources>
diff --git a/packages/SettingsLib/SettingsTheme/res/values/styles_preference.xml b/packages/SettingsLib/SettingsTheme/res/values/styles_preference.xml
new file mode 100644
index 0000000..d9c1586
--- /dev/null
+++ b/packages/SettingsLib/SettingsTheme/res/values/styles_preference.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2021 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.
+  -->
+<resources>
+    <style name="PreferenceTheme" parent="@style/PreferenceThemeOverlay">
+        <item name="preferenceCategoryStyle">@style/SettingsCategoryPreference</item>
+        <!-- For preference category color -->
+        <item name="preferenceCategoryTitleTextAppearance">@style/TextAppearance.CategoryTitle
+        </item>
+        <item name="preferenceCategoryTitleTextColor">?android:attr/textColorSecondary</item>
+    </style>
+
+    <style name="SettingsCategoryPreference" parent="@style/Preference.Category.Material">
+        <item name="android:layout">@layout/preference_category_settings</item>
+        <item name="allowDividerAbove">false</item>
+        <item name="allowDividerBelow">false</item>
+    </style>
+</resources>
diff --git a/packages/SettingsLib/UsageProgressBarPreference/Android.bp b/packages/SettingsLib/UsageProgressBarPreference/Android.bp
new file mode 100644
index 0000000..f346a59
--- /dev/null
+++ b/packages/SettingsLib/UsageProgressBarPreference/Android.bp
@@ -0,0 +1,13 @@
+android_library {
+    name: "SettingsLibUsageProgressBarPreference",
+
+    srcs: ["src/**/*.java"],
+    resource_dirs: ["res"],
+
+    static_libs: [
+        "androidx.preference_preference",
+    ],
+
+    sdk_version: "system_current",
+    min_sdk_version: "21",
+}
diff --git a/packages/SettingsLib/UsageProgressBarPreference/AndroidManifest.xml b/packages/SettingsLib/UsageProgressBarPreference/AndroidManifest.xml
new file mode 100644
index 0000000..51fc7ed
--- /dev/null
+++ b/packages/SettingsLib/UsageProgressBarPreference/AndroidManifest.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2021 The Android Open Source Project
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.android.settingslib.widget">
+
+    <uses-sdk android:minSdkVersion="21" />
+
+</manifest>
diff --git a/packages/SettingsLib/UsageProgressBarPreference/res/layout/preference_usage_progress_bar.xml b/packages/SettingsLib/UsageProgressBarPreference/res/layout/preference_usage_progress_bar.xml
new file mode 100644
index 0000000..9dbd5fa
--- /dev/null
+++ b/packages/SettingsLib/UsageProgressBarPreference/res/layout/preference_usage_progress_bar.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  Copyright (C) 2021 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.
+  -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:gravity="center_vertical"
+    android:orientation="vertical"
+    android:layout_marginStart="16dp"
+    android:layout_marginEnd="16dp"
+    android:paddingTop="32dp"
+    android:paddingBottom="32dp">
+
+    <RelativeLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content">
+        <TextView
+            android:id="@+id/usage_summary"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignParentStart="true"
+            android:layout_alignBaseline="@id/total_summary"
+            android:singleLine="true"
+            android:ellipsize="marquee"
+            android:fontFamily="@*android:string/config_headlineFontFamily"
+            android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Display1"
+            android:textSize="20sp"/>
+        <TextView
+            android:id="@+id/total_summary"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignParentEnd="true"
+            android:singleLine="true"
+            android:ellipsize="marquee"
+            android:textAppearance="@*android:style/TextAppearance.DeviceDefault.Body1"/>
+    </RelativeLayout>
+
+    <ProgressBar
+        android:id="@android:id/progress"
+        style="?android:attr/progressBarStyleHorizontal"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:scaleY="2"
+        android:layout_marginTop="4dp"
+        android:max="100"/>
+</LinearLayout>
diff --git a/packages/SettingsLib/UsageProgressBarPreference/src/com/android/settingslib/widget/UsageProgressBarPreference.java b/packages/SettingsLib/UsageProgressBarPreference/src/com/android/settingslib/widget/UsageProgressBarPreference.java
new file mode 100644
index 0000000..950a8b4
--- /dev/null
+++ b/packages/SettingsLib/UsageProgressBarPreference/src/com/android/settingslib/widget/UsageProgressBarPreference.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2021 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.settingslib.widget;
+
+import android.content.Context;
+import android.text.SpannableString;
+import android.text.Spanned;
+import android.text.TextUtils;
+import android.text.style.RelativeSizeSpan;
+import android.util.AttributeSet;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+
+import androidx.preference.Preference;
+import androidx.preference.PreferenceViewHolder;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Progres bar preference with a usage summary and a total summary.
+ * This preference shows number in usage summary with enlarged font size.
+ */
+public class UsageProgressBarPreference extends Preference {
+
+    private final Pattern mNumberPattern = Pattern.compile("[\\d]*\\.?[\\d]+");
+
+    private CharSequence mUsageSummary;
+    private CharSequence mTotalSummary;
+    private int mPercent = -1;
+
+    /**
+     * Perform inflation from XML and apply a class-specific base style.
+     *
+     * @param context  The {@link Context} this is associated with, through which it can
+     *                 access the current theme, resources, {@link SharedPreferences}, etc.
+     * @param attrs    The attributes of the XML tag that is inflating the preference
+     * @param defStyle An attribute in the current theme that contains a reference to a style
+     *                 resource that supplies default values for the view. Can be 0 to not
+     *                 look for defaults.
+     */
+    public UsageProgressBarPreference(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+        setLayoutResource(R.layout.preference_usage_progress_bar);
+    }
+
+    /**
+     * Perform inflation from XML and apply a class-specific base style.
+     *
+     * @param context The {@link Context} this is associated with, through which it can
+     *                access the current theme, resources, {@link SharedPreferences}, etc.
+     * @param attrs   The attributes of the XML tag that is inflating the preference
+     */
+    public UsageProgressBarPreference(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        setLayoutResource(R.layout.preference_usage_progress_bar);
+    }
+
+    /**
+     * Constructor to create a preference.
+     *
+     * @param context The Context this is associated with.
+     */
+    public UsageProgressBarPreference(Context context) {
+        this(context, null);
+    }
+
+    /** Set usage summary, number in the summary will show with enlarged font size. */
+    public void setUsageSummary(CharSequence usageSummary) {
+        if (TextUtils.equals(mUsageSummary, usageSummary)) {
+            return;
+        }
+        mUsageSummary = usageSummary;
+        notifyChanged();
+    }
+
+    /** Set total summary. */
+    public void setTotalSummary(CharSequence totalSummary) {
+        if (TextUtils.equals(mTotalSummary, totalSummary)) {
+            return;
+        }
+        mTotalSummary = totalSummary;
+        notifyChanged();
+    }
+
+    /** Set percentage of the progress bar. */
+    public void setPercent(long usage, long total) {
+        if (total == 0L || usage >  total) {
+            return;
+        }
+        final int percent = (int) (usage / (double) total * 100);
+        if (mPercent == percent) {
+            return;
+        }
+        mPercent = percent;
+        notifyChanged();
+    }
+
+    /**
+     * Binds the created View to the data for this preference.
+     *
+     * <p>This is a good place to grab references to custom Views in the layout and set
+     * properties on them.
+     *
+     * <p>Make sure to call through to the superclass's implementation.
+     *
+     * @param holder The ViewHolder that provides references to the views to fill in. These views
+     *               will be recycled, so you should not hold a reference to them after this method
+     *               returns.
+     */
+    @Override
+    public void onBindViewHolder(PreferenceViewHolder holder) {
+        super.onBindViewHolder(holder);
+
+        final TextView usageSummary = (TextView) holder.findViewById(R.id.usage_summary);
+        usageSummary.setText(enlargeFontOfNumber(mUsageSummary));
+
+        final TextView totalSummary = (TextView) holder.findViewById(R.id.total_summary);
+        if (mTotalSummary != null) {
+            totalSummary.setText(mTotalSummary);
+        }
+
+        final ProgressBar progressBar = (ProgressBar) holder.findViewById(android.R.id.progress);
+        if (mPercent < 0) {
+            progressBar.setIndeterminate(true);
+        } else {
+            progressBar.setIndeterminate(false);
+            progressBar.setProgress(mPercent);
+        }
+    }
+
+    private CharSequence enlargeFontOfNumber(CharSequence summary) {
+        if (TextUtils.isEmpty(summary)) {
+            return "";
+        }
+
+        final Matcher matcher = mNumberPattern.matcher(summary);
+        if (matcher.find()) {
+            final SpannableString spannableSummary =  new SpannableString(summary);
+            spannableSummary.setSpan(new RelativeSizeSpan(2.4f), matcher.start(),
+                    matcher.end(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+            return spannableSummary;
+        }
+        return summary;
+    }
+}
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index c2b26bc..d76a8a1 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -553,7 +553,8 @@
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Skep tans nuwe gebruiker …"</string>
     <string name="user_nickname" msgid="262624187455825083">"Bynaam"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Voeg gas by"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"Verwyder gas"</string>
+    <!-- no translation found for guest_exit_guest (4754204715192830850) -->
+    <skip />
     <string name="guest_nickname" msgid="6332276931583337261">"Gas"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Neem \'n foto"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Kies \'n prent"</string>
diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
index 65ce6e0..85b5bc2 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -553,7 +553,8 @@
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"አዲስ ተጠቃሚ በመፍጠር ላይ…"</string>
     <string name="user_nickname" msgid="262624187455825083">"ቅጽል ስም"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"እንግዳን አክል"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"እንግዳን አስወግድ"</string>
+    <!-- no translation found for guest_exit_guest (4754204715192830850) -->
+    <skip />
     <string name="guest_nickname" msgid="6332276931583337261">"እንግዳ"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"ፎቶ አንሳ"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"ምስል ይምረጡ"</string>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index 3ea96c7..f260559 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -450,7 +450,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> إلى أن يتم شحن الجهاز بالكامل"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> إلى أن يتم شحن الجهاز بالكامل"</string>
-    <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g> - التحسين لسلامة البطارية"</string>
+    <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g> - التحسين للحفاظ على سلامة البطارية"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"غير معروف"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"جارٍ الشحن"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"جارٍ الشحن سريعًا"</string>
@@ -557,7 +557,8 @@
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"جارٍ إنشاء مستخدم جديد…"</string>
     <string name="user_nickname" msgid="262624187455825083">"اللقب"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"إضافة ضيف"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"إزالة جلسة الضيف"</string>
+    <!-- no translation found for guest_exit_guest (4754204715192830850) -->
+    <skip />
     <string name="guest_nickname" msgid="6332276931583337261">"ضيف"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"التقاط صورة"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"اختيار صورة"</string>
diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml
index a259687..32be72d 100644
--- a/packages/SettingsLib/res/values-as/strings.xml
+++ b/packages/SettingsLib/res/values-as/strings.xml
@@ -553,7 +553,7 @@
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"নতুন ব্যৱহাৰকাৰী সৃষ্টি কৰি থকা হৈছে…"</string>
     <string name="user_nickname" msgid="262624187455825083">"উপনাম"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"অতিথি যোগ কৰক"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"অতিথি আঁতৰাওক"</string>
+    <string name="guest_exit_guest" msgid="4754204715192830850">"অতিথিৰ ছেশ্বন সমাপ্ত কৰক"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"অতিথি"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"এখন ফট’ তোলক"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"এখন প্ৰতিচ্ছবি বাছনি কৰক"</string>
diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml
index 2cbd681..88e99ce 100644
--- a/packages/SettingsLib/res/values-az/strings.xml
+++ b/packages/SettingsLib/res/values-az/strings.xml
@@ -553,7 +553,8 @@
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Yeni istifadəçi yaradılır…"</string>
     <string name="user_nickname" msgid="262624187455825083">"Ləqəb"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Qonaq əlavə edin"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"Qonağı silin"</string>
+    <!-- no translation found for guest_exit_guest (4754204715192830850) -->
+    <skip />
     <string name="guest_nickname" msgid="6332276931583337261">"Qonaq"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Foto çəkin"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Şəkil seçin"</string>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
index 4d8e348..4a820ea 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
@@ -450,7 +450,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Napuniće se za <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – napuniće se za <xliff:g id="TIME">%2$s</xliff:g>"</string>
-    <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g> – Optimizuje se radi stanja baterije"</string>
+    <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g> – Optimizuje se radi boljeg stanja baterije"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Nepoznato"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Puni se"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Brzo se puni"</string>
@@ -554,7 +554,8 @@
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Pravi se novi korisnik…"</string>
     <string name="user_nickname" msgid="262624187455825083">"Nadimak"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Dodaj gosta"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"Ukloni gosta"</string>
+    <!-- no translation found for guest_exit_guest (4754204715192830850) -->
+    <skip />
     <string name="guest_nickname" msgid="6332276931583337261">"Gost"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Slikaj"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Odaberi sliku"</string>
diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml
index 4d6f3ac..6a16246 100644
--- a/packages/SettingsLib/res/values-be/strings.xml
+++ b/packages/SettingsLib/res/values-be/strings.xml
@@ -555,7 +555,8 @@
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Ствараецца новы карыстальнік…"</string>
     <string name="user_nickname" msgid="262624187455825083">"Псеўданім"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Дадаць госця"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"Выдаліць госця"</string>
+    <!-- no translation found for guest_exit_guest (4754204715192830850) -->
+    <skip />
     <string name="guest_nickname" msgid="6332276931583337261">"Госць"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Зрабіць фота"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Выбраць відарыс"</string>
diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml
index ef0412b..07ba66a 100644
--- a/packages/SettingsLib/res/values-bg/strings.xml
+++ b/packages/SettingsLib/res/values-bg/strings.xml
@@ -450,7 +450,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Оставащо време до пълно зареждане: <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до пълно зареждане"</string>
-    <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g> – Оптимизиране за състоян. на батерията"</string>
+    <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g> – Оптимизиране с цел състоянието на батерията"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Неизвестно"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Зарежда се"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Зарежда се бързо"</string>
@@ -553,7 +553,7 @@
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Създава се нов потребител…"</string>
     <string name="user_nickname" msgid="262624187455825083">"Псевдоним"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Добавяне на гост"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"Премахване на госта"</string>
+    <string name="guest_exit_guest" msgid="4754204715192830850">"Прекратяване на сесията като гост"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Гост"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Правене на снимка"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Избиране на изображение"</string>
diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml
index 5b95399..99ee1d6 100644
--- a/packages/SettingsLib/res/values-bn/strings.xml
+++ b/packages/SettingsLib/res/values-bn/strings.xml
@@ -553,7 +553,8 @@
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"নতুন ব্যবহারকারী তৈরি করা হচ্ছে…"</string>
     <string name="user_nickname" msgid="262624187455825083">"বিশেষ নাম"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"অতিথি যোগ করুন"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"অতিথি সরান"</string>
+    <!-- no translation found for guest_exit_guest (4754204715192830850) -->
+    <skip />
     <string name="guest_nickname" msgid="6332276931583337261">"অতিথি"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"ফটো তুলুন"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"একটি ইমেজ বেছে নিন"</string>
diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml
index 00d8087..eedad34 100644
--- a/packages/SettingsLib/res/values-bs/strings.xml
+++ b/packages/SettingsLib/res/values-bs/strings.xml
@@ -554,7 +554,8 @@
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Kreiranje novog korisnika…"</string>
     <string name="user_nickname" msgid="262624187455825083">"Nadimak"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Dodaj gosta"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"Ukloni gosta"</string>
+    <!-- no translation found for guest_exit_guest (4754204715192830850) -->
+    <skip />
     <string name="guest_nickname" msgid="6332276931583337261">"Gost"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Snimite fotografiju"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Odaberite sliku"</string>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index 98d502d..209b2d6 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -450,7 +450,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> per completar la càrrega"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g> per completar la càrrega"</string>
-    <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g>: optimitzant per a l\'estat de la bateria"</string>
+    <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g>: s\'està optimitzant per preservar l\'estat de la bateria"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Desconegut"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"S\'està carregant"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Carregant ràpidament"</string>
@@ -553,7 +553,8 @@
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"S\'està creant l\'usuari…"</string>
     <string name="user_nickname" msgid="262624187455825083">"Àlies"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Afegeix un convidat"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"Suprimeix el convidat"</string>
+    <!-- no translation found for guest_exit_guest (4754204715192830850) -->
+    <skip />
     <string name="guest_nickname" msgid="6332276931583337261">"Convidat"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Fes una foto"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Tria una imatge"</string>
diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
index a9a5f48..b3141e4 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -555,7 +555,8 @@
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Vytváření nového uživatele…"</string>
     <string name="user_nickname" msgid="262624187455825083">"Přezdívka"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Přidat hosta"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"Odstranit hosta"</string>
+    <!-- no translation found for guest_exit_guest (4754204715192830850) -->
+    <skip />
     <string name="guest_nickname" msgid="6332276931583337261">"Host"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Pořídit fotku"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Vybrat obrázek"</string>
diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
index 738ed3c..a7fa91f 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -553,7 +553,8 @@
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Opretter ny bruger…"</string>
     <string name="user_nickname" msgid="262624187455825083">"Kaldenavn"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Tilføj gæsten"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"Fjern gæsten"</string>
+    <!-- no translation found for guest_exit_guest (4754204715192830850) -->
+    <skip />
     <string name="guest_nickname" msgid="6332276931583337261">"Gæst"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Tag et billede"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Vælg et billede"</string>
diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml
index e274d63..361b932 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -553,7 +553,8 @@
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Neuer Nutzer wird erstellt…"</string>
     <string name="user_nickname" msgid="262624187455825083">"Alias"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Gast hinzufügen"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"Gast entfernen"</string>
+    <!-- no translation found for guest_exit_guest (4754204715192830850) -->
+    <skip />
     <string name="guest_nickname" msgid="6332276931583337261">"Gast"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Foto machen"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Bild auswählen"</string>
diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml
index 66244eb..a3b3d26 100644
--- a/packages/SettingsLib/res/values-el/strings.xml
+++ b/packages/SettingsLib/res/values-el/strings.xml
@@ -450,7 +450,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Απομένουν <xliff:g id="TIME">%1$s</xliff:g> για ολοκλήρωση της φόρτισης"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> για την ολοκλήρωση της φόρτισης"</string>
-    <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g> - Βελτιστοποίηση κατάστασης μπαταρίας"</string>
+    <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g> - Βελτιστοποίηση για τη διατήρηση της καλής κατάστασης της μπαταρίας"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Άγνωστο"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Φόρτιση"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Ταχεία φόρτιση"</string>
@@ -553,7 +553,8 @@
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Δημιουργία νέου χρήστη…"</string>
     <string name="user_nickname" msgid="262624187455825083">"Ψευδώνυμο"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Προσθήκη επισκέπτη"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"Κατάργηση επισκέπτη"</string>
+    <!-- no translation found for guest_exit_guest (4754204715192830850) -->
+    <skip />
     <string name="guest_nickname" msgid="6332276931583337261">"Επισκέπτης"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Λήψη φωτογραφίας"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Επιλογή εικόνας"</string>
diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml
index f5a0e2c..fe7515f 100644
--- a/packages/SettingsLib/res/values-en-rAU/strings.xml
+++ b/packages/SettingsLib/res/values-en-rAU/strings.xml
@@ -553,7 +553,7 @@
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Creating new user…"</string>
     <string name="user_nickname" msgid="262624187455825083">"Nickname"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Add guest"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"Remove guest"</string>
+    <string name="guest_exit_guest" msgid="4754204715192830850">"End Guest session"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Guest"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Take a photo"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Choose an image"</string>
diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml
index 6f11e80..75a6678 100644
--- a/packages/SettingsLib/res/values-en-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-en-rCA/strings.xml
@@ -553,7 +553,7 @@
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Creating new user…"</string>
     <string name="user_nickname" msgid="262624187455825083">"Nickname"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Add guest"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"Remove guest"</string>
+    <string name="guest_exit_guest" msgid="4754204715192830850">"End Guest session"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Guest"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Take a photo"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Choose an image"</string>
diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml
index f5a0e2c..fe7515f 100644
--- a/packages/SettingsLib/res/values-en-rGB/strings.xml
+++ b/packages/SettingsLib/res/values-en-rGB/strings.xml
@@ -553,7 +553,7 @@
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Creating new user…"</string>
     <string name="user_nickname" msgid="262624187455825083">"Nickname"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Add guest"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"Remove guest"</string>
+    <string name="guest_exit_guest" msgid="4754204715192830850">"End Guest session"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Guest"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Take a photo"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Choose an image"</string>
diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml
index f5a0e2c..fe7515f 100644
--- a/packages/SettingsLib/res/values-en-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-en-rIN/strings.xml
@@ -553,7 +553,7 @@
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Creating new user…"</string>
     <string name="user_nickname" msgid="262624187455825083">"Nickname"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Add guest"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"Remove guest"</string>
+    <string name="guest_exit_guest" msgid="4754204715192830850">"End Guest session"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Guest"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Take a photo"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Choose an image"</string>
diff --git a/packages/SettingsLib/res/values-en-rXC/strings.xml b/packages/SettingsLib/res/values-en-rXC/strings.xml
index bfca1095..aed0800 100644
--- a/packages/SettingsLib/res/values-en-rXC/strings.xml
+++ b/packages/SettingsLib/res/values-en-rXC/strings.xml
@@ -553,7 +553,7 @@
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‏‎‎‏‎‎‎‏‏‎‎‎‎‎‎‏‎‏‏‎‏‎‎‏‎‎‎‏‎‏‏‏‎‏‎‏‎‏‏‎‎‎‏‎‏‏‏‏‎‏‎‎‏‏‎‎‎‎‎‏‏‏‎Creating new user…‎‏‎‎‏‎"</string>
     <string name="user_nickname" msgid="262624187455825083">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‎‏‎‏‏‏‎‏‎‎‏‎‏‎‎‎‎‎‏‏‏‎‏‎‎‏‏‏‎‏‎‎‏‏‎‏‎‏‎‏‏‏‎‏‎‏‏‏‎‎‏‎‎‏‎‏‏‏‎‏‏‎Nickname‎‏‎‎‏‎"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‎‏‏‎‎‎‎‎‏‎‏‎‎‏‎‏‎‏‎‎‏‎‎‎‏‎‎‏‏‏‏‎‏‏‎‏‏‎‎‎‎‎‎‎‏‏‏‏‏‏‎‎‏‏‎‏‏‏‎‎‏‎‏‎Add guest‎‏‎‎‏‎"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‏‎‎‎‏‏‏‏‏‏‏‏‎‎‏‎‎‏‎‎‎‏‎‎‏‎‏‎‏‏‏‎‏‎‏‎‎‏‎‎‏‏‎‎‎‎‎‎‏‏‎‎‏‎‎‏‏‏‎‎‎‎Remove guest‎‏‎‎‏‎"</string>
+    <string name="guest_exit_guest" msgid="4754204715192830850">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‎‏‎‎‏‏‏‏‏‏‏‏‏‏‏‏‏‎‎‏‏‎‏‎‎‎‏‏‏‎‎‏‎‎‏‎‏‏‏‎‎‎‎‎‏‎‎End guest session‎‏‎‎‏‎"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‏‎‏‏‏‏‏‏‎‎‎‎‎‏‏‎‎‎‏‎‎‎‏‎‏‎‎‎‏‏‏‎‎‎‏‏‎‎‏‏‎‏‏‎‎‏‏‏‎‎‎‏‏‎‎‏‎‏‏‎‏‎Guest‎‏‎‎‏‎"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‎‏‏‏‏‎‎‏‏‏‏‏‎‎‏‏‏‏‎‎‎‎‏‎‎‎‏‏‏‎‎‎‏‎‎‎‎‎‏‎‎‎‏‎‏‎‎‎‏‏‎‎‎‎‏‎‎‎‎‎‏‎‎Take a photo‎‏‎‎‏‎"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‏‎‏‏‏‏‎‏‏‎‎‏‎‏‏‏‎‏‏‎‏‎‏‎‎‎‎‏‏‏‏‏‎‎‏‏‎‎‏‏‎‏‏‏‎‏‏‏‏‏‏‎‏‏‎‏‎‎‎‎‎‏‎‏‏‏‏‎‎‎Choose an image‎‏‎‎‏‎"</string>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index 44ae8c2..f9260a8 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -553,7 +553,7 @@
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Creando usuario nuevo…"</string>
     <string name="user_nickname" msgid="262624187455825083">"Sobrenombre"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Agregar invitado"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"Quitar invitado"</string>
+    <string name="guest_exit_guest" msgid="4754204715192830850">"Finalizar sesión de invitado"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Invitado"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Tomar una foto"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Elegir una imagen"</string>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index 236394c..bc17623 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -553,7 +553,7 @@
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Creando usuario…"</string>
     <string name="user_nickname" msgid="262624187455825083">"Apodo"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Añadir invitado"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"Quitar invitado"</string>
+    <string name="guest_exit_guest" msgid="4754204715192830850">"Finalizar sesión de invitado"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Invitado"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Hacer foto"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Seleccionar una imagen"</string>
diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml
index c8fd94f..c73f136 100644
--- a/packages/SettingsLib/res/values-et/strings.xml
+++ b/packages/SettingsLib/res/values-et/strings.xml
@@ -553,7 +553,8 @@
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Uue kasutaja loomine …"</string>
     <string name="user_nickname" msgid="262624187455825083">"Hüüdnimi"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Lisa külaline"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"Eemalda külaline"</string>
+    <!-- no translation found for guest_exit_guest (4754204715192830850) -->
+    <skip />
     <string name="guest_nickname" msgid="6332276931583337261">"Külaline"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Pildistage"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Valige pilt"</string>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index 226ffba..2f51ac0 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -450,7 +450,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> guztiz kargatu arte"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> guztiz kargatu arte"</string>
-    <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g> - Optimizatzen bateria egoera onean mantentzeko"</string>
+    <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g> - Optimizatzen, bateria egoera onean mantentzeko"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Ezezaguna"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Kargatzen"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Bizkor kargatzen"</string>
@@ -553,7 +553,7 @@
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Beste erabiltzaile bat sortzen…"</string>
     <string name="user_nickname" msgid="262624187455825083">"Goitizena"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Gehitu gonbidatua"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"Kendu gonbidatua"</string>
+    <string name="guest_exit_guest" msgid="4754204715192830850">"Amaitu gonbidatuentzako saioa"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Gonbidatua"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Atera argazki bat"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Aukeratu irudi bat"</string>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index 917a637..038b815 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -553,7 +553,8 @@
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"درحال ایجاد کاربر جدید…"</string>
     <string name="user_nickname" msgid="262624187455825083">"نام مستعار"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"افزودن مهمان"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"حذف مهمان"</string>
+    <!-- no translation found for guest_exit_guest (4754204715192830850) -->
+    <skip />
     <string name="guest_nickname" msgid="6332276931583337261">"مهمان"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"عکس گرفتن"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"انتخاب تصویر"</string>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index b103802..33b6142 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -553,7 +553,8 @@
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Luodaan uutta käyttäjää…"</string>
     <string name="user_nickname" msgid="262624187455825083">"Lempinimi"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Lisää vieras"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"Poista vieras"</string>
+    <!-- no translation found for guest_exit_guest (4754204715192830850) -->
+    <skip />
     <string name="guest_nickname" msgid="6332276931583337261">"Vieras"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Ota kuva"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Valitse kuva"</string>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index 91d14d4..01492c2 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -450,7 +450,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> jusqu\'à la charge complète"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> : <xliff:g id="TIME">%2$s</xliff:g> jusqu\'à la charge complète"</string>
-    <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g> – Optimisation pour la santé de la pile"</string>
+    <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g> – Optimisation pour préserver la pile"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Inconnu"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Charge en cours…"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Recharge rapide"</string>
@@ -553,7 +553,7 @@
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Créer un utilisateur…"</string>
     <string name="user_nickname" msgid="262624187455825083">"Pseudo"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Ajouter un invité"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"Supprimer l\'invité"</string>
+    <string name="guest_exit_guest" msgid="4754204715192830850">"Mettre fin à la session d\'invité"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Invité"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Prendre une photo"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Sélectionner une image"</string>
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index 2518fe1..04ed5fe 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -553,7 +553,8 @@
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Création d\'un nouvel utilisateur…"</string>
     <string name="user_nickname" msgid="262624187455825083">"Pseudo"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Ajouter un invité"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"Supprimer l\'invité"</string>
+    <!-- no translation found for guest_exit_guest (4754204715192830850) -->
+    <skip />
     <string name="guest_nickname" msgid="6332276931583337261">"Invité"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Prendre une photo"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Choisir une image"</string>
diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml
index 486afe3..b59395c 100644
--- a/packages/SettingsLib/res/values-gl/strings.xml
+++ b/packages/SettingsLib/res/values-gl/strings.xml
@@ -450,7 +450,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> para completar a carga"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g>: <xliff:g id="TIME">%2$s</xliff:g> para completar a carga"</string>
-    <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g>: optimizando para manter a batería en bo estado"</string>
+    <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g>: optimizando a preservación da batería"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Descoñecido"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Cargando"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Cargando rapidamente"</string>
@@ -553,7 +553,8 @@
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Creando usuario novo…"</string>
     <string name="user_nickname" msgid="262624187455825083">"Alcume"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Engadir convidado"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"Quitar convidado"</string>
+    <!-- no translation found for guest_exit_guest (4754204715192830850) -->
+    <skip />
     <string name="guest_nickname" msgid="6332276931583337261">"Convidado"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Tirar foto"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Escoller imaxe"</string>
diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml
index 2bebe36..601ffde 100644
--- a/packages/SettingsLib/res/values-gu/strings.xml
+++ b/packages/SettingsLib/res/values-gu/strings.xml
@@ -553,7 +553,8 @@
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"નવા વપરાશકર્તા બનાવી રહ્યાં છીએ…"</string>
     <string name="user_nickname" msgid="262624187455825083">"ઉપનામ"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"અતિથિ ઉમેરો"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"અતિથિને કાઢી નાખો"</string>
+    <!-- no translation found for guest_exit_guest (4754204715192830850) -->
+    <skip />
     <string name="guest_nickname" msgid="6332276931583337261">"અતિથિ"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"ફોટો લો"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"છબી પસંદ કરો"</string>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index 824ab25..974d00c 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -553,7 +553,7 @@
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"नया उपयोगकर्ता बनाया जा रहा है…"</string>
     <string name="user_nickname" msgid="262624187455825083">"प्रचलित नाम"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"मेहमान जोड़ें"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"मेहमान हटाएं"</string>
+    <string name="guest_exit_guest" msgid="4754204715192830850">"मेहमान के तौर पर ब्राउज़ करने का सेशन खत्म करें"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"मेहमान"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"फ़ोटो खींचें"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"कोई इमेज चुनें"</string>
diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml
index cd21a5f..f792428 100644
--- a/packages/SettingsLib/res/values-hr/strings.xml
+++ b/packages/SettingsLib/res/values-hr/strings.xml
@@ -554,7 +554,8 @@
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Izrada novog korisnika…"</string>
     <string name="user_nickname" msgid="262624187455825083">"Nadimak"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Dodavanje gosta"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"Uklanjanje gosta"</string>
+    <!-- no translation found for guest_exit_guest (4754204715192830850) -->
+    <skip />
     <string name="guest_nickname" msgid="6332276931583337261">"Gost"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Fotografiraj"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Odaberi sliku"</string>
diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml
index c3b859c..2bf5325 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -553,7 +553,8 @@
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Új felhasználó létrehozása…"</string>
     <string name="user_nickname" msgid="262624187455825083">"Becenév"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Vendég hozzáadása"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"Vendég munkamenet eltávolítása"</string>
+    <!-- no translation found for guest_exit_guest (4754204715192830850) -->
+    <skip />
     <string name="guest_nickname" msgid="6332276931583337261">"Vendég"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Fotó készítése"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Kép kiválasztása"</string>
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index 041ad0e..fea1e12 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -450,7 +450,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> մինչև լիցքավորումը"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> մինչև լիցքավորումը"</string>
-    <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g> – Օպտիմալացվում է"</string>
+    <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g> – Օպտիմալացվում է մարտկոցի պահպանման համար"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Անհայտ"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Լիցքավորում"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Արագ լիցքավորում"</string>
@@ -553,7 +553,7 @@
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Ստեղծվում է օգտատիրոջ նոր պրոֆիլ…"</string>
     <string name="user_nickname" msgid="262624187455825083">"Կեղծանուն"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Ավելացնել հյուր"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"Հեռացնել հյուրին"</string>
+    <string name="guest_exit_guest" msgid="4754204715192830850">"Ավարտել հյուրի աշխատաշրջանը"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Հյուր"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Լուսանկարել"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Ընտրել պատկեր"</string>
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index 332d5b3..7298cea 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -553,7 +553,8 @@
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Membuat pengguna baru …"</string>
     <string name="user_nickname" msgid="262624187455825083">"Nama panggilan"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Tambahkan tamu"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"Hapus tamu"</string>
+    <!-- no translation found for guest_exit_guest (4754204715192830850) -->
+    <skip />
     <string name="guest_nickname" msgid="6332276931583337261">"Tamu"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Ambil foto"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Pilih gambar"</string>
diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml
index f401364..bf6c031 100644
--- a/packages/SettingsLib/res/values-is/strings.xml
+++ b/packages/SettingsLib/res/values-is/strings.xml
@@ -553,7 +553,8 @@
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Stofnar nýjan notanda…"</string>
     <string name="user_nickname" msgid="262624187455825083">"Gælunafn"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Bæta gesti við"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"Fjarlægja gest"</string>
+    <!-- no translation found for guest_exit_guest (4754204715192830850) -->
+    <skip />
     <string name="guest_nickname" msgid="6332276931583337261">"Gestur"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Taka mynd"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Velja mynd"</string>
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index ef5e2ff..2ed92e4 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -553,7 +553,8 @@
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Creazione nuovo utente…"</string>
     <string name="user_nickname" msgid="262624187455825083">"Nickname"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Aggiungi ospite"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"Rimuovi ospite"</string>
+    <!-- no translation found for guest_exit_guest (4754204715192830850) -->
+    <skip />
     <string name="guest_nickname" msgid="6332276931583337261">"Ospite"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Scatta una foto"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Scegli un\'immagine"</string>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index db2ffe5..bf4c35e 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -555,7 +555,8 @@
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"בתהליך יצירה של משתמש חדש…"</string>
     <string name="user_nickname" msgid="262624187455825083">"כינוי"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"הוספת אורח"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"הסרת אורח"</string>
+    <!-- no translation found for guest_exit_guest (4754204715192830850) -->
+    <skip />
     <string name="guest_nickname" msgid="6332276931583337261">"אורח"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"צילום תמונה"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"לבחירת תמונה"</string>
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index 9e9db3d..f467c60 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -553,7 +553,7 @@
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"新しいユーザーを作成しています…"</string>
     <string name="user_nickname" msgid="262624187455825083">"ニックネーム"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"ゲストを追加"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"ゲストを削除"</string>
+    <string name="guest_exit_guest" msgid="4754204715192830850">"ゲスト セッションを終了する"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"ゲスト"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"写真を撮る"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"画像を選択"</string>
diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml
index ec39008..493839f 100644
--- a/packages/SettingsLib/res/values-ka/strings.xml
+++ b/packages/SettingsLib/res/values-ka/strings.xml
@@ -553,7 +553,8 @@
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"მიმდინარეობს ახალი მომხმარებლის შექმნა…"</string>
     <string name="user_nickname" msgid="262624187455825083">"მეტსახელი"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"სტუმრის დამატება"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"სტუმრის ამოშლა"</string>
+    <!-- no translation found for guest_exit_guest (4754204715192830850) -->
+    <skip />
     <string name="guest_nickname" msgid="6332276931583337261">"სტუმარი"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"ფოტოს გადაღება"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"აირჩიეთ სურათი"</string>
diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml
index 881a13c..83d859b 100644
--- a/packages/SettingsLib/res/values-kk/strings.xml
+++ b/packages/SettingsLib/res/values-kk/strings.xml
@@ -450,7 +450,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Зарядталғанға дейін <xliff:g id="TIME">%1$s</xliff:g> қалды"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – зарядталғанға дейін <xliff:g id="TIME">%2$s</xliff:g>"</string>
-    <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g> - Батареяның жұмыс істеу қабілеті оңтайландырылуда"</string>
+    <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g> - Батарея жұмысын оңтайландыру"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Белгісіз"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Зарядталуда"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Жылдам зарядталуда"</string>
@@ -553,7 +553,8 @@
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Жаңа пайдаланушы профилі жасалуда…"</string>
     <string name="user_nickname" msgid="262624187455825083">"Лақап ат"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Қонақты енгізу"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"Қонақты өшіру"</string>
+    <!-- no translation found for guest_exit_guest (4754204715192830850) -->
+    <skip />
     <string name="guest_nickname" msgid="6332276931583337261">"Қонақ"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Фотосуретке түсіру"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Сурет таңдау"</string>
diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml
index 14f7788..db571d6 100644
--- a/packages/SettingsLib/res/values-km/strings.xml
+++ b/packages/SettingsLib/res/values-km/strings.xml
@@ -553,7 +553,8 @@
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"កំពុងបង្កើត​អ្នកប្រើប្រាស់ថ្មី…"</string>
     <string name="user_nickname" msgid="262624187455825083">"ឈ្មោះ​ហៅក្រៅ"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"បញ្ចូល​ភ្ញៀវ"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"លុប​​​ភ្ញៀវ"</string>
+    <!-- no translation found for guest_exit_guest (4754204715192830850) -->
+    <skip />
     <string name="guest_nickname" msgid="6332276931583337261">"ភ្ញៀវ"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"ថតរូប"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"ជ្រើសរើស​រូបភាព"</string>
diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml
index a279d22..2e8b9f1 100644
--- a/packages/SettingsLib/res/values-kn/strings.xml
+++ b/packages/SettingsLib/res/values-kn/strings.xml
@@ -450,7 +450,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"ಚಾರ್ಜ್ ಆಗಲು <xliff:g id="TIME">%1$s</xliff:g> ಸಮಯ ಬಾಕಿ ಇದೆ"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - ಚಾರ್ಜ್ ಆಗಲು <xliff:g id="TIME">%2$s</xliff:g> ಸಮಯ ಬೇಕು"</string>
-    <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g> - ಬ್ಯಾಟರಿಯ ಆರೋಗ್ಯಕ್ಕಾಗಿ ಆಪ್ಟಿಮೈಸ್ ಮಾಡಲಾಗುತ್ತಿದೆ"</string>
+    <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g> - ಬ್ಯಾಟರಿಯ ಸುಸ್ಥಿತಿಗಾಗಿ ಆಪ್ಟಿಮೈಸ್ ಮಾಡಲಾಗುತ್ತಿದೆ"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"ಅಪರಿಚಿತ"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"ಚಾರ್ಜ್ ಆಗುತ್ತಿದೆ"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"ವೇಗದ ಚಾರ್ಜಿಂಗ್"</string>
@@ -553,7 +553,8 @@
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"ಹೊಸ ಬಳಕೆದಾರರನ್ನು ರಚಿಸಲಾಗುತ್ತಿದೆ…"</string>
     <string name="user_nickname" msgid="262624187455825083">"ಅಡ್ಡ ಹೆಸರು"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"ಅತಿಥಿಯನ್ನು ಸೇರಿಸಿ"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"ಅತಿಥಿಯನ್ನು ತೆಗೆದುಹಾಕಿ"</string>
+    <!-- no translation found for guest_exit_guest (4754204715192830850) -->
+    <skip />
     <string name="guest_nickname" msgid="6332276931583337261">"ಅತಿಥಿ"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"ಫೋಟೋ ತೆಗೆದುಕೊಳ್ಳಿ"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"ಚಿತ್ರವನ್ನು ಆರಿಸಿ"</string>
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index de69488..0294e9d 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -553,7 +553,8 @@
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"새로운 사용자를 만드는 중…"</string>
     <string name="user_nickname" msgid="262624187455825083">"닉네임"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"게스트 추가"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"게스트 삭제"</string>
+    <!-- no translation found for guest_exit_guest (4754204715192830850) -->
+    <skip />
     <string name="guest_nickname" msgid="6332276931583337261">"게스트"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"사진 찍기"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"이미지 선택"</string>
diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml
index e70460f..0c73cf6 100644
--- a/packages/SettingsLib/res/values-ky/strings.xml
+++ b/packages/SettingsLib/res/values-ky/strings.xml
@@ -553,7 +553,8 @@
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Жаңы колдонуучу түзүлүүдө…"</string>
     <string name="user_nickname" msgid="262624187455825083">"Ылакап аты"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Конок кошуу"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"Конокту өчүрүү"</string>
+    <!-- no translation found for guest_exit_guest (4754204715192830850) -->
+    <skip />
     <string name="guest_nickname" msgid="6332276931583337261">"Конок"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Сүрөткө тартуу"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Сүрөт тандаңыз"</string>
diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml
index c63cec8..fbe814a 100644
--- a/packages/SettingsLib/res/values-lo/strings.xml
+++ b/packages/SettingsLib/res/values-lo/strings.xml
@@ -553,7 +553,8 @@
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"ກຳລັງສ້າງຜູ້ໃຊ້ໃໝ່…"</string>
     <string name="user_nickname" msgid="262624187455825083">"ຊື່ຫຼິ້ນ"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"ເພີ່ມແຂກ"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"ລຶບແຂກອອກ"</string>
+    <!-- no translation found for guest_exit_guest (4754204715192830850) -->
+    <skip />
     <string name="guest_nickname" msgid="6332276931583337261">"ແຂກ"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"ຖ່າຍຮູບ"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"ເລືອກຮູບ"</string>
diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml
index d040edf..b3d8e17 100644
--- a/packages/SettingsLib/res/values-lt/strings.xml
+++ b/packages/SettingsLib/res/values-lt/strings.xml
@@ -555,7 +555,8 @@
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Kuriamas naujas naudotojas…"</string>
     <string name="user_nickname" msgid="262624187455825083">"Slapyvardis"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Pridėti svečią"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"Pašalinti svečią"</string>
+    <!-- no translation found for guest_exit_guest (4754204715192830850) -->
+    <skip />
     <string name="guest_nickname" msgid="6332276931583337261">"Svečias"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Fotografuoti"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Pasirinkti vaizdą"</string>
diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml
index 74c0dd5..3c69e64 100644
--- a/packages/SettingsLib/res/values-lv/strings.xml
+++ b/packages/SettingsLib/res/values-lv/strings.xml
@@ -554,7 +554,8 @@
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Notiek jauna lietotāja izveide…"</string>
     <string name="user_nickname" msgid="262624187455825083">"Segvārds"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Pievienot viesi"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"Noņemt viesi"</string>
+    <!-- no translation found for guest_exit_guest (4754204715192830850) -->
+    <skip />
     <string name="guest_nickname" msgid="6332276931583337261">"Viesis"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Uzņemt fotoattēlu"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Izvēlēties attēlu"</string>
diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml
index 739bceb..b909359 100644
--- a/packages/SettingsLib/res/values-mk/strings.xml
+++ b/packages/SettingsLib/res/values-mk/strings.xml
@@ -553,7 +553,8 @@
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Се создава нов корисник…"</string>
     <string name="user_nickname" msgid="262624187455825083">"Прекар"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Додај гостин"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"Отстрани гостин"</string>
+    <!-- no translation found for guest_exit_guest (4754204715192830850) -->
+    <skip />
     <string name="guest_nickname" msgid="6332276931583337261">"Гостин"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Фотографирајте"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Одберете слика"</string>
diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml
index b7de429..8f83ee1 100644
--- a/packages/SettingsLib/res/values-ml/strings.xml
+++ b/packages/SettingsLib/res/values-ml/strings.xml
@@ -553,7 +553,8 @@
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"പുതിയ ഉപയോക്താവിനെ സൃഷ്‌ടിക്കുന്നു…"</string>
     <string name="user_nickname" msgid="262624187455825083">"വിളിപ്പേര്"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"അതിഥിയെ ചേർക്കുക"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"അതിഥിയെ നീക്കം ചെയ്യുക"</string>
+    <!-- no translation found for guest_exit_guest (4754204715192830850) -->
+    <skip />
     <string name="guest_nickname" msgid="6332276931583337261">"അതിഥി"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"ഒരു ഫോട്ടോ എടുക്കുക"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"ഒരു ചിത്രം തിരഞ്ഞെടുക്കുക"</string>
diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml
index c7a2edd..8168b79 100644
--- a/packages/SettingsLib/res/values-mn/strings.xml
+++ b/packages/SettingsLib/res/values-mn/strings.xml
@@ -450,7 +450,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Цэнэглэх хүртэл үлдсэн <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - цэнэглэх хүртэл <xliff:g id="TIME">%2$s</xliff:g>"</string>
-    <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g> - Батарейн чанарыг оновчилж байна"</string>
+    <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g> - Батарейн барилтыг оновчилж байна"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Тодорхойгүй"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Цэнэглэж байна"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Хурдан цэнэглэж байна"</string>
@@ -553,7 +553,7 @@
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Шинэ хэрэглэгч үүсгэж байна…"</string>
     <string name="user_nickname" msgid="262624187455825083">"Хоч"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Зочин нэмэх"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"Зочин хасах"</string>
+    <string name="guest_exit_guest" msgid="4754204715192830850">"Зочны сургалтыг дуусгах"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Зочин"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Зураг авах"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Зураг сонгох"</string>
diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index d29cb59..f47d8e0 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -553,7 +553,8 @@
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"नवीन वापरकर्ता तयार करत आहे…"</string>
     <string name="user_nickname" msgid="262624187455825083">"टोपणनाव"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"अतिथी जोडा"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"अतिथी काढून टाका"</string>
+    <!-- no translation found for guest_exit_guest (4754204715192830850) -->
+    <skip />
     <string name="guest_nickname" msgid="6332276931583337261">"अतिथी"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"फोटो काढा"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"इमेज निवडा"</string>
diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml
index c593e4b..fb2826f 100644
--- a/packages/SettingsLib/res/values-ms/strings.xml
+++ b/packages/SettingsLib/res/values-ms/strings.xml
@@ -553,7 +553,7 @@
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Mencipta pengguna baharu…"</string>
     <string name="user_nickname" msgid="262624187455825083">"Nama panggilan"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Tambah tetamu"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"Alih keluar tetamu"</string>
+    <string name="guest_exit_guest" msgid="4754204715192830850">"Tamatkan sesi tetamu"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Tetamu"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Ambil foto"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Pilih imej"</string>
diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml
index aa9d3d6..d98d442 100644
--- a/packages/SettingsLib/res/values-my/strings.xml
+++ b/packages/SettingsLib/res/values-my/strings.xml
@@ -553,7 +553,8 @@
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"အသုံးပြုသူအသစ် ပြုလုပ်နေသည်…"</string>
     <string name="user_nickname" msgid="262624187455825083">"နာမည်ပြောင်"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"ဧည့်သည့် ထည့်ရန်"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"ဧည့်သည်ကို ဖယ်ထုတ်ရန်"</string>
+    <!-- no translation found for guest_exit_guest (4754204715192830850) -->
+    <skip />
     <string name="guest_nickname" msgid="6332276931583337261">"ဧည့်သည်"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"ဓာတ်ပုံရိုက်ရန်"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"ပုံရွေးရန်"</string>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index 9494c39..944c48e 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -450,7 +450,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> til batteriet er fulladet"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> til batteriet er fulladet"</string>
-    <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g> – Optimaliserer batteritilstanden"</string>
+    <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g> – optimaliserer batteritilstanden"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Ukjent"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Lader"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Lader raskt"</string>
@@ -553,7 +553,8 @@
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Oppretter en ny bruker …"</string>
     <string name="user_nickname" msgid="262624187455825083">"Kallenavn"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Legg til en gjest"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"Fjern gjesten"</string>
+    <!-- no translation found for guest_exit_guest (4754204715192830850) -->
+    <skip />
     <string name="guest_nickname" msgid="6332276931583337261">"Gjest"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Ta et bilde"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Velg et bilde"</string>
diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml
index e31fb4e..9a0d2ed 100644
--- a/packages/SettingsLib/res/values-ne/strings.xml
+++ b/packages/SettingsLib/res/values-ne/strings.xml
@@ -553,7 +553,8 @@
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"नयाँ प्रयोगकर्ता बनाउँदै…"</string>
     <string name="user_nickname" msgid="262624187455825083">"उपनाम"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"अतिथि थप्नुहोस्"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"अतिथि हटाउनुहोस्"</string>
+    <!-- no translation found for guest_exit_guest (4754204715192830850) -->
+    <skip />
     <string name="guest_nickname" msgid="6332276931583337261">"अतिथि"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"फोटो खिच्नुहोस्"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"कुनै फोटो छनौट गर्नुहोस्"</string>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index 0b75fdd..a8e4fb5 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -553,7 +553,7 @@
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Nieuwe gebruiker maken…"</string>
     <string name="user_nickname" msgid="262624187455825083">"Bijnaam"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Gast toevoegen"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"Gast verwijderen"</string>
+    <string name="guest_exit_guest" msgid="4754204715192830850">"Gastsessie beëindigen"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Gast"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Foto maken"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Afbeelding kiezen"</string>
diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml
index cfde61f..306ef44 100644
--- a/packages/SettingsLib/res/values-or/strings.xml
+++ b/packages/SettingsLib/res/values-or/strings.xml
@@ -553,7 +553,8 @@
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"ନୂଆ ଉପଯୋଗକର୍ତ୍ତା ତିଆରି କରାଯାଉଛି…"</string>
     <string name="user_nickname" msgid="262624187455825083">"ଡାକନାମ"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"ଅତିଥି ଯୋଗ କରନ୍ତୁ"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"ଅତିଥିଙ୍କୁ କାଢ଼ି ଦିଅନ୍ତୁ"</string>
+    <!-- no translation found for guest_exit_guest (4754204715192830850) -->
+    <skip />
     <string name="guest_nickname" msgid="6332276931583337261">"ଅତିଥି"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"ଗୋଟିଏ ଫଟୋ ଉଠାନ୍ତୁ"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"ଏକ ଛବି ବାଛନ୍ତୁ"</string>
diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml
index 1221862..e231a26 100644
--- a/packages/SettingsLib/res/values-pa/strings.xml
+++ b/packages/SettingsLib/res/values-pa/strings.xml
@@ -450,7 +450,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"ਚਾਰਜ ਹੋਣ ਵਿੱਚ <xliff:g id="TIME">%1$s</xliff:g> ਬਾਕੀ"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> ਤੱਕ ਚਾਰਜ ਹੋ ਜਾਵੇਗੀ"</string>
-    <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g> - ਬੈਟਰੀ ਸਥਿਤੀ ਲਈ ਅਨੁਕੂਲ ਬਣਾਇਆ ਜਾ ਰਿਹਾ"</string>
+    <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g> - ਬੈਟਰੀ ਦੀ ਸਥਿਤੀ ਲਈ ਅਨੁਕੂਲ ਬਣਾਇਆ ਜਾ ਰਿਹਾ ਹੈ"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"ਅਗਿਆਤ"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"ਚਾਰਜ ਹੋ ਰਿਹਾ ਹੈ"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"ਤੇਜ਼ ਚਾਰਜ ਹੋ ਰਹੀ ਹੈ"</string>
@@ -553,7 +553,8 @@
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"ਨਵਾਂ ਵਰਤੋਂਕਾਰ ਬਣਾਇਆ ਜਾ ਰਿਹਾ ਹੈ…"</string>
     <string name="user_nickname" msgid="262624187455825083">"ਉਪਨਾਮ"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"ਮਹਿਮਾਨ ਸ਼ਾਮਲ ਕਰੋ"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"ਮਹਿਮਾਨ ਹਟਾਓ"</string>
+    <!-- no translation found for guest_exit_guest (4754204715192830850) -->
+    <skip />
     <string name="guest_nickname" msgid="6332276931583337261">"ਮਹਿਮਾਨ"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"ਇੱਕ ਫ਼ੋਟੋ ਖਿੱਚੋ"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"ਕੋਈ ਚਿੱਤਰ ਚੁਣੋ"</string>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index 102835c..a62e9cc 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -450,7 +450,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Do naładowania <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – do naładowania <xliff:g id="TIME">%2$s</xliff:g>"</string>
-    <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g> – Optymalizuję, by utrzymać baterię w dobrym stanie"</string>
+    <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g> – Optymalizuję, aby utrzymać baterię w dobrym stanie"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Nieznane"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Ładowanie"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Szybkie ładowanie"</string>
@@ -555,7 +555,8 @@
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Tworzę nowego użytkownika…"</string>
     <string name="user_nickname" msgid="262624187455825083">"Pseudonim"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Dodaj gościa"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"Usuń gościa"</string>
+    <!-- no translation found for guest_exit_guest (4754204715192830850) -->
+    <skip />
     <string name="guest_nickname" msgid="6332276931583337261">"Gość"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Zrób zdjęcie"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Wybierz obraz"</string>
diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml
index 99019a6..fe922c0 100644
--- a/packages/SettingsLib/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml
@@ -553,7 +553,8 @@
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Criando novo usuário…"</string>
     <string name="user_nickname" msgid="262624187455825083">"Apelido"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Adicionar convidado"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"Remover convidado"</string>
+    <!-- no translation found for guest_exit_guest (4754204715192830850) -->
+    <skip />
     <string name="guest_nickname" msgid="6332276931583337261">"Convidado"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Tirar uma foto"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Escolher uma imagem"</string>
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index 238e54b..5d1f880 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -553,7 +553,8 @@
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"A criar novo utilizador…"</string>
     <string name="user_nickname" msgid="262624187455825083">"Pseudónimo"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Adicionar convidado"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"Remover convidado"</string>
+    <!-- no translation found for guest_exit_guest (4754204715192830850) -->
+    <skip />
     <string name="guest_nickname" msgid="6332276931583337261">"Convidado"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Tirar uma foto"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Escolher uma imagem"</string>
diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml
index 99019a6..fe922c0 100644
--- a/packages/SettingsLib/res/values-pt/strings.xml
+++ b/packages/SettingsLib/res/values-pt/strings.xml
@@ -553,7 +553,8 @@
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Criando novo usuário…"</string>
     <string name="user_nickname" msgid="262624187455825083">"Apelido"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Adicionar convidado"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"Remover convidado"</string>
+    <!-- no translation found for guest_exit_guest (4754204715192830850) -->
+    <skip />
     <string name="guest_nickname" msgid="6332276931583337261">"Convidado"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Tirar uma foto"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Escolher uma imagem"</string>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index df95dbc..765f83a 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -554,7 +554,8 @@
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Se creează un utilizator nou…"</string>
     <string name="user_nickname" msgid="262624187455825083">"Pseudonim"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Adăugați un invitat"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"Ștergeți invitatul"</string>
+    <!-- no translation found for guest_exit_guest (4754204715192830850) -->
+    <skip />
     <string name="guest_nickname" msgid="6332276931583337261">"Invitat"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Faceți o fotografie"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Alegeți o imagine"</string>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index 68826d7..98b3203 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -555,7 +555,8 @@
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Создаем нового пользователя…"</string>
     <string name="user_nickname" msgid="262624187455825083">"Псевдоним"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Добавить аккаунт гостя"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"Удалить аккаунт гостя"</string>
+    <!-- no translation found for guest_exit_guest (4754204715192830850) -->
+    <skip />
     <string name="guest_nickname" msgid="6332276931583337261">"Гость"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Сделать снимок"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Выбрать фото"</string>
diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml
index 736445d..087f1403 100644
--- a/packages/SettingsLib/res/values-si/strings.xml
+++ b/packages/SettingsLib/res/values-si/strings.xml
@@ -450,7 +450,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"ආරෝපණය වන තෙක් <xliff:g id="TIME">%1$s</xliff:g> ඇත"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - ආරෝපණය වන තෙක් <xliff:g id="TIME">%2$s</xliff:g>"</string>
-    <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g> - බැටරි සෞඛ්‍යය සඳහා ප්‍රශස්ත කරමින්"</string>
+    <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g> - බැටරි ආයු කාලය වැඩි දියුණු කරමින්"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"නොදනී"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"ආරෝපණය වෙමින්"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"ශීඝ්‍ර ආරෝපණය"</string>
@@ -553,7 +553,8 @@
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"නව පරිශීලක තනමින්…"</string>
     <string name="user_nickname" msgid="262624187455825083">"අපනාමය"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"අමුත්තා එක් කරන්න"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"අමුත්තා ඉවත් කරන්න"</string>
+    <!-- no translation found for guest_exit_guest (4754204715192830850) -->
+    <skip />
     <string name="guest_nickname" msgid="6332276931583337261">"අමුත්තා"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"ඡායාරූපයක් ගන්න"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"රූපයක් තෝරන්න"</string>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index 265ec8f..858f017 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -555,7 +555,8 @@
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Vytvára sa nový používateľ…"</string>
     <string name="user_nickname" msgid="262624187455825083">"Prezývka"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Pridať hosťa"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"Odobrať hosťa"</string>
+    <!-- no translation found for guest_exit_guest (4754204715192830850) -->
+    <skip />
     <string name="guest_nickname" msgid="6332276931583337261">"Hosť"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Odfotiť"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Vybrať obrázok"</string>
diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml
index 2e6fc0e..2386b13 100644
--- a/packages/SettingsLib/res/values-sl/strings.xml
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -450,7 +450,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Še <xliff:g id="TIME">%1$s</xliff:g> do polne napolnjenosti"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> do polne napolnjenosti"</string>
-    <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g> – Optimizacija za ohran. zmog. baterije"</string>
+    <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g> – Optimizacija za ohranjanje zmogljivosti baterije"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Neznano"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Polnjenje"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Hitro polnjenje"</string>
@@ -555,7 +555,8 @@
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Ustvarjanje novega uporabnika …"</string>
     <string name="user_nickname" msgid="262624187455825083">"Vzdevek"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Dodajanje gosta"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"Odstranitev gosta"</string>
+    <!-- no translation found for guest_exit_guest (4754204715192830850) -->
+    <skip />
     <string name="guest_nickname" msgid="6332276931583337261">"Gost"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Fotografiranje"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Izberi sliko"</string>
diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml
index 821e897b..8d53b25 100644
--- a/packages/SettingsLib/res/values-sq/strings.xml
+++ b/packages/SettingsLib/res/values-sq/strings.xml
@@ -553,7 +553,7 @@
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Po krijohet një përdorues i ri…"</string>
     <string name="user_nickname" msgid="262624187455825083">"Pseudonimi"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Shto të ftuar"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"Hiq të ftuarin"</string>
+    <string name="guest_exit_guest" msgid="4754204715192830850">"Jepi fund sesionit të vizitorit"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"I ftuar"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Bëj një fotografi"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Zgjidh një imazh"</string>
diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml
index 207ca74..0b64a70 100644
--- a/packages/SettingsLib/res/values-sr/strings.xml
+++ b/packages/SettingsLib/res/values-sr/strings.xml
@@ -450,7 +450,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"Напуниће се за <xliff:g id="TIME">%1$s</xliff:g>"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – напуниће се за <xliff:g id="TIME">%2$s</xliff:g>"</string>
-    <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g> – Оптимизује се ради стања батерије"</string>
+    <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g> – Оптимизује се ради бољег стања батерије"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Непознато"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Пуни се"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Брзо се пуни"</string>
@@ -554,7 +554,8 @@
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Прави се нови корисник…"</string>
     <string name="user_nickname" msgid="262624187455825083">"Надимак"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Додај госта"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"Уклони госта"</string>
+    <!-- no translation found for guest_exit_guest (4754204715192830850) -->
+    <skip />
     <string name="guest_nickname" msgid="6332276931583337261">"Гост"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Сликај"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Одабери слику"</string>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index 7afd344..58eda86 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -553,7 +553,8 @@
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Skapar ny användare …"</string>
     <string name="user_nickname" msgid="262624187455825083">"Smeknamn"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Lägg till gäst"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"Ta bort gäst"</string>
+    <!-- no translation found for guest_exit_guest (4754204715192830850) -->
+    <skip />
     <string name="guest_nickname" msgid="6332276931583337261">"Gäst"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Ta ett foto"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Välj en bild"</string>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index b08da89..889163b 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -492,8 +492,8 @@
     <string name="status_unavailable" msgid="5279036186589861608">"Hamna"</string>
     <string name="wifi_status_mac_randomized" msgid="466382542497832189">"Imechagua anwani ya MAC kwa nasibu"</string>
     <plurals name="wifi_tether_connected_summary" formatted="false" msgid="6317236306047306139">
-      <item quantity="other">Imeunganisha vifaa %1$d</item>
-      <item quantity="one">Imeunganisha kifaa %1$d</item>
+      <item quantity="other">Vifaa %1$d vimeunganishwa</item>
+      <item quantity="one">Kifaa %1$d kimeunganishwa</item>
     </plurals>
     <string name="accessibility_manual_zen_more_time" msgid="5141801092071134235">"Muda zaidi."</string>
     <string name="accessibility_manual_zen_less_time" msgid="6828877595848229965">"Muda kidogo."</string>
@@ -553,7 +553,8 @@
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Inaweka mtumiaji mpya…"</string>
     <string name="user_nickname" msgid="262624187455825083">"Jina wakilishi"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Weka mgeni"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"Ondoa mgeni"</string>
+    <!-- no translation found for guest_exit_guest (4754204715192830850) -->
+    <skip />
     <string name="guest_nickname" msgid="6332276931583337261">"Mgeni"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Piga picha"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Chagua picha"</string>
diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml
index 5fdf8bd..e0de778 100644
--- a/packages/SettingsLib/res/values-ta/strings.xml
+++ b/packages/SettingsLib/res/values-ta/strings.xml
@@ -553,7 +553,8 @@
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"புதிய பயனரை உருவாக்குகிறது…"</string>
     <string name="user_nickname" msgid="262624187455825083">"புனைப்பெயர்"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"கெஸ்ட்டைச் சேர்"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"கெஸ்ட்டை அகற்று"</string>
+    <!-- no translation found for guest_exit_guest (4754204715192830850) -->
+    <skip />
     <string name="guest_nickname" msgid="6332276931583337261">"கெஸ்ட்"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"படமெடுங்கள்"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"படத்தைத் தேர்வுசெய்யுங்கள்"</string>
diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml
index 38f08b2..53f4abf 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -450,7 +450,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"ఛార్జ్ అవ్వడానికి <xliff:g id="TIME">%1$s</xliff:g> సమయం మిగిలి ఉంది"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - ఛార్జ్ అవ్వడానికి <xliff:g id="TIME">%2$s</xliff:g> పడుతుంది"</string>
-    <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g> - బ్యాటరీ స్థితిని ఆప్టిమైజ్ చేయడం కోసం"</string>
+    <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g> - బ్యాటరీ జీవితకాలాన్ని పెంచడం కోసం ఆప్టిమైజ్ చేస్తోంది"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"తెలియదు"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"ఛార్జ్ అవుతోంది"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"వేగవంతమైన ఛార్జింగ్"</string>
@@ -553,7 +553,8 @@
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"కొత్త యూజర్‌ను క్రియేట్ చేస్తోంది…"</string>
     <string name="user_nickname" msgid="262624187455825083">"మారుపేరు"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"అతిథిని జోడించండి"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"అతిథిని తీసివేయండి"</string>
+    <!-- no translation found for guest_exit_guest (4754204715192830850) -->
+    <skip />
     <string name="guest_nickname" msgid="6332276931583337261">"అతిథి"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"ఒక ఫోటో తీయండి"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"ఇమేజ్‌ను ఎంచుకోండి"</string>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index a895138..5f93563 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -284,7 +284,7 @@
     <string name="wifi_verbose_logging_summary" msgid="4993823188807767892">"เพิ่มระดับการบันทึก Wi‑Fi แสดงต่อ SSID RSSI ในตัวเลือก Wi‑Fi"</string>
     <string name="wifi_scan_throttling_summary" msgid="2577105472017362814">"ลดการเปลืองแบตเตอรี่และเพิ่มประสิทธิภาพเครือข่าย"</string>
     <string name="wifi_enhanced_mac_randomization_summary" msgid="1210663439867489931">"เมื่อเปิดใช้โหมดนี้ ที่อยู่ MAC ของอุปกรณ์นี้อาจเปลี่ยนทุกครั้งที่เชื่อมต่อกับเครือข่ายที่มีการเปิดใช้การสุ่ม MAC"</string>
-    <string name="wifi_metered_label" msgid="8737187690304098638">"แบบจำกัดปริมาณอินเทอร์เน็ต"</string>
+    <string name="wifi_metered_label" msgid="8737187690304098638">"แบบจำกัดปริมาณ"</string>
     <string name="wifi_unmetered_label" msgid="6174142840934095093">"ไม่มีการวัดปริมาณอินเทอร์เน็ต"</string>
     <string name="select_logd_size_title" msgid="1604578195914595173">"ขนาดบัฟเฟอร์ของตัวบันทึก"</string>
     <string name="select_logd_size_dialog_title" msgid="2105401994681013578">"เลือกขนาด Logger ต่อบัฟเฟอร์ไฟล์บันทึก"</string>
@@ -553,7 +553,7 @@
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"กำลังสร้างผู้ใช้ใหม่…"</string>
     <string name="user_nickname" msgid="262624187455825083">"ชื่อเล่น"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"เพิ่มผู้เข้าร่วม"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"นำผู้เข้าร่วมออก"</string>
+    <string name="guest_exit_guest" msgid="4754204715192830850">"จบเซสชันผู้เยี่ยมชม"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"ผู้ใช้ชั่วคราว"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"ถ่ายรูป"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"เลือกรูปภาพ"</string>
diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml
index b451d89..9bbfb1d 100644
--- a/packages/SettingsLib/res/values-tl/strings.xml
+++ b/packages/SettingsLib/res/values-tl/strings.xml
@@ -553,7 +553,7 @@
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Gumagawa ng bagong user…"</string>
     <string name="user_nickname" msgid="262624187455825083">"Nickname"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Magdagdag ng bisita"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"Alisin ang bisita"</string>
+    <string name="guest_exit_guest" msgid="4754204715192830850">"Tapusin ang session ng bisita"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Bisita"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Kumuha ng larawan"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Pumili ng larawan"</string>
diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml
index e1ff56b..9dd32b6 100644
--- a/packages/SettingsLib/res/values-tr/strings.xml
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -553,7 +553,7 @@
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Yeni kullanıcı oluşturuluyor…"</string>
     <string name="user_nickname" msgid="262624187455825083">"Takma ad"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Misafir ekle"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"Misafir oturumunu kaldır"</string>
+    <string name="guest_exit_guest" msgid="4754204715192830850">"Misafir oturumunu sonlandır"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Misafir"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Fotoğraf çek"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Resim seç"</string>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index 6806ce6..99cc958 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -450,7 +450,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> до повного заряду"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> до повного заряду"</string>
-    <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g> – стан акумулятора оптимізується"</string>
+    <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g> – Оптимізація для збереження заряду акумулятора"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Невідомо"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Заряджається"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Швидке заряджання"</string>
@@ -555,7 +555,8 @@
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Створення нового користувача…"</string>
     <string name="user_nickname" msgid="262624187455825083">"Псевдонім"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Додати гостя"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"Видалити гостя"</string>
+    <!-- no translation found for guest_exit_guest (4754204715192830850) -->
+    <skip />
     <string name="guest_nickname" msgid="6332276931583337261">"Гість"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Зробити фотографію"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Вибрати зображення"</string>
diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml
index 5a8b6dc..515cf70 100644
--- a/packages/SettingsLib/res/values-ur/strings.xml
+++ b/packages/SettingsLib/res/values-ur/strings.xml
@@ -553,7 +553,8 @@
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"نیا صارف تخلیق کرنا…"</string>
     <string name="user_nickname" msgid="262624187455825083">"عرفی نام"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"مہمان کو شامل کریں"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"مہمان کو ہٹائیں"</string>
+    <!-- no translation found for guest_exit_guest (4754204715192830850) -->
+    <skip />
     <string name="guest_nickname" msgid="6332276931583337261">"مہمان"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"ایک تصویر لیں"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"ایک تصویر منتخب کریں"</string>
diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml
index 7530016..1830046 100644
--- a/packages/SettingsLib/res/values-uz/strings.xml
+++ b/packages/SettingsLib/res/values-uz/strings.xml
@@ -450,7 +450,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g> ichida toʻladi"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> – <xliff:g id="TIME">%2$s</xliff:g> ichida toʻladi"</string>
-    <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g> – Batareya quvvati muvozanatlanmoqda"</string>
+    <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g> – Batareya uchun optimizatsiya"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"Noma’lum"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"Quvvat olmoqda"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"Tezkor quvvat olmoqda"</string>
@@ -553,7 +553,7 @@
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Yangi foydalanuvchi yaratilmoqda…"</string>
     <string name="user_nickname" msgid="262624187455825083">"Nik"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Mehmon kiritish"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"Mehmon rejimini olib tashlash"</string>
+    <string name="guest_exit_guest" msgid="4754204715192830850">"Mehmon seansini yakunlash"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Mehmon"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Suratga olish"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Rasm tanlash"</string>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index 643fdb9..2d56e45 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -553,7 +553,7 @@
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Đang tạo người dùng mới…"</string>
     <string name="user_nickname" msgid="262624187455825083">"Biệt hiệu"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Thêm khách"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"Xóa phiên khách"</string>
+    <string name="guest_exit_guest" msgid="4754204715192830850">"Kết thúc phiên khách"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"Khách"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Chụp ảnh"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Chọn một hình ảnh"</string>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index 81d8106..6b64d27 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -553,7 +553,8 @@
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"正在创建新用户…"</string>
     <string name="user_nickname" msgid="262624187455825083">"昵称"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"添加访客"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"移除访客"</string>
+    <!-- no translation found for guest_exit_guest (4754204715192830850) -->
+    <skip />
     <string name="guest_nickname" msgid="6332276931583337261">"访客"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"拍摄照片"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"选择图片"</string>
diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml
index 39a12aa..4ab580e 100644
--- a/packages/SettingsLib/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml
@@ -450,7 +450,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"還需 <xliff:g id="TIME">%1$s</xliff:g>才能充滿電"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - 還需 <xliff:g id="TIME">%2$s</xliff:g>才能充滿電"</string>
-    <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g> - 正在優化電池狀態"</string>
+    <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g> - 優化電池效能"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"未知"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"充電中"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"正在快速充電"</string>
@@ -553,7 +553,7 @@
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"正在建立新使用者…"</string>
     <string name="user_nickname" msgid="262624187455825083">"暱稱"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"新增訪客"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"移除訪客"</string>
+    <string name="guest_exit_guest" msgid="4754204715192830850">"結束訪客工作階段"</string>
     <string name="guest_nickname" msgid="6332276931583337261">"訪客"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"拍照"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"選擇圖片"</string>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index cc35ed3..46695db 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -450,7 +450,7 @@
     <string name="power_charging" msgid="6727132649743436802">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATE">%2$s</xliff:g>"</string>
     <string name="power_remaining_charging_duration_only" msgid="7415639699283965818">"<xliff:g id="TIME">%1$s</xliff:g>後充飽電"</string>
     <string name="power_charging_duration" msgid="5005740040558984057">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g>後充飽電"</string>
-    <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g> - 針對電池狀態進行最佳化調整"</string>
+    <string name="power_charging_limited" msgid="1956874810658999681">"<xliff:g id="LEVEL">%1$s</xliff:g> - 最佳化調整電池狀態"</string>
     <string name="battery_info_status_unknown" msgid="268625384868401114">"不明"</string>
     <string name="battery_info_status_charging" msgid="4279958015430387405">"充電中"</string>
     <string name="battery_info_status_charging_fast" msgid="8027559755902954885">"快速充電中"</string>
@@ -553,7 +553,8 @@
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"正在建立新使用者…"</string>
     <string name="user_nickname" msgid="262624187455825083">"暱稱"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"新增訪客"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"移除訪客"</string>
+    <!-- no translation found for guest_exit_guest (4754204715192830850) -->
+    <skip />
     <string name="guest_nickname" msgid="6332276931583337261">"訪客"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"拍照"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"選擇圖片"</string>
diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml
index bcc6369..f4ff6ec 100644
--- a/packages/SettingsLib/res/values-zu/strings.xml
+++ b/packages/SettingsLib/res/values-zu/strings.xml
@@ -553,7 +553,8 @@
     <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"Idala umsebenzisi omusha…"</string>
     <string name="user_nickname" msgid="262624187455825083">"Isiteketiso"</string>
     <string name="guest_new_guest" msgid="3482026122932643557">"Engeza isivakashi"</string>
-    <string name="guest_exit_guest" msgid="5908239569510734136">"Susa isihambeli"</string>
+    <!-- no translation found for guest_exit_guest (4754204715192830850) -->
+    <skip />
     <string name="guest_nickname" msgid="6332276931583337261">"Isihambeli"</string>
     <string name="user_image_take_photo" msgid="467512954561638530">"Thatha isithombe"</string>
     <string name="user_image_choose_photo" msgid="1363820919146782908">"Khetha isithombe"</string>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index f518b74..0ce8dd8 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1377,7 +1377,7 @@
     <!-- Label for adding a new guest in the user switcher [CHAR LIMIT=35] -->
     <string name="guest_new_guest">Add guest</string>
     <!-- Label for exiting and removing the guest session in the user switcher [CHAR LIMIT=35] -->
-    <string name="guest_exit_guest">Remove guest</string>
+    <string name="guest_exit_guest">End guest session</string>
     <!-- Name for the guest user [CHAR LIMIT=35] -->
     <string name="guest_nickname">Guest</string>
 
diff --git a/packages/SettingsLib/res/values/styles_support_preference.xml b/packages/SettingsLib/res/values/styles_support_preference.xml
index 6e61196..8ba9033 100644
--- a/packages/SettingsLib/res/values/styles_support_preference.xml
+++ b/packages/SettingsLib/res/values/styles_support_preference.xml
@@ -23,7 +23,6 @@
     <!-- Footer Preferences -->
     <style name="Preference.FooterPreference.SettingsBase" parent="@style/Preference.Material">
         <item name="android:layout">@layout/preference_footer</item>
-        <item name="allowDividerAbove">true</item>
     </style>
 
     <style name="PreferenceThemeOverlay.SettingsBase" parent="@style/PreferenceThemeOverlay">
diff --git a/packages/SettingsLib/src/com/android/settingslib/connectivity/ConnectivitySubsystemsRecoveryManager.java b/packages/SettingsLib/src/com/android/settingslib/connectivity/ConnectivitySubsystemsRecoveryManager.java
new file mode 100644
index 0000000..a921053
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/connectivity/ConnectivitySubsystemsRecoveryManager.java
@@ -0,0 +1,310 @@
+/*
+ * 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.settingslib.connectivity;
+
+import android.annotation.NonNull;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.net.wifi.WifiManager;
+import android.os.Handler;
+import android.os.HandlerExecutor;
+import android.provider.Settings;
+import android.telephony.PhoneStateListener;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
+import android.util.Log;
+
+/**
+ * An interface class to manage connectivity subsystem recovery/restart operations.
+ */
+public class ConnectivitySubsystemsRecoveryManager {
+    private static final String TAG = "ConnectivitySubsystemsRecoveryManager";
+
+    private final Context mContext;
+    private final Handler mHandler;
+    private RecoveryAvailableListener mRecoveryAvailableListener = null;
+
+    private static final long RESTART_TIMEOUT_MS = 15_000; // 15 seconds
+
+    private WifiManager mWifiManager = null;
+    private TelephonyManager mTelephonyManager = null;
+    private final BroadcastReceiver mApmMonitor = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            RecoveryAvailableListener listener = mRecoveryAvailableListener;
+            if (listener != null) {
+                listener.onRecoveryAvailableChangeListener(isRecoveryAvailable());
+            }
+        }
+    };
+    private boolean mApmMonitorRegistered = false;
+    private boolean mWifiRestartInProgress = false;
+    private boolean mTelephonyRestartInProgress = false;
+    private RecoveryStatusCallback mCurrentRecoveryCallback = null;
+    private final BroadcastReceiver mWifiMonitor = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (!mWifiRestartInProgress || mCurrentRecoveryCallback == null) {
+                stopTrackingWifiRestart();
+            }
+
+            // TODO: harden this code to avoid race condition. What if WiFi toggled just before
+            // recovery triggered. Either use new broadcasts from framework or detect more state
+            // changes.
+            boolean recoveryDone = false;
+            if (TextUtils.equals(intent.getAction(), WifiManager.WIFI_STATE_CHANGED_ACTION)) {
+                if (intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
+                        WifiManager.WIFI_STATE_UNKNOWN) == WifiManager.WIFI_STATE_ENABLED) {
+                    recoveryDone = true;
+                }
+            } else if (TextUtils.equals(intent.getAction(),
+                    WifiManager.WIFI_AP_STATE_CHANGED_ACTION)) {
+                if (intent.getIntExtra(WifiManager.EXTRA_WIFI_AP_STATE,
+                        WifiManager.WIFI_AP_STATE_FAILED) == WifiManager.WIFI_AP_STATE_ENABLED) {
+                    recoveryDone = true;
+                }
+            }
+
+            if (recoveryDone) {
+                mWifiRestartInProgress = false;
+                stopTrackingWifiRestart();
+                checkIfAllSubsystemsRestartsAreDone();
+            }
+        }
+    };
+    private final PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
+        @Override
+        public void onRadioPowerStateChanged(int state) {
+            if (!mTelephonyRestartInProgress || mCurrentRecoveryCallback == null) {
+                stopTrackingTelephonyRestart();
+            }
+
+            if (state == TelephonyManager.RADIO_POWER_ON) {
+                mTelephonyRestartInProgress = false;
+                stopTrackingTelephonyRestart();
+                checkIfAllSubsystemsRestartsAreDone();
+            }
+        }
+    };
+
+    public ConnectivitySubsystemsRecoveryManager(@NonNull Context context,
+            @NonNull Handler handler) {
+        mContext = context;
+        mHandler = new Handler(handler.getLooper());
+
+        if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI)) {
+            mWifiManager = mContext.getSystemService(WifiManager.class);
+            if (mWifiManager == null) {
+                Log.e(TAG, "WifiManager not available!?");
+            }
+        }
+
+        if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+            mTelephonyManager = mContext.getSystemService(TelephonyManager.class);
+            if (mTelephonyManager == null) {
+                Log.e(TAG, "TelephonyManager not available!?");
+            }
+        }
+    }
+
+    /**
+     * A listener which indicates to the caller whether a recovery operation is available across
+     * the specified technologies.
+     *
+     * Set using {@link #setRecoveryAvailableListener(RecoveryAvailableListener)}, cleared
+     * using {@link #clearRecoveryAvailableListener()}.
+     */
+    public interface RecoveryAvailableListener {
+        /**
+         * Called whenever the recovery availability status changes.
+         *
+         * @param isAvailable True if recovery is available across ANY of the requested
+         *                    technologies, false if recovery is not available across ALL of the
+         *                    requested technologies.
+         */
+        void onRecoveryAvailableChangeListener(boolean isAvailable);
+    }
+
+    /**
+     * Set a {@link RecoveryAvailableListener} to listen to changes in the recovery availability
+     * operation for the specified technology(ies).
+     *
+     * @param listener Listener to be triggered
+     */
+    public void setRecoveryAvailableListener(@NonNull RecoveryAvailableListener listener) {
+        mHandler.post(() -> {
+            mRecoveryAvailableListener = listener;
+            startTrackingRecoveryAvailability();
+        });
+    }
+
+    /**
+     * Clear a listener set with
+     * {@link #setRecoveryAvailableListener(RecoveryAvailableListener)}.
+     */
+    public void clearRecoveryAvailableListener() {
+        mHandler.post(() -> {
+            mRecoveryAvailableListener = null;
+            stopTrackingRecoveryAvailability();
+        });
+    }
+
+    private boolean isApmEnabled() {
+        return Settings.Global.getInt(mContext.getContentResolver(),
+                Settings.Global.AIRPLANE_MODE_ON, 0) == 1;
+    }
+
+    private boolean isWifiEnabled() {
+        // TODO: this doesn't consider the scan-only mode. I.e. WiFi is "disabled" while location
+        // mode is enabled. Probably need to reset WiFi in that state as well. Though this may
+        // appear strange to the user in that they've actually disabled WiFi.
+        return mWifiManager != null && (mWifiManager.isWifiEnabled()
+                || mWifiManager.isWifiApEnabled());
+    }
+
+    /**
+     * Provide an indication as to whether subsystem recovery is "available" - i.e. will be
+     * executed if triggered via {@link #triggerSubsystemRestart(String, RecoveryStatusCallback)}.
+     *
+     * @return true if a subsystem recovery is available, false otherwise.
+     */
+    public boolean isRecoveryAvailable() {
+        if (!isApmEnabled()) return true;
+
+        // even if APM is enabled we may still have recovery potential if WiFi is enabled
+        return isWifiEnabled();
+    }
+
+    private void startTrackingRecoveryAvailability() {
+        if (mApmMonitorRegistered) return;
+
+        mContext.registerReceiver(mApmMonitor,
+                new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED), null, mHandler);
+        mApmMonitorRegistered = true;
+    }
+
+    private void stopTrackingRecoveryAvailability() {
+        if (!mApmMonitorRegistered) return;
+
+        mContext.unregisterReceiver(mApmMonitor);
+        mApmMonitorRegistered = false;
+    }
+
+    private void startTrackingWifiRestart() {
+        IntentFilter filter = new IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION);
+        filter.addAction(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
+        mContext.registerReceiver(mWifiMonitor, filter, null, mHandler);
+    }
+
+    private void stopTrackingWifiRestart() {
+        mContext.unregisterReceiver(mWifiMonitor);
+    }
+
+    private void startTrackingTelephonyRestart() {
+        mTelephonyManager.registerPhoneStateListener(new HandlerExecutor(mHandler),
+                mPhoneStateListener);
+    }
+
+    private void stopTrackingTelephonyRestart() {
+        mTelephonyManager.unregisterPhoneStateListener(mPhoneStateListener);
+    }
+
+    private void checkIfAllSubsystemsRestartsAreDone() {
+        if (!mWifiRestartInProgress && !mTelephonyRestartInProgress) {
+            mCurrentRecoveryCallback.onSubsystemRestartOperationEnd();
+            mCurrentRecoveryCallback = null;
+        }
+    }
+
+    /**
+     * Callbacks used with
+     * {@link #triggerSubsystemRestart(String, RecoveryStatusCallback)} to get
+     * information about when recovery starts and is completed.
+     */
+    public interface RecoveryStatusCallback {
+        /**
+         * Callback for a subsystem restart triggered via
+         * {@link #triggerSubsystemRestart(String, RecoveryStatusCallback)} - indicates
+         * that operation has started.
+         */
+        void onSubsystemRestartOperationBegin();
+
+        /**
+         * Callback for a subsystem restart triggered via
+         * {@link #triggerSubsystemRestart(String, RecoveryStatusCallback)} - indicates
+         * that operation has ended. Note that subsystems may still take some time to come up to
+         * full functionality.
+         */
+        void onSubsystemRestartOperationEnd();
+    }
+
+    /**
+     * Trigger connectivity recovery for all requested technologies.
+     *
+     * @param reason   An optional reason code to pass through to the technology-specific
+     *                 API. May be used to trigger a bug report.
+     * @param callback Callbacks triggered when recovery status changes.
+     */
+    public void triggerSubsystemRestart(String reason, @NonNull RecoveryStatusCallback callback) {
+        mHandler.post(() -> {
+            boolean someSubsystemRestarted = false;
+
+            if (mWifiRestartInProgress) {
+                Log.e(TAG, "Wifi restart still in progress");
+                return;
+            }
+
+            if (mTelephonyRestartInProgress) {
+                Log.e(TAG, "Telephony restart still in progress");
+                return;
+            }
+
+            if (isWifiEnabled()) {
+                mWifiManager.restartWifiSubsystem(reason);
+                mWifiRestartInProgress = true;
+                someSubsystemRestarted = true;
+                startTrackingWifiRestart();
+            }
+
+            if (mTelephonyManager != null && !isApmEnabled()) {
+                if (mTelephonyManager.rebootRadio()) {
+                    mTelephonyRestartInProgress = true;
+                    someSubsystemRestarted = true;
+                    startTrackingTelephonyRestart();
+                }
+            }
+
+            if (someSubsystemRestarted) {
+                mCurrentRecoveryCallback = callback;
+                callback.onSubsystemRestartOperationBegin();
+
+                mHandler.postDelayed(() -> {
+                    stopTrackingWifiRestart();
+                    stopTrackingTelephonyRestart();
+                    mWifiRestartInProgress = false;
+                    mTelephonyRestartInProgress = false;
+                    mCurrentRecoveryCallback.onSubsystemRestartOperationEnd();
+                    mCurrentRecoveryCallback = null;
+                }, RESTART_TIMEOUT_MS);
+            }
+        });
+    }
+}
+
diff --git a/packages/SettingsLib/src/com/android/settingslib/connectivity/OWNERS b/packages/SettingsLib/src/com/android/settingslib/connectivity/OWNERS
new file mode 100644
index 0000000..e7a20b3
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/connectivity/OWNERS
@@ -0,0 +1,8 @@
+# Default reviewers for this and subdirectories.
+andychou@google.com
+arcwang@google.com
+goldmanj@google.com
+qal@google.com
+wengsu@google.com
+
+# Emergency approvers in case the above are not available
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryKey.java b/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryKey.java
index b07fc2b..bbf0a02 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryKey.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryKey.java
@@ -34,6 +34,7 @@
     public static final String CATEGORY_DISPLAY = "com.android.settings.category.ia.display";
     public static final String CATEGORY_SOUND = "com.android.settings.category.ia.sound";
     public static final String CATEGORY_STORAGE = "com.android.settings.category.ia.storage";
+    public static final String CATEGORY_EMERGENCY = "com.android.settings.category.ia.emergency";
     public static final String CATEGORY_SECURITY = "com.android.settings.category.ia.security";
     public static final String CATEGORY_SECURITY_LOCKSCREEN =
             "com.android.settings.category.ia.lockscreen";
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
index c57d4ad..cbb5105 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java
@@ -66,31 +66,40 @@
         @Override
         public void onCapabilitiesChanged(
                 Network network, NetworkCapabilities networkCapabilities) {
+            WifiInfo wifiInfo = (WifiInfo) networkCapabilities.getTransportInfo();
+            updateWifiInfo(wifiInfo);
+            updateStatusLabel();
+            mCallback.run();
+        }
+
+        @Override
+        public void onLost(Network network) {
+            updateWifiInfo(null);
             updateStatusLabel();
             mCallback.run();
         }
     };
     private final NetworkCallback mDefaultNetworkCallback = new NetworkCallback() {
-                @Override
-                public void onCapabilitiesChanged(Network network, NetworkCapabilities nc) {
-                    // network is now the default network, and its capabilities are nc.
-                    // This method will always be called immediately after the network becomes the
-                    // default, in addition to any time the capabilities change while the network is
-                    // the default.
-                    mDefaultNetwork = network;
-                    mDefaultNetworkCapabilities = nc;
-                    updateStatusLabel();
-                    mCallback.run();
-                }
-                @Override
-                public void onLost(Network network) {
-                    // The system no longer has a default network.
-                    mDefaultNetwork = null;
-                    mDefaultNetworkCapabilities = null;
-                    updateStatusLabel();
-                    mCallback.run();
-                }
-            };
+        @Override
+        public void onCapabilitiesChanged(Network network, NetworkCapabilities nc) {
+            // network is now the default network, and its capabilities are nc.
+            // This method will always be called immediately after the network becomes the
+            // default, in addition to any time the capabilities change while the network is
+            // the default.
+            mDefaultNetwork = network;
+            mDefaultNetworkCapabilities = nc;
+            updateStatusLabel();
+            mCallback.run();
+        }
+        @Override
+        public void onLost(Network network) {
+            // The system no longer has a default network.
+            mDefaultNetwork = null;
+            mDefaultNetworkCapabilities = null;
+            updateStatusLabel();
+            mCallback.run();
+        }
+    };
     private Network mDefaultNetwork = null;
     private NetworkCapabilities mDefaultNetworkCapabilities = null;
     private final Runnable mCallback;
@@ -170,32 +179,22 @@
         String action = intent.getAction();
         if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
             updateWifiState();
-        } else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
-            updateWifiState();
-            final NetworkInfo networkInfo =
-                    intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
-            connected = networkInfo != null && networkInfo.isConnected();
-            mWifiInfo = null;
-            ssid = null;
-            if (connected) {
-                mWifiInfo = mWifiManager.getConnectionInfo();
-                if (mWifiInfo != null) {
-                    if (mWifiInfo.isPasspointAp() || mWifiInfo.isOsuAp()) {
-                        ssid = mWifiInfo.getPasspointProviderFriendlyName();
-                    } else {
-                        ssid = getValidSsid(mWifiInfo);
-                    }
-                    updateRssi(mWifiInfo.getRssi());
-                    maybeRequestNetworkScore();
-                }
+        }
+    }
+
+    private void updateWifiInfo(WifiInfo wifiInfo) {
+        updateWifiState();
+        connected = wifiInfo != null;
+        mWifiInfo = wifiInfo;
+        ssid = null;
+        if (mWifiInfo != null) {
+            if (mWifiInfo.isPasspointAp() || mWifiInfo.isOsuAp()) {
+                ssid = mWifiInfo.getPasspointProviderFriendlyName();
+            } else {
+                ssid = getValidSsid(mWifiInfo);
             }
-            updateStatusLabel();
-            mCallback.run();
-        } else if (action.equals(WifiManager.RSSI_CHANGED_ACTION)) {
-            // Default to -200 as its below WifiManager.MIN_RSSI.
-            updateRssi(intent.getIntExtra(WifiManager.EXTRA_NEW_RSSI, -200));
-            updateStatusLabel();
-            mCallback.run();
+            updateRssi(mWifiInfo.getRssi());
+            maybeRequestNetworkScore();
         }
     }
 
diff --git a/packages/SettingsLib/tests/integ/Android.bp b/packages/SettingsLib/tests/integ/Android.bp
index 2ccff1e..f6f9dba 100644
--- a/packages/SettingsLib/tests/integ/Android.bp
+++ b/packages/SettingsLib/tests/integ/Android.bp
@@ -33,10 +33,12 @@
     test_suites: ["device-tests"],
 
     static_libs: [
+        "androidx.test.core",
         "androidx.test.rules",
         "androidx.test.espresso.core",
         "mockito-target-minus-junit4",
         "truth-prebuilt",
+        "SettingsLibUsageProgressBarPreference",
     ],
 
     dxflags: ["--multi-dex"],
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/widget/UsageProgressBarPreferenceTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/widget/UsageProgressBarPreferenceTest.java
new file mode 100644
index 0000000..85e2174
--- /dev/null
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/widget/UsageProgressBarPreferenceTest.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2021 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.settingslib.widget;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Context;
+import android.text.SpannedString;
+import android.text.style.RelativeSizeSpan;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.LinearLayout;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+
+import androidx.preference.PreferenceViewHolder;
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(AndroidJUnit4.class)
+public class UsageProgressBarPreferenceTest {
+
+    private UsageProgressBarPreference mUsageProgressBarPreference;
+    private PreferenceViewHolder mViewHolder;
+
+    @Before
+    public void setUp() {
+        final Context context = ApplicationProvider.getApplicationContext();
+        mUsageProgressBarPreference = new UsageProgressBarPreference(context);
+        final LayoutInflater inflater = LayoutInflater.from(context);
+        final View rootView = inflater.inflate(mUsageProgressBarPreference.getLayoutResource(),
+                new LinearLayout(context), false /* attachToRoot */);
+        mViewHolder = PreferenceViewHolder.createInstanceForTests(rootView);
+    }
+
+    @Test
+    public void setUsageSummary_noNumber_noRelativeSizeSpan() {
+        mUsageProgressBarPreference.setUsageSummary("test");
+
+        mUsageProgressBarPreference.onBindViewHolder(mViewHolder);
+
+        final TextView usageSummary = (TextView) mViewHolder.findViewById(R.id.usage_summary);
+        final SpannedString summary = new SpannedString(usageSummary.getText());
+        assertThat(summary.getSpans(0, summary.length(), RelativeSizeSpan.class).length)
+                .isEqualTo(0);
+    }
+
+    @Test
+    public void setUsageSummary_integerNumber_findRelativeSizeSpan() {
+        mUsageProgressBarPreference.setUsageSummary("10Test");
+
+        mUsageProgressBarPreference.onBindViewHolder(mViewHolder);
+
+        final TextView usageSummary = (TextView) mViewHolder.findViewById(R.id.usage_summary);
+        final SpannedString summary = new SpannedString(usageSummary.getText());
+        assertThat(summary.getSpans(0, summary.length(), RelativeSizeSpan.class).length)
+                .isEqualTo(1);
+    }
+
+    @Test
+    public void setUsageSummary_floatNumber_findRelativeSizeSpan() {
+        mUsageProgressBarPreference.setUsageSummary("3.14Test");
+
+        mUsageProgressBarPreference.onBindViewHolder(mViewHolder);
+
+        final TextView usageSummary = (TextView) mViewHolder.findViewById(R.id.usage_summary);
+        final SpannedString summary = new SpannedString(usageSummary.getText());
+        assertThat(summary.getSpans(0, summary.length(), RelativeSizeSpan.class).length)
+                .isEqualTo(1);
+    }
+
+    @Test
+    public void setPercent_getCorrectProgress() {
+        mUsageProgressBarPreference.setPercent(31, 80);
+
+        mUsageProgressBarPreference.onBindViewHolder(mViewHolder);
+
+        final ProgressBar progressBar = (ProgressBar) mViewHolder
+                .findViewById(android.R.id.progress);
+        assertThat(progressBar.getProgress()).isEqualTo((int) (31.0f / 80 * 100));
+    }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/emergencynumber/EmergencyNumberUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/emergencynumber/EmergencyNumberUtilsTest.java
index 99d2ff7..695b5b6 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/emergencynumber/EmergencyNumberUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/emergencynumber/EmergencyNumberUtilsTest.java
@@ -18,15 +18,21 @@
 
 import static android.telephony.emergency.EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_POLICE;
 
+import static com.android.settingslib.emergencynumber.EmergencyNumberUtils.EMERGENCY_GESTURE_CALL_NUMBER;
+
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.nullable;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.pm.PackageManager;
-import android.provider.Settings;
+import android.net.Uri;
+import android.os.Bundle;
 import android.telephony.SubscriptionManager;
 import android.telephony.TelephonyManager;
 import android.telephony.emergency.EmergencyNumber;
@@ -38,7 +44,6 @@
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.robolectric.RobolectricTestRunner;
-import org.robolectric.RuntimeEnvironment;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -55,12 +60,15 @@
     private PackageManager mPackageManager;
     @Mock
     private TelephonyManager mTelephonyManager;
+    @Mock
+    ContentResolver mContentResolver;
     private EmergencyNumberUtils mUtils;
 
     @Before
     public void setup() {
         MockitoAnnotations.initMocks(this);
         when(mContext.getPackageManager()).thenReturn(mPackageManager);
+        when(mContext.getContentResolver()).thenReturn(mContentResolver);
     }
 
     @Test
@@ -89,11 +97,10 @@
         when(mContext.getSystemService(TelephonyManager.class)).thenReturn(mTelephonyManager);
         addEmergencyNumberToTelephony();
 
-        ContentResolver resolver = RuntimeEnvironment.application.getContentResolver();
-        when(mContext.getContentResolver()).thenReturn(resolver);
-        Settings.Secure.putString(resolver, Settings.Secure.EMERGENCY_GESTURE_CALL_NUMBER,
-                USER_OVERRIDE_EMERGENCY_NUMBER);
-
+        Bundle bundle = new Bundle();
+        bundle.putString(EMERGENCY_GESTURE_CALL_NUMBER, USER_OVERRIDE_EMERGENCY_NUMBER);
+        when(mContentResolver.call(any(Uri.class), anyString(), nullable(String.class), nullable(
+                Bundle.class))).thenReturn(bundle);
         mUtils = new EmergencyNumberUtils(mContext);
 
         assertThat(mUtils.getPoliceNumber()).isEqualTo(USER_OVERRIDE_EMERGENCY_NUMBER);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/RadioButtonPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/RadioButtonPreferenceTest.java
index d58e68a..a5028ff 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/RadioButtonPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/RadioButtonPreferenceTest.java
@@ -40,10 +40,30 @@
     private Application mContext;
     private RadioButtonPreference mPreference;
 
+    private View mExtraWidgetContainer;
+    private View mExtraWidget;
+
+    private boolean mIsClickListenerCalled;
+    private View.OnClickListener mClickListener = new View.OnClickListener() {
+        @Override
+        public void onClick(View v) {
+            mIsClickListenerCalled = true;
+        }
+    };
+
     @Before
     public void setUp() {
         mContext = RuntimeEnvironment.application;
         mPreference = new RadioButtonPreference(mContext);
+
+        View view = LayoutInflater.from(mContext)
+                .inflate(R.layout.preference_radio, null /* root */);
+        PreferenceViewHolder preferenceViewHolder =
+                PreferenceViewHolder.createInstanceForTests(view);
+        mPreference.onBindViewHolder(preferenceViewHolder);
+
+        mExtraWidgetContainer = view.findViewById(R.id.radio_extra_widget_container);
+        mExtraWidget = view.findViewById(R.id.radio_extra_widget);
     }
 
     @Test
@@ -57,26 +77,30 @@
     }
 
     @Test
-    public void summary_containerShouldBeVisible() {
+    public void onBindViewHolder_withSummary_containerShouldBeVisible() {
         mPreference.setSummary("some summary");
         View summaryContainer = new View(mContext);
         View view = mock(View.class);
         when(view.findViewById(R.id.summary_container)).thenReturn(summaryContainer);
         PreferenceViewHolder preferenceViewHolder =
                 PreferenceViewHolder.createInstanceForTests(view);
+
         mPreference.onBindViewHolder(preferenceViewHolder);
+
         assertEquals(View.VISIBLE, summaryContainer.getVisibility());
     }
 
     @Test
-    public void emptySummary_containerShouldBeGone() {
+    public void onBindViewHolder_emptySummary_containerShouldBeGone() {
         mPreference.setSummary("");
         View summaryContainer = new View(mContext);
         View view = mock(View.class);
         when(view.findViewById(R.id.summary_container)).thenReturn(summaryContainer);
         PreferenceViewHolder preferenceViewHolder =
                 PreferenceViewHolder.createInstanceForTests(view);
+
         mPreference.onBindViewHolder(preferenceViewHolder);
+
         assertEquals(View.GONE, summaryContainer.getVisibility());
     }
 
@@ -93,11 +117,36 @@
     }
 
     @Test
-    public void hideAppendix_shouldBeGone() {
+    public void setAppendixVisibility_setGone_shouldBeGone() {
         mPreference.setAppendixVisibility(View.GONE);
-        View view = LayoutInflater.from(mContext).inflate(R.layout.preference_radio, null);
+
+        View view = LayoutInflater.from(mContext)
+                .inflate(R.layout.preference_radio, null /* root */);
         PreferenceViewHolder holder = PreferenceViewHolder.createInstanceForTests(view);
         mPreference.onBindViewHolder(holder);
         assertThat(holder.findViewById(R.id.appendix).getVisibility()).isEqualTo(View.GONE);
     }
+
+    @Test
+    public void setExtraWidgetListener_setNull_extraWidgetShouldInvisible() {
+        mPreference.setExtraWidgetOnClickListener(null);
+
+        assertEquals(View.GONE, mExtraWidgetContainer.getVisibility());
+    }
+
+    @Test
+    public void setExtraWidgetListener_extraWidgetShouldVisible() {
+        mPreference.setExtraWidgetOnClickListener(mClickListener);
+
+        assertEquals(View.VISIBLE, mExtraWidgetContainer.getVisibility());
+    }
+
+    @Test
+    public void onClickListener_setExtraWidgetOnClickListener_ShouldCalled() {
+        mPreference.setExtraWidgetOnClickListener(mClickListener);
+
+        assertThat(mIsClickListenerCalled).isFalse();
+        mExtraWidget.callOnClick();
+        assertThat(mIsClickListenerCalled).isTrue();
+    }
 }
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index b6e45fc0..fcb6cc3 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -181,6 +181,9 @@
         Settings.Secure.ADAPTIVE_CONNECTIVITY_ENABLED,
         Settings.Secure.ASSIST_HANDLES_LEARNING_TIME_ELAPSED_MILLIS,
         Settings.Secure.ASSIST_HANDLES_LEARNING_EVENT_COUNT,
-        Settings.Secure.ACCESSIBILITY_BUTTON_MODE
+        Settings.Secure.ACCESSIBILITY_BUTTON_MODE,
+        Settings.Secure.ACCESSIBILITY_FLOATING_MENU_SIZE,
+        Settings.Secure.ACCESSIBILITY_FLOATING_MENU_ICON_TYPE,
+        Settings.Secure.ACCESSIBILITY_FLOATING_MENU_OPACITY
     };
 }
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index d14acc6..53e67e1 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -273,5 +273,11 @@
                 new InclusiveIntegerRangeValidator(
                         Secure.ACCESSIBILITY_BUTTON_MODE_NAVIGATION_BAR,
                         Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU));
+        VALIDATORS.put(Secure.ACCESSIBILITY_FLOATING_MENU_SIZE,
+                new DiscreteValueValidator(new String[] {"0", "1"}));
+        VALIDATORS.put(Secure.ACCESSIBILITY_FLOATING_MENU_ICON_TYPE,
+                new DiscreteValueValidator(new String[] {"0", "1"}));
+        VALIDATORS.put(Secure.ACCESSIBILITY_FLOATING_MENU_OPACITY,
+                new InclusiveFloatRangeValidator(0.0f, 1.0f));
     }
 }
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
index 5c7a466..713be54 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java
@@ -1813,6 +1813,15 @@
         dumpSetting(s, p,
                 Settings.Secure.ACCESSIBILITY_BUTTON_MODE,
                 SecureSettingsProto.Accessibility.ACCESSIBILITY_BUTTON_MODE);
+        dumpSetting(s, p,
+                Settings.Secure.ACCESSIBILITY_FLOATING_MENU_SIZE,
+                SecureSettingsProto.Accessibility.ACCESSIBILITY_FLOATING_MENU_SIZE);
+        dumpSetting(s, p,
+                Settings.Secure.ACCESSIBILITY_FLOATING_MENU_ICON_TYPE,
+                SecureSettingsProto.Accessibility.ACCESSIBILITY_FLOATING_MENU_ICON_TYPE);
+        dumpSetting(s, p,
+                Settings.Secure.ACCESSIBILITY_FLOATING_MENU_OPACITY,
+                SecureSettingsProto.Accessibility.ACCESSIBILITY_FLOATING_MENU_OPACITY);
         p.end(accessibilityToken);
 
         final long adaptiveSleepToken = p.start(SecureSettingsProto.ADAPTIVE_SLEEP);
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 2070773..c1c1d65 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -302,6 +302,7 @@
                     Settings.Global.HDMI_SYSTEM_AUDIO_CONTROL_ENABLED,
                     Settings.Global.HEADS_UP_NOTIFICATIONS_ENABLED,
                     Settings.Global.HIDDEN_API_POLICY,
+                    Settings.Global.FORCE_NON_DEBUGGABLE_FINAL_BUILD_FOR_COMPAT,
                     Settings.Global.HIDE_ERROR_DIALOGS,
                     Settings.Global.HTTP_PROXY,
                     HYBRID_SYSUI_BATTERY_WARNING_FLAGS,
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 7ea9686..0a43014 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -120,6 +120,7 @@
     <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
     <uses-permission android:name="android.permission.CREATE_USERS" />
     <uses-permission android:name="android.permission.MANAGE_DEVICE_ADMINS" />
+    <uses-permission android:name="android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS" />
     <uses-permission android:name="android.permission.ACCESS_LOWPAN_STATE"/>
     <uses-permission android:name="android.permission.CHANGE_LOWPAN_STATE"/>
     <uses-permission android:name="android.permission.READ_LOWPAN_CREDENTIAL"/>
@@ -298,6 +299,9 @@
     <uses-permission android:name="android.permission.UPGRADE_RUNTIME_PERMISSIONS" />
 
     <!-- Permission needed to read wifi network credentials for CtsNetTestCases -->
+    <uses-permission android:name="android.permission.NETWORK_AIRPLANE_MODE" />
+
+    <!-- Permission needed to read wifi network credentials for CtsNetTestCases -->
     <uses-permission android:name="android.permission.READ_WIFI_CREDENTIAL" />
 
     <!-- Permission needed to use wifi usability API's for CtsNetTestCases -->
@@ -371,6 +375,10 @@
     <!-- Permission required for CTS test - CtsHdmiCecHostTestCases -->
     <uses-permission android:name="android.permission.HDMI_CEC" />
 
+    <!-- Permission needed for CTS test - WifiManagerTest -->
+    <uses-permission android:name="android.permission.WIFI_ACCESS_COEX_UNSAFE_CHANNELS" />
+    <uses-permission android:name="android.permission.WIFI_UPDATE_COEX_UNSAFE_CHANNELS" />
+
     <application android:label="@string/app_label"
                 android:theme="@android:style/Theme.DeviceDefault.DayNight"
                 android:defaultToDeviceProtectedStorage="true"
diff --git a/packages/Shell/src/com/android/shell/BugreportProgressService.java b/packages/Shell/src/com/android/shell/BugreportProgressService.java
index a2608a0..8c70112 100644
--- a/packages/Shell/src/com/android/shell/BugreportProgressService.java
+++ b/packages/Shell/src/com/android/shell/BugreportProgressService.java
@@ -2148,8 +2148,10 @@
             name = in.readString();
             initialName = in.readString();
             title = in.readString();
+            shareTitle = in.readString();
             description = in.readString();
             progress.set(in.readInt());
+            lastProgress.set(in.readInt());
             lastUpdate.set(in.readLong());
             formattedLastUpdate = in.readString();
             bugreportFile = readFile(in);
@@ -2160,9 +2162,10 @@
             }
 
             finished.set(in.readInt() == 1);
+            addingDetailsToZip = in.readBoolean();
+            addedDetailsToZip = in.readBoolean();
             screenshotCounter = in.readInt();
             shareDescription = in.readString();
-            shareTitle = in.readString();
             type = in.readInt();
         }
 
@@ -2173,8 +2176,10 @@
             dest.writeString(name);
             dest.writeString(initialName);
             dest.writeString(title);
+            dest.writeString(shareTitle);
             dest.writeString(description);
             dest.writeInt(progress.intValue());
+            dest.writeInt(lastProgress.intValue());
             dest.writeLong(lastUpdate.longValue());
             dest.writeString(getFormattedLastUpdate());
             writeFile(dest, bugreportFile);
@@ -2185,9 +2190,10 @@
             }
 
             dest.writeInt(finished.get() ? 1 : 0);
+            dest.writeBoolean(addingDetailsToZip);
+            dest.writeBoolean(addedDetailsToZip);
             dest.writeInt(screenshotCounter);
             dest.writeString(shareDescription);
-            dest.writeString(shareTitle);
             dest.writeInt(type);
         }
 
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index fda1e36..9021864 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -63,6 +63,7 @@
         "androidx.recyclerview_recyclerview",
         "androidx.preference_preference",
         "androidx.appcompat_appcompat",
+        "androidx.concurrent_concurrent-futures",
         "androidx.mediarouter_mediarouter",
         "androidx.palette_palette",
         "androidx.legacy_legacy-preference-v14",
@@ -130,6 +131,7 @@
         "androidx.recyclerview_recyclerview",
         "androidx.preference_preference",
         "androidx.appcompat_appcompat",
+        "androidx.concurrent_concurrent-futures",
         "androidx.mediarouter_mediarouter",
         "androidx.palette_palette",
         "androidx.legacy_legacy-preference-v14",
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 070c021..a06bb93 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -570,6 +570,7 @@
         <!-- People Space UI Screen -->
         <activity android:name=".people.PeopleSpaceActivity"
             android:label="People"
+            android:enabled="true"
             android:exported="true"
             android:theme="@android:style/Theme.Material.NoActionBar">
             <intent-filter>
diff --git a/packages/SystemUI/TEST_MAPPING b/packages/SystemUI/TEST_MAPPING
index d7f135d..6c7a5b8 100644
--- a/packages/SystemUI/TEST_MAPPING
+++ b/packages/SystemUI/TEST_MAPPING
@@ -1,7 +1,7 @@
 {
   // Looking for unit test presubmit configuration?
   // This currently lives in ATP config apct/system_ui/unit_test
-  "staged-platinum-postsubmit": [
+  "presubmit": [
     {
       "name": "PlatformScenarioTests",
       "options": [
@@ -16,10 +16,30 @@
         },
         {
             "exclude-annotation": "androidx.test.filters.FlakyTest"
+        },
+        {
+            "exclude-annotation": "android.platform.helpers.Staging"
+        },
+        {
+            "exclude-annotation": "android.platform.test.annotations.Postsubmit"
         }
       ]
     }
   ],
+
+  // Curious where your @Scenario tests will run?
+  //
+  // @Ignore or @FlakyTest: nowhere
+  // @Staging: in staged-postsubmit, but not postsubmit or presubmit
+  // @Postsubmit: in postsubmit and staged-postsubmit, but not presubmit
+  // none of the above: in presubmit, postsubmit, and staged-postsubmit
+  //
+  // Therefore, please annotate new tests with @Staging, then with @Postsubmit
+  // once they're ready for postsubmit, then with neither once they're ready
+  // for presubmit.
+  //
+  // If you don't use @Staging or @Postsubmit, your new test will immediately
+  // block presubmit, which is probably not what you want!
   "platinum-postsubmit": [
     {
       "name": "PlatformScenarioTests",
@@ -42,6 +62,25 @@
       ]
     }
   ],
+  "staged-platinum-postsubmit": [
+    {
+      "name": "PlatformScenarioTests",
+      "options": [
+        {
+            "include-filter": "android.platform.test.scenario.sysui"
+        },
+        {
+            "include-annotation": "android.platform.test.scenario.annotation.Scenario"
+        },
+        {
+            "exclude-annotation": "org.junit.Ignore"
+        },
+        {
+            "exclude-annotation": "androidx.test.filters.FlakyTest"
+        }
+      ]
+    }
+  ],
   "auto-end-to-end-postsubmit": [
     {
       "name": "AndroidAutoUiTests",
diff --git a/packages/SystemUI/res-keyguard/values-ar/strings.xml b/packages/SystemUI/res-keyguard/values-ar/strings.xml
index 459d162..c027f8d 100644
--- a/packages/SystemUI/res-keyguard/values-ar/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ar/strings.xml
@@ -38,7 +38,7 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • جارٍ الشحن"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • جارٍ الشحن سريعًا"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • جارٍ الشحن ببطء"</string>
-    <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • التحسين لسلامة البطارية"</string>
+    <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • التحسين للحفاظ على سلامة البطارية"</string>
     <string name="keyguard_low_battery" msgid="1868012396800230904">"توصيل جهاز الشحن."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"اضغط على \"القائمة\" لإلغاء التأمين."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"الشبكة مؤمّنة"</string>
diff --git a/packages/SystemUI/res-keyguard/values-b+sr+Latn/strings.xml b/packages/SystemUI/res-keyguard/values-b+sr+Latn/strings.xml
index 3468740..4220526 100644
--- a/packages/SystemUI/res-keyguard/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-b+sr+Latn/strings.xml
@@ -38,7 +38,7 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Puni se"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Brzo se puni"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Sporo se puni"</string>
-    <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Optimizuje se radi dobrog stanja baterije"</string>
+    <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Optimizuje se radi boljeg stanja baterije"</string>
     <string name="keyguard_low_battery" msgid="1868012396800230904">"Priključite punjač."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Pritisnite Meni da biste otključali."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Mreža je zaključana"</string>
diff --git a/packages/SystemUI/res-keyguard/values-bg/strings.xml b/packages/SystemUI/res-keyguard/values-bg/strings.xml
index 0911efa..dc4ee69 100644
--- a/packages/SystemUI/res-keyguard/values-bg/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-bg/strings.xml
@@ -38,7 +38,7 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Зарежда се"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Зарежда се бързо"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Зарежда се бавно"</string>
-    <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Оптимизиране за състоянието на батерията"</string>
+    <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Оптимизиране с цел състоянието на батерията"</string>
     <string name="keyguard_low_battery" msgid="1868012396800230904">"Свържете зарядното си устройство."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Натиснете „Меню“, за да отключите."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Мрежата е заключена"</string>
diff --git a/packages/SystemUI/res-keyguard/values-ca/strings.xml b/packages/SystemUI/res-keyguard/values-ca/strings.xml
index 421ddb6..2e3b4af 100644
--- a/packages/SystemUI/res-keyguard/values-ca/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-ca/strings.xml
@@ -38,7 +38,7 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • S\'està carregant"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • S\'està carregant ràpidament"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • S\'està carregant lentament"</string>
-    <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • S\'està optimitzant per mantenir el bon estat de la bateria"</string>
+    <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • S\'està optimitzant per preservar l\'estat de la bateria"</string>
     <string name="keyguard_low_battery" msgid="1868012396800230904">"Connecta el carregador."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Prem Menú per desbloquejar."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"La xarxa està bloquejada"</string>
diff --git a/packages/SystemUI/res-keyguard/values-eu/strings.xml b/packages/SystemUI/res-keyguard/values-eu/strings.xml
index ce8ef20..93fe516 100644
--- a/packages/SystemUI/res-keyguard/values-eu/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-eu/strings.xml
@@ -38,7 +38,7 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Kargatzen"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Bizkor kargatzen"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Mantso kargatzen"</string>
-    <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Optimizatzen bateria egoera onean mantentzeko"</string>
+    <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Optimizatzen, bateria egoera onean mantentzeko"</string>
     <string name="keyguard_low_battery" msgid="1868012396800230904">"Konektatu kargagailua."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Desblokeatzeko, sakatu Menua."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Sarea blokeatuta dago"</string>
diff --git a/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml b/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml
index f215625..d186901 100644
--- a/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-fr-rCA/strings.xml
@@ -38,7 +38,7 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"En recharge : <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"En recharge rapide : <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"En recharge lente : <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
-    <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Optimisation en fonction de la santé de la pile"</string>
+    <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Optimisation pour préserver la pile"</string>
     <string name="keyguard_low_battery" msgid="1868012396800230904">"Branchez votre chargeur."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Appuyez sur la touche Menu pour déverrouiller l\'appareil."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Réseau verrouillé"</string>
diff --git a/packages/SystemUI/res-keyguard/values-gl/strings.xml b/packages/SystemUI/res-keyguard/values-gl/strings.xml
index 01a0fac..f8c5eba 100644
--- a/packages/SystemUI/res-keyguard/values-gl/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-gl/strings.xml
@@ -38,7 +38,7 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Cargando"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Cargando rapidamente"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Cargando lentamente"</string>
-    <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Optimizando para manter a batería en bo estado"</string>
+    <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Optimizando a preservación da batería"</string>
     <string name="keyguard_low_battery" msgid="1868012396800230904">"Conecta o cargador."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Preme Menú para desbloquear."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Bloqueada pola rede"</string>
diff --git a/packages/SystemUI/res-keyguard/values-hy/strings.xml b/packages/SystemUI/res-keyguard/values-hy/strings.xml
index c8ed16d..ab07ba46 100644
--- a/packages/SystemUI/res-keyguard/values-hy/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-hy/strings.xml
@@ -38,7 +38,7 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Լիցքավորում"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Արագ լիցքավորում"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Դանդաղ լիցքավորում"</string>
-    <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Օպտիմալացվում է մարտկոցի աշխատանքային հզորության համար"</string>
+    <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Օպտիմալացվում է մարտկոցի պահպանման համար"</string>
     <string name="keyguard_low_battery" msgid="1868012396800230904">"Միացրեք լիցքավորիչը:"</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Ապակողպելու համար սեղմեք Ընտրացանկը:"</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Ցանցը կողպված է"</string>
diff --git a/packages/SystemUI/res-keyguard/values-is/strings.xml b/packages/SystemUI/res-keyguard/values-is/strings.xml
index 3f3bec9..bb8a908 100644
--- a/packages/SystemUI/res-keyguard/values-is/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-is/strings.xml
@@ -38,7 +38,7 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Í hleðslu"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Hröð hleðsla"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Hæg hleðsla"</string>
-    <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Fínstillir til að bæta rafhlöðuendingu"</string>
+    <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Fínstillir fyrir rafhlöðuendingu"</string>
     <string name="keyguard_low_battery" msgid="1868012396800230904">"Tengdu hleðslutækið."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Ýttu á valmyndarhnappinn til að taka úr lás."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Net læst"</string>
diff --git a/packages/SystemUI/res-keyguard/values-it/strings.xml b/packages/SystemUI/res-keyguard/values-it/strings.xml
index 47d17ce..c1baadf 100644
--- a/packages/SystemUI/res-keyguard/values-it/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-it/strings.xml
@@ -38,7 +38,7 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • In carica"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ricarica veloce"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ricarica lenta"</string>
-    <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • È in corso l\'ottimizzazione per l\'integrità della batteria"</string>
+    <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ottimizzazione per integrità batteria"</string>
     <string name="keyguard_low_battery" msgid="1868012396800230904">"Collega il caricabatterie."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Premi Menu per sbloccare."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Rete bloccata"</string>
diff --git a/packages/SystemUI/res-keyguard/values-kk/strings.xml b/packages/SystemUI/res-keyguard/values-kk/strings.xml
index b20cb05..8e701fe 100644
--- a/packages/SystemUI/res-keyguard/values-kk/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-kk/strings.xml
@@ -38,7 +38,7 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Зарядталуда"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Жылдам зарядталуда"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Баяу зарядталуда"</string>
-    <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Батареяның жұмыс істеу қабілеті оңтайландырылуда"</string>
+    <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Батарея жұмысын оңтайландыру"</string>
     <string name="keyguard_low_battery" msgid="1868012396800230904">"Зарядтағышты қосыңыз."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Ашу үшін \"Мәзір\" пернесін басыңыз."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Желі құлыптаулы"</string>
diff --git a/packages/SystemUI/res-keyguard/values-kn/strings.xml b/packages/SystemUI/res-keyguard/values-kn/strings.xml
index a527123..9686541 100644
--- a/packages/SystemUI/res-keyguard/values-kn/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-kn/strings.xml
@@ -38,7 +38,7 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ಚಾರ್ಜ್‌ ಮಾಡಲಾಗುತ್ತಿದೆ"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ವೇಗವಾಗಿ ಚಾರ್ಜ್‌ಮಾಡಲಾಗುತ್ತಿದೆ"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ನಿಧಾನವಾಗಿ ಚಾರ್ಜ್ ಮಾಡಲಾಗುತ್ತಿದೆ"</string>
-    <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ಬ್ಯಾಟರಿಯ ಆರೋಗ್ಯಕ್ಕಾಗಿ ಆಪ್ಟಿಮೈಸ್ ಮಾಡಲಾಗುತ್ತಿದೆ"</string>
+    <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ಬ್ಯಾಟರಿಯ ಸುಸ್ಥಿತಿಗಾಗಿ ಆಪ್ಟಿಮೈಸ್ ಮಾಡಲಾಗುತ್ತಿದೆ"</string>
     <string name="keyguard_low_battery" msgid="1868012396800230904">"ನಿಮ್ಮ ಚಾರ್ಜರ್ ಸಂಪರ್ಕಗೊಳಿಸಿ."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"ಅನ್‌ಲಾಕ್ ಮಾಡಲು ಮೆನು ಒತ್ತಿರಿ."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"ನೆಟ್‌ವರ್ಕ್ ಲಾಕ್ ಆಗಿದೆ"</string>
diff --git a/packages/SystemUI/res-keyguard/values-mn/strings.xml b/packages/SystemUI/res-keyguard/values-mn/strings.xml
index 2ff4a22..13c152f 100644
--- a/packages/SystemUI/res-keyguard/values-mn/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-mn/strings.xml
@@ -38,7 +38,7 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Цэнэглэж байна"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Хурдан цэнэглэж байна"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Удаан цэнэглэж байна"</string>
-    <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Батарейн чанарыг оновчилж байна"</string>
+    <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Батарейн барилтыг оновчилж байна"</string>
     <string name="keyguard_low_battery" msgid="1868012396800230904">"Цэнэглэгчээ холбоно уу."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Түгжээг тайлах бол цэсийг дарна уу."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Сүлжээ түгжигдсэн"</string>
diff --git a/packages/SystemUI/res-keyguard/values-pa/strings.xml b/packages/SystemUI/res-keyguard/values-pa/strings.xml
index 43d549c..04419e0 100644
--- a/packages/SystemUI/res-keyguard/values-pa/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-pa/strings.xml
@@ -38,7 +38,7 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ਚਾਰਜ ਹੋ ਰਿਹਾ ਹੈ"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ਤੇਜ਼ੀ ਨਾਲ ਚਾਰਜ ਹੋ ਰਿਹਾ ਹੈ"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ਹੌਲੀ-ਹੌਲੀ ਚਾਰਜ ਹੋ ਰਿਹਾ ਹੈ"</string>
-    <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ਬੈਟਰੀ ਸਥਿਤੀ ਲਈ ਅਨੁਕੂਲ ਬਣਾਇਆ ਜਾ ਰਿਹਾ"</string>
+    <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ਬੈਟਰੀ ਦੀ ਸਥਿਤੀ ਲਈ ਅਨੁਕੂਲ ਬਣਾਇਆ ਜਾ ਰਿਹਾ ਹੈ"</string>
     <string name="keyguard_low_battery" msgid="1868012396800230904">"ਆਪਣਾ ਚਾਰਜਰ ਕਨੈਕਟ ਕਰੋ।"</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"ਅਣਲਾਕ ਕਰਨ ਲਈ \"ਮੀਨੂ\" ਦਬਾਓ।"</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"ਨੈੱਟਵਰਕ  ਲਾਕ  ਕੀਤਾ ਗਿਆ"</string>
diff --git a/packages/SystemUI/res-keyguard/values-pl/strings.xml b/packages/SystemUI/res-keyguard/values-pl/strings.xml
index 76f755c..01427be 100644
--- a/packages/SystemUI/res-keyguard/values-pl/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-pl/strings.xml
@@ -38,7 +38,7 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Ładowanie"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Szybkie ładowanie"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Wolne ładowanie"</string>
-    <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Optymalizuję, by utrzymać baterię w dobrym stanie"</string>
+    <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Optymalizuję, aby utrzymać baterię w dobrym stanie"</string>
     <string name="keyguard_low_battery" msgid="1868012396800230904">"Podłącz ładowarkę."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Naciśnij Menu, aby odblokować."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Sieć zablokowana"</string>
diff --git a/packages/SystemUI/res-keyguard/values-si/strings.xml b/packages/SystemUI/res-keyguard/values-si/strings.xml
index 3a60782..64609b3 100644
--- a/packages/SystemUI/res-keyguard/values-si/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-si/strings.xml
@@ -38,7 +38,7 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ආරෝපණය වෙමින්"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • වේගයෙන් ආරෝපණය වෙමින්"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • සෙමින් ආරෝපණය වෙමින්"</string>
-    <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • බැටරි සෞඛ්‍යය සඳහා ප්‍රශස්ත කරමින්"</string>
+    <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • බැටරි ආයු කාලය වැඩි දියුණු කරමින්"</string>
     <string name="keyguard_low_battery" msgid="1868012396800230904">"ඔබගේ ආරෝපකයට සම්බන්ධ කරන්න."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"අගුලු හැරීමට මෙනුව ඔබන්න."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"ජාලය අගුළු දමා ඇත"</string>
diff --git a/packages/SystemUI/res-keyguard/values-sr/strings.xml b/packages/SystemUI/res-keyguard/values-sr/strings.xml
index 90b8a3e..7ca04ce 100644
--- a/packages/SystemUI/res-keyguard/values-sr/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-sr/strings.xml
@@ -38,7 +38,7 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Пуни се"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Брзо се пуни"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Споро се пуни"</string>
-    <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Оптимизује се ради доброг стања батерије"</string>
+    <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Оптимизује се ради бољег стања батерије"</string>
     <string name="keyguard_low_battery" msgid="1868012396800230904">"Прикључите пуњач."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Притисните Мени да бисте откључали."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Мрежа је закључана"</string>
diff --git a/packages/SystemUI/res-keyguard/values-te/strings.xml b/packages/SystemUI/res-keyguard/values-te/strings.xml
index c69a8f5..990253a 100644
--- a/packages/SystemUI/res-keyguard/values-te/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-te/strings.xml
@@ -38,7 +38,7 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • ఛార్జ్ అవుతోంది"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • వేగంగా ఛార్జ్ అవుతోంది"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • నెమ్మదిగా ఛార్జ్ అవుతోంది"</string>
-    <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • బ్యాటరీ జీవితకాలాన్ని ఆప్టిమైజ్ చేయడం కోసం"</string>
+    <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • బ్యాటరీ జీవితకాలాన్ని పెంచడం కోసం ఆప్టిమైజ్ చేస్తోంది"</string>
     <string name="keyguard_low_battery" msgid="1868012396800230904">"మీ ఛార్జర్‌ను కనెక్ట్ చేయండి."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"అన్‌లాక్ చేయడానికి మెనుని నొక్కండి."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"నెట్‌వర్క్ లాక్ చేయబడింది"</string>
diff --git a/packages/SystemUI/res-keyguard/values-uk/strings.xml b/packages/SystemUI/res-keyguard/values-uk/strings.xml
index a16576d..62950e2 100644
--- a/packages/SystemUI/res-keyguard/values-uk/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-uk/strings.xml
@@ -38,7 +38,7 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Заряджання"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Швидке заряджання"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Повільне заряджання"</string>
-    <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Стан акумулятора оптимізується"</string>
+    <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Оптимізація для збереження заряду акумулятора"</string>
     <string name="keyguard_low_battery" msgid="1868012396800230904">"Підключіть зарядний пристрій."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Натисніть меню, щоб розблокувати."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Мережу заблоковано"</string>
diff --git a/packages/SystemUI/res-keyguard/values-uz/strings.xml b/packages/SystemUI/res-keyguard/values-uz/strings.xml
index 44e4191..f2ca159 100644
--- a/packages/SystemUI/res-keyguard/values-uz/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-uz/strings.xml
@@ -38,7 +38,7 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Quvvat olmoqda"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Tezkor quvvat olmoqda"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Sekin quvvat olmoqda"</string>
-    <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Batareya quvvati muvozanatlanmoqda"</string>
+    <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • Batareya uchun optimizatsiya"</string>
     <string name="keyguard_low_battery" msgid="1868012396800230904">"Quvvatlash moslamasini ulang."</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"Qulfdan chiqarish uchun Menyu tugmasini bosing."</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"Tarmoq qulflangan"</string>
diff --git a/packages/SystemUI/res-keyguard/values-zh-rHK/strings.xml b/packages/SystemUI/res-keyguard/values-zh-rHK/strings.xml
index 1054e36..b4006e8 100644
--- a/packages/SystemUI/res-keyguard/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-zh-rHK/strings.xml
@@ -38,7 +38,7 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 正在充電"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 正在快速充電"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> •正在慢速充電"</string>
-    <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 正在優化電池狀態"</string>
+    <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 優化電池效能"</string>
     <string name="keyguard_low_battery" msgid="1868012396800230904">"請連接充電器。"</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"按下 [選單] 即可解鎖。"</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"網絡已鎖定"</string>
diff --git a/packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml b/packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml
index 7efa2f9..757bf56 100644
--- a/packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res-keyguard/values-zh-rTW/strings.xml
@@ -38,7 +38,7 @@
     <string name="keyguard_plugged_in" msgid="8169926454348380863">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 充電中"</string>
     <string name="keyguard_plugged_in_charging_fast" msgid="4386594091107340426">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 快速充電中"</string>
     <string name="keyguard_plugged_in_charging_slowly" msgid="217655355424210">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 慢速充電中"</string>
-    <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 針對電池狀態進行最佳化調整"</string>
+    <string name="keyguard_plugged_in_charging_limited" msgid="1158086783302116604">"<xliff:g id="PERCENTAGE">%s</xliff:g> • 最佳化調整電池狀態"</string>
     <string name="keyguard_low_battery" msgid="1868012396800230904">"請連接充電器。"</string>
     <string name="keyguard_instructions_when_pattern_disabled" msgid="8448804180089936954">"按選單鍵解鎖。"</string>
     <string name="keyguard_network_locked_message" msgid="407096292844868608">"網路已鎖定"</string>
diff --git a/packages/SystemUI/res/drawable/ic_exit_to_app.xml b/packages/SystemUI/res/drawable/ic_exit_to_app.xml
new file mode 100644
index 0000000..a2f3c68
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_exit_to_app.xml
@@ -0,0 +1,27 @@
+<!--
+  ~ 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.
+  -->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+        android:width="24dp"
+        android:height="24dp"
+        android:viewportWidth="24"
+        android:viewportHeight="24">
+
+    <path
+        android:pathData="M10.09 15.59L11.5 17l5-5-5-5-1.41 1.41L12.67 11H3v2h9.67l-2.58 2.59zM19 3H5c-1.11 0-2 .9-2 2v4h2V5h14v14H5v-4H3v4c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2z"
+        android:fillColor="#ffffff"
+        android:fillType="evenOdd"/>
+
+</vector>
diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml
index fa4eea5..fe4ba63 100644
--- a/packages/SystemUI/res/values-af/strings.xml
+++ b/packages/SystemUI/res/values-af/strings.xml
@@ -353,6 +353,8 @@
     <string name="quick_settings_user_title" msgid="8673045967216204537">"Gebruiker"</string>
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Nuwe gebruiker"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
+    <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+    <skip />
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Nie gekoppel nie"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Geen netwerk nie"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi af"</string>
@@ -454,9 +456,11 @@
     <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Wys profiel"</string>
     <string name="user_add_user" msgid="4336657383006913022">"Voeg gebruiker by"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"Nuwe gebruiker"</string>
-    <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Verwyder gas?"</string>
+    <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) -->
+    <skip />
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alle programme en data in hierdie sessie sal uitgevee word."</string>
-    <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Verwyder"</string>
+    <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) -->
+    <skip />
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"Welkom terug, gas!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"Wiil jy jou sessie voortsit?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Begin van voor af"</string>
diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml
index 5a22d91..9149aa6 100644
--- a/packages/SystemUI/res/values-am/strings.xml
+++ b/packages/SystemUI/res/values-am/strings.xml
@@ -353,6 +353,8 @@
     <string name="quick_settings_user_title" msgid="8673045967216204537">"ተጠቃሚ"</string>
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"አዲስ ተጠቃሚ"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
+    <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+    <skip />
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"አልተገናኘም"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"ምንም አውታረ መረብ የለም"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi ጠፍቷል"</string>
@@ -454,9 +456,11 @@
     <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"መገለጫ አሳይ"</string>
     <string name="user_add_user" msgid="4336657383006913022">"ተጠቃሚ አክል"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"አዲስ ተጠቃሚ"</string>
-    <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"እንግዳ ይወገድ?"</string>
+    <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) -->
+    <skip />
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"በዚህ ክፍለ-ጊዜ ውስጥ ያሉ ሁሉም መተግበሪያዎች እና ውሂብ ይሰረዛሉ።"</string>
-    <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"አስወግድ"</string>
+    <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) -->
+    <skip />
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"እንኳን በደህና ተመለሱ እንግዳ!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"ክፍለ-ጊዜዎን መቀጠል ይፈልጋሉ?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"እንደገና ጀምር"</string>
diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml
index 39afa83..0146fcd 100644
--- a/packages/SystemUI/res/values-ar/strings.xml
+++ b/packages/SystemUI/res/values-ar/strings.xml
@@ -357,6 +357,8 @@
     <string name="quick_settings_user_title" msgid="8673045967216204537">"المستخدم"</string>
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"مستخدم جديد"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
+    <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+    <skip />
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"ليست متصلة"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"لا تتوفر شبكة"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"‏إيقاف Wi-Fi"</string>
@@ -462,9 +464,11 @@
     <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"عرض الملف الشخصي"</string>
     <string name="user_add_user" msgid="4336657383006913022">"إضافة مستخدم"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"مستخدم جديد"</string>
-    <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"هل تريد إزالة جلسة الضيف؟"</string>
+    <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) -->
+    <skip />
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"سيتم حذف كل التطبيقات والبيانات في هذه الجلسة."</string>
-    <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"إزالة"</string>
+    <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) -->
+    <skip />
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"مرحبًا بك مجددًا في جلسة الضيف"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"هل تريد متابعة جلستك؟"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"البدء من جديد"</string>
diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml
index cfb3009..3c206f2 100644
--- a/packages/SystemUI/res/values-as/strings.xml
+++ b/packages/SystemUI/res/values-as/strings.xml
@@ -353,6 +353,8 @@
     <string name="quick_settings_user_title" msgid="8673045967216204537">"ব্যৱহাৰকাৰী"</string>
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"নতুন ব্যৱহাৰকাৰী"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"ৱাই-ফাই"</string>
+    <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+    <skip />
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"সংযোগ হৈ থকা নাই"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"নেটৱৰ্ক নাই"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"ৱাই-ফাই অফ"</string>
@@ -454,9 +456,11 @@
     <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"প্ৰ\'ফাইল দেখুৱাওক"</string>
     <string name="user_add_user" msgid="4336657383006913022">"ব্যৱহাৰকাৰী যোগ কৰক"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"নতুন ব্যৱহাৰকাৰী"</string>
-    <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"অতিথি আঁতৰাবনে?"</string>
+    <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) -->
+    <skip />
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"এই ছেশ্বনৰ সকলো এপ্ আৰু ডেটা মচা হ\'ব।"</string>
-    <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"আঁতৰাওক"</string>
+    <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) -->
+    <skip />
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"আপোনাক পুনৰাই স্বাগতম জনাইছোঁ!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"আপুনি আপোনাৰ ছেশ্বন অব্যাহত ৰাখিব বিচাৰেনে?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"আকৌ আৰম্ভ কৰক"</string>
diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml
index fef70c3..67e1718 100644
--- a/packages/SystemUI/res/values-az/strings.xml
+++ b/packages/SystemUI/res/values-az/strings.xml
@@ -353,6 +353,8 @@
     <string name="quick_settings_user_title" msgid="8673045967216204537">"İstifadəçi"</string>
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Yeni istifadəçi"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
+    <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+    <skip />
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Bağlantı yoxdur"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Şəbəkə yoxdur"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi sönülüdür"</string>
@@ -454,9 +456,11 @@
     <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Show profile"</string>
     <string name="user_add_user" msgid="4336657383006913022">"İstifadəçi əlavə edin"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"Yeni istifadəçi"</string>
-    <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Qonaq silinsin?"</string>
+    <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) -->
+    <skip />
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Bu sessiyada bütün tətbiqlər və data silinəcək."</string>
-    <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Yığışdır"</string>
+    <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) -->
+    <skip />
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"Xoş gəlmisiniz!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"Sessiya davam etsin?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Yenidən başlayın"</string>
diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
index 978b46d..ae8ffbf 100644
--- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml
+++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml
@@ -354,6 +354,8 @@
     <string name="quick_settings_user_title" msgid="8673045967216204537">"Korisnik"</string>
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Novi korisnik"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"WiFi"</string>
+    <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+    <skip />
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Veza nije uspostavljena"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Nema mreže"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"WiFi je isključen"</string>
@@ -456,9 +458,11 @@
     <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Prikaži profil"</string>
     <string name="user_add_user" msgid="4336657383006913022">"Dodaj korisnika"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"Novi korisnik"</string>
-    <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Želite li da uklonite gosta?"</string>
+    <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) -->
+    <skip />
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Sve aplikacije i podaci u ovoj sesiji će biti izbrisani."</string>
-    <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Ukloni"</string>
+    <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) -->
+    <skip />
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"Dobro došli nazad, goste!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"Želite li da nastavite sesiju?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Počni iz početka"</string>
diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml
index 641134e..b90d57e 100644
--- a/packages/SystemUI/res/values-be/strings.xml
+++ b/packages/SystemUI/res/values-be/strings.xml
@@ -355,6 +355,8 @@
     <string name="quick_settings_user_title" msgid="8673045967216204537">"Карыстальнік"</string>
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Новы карыстальнік"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
+    <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+    <skip />
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Няма падключэння"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Няма сеткi"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi адключаны"</string>
@@ -458,9 +460,11 @@
     <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Паказаць профіль"</string>
     <string name="user_add_user" msgid="4336657383006913022">"Дадаць карыстальніка"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"Новы карыстальнік"</string>
-    <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Выдаліць госця?"</string>
+    <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) -->
+    <skip />
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Усе праграмы і даныя гэтага сеанса будуць выдалены."</string>
-    <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Выдаліць"</string>
+    <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) -->
+    <skip />
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"З вяртаннем, госць!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"Хочаце працягнуць сеанс?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Пачаць зноў"</string>
diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml
index 0848383..bfc8ef1 100644
--- a/packages/SystemUI/res/values-bg/strings.xml
+++ b/packages/SystemUI/res/values-bg/strings.xml
@@ -353,6 +353,8 @@
     <string name="quick_settings_user_title" msgid="8673045967216204537">"Потребител"</string>
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Нов потребител"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
+    <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+    <skip />
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Няма връзка"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Няма мрежа"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi е изключен"</string>
@@ -454,9 +456,11 @@
     <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Показване на потребителския профил"</string>
     <string name="user_add_user" msgid="4336657383006913022">"Добавяне на потребител"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"Нов потребител"</string>
-    <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Да се премахне ли гостът?"</string>
+    <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) -->
+    <skip />
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Всички приложения и данни в тази сесия ще бъдат изтрити."</string>
-    <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Премахване"</string>
+    <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) -->
+    <skip />
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"Добре дошли отново в сесията като гост!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"Искате ли да продължите сесията си?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Започване отначало"</string>
diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml
index 1099901..c41a434 100644
--- a/packages/SystemUI/res/values-bn/strings.xml
+++ b/packages/SystemUI/res/values-bn/strings.xml
@@ -85,8 +85,7 @@
     <string name="screenshot_failed_title" msgid="3259148215671936891">"স্ক্রিনশট সেভ করা যায়নি"</string>
     <string name="screenshot_failed_to_save_user_locked_text" msgid="6156607948256936920">"স্ক্রিনশট সেভ করার আগে ডিভাইসটি অবশ্যই আনলক করতে হবে"</string>
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"আবার স্ক্রিনশট নেওয়ার চেষ্টা করুন"</string>
-    <!-- no translation found for screenshot_failed_to_save_text (7232739948999195960) -->
-    <skip />
+    <string name="screenshot_failed_to_save_text" msgid="7232739948999195960">"স্ক্রিনশট সেভ করা যায়নি"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"এই অ্যাপ বা আপনার প্রতিষ্ঠান স্ক্রিনশট নেওয়ার অনুমতি দেয়নি"</string>
     <string name="screenshot_edit_label" msgid="8754981973544133050">"এডিট করুন"</string>
     <string name="screenshot_edit_description" msgid="3333092254706788906">"স্ক্রিনশট এডিট করুন"</string>
@@ -354,6 +353,8 @@
     <string name="quick_settings_user_title" msgid="8673045967216204537">"ব্যবহারকারী"</string>
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"নতুন ব্যবহারকারী"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"ওয়াই-ফাই"</string>
+    <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+    <skip />
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"সংযুক্ত নয়"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"কোনো নেটওয়ার্ক নেই"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"ওয়াই-ফাই বন্ধ"</string>
@@ -455,9 +456,11 @@
     <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"প্রোফাইল দেখান"</string>
     <string name="user_add_user" msgid="4336657383006913022">"ব্যবহারকারী জুড়ুন"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"নতুন ব্যবহারকারী"</string>
-    <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"অতিথি সরাবেন?"</string>
+    <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) -->
+    <skip />
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"এই সেশনের সব অ্যাপ্লিকেশান ও ডেটা মুছে ফেলা হবে।"</string>
-    <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"সরান"</string>
+    <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) -->
+    <skip />
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"অতিথি, আপনি ফিরে আসায় আপনাকে স্বাগত!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"আপনি কি আপনার সেশনটি অবিরত রাখতে চান?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"আবার শুরু করুন"</string>
diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml
index 3c81a4b..9a28773 100644
--- a/packages/SystemUI/res/values-bs/strings.xml
+++ b/packages/SystemUI/res/values-bs/strings.xml
@@ -354,6 +354,8 @@
     <string name="quick_settings_user_title" msgid="8673045967216204537">"Korisnik"</string>
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Novi korisnik"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"WiFi"</string>
+    <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+    <skip />
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Nije povezano"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Nema mreže"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"WiFi je isključen"</string>
@@ -456,9 +458,11 @@
     <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Pokaži profil"</string>
     <string name="user_add_user" msgid="4336657383006913022">"Dodaj korisnika"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"Novi korisnik"</string>
-    <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Želite li ukloniti gosta?"</string>
+    <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) -->
+    <skip />
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Sve aplikacije i svi podaci iz ove sesije bit će izbrisani."</string>
-    <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Ukloni"</string>
+    <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) -->
+    <skip />
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"Zdravo! Lijepo je opet vidjeti goste."</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"Želite li nastaviti sesiju?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Počni ispočetka"</string>
diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml
index ef419ab..426005a 100644
--- a/packages/SystemUI/res/values-ca/strings.xml
+++ b/packages/SystemUI/res/values-ca/strings.xml
@@ -353,6 +353,8 @@
     <string name="quick_settings_user_title" msgid="8673045967216204537">"Usuari"</string>
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Usuari nou"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
+    <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+    <skip />
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Desconnectat"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"No hi ha cap xarxa"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi desconnectada"</string>
@@ -454,9 +456,11 @@
     <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Mostra el perfil"</string>
     <string name="user_add_user" msgid="4336657383006913022">"Afegeix un usuari"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"Usuari nou"</string>
-    <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Vols suprimir el convidat?"</string>
+    <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) -->
+    <skip />
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Totes les aplicacions i les dades d\'aquesta sessió se suprimiran."</string>
-    <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Suprimeix"</string>
+    <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) -->
+    <skip />
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"Benvingut de nou, convidat."</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"Vols continuar amb la sessió?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Torna a començar"</string>
diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml
index 972232f..3e015ec 100644
--- a/packages/SystemUI/res/values-cs/strings.xml
+++ b/packages/SystemUI/res/values-cs/strings.xml
@@ -355,6 +355,8 @@
     <string name="quick_settings_user_title" msgid="8673045967216204537">"Uživatel"</string>
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Nový uživatel"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
+    <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+    <skip />
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Nepřipojeno"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Žádná síť"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi vypnuta"</string>
@@ -458,9 +460,11 @@
     <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Zobrazit profil"</string>
     <string name="user_add_user" msgid="4336657383006913022">"Přidat uživatele"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"Nový uživatel"</string>
-    <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Odstranit hosta?"</string>
+    <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) -->
+    <skip />
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Veškeré aplikace a data v této relaci budou vymazána."</string>
-    <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Odstranit"</string>
+    <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) -->
+    <skip />
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"Vítejte zpět v relaci hosta!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"Chcete v relaci pokračovat?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Začít znovu"</string>
diff --git a/packages/SystemUI/res/values-cs/strings_tv.xml b/packages/SystemUI/res/values-cs/strings_tv.xml
index 0a9850c..eeab0d9 100644
--- a/packages/SystemUI/res/values-cs/strings_tv.xml
+++ b/packages/SystemUI/res/values-cs/strings_tv.xml
@@ -23,7 +23,7 @@
     <string name="app_accessed_mic" msgid="2754428675130470196">"Aplikace %1$s použila mikrofon"</string>
     <string name="notification_vpn_connected" msgid="3891023882833274730">"Síť VPN je připojena"</string>
     <string name="notification_vpn_disconnected" msgid="7150747626448044843">"Síť VPN je odpojena"</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Prostřednictvím aplikace <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Přes <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
     <string name="tv_notification_panel_title" msgid="5311050946506276154">"Oznámení"</string>
     <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Žádná oznámení"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml
index eb5f8ca..8fd48b1 100644
--- a/packages/SystemUI/res/values-da/strings.xml
+++ b/packages/SystemUI/res/values-da/strings.xml
@@ -353,6 +353,8 @@
     <string name="quick_settings_user_title" msgid="8673045967216204537">"Bruger"</string>
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Ny bruger"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
+    <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+    <skip />
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Ikke forbundet"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Intet netværk"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi slået fra"</string>
@@ -454,9 +456,11 @@
     <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Vis profil"</string>
     <string name="user_add_user" msgid="4336657383006913022">"Tilføj bruger"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"Ny bruger"</string>
-    <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Vil du fjerne gæsten?"</string>
+    <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) -->
+    <skip />
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alle apps og data i denne session slettes."</string>
-    <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Fjern"</string>
+    <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) -->
+    <skip />
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"Velkommen tilbage, gæst!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"Vil du fortsætte din session?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Start forfra"</string>
diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml
index 66fb7eb..84b63ba 100644
--- a/packages/SystemUI/res/values-de/strings.xml
+++ b/packages/SystemUI/res/values-de/strings.xml
@@ -353,6 +353,8 @@
     <string name="quick_settings_user_title" msgid="8673045967216204537">"Nutzer"</string>
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Neuer Nutzer"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"WLAN"</string>
+    <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+    <skip />
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Nicht verbunden"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Kein Netz"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"WLAN aus"</string>
@@ -454,9 +456,11 @@
     <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Profil öffnen"</string>
     <string name="user_add_user" msgid="4336657383006913022">"Nutzer hinzufügen"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"Neuer Nutzer"</string>
-    <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Gast entfernen?"</string>
+    <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) -->
+    <skip />
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alle Apps und Daten in dieser Sitzung werden gelöscht."</string>
-    <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Entfernen"</string>
+    <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) -->
+    <skip />
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"Willkommen zurück im Gastmodus"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"Möchtest du deine Sitzung fortsetzen?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Neu starten"</string>
diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml
index 40e2294..bec78c0 100644
--- a/packages/SystemUI/res/values-el/strings.xml
+++ b/packages/SystemUI/res/values-el/strings.xml
@@ -353,6 +353,8 @@
     <string name="quick_settings_user_title" msgid="8673045967216204537">"Χρήστης"</string>
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Νέος χρήστης"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
+    <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+    <skip />
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Μη συνδεδεμένο"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Κανένα δίκτυο"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi ανενεργό"</string>
@@ -454,9 +456,11 @@
     <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Εμφάνιση προφίλ"</string>
     <string name="user_add_user" msgid="4336657383006913022">"Προσθήκη χρήστη"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"Νέος χρήστης"</string>
-    <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Κατάργηση επισκέπτη;"</string>
+    <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) -->
+    <skip />
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Όλες οι εφαρμογές και τα δεδομένα αυτής της περιόδου σύνδεσης θα διαγραφούν."</string>
-    <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Κατάργηση"</string>
+    <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) -->
+    <skip />
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"Επισκέπτη , καλώς όρισες ξανά!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"Θέλετε να συνεχίσετε την περίοδο σύνδεσής σας;"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Έναρξη από την αρχή"</string>
diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml
index ca81ed6..21cac18 100644
--- a/packages/SystemUI/res/values-en-rAU/strings.xml
+++ b/packages/SystemUI/res/values-en-rAU/strings.xml
@@ -353,6 +353,8 @@
     <string name="quick_settings_user_title" msgid="8673045967216204537">"User"</string>
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"New user"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
+    <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+    <skip />
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Not Connected"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"No Network"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi Off"</string>
@@ -454,9 +456,9 @@
     <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Show profile"</string>
     <string name="user_add_user" msgid="4336657383006913022">"Add user"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"New user"</string>
-    <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Remove guest?"</string>
+    <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"End Guest session?"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"All apps and data in this session will be deleted."</string>
-    <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Remove"</string>
+    <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"End session"</string>
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"Welcome back, guest!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"Do you want to continue your session?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Start again"</string>
diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml
index 89537e9..7ac4c0d 100644
--- a/packages/SystemUI/res/values-en-rCA/strings.xml
+++ b/packages/SystemUI/res/values-en-rCA/strings.xml
@@ -353,6 +353,8 @@
     <string name="quick_settings_user_title" msgid="8673045967216204537">"User"</string>
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"New user"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
+    <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+    <skip />
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Not Connected"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"No Network"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi Off"</string>
@@ -454,9 +456,9 @@
     <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Show profile"</string>
     <string name="user_add_user" msgid="4336657383006913022">"Add user"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"New user"</string>
-    <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Remove guest?"</string>
+    <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"End Guest session?"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"All apps and data in this session will be deleted."</string>
-    <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Remove"</string>
+    <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"End session"</string>
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"Welcome back, guest!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"Do you want to continue your session?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Start again"</string>
diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml
index ca81ed6..21cac18 100644
--- a/packages/SystemUI/res/values-en-rGB/strings.xml
+++ b/packages/SystemUI/res/values-en-rGB/strings.xml
@@ -353,6 +353,8 @@
     <string name="quick_settings_user_title" msgid="8673045967216204537">"User"</string>
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"New user"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
+    <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+    <skip />
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Not Connected"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"No Network"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi Off"</string>
@@ -454,9 +456,9 @@
     <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Show profile"</string>
     <string name="user_add_user" msgid="4336657383006913022">"Add user"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"New user"</string>
-    <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Remove guest?"</string>
+    <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"End Guest session?"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"All apps and data in this session will be deleted."</string>
-    <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Remove"</string>
+    <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"End session"</string>
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"Welcome back, guest!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"Do you want to continue your session?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Start again"</string>
diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml
index ca81ed6..21cac18 100644
--- a/packages/SystemUI/res/values-en-rIN/strings.xml
+++ b/packages/SystemUI/res/values-en-rIN/strings.xml
@@ -353,6 +353,8 @@
     <string name="quick_settings_user_title" msgid="8673045967216204537">"User"</string>
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"New user"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
+    <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+    <skip />
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Not Connected"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"No Network"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi Off"</string>
@@ -454,9 +456,9 @@
     <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Show profile"</string>
     <string name="user_add_user" msgid="4336657383006913022">"Add user"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"New user"</string>
-    <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Remove guest?"</string>
+    <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"End Guest session?"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"All apps and data in this session will be deleted."</string>
-    <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Remove"</string>
+    <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"End session"</string>
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"Welcome back, guest!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"Do you want to continue your session?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Start again"</string>
diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml
index 6fa2171..2f30c41 100644
--- a/packages/SystemUI/res/values-en-rXC/strings.xml
+++ b/packages/SystemUI/res/values-en-rXC/strings.xml
@@ -353,6 +353,8 @@
     <string name="quick_settings_user_title" msgid="8673045967216204537">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‏‎‎‎‎‏‎‏‏‏‎‎‏‏‎‏‏‎‎‏‎‏‎‏‏‎‏‎‏‏‏‏‏‏‎‎‏‎‎‎‏‏‏‎‎‏‎‏‏‎‏‎‏‏‏‏‏‎‎‏‎User‎‏‎‎‏‎"</string>
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‏‎‎‏‏‏‎‏‏‎‎‎‏‎‎‏‏‎‏‎‏‎‎‏‏‎‏‏‏‎‏‎‏‎‎‏‏‎‏‏‏‎‎‏‎‏‏‎‏‎‎‎‏‎‎‎‏‎‎New user‎‏‎‎‏‎"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‎‏‏‏‏‏‏‏‎‏‏‎‎‎‎‏‎‎‎‎‏‏‎‏‏‎‎‎‎‏‎‎‏‏‏‏‏‎‎‎‏‎‏‎‏‏‎‎‏‎‏‏‎‎‎‎‏‏‎‎‎Wi-Fi‎‏‎‎‏‎"</string>
+    <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+    <skip />
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‎‎‎‎‏‏‏‏‏‏‏‎‏‏‏‎‎‎‏‏‎‏‎‎‎‎‎‏‎‎‎‎‏‏‎‎‎‏‏‏‏‎‎‏‎‎‎‏‏‎‏‏‎‏‏‏‎‎‎‎Not Connected‎‏‎‎‏‎"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‏‎‎‏‏‎‏‎‎‏‏‏‏‏‎‎‏‎‎‏‎‏‏‏‏‎‎‏‎‏‎‎‎‏‎‏‎‏‎‏‎‎‎‏‎‏‏‏‏‏‏‏‎‏‏‎‏‎‎‎‏‎No Network‎‏‎‎‏‎"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‎‏‏‏‏‎‎‎‏‏‏‎‏‏‎‏‏‏‎‎‏‎‏‎‏‎‎‎‏‏‎‎‎‎‏‏‏‏‎‎‏‏‎‎‏‏‎‏‎‏‏‏‎‎‎‎‏‎‏‎‎Wi-Fi Off‎‏‎‎‏‎"</string>
@@ -454,9 +456,9 @@
     <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‏‎‏‎‎‎‎‎‏‏‎‎‏‏‏‎‏‎‏‏‏‏‏‏‏‎‎‎‎‏‏‎‎‏‏‎‎‎‎‏‎‏‎‏‎‎‏‏‎‏‎‎‎‏‎‎‎‎‎Show profile‎‏‎‎‏‎"</string>
     <string name="user_add_user" msgid="4336657383006913022">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‏‏‏‎‎‎‎‏‎‏‏‏‎‏‏‏‎‎‏‏‎‏‏‏‎‏‎‎‏‏‏‎‎‏‎‏‎‏‎‏‏‎‎‏‏‏‏‎‎‎‎‎‏‏‏‏‏‏‏‏‎‎Add user‎‏‎‎‏‎"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‎‎‎‎‎‎‎‏‎‏‏‎‎‎‎‏‎‏‎‎‎‎‏‎‎‏‎‎‏‏‏‏‏‏‎‎‎‎‎‏‏‏‏‎‎‏‎‎‎‎‏‏‎‎‏‏‎‏‎New user‎‏‎‎‏‎"</string>
-    <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‎‎‎‏‎‏‏‎‎‏‏‎‏‏‎‏‎‏‎‏‏‎‎‏‎‏‏‎‏‏‏‎‏‎‎‎‏‎‏‏‎‎‏‏‏‎‏‎‎‏‏‏‎‎‏‎‎‎‏‏‏‎‎Remove guest?‎‏‎‎‏‎"</string>
+    <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‎‏‏‏‏‎‎‎‎‏‏‏‎‏‏‏‏‏‎‏‏‎‏‏‎‏‏‎‏‏‎‎‏‏‏‎‏‏‎‏‏‏‏‎‏‎‎‎‏‎‎‎‏‏‏‏‏‎‏‎‏‎‏‎End guest session?‎‏‎‎‏‎"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‎‎‏‏‎‎‏‎‎‎‏‎‏‏‏‎‏‎‏‎‏‎‏‎‎‎‎‎‏‎‎‏‎‏‏‎‎‏‏‎‏‎‎‏‏‏‎‏‏‏‏‎‏‎‏‏‏‎‏‎All apps and data in this session will be deleted.‎‏‎‎‏‎"</string>
-    <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‎‎‎‎‎‏‎‏‎‏‎‎‎‎‎‎‏‎‏‎‏‎‏‎‏‎‎‏‏‏‎‏‎‏‎‏‏‏‎‎‏‏‎‎‏‎‎‎‎‏‏‏‎‎‎‏‏‎‏‎Remove‎‏‎‎‏‎"</string>
+    <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‏‎‏‏‎‎‏‏‎‏‎‏‏‏‏‏‏‎‏‏‎‎‎‎‏‏‎‏‎‏‎‎‎‏‏‎‏‎‏‏‎‏‎‏‏‏‏‎‏‏‏‎‎‎‎‏‏‏‏‏‏‎End session‎‏‎‎‏‎"</string>
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‎‎‏‏‎‎‏‏‎‎‏‎‏‎‏‎‏‏‎‏‎‎‎‎‏‎‎‏‎‏‏‏‎‎‏‎‏‏‎‏‏‏‏‏‏‏‏‏‏‎‏‏‏‎‏‎‎‎‎‎‎Welcome back, guest!‎‏‎‎‏‎"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‎‏‎‏‏‏‏‎‎‎‏‏‎‎‏‎‏‎‎‏‎‎‎‏‎‎‏‎‏‏‏‏‎‎‏‏‏‏‏‏‎‏‏‎‏‎‎‏‏‎‎‎‏‎‏‏‏‏‏‎‎‎‏‎Do you want to continue your session?‎‏‎‎‏‎"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"‎‏‎‎‎‎‎‏‎‏‏‏‎‎‎‎‎‎‏‎‎‏‎‎‎‎‏‏‏‏‏‏‏‏‏‎‏‏‏‏‏‏‎‎‏‏‏‏‏‎‏‎‎‎‏‎‎‎‏‎‏‏‏‏‏‏‎‎‎‏‎‏‎‎‏‏‏‏‏‎‎‏‏‏‎‎‏‎‎‎‎‏‏‏‎‏‎Start over‎‏‎‎‏‎"</string>
diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml
index e9a2785..7ef9cdb 100644
--- a/packages/SystemUI/res/values-es-rUS/strings.xml
+++ b/packages/SystemUI/res/values-es-rUS/strings.xml
@@ -353,6 +353,8 @@
     <string name="quick_settings_user_title" msgid="8673045967216204537">"Usuario"</string>
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Usuario nuevo"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
+    <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+    <skip />
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Sin conexión"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Sin red"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi desactivada"</string>
@@ -454,9 +456,9 @@
     <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Mostrar perfil"</string>
     <string name="user_add_user" msgid="4336657383006913022">"Agregar usuario"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"Usuario nuevo"</string>
-    <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"¿Eliminar invitado?"</string>
+    <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"¿Quieres finalizar la sesión de invitado?"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Se eliminarán las aplicaciones y los datos de esta sesión."</string>
-    <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Eliminar"</string>
+    <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Finalizar sesión"</string>
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"Bienvenido nuevamente, invitado."</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"¿Quieres retomar la sesión?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Volver a empezar"</string>
diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml
index 11ff9af..6c6b511 100644
--- a/packages/SystemUI/res/values-es/strings.xml
+++ b/packages/SystemUI/res/values-es/strings.xml
@@ -85,8 +85,7 @@
     <string name="screenshot_failed_title" msgid="3259148215671936891">"No se ha podido guardar la captura de pantalla"</string>
     <string name="screenshot_failed_to_save_user_locked_text" msgid="6156607948256936920">"El dispositivo debe desbloquearse para que se pueda guardar la captura de pantalla"</string>
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"Vuelve a intentar hacer la captura de pantalla"</string>
-    <!-- no translation found for screenshot_failed_to_save_text (7232739948999195960) -->
-    <skip />
+    <string name="screenshot_failed_to_save_text" msgid="7232739948999195960">"No se puede guardar la captura de pantalla"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"La aplicación o tu organización no permiten realizar capturas de pantalla"</string>
     <string name="screenshot_edit_label" msgid="8754981973544133050">"Editar"</string>
     <string name="screenshot_edit_description" msgid="3333092254706788906">"Editar captura de pantalla"</string>
@@ -354,6 +353,8 @@
     <string name="quick_settings_user_title" msgid="8673045967216204537">"Usuario"</string>
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Nuevo usuario"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
+    <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+    <skip />
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"No conectado"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"No hay red."</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi desactivado"</string>
@@ -455,9 +456,9 @@
     <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Mostrar perfil"</string>
     <string name="user_add_user" msgid="4336657383006913022">"Añadir usuario"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"Nuevo usuario"</string>
-    <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"¿Quitar invitado?"</string>
+    <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"¿Finalizar sesión de invitado?"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Se eliminarán todas las aplicaciones y datos de esta sesión."</string>
-    <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Quitar"</string>
+    <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Finalizar sesión"</string>
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"Hola de nuevo, invitado"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"¿Quieres continuar con la sesión?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Volver a empezar"</string>
diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml
index 27a9df0..c64a07e 100644
--- a/packages/SystemUI/res/values-et/strings.xml
+++ b/packages/SystemUI/res/values-et/strings.xml
@@ -353,6 +353,8 @@
     <string name="quick_settings_user_title" msgid="8673045967216204537">"Kasutaja"</string>
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Uus kasutaja"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"WiFi"</string>
+    <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+    <skip />
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Ühendus puudub"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Võrku pole"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"WiFi-ühendus on väljas"</string>
@@ -454,9 +456,11 @@
     <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Kuva profiil"</string>
     <string name="user_add_user" msgid="4336657383006913022">"Lisa kasutaja"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"Uus kasutaja"</string>
-    <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Kas eemaldada külaline?"</string>
+    <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) -->
+    <skip />
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Seansi kõik rakendused ja andmed kustutatakse."</string>
-    <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Eemalda"</string>
+    <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) -->
+    <skip />
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"Tere tulemast tagasi, külaline!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"Kas soovite seansiga jätkata?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Alusta uuesti"</string>
diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml
index b2dc3d6..5047cf1 100644
--- a/packages/SystemUI/res/values-eu/strings.xml
+++ b/packages/SystemUI/res/values-eu/strings.xml
@@ -353,6 +353,8 @@
     <string name="quick_settings_user_title" msgid="8673045967216204537">"Erabiltzailea"</string>
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Erabiltzaile berria"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wifia"</string>
+    <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+    <skip />
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Konektatu gabe"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Ez dago sarerik"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi konexioa desaktibatuta"</string>
@@ -454,9 +456,9 @@
     <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Erakutsi profila"</string>
     <string name="user_add_user" msgid="4336657383006913022">"Gehitu erabiltzailea"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"Erabiltzaile berria"</string>
-    <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Gonbidatua kendu nahi duzu?"</string>
+    <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Gonbidatuentzako saioa amaitu nahi duzu?"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Saioko aplikazio eta datu guztiak ezabatuko dira."</string>
-    <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Kendu"</string>
+    <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Amaitu saioa"</string>
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"Ongi etorri berriro, gonbidatu hori!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"Saioarekin jarraitu nahi duzu?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Hasi berriro"</string>
diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml
index 214f5c2..34c6bf6 100644
--- a/packages/SystemUI/res/values-fa/strings.xml
+++ b/packages/SystemUI/res/values-fa/strings.xml
@@ -353,6 +353,8 @@
     <string name="quick_settings_user_title" msgid="8673045967216204537">"کاربر"</string>
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"کاربر جدید"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
+    <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+    <skip />
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"متصل نیست"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"شبکه‌ای موجود نیست"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"‏Wi-Fi خاموش است"</string>
@@ -454,9 +456,11 @@
     <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"نمایش نمایه"</string>
     <string name="user_add_user" msgid="4336657383006913022">"افزودن کاربر"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"کاربر جدید"</string>
-    <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"مهمان حذف شود؟"</string>
+    <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) -->
+    <skip />
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"همه برنامه‌ها و داده‌های این جلسه حذف خواهد شد."</string>
-    <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"حذف"</string>
+    <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) -->
+    <skip />
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"مهمان گرامی، بازگشتتان را خوش آمد می‌گوییم!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"آیا می‌خواهید جلسه‌تان را ادامه دهید؟"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"شروع مجدد"</string>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index cb60ff4..5486e39 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -353,6 +353,8 @@
     <string name="quick_settings_user_title" msgid="8673045967216204537">"Käyttäjä"</string>
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Uusi käyttäjä"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
+    <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+    <skip />
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Ei yhteyttä"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Ei verkkoa"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi-yhteys pois käytöstä"</string>
@@ -454,9 +456,11 @@
     <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Näytä profiili"</string>
     <string name="user_add_user" msgid="4336657383006913022">"Lisää käyttäjä"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"Uusi käyttäjä"</string>
-    <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Poistetaaanko vieras?"</string>
+    <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) -->
+    <skip />
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Kaikki sovellukset ja tämän istunnon tiedot poistetaan."</string>
-    <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Poista"</string>
+    <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) -->
+    <skip />
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"Tervetuloa takaisin!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"Haluatko jatkaa istuntoa?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Aloita alusta"</string>
diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml
index 17752c5..63f65e0 100644
--- a/packages/SystemUI/res/values-fr-rCA/strings.xml
+++ b/packages/SystemUI/res/values-fr-rCA/strings.xml
@@ -353,6 +353,8 @@
     <string name="quick_settings_user_title" msgid="8673045967216204537">"Utilisateur"</string>
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Nouvel utilisateur"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
+    <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+    <skip />
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Non connecté"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Aucun réseau"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi désactivé"</string>
@@ -454,9 +456,11 @@
     <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Afficher le profil"</string>
     <string name="user_add_user" msgid="4336657383006913022">"Ajouter un utilisateur"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"Nouvel utilisateur"</string>
-    <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Supprimer l\'invité?"</string>
+    <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) -->
+    <skip />
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Toutes les applications et les données de cette session seront supprimées."</string>
-    <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Supprimer"</string>
+    <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) -->
+    <skip />
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"Bienvenue à nouveau dans la session Invité"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"Voulez-vous poursuivre la session?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Recommencer"</string>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index 39d82ef..ebf199bf 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -353,6 +353,8 @@
     <string name="quick_settings_user_title" msgid="8673045967216204537">"Utilisateur"</string>
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Nouvel utilisateur"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
+    <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+    <skip />
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Non connecté"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Aucun réseau"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi désactivé"</string>
@@ -454,9 +456,11 @@
     <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Afficher le profil"</string>
     <string name="user_add_user" msgid="4336657383006913022">"Ajouter un utilisateur"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"Nouvel utilisateur"</string>
-    <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Supprimer l\'invité ?"</string>
+    <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) -->
+    <skip />
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Toutes les applications et les données de cette session seront supprimées."</string>
-    <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Supprimer"</string>
+    <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) -->
+    <skip />
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"Bienvenue à nouveau dans la session Invité"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"Voulez-vous poursuivre la dernière session ?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Non, nouvelle session"</string>
diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml
index 6494e0afc..36f7f4c 100644
--- a/packages/SystemUI/res/values-gl/strings.xml
+++ b/packages/SystemUI/res/values-gl/strings.xml
@@ -353,6 +353,8 @@
     <string name="quick_settings_user_title" msgid="8673045967216204537">"Usuario"</string>
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Novo usuario"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wifi"</string>
+    <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+    <skip />
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Non conectada"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Non hai rede"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wifi desactivada"</string>
@@ -454,9 +456,11 @@
     <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Mostrar perfil"</string>
     <string name="user_add_user" msgid="4336657383006913022">"Engadir usuario"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"Novo usuario"</string>
-    <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Queres eliminar o invitado?"</string>
+    <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) -->
+    <skip />
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Eliminaranse todas as aplicacións e datos desta sesión."</string>
-    <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Eliminar"</string>
+    <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) -->
+    <skip />
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"Benvido de novo, convidado."</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"Queres continuar coa túa sesión?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Comezar de novo"</string>
diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml
index 369fd1d..66423e4 100644
--- a/packages/SystemUI/res/values-gu/strings.xml
+++ b/packages/SystemUI/res/values-gu/strings.xml
@@ -353,6 +353,8 @@
     <string name="quick_settings_user_title" msgid="8673045967216204537">"વપરાશકર્તા"</string>
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"નવો વપરાશકર્તા"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"વાઇ-ફાઇ"</string>
+    <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+    <skip />
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"કનેક્ટ થયેલ નથી"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"કોઈ નેટવર્ક નથી"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"વાઇ-ફાઇ બંધ"</string>
@@ -454,9 +456,11 @@
     <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"પ્રોફાઇલ બતાવો"</string>
     <string name="user_add_user" msgid="4336657383006913022">"વપરાશકર્તા ઉમેરો"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"નવો વપરાશકર્તા"</string>
-    <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"અતિથિ દૂર કરીએ?"</string>
+    <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) -->
+    <skip />
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"આ સત્રમાંની તમામ ઍપ્લિકેશનો અને ડેટા કાઢી નાખવામાં આવશે."</string>
-    <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"દૂર કરો"</string>
+    <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) -->
+    <skip />
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"ફરી સ્વાગત છે, અતિથિ!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"શું તમે તમારું સત્ર ચાલુ કરવા માંગો છો?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"શરૂ કરો"</string>
diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml
index 0b16f52..65cba2f 100644
--- a/packages/SystemUI/res/values-hi/strings.xml
+++ b/packages/SystemUI/res/values-hi/strings.xml
@@ -85,7 +85,7 @@
     <string name="screenshot_failed_title" msgid="3259148215671936891">"स्क्रीनशॉट सेव नहीं किया जा सका"</string>
     <string name="screenshot_failed_to_save_user_locked_text" msgid="6156607948256936920">"स्क्रीनशॉट सेव करने के लिए डिवाइस का अनलॉक होना ज़रूरी है"</string>
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"स्क्रीनशॉट दोबारा लेने की कोशिश करें"</string>
-    <string name="screenshot_failed_to_save_text" msgid="7232739948999195960">"स्क्रीनशॉट को सेव नहीं किया जा सका"</string>
+    <string name="screenshot_failed_to_save_text" msgid="7232739948999195960">"स्क्रीनशॉट को सेव नहीं किया जा सकता"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"ऐप्लिकेशन या आपका संगठन स्क्रीनशॉट लेने की अनुमति नहीं देता"</string>
     <string name="screenshot_edit_label" msgid="8754981973544133050">"बदलाव करें"</string>
     <string name="screenshot_edit_description" msgid="3333092254706788906">"स्क्रीनशॉट में बदलाव करें"</string>
@@ -355,6 +355,8 @@
     <string name="quick_settings_user_title" msgid="8673045967216204537">"उपयोगकर्ता"</string>
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"नया उपयोगकर्ता"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"वाई-फ़ाई"</string>
+    <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+    <skip />
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"कनेक्ट नहीं है"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"कोई नेटवर्क नहीं"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"वाई-फ़ाई  बंद"</string>
@@ -456,9 +458,11 @@
     <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"प्रोफ़ाइल दिखाएं"</string>
     <string name="user_add_user" msgid="4336657383006913022">"उपयोगकर्ता जोड़ें"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"नया उपयोगकर्ता"</string>
-    <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"अतिथि को निकालें?"</string>
+    <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) -->
+    <skip />
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"इस सत्र के सभी ऐप्स और डेटा को हटा दिया जाएगा."</string>
-    <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"निकालें"</string>
+    <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) -->
+    <skip />
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"अतिथि, आपका फिर से स्वागत है!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"क्‍या आप अपना सत्र जारी रखना चाहते हैं?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"फिर से शुरू करें"</string>
diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml
index 2f13421..7d60619 100644
--- a/packages/SystemUI/res/values-hr/strings.xml
+++ b/packages/SystemUI/res/values-hr/strings.xml
@@ -354,6 +354,8 @@
     <string name="quick_settings_user_title" msgid="8673045967216204537">"Korisnik"</string>
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Novi korisnik"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
+    <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+    <skip />
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Nije povezano"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Nema mreže"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi isključen"</string>
@@ -456,9 +458,11 @@
     <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Prikaz profila"</string>
     <string name="user_add_user" msgid="4336657383006913022">"Dodavanje korisnika"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"Novi korisnik"</string>
-    <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Ukloniti gosta?"</string>
+    <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) -->
+    <skip />
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Sve aplikacije i podaci u ovoj sesiji bit će izbrisani."</string>
-    <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Ukloni"</string>
+    <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) -->
+    <skip />
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"Dobro došli natrag, gostu!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"Želite li nastaviti sesiju?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Počni ispočetka"</string>
diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml
index a7b3cc3..2ee8913 100644
--- a/packages/SystemUI/res/values-hu/strings.xml
+++ b/packages/SystemUI/res/values-hu/strings.xml
@@ -353,6 +353,8 @@
     <string name="quick_settings_user_title" msgid="8673045967216204537">"Felhasználó"</string>
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Új felhasználó"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
+    <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+    <skip />
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Nincs kapcsolat"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Nincs hálózat"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi kikapcsolva"</string>
@@ -454,9 +456,11 @@
     <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Profil megjelenítése"</string>
     <string name="user_add_user" msgid="4336657383006913022">"Felhasználó hozzáadása"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"Új felhasználó"</string>
-    <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Eltávolítja a vendég munkamenetet?"</string>
+    <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) -->
+    <skip />
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"A munkamenetben található összes alkalmazás és adat törlődni fog."</string>
-    <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Eltávolítás"</string>
+    <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) -->
+    <skip />
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"Örülünk, hogy visszatért, vendég!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"Folytatja a munkamenetet?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Újrakezdés"</string>
diff --git a/packages/SystemUI/res/values-hu/strings_tv.xml b/packages/SystemUI/res/values-hu/strings_tv.xml
index 78d6099..91183af 100644
--- a/packages/SystemUI/res/values-hu/strings_tv.xml
+++ b/packages/SystemUI/res/values-hu/strings_tv.xml
@@ -23,7 +23,7 @@
     <string name="app_accessed_mic" msgid="2754428675130470196">"A(z) %1$s hozzáfért a mikrofonhoz"</string>
     <string name="notification_vpn_connected" msgid="3891023882833274730">"A VPN-kapcsolat létrejött"</string>
     <string name="notification_vpn_disconnected" msgid="7150747626448044843">"A VPN-kapcsolat megszakadt"</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"A következő szolgáltatás használatával: <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Ezzel: <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
     <string name="tv_notification_panel_title" msgid="5311050946506276154">"Értesítések"</string>
     <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Nincs értesítés"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml
index fed73da..18af1ea 100644
--- a/packages/SystemUI/res/values-hy/strings.xml
+++ b/packages/SystemUI/res/values-hy/strings.xml
@@ -353,6 +353,8 @@
     <string name="quick_settings_user_title" msgid="8673045967216204537">"Օգտատեր"</string>
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Նոր օգտատեր"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
+    <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+    <skip />
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Միացված չէ"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Ցանց չկա"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi-ը անջատված է"</string>
@@ -454,9 +456,9 @@
     <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Ցույց տալ դիտարկումը"</string>
     <string name="user_add_user" msgid="4336657383006913022">"Ավելացնել օգտատեր"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"Նոր օգտատեր"</string>
-    <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Հեռացնե՞լ հյուրին:"</string>
+    <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Ավարտե՞լ հյուրի աշխատաշրջանը"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Այս աշխատաշրջանի բոլոր ծրագրերն ու տվյալները կջնջվեն:"</string>
-    <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Հեռացնել"</string>
+    <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Ավարտել աշխատաշրջանը"</string>
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"Բարի վերադարձ, հյուր:"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"Դուք ցանկանու՞մ եք շարունակել ձեր գործողությունը:"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Սկսել"</string>
diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml
index 7ae3e5d..4a048bb 100644
--- a/packages/SystemUI/res/values-in/strings.xml
+++ b/packages/SystemUI/res/values-in/strings.xml
@@ -353,6 +353,8 @@
     <string name="quick_settings_user_title" msgid="8673045967216204537">"Pengguna"</string>
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Pengguna baru"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
+    <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+    <skip />
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Tidak Terhubung"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Tidak Ada Jaringan"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi Mati"</string>
@@ -454,9 +456,11 @@
     <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Tampilkan profil"</string>
     <string name="user_add_user" msgid="4336657383006913022">"Tambahkan pengguna"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"Pengguna baru"</string>
-    <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Hapus tamu?"</string>
+    <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) -->
+    <skip />
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Semua aplikasi dan data di sesi ini akan dihapus."</string>
-    <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Hapus"</string>
+    <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) -->
+    <skip />
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"Selamat datang kembali, tamu!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"Lanjutkan sesi Anda?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Mulai ulang"</string>
diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml
index 0361ba4..10e11fc 100644
--- a/packages/SystemUI/res/values-is/strings.xml
+++ b/packages/SystemUI/res/values-is/strings.xml
@@ -353,6 +353,8 @@
     <string name="quick_settings_user_title" msgid="8673045967216204537">"Notandi"</string>
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Nýr notandi"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
+    <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+    <skip />
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Engin tenging"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Ekkert net"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Slökkt á Wi-Fi"</string>
@@ -454,9 +456,11 @@
     <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Sýna snið"</string>
     <string name="user_add_user" msgid="4336657383006913022">"Bæta notanda við"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"Nýr notandi"</string>
-    <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Fjarlægja gest?"</string>
+    <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) -->
+    <skip />
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Öllum forritum og gögnum í þessari lotu verður eytt."</string>
-    <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Fjarlægja"</string>
+    <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) -->
+    <skip />
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"Velkominn aftur, gestur!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"Viltu halda áfram með lotuna?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Byrja upp á nýtt"</string>
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 948faf1..24e3f47 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -353,6 +353,8 @@
     <string name="quick_settings_user_title" msgid="8673045967216204537">"Utente"</string>
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Nuovo utente"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
+    <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+    <skip />
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Non connessa"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Nessuna rete"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi disattivato"</string>
@@ -454,9 +456,11 @@
     <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Mostra profilo"</string>
     <string name="user_add_user" msgid="4336657383006913022">"Aggiungi utente"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"Nuovo utente"</string>
-    <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Rimuovere l\'ospite?"</string>
+    <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) -->
+    <skip />
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Tutte le app e i dati di questa sessione verranno eliminati."</string>
-    <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Rimuovi"</string>
+    <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) -->
+    <skip />
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"Bentornato, ospite."</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"Vuoi continuare la sessione?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Ricomincia"</string>
diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml
index d20623e..76dbdf9 100644
--- a/packages/SystemUI/res/values-iw/strings.xml
+++ b/packages/SystemUI/res/values-iw/strings.xml
@@ -35,7 +35,7 @@
     <string name="battery_low_why" msgid="2056750982959359863">"הגדרות"</string>
     <string name="battery_saver_confirmation_title" msgid="1234998463717398453">"להפעיל את תכונת החיסכון בסוללה?"</string>
     <string name="battery_saver_confirmation_title_generic" msgid="2299231884234959849">"מידע על מצב החיסכון בסוללה"</string>
-    <string name="battery_saver_confirmation_ok" msgid="5042136476802816494">"הפעל"</string>
+    <string name="battery_saver_confirmation_ok" msgid="5042136476802816494">"הפעלה"</string>
     <string name="battery_saver_start_action" msgid="4553256017945469937">"הפעלת תכונת החיסכון בסוללה"</string>
     <string name="status_bar_settings_settings_button" msgid="534331565185171556">"הגדרות"</string>
     <string name="status_bar_settings_wifi_button" msgid="7243072479837270946">"Wi-Fi"</string>
@@ -278,8 +278,8 @@
     <string name="accessibility_quick_settings_flashlight_changed_on" msgid="4747870681508334200">"הפנס הופעל."</string>
     <string name="accessibility_quick_settings_color_inversion_changed_off" msgid="7548045840282925393">"היפוך צבעים כבוי."</string>
     <string name="accessibility_quick_settings_color_inversion_changed_on" msgid="4711141858364404084">"היפוך צבעים מופעל."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"נקודה לשיתוף אינטרנט בנייד כבויה."</string>
-    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"נקודה לשיתוף אינטרנט בנייד מופעלת."</string>
+    <string name="accessibility_quick_settings_hotspot_changed_off" msgid="7002061268910095176">"‏נקודת האינטרנט (hotspot) כבויה."</string>
+    <string name="accessibility_quick_settings_hotspot_changed_on" msgid="2576895346762408840">"‏נקודת האינטרנט (hotspot) מופעלת."</string>
     <string name="accessibility_casting_turned_off" msgid="1387906158563374962">"העברת המסך הופסקה."</string>
     <string name="accessibility_quick_settings_work_mode_off" msgid="562749867895549696">"מצב עבודה כבוי."</string>
     <string name="accessibility_quick_settings_work_mode_on" msgid="2779253456042059110">"מצב עבודה מופעל."</string>
@@ -355,6 +355,8 @@
     <string name="quick_settings_user_title" msgid="8673045967216204537">"משתמש"</string>
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"משתמש חדש"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
+    <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+    <skip />
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"אין חיבור"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"אין רשת"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"‏Wi-Fi כבוי"</string>
@@ -377,7 +379,7 @@
     <string name="quick_settings_connected_battery_level" msgid="1322075669498906959">"מחובר, הסוללה ב-<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%1$s</xliff:g>"</string>
     <string name="quick_settings_connecting" msgid="2381969772953268809">"מתחבר..."</string>
     <string name="quick_settings_tethering_label" msgid="5257299852322475780">"שיתוף אינטרנט בין ניידים"</string>
-    <string name="quick_settings_hotspot_label" msgid="1199196300038363424">"נקודה לשיתוף אינטרנט"</string>
+    <string name="quick_settings_hotspot_label" msgid="1199196300038363424">"‏נקודת אינטרנט (hotspot)"</string>
     <string name="quick_settings_hotspot_secondary_label_transient" msgid="7585604088079160564">"ההפעלה מתבצעת…"</string>
     <string name="quick_settings_hotspot_secondary_label_data_saver_enabled" msgid="1280433136266439372">"חוסך הנתונים פועל"</string>
     <plurals name="quick_settings_hotspot_secondary_label_num_devices" formatted="false" msgid="3142308865165871976">
@@ -458,9 +460,11 @@
     <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"הצג פרופיל"</string>
     <string name="user_add_user" msgid="4336657383006913022">"הוספת משתמש"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"משתמש חדש"</string>
-    <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"להסיר אורח?"</string>
+    <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) -->
+    <skip />
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"כל האפליקציות והנתונים בפעילות זו באתר יימחקו."</string>
-    <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"הסר"</string>
+    <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) -->
+    <skip />
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"שמחים לראותך שוב!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"האם ברצונך להמשיך בפעילות באתר?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"ברצוני להתחיל מחדש"</string>
@@ -649,7 +653,7 @@
     <string name="alarm_template" msgid="2234991538018805736">"בשעה <xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="alarm_template_far" msgid="3561752195856839456">"ב-<xliff:g id="WHEN">%1$s</xliff:g>"</string>
     <string name="accessibility_quick_settings_detail" msgid="544463655956179791">"הגדרות מהירות, <xliff:g id="TITLE">%s</xliff:g>."</string>
-    <string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"נקודה לשיתוף אינטרנט"</string>
+    <string name="accessibility_status_bar_hotspot" msgid="2888479317489131669">"‏נקודת אינטרנט (hotspot)"</string>
     <string name="accessibility_managed_profile" msgid="4703836746209377356">"פרופיל עבודה"</string>
     <string name="tuner_warning_title" msgid="7721976098452135267">"מהנה בשביל חלק מהאנשים, אבל לא בשביל כולם"</string>
     <string name="tuner_warning" msgid="1861736288458481650">"‏System UI Tuner מספק לך דרכים נוספות להתאים אישית את ממשק המשתמש של Android. התכונות הניסיוניות האלה עשויות להשתנות, להתקלקל או להיעלם בגרסאות עתידיות. המשך בזהירות."</string>
@@ -666,7 +670,7 @@
     <string name="experimental" msgid="3549865454812314826">"ניסיוני"</string>
     <string name="enable_bluetooth_title" msgid="866883307336662596">"‏האם להפעיל את ה-Bluetooth?"</string>
     <string name="enable_bluetooth_message" msgid="6740938333772779717">"‏כדי לחבר את המקלדת לטאבלט, תחילה עליך להפעיל את ה-Bluetooth."</string>
-    <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"הפעל"</string>
+    <string name="enable_bluetooth_confirmation_ok" msgid="2866408183324184876">"הפעלה"</string>
     <string name="show_silently" msgid="5629369640872236299">"הצגת התראות בלי להשמיע צליל"</string>
     <string name="block" msgid="188483833983476566">"חסימת כל ההתראות"</string>
     <string name="do_not_silence" msgid="4982217934250511227">"לא להשתיק"</string>
diff --git a/packages/SystemUI/res/values-iw/strings_tv.xml b/packages/SystemUI/res/values-iw/strings_tv.xml
index 7483116..1258f0d 100644
--- a/packages/SystemUI/res/values-iw/strings_tv.xml
+++ b/packages/SystemUI/res/values-iw/strings_tv.xml
@@ -21,8 +21,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="mic_active" msgid="5766614241012047024">"המיקרופון פעיל"</string>
     <string name="app_accessed_mic" msgid="2754428675130470196">"‏%1$s קיבלה גישה למיקרופון שלך"</string>
-    <string name="notification_vpn_connected" msgid="3891023882833274730">"‏VPN מחובר"</string>
-    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"‏VPN מנותק"</string>
+    <string name="notification_vpn_connected" msgid="3891023882833274730">"‏ה-VPN מחובר"</string>
+    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"‏ה-VPN מנותק"</string>
     <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"באמצעות <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
     <string name="tv_notification_panel_title" msgid="5311050946506276154">"התראות"</string>
     <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"אין התראות"</string>
diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml
index 3bb841d..b4edfe5 100644
--- a/packages/SystemUI/res/values-ja/strings.xml
+++ b/packages/SystemUI/res/values-ja/strings.xml
@@ -353,6 +353,8 @@
     <string name="quick_settings_user_title" msgid="8673045967216204537">"ユーザー"</string>
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"新しいユーザー"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
+    <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+    <skip />
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"接続されていません"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"ネットワークなし"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi OFF"</string>
@@ -454,9 +456,11 @@
     <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"プロファイルを表示"</string>
     <string name="user_add_user" msgid="4336657383006913022">"ユーザーを追加"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"新しいユーザー"</string>
-    <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"ゲストを削除しますか?"</string>
+    <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) -->
+    <skip />
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"このセッションでのアプリとデータはすべて削除されます。"</string>
-    <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"削除"</string>
+    <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) -->
+    <skip />
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"おかえりなさい、ゲストさん"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"セッションを続行しますか?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"最初から開始"</string>
diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml
index 3e3405e..26bde65 100644
--- a/packages/SystemUI/res/values-ka/strings.xml
+++ b/packages/SystemUI/res/values-ka/strings.xml
@@ -353,6 +353,8 @@
     <string name="quick_settings_user_title" msgid="8673045967216204537">"მომხმარებელი"</string>
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"ახალი მომხმარებელი"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
+    <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+    <skip />
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"არ არის დაკავშირებული."</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"ქსელი არ არის"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi გამორთულია"</string>
@@ -454,9 +456,11 @@
     <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"პროფილის ჩვენება"</string>
     <string name="user_add_user" msgid="4336657383006913022">"მომხმარებლის დამატება"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"ახალი მომხმარებელი"</string>
-    <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"სტუმრის ამოშლა?"</string>
+    <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) -->
+    <skip />
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ამ სესიის ყველა აპი და მონაცემი წაიშლება."</string>
-    <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"ამოშლა"</string>
+    <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) -->
+    <skip />
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"სტუმარო, გვიხარია, რომ დაბრუნდით!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"გსურთ, თქვენი სესიის გაგრძელება?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"ხელახლა დაწყება"</string>
diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml
index 7cbb40b..9ce0a29 100644
--- a/packages/SystemUI/res/values-kk/strings.xml
+++ b/packages/SystemUI/res/values-kk/strings.xml
@@ -353,6 +353,8 @@
     <string name="quick_settings_user_title" msgid="8673045967216204537">"Пайдаланушы"</string>
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Жаңа пайдаланушы"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
+    <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+    <skip />
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Жалғанбаған"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Желі жоқ"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi өшірулі"</string>
@@ -454,9 +456,11 @@
     <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Профильді көрсету"</string>
     <string name="user_add_user" msgid="4336657383006913022">"Пайдаланушы қосу"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"Жаңа пайдаланушы"</string>
-    <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Қонақты жою керек пе?"</string>
+    <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) -->
+    <skip />
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Осы сеанстағы барлық қолданбалар мен деректер жойылады."</string>
-    <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Алып тастау"</string>
+    <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) -->
+    <skip />
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"Қош келдіңіз, қонақ"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"Сеансты жалғастыру керек пе?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Қайта бастау"</string>
diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml
index e15cc92..1e6c7fa6e 100644
--- a/packages/SystemUI/res/values-km/strings.xml
+++ b/packages/SystemUI/res/values-km/strings.xml
@@ -353,6 +353,8 @@
     <string name="quick_settings_user_title" msgid="8673045967216204537">"អ្នកប្រើ"</string>
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"អ្នកប្រើ​ថ្មី"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
+    <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+    <skip />
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"មិន​បាន​តភ្ជាប់"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"គ្មាន​បណ្ដាញ"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"វ៉ាយហ្វាយ​បានបិទ"</string>
@@ -454,9 +456,11 @@
     <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"បង្ហាញ​ប្រវត្តិរូប"</string>
     <string name="user_add_user" msgid="4336657383006913022">"បន្ថែម​អ្នកប្រើ"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"អ្នកប្រើ​ថ្មី"</string>
-    <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"លុប​ភ្ញៀវ​?"</string>
+    <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) -->
+    <skip />
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ទិន្នន័យ និង​កម្មវិធី​ទាំងអស់​ក្នុង​សម័យ​នេះ​នឹង​ត្រូវ​បាន​លុប។"</string>
-    <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"លុបចេញ"</string>
+    <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) -->
+    <skip />
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"សូម​ស្វាគមន៍​ការ​ត្រឡប់​មកវិញ, ភ្ញៀវ!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"តើ​អ្នក​ចង់​បន្ត​សម័យ​របស់​អ្នក​?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"ចាប់ផ្ដើម"</string>
diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml
index 177db59..2958b29 100644
--- a/packages/SystemUI/res/values-kn/strings.xml
+++ b/packages/SystemUI/res/values-kn/strings.xml
@@ -353,6 +353,8 @@
     <string name="quick_settings_user_title" msgid="8673045967216204537">"ಬಳಕೆದಾರ"</string>
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"ಹೊಸ ಬಳಕೆದಾರರು"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"ವೈ-ಫೈ"</string>
+    <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+    <skip />
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"ಸಂಪರ್ಕಗೊಂಡಿಲ್ಲ"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"ನೆಟ್‌ವರ್ಕ್ ಇಲ್ಲ"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"ವೈ-ಫೈ ಆಫ್"</string>
@@ -454,9 +456,11 @@
     <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"ಪ್ರೊಫೈಲ್‌ ತೋರಿಸು"</string>
     <string name="user_add_user" msgid="4336657383006913022">"ಬಳಕೆದಾರರನ್ನು ಸೇರಿಸಿ"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"ಹೊಸ ಬಳಕೆದಾರರು"</string>
-    <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"ಅತಿಥಿಯನ್ನು ತೆಗೆದುಹಾಕುವುದೇ?"</string>
+    <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) -->
+    <skip />
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ಈ ಸೆಷನ್‌ನಲ್ಲಿನ ಎಲ್ಲ ಅಪ್ಲಿಕೇಶನ್‌ಗಳು ಮತ್ತು ಡೇಟಾವನ್ನು ಅಳಿಸಲಾಗುತ್ತದೆ."</string>
-    <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"ತೆಗೆದುಹಾಕಿ"</string>
+    <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) -->
+    <skip />
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"ಮತ್ತೆ ಸುಸ್ವಾಗತ, ಅತಿಥಿ!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"ನಿಮ್ಮ ಸೆಷನ್‌ ಮುಂದುವರಿಸಲು ಇಚ್ಚಿಸುವಿರಾ?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"ಪ್ರಾರಂಭಿಸಿ"</string>
diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml
index eb81396..cd613b6 100644
--- a/packages/SystemUI/res/values-ko/strings.xml
+++ b/packages/SystemUI/res/values-ko/strings.xml
@@ -353,6 +353,8 @@
     <string name="quick_settings_user_title" msgid="8673045967216204537">"사용자"</string>
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"신규 사용자"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
+    <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+    <skip />
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"연결되어 있지 않음"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"네트워크가 연결되지 않음"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi 꺼짐"</string>
@@ -454,9 +456,11 @@
     <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"프로필 표시"</string>
     <string name="user_add_user" msgid="4336657383006913022">"사용자 추가"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"신규 사용자"</string>
-    <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"게스트를 삭제하시겠습니까?"</string>
+    <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) -->
+    <skip />
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"이 세션에 있는 모든 앱과 데이터가 삭제됩니다."</string>
-    <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"삭제"</string>
+    <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) -->
+    <skip />
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"게스트 세션 다시 시작"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"세션을 계속 진행하시겠습니까?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"다시 시작"</string>
diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml
index caa2b31..e605341 100644
--- a/packages/SystemUI/res/values-ky/strings.xml
+++ b/packages/SystemUI/res/values-ky/strings.xml
@@ -355,6 +355,8 @@
     <string name="quick_settings_user_title" msgid="8673045967216204537">"Колдонуучу"</string>
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Жаңы колдонуучу"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
+    <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+    <skip />
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Байланышкан жок"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Желе жок"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi өчүк"</string>
@@ -456,9 +458,11 @@
     <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Профилди көрсөтүү"</string>
     <string name="user_add_user" msgid="4336657383006913022">"Колдонуучу кошуу"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"Жаңы колдонуучу"</string>
-    <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Конокту алып саласызбы?"</string>
+    <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) -->
+    <skip />
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Бул сеанстагы бардык колдонмолор жана дайындар өчүрүлөт."</string>
-    <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Алып салуу"</string>
+    <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) -->
+    <skip />
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"Кайтып келишиңиз менен, конок!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"Сеансыңызды улантасызбы?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Кайра баштоо"</string>
diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml
index 5ed1d4b..b8cc3de 100644
--- a/packages/SystemUI/res/values-lo/strings.xml
+++ b/packages/SystemUI/res/values-lo/strings.xml
@@ -353,6 +353,8 @@
     <string name="quick_settings_user_title" msgid="8673045967216204537">"ຜູ້ໃຊ້"</string>
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"ຜູ່ໃຊ້ໃໝ່"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi​-Fi"</string>
+    <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+    <skip />
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"ບໍ່ໄດ້ເຊື່ອມຕໍ່"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"ບໍ່ມີເຄືອຂ່າຍ"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi​-Fi ປິດ"</string>
@@ -454,9 +456,11 @@
     <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"​ສະ​ແດງ​ໂປຣ​ໄຟລ໌"</string>
     <string name="user_add_user" msgid="4336657383006913022">"ເພີ່ມຜູ້ໃຊ້"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"ຜູ່ໃຊ້ໃໝ່"</string>
-    <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"ລຶບ​ແຂກ​ບໍ?"</string>
+    <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) -->
+    <skip />
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ແອັບຯ​ແລະ​ຂໍ້​ມູນ​ທັງ​ໝົດ​ໃນ​ເຊດ​ຊັນ​ນີ້​ຈະ​ຖືກ​ລຶບ​ອອກ."</string>
-    <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"ລຶບ​"</string>
+    <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) -->
+    <skip />
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"ຍິນ​ດີ​ຕ້ອນ​ຮັບ​ກັບ​ມາ, ຜູ່​ຢ້ຽມ​ຢາມ!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"ທ່ານ​ຕ້ອງ​ການ​ສືບ​ຕໍ່​ເຊດ​ຊັນ​ຂອງ​ທ່ານບໍ່?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"ເລີ່ມຕົ້ນໃຫມ່"</string>
diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml
index 17d517c..8a32436 100644
--- a/packages/SystemUI/res/values-lt/strings.xml
+++ b/packages/SystemUI/res/values-lt/strings.xml
@@ -355,6 +355,8 @@
     <string name="quick_settings_user_title" msgid="8673045967216204537">"Naudotojas"</string>
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Naujas naudotojas"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
+    <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+    <skip />
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Neprisijungta"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Tinklo nėra"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"„Wi-Fi“ išjungta"</string>
@@ -458,9 +460,11 @@
     <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Rodyti profilį"</string>
     <string name="user_add_user" msgid="4336657383006913022">"Pridėti naudotoją"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"Naujas naudotojas"</string>
-    <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Pašalinti svečią?"</string>
+    <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) -->
+    <skip />
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Bus ištrintos visos šios sesijos programos ir duomenys."</string>
-    <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Pašalinti"</string>
+    <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) -->
+    <skip />
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"Sveiki sugrįžę, svety!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"Ar norite tęsti sesiją?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Pradėti iš naujo"</string>
diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml
index 6fd32b59..56ce376 100644
--- a/packages/SystemUI/res/values-lv/strings.xml
+++ b/packages/SystemUI/res/values-lv/strings.xml
@@ -354,6 +354,8 @@
     <string name="quick_settings_user_title" msgid="8673045967216204537">"Lietotājs"</string>
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Jauns lietotājs"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
+    <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+    <skip />
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Nav izveidots savienojums"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Nav tīkla"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi ir izslēgts"</string>
@@ -456,9 +458,11 @@
     <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Parādīt profilu"</string>
     <string name="user_add_user" msgid="4336657383006913022">"Lietotāja pievienošana"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"Jauns lietotājs"</string>
-    <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Vai noņemt viesi?"</string>
+    <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) -->
+    <skip />
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Tiks dzēstas visas šīs sesijas lietotnes un dati."</string>
-    <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Noņemt"</string>
+    <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) -->
+    <skip />
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"Laipni lūdzam atpakaļ, viesi!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"Vai vēlaties turpināt savu sesiju?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Sākt no sākuma"</string>
diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml
index 51f9b8c..fc83bb7 100644
--- a/packages/SystemUI/res/values-mk/strings.xml
+++ b/packages/SystemUI/res/values-mk/strings.xml
@@ -353,6 +353,8 @@
     <string name="quick_settings_user_title" msgid="8673045967216204537">"Корисник"</string>
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Нов корисник"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
+    <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+    <skip />
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Не е поврзано"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Нема мрежа"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi е исклучено"</string>
@@ -454,9 +456,11 @@
     <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Прикажи го профилот"</string>
     <string name="user_add_user" msgid="4336657383006913022">"Додај корисник"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"Нов корисник"</string>
-    <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Да се отстрани гостинот?"</string>
+    <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) -->
+    <skip />
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Сите апликации и податоци во сесијата ќе се избришат."</string>
-    <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Отстрани"</string>
+    <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) -->
+    <skip />
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"Добре дојде пак, гостине!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"Дали сакате да продолжите со сесијата?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Почни одново"</string>
diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml
index 534faa7..9cd0412 100644
--- a/packages/SystemUI/res/values-ml/strings.xml
+++ b/packages/SystemUI/res/values-ml/strings.xml
@@ -353,6 +353,8 @@
     <string name="quick_settings_user_title" msgid="8673045967216204537">"ഉപയോക്താവ്"</string>
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"പുതിയ ഉപയോക്താവ്"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"വൈഫൈ"</string>
+    <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+    <skip />
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"കണ‌ക്റ്റ് ചെയ്‌തിട്ടില്ല"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"നെറ്റ്‌വർക്ക് ഒന്നുമില്ല"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"വൈഫൈ ഓഫുചെയ്യുക"</string>
@@ -454,9 +456,11 @@
     <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"പ്രൊഫൈൽ കാണിക്കുക"</string>
     <string name="user_add_user" msgid="4336657383006913022">"ഉപയോക്താവിനെ ചേര്‍ക്കുക"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"പുതിയ ഉപയോക്താവ്"</string>
-    <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"അതിഥിയെ നീക്കംചെയ്യണോ?"</string>
+    <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) -->
+    <skip />
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ഈ സെഷനിലെ എല്ലാ അപ്ലിക്കേഷനുകളും ഡാറ്റയും ഇല്ലാതാക്കും."</string>
-    <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"നീക്കംചെയ്യുക"</string>
+    <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) -->
+    <skip />
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"അതിഥിയ്‌ക്ക് വീണ്ടും സ്വാഗതം!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"നിങ്ങളുടെ സെഷൻ തുടരണോ?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"പുനരാംരംഭിക്കുക"</string>
diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml
index 190e3e5..b0159b0 100644
--- a/packages/SystemUI/res/values-mn/strings.xml
+++ b/packages/SystemUI/res/values-mn/strings.xml
@@ -353,6 +353,8 @@
     <string name="quick_settings_user_title" msgid="8673045967216204537">"Хэрэглэгч"</string>
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Шинэ хэрэглэгч"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
+    <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+    <skip />
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Холбогдоогүй"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Сүлжээгүй"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi унтарсан"</string>
@@ -454,9 +456,11 @@
     <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Профайлыг харуулах"</string>
     <string name="user_add_user" msgid="4336657383006913022">"Хэрэглэгч нэмэх"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"Шинэ хэрэглэгч"</string>
-    <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Зочныг хасах уу?"</string>
+    <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) -->
+    <skip />
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Энэ сешний бүх апп болон дата устах болно."</string>
-    <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Хасах"</string>
+    <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) -->
+    <skip />
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"Тавтай морилно уу!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"Та үргэлжлүүлэхийг хүсэж байна уу?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Дахин эхлүүлэх"</string>
diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml
index 1f2e82e..965b895 100644
--- a/packages/SystemUI/res/values-mr/strings.xml
+++ b/packages/SystemUI/res/values-mr/strings.xml
@@ -85,8 +85,7 @@
     <string name="screenshot_failed_title" msgid="3259148215671936891">"स्क्रीनशॉट सेव्ह करू शकलो नाही"</string>
     <string name="screenshot_failed_to_save_user_locked_text" msgid="6156607948256936920">"स्क्रीनशॉट सेव्ह करण्याआधी डिव्हाइस अनलॉक करणे आवश्यक आहे"</string>
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"स्क्रीनशॉट पुन्हा घेण्याचा प्रयत्न करा"</string>
-    <!-- no translation found for screenshot_failed_to_save_text (7232739948999195960) -->
-    <skip />
+    <string name="screenshot_failed_to_save_text" msgid="7232739948999195960">"स्क्रीनशॉट सेव्ह करू शकत नाही"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"अ‍ॅप किंवा आपल्या संस्थेद्वारे स्क्रीनशॉट घेण्याची अनुमती नाही"</string>
     <string name="screenshot_edit_label" msgid="8754981973544133050">"संपादित करा"</string>
     <string name="screenshot_edit_description" msgid="3333092254706788906">"स्क्रीनशॉट संपादित करा"</string>
@@ -354,6 +353,8 @@
     <string name="quick_settings_user_title" msgid="8673045967216204537">"वापरकर्ता"</string>
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"नवीन वापरकर्ता"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"वाय-फाय"</string>
+    <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+    <skip />
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"कनेक्ट केले नाही"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"नेटवर्क नाही"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"वाय-फाय बंद"</string>
@@ -455,9 +456,11 @@
     <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"प्रोफाईल दर्शवा"</string>
     <string name="user_add_user" msgid="4336657383006913022">"वापरकर्ता जोडा"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"नवीन वापरकर्ता"</string>
-    <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"अतिथी काढायचे?"</string>
+    <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) -->
+    <skip />
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"या सत्रातील सर्व अ‍ॅप्स आणि डेटा हटवला जाईल."</string>
-    <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"काढा"</string>
+    <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) -->
+    <skip />
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"अतिथी, तुमचे पुन्‍हा स्‍वागत आहे!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"तुम्ही तुमचे सत्र सुरू ठेवू इच्छिता?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"येथून सुरू करा"</string>
diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml
index 17fe32d..b9b525e 100644
--- a/packages/SystemUI/res/values-ms/strings.xml
+++ b/packages/SystemUI/res/values-ms/strings.xml
@@ -353,6 +353,8 @@
     <string name="quick_settings_user_title" msgid="8673045967216204537">"Pengguna"</string>
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Pengguna baharu"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
+    <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+    <skip />
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Tidak Disambungkan"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Tiada Rangkaian"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi Dimatikan"</string>
@@ -454,9 +456,11 @@
     <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Tunjuk profil"</string>
     <string name="user_add_user" msgid="4336657383006913022">"Tambah pengguna"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"Pengguna baharu"</string>
-    <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Alih keluar tetamu?"</string>
+    <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) -->
+    <skip />
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Semua apl dan data dalam sesi ini akan dipadam."</string>
-    <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Alih keluar"</string>
+    <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) -->
+    <skip />
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"Selamat kembali, tetamu!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"Adakah anda ingin meneruskan sesi anda?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Mulakan semula"</string>
diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml
index 610105e..9801ec6 100644
--- a/packages/SystemUI/res/values-my/strings.xml
+++ b/packages/SystemUI/res/values-my/strings.xml
@@ -353,6 +353,8 @@
     <string name="quick_settings_user_title" msgid="8673045967216204537">"အသုံးပြုသူ"</string>
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"အသုံးပြုသူ အသစ်"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
+    <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+    <skip />
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"ချိတ်ဆက်မထားပါ"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"ကွန်ရက်မရှိပါ"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"ဝိုင်ဖိုင်ပိတ်ရန်"</string>
@@ -454,9 +456,11 @@
     <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"ပရိုဖိုင်ကို ပြရန်"</string>
     <string name="user_add_user" msgid="4336657383006913022">"အသုံးပြုသူ ထည့်ရန်"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"အသုံးပြုသူ အသစ်"</string>
-    <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"ဧည့်သည်ကို ဖယ်ထုတ်လိုက်ရမလား?"</string>
+    <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) -->
+    <skip />
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ဒီချိတ်ဆက်မှု ထဲက အက်ပ်များ အားလုံး နှင့် ဒေတာကို ဖျက်ပစ်မည်။"</string>
-    <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"ဖယ်ထုတ်ပါ"</string>
+    <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) -->
+    <skip />
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"ပြန်လာတာ ကြိုဆိုပါသည်၊ ဧည့်သည်!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"သင်သည် သင်၏ ချိတ်ဆက်မှုကို ဆက်ပြုလုပ် လိုပါသလား?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"အစမှ ပြန်စပါ"</string>
diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml
index b16f1e7..6724286 100644
--- a/packages/SystemUI/res/values-nb/strings.xml
+++ b/packages/SystemUI/res/values-nb/strings.xml
@@ -353,6 +353,8 @@
     <string name="quick_settings_user_title" msgid="8673045967216204537">"Bruker"</string>
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Ny bruker"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
+    <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+    <skip />
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Ikke tilkoblet"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Ingen nettverk"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi er av"</string>
@@ -454,9 +456,11 @@
     <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Vis profil"</string>
     <string name="user_add_user" msgid="4336657383006913022">"Legg til brukere"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"Ny bruker"</string>
-    <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Vil du fjerne gjesten?"</string>
+    <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) -->
+    <skip />
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alle appene og all informasjon i denne økten slettes."</string>
-    <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Fjern"</string>
+    <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) -->
+    <skip />
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"Velkommen tilbake, gjest!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"Vil du fortsette økten?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Start på nytt"</string>
diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml
index fb4dca0..c104ffd 100644
--- a/packages/SystemUI/res/values-ne/strings.xml
+++ b/packages/SystemUI/res/values-ne/strings.xml
@@ -85,8 +85,7 @@
     <string name="screenshot_failed_title" msgid="3259148215671936891">"स्क्रिनसट सुरक्षित गर्न सकिएन"</string>
     <string name="screenshot_failed_to_save_user_locked_text" msgid="6156607948256936920">"यन्त्र अनलक गरेपछि मात्र स्क्रिनसट सुरक्षित गर्न सकिन्छ"</string>
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"स्क्रिनसट फेरि लिएर हेर्नुहोस्"</string>
-    <!-- no translation found for screenshot_failed_to_save_text (7232739948999195960) -->
-    <skip />
+    <string name="screenshot_failed_to_save_text" msgid="7232739948999195960">"स्क्रिनसट सुरक्षित गर्न सकिएन"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"उक्त एप वा तपाईंको संगठनले स्क्रिनसटहरू लिन दिँदैन"</string>
     <string name="screenshot_edit_label" msgid="8754981973544133050">"सम्पादन गर्नुहोस्"</string>
     <string name="screenshot_edit_description" msgid="3333092254706788906">"स्क्रिनसट सम्पादन गर्नुहोस्"</string>
@@ -354,6 +353,8 @@
     <string name="quick_settings_user_title" msgid="8673045967216204537">"प्रयोगकर्ता"</string>
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"नयाँ प्रयोगकर्ता"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
+    <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+    <skip />
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"जोडिएको छैन"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"नेटवर्क छैन"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi बन्द"</string>
@@ -455,9 +456,11 @@
     <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"प्रोफाइल देखाउनुहोस्"</string>
     <string name="user_add_user" msgid="4336657383006913022">"प्रयोगकर्ता थप्नुहोस्"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"नयाँ प्रयोगकर्ता"</string>
-    <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"अतिथि हटाउने हो?"</string>
+    <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) -->
+    <skip />
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"यस सत्रमा सबै एपहरू र डेटा मेटाइनेछ।"</string>
-    <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"हटाउनुहोस्"</string>
+    <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) -->
+    <skip />
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"पुनः स्वागत, अतिथि!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"तपाईं आफ्नो सत्र जारी गर्न चाहनुहुन्छ?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"सुरु गर्नुहोस्"</string>
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index a858a1d..7d87817 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -353,6 +353,8 @@
     <string name="quick_settings_user_title" msgid="8673045967216204537">"Gebruiker"</string>
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Nieuwe gebruiker"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wifi"</string>
+    <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+    <skip />
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Niet verbonden"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Geen netwerk"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wifi uit"</string>
@@ -454,9 +456,11 @@
     <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Profiel weergeven"</string>
     <string name="user_add_user" msgid="4336657383006913022">"Gebruiker toevoegen"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"Nieuwe gebruiker"</string>
-    <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Gast verwijderen?"</string>
+    <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) -->
+    <skip />
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alle apps en gegevens in deze sessie worden verwijderd."</string>
-    <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Verwijderen"</string>
+    <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) -->
+    <skip />
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"Welkom terug, gast!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"Wil je doorgaan met je sessie?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Opnieuw starten"</string>
diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml
index 4931b77..7d7b84f 100644
--- a/packages/SystemUI/res/values-or/strings.xml
+++ b/packages/SystemUI/res/values-or/strings.xml
@@ -353,6 +353,8 @@
     <string name="quick_settings_user_title" msgid="8673045967216204537">"ୟୁଜର୍‌"</string>
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"ନୂଆ ଉପଯୋଗକର୍ତ୍ତା"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"ୱାଇ-ଫାଇ"</string>
+    <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+    <skip />
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"ସଂଯୁକ୍ତ ହୋଇନାହିଁ"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"ନେଟ୍‌ୱର୍କ ନାହିଁ"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"ୱାଇ-ଫାଇ ଅଫ୍‍"</string>
@@ -454,9 +456,11 @@
     <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"ପ୍ରୋଫାଇଲ୍ ଦେଖାନ୍ତୁ"</string>
     <string name="user_add_user" msgid="4336657383006913022">"ଉପଯୋଗକର୍ତ୍ତାଙ୍କୁ ଯୋଗ କରନ୍ତୁ"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"ନୂଆ ଉପଯୋଗକର୍ତ୍ତା"</string>
-    <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"ଅତିଥିଙ୍କୁ କାଢ଼ିଦେବେ?"</string>
+    <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) -->
+    <skip />
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ଏହି ଅବଧିର ସମସ୍ତ ଆପ୍‌ ଓ ଡାଟା ଡିଲିଟ୍‌ ହୋଇଯିବ।"</string>
-    <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"କାଢ଼ିଦିଅନ୍ତୁ"</string>
+    <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) -->
+    <skip />
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"ପୁଣି ସ୍ୱାଗତ, ଅତିଥି!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"ଆପଣ ନିଜର ଅବଧି ଜାରି ରଖିବାକୁ ଚାହାନ୍ତି କି?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"ଆରମ୍ଭ କରନ୍ତୁ"</string>
diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml
index a2ff2b2..7d5bc96 100644
--- a/packages/SystemUI/res/values-pa/strings.xml
+++ b/packages/SystemUI/res/values-pa/strings.xml
@@ -85,8 +85,7 @@
     <string name="screenshot_failed_title" msgid="3259148215671936891">"ਸਕ੍ਰੀਨਸ਼ਾਟ ਰੱਖਿਅਤ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ"</string>
     <string name="screenshot_failed_to_save_user_locked_text" msgid="6156607948256936920">"ਸਕ੍ਰੀਨਸ਼ਾਟ ਨੂੰ ਰੱਖਿਅਤ ਕੀਤੇ ਜਾਣ ਤੋਂ ਪਹਿਲਾਂ ਡੀਵਾਈਸ ਨੂੰ ਅਣਲਾਕ ਕੀਤਾ ਹੋਣਾ ਲਾਜ਼ਮੀ ਹੈ"</string>
     <string name="screenshot_failed_to_save_unknown_text" msgid="1506621600548684129">"ਸਕ੍ਰੀਨਸ਼ਾਟ ਦੁਬਾਰਾ ਲੈ ਕੇ ਦੇਖੋ"</string>
-    <!-- no translation found for screenshot_failed_to_save_text (7232739948999195960) -->
-    <skip />
+    <string name="screenshot_failed_to_save_text" msgid="7232739948999195960">"ਸਕ੍ਰੀਨਸ਼ਾਟ ਨੂੰ ਰੱਖਿਅਤ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ"</string>
     <string name="screenshot_failed_to_capture_text" msgid="7818288545874407451">"ਐਪ ਜਾਂ ਤੁਹਾਡੀ ਸੰਸਥਾ ਵੱਲੋਂ ਸਕ੍ਰੀਨਸ਼ਾਟ ਲੈਣ ਦੀ ਇਜਾਜ਼ਤ ਨਹੀਂ ਦਿੱਤੀ ਗਈ ਹੈ"</string>
     <string name="screenshot_edit_label" msgid="8754981973544133050">"ਸੰਪਾਦਨ ਕਰੋ"</string>
     <string name="screenshot_edit_description" msgid="3333092254706788906">"ਸਕ੍ਰੀਨਸ਼ਾਟ ਦਾ ਸੰਪਾਦਨ ਕਰੋ"</string>
@@ -354,6 +353,8 @@
     <string name="quick_settings_user_title" msgid="8673045967216204537">"ਵਰਤੋਂਕਾਰ"</string>
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"ਨਵਾਂ ਵਰਤੋਂਕਾਰ"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"ਵਾਈ-ਫਾਈ"</string>
+    <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+    <skip />
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"ਕਨੈਕਟ ਨਹੀਂ ਕੀਤਾ"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"ਕੋਈ ਨੈੱਟਵਰਕ ਨਹੀਂ"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"ਵਾਈ-ਫਾਈ ਬੰਦ"</string>
@@ -455,9 +456,11 @@
     <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"ਪ੍ਰੋਫਾਈਲ ਦਿਖਾਓ"</string>
     <string name="user_add_user" msgid="4336657383006913022">"ਵਰਤੋਂਕਾਰ ਸ਼ਾਮਲ ਕਰੋ"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"ਨਵਾਂ ਵਰਤੋਂਕਾਰ"</string>
-    <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"ਕੀ ਮਹਿਮਾਨ ਹਟਾਉਣਾ ਹੈ?"</string>
+    <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) -->
+    <skip />
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ਇਸ ਸੈਸ਼ਨ ਵਿੱਚ ਸਾਰੀਆਂ ਐਪਾਂ ਅਤੇ ਡਾਟਾ ਨੂੰ ਮਿਟਾ ਦਿੱਤਾ ਜਾਏਗਾ।"</string>
-    <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"ਹਟਾਓ"</string>
+    <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) -->
+    <skip />
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"ਮਹਿਮਾਨ, ਫਿਰ ਤੁਹਾਡਾ ਸੁਆਗਤ ਹੈ!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"ਕੀ ਤੁਸੀਂ ਆਪਣਾ ਸੈਸ਼ਨ ਜਾਰੀ ਰੱਖਣਾ ਚਾਹੁੰਦੇ ਹੋ?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"ਸ਼ੁਰੂ ਕਰੋ"</string>
diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml
index 9955285..8a19e28 100644
--- a/packages/SystemUI/res/values-pl/strings.xml
+++ b/packages/SystemUI/res/values-pl/strings.xml
@@ -355,6 +355,8 @@
     <string name="quick_settings_user_title" msgid="8673045967216204537">"Użytkownik"</string>
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Nowy użytkownik"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
+    <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+    <skip />
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Brak połączenia"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Brak sieci"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi wyłączone"</string>
@@ -458,9 +460,11 @@
     <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Pokaż profil"</string>
     <string name="user_add_user" msgid="4336657383006913022">"Dodaj użytkownika"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"Nowy użytkownik"</string>
-    <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Usunąć gościa?"</string>
+    <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) -->
+    <skip />
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Wszystkie aplikacje i dane w tej sesji zostaną usunięte."</string>
-    <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Usuń"</string>
+    <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) -->
+    <skip />
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"Witaj ponownie, gościu!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"Chcesz kontynuować sesję?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Rozpocznij nową"</string>
diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml
index 9b7220f..57b924d 100644
--- a/packages/SystemUI/res/values-pt-rBR/strings.xml
+++ b/packages/SystemUI/res/values-pt-rBR/strings.xml
@@ -353,6 +353,8 @@
     <string name="quick_settings_user_title" msgid="8673045967216204537">"Usuário"</string>
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Novo usuário"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
+    <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+    <skip />
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Não conectado"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Sem rede"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi desligado"</string>
@@ -454,9 +456,11 @@
     <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Mostrar perfil"</string>
     <string name="user_add_user" msgid="4336657383006913022">"Adicionar usuário"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"Novo usuário"</string>
-    <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Remover convidado?"</string>
+    <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) -->
+    <skip />
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Todos os apps e dados nesta sessão serão excluídos."</string>
-    <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Remover"</string>
+    <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) -->
+    <skip />
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"Bem-vindo, convidado."</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"Quer continuar a sessão?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Recomeçar"</string>
@@ -836,7 +840,7 @@
     <item msgid="7453955063378349599">"Inclinação à esquerda"</item>
     <item msgid="5874146774389433072">"Inclinação à direita"</item>
   </string-array>
-    <string name="menu_ime" msgid="5677467548258017952">"Alternador de teclado"</string>
+    <string name="menu_ime" msgid="5677467548258017952">"Seletor de teclado"</string>
     <string name="save" msgid="3392754183673848006">"Salvar"</string>
     <string name="reset" msgid="8715144064608810383">"Redefinir"</string>
     <string name="adjust_button_width" msgid="8313444823666482197">"Ajustar largura do botão"</string>
diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml
index 9b52677..e39f757c 100644
--- a/packages/SystemUI/res/values-pt-rPT/strings.xml
+++ b/packages/SystemUI/res/values-pt-rPT/strings.xml
@@ -353,6 +353,8 @@
     <string name="quick_settings_user_title" msgid="8673045967216204537">"Utilizador"</string>
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Novo utilizador"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
+    <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+    <skip />
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Não Ligado"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Sem Rede"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi Desligado"</string>
@@ -454,9 +456,11 @@
     <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Mostrar perfil"</string>
     <string name="user_add_user" msgid="4336657383006913022">"Adicionar utilizador"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"Novo utilizador"</string>
-    <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Remover o convidado?"</string>
+    <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) -->
+    <skip />
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Todas as aplicações e dados desta sessão serão eliminados."</string>
-    <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Remover"</string>
+    <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) -->
+    <skip />
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"Bem-vindo de volta, caro(a) convidado(a)!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"Pretende continuar a sessão?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Recomeçar"</string>
diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml
index 9b7220f..57b924d 100644
--- a/packages/SystemUI/res/values-pt/strings.xml
+++ b/packages/SystemUI/res/values-pt/strings.xml
@@ -353,6 +353,8 @@
     <string name="quick_settings_user_title" msgid="8673045967216204537">"Usuário"</string>
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Novo usuário"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
+    <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+    <skip />
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Não conectado"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Sem rede"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi desligado"</string>
@@ -454,9 +456,11 @@
     <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Mostrar perfil"</string>
     <string name="user_add_user" msgid="4336657383006913022">"Adicionar usuário"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"Novo usuário"</string>
-    <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Remover convidado?"</string>
+    <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) -->
+    <skip />
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Todos os apps e dados nesta sessão serão excluídos."</string>
-    <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Remover"</string>
+    <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) -->
+    <skip />
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"Bem-vindo, convidado."</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"Quer continuar a sessão?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Recomeçar"</string>
@@ -836,7 +840,7 @@
     <item msgid="7453955063378349599">"Inclinação à esquerda"</item>
     <item msgid="5874146774389433072">"Inclinação à direita"</item>
   </string-array>
-    <string name="menu_ime" msgid="5677467548258017952">"Alternador de teclado"</string>
+    <string name="menu_ime" msgid="5677467548258017952">"Seletor de teclado"</string>
     <string name="save" msgid="3392754183673848006">"Salvar"</string>
     <string name="reset" msgid="8715144064608810383">"Redefinir"</string>
     <string name="adjust_button_width" msgid="8313444823666482197">"Ajustar largura do botão"</string>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index d0d0103..acbe801 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -354,6 +354,8 @@
     <string name="quick_settings_user_title" msgid="8673045967216204537">"Utilizator"</string>
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Utilizator nou"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
+    <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+    <skip />
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Neconectată"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Nicio rețea"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi deconectat"</string>
@@ -456,9 +458,11 @@
     <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Afișați profilul"</string>
     <string name="user_add_user" msgid="4336657383006913022">"Adăugați un utilizator"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"Utilizator nou"</string>
-    <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Ștergeți invitatul?"</string>
+    <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) -->
+    <skip />
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Toate aplicațiile și datele din această sesiune vor fi șterse."</string>
-    <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Ștergeți"</string>
+    <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) -->
+    <skip />
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"Bine ați revenit în sesiunea pentru invitați!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"Vreți să continuați sesiunea?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Începeți din nou"</string>
diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml
index 1b1aa29..c59bd2ae 100644
--- a/packages/SystemUI/res/values-ru/strings.xml
+++ b/packages/SystemUI/res/values-ru/strings.xml
@@ -355,6 +355,8 @@
     <string name="quick_settings_user_title" msgid="8673045967216204537">"Пользователь"</string>
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Новый пользователь"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
+    <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+    <skip />
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Нет соединения"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Нет сети"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi выкл."</string>
@@ -458,9 +460,11 @@
     <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Показать профиль."</string>
     <string name="user_add_user" msgid="4336657383006913022">"Добавить пользователя"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"Новый пользователь"</string>
-    <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Удалить аккаунт гостя?"</string>
+    <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) -->
+    <skip />
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Все приложения и данные этого профиля будут удалены."</string>
-    <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Удалить"</string>
+    <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) -->
+    <skip />
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"Рады видеть вас снова!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"Продолжить сеанс?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Начать заново"</string>
diff --git a/packages/SystemUI/res/values-ru/strings_tv.xml b/packages/SystemUI/res/values-ru/strings_tv.xml
index e08ade3..8ce0dc2 100644
--- a/packages/SystemUI/res/values-ru/strings_tv.xml
+++ b/packages/SystemUI/res/values-ru/strings_tv.xml
@@ -23,7 +23,7 @@
     <string name="app_accessed_mic" msgid="2754428675130470196">"Приложение \"%1$s\" использовало доступ к микрофону."</string>
     <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN-подключение установлено"</string>
     <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN-подключение отключено"</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Отправлено через <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Через приложение <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
     <string name="tv_notification_panel_title" msgid="5311050946506276154">"Уведомления"</string>
     <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Уведомлений нет."</string>
 </resources>
diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml
index 9981614..a49ca86 100644
--- a/packages/SystemUI/res/values-si/strings.xml
+++ b/packages/SystemUI/res/values-si/strings.xml
@@ -353,6 +353,8 @@
     <string name="quick_settings_user_title" msgid="8673045967216204537">"පරිශීලක"</string>
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"නව පරිශීලකයා"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
+    <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+    <skip />
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"සම්බන්ධ වී නොමැත"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"ජාලයක් නැත"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi අක්‍රියයි"</string>
@@ -454,9 +456,11 @@
     <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"පැතිකඩ පෙන්වන්න"</string>
     <string name="user_add_user" msgid="4336657383006913022">"පරිශීලකයෙක් එක් කරන්න"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"නව පරිශීලකයා"</string>
-    <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"අමුත්තාන් ඉවත් කරන්නද?"</string>
+    <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) -->
+    <skip />
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"මෙම සැසියේ සියළුම යෙදුම් සහ දත්ත මකාවී."</string>
-    <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"ඉවත් කරන්න"</string>
+    <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) -->
+    <skip />
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"නැවත සාදරයෙන් පිළිගනිමු, අමුත්තා!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"ඔබගේ සැසිය දිගටම කරගෙන යෑමට ඔබට අවශ්‍යද?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"යළි මුල සිට අරඹන්න"</string>
diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml
index 470b021..fe63b70 100644
--- a/packages/SystemUI/res/values-sk/strings.xml
+++ b/packages/SystemUI/res/values-sk/strings.xml
@@ -355,6 +355,8 @@
     <string name="quick_settings_user_title" msgid="8673045967216204537">"Používateľ"</string>
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Nový používateľ"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi‑Fi"</string>
+    <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+    <skip />
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Nepripojené"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Žiadna sieť"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Sieť Wi‑Fi je vypnutá"</string>
@@ -458,9 +460,11 @@
     <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Zobraziť profil"</string>
     <string name="user_add_user" msgid="4336657383006913022">"Pridať používateľa"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"Nový používateľ"</string>
-    <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Odstrániť hosťa?"</string>
+    <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) -->
+    <skip />
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Všetky aplikácie a údaje v tejto relácii budú odstránené."</string>
-    <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Odstrániť"</string>
+    <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) -->
+    <skip />
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"Hosť, vitajte späť!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"Chcete v relácii pokračovať?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Začať odznova"</string>
diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml
index 08fdb58..85956d6 100644
--- a/packages/SystemUI/res/values-sl/strings.xml
+++ b/packages/SystemUI/res/values-sl/strings.xml
@@ -355,6 +355,8 @@
     <string name="quick_settings_user_title" msgid="8673045967216204537">"Uporabnik"</string>
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Nov uporabnik"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
+    <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+    <skip />
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Povezava ni vzpostavljena"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Ni omrežja"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi izklopljen"</string>
@@ -458,9 +460,11 @@
     <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Prikaz profila"</string>
     <string name="user_add_user" msgid="4336657383006913022">"Dodajanje uporabnika"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"Nov uporabnik"</string>
-    <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Želite odstraniti gosta?"</string>
+    <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) -->
+    <skip />
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Vse aplikacije in podatki v tej seji bodo izbrisani."</string>
-    <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Odstrani"</string>
+    <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) -->
+    <skip />
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"Znova pozdravljeni, gost!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"Želite nadaljevati sejo?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Začni znova"</string>
diff --git a/packages/SystemUI/res/values-sl/strings_tv.xml b/packages/SystemUI/res/values-sl/strings_tv.xml
index 57d70c0..1f66138 100644
--- a/packages/SystemUI/res/values-sl/strings_tv.xml
+++ b/packages/SystemUI/res/values-sl/strings_tv.xml
@@ -21,8 +21,8 @@
     xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="mic_active" msgid="5766614241012047024">"Mikrofon je aktiven"</string>
     <string name="app_accessed_mic" msgid="2754428675130470196">"Aplikacija %1$s je dostopala do mikrofona"</string>
-    <string name="notification_vpn_connected" msgid="3891023882833274730">"Povezava z navideznim zasebnim omrežjem je vzpostavljena"</string>
-    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"Povezava z navideznim zasebnim omrežjem je prekinjena"</string>
+    <string name="notification_vpn_connected" msgid="3891023882833274730">"Povezava z omrežjem VPN je vzpostavljena"</string>
+    <string name="notification_vpn_disconnected" msgid="7150747626448044843">"Povezava z omrežjem VPN je prekinjena"</string>
     <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Prek storitve <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
     <string name="tv_notification_panel_title" msgid="5311050946506276154">"Obvestila"</string>
     <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Ni obvestil"</string>
diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml
index aaee225..733db0e 100644
--- a/packages/SystemUI/res/values-sq/strings.xml
+++ b/packages/SystemUI/res/values-sq/strings.xml
@@ -57,15 +57,15 @@
     <string name="label_view" msgid="6815442985276363364">"Pamje"</string>
     <string name="always_use_device" msgid="210535878779644679">"Hap gjithmonë <xliff:g id="APPLICATION">%1$s</xliff:g> kur lidhet <xliff:g id="USB_DEVICE">%2$s</xliff:g>"</string>
     <string name="always_use_accessory" msgid="1977225429341838444">"Hap gjithmonë <xliff:g id="APPLICATION">%1$s</xliff:g> kur lidhet <xliff:g id="USB_ACCESSORY">%2$s</xliff:g>"</string>
-    <string name="usb_debugging_title" msgid="8274884945238642726">"Të lejohet korrigjimi i USB-së?"</string>
+    <string name="usb_debugging_title" msgid="8274884945238642726">"Të lejohet korrigjimi përmes USB-së?"</string>
     <string name="usb_debugging_message" msgid="5794616114463921773">"Gjurma e gishtit të tastit \"RSA\" së kompjuterit është:\n<xliff:g id="FINGERPRINT">%1$s</xliff:g>"</string>
     <string name="usb_debugging_always" msgid="4003121804294739548">"Lejo gjithmonë nga ky kompjuter"</string>
     <string name="usb_debugging_allow" msgid="1722643858015321328">"Lejo"</string>
-    <string name="usb_debugging_secondary_user_title" msgid="7843050591380107998">"Korrigjimi i USB-së nuk lejohet"</string>
-    <string name="usb_debugging_secondary_user_message" msgid="3740347841470403244">"Përdoruesi i identifikuar aktualisht në këtë pajisje nuk mund ta aktivizojë korrigjimin e USB-së. Për ta përdorur këtë funksion, kalo te përdoruesi parësor."</string>
+    <string name="usb_debugging_secondary_user_title" msgid="7843050591380107998">"Korrigjimi përmes USB-së nuk lejohet"</string>
+    <string name="usb_debugging_secondary_user_message" msgid="3740347841470403244">"Përdoruesi i identifikuar aktualisht në këtë pajisje nuk mund ta aktivizojë korrigjimin përmes USB-së. Për ta përdorur këtë veçori, kalo te përdoruesi parësor."</string>
     <string name="wifi_debugging_title" msgid="7300007687492186076">"Do ta lejosh korrigjimin përmes Wi-Fi në këtë rrjet?"</string>
     <string name="wifi_debugging_message" msgid="5461204211731802995">"Emri i rrjetit (SSID)\n<xliff:g id="SSID_0">%1$s</xliff:g>\n\nAdresa Wi‑Fi (BSSID)\n<xliff:g id="BSSID_1">%2$s</xliff:g>"</string>
-    <string name="wifi_debugging_always" msgid="2968383799517975155">"Shfaq gjithmonë në këtë rrjet"</string>
+    <string name="wifi_debugging_always" msgid="2968383799517975155">"Lejo gjithmonë në këtë rrjet"</string>
     <string name="wifi_debugging_allow" msgid="4573224609684957886">"Lejo"</string>
     <string name="wifi_debugging_secondary_user_title" msgid="2493201475880517725">"Korrigjimi përmes Wi-Fi nuk lejohet"</string>
     <string name="wifi_debugging_secondary_user_message" msgid="4492383073970079751">"Përdoruesi i identifikuar aktualisht në këtë pajisje nuk mund ta aktivizojë korrigjimin përmes Wi-Fi. Për ta përdorur këtë veçori, kalo te përdoruesi parësor."</string>
@@ -353,6 +353,8 @@
     <string name="quick_settings_user_title" msgid="8673045967216204537">"Përdoruesi"</string>
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Përdorues i ri"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
+    <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+    <skip />
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Nuk është i lidhur"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Nuk ka rrjet"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi është i çaktivizuar"</string>
@@ -454,9 +456,9 @@
     <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Shfaq profilin"</string>
     <string name="user_add_user" msgid="4336657383006913022">"Shto përdorues"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"Përdorues i ri"</string>
-    <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Të hiqet i ftuari?"</string>
+    <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Dëshiron t\'i japësh fund sesionit të vizitorit?"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Të gjitha aplikacionet dhe të dhënat në këtë sesion do të fshihen."</string>
-    <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Hiq"</string>
+    <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Jepi fund sesionit"</string>
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"Mirë se erdhe, i ftuar!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"Dëshiron ta vazhdosh sesionin tënd?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Fillo nga e para"</string>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index d7645be..c5414a4 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -354,6 +354,8 @@
     <string name="quick_settings_user_title" msgid="8673045967216204537">"Корисник"</string>
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Нови корисник"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"WiFi"</string>
+    <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+    <skip />
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Веза није успостављена"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Нема мреже"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"WiFi је искључен"</string>
@@ -456,9 +458,11 @@
     <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Прикажи профил"</string>
     <string name="user_add_user" msgid="4336657383006913022">"Додај корисника"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"Нови корисник"</string>
-    <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Желите ли да уклоните госта?"</string>
+    <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) -->
+    <skip />
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Све апликације и подаци у овој сесији ће бити избрисани."</string>
-    <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Уклони"</string>
+    <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) -->
+    <skip />
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"Добро дошли назад, госте!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"Желите ли да наставите сесију?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Почни из почетка"</string>
diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml
index 8e5e79e..d2c993a0 100644
--- a/packages/SystemUI/res/values-sv/strings.xml
+++ b/packages/SystemUI/res/values-sv/strings.xml
@@ -353,6 +353,8 @@
     <string name="quick_settings_user_title" msgid="8673045967216204537">"Användare"</string>
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Ny användare"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
+    <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+    <skip />
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Ej ansluten"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Inget nätverk"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi av"</string>
@@ -454,9 +456,11 @@
     <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Visa profil"</string>
     <string name="user_add_user" msgid="4336657383006913022">"Lägg till användare"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"Ny användare"</string>
-    <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Vill du ta bort gästen?"</string>
+    <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) -->
+    <skip />
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Alla appar och data i denna session kommer att raderas."</string>
-    <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Ta bort"</string>
+    <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) -->
+    <skip />
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"Välkommen tillbaka gäst!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"Vill du fortsätta sessionen?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Börja om"</string>
diff --git a/packages/SystemUI/res/values-sv/strings_tv.xml b/packages/SystemUI/res/values-sv/strings_tv.xml
index d7261e6b..fd8fa4b 100644
--- a/packages/SystemUI/res/values-sv/strings_tv.xml
+++ b/packages/SystemUI/res/values-sv/strings_tv.xml
@@ -23,7 +23,7 @@
     <string name="app_accessed_mic" msgid="2754428675130470196">"%1$s har fått åtkomst till mikrofonen"</string>
     <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN är anslutet"</string>
     <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN är frånkopplat"</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"Via <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
+    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"via <xliff:g id="VPN_APP">%1$s</xliff:g>"</string>
     <string name="tv_notification_panel_title" msgid="5311050946506276154">"Aviseringar"</string>
     <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Inga aviseringar"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml
index 768fd85..29045f3 100644
--- a/packages/SystemUI/res/values-sw/strings.xml
+++ b/packages/SystemUI/res/values-sw/strings.xml
@@ -353,6 +353,8 @@
     <string name="quick_settings_user_title" msgid="8673045967216204537">"Mtumiaji"</string>
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Mtumiaji mpya"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
+    <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+    <skip />
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Haijaunganishwa"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Hakuna Mtandao"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi Imezimwa"</string>
@@ -454,9 +456,11 @@
     <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Onyesha wasifu"</string>
     <string name="user_add_user" msgid="4336657383006913022">"Ongeza mtumiaji"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"Mtumiaji mpya"</string>
-    <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Ungependa kumwondoa mgeni?"</string>
+    <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) -->
+    <skip />
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Data na programu zote katika kipindi hiki zitafutwa."</string>
-    <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Ondoa"</string>
+    <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) -->
+    <skip />
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"Karibu tena, mwalikwa!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"Je, unataka kuendelea na kipindi chako?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Anza tena"</string>
diff --git a/packages/SystemUI/res/values-sw600dp-land/config.xml b/packages/SystemUI/res/values-sw600dp-land/config.xml
index 3b00ad1..6dff2e3 100644
--- a/packages/SystemUI/res/values-sw600dp-land/config.xml
+++ b/packages/SystemUI/res/values-sw600dp-land/config.xml
@@ -16,4 +16,7 @@
   -->
 <resources>
     <integer name="quick_settings_num_columns">3</integer>
+
+    <!-- Max number of columns for quick controls area -->
+    <integer name="controls_max_columns">2</integer>
 </resources>
diff --git a/packages/SystemUI/res/values-sw600dp/config.xml b/packages/SystemUI/res/values-sw600dp/config.xml
index d886f00..2f5e8ea 100644
--- a/packages/SystemUI/res/values-sw600dp/config.xml
+++ b/packages/SystemUI/res/values-sw600dp/config.xml
@@ -35,4 +35,7 @@
     <!-- Whether wallet view is shown in landscape / seascape orientations -->
     <bool name="global_actions_show_landscape_wallet_view">true</bool>
 
+    <!-- Max number of columns for quick controls area -->
+    <integer name="controls_max_columns">4</integer>
+
 </resources>
diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml
index efdace4..2ca6720 100644
--- a/packages/SystemUI/res/values-ta/strings.xml
+++ b/packages/SystemUI/res/values-ta/strings.xml
@@ -353,6 +353,8 @@
     <string name="quick_settings_user_title" msgid="8673045967216204537">"பயனர்"</string>
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"புதியவர்"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"வைஃபை"</string>
+    <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+    <skip />
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"இணைக்கப்படவில்லை"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"நெட்வொர்க் இல்லை"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"வைஃபையை முடக்கு"</string>
@@ -454,9 +456,11 @@
     <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"சுயவிவரத்தைக் காட்டு"</string>
     <string name="user_add_user" msgid="4336657383006913022">"பயனரைச் சேர்"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"புதியவர்"</string>
-    <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"கெஸ்ட்டை அகற்றவா?"</string>
+    <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) -->
+    <skip />
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"இந்த அமர்வின் எல்லா பயன்பாடுகளும், தரவும் நீக்கப்படும்."</string>
-    <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"அகற்று"</string>
+    <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) -->
+    <skip />
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"நல்வரவு!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"உங்கள் அமர்வைத் தொடர விருப்பமா?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"மீண்டும் தொடங்கு"</string>
diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml
index 36a5de7..9ce5fc7 100644
--- a/packages/SystemUI/res/values-te/strings.xml
+++ b/packages/SystemUI/res/values-te/strings.xml
@@ -353,6 +353,8 @@
     <string name="quick_settings_user_title" msgid="8673045967216204537">"వినియోగదారు"</string>
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"కొత్త వినియోగదారు"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
+    <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+    <skip />
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"కనెక్ట్ చేయబడలేదు"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"నెట్‌వర్క్ లేదు"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi ఆఫ్‌లో ఉంది"</string>
@@ -454,9 +456,11 @@
     <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"ప్రొఫైల్‌ని చూపు"</string>
     <string name="user_add_user" msgid="4336657383006913022">"వినియోగదారుని జోడించండి"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"కొత్త వినియోగదారు"</string>
-    <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"అతిథిని తీసివేయాలా?"</string>
+    <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) -->
+    <skip />
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ఈ సెషన్‌లోని అన్ని అనువర్తనాలు మరియు డేటా తొలగించబడతాయి."</string>
-    <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"తీసివేయి"</string>
+    <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) -->
+    <skip />
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"పునఃస్వాగతం, అతిథి!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"మీరు మీ సెషన్‌ని కొనసాగించాలనుకుంటున్నారా?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"మొదటి నుండి ప్రారంభించు"</string>
diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml
index 5dd4400..900012e 100644
--- a/packages/SystemUI/res/values-th/strings.xml
+++ b/packages/SystemUI/res/values-th/strings.xml
@@ -93,7 +93,7 @@
     <string name="screenshot_scroll_description" msgid="7855773867093272175">"เลื่อนจับภาพหน้าจอ"</string>
     <string name="screenshot_dismiss_description" msgid="4702341245899508786">"ปิดภาพหน้าจอ"</string>
     <string name="screenshot_preview_description" msgid="7606510140714080474">"ตัวอย่างภาพหน้าจอ"</string>
-    <string name="screenrecord_name" msgid="2596401223859996572">"โปรแกรมอัดหน้าจอ"</string>
+    <string name="screenrecord_name" msgid="2596401223859996572">"โปรแกรมบันทึกหน้าจอ"</string>
     <string name="screenrecord_background_processing_label" msgid="7244617554884238898">"กำลังประมวลผลการอัดหน้าจอ"</string>
     <string name="screenrecord_channel_description" msgid="4147077128486138351">"การแจ้งเตือนต่อเนื่องสำหรับเซสชันการบันทึกหน้าจอ"</string>
     <string name="screenrecord_start_label" msgid="1750350278888217473">"เริ่มบันทึกเลยไหม"</string>
@@ -353,6 +353,8 @@
     <string name="quick_settings_user_title" msgid="8673045967216204537">"ผู้ใช้"</string>
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"ผู้ใช้ใหม่"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
+    <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+    <skip />
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"ไม่ได้เชื่อมต่อ"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"ไม่มีเครือข่าย"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"ปิด WiFi"</string>
@@ -454,9 +456,11 @@
     <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"แสดงโปรไฟล์"</string>
     <string name="user_add_user" msgid="4336657383006913022">"เพิ่มผู้ใช้"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"ผู้ใช้ใหม่"</string>
-    <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"ต้องการนำผู้เข้าร่วมออกไหม"</string>
+    <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) -->
+    <skip />
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"ระบบจะลบแอปและข้อมูลทั้งหมดในเซสชันนี้"</string>
-    <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"นำออก"</string>
+    <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) -->
+    <skip />
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"ยินดีต้อนรับท่านผู้เยี่ยมชมกลับมาอีกครั้ง!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"คุณต้องการอยู่ในเซสชันต่อไปไหม"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"เริ่มต้นใหม่"</string>
@@ -744,7 +748,7 @@
     <string name="notification_conversation_home_screen" msgid="8347136037958438935">"เพิ่มลงในหน้าจอหลัก"</string>
     <string name="notification_menu_accessibility" msgid="8984166825879886773">"<xliff:g id="APP_NAME">%1$s</xliff:g> <xliff:g id="MENU_DESCRIPTION">%2$s</xliff:g>"</string>
     <string name="notification_menu_gear_description" msgid="6429668976593634862">"ส่วนควบคุมการแจ้งเตือน"</string>
-    <string name="notification_menu_snooze_description" msgid="4740133348901973244">"ตัวเลือกการปิดเสียงแจ้งเตือนชั่วคราว"</string>
+    <string name="notification_menu_snooze_description" msgid="4740133348901973244">"ตัวเลือกการเลื่อนการแจ้งเตือน"</string>
     <string name="notification_menu_snooze_action" msgid="5415729610393475019">"เตือนฉัน"</string>
     <string name="notification_menu_settings_action" msgid="7085494017202764285">"การตั้งค่า"</string>
     <string name="snooze_undo" msgid="60890935148417175">"เลิกทำ"</string>
diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml
index 0ade0ed..f531de5 100644
--- a/packages/SystemUI/res/values-tl/strings.xml
+++ b/packages/SystemUI/res/values-tl/strings.xml
@@ -353,6 +353,8 @@
     <string name="quick_settings_user_title" msgid="8673045967216204537">"User"</string>
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Bagong user"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
+    <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+    <skip />
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Hindi Nakakonekta"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Walang Network"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Naka-off ang Wi-Fi"</string>
@@ -454,9 +456,11 @@
     <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Ipakita ang profile"</string>
     <string name="user_add_user" msgid="4336657383006913022">"Magdagdag ng user"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"Bagong user"</string>
-    <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Alisin ang bisita?"</string>
+    <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) -->
+    <skip />
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Ide-delete ang lahat ng app at data sa session na ito."</string>
-    <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Alisin"</string>
+    <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) -->
+    <skip />
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"Maligayang pagbabalik, bisita!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"Gusto mo bang ipagpatuloy ang iyong session?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Magsimulang muli"</string>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index 2608931..ba70531 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -353,6 +353,8 @@
     <string name="quick_settings_user_title" msgid="8673045967216204537">"Kullanıcı"</string>
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Yeni kullanıcı"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Kablosuz"</string>
+    <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+    <skip />
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Bağlı Değil"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Ağ yok"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Kablosuz Kapalı"</string>
@@ -454,9 +456,11 @@
     <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Profili göster"</string>
     <string name="user_add_user" msgid="4336657383006913022">"Kullanıcı ekle"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"Yeni kullanıcı"</string>
-    <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Misafir oturumu kaldırılsın mı?"</string>
+    <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) -->
+    <skip />
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Bu oturumdaki tüm uygulamalar ve veriler silinecek."</string>
-    <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Kaldır"</string>
+    <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) -->
+    <skip />
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"Tekrar hoş geldiniz sayın misafir!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"Oturumunuza devam etmek istiyor musunuz?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Baştan başla"</string>
diff --git a/packages/SystemUI/res/values-tr/strings_tv.xml b/packages/SystemUI/res/values-tr/strings_tv.xml
index babd460..49e76af 100644
--- a/packages/SystemUI/res/values-tr/strings_tv.xml
+++ b/packages/SystemUI/res/values-tr/strings_tv.xml
@@ -23,7 +23,7 @@
     <string name="app_accessed_mic" msgid="2754428675130470196">"%1$s mikrofonunuza erişti"</string>
     <string name="notification_vpn_connected" msgid="3891023882833274730">"VPN bağlandı"</string>
     <string name="notification_vpn_disconnected" msgid="7150747626448044843">"VPN bağlantısı kesildi"</string>
-    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"<xliff:g id="VPN_APP">%1$s</xliff:g> yoluyla"</string>
+    <string name="notification_disclosure_vpn_text" msgid="3873532735584866236">"<xliff:g id="VPN_APP">%1$s</xliff:g> üzerinden"</string>
     <string name="tv_notification_panel_title" msgid="5311050946506276154">"Bildirimler"</string>
     <string name="tv_notification_panel_no_notifications" msgid="9115191912267270678">"Bildirim Yok"</string>
 </resources>
diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml
index ce00b92..b2ae569 100644
--- a/packages/SystemUI/res/values-uk/strings.xml
+++ b/packages/SystemUI/res/values-uk/strings.xml
@@ -355,6 +355,8 @@
     <string name="quick_settings_user_title" msgid="8673045967216204537">"Користувач"</string>
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Новий користувач"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
+    <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+    <skip />
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Не під’єднано."</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Немає мережі"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi вимкнено"</string>
@@ -458,9 +460,11 @@
     <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Показати профіль"</string>
     <string name="user_add_user" msgid="4336657383006913022">"Додати користувача"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"Новий користувач"</string>
-    <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Вийти з режиму гостя?"</string>
+    <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) -->
+    <skip />
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Усі додатки й дані з цього сеансу буде видалено."</string>
-    <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Вийти"</string>
+    <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) -->
+    <skip />
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"З поверненням!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"Продовжити сеанс?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Почати знову"</string>
diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml
index 0032c6b..06a448b 100644
--- a/packages/SystemUI/res/values-ur/strings.xml
+++ b/packages/SystemUI/res/values-ur/strings.xml
@@ -353,6 +353,8 @@
     <string name="quick_settings_user_title" msgid="8673045967216204537">"صارف"</string>
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"نیا صارف"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
+    <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+    <skip />
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"مربوط نہیں ہے"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"کوئی نیٹ ورک نہیں ہے"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"‏Wi-Fi آف ہے"</string>
@@ -454,9 +456,11 @@
     <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"پروفائل دکھائیں"</string>
     <string name="user_add_user" msgid="4336657383006913022">"صارف کو شامل کریں"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"نیا صارف"</string>
-    <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"مہمان کو ہٹائیں؟"</string>
+    <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) -->
+    <skip />
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"اس سیشن میں موجود سبھی ایپس اور ڈیٹا کو حذف کر دیا جائے گا۔"</string>
-    <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"ہٹائیں"</string>
+    <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) -->
+    <skip />
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"مہمان، پھر سے خوش آمدید!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"کیا آپ اپنا سیشن جاری رکھنا چاہتے ہیں؟"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"دوبارہ شروع کریں"</string>
diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml
index 532fa40..e1add65 100644
--- a/packages/SystemUI/res/values-uz/strings.xml
+++ b/packages/SystemUI/res/values-uz/strings.xml
@@ -353,6 +353,8 @@
     <string name="quick_settings_user_title" msgid="8673045967216204537">"Foydalanuvchi"</string>
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Yangi foydalanuvchi"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
+    <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+    <skip />
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Ulanmagan"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Tarmoq mavjud emas"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi o‘chiq"</string>
@@ -454,9 +456,9 @@
     <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Profilni ko‘rsatish"</string>
     <string name="user_add_user" msgid="4336657383006913022">"Foydalanuvchi"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"Yangi foydalanuvchi"</string>
-    <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Mehmon hisobi o‘chirib tashlansinmi?"</string>
+    <string name="guest_exit_guest_dialog_title" msgid="2034481024623462357">"Mehmon seansi yakunlansinmi?"</string>
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Ushbu seansdagi barcha ilovalar va ma’lumotlar o‘chirib tashlanadi."</string>
-    <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Olib tashlash"</string>
+    <string name="guest_exit_guest_dialog_remove" msgid="8533184512885775423">"Seansni yakunlash"</string>
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"Xush kelibsiz, mehmon!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"Seansni davom ettirmoqchimisiz?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Boshidan boshlansin"</string>
@@ -493,7 +495,7 @@
     <string name="notification_section_header_alerting" msgid="5581175033680477651">"Bildirishnomalar"</string>
     <string name="notification_section_header_conversations" msgid="821834744538345661">"Suhbatlar"</string>
     <string name="accessibility_notification_section_header_gentle_clear_all" msgid="6490207897764933919">"Barcha sokin bildirishnomalarni tozalash"</string>
-    <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Bezovta qilinmasin rejimida bildirishnomalar pauza qilingan"</string>
+    <string name="dnd_suppressing_shade_text" msgid="5588252250634464042">"Bezovta qilinmasin rejimida bildirishnomalar pauza qilinadi"</string>
     <string name="media_projection_action_text" msgid="3634906766918186440">"Boshlash"</string>
     <string name="empty_shade_text" msgid="8935967157319717412">"Bildirishnomalar yo‘q"</string>
     <string name="profile_owned_footer" msgid="2756770645766113964">"Profil kuzatilishi mumkin"</string>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index 0e99877..021044d 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -353,6 +353,8 @@
     <string name="quick_settings_user_title" msgid="8673045967216204537">"Người dùng"</string>
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Người dùng mới"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
+    <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+    <skip />
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Chưa được kết nối"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Không có mạng nào"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Tắt Wi-Fi"</string>
@@ -454,9 +456,11 @@
     <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Hiển thị hồ sơ"</string>
     <string name="user_add_user" msgid="4336657383006913022">"Thêm người dùng"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"Người dùng mới"</string>
-    <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Xóa phiên khách?"</string>
+    <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) -->
+    <skip />
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Tất cả ứng dụng và dữ liệu trong phiên này sẽ bị xóa."</string>
-    <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Xóa"</string>
+    <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) -->
+    <skip />
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"Chào mừng bạn trở lại!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"Bạn có muốn tiếp tục phiên của mình không?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Bắt đầu lại"</string>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index 07232ff..60acb83 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -353,6 +353,8 @@
     <string name="quick_settings_user_title" msgid="8673045967216204537">"用户"</string>
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"新用户"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"WLAN"</string>
+    <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+    <skip />
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"未连接"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"无网络"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"WLAN:关闭"</string>
@@ -454,9 +456,11 @@
     <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"显示个人资料"</string>
     <string name="user_add_user" msgid="4336657383006913022">"添加用户"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"新用户"</string>
-    <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"要移除访客吗?"</string>
+    <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) -->
+    <skip />
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"此会话中的所有应用和数据都将被删除。"</string>
-    <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"移除"</string>
+    <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) -->
+    <skip />
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"访客,欢迎回来!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"要继续您的会话吗?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"重新开始"</string>
diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml
index 7a07af1..db55580 100644
--- a/packages/SystemUI/res/values-zh-rHK/strings.xml
+++ b/packages/SystemUI/res/values-zh-rHK/strings.xml
@@ -353,6 +353,8 @@
     <string name="quick_settings_user_title" msgid="8673045967216204537">"使用者"</string>
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"新使用者"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
+    <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+    <skip />
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"未連線"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"沒有網絡"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi 關閉"</string>
@@ -454,9 +456,11 @@
     <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"顯示個人檔案"</string>
     <string name="user_add_user" msgid="4336657383006913022">"加入使用者"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"新使用者"</string>
-    <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"移除訪客?"</string>
+    <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) -->
+    <skip />
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"這個工作階段中的所有應用程式和資料都會被刪除。"</string>
-    <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"移除"</string>
+    <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) -->
+    <skip />
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"訪客您好,歡迎回來!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"您要繼續您的工作階段嗎?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"重新開始"</string>
diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml
index 0369d63..1487ad3 100644
--- a/packages/SystemUI/res/values-zh-rTW/strings.xml
+++ b/packages/SystemUI/res/values-zh-rTW/strings.xml
@@ -353,6 +353,8 @@
     <string name="quick_settings_user_title" msgid="8673045967216204537">"使用者"</string>
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"新使用者"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string>
+    <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+    <skip />
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"未連線"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"沒有網路"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"Wi-Fi 已關閉"</string>
@@ -454,9 +456,11 @@
     <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"顯示設定檔"</string>
     <string name="user_add_user" msgid="4336657383006913022">"新增使用者"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"新使用者"</string>
-    <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"移除訪客?"</string>
+    <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) -->
+    <skip />
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"這個工作階段中的所有應用程式和資料都會遭到刪除。"</string>
-    <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"移除"</string>
+    <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) -->
+    <skip />
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"訪客你好,歡迎回來!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"你要繼續這個工作階段嗎?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"重新開始"</string>
diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml
index 068e2e5..b0f084b 100644
--- a/packages/SystemUI/res/values-zu/strings.xml
+++ b/packages/SystemUI/res/values-zu/strings.xml
@@ -353,6 +353,8 @@
     <string name="quick_settings_user_title" msgid="8673045967216204537">"Umsebenzisi"</string>
     <string name="quick_settings_user_new_user" msgid="3347905871336069666">"Umsebenzisi omusha"</string>
     <string name="quick_settings_wifi_label" msgid="2879507532983487244">"I-Wi-Fi"</string>
+    <!-- no translation found for quick_settings_internet_label (6603068555872455463) -->
+    <skip />
     <string name="quick_settings_wifi_not_connected" msgid="4071097522427039160">"Akuxhunyiwe"</string>
     <string name="quick_settings_wifi_no_network" msgid="6003178398713839313">"Ayikho inethiwekhi"</string>
     <string name="quick_settings_wifi_off_label" msgid="4003379736176547594">"I-Wi-Fi icimile"</string>
@@ -454,9 +456,11 @@
     <string name="accessibility_multi_user_switch_quick_contact" msgid="4504508915324898576">"Bonisa iphrofayela"</string>
     <string name="user_add_user" msgid="4336657383006913022">"Engeza umsebenzisi"</string>
     <string name="user_new_user_name" msgid="2019166282704195789">"Umsebenzisi omusha"</string>
-    <string name="guest_exit_guest_dialog_title" msgid="5015697561580641422">"Susa isivakashi?"</string>
+    <!-- no translation found for guest_exit_guest_dialog_title (2034481024623462357) -->
+    <skip />
     <string name="guest_exit_guest_dialog_message" msgid="8183450985628495709">"Zonke izinhlelo zokusebenza nedatha kulesi sikhathi zizosuswa."</string>
-    <string name="guest_exit_guest_dialog_remove" msgid="7505817591242703757">"Susa"</string>
+    <!-- no translation found for guest_exit_guest_dialog_remove (8533184512885775423) -->
+    <skip />
     <string name="guest_wipe_session_title" msgid="7147965814683990944">"Siyakwamukela futhi, sivakashi!"</string>
     <string name="guest_wipe_session_message" msgid="3393823610257065457">"Ingabe ufuna ukuqhubeka ngesikhathi sakho?"</string>
     <string name="guest_wipe_session_wipe" msgid="8056836584445473309">"Qala phansi"</string>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 0687d06..86af464 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -839,6 +839,8 @@
     <string name="quick_settings_user_new_user">New user</string>
     <!-- QuickSettings: Wifi [CHAR LIMIT=NONE] -->
     <string name="quick_settings_wifi_label">Wi-Fi</string>
+    <!-- QuickSettings: Internet [CHAR LIMIT=NONE] -->
+    <string name="quick_settings_internet_label">Internet</string>
     <!-- QuickSettings: Wifi (Not connected) [CHAR LIMIT=NONE] -->
     <string name="quick_settings_wifi_not_connected">Not Connected</string>
     <!-- QuickSettings: Wifi (No network) [CHAR LIMIT=NONE] -->
@@ -1096,13 +1098,13 @@
     <string name="user_new_user_name">New user</string>
 
     <!-- Title of the confirmation dialog when exiting guest session [CHAR LIMIT=NONE] -->
-    <string name="guest_exit_guest_dialog_title">Remove guest?</string>
+    <string name="guest_exit_guest_dialog_title">End guest session?</string>
 
     <!-- Message of the confirmation dialog when exiting guest session [CHAR LIMIT=NONE] -->
     <string name="guest_exit_guest_dialog_message">All apps and data in this session will be deleted.</string>
 
     <!-- Label for button in confirmation dialog when exiting guest session [CHAR LIMIT=35] -->
-    <string name="guest_exit_guest_dialog_remove">Remove</string>
+    <string name="guest_exit_guest_dialog_remove">End session</string>
 
     <!-- Title of the notification when resuming an existing guest session [CHAR LIMIT=NONE] -->
     <string name="guest_wipe_session_title">Welcome back, guest!</string>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java
index 1a71f11..d672c2c 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java
@@ -16,8 +16,6 @@
 
 package com.android.systemui.shared.system;
 
-import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT;
-import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
@@ -48,9 +46,6 @@
         options.setLaunchWindowingMode(isPrimary
                 ? WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
                 : WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
-        options.setSplitScreenCreateMode(dockTopLeft
-                ? SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT
-                : SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT);
         return options;
     }
 
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/LatencyTrackerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/LatencyTrackerCompat.java
index bf4fb0b..a8c19ec 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/LatencyTrackerCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/LatencyTrackerCompat.java
@@ -24,10 +24,6 @@
  * @see LatencyTracker
  */
 public class LatencyTrackerCompat {
-    public static boolean isEnabled(Context context) {
-        return LatencyTracker.isEnabled(context);
-    }
-
     /**
      * @see LatencyTracker
      * @deprecated Please use {@link LatencyTrackerCompat#logToggleRecents(Context, int)} instead.
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewTreeObserverWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewTreeObserverWrapper.java
index 4a870f1..cfb23f9 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewTreeObserverWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewTreeObserverWrapper.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.shared.system;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.graphics.Rect;
 import android.graphics.Region;
@@ -26,18 +27,21 @@
 
 public class ViewTreeObserverWrapper {
 
+    private static final HashMap<OnComputeInsetsListener, ViewTreeObserver>
+            sListenerObserverMap = new HashMap<>();
     private static final HashMap<OnComputeInsetsListener, OnComputeInternalInsetsListener>
-            sOnComputeInsetsListenerMap = new HashMap<>();
+            sListenerInternalListenerMap = new HashMap<>();
 
     /**
-     * Register a callback to be invoked when the invoked when it is time to
-     * compute the window's insets.
+     * Register a callback to be invoked when the invoked when it is time to compute the window's
+     * insets.
      *
+     * @param observer The observer to be added
      * @param listener The callback to add
      * @throws IllegalStateException If {@link ViewTreeObserver#isAlive()} returns false
      */
     public static void addOnComputeInsetsListener(
-            ViewTreeObserver observer, OnComputeInsetsListener listener) {
+            @NonNull ViewTreeObserver observer, @NonNull OnComputeInsetsListener listener) {
         final OnComputeInternalInsetsListener internalListener = internalInOutInfo -> {
             final InsetsInfo inOutInfo = new InsetsInfo();
             inOutInfo.contentInsets.set(internalInOutInfo.contentInsets);
@@ -49,23 +53,26 @@
             internalInOutInfo.touchableRegion.set(inOutInfo.touchableRegion);
             internalInOutInfo.setTouchableInsets(inOutInfo.mTouchableInsets);
         };
-        sOnComputeInsetsListenerMap.put(listener, internalListener);
+        sListenerObserverMap.put(listener, observer);
+        sListenerInternalListenerMap.put(listener, internalListener);
         observer.addOnComputeInternalInsetsListener(internalListener);
     }
 
     /**
-     * Remove a previously installed insets computation callback
+     * Remove a previously installed insets computation callback.
      *
      * @param victim The callback to remove
      * @throws IllegalStateException If {@link ViewTreeObserver#isAlive()} returns false
      * @see #addOnComputeInsetsListener(ViewTreeObserver, OnComputeInsetsListener)
      */
-    public void removeOnComputeInsetsListener(
-            ViewTreeObserver observer, OnComputeInsetsListener victim) {
-        final OnComputeInternalInsetsListener listener = sOnComputeInsetsListenerMap.get(victim);
-        if (listener != null) {
+    public static void removeOnComputeInsetsListener(@NonNull OnComputeInsetsListener victim) {
+        final ViewTreeObserver observer = sListenerObserverMap.get(victim);
+        final OnComputeInternalInsetsListener listener = sListenerInternalListenerMap.get(victim);
+        if (observer != null && listener != null) {
             observer.removeOnComputeInternalInsetsListener(listener);
         }
+        sListenerObserverMap.remove(victim);
+        sListenerInternalListenerMap.remove(victim);
     }
 
     /**
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
index d3066b4..b38270c 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
@@ -83,6 +83,11 @@
     public static final int WINDOWING_MODE_FREEFORM = WindowConfiguration.WINDOWING_MODE_FREEFORM;
 
     public static final int ITYPE_EXTRA_NAVIGATION_BAR = InsetsState.ITYPE_EXTRA_NAVIGATION_BAR;
+    public static final int ITYPE_LEFT_TAPPABLE_ELEMENT = InsetsState.ITYPE_LEFT_TAPPABLE_ELEMENT;
+    public static final int ITYPE_TOP_TAPPABLE_ELEMENT = InsetsState.ITYPE_TOP_TAPPABLE_ELEMENT;
+    public static final int ITYPE_RIGHT_TAPPABLE_ELEMENT = InsetsState.ITYPE_RIGHT_TAPPABLE_ELEMENT;
+    public static final int ITYPE_BOTTOM_TAPPABLE_ELEMENT =
+            InsetsState.ITYPE_BOTTOM_TAPPABLE_ELEMENT;
 
     private static final WindowManagerWrapper sInstance = new WindowManagerWrapper();
 
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardInputView.java
index d58b95c..d1494df 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardInputView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardInputView.java
@@ -23,10 +23,16 @@
 
 import androidx.annotation.Nullable;
 
+import com.android.systemui.Gefingerpoken;
+
+import java.util.ArrayList;
+import java.util.List;
+
 /**
  * A Base class for all Keyguard password/pattern/pin related inputs.
  */
 public abstract class KeyguardInputView extends LinearLayout {
+    private final List<Gefingerpoken> mMotionEventListener = new ArrayList<>();
 
     public KeyguardInputView(Context context) {
         super(context);
@@ -56,4 +62,25 @@
     boolean startDisappearAnimation(Runnable finishRunnable) {
         return false;
     }
+
+    void addMotionEventListener(Gefingerpoken listener) {
+        mMotionEventListener.add(listener);
+    }
+
+    void removeMotionEventListener(Gefingerpoken listener) {
+        mMotionEventListener.remove(listener);
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent event) {
+        return mMotionEventListener.stream().anyMatch(listener -> listener.onTouchEvent(event))
+                || super.onTouchEvent(event);
+    }
+
+    @Override
+    public boolean onInterceptTouchEvent(MotionEvent event) {
+        return mMotionEventListener.stream().anyMatch(
+                listener -> listener.onInterceptTouchEvent(event))
+                || super.onInterceptTouchEvent(event);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
index 6aa5e0d..1c691e7 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
@@ -26,6 +26,7 @@
 import com.android.internal.widget.LockPatternUtils;
 import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
 import com.android.systemui.R;
+import com.android.systemui.classifier.FalsingCollector;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.util.ViewController;
 import com.android.systemui.util.concurrency.DelayableExecutor;
@@ -155,6 +156,7 @@
         private final Resources mResources;
         private LiftToActivateListener mLiftToActivateListener;
         private TelephonyManager mTelephonyManager;
+        private final FalsingCollector mFalsingCollector;
 
         @Inject
         public Factory(KeyguardUpdateMonitor keyguardUpdateMonitor,
@@ -163,7 +165,7 @@
                 KeyguardMessageAreaController.Factory messageAreaControllerFactory,
                 InputMethodManager inputMethodManager, @Main DelayableExecutor mainExecutor,
                 @Main Resources resources, LiftToActivateListener liftToActivateListener,
-                TelephonyManager telephonyManager) {
+                TelephonyManager telephonyManager, FalsingCollector falsingCollector) {
             mKeyguardUpdateMonitor = keyguardUpdateMonitor;
             mLockPatternUtils = lockPatternUtils;
             mLatencyTracker = latencyTracker;
@@ -173,6 +175,7 @@
             mResources = resources;
             mLiftToActivateListener = liftToActivateListener;
             mTelephonyManager = telephonyManager;
+            mFalsingCollector = falsingCollector;
         }
 
         /** Create a new {@link KeyguardInputViewController}. */
@@ -191,17 +194,17 @@
                 return new KeyguardPinViewController((KeyguardPINView) keyguardInputView,
                         mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
                         keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker,
-                        mLiftToActivateListener);
+                        mLiftToActivateListener, mFalsingCollector);
             } else if (keyguardInputView instanceof KeyguardSimPinView) {
                 return new KeyguardSimPinViewController((KeyguardSimPinView) keyguardInputView,
                         mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
                         keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker,
-                        mLiftToActivateListener, mTelephonyManager);
+                        mLiftToActivateListener, mTelephonyManager, mFalsingCollector);
             } else if (keyguardInputView instanceof KeyguardSimPukView) {
                 return new KeyguardSimPukViewController((KeyguardSimPukView) keyguardInputView,
                         mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
                         keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker,
-                        mLiftToActivateListener, mTelephonyManager);
+                        mLiftToActivateListener, mTelephonyManager, mFalsingCollector);
             }
 
             throw new RuntimeException("Unable to find controller for " + keyguardInputView);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java
index 4d0ebff..f247948 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java
@@ -25,12 +25,15 @@
 import com.android.internal.util.LatencyTracker;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
+import com.android.systemui.Gefingerpoken;
 import com.android.systemui.R;
+import com.android.systemui.classifier.FalsingCollector;
 
 public abstract class KeyguardPinBasedInputViewController<T extends KeyguardPinBasedInputView>
         extends KeyguardAbsKeyInputViewController<T> {
 
     private final LiftToActivateListener mLiftToActivateListener;
+    private final FalsingCollector mFalsingCollector;
     protected PasswordTextView mPasswordEntry;
 
     private final OnKeyListener mOnKeyListener = (v, keyCode, event) -> {
@@ -40,13 +43,26 @@
         return false;
     };
 
-    private final OnTouchListener mOnTouchListener = (v, event) -> {
+    private final OnTouchListener mActionButtonTouchListener = (v, event) -> {
         if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
             mView.doHapticKeyClick();
         }
         return false;
     };
 
+    private final Gefingerpoken mGlobalTouchListener = new Gefingerpoken() {
+        @Override
+        public boolean onInterceptTouchEvent(MotionEvent ev) {
+            mFalsingCollector.avoidGesture();
+            return false;
+        }
+
+        @Override
+        public boolean onTouchEvent(MotionEvent ev) {
+            return false;
+        }
+    };
+
     protected KeyguardPinBasedInputViewController(T view,
             KeyguardUpdateMonitor keyguardUpdateMonitor,
             SecurityMode securityMode,
@@ -54,10 +70,12 @@
             KeyguardSecurityCallback keyguardSecurityCallback,
             KeyguardMessageAreaController.Factory messageAreaControllerFactory,
             LatencyTracker latencyTracker,
-            LiftToActivateListener liftToActivateListener) {
+            LiftToActivateListener liftToActivateListener,
+            FalsingCollector falsingCollector) {
         super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
                 messageAreaControllerFactory, latencyTracker);
         mLiftToActivateListener = liftToActivateListener;
+        mFalsingCollector = falsingCollector;
         mPasswordEntry = mView.findViewById(mView.getPasswordTextViewId());
     }
 
@@ -65,11 +83,13 @@
     protected void onViewAttached() {
         super.onViewAttached();
 
+        mView.addMotionEventListener(mGlobalTouchListener);
+
         mPasswordEntry.setOnKeyListener(mOnKeyListener);
         mPasswordEntry.setUserActivityListener(this::onUserInput);
 
         View deleteButton = mView.findViewById(R.id.delete_button);
-        deleteButton.setOnTouchListener(mOnTouchListener);
+        deleteButton.setOnTouchListener(mActionButtonTouchListener);
         deleteButton.setOnClickListener(v -> {
             // check for time-based lockouts
             if (mPasswordEntry.isEnabled()) {
@@ -87,7 +107,7 @@
 
         View okButton = mView.findViewById(R.id.key_enter);
         if (okButton != null) {
-            okButton.setOnTouchListener(mOnTouchListener);
+            okButton.setOnTouchListener(mActionButtonTouchListener);
             okButton.setOnClickListener(new View.OnClickListener() {
                 @Override
                 public void onClick(View v) {
@@ -101,6 +121,12 @@
     }
 
     @Override
+    protected void onViewDetached() {
+        super.onViewDetached();
+        mView.removeMotionEventListener(mGlobalTouchListener);
+    }
+
+    @Override
     public void onResume(int reason) {
         super.onResume(reason);
         mPasswordEntry.requestFocus();
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
index fb0d6be..c0aa2af 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
@@ -22,6 +22,7 @@
 import com.android.internal.widget.LockPatternUtils;
 import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
 import com.android.systemui.R;
+import com.android.systemui.classifier.FalsingCollector;
 
 public class KeyguardPinViewController
         extends KeyguardPinBasedInputViewController<KeyguardPINView> {
@@ -32,10 +33,11 @@
             SecurityMode securityMode, LockPatternUtils lockPatternUtils,
             KeyguardSecurityCallback keyguardSecurityCallback,
             KeyguardMessageAreaController.Factory messageAreaControllerFactory,
-            LatencyTracker latencyTracker,
-            LiftToActivateListener liftToActivateListener) {
+            LatencyTracker latencyTracker, LiftToActivateListener liftToActivateListener,
+            FalsingCollector falsingCollector) {
         super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
-                messageAreaControllerFactory, latencyTracker, liftToActivateListener);
+                messageAreaControllerFactory, latencyTracker, liftToActivateListener,
+                falsingCollector);
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
     }
 
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
index 5b4a7ff..b218141 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
@@ -38,6 +38,7 @@
 import com.android.internal.widget.LockPatternUtils;
 import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
 import com.android.systemui.R;
+import com.android.systemui.classifier.FalsingCollector;
 
 public class KeyguardSimPinViewController
         extends KeyguardPinBasedInputViewController<KeyguardSimPinView> {
@@ -76,11 +77,11 @@
             SecurityMode securityMode, LockPatternUtils lockPatternUtils,
             KeyguardSecurityCallback keyguardSecurityCallback,
             KeyguardMessageAreaController.Factory messageAreaControllerFactory,
-            LatencyTracker latencyTracker,
-            LiftToActivateListener liftToActivateListener,
-            TelephonyManager telephonyManager) {
+            LatencyTracker latencyTracker, LiftToActivateListener liftToActivateListener,
+            TelephonyManager telephonyManager, FalsingCollector falsingCollector) {
         super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
-                messageAreaControllerFactory, latencyTracker, liftToActivateListener);
+                messageAreaControllerFactory, latencyTracker, liftToActivateListener,
+                falsingCollector);
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
         mTelephonyManager = telephonyManager;
         mSimImageView = mView.findViewById(R.id.keyguard_sim);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
index eafb33f..890a17c 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
@@ -39,6 +39,7 @@
 import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
+import com.android.systemui.classifier.FalsingCollector;
 
 public class KeyguardSimPukViewController
         extends KeyguardPinBasedInputViewController<KeyguardSimPukView> {
@@ -83,11 +84,11 @@
             SecurityMode securityMode, LockPatternUtils lockPatternUtils,
             KeyguardSecurityCallback keyguardSecurityCallback,
             KeyguardMessageAreaController.Factory messageAreaControllerFactory,
-            LatencyTracker latencyTracker,
-            LiftToActivateListener liftToActivateListener,
-            TelephonyManager telephonyManager) {
+            LatencyTracker latencyTracker, LiftToActivateListener liftToActivateListener,
+            TelephonyManager telephonyManager, FalsingCollector falsingCollector) {
         super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
-                messageAreaControllerFactory, latencyTracker, liftToActivateListener);
+                messageAreaControllerFactory, latencyTracker, liftToActivateListener,
+                falsingCollector);
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
         mTelephonyManager = telephonyManager;
         mSimImageView = mView.findViewById(R.id.keyguard_sim);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
index 3cbab8e..fb97a30 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
@@ -26,6 +26,7 @@
 import android.content.res.Resources;
 import android.graphics.Color;
 import android.graphics.drawable.Drawable;
+import android.graphics.drawable.InsetDrawable;
 import android.graphics.text.LineBreaker;
 import android.net.Uri;
 import android.os.Trace;
@@ -239,6 +240,12 @@
                 final int iconSize = mHasHeader ? mIconSizeWithHeader : mIconSize;
                 iconDrawable = icon.getIcon().loadDrawable(mContext);
                 if (iconDrawable != null) {
+                    if ((iconDrawable instanceof InsetDrawable)
+                            && mLockScreenMode == KeyguardUpdateMonitor.LOCK_SCREEN_MODE_LAYOUT_1) {
+                        // System icons (DnD) use insets which are fine for centered slice content
+                        // but will cause a slight indent for left/right-aligned slice views
+                        iconDrawable = ((InsetDrawable) iconDrawable).getDrawable();
+                    }
                     final int width = (int) (iconDrawable.getIntrinsicWidth()
                             / (float) iconDrawable.getIntrinsicHeight() * iconSize);
                     iconDrawable.setBounds(0, 0, Math.max(width, 1), iconSize);
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationGestureDetector.java b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationGestureDetector.java
new file mode 100644
index 0000000..4c892e29
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationGestureDetector.java
@@ -0,0 +1,195 @@
+/*
+ * 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.systemui.accessibility;
+
+import android.annotation.DisplayContext;
+import android.annotation.NonNull;
+import android.content.Context;
+import android.graphics.PointF;
+import android.os.Handler;
+import android.view.Display;
+import android.view.MotionEvent;
+import android.view.ViewConfiguration;
+
+/**
+ * Detects single tap and drag gestures using the supplied {@link MotionEvent}s. The {@link
+ * OnGestureListener} callback will notify users when a particular motion event has occurred. This
+ * class should only be used with {@link MotionEvent}s reported via touch (don't use for trackball
+ * events).
+ */
+class MagnificationGestureDetector {
+
+    interface OnGestureListener {
+        /**
+         * Called when a tap is completed within {@link ViewConfiguration#getLongPressTimeout()} and
+         * the offset between {@link MotionEvent}s and the down event doesn't exceed {@link
+         * ViewConfiguration#getScaledTouchSlop()}.
+         *
+         * @return {@code true} if this gesture is handled.
+         */
+        boolean onSingleTap();
+
+        /**
+         * Called when the user is performing dragging gesture. It is started after the offset
+         * between the down location and the move event location exceed
+         * {@link ViewConfiguration#getScaledTouchSlop()}.
+         *
+         * @param offsetX The X offset in screen coordinate.
+         * @param offsetY The Y offset in screen coordinate.
+         * @return {@code true} if this gesture is handled.
+         */
+        boolean onDrag(float offsetX, float offsetY);
+
+        /**
+         * Notified when a tap occurs with the down {@link MotionEvent} that triggered it. This will
+         * be triggered immediately for every down event. All other events should be preceded by
+         * this.
+         *
+         * @param x The X coordinate of the down event.
+         * @param y The Y coordinate of the down event.
+         * @return {@code true} if the down event is handled, otherwise the events won't be sent to
+         * the view.
+         */
+        boolean onStart(float x, float y);
+
+        /**
+         * Called when the detection is finished. In other words, it is called when up/cancel {@link
+         * MotionEvent} is received. It will be triggered after single-tap
+         *
+         * @param x The X coordinate on the screen of the up event or the cancel event.
+         * @param y The Y coordinate on the screen of the up event or the cancel event.
+         * @return {@code true} if the event is handled.
+         */
+        boolean onFinish(float x, float y);
+    }
+
+    private final PointF mPointerDown = new PointF();
+    private final PointF mPointerLocation = new PointF(Float.NaN, Float.NaN);
+    private final Handler mHandler;
+    private final Runnable mCancelTapGestureRunnable;
+    private final OnGestureListener mOnGestureListener;
+    private int mTouchSlopSquare;
+    // Assume the gesture default is a single-tap. Set it to false if the gesture couldn't be a
+    // single-tap anymore.
+    private boolean mDetectSingleTap = true;
+    private boolean mDraggingDetected = false;
+
+    /**
+     * @param context  {@link Context} that is from {@link Context#createDisplayContext(Display)}.
+     * @param handler  The handler to post the runnable.
+     * @param listener The listener invoked for all the callbacks.
+     */
+    MagnificationGestureDetector(@DisplayContext Context context, @NonNull Handler handler,
+            @NonNull OnGestureListener listener) {
+        final int touchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
+        mTouchSlopSquare = touchSlop * touchSlop;
+        mHandler = handler;
+        mOnGestureListener = listener;
+        mCancelTapGestureRunnable = () -> mDetectSingleTap = false;
+    }
+
+    /**
+     * Analyzes the given motion event and if applicable to trigger the appropriate callbacks on the
+     * {@link OnGestureListener} supplied.
+     *
+     * @param event The current motion event.
+     * @return {@code True} if the {@link OnGestureListener} consumes the event, else false.
+     */
+    boolean onTouch(MotionEvent event) {
+        final float rawX = event.getRawX();
+        final float rawY = event.getRawY();
+        boolean handled = false;
+        switch (event.getAction()) {
+            case MotionEvent.ACTION_DOWN:
+                mPointerDown.set(rawX, rawY);
+                mHandler.postAtTime(mCancelTapGestureRunnable,
+                        event.getDownTime() + ViewConfiguration.getLongPressTimeout());
+                handled |= mOnGestureListener.onStart(rawX, rawY);
+                break;
+            case MotionEvent.ACTION_POINTER_DOWN:
+                stopSingleTapDetection();
+                break;
+            case MotionEvent.ACTION_MOVE:
+                stopSingleTapDetectionIfNeeded(rawX, rawY);
+                handled |= notifyDraggingGestureIfNeeded(rawX, rawY);
+                break;
+            case MotionEvent.ACTION_UP:
+                stopSingleTapDetectionIfNeeded(rawX, rawY);
+                if (mDetectSingleTap) {
+                    handled |= mOnGestureListener.onSingleTap();
+                }
+                // Fall through
+            case MotionEvent.ACTION_CANCEL:
+                handled |= mOnGestureListener.onFinish(rawX, rawY);
+                reset();
+                break;
+        }
+        return handled;
+    }
+
+    private void stopSingleTapDetectionIfNeeded(float x, float y) {
+        if (mDraggingDetected) {
+            return;
+        }
+        if (!isLocationValid(mPointerDown)) {
+            return;
+        }
+
+        final int deltaX = (int) (mPointerDown.x - x);
+        final int deltaY = (int) (mPointerDown.y - y);
+        final int distanceSquare = (deltaX * deltaX) + (deltaY * deltaY);
+        if (distanceSquare > mTouchSlopSquare) {
+            mDraggingDetected = true;
+            stopSingleTapDetection();
+        }
+    }
+
+    private void stopSingleTapDetection() {
+        mHandler.removeCallbacks(mCancelTapGestureRunnable);
+        mDetectSingleTap = false;
+    }
+
+    private boolean notifyDraggingGestureIfNeeded(float x, float y) {
+        if (!mDraggingDetected) {
+            return false;
+        }
+        if (!isLocationValid(mPointerLocation)) {
+            mPointerLocation.set(mPointerDown);
+        }
+        final float offsetX = x - mPointerLocation.x;
+        final float offsetY = y - mPointerLocation.y;
+        mPointerLocation.set(x, y);
+        return mOnGestureListener.onDrag(offsetX, offsetY);
+    }
+
+    private void reset() {
+        resetPointF(mPointerDown);
+        resetPointF(mPointerLocation);
+        mHandler.removeCallbacks(mCancelTapGestureRunnable);
+        mDetectSingleTap = true;
+        mDraggingDetected = false;
+    }
+
+    private static void resetPointF(PointF pointF) {
+        pointF.x = Float.NaN;
+        pointF.y = Float.NaN;
+    }
+
+    private static boolean isLocationValid(PointF location) {
+        return !Float.isNaN(location.x) && !Float.isNaN(location.y);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java
index edc3216..c1cf8d3 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java
@@ -22,16 +22,13 @@
 import android.content.Context;
 import android.content.pm.ActivityInfo;
 import android.graphics.PixelFormat;
-import android.graphics.PointF;
 import android.graphics.Rect;
 import android.os.Bundle;
 import android.os.UserHandle;
 import android.provider.Settings;
-import android.util.MathUtils;
 import android.view.Gravity;
 import android.view.MotionEvent;
 import android.view.View;
-import android.view.ViewConfiguration;
 import android.view.WindowManager;
 import android.view.WindowManager.LayoutParams;
 import android.view.accessibility.AccessibilityManager;
@@ -46,11 +43,11 @@
 
 /**
  * Shows/hides a {@link android.widget.ImageView} on the screen and changes the values of
- * {@link Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE} when the UI is toggled.
+ * {@link Settings.Secure#ACCESSIBILITY_MAGNIFICATION_MODE} when the UI is toggled.
  * The button icon is movable by dragging. And the button UI would automatically be dismissed after
  * displaying for a period of time.
  */
-class MagnificationModeSwitch {
+class MagnificationModeSwitch implements MagnificationGestureDetector.OnGestureListener {
 
     @VisibleForTesting
     static final long FADING_ANIMATION_DURATION_MS = 300;
@@ -66,13 +63,11 @@
     private final AccessibilityManager mAccessibilityManager;
     private final WindowManager mWindowManager;
     private final ImageView mImageView;
-    private final PointF mLastDown = new PointF();
-    private final PointF mLastDrag = new PointF();
-    private final int mTapTimeout = ViewConfiguration.getTapTimeout();
-    private final int mTouchSlop;
     private int mMagnificationMode = Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
     private final LayoutParams mParams;
     private boolean mIsVisible = false;
+    private final MagnificationGestureDetector mGestureDetector;
+    private boolean mSingleTapDetected = false;
 
     MagnificationModeSwitch(Context context) {
         this(context, createView(context));
@@ -86,7 +81,6 @@
                 Context.WINDOW_SERVICE);
         mParams = createLayoutParams(context);
         mImageView = imageView;
-        mTouchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop();
         applyResourcesValues();
         mImageView.setOnTouchListener(this::onTouch);
         mImageView.setAccessibilityDelegate(new View.AccessibilityDelegate() {
@@ -127,6 +121,8 @@
                     .start();
             mIsFadeOutAnimating = true;
         };
+        mGestureDetector = new MagnificationGestureDetector(context,
+                context.getMainThreadHandler(), this);
     }
 
     private CharSequence formatStateDescription() {
@@ -144,39 +140,38 @@
     }
 
     private boolean onTouch(View v, MotionEvent event) {
-        if (!mIsVisible || mImageView == null) {
+        if (!mIsVisible) {
             return false;
         }
-        switch (event.getAction()) {
-            case MotionEvent.ACTION_DOWN:
-                stopFadeOutAnimation();
-                mLastDown.set(event.getRawX(), event.getRawY());
-                mLastDrag.set(event.getRawX(), event.getRawY());
-                return true;
-            case MotionEvent.ACTION_MOVE:
-                // Move the button position.
-                moveButton(event.getRawX() - mLastDrag.x,
-                        event.getRawY() - mLastDrag.y);
-                mLastDrag.set(event.getRawX(), event.getRawY());
-                return true;
-            case MotionEvent.ACTION_UP:
-                // Single tap to toggle magnification mode and the button position will be reset
-                // after the action is performed.
-                final float distance = MathUtils.dist(mLastDown.x, mLastDown.y,
-                        event.getRawX(), event.getRawY());
-                if ((event.getEventTime() - event.getDownTime()) <= mTapTimeout
-                        && distance <= mTouchSlop) {
-                    handleSingleTap();
-                } else {
-                    showButton(mMagnificationMode);
-                }
-                return true;
-            case MotionEvent.ACTION_CANCEL:
-                showButton(mMagnificationMode);
-                return true;
-            default:
-                return false;
+        return mGestureDetector.onTouch(event);
+    }
+
+    @Override
+    public boolean onSingleTap() {
+        mSingleTapDetected = true;
+        handleSingleTap();
+        return true;
+    }
+
+    @Override
+    public boolean onDrag(float offsetX, float offsetY) {
+        moveButton(offsetX, offsetY);
+        return true;
+    }
+
+    @Override
+    public boolean onStart(float x, float y) {
+        stopFadeOutAnimation();
+        return true;
+    }
+
+    @Override
+    public boolean onFinish(float xOffset, float yOffset) {
+        if (!mSingleTapDetected) {
+            showButton(mMagnificationMode);
         }
+        mSingleTapDetected = false;
+        return true;
     }
 
     private void moveButton(float offsetX, float offsetY) {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
index 87dc6a5..3f7d2d8 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
@@ -20,6 +20,8 @@
 import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_2BUTTON;
 import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
 
+import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
@@ -28,7 +30,6 @@
 import android.graphics.Matrix;
 import android.graphics.PixelFormat;
 import android.graphics.Point;
-import android.graphics.PointF;
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.graphics.Region;
@@ -66,7 +67,7 @@
  * Class to handle adding and removing a window magnification.
  */
 class WindowMagnificationController implements View.OnTouchListener, SurfaceHolder.Callback,
-        MirrorWindowControl.MirrorWindowDelegate {
+        MirrorWindowControl.MirrorWindowDelegate, MagnificationGestureDetector.OnGestureListener {
 
     private static final String TAG = "WindowMagnificationController";
     // Delay to avoid updating state description too frequently.
@@ -74,6 +75,7 @@
     // It should be consistent with the value defined in WindowMagnificationGestureHandler.
     private static final Range<Float> A11Y_ACTION_SCALE_RANGE = new Range<>(2.0f, 8.0f);
     private static final float A11Y_CHANGE_SCALE_DIFFERENCE = 1.0f;
+    private static final float ANIMATION_BOUNCE_EFFECT_SCALE = 1.05f;
     private final Context mContext;
     private final Resources mResources;
     private final Handler mHandler;
@@ -102,7 +104,6 @@
     private View mRightDrag;
     private View mBottomDrag;
 
-    private final PointF mLastDrag = new PointF();
     @NonNull
     private final WindowMagnifierCallback mWindowMagnifierCallback;
 
@@ -123,9 +124,12 @@
     private int mNavGestureHeight;
 
     private final SfVsyncFrameCallbackProvider mSfVsyncFrameProvider;
+    private final MagnificationGestureDetector mGestureDetector;
+    private final int mBounceEffectDuration;
     private Choreographer.FrameCallback mMirrorViewGeometryVsyncCallback;
     private Locale mLocale;
     private NumberFormat mPercentFormat;
+    private float mBounceEffectAnimationScale;
 
     @Nullable
     private MirrorWindowControl mMirrorWindowControl;
@@ -147,14 +151,19 @@
 
         mResources = mContext.getResources();
         mScale = mResources.getInteger(R.integer.magnification_default_scale);
+        mBounceEffectDuration = mResources.getInteger(
+                com.android.internal.R.integer.config_shortAnimTime);
         updateDimensions();
+        setInitialStartBounds();
+        computeBounceAnimationScale();
 
         mMirrorWindowControl = mirrorWindowControl;
         if (mMirrorWindowControl != null) {
             mMirrorWindowControl.setWindowDelegate(this);
         }
         mTransaction = transaction;
-        setInitialStartBounds();
+        mGestureDetector =
+                new MagnificationGestureDetector(mContext, handler, this);
 
         // Initialize listeners.
         mMirrorViewRunnable = () -> {
@@ -203,6 +212,13 @@
         updateNavigationBarDimensions();
     }
 
+    private void computeBounceAnimationScale() {
+        final float windowWidth = mMagnificationFrame.width() + 2 * mMirrorSurfaceMargin;
+        final float visibleWindowWidth = windowWidth - 2 * mOuterBorderSize;
+        final float animationScaleMax = windowWidth / visibleWindowWidth;
+        mBounceEffectAnimationScale = Math.min(animationScaleMax, ANIMATION_BOUNCE_EFFECT_SCALE);
+    }
+
     private void updateNavigationBarDimensions() {
         if (!supportsSwipeUpGesture()) {
             mNavGestureHeight = 0;
@@ -248,6 +264,7 @@
     void onConfigurationChanged(int configDiff) {
         if ((configDiff & ActivityInfo.CONFIG_DENSITY) != 0) {
             updateDimensions();
+            computeBounceAnimationScale();
             if (isWindowVisible()) {
                 deleteWindowMagnification();
                 enableWindowMagnification(Float.NaN, Float.NaN, Float.NaN);
@@ -483,20 +500,7 @@
     public boolean onTouch(View v, MotionEvent event) {
         if (v == mDragView || v == mLeftDrag || v == mTopDrag || v == mRightDrag
                 || v == mBottomDrag) {
-            return handleDragTouchEvent(event);
-        }
-        return false;
-    }
-
-    private boolean handleDragTouchEvent(MotionEvent event) {
-        switch (event.getAction()) {
-            case MotionEvent.ACTION_DOWN:
-                mLastDrag.set(event.getRawX(), event.getRawY());
-                return true;
-            case MotionEvent.ACTION_MOVE:
-                moveWindowMagnifier(event.getRawX() - mLastDrag.x, event.getRawY() - mLastDrag.y);
-                mLastDrag.set(event.getRawX(), event.getRawY());
-                return true;
+            return mGestureDetector.onTouch(event);
         }
         return false;
     }
@@ -685,6 +689,36 @@
         return mPercentFormat.format(scale);
     }
 
+    @Override
+    public boolean onSingleTap() {
+        animateBounceEffect();
+        return true;
+    }
+
+    @Override
+    public boolean onDrag(float offsetX, float offsetY) {
+        moveWindowMagnifier(offsetX, offsetY);
+        return true;
+    }
+
+    @Override
+    public boolean onStart(float x, float y) {
+        return true;
+    }
+
+    @Override
+    public boolean onFinish(float x, float y) {
+        return false;
+    }
+
+    private void animateBounceEffect() {
+        final ObjectAnimator scaleAnimator = ObjectAnimator.ofPropertyValuesHolder(mMirrorView,
+                PropertyValuesHolder.ofFloat(View.SCALE_X, 1, mBounceEffectAnimationScale, 1),
+                PropertyValuesHolder.ofFloat(View.SCALE_Y, 1, mBounceEffectAnimationScale, 1));
+        scaleAnimator.setDuration(mBounceEffectDuration);
+        scaleAnimator.start();
+    }
+
     private class MirrorWindowA11yDelegate extends View.AccessibilityDelegate {
 
         @Override
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 65a6f29..60a14be 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -26,6 +26,7 @@
 import android.graphics.PixelFormat;
 import android.graphics.Point;
 import android.graphics.RectF;
+import android.hardware.display.DisplayManager;
 import android.hardware.fingerprint.FingerprintManager;
 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
 import android.hardware.fingerprint.IUdfpsOverlayController;
@@ -166,7 +167,7 @@
             @Main Resources resources,
             LayoutInflater inflater,
             @Nullable FingerprintManager fingerprintManager,
-            PowerManager powerManager,
+            DisplayManager displayManager,
             WindowManager windowManager,
             SystemSettings systemSettings,
             @NonNull StatusBarStateController statusBarStateController,
@@ -246,7 +247,7 @@
 
         mBacklightToNitsSpline = Spline.createSpline(normalizedBacklightRange, nitsRange);
         mNitsToHbmBacklightSpline = Spline.createSpline(hbmNitsRange, normalizedBacklightRange);
-        mDefaultBrightness = obtainDefaultBrightness(powerManager);
+        mDefaultBrightness = obtainDefaultBrightness(mContext);
 
         // TODO(b/160025856): move to the "dump" method.
         Log.v(TAG, String.format("ctor | mNitsRange: [%f, %f]", nitsRange[0],
@@ -450,14 +451,9 @@
         }
     }
 
-    private static float obtainDefaultBrightness(PowerManager powerManager) {
-        if (powerManager == null) {
-            Log.e(TAG, "PowerManager is unavailable. Can't obtain default brightness.");
-            return 0f;
-        }
-        return MathUtils.constrain(powerManager.getBrightnessConstraint(
-                PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DEFAULT), PowerManager.BRIGHTNESS_MIN,
-                PowerManager.BRIGHTNESS_MAX);
+    private static float obtainDefaultBrightness(Context context) {
+        return MathUtils.constrain(context.getDisplay().getBrightnessDefault(),
+                PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX);
     }
 
     private static float[] toFloatArray(TypedArray array) {
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java b/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java
index e78057f..6572ca9 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/BrightLineFalsingManager.java
@@ -18,6 +18,7 @@
 
 import static com.android.systemui.classifier.FalsingManagerProxy.FALSING_SUCCESS;
 import static com.android.systemui.classifier.FalsingModule.BRIGHT_LINE_GESTURE_CLASSIFERS;
+import static com.android.systemui.classifier.FalsingModule.DOUBLE_TAP_TIMEOUT_MS;
 
 import android.net.Uri;
 import android.os.Build;
@@ -28,23 +29,26 @@
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.systemui.classifier.FalsingDataProvider.SessionListener;
+import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.dagger.qualifiers.TestHarness;
 import com.android.systemui.dock.DockManager;
 import com.android.systemui.plugins.FalsingManager;
+import com.android.systemui.util.concurrency.DelayableExecutor;
 import com.android.systemui.util.sensors.ThresholdSensor;
-import com.android.systemui.util.time.SystemClock;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayDeque;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.List;
 import java.util.Locale;
 import java.util.Queue;
 import java.util.Set;
 import java.util.StringJoiner;
 import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 import javax.inject.Inject;
 import javax.inject.Named;
@@ -65,7 +69,8 @@
     private final SingleTapClassifier mSingleTapClassifier;
     private final DoubleTapClassifier mDoubleTapClassifier;
     private final HistoryTracker mHistoryTracker;
-    private final SystemClock mSystemClock;
+    private final DelayableExecutor mDelayableExecutor;
+    private final long mDoubleTapTimeMs;
     private final boolean mTestHarness;
     private final MetricsLogger mMetricsLogger;
     private int mIsFalseTouchCalls;
@@ -90,23 +95,44 @@
 
     private final FalsingDataProvider.GestureCompleteListener mGestureCompleteListener =
             new FalsingDataProvider.GestureCompleteListener() {
-        @Override
-        public void onGestureComplete() {
-            mHistoryTracker.addResults(
-                    mClassifiers.stream().map(FalsingClassifier::classifyGesture)
-                            .collect(Collectors.toCollection(ArrayList::new)),
-                    mSystemClock.uptimeMillis());
+                @Override
+        public void onGestureComplete(long completionTimeMs) {
+            if (mPriorResults != null) {
+                // Single taps that may become double taps don't get added right away.
+                if (mClassifyAsSingleTap) {
+                    Collection<FalsingClassifier.Result> singleTapResults = mPriorResults;
+                    mSingleTapHistoryCanceller = mDelayableExecutor.executeDelayed(
+                            () -> {
+                                mSingleTapHistoryCanceller = null;
+                                mHistoryTracker.addResults(singleTapResults, completionTimeMs);
+                            },
+                            mDoubleTapTimeMs);
+                    mClassifyAsSingleTap = false;  // Don't treat things as single taps by default.
+                } else {
+                    mHistoryTracker.addResults(mPriorResults, completionTimeMs);
+                }
+                mPriorResults = null;
+            } else {
+                // Gestures that were not classified get treated as a false.
+                mHistoryTracker.addResults(
+                        Collections.singleton(
+                                FalsingClassifier.Result.falsed(.8, "unclassified")),
+                        completionTimeMs);
+            }
         }
     };
 
-    private boolean mPreviousResult = false;
+    private Collection<FalsingClassifier.Result> mPriorResults;
+    private boolean mClassifyAsSingleTap;
+    private Runnable mSingleTapHistoryCanceller;
 
     @Inject
     public BrightLineFalsingManager(FalsingDataProvider falsingDataProvider,
             DockManager dockManager, MetricsLogger metricsLogger,
             @Named(BRIGHT_LINE_GESTURE_CLASSIFERS) Set<FalsingClassifier> classifiers,
             SingleTapClassifier singleTapClassifier, DoubleTapClassifier doubleTapClassifier,
-            HistoryTracker historyTracker, SystemClock systemClock,
+            HistoryTracker historyTracker, @Main DelayableExecutor delayableExecutor,
+            @Named(DOUBLE_TAP_TIMEOUT_MS) long doubleTapTimeMs,
             @TestHarness boolean testHarness) {
         mDataProvider = falsingDataProvider;
         mDockManager = dockManager;
@@ -115,7 +141,8 @@
         mSingleTapClassifier = singleTapClassifier;
         mDoubleTapClassifier = doubleTapClassifier;
         mHistoryTracker = historyTracker;
-        mSystemClock = systemClock;
+        mDelayableExecutor = delayableExecutor;
+        mDoubleTapTimeMs = doubleTapTimeMs;
         mTestHarness = testHarness;
 
         mDataProvider.addSessionListener(mSessionListener);
@@ -129,38 +156,51 @@
 
     @Override
     public boolean isFalseTouch(@Classifier.InteractionType int interactionType) {
+        boolean result;
+
+        mClassifyAsSingleTap = false;
         mDataProvider.setInteractionType(interactionType);
-        if (!mDataProvider.isDirty()) {
-            return mPreviousResult;
+
+        if (!mTestHarness && !mDataProvider.isJustUnlockedWithFace() && !mDockManager.isDocked()) {
+            Stream<FalsingClassifier.Result> results =
+                    mClassifiers.stream().map(falsingClassifier -> {
+                        FalsingClassifier.Result classifierResult =
+                                falsingClassifier.classifyGesture(
+                                        mHistoryTracker.falsePenalty(),
+                                        mHistoryTracker.falseConfidence());
+                        if (classifierResult.isFalse()) {
+                            logInfo(String.format(
+                                    (Locale) null,
+                                    "{classifier=%s, interactionType=%d}",
+                                    falsingClassifier.getClass().getName(),
+                                    mDataProvider.getInteractionType()));
+                            String reason = classifierResult.getReason();
+                            if (reason != null) {
+                                logInfo(reason);
+                            }
+                        } else {
+                            logDebug(falsingClassifier.getClass().getName() + ": false");
+                        }
+                        return classifierResult;
+                    });
+            mPriorResults = new ArrayList<>();
+            final boolean[] localResult = {false};
+            results.forEach(classifierResult -> {
+                localResult[0] |= classifierResult.isFalse();
+                mPriorResults.add(classifierResult);
+            });
+            result = localResult[0];
+        } else {
+            result = false;
+            mPriorResults = Collections.singleton(FalsingClassifier.Result.passed(1));
         }
 
-        mPreviousResult = !mTestHarness
-                && !mDataProvider.isJustUnlockedWithFace() && !mDockManager.isDocked()
-                && mClassifiers.stream().anyMatch(falsingClassifier -> {
-                    FalsingClassifier.Result result = falsingClassifier.classifyGesture(
-                            mHistoryTracker.falsePenalty(), mHistoryTracker.falseConfidence());
-                    if (result.isFalse()) {
-                        logInfo(String.format(
-                                (Locale) null,
-                                "{classifier=%s, interactionType=%d}",
-                                falsingClassifier.getClass().getName(),
-                                mDataProvider.getInteractionType()));
-                        String reason = result.getReason();
-                        if (reason != null) {
-                            logInfo(reason);
-                        }
-                    } else {
-                        logDebug(falsingClassifier.getClass().getName() + ": false");
-                    }
-                    return result.isFalse();
-                });
-
-        logDebug("Is false touch? " + mPreviousResult);
+        logDebug("Is false touch? " + result);
 
         if (Build.IS_ENG || Build.IS_USERDEBUG) {
             // Copy motion events, as the passed in list gets emptied out elsewhere in the code.
             RECENT_SWIPES.add(new DebugSwipeRecord(
-                    mPreviousResult,
+                    result,
                     mDataProvider.getInteractionType(),
                     mDataProvider.getRecentMotionEvents().stream().map(
                             motionEvent -> new XYDt(
@@ -173,13 +213,16 @@
             }
         }
 
-        return mPreviousResult;
+        return result;
     }
 
     @Override
     public boolean isFalseTap(boolean robustCheck) {
+        mClassifyAsSingleTap = true;
+
         FalsingClassifier.Result singleTapResult =
                 mSingleTapClassifier.isTap(mDataProvider.getRecentMotionEvents());
+        mPriorResults = Collections.singleton(singleTapResult);
         if (singleTapResult.isFalse()) {
             logInfo(String.format(
                     (Locale) null, "{classifier=%s}", mSingleTapClassifier.getClass().getName()));
@@ -192,7 +235,12 @@
 
         // TODO(b/172655679): More heuristics to come. For now, allow touches through if face-authed
         if (robustCheck) {
-            return !mDataProvider.isJustUnlockedWithFace();
+            boolean result = !mDataProvider.isJustUnlockedWithFace();
+            mPriorResults = Collections.singleton(
+                    result ? FalsingClassifier.Result.falsed(0.1, "no face detected")
+                            : FalsingClassifier.Result.passed(1));
+
+            return result;
         }
 
         return false;
@@ -200,7 +248,9 @@
 
     @Override
     public boolean isFalseDoubleTap() {
+        mClassifyAsSingleTap = false;
         FalsingClassifier.Result result = mDoubleTapClassifier.classifyGesture();
+        mPriorResults = Collections.singleton(result);
         if (result.isFalse()) {
             logInfo(String.format(
                     (Locale) null, "{classifier=%s}", mDoubleTapClassifier.getClass().getName()));
@@ -208,6 +258,12 @@
             if (reason != null) {
                 logInfo(reason);
             }
+        } else {
+            // A valid double tap prevents an invalid single tap from going into history.
+            if (mSingleTapHistoryCanceller != null) {
+                mSingleTapHistoryCanceller.run();
+                mSingleTapHistoryCanceller = null;
+            }
         }
         return result.isFalse();
     }
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollector.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollector.java
index fe47162..b0bbab3 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollector.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollector.java
@@ -116,6 +116,9 @@
     void onTouchEvent(MotionEvent ev);
 
     /** */
+    void avoidGesture();
+
+    /** */
     void cleanup();
 }
 
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorFake.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorFake.java
index fd05989..12a0604 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorFake.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorFake.java
@@ -147,6 +147,10 @@
     }
 
     @Override
+    public void avoidGesture() {
+    }
+
+    @Override
     public void cleanup() {
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java
index 4c11ecf..e08b43b 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingCollectorImpl.java
@@ -49,6 +49,8 @@
     private boolean mShowingAod;
     private boolean mScreenOn;
     private boolean mSessionStarted;
+    private MotionEvent mPendingDownEvent;
+    private boolean mAvoidGesture;
 
     private final ThresholdSensor.Listener mSensorEventListener = this::onProximityEvent;
 
@@ -245,7 +247,32 @@
 
     @Override
     public void onTouchEvent(MotionEvent ev) {
-        mFalsingDataProvider.onMotionEvent(ev);
+        // We delay processing down events to see if another component wants to process them.
+        // If #avoidGesture is called after a MotionEvent.ACTION_DOWN, all following motion events
+        // will be ignored by the collector until another MotionEvent.ACTION_DOWN is passed in.
+        // avoidGesture must be called immediately following the MotionEvent.ACTION_DOWN, before
+        // any other events are processed, otherwise the whole gesture will be recorded.
+        if (ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
+            // Make a copy of ev, since it will be recycled after we exit this method.
+            mPendingDownEvent = MotionEvent.obtain(ev);
+            mAvoidGesture = false;
+        } else if (!mAvoidGesture) {
+            if (mPendingDownEvent != null) {
+                mFalsingDataProvider.onMotionEvent(mPendingDownEvent);
+                mPendingDownEvent.recycle();
+                mPendingDownEvent = null;
+            }
+            mFalsingDataProvider.onMotionEvent(ev);
+        }
+    }
+
+    @Override
+    public void avoidGesture() {
+        if (mPendingDownEvent != null) {
+            mAvoidGesture = true;
+            mPendingDownEvent.recycle();
+            mPendingDownEvent = null;
+        }
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/FalsingDataProvider.java b/packages/SystemUI/src/com/android/systemui/classifier/FalsingDataProvider.java
index deb9e6d..4bacc15 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/FalsingDataProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/FalsingDataProvider.java
@@ -90,10 +90,8 @@
         }
 
         if (motionEvent.getActionMasked() == MotionEvent.ACTION_DOWN) {
-            if (!mRecentMotionEvents.isEmpty()) {
-                mExtendedMotionEvents.addFirst(mRecentMotionEvents);
-                mRecentMotionEvents = new TimeLimitedMotionEventBuffer(MOTION_EVENT_AGE_MS);
-            }
+            completePriorGesture();
+            mRecentMotionEvents = new TimeLimitedMotionEventBuffer(MOTION_EVENT_AGE_MS);
         }
         mRecentMotionEvents.addAll(motionEvents);
 
@@ -101,9 +99,25 @@
 
         mMotionEventListeners.forEach(listener -> listener.onMotionEvent(motionEvent));
 
+        // We explicitly do not complete a gesture on UP or CANCEL events.
+        // We wait for the next gesture to start before marking the prior gesture as complete.  This
+        // has multiple benefits. First, it makes it trivial to track the "current" or "recent"
+        // gesture, as it will always be found in mRecentMotionEvents. Second, and most importantly,
+        // it ensures that the current gesture doesn't get added to this HistoryTracker before it
+        // is analyzed.
+
         mDirty = true;
     }
 
+    private void completePriorGesture() {
+        if (!mRecentMotionEvents.isEmpty()) {
+            mGestuerCompleteListeners.forEach(listener -> listener.onGestureComplete(
+                    mRecentMotionEvents.get(mRecentMotionEvents.size() - 1).getEventTime()));
+
+            mExtendedMotionEvents.addFirst(mRecentMotionEvents);
+        }
+    }
+
     /** Returns screen width in pixels. */
     public int getWidthPixels() {
         return mWidthPixels;
@@ -146,13 +160,6 @@
         }
     }
 
-    /**
-     * Returns true if new data has been supplied since the last time this class has been accessed.
-     */
-    public boolean isDirty() {
-        return mDirty;
-    }
-
     /** Return the interaction type that is being compared against for falsing. */
     public  final int getInteractionType() {
         return mInteractionType;
@@ -387,6 +394,6 @@
     /** Callback to be alerted when the current gesture ends. */
     public interface GestureCompleteListener {
         /** */
-        void onGestureComplete();
+        void onGestureComplete(long completionTimeMs);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt b/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt
index 5c1c60c..80d1371 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt
@@ -29,11 +29,15 @@
 import android.service.media.MediaBrowserService
 import android.util.Log
 import com.android.internal.annotations.VisibleForTesting
+import com.android.systemui.Dumpable
 import com.android.systemui.broadcast.BroadcastDispatcher
 import com.android.systemui.dagger.SysUISingleton
 import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dump.DumpManager
 import com.android.systemui.tuner.TunerService
 import com.android.systemui.util.Utils
+import java.io.FileDescriptor
+import java.io.PrintWriter
 import java.util.concurrent.ConcurrentLinkedQueue
 import java.util.concurrent.Executor
 import javax.inject.Inject
@@ -49,8 +53,9 @@
     private val broadcastDispatcher: BroadcastDispatcher,
     @Background private val backgroundExecutor: Executor,
     private val tunerService: TunerService,
-    private val mediaBrowserFactory: ResumeMediaBrowserFactory
-) : MediaDataManager.Listener {
+    private val mediaBrowserFactory: ResumeMediaBrowserFactory,
+    dumpManager: DumpManager
+) : MediaDataManager.Listener, Dumpable {
 
     private var useMediaResumption: Boolean = Utils.useMediaResumption(context)
     private val resumeComponents: ConcurrentLinkedQueue<ComponentName> = ConcurrentLinkedQueue()
@@ -99,6 +104,7 @@
 
     init {
         if (useMediaResumption) {
+            dumpManager.registerDumpable(TAG, this)
             val unlockFilter = IntentFilter()
             unlockFilter.addAction(Intent.ACTION_USER_UNLOCKED)
             unlockFilter.addAction(Intent.ACTION_USER_SWITCHED)
@@ -283,4 +289,10 @@
             mediaBrowser?.restart()
         }
     }
+
+    override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
+        pw.apply {
+            println("resumeComponents: $resumeComponents")
+        }
+    }
 }
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
index 0d0d012..ac3fd4b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
@@ -28,6 +28,7 @@
 import android.service.quicksettings.Tile;
 import android.text.TextUtils;
 import android.util.ArraySet;
+import android.util.FeatureFlagUtils;
 import android.util.Log;
 
 import com.android.internal.logging.InstanceId;
@@ -444,6 +445,11 @@
         final ArrayList<String> tiles = new ArrayList<String>();
         boolean addedDefault = false;
         Set<String> addedSpecs = new ArraySet<>();
+        // TODO(b/174753536): Move it into the config file.
+        if (FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL)) {
+            tiles.add("internet");
+            addedSpecs.add("internet");
+        }
         for (String tile : tileList.split(",")) {
             tile = tile.trim();
             if (tile.isEmpty()) continue;
@@ -459,6 +465,12 @@
                     addedDefault = true;
                 }
             } else {
+                // TODO(b/174753536): Move it into the config file.
+                if (FeatureFlagUtils.isEnabled(context, FeatureFlagUtils.SETTINGS_PROVIDER_MODEL)) {
+                    if (tile.equals("wifi") || tile.equals("cell")) {
+                        continue;
+                    }
+                }
                 if (!addedSpecs.contains(tile)) {
                     tiles.add(tile);
                     addedSpecs.add(tile);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
index 24c0fd7..e9d481b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
@@ -37,6 +37,7 @@
 import com.android.systemui.qs.tiles.DndTile;
 import com.android.systemui.qs.tiles.FlashlightTile;
 import com.android.systemui.qs.tiles.HotspotTile;
+import com.android.systemui.qs.tiles.InternetTile;
 import com.android.systemui.qs.tiles.LocationTile;
 import com.android.systemui.qs.tiles.NfcTile;
 import com.android.systemui.qs.tiles.NightDisplayTile;
@@ -60,6 +61,7 @@
     private static final String TAG = "QSFactory";
 
     private final Provider<WifiTile> mWifiTileProvider;
+    private final Provider<InternetTile> mInternetTileProvider;
     private final Provider<BluetoothTile> mBluetoothTileProvider;
     private final Provider<CellularTile> mCellularTileProvider;
     private final Provider<DndTile> mDndTileProvider;
@@ -89,6 +91,7 @@
             Lazy<QSHost> qsHostLazy,
             Provider<CustomTile.Builder> customTileBuilderProvider,
             Provider<WifiTile> wifiTileProvider,
+            Provider<InternetTile> internetTileProvider,
             Provider<BluetoothTile> bluetoothTileProvider,
             Provider<CellularTile> cellularTileProvider,
             Provider<DndTile> dndTileProvider,
@@ -113,6 +116,7 @@
         mCustomTileBuilderProvider = customTileBuilderProvider;
 
         mWifiTileProvider = wifiTileProvider;
+        mInternetTileProvider = internetTileProvider;
         mBluetoothTileProvider = bluetoothTileProvider;
         mCellularTileProvider = cellularTileProvider;
         mDndTileProvider = dndTileProvider;
@@ -148,6 +152,8 @@
         switch (tileSpec) {
             case "wifi":
                 return mWifiTileProvider.get();
+            case "internet":
+                return mInternetTileProvider.get();
             case "bt":
                 return mBluetoothTileProvider.get();
             case "cell":
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
new file mode 100644
index 0000000..86524f5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/InternetTile.java
@@ -0,0 +1,449 @@
+/*
+ * 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.systemui.qs.tiles;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.service.quicksettings.Tile;
+import android.text.Html;
+import android.text.TextUtils;
+import android.util.Log;
+import android.widget.Switch;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.settingslib.graph.SignalDrawable;
+import com.android.settingslib.net.DataUsageController;
+import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.qs.QSIconView;
+import com.android.systemui.plugins.qs.QSTile;
+import com.android.systemui.plugins.qs.QSTile.Icon;
+import com.android.systemui.plugins.qs.QSTile.SignalState;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.qs.AlphaControlledSignalTileView;
+import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.logging.QSLogger;
+import com.android.systemui.qs.tileimpl.QSTileImpl;
+import com.android.systemui.statusbar.policy.NetworkController;
+import com.android.systemui.statusbar.policy.NetworkController.IconState;
+import com.android.systemui.statusbar.policy.NetworkController.SignalCallback;
+import com.android.systemui.statusbar.policy.WifiIcons;
+
+import javax.inject.Inject;
+
+/** Quick settings tile: Internet **/
+public class InternetTile extends QSTileImpl<SignalState> {
+    private static final Intent WIFI_SETTINGS = new Intent(Settings.ACTION_WIFI_SETTINGS);
+
+    protected final NetworkController mController;
+    private final DataUsageController mDataController;
+    private final QSTile.SignalState mStateBeforeClick = newTileState();
+    // The last updated tile state, 0: mobile, 1: wifi
+    private int mLastTileState = -1;
+
+    protected final InternetSignalCallback mSignalCallback = new InternetSignalCallback();
+
+    @Inject
+    public InternetTile(
+            QSHost host,
+            @Background Looper backgroundLooper,
+            @Main Handler mainHandler,
+            MetricsLogger metricsLogger,
+            StatusBarStateController statusBarStateController,
+            ActivityStarter activityStarter,
+            QSLogger qsLogger,
+            NetworkController networkController
+    ) {
+        super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController,
+                activityStarter, qsLogger);
+        mController = networkController;
+        mDataController = mController.getMobileDataController();
+        mController.observe(getLifecycle(), mSignalCallback);
+    }
+
+    @Override
+    public SignalState newTileState() {
+        return new SignalState();
+    }
+
+    @Override
+    public QSIconView createTileView(Context context) {
+        return new AlphaControlledSignalTileView(context);
+    }
+
+    @Override
+    public Intent getLongClickIntent() {
+        return WIFI_SETTINGS;
+    }
+
+    @Override
+    protected void handleClick() {
+        mActivityStarter.postStartActivityDismissingKeyguard(WIFI_SETTINGS, 0);
+    }
+
+    @Override
+    public CharSequence getTileLabel() {
+        return mContext.getString(R.string.quick_settings_internet_label);
+    }
+
+    @Override
+    public int getMetricsCategory() {
+        return MetricsEvent.QS_WIFI;
+    }
+
+    @Override
+    public boolean isAvailable() {
+        return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI)
+                || (mController.hasMobileDataFeature()
+                        && mHost.getUserContext().getUserId() == UserHandle.USER_SYSTEM);
+    }
+
+    private CharSequence getSecondaryLabel(boolean isTransient, String statusLabel) {
+        return isTransient
+                ? mContext.getString(R.string.quick_settings_wifi_secondary_label_transient)
+                : statusLabel;
+    }
+
+    private static String removeDoubleQuotes(String string) {
+        if (string == null) return null;
+        final int length = string.length();
+        if ((length > 1) && (string.charAt(0) == '"') && (string.charAt(length - 1) == '"')) {
+            return string.substring(1, length - 1);
+        }
+        return string;
+    }
+
+    private static final class WifiCallbackInfo {
+        boolean mEnabled;
+        boolean mConnected;
+        int mWifiSignalIconId;
+        String mSsid;
+        boolean mActivityIn;
+        boolean mActivityOut;
+        String mWifiSignalContentDescription;
+        boolean mIsTransient;
+        public String mStatusLabel;
+
+        @Override
+        public String toString() {
+            return new StringBuilder("WifiCallbackInfo[")
+                    .append("mEnabled=").append(mEnabled)
+                    .append(",mConnected=").append(mConnected)
+                    .append(",mWifiSignalIconId=").append(mWifiSignalIconId)
+                    .append(",mSsid=").append(mSsid)
+                    .append(",mActivityIn=").append(mActivityIn)
+                    .append(",mActivityOut=").append(mActivityOut)
+                    .append(",mWifiSignalContentDescription=").append(mWifiSignalContentDescription)
+                    .append(",mIsTransient=").append(mIsTransient)
+                    .append(']').toString();
+        }
+    }
+
+    private static final class CellularCallbackInfo {
+        boolean mAirplaneModeEnabled;
+        CharSequence mDataSubscriptionName;
+        CharSequence mDataContentDescription;
+        int mMobileSignalIconId;
+        boolean mActivityIn;
+        boolean mActivityOut;
+        boolean mNoSim;
+        boolean mRoaming;
+        boolean mMultipleSubs;
+
+        @Override
+        public String toString() {
+            return new StringBuilder("CellularCallbackInfo[")
+                .append("mAirplaneModeEnabled=").append(mAirplaneModeEnabled)
+                .append(",mDataSubscriptionName=").append(mDataSubscriptionName)
+                .append(",mDataContentDescription=").append(mDataContentDescription)
+                .append(",mMobileSignalIconId=").append(mMobileSignalIconId)
+                .append(",mActivityIn=").append(mActivityIn)
+                .append(",mActivityOut=").append(mActivityOut)
+                .append(",mNoSim=").append(mNoSim)
+                .append(",mRoaming=").append(mRoaming)
+                .append(",mMultipleSubs=").append(mMultipleSubs)
+                .append(']').toString();
+        }
+    }
+
+    protected final class InternetSignalCallback implements SignalCallback {
+        final WifiCallbackInfo mWifiInfo = new WifiCallbackInfo();
+        final CellularCallbackInfo mCellularInfo = new CellularCallbackInfo();
+
+        @Override
+        public void setWifiIndicators(boolean enabled, IconState statusIcon, IconState qsIcon,
+                boolean activityIn, boolean activityOut, String description, boolean isTransient,
+                String statusLabel) {
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.d(TAG, "setWifiIndicators: "
+                        + "enabled = " + enabled + ","
+                        + "statusIcon = " + (statusIcon == null ? "" : statusIcon.toString()) + ","
+                        + "qsIcon = " + (qsIcon == null ? "" : qsIcon.toString()) + ","
+                        + "activityIn = " + activityIn + ","
+                        + "activityOut = " + activityOut + ","
+                        + "description = " + description + ","
+                        + "isTransient = " + isTransient + ","
+                        + "statusLabel = " + statusLabel);
+            }
+            mWifiInfo.mEnabled = enabled;
+            mWifiInfo.mConnected = qsIcon.visible;
+            mWifiInfo.mWifiSignalIconId = qsIcon.icon;
+            mWifiInfo.mSsid = description;
+            mWifiInfo.mActivityIn = activityIn;
+            mWifiInfo.mActivityOut = activityOut;
+            mWifiInfo.mWifiSignalContentDescription = qsIcon.contentDescription;
+            mWifiInfo.mIsTransient = isTransient;
+            mWifiInfo.mStatusLabel = statusLabel;
+            refreshState(mWifiInfo);
+        }
+
+        @Override
+        public void setMobileDataIndicators(IconState statusIcon, IconState qsIcon, int statusType,
+                int qsType, boolean activityIn, boolean activityOut,
+                CharSequence typeContentDescription,
+                CharSequence typeContentDescriptionHtml, CharSequence description,
+                boolean isWide, int subId, boolean roaming) {
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.d(TAG, "setMobileDataIndicators: "
+                        + "statusIcon = " + (statusIcon == null ? "" :  statusIcon.toString()) + ","
+                        + "qsIcon = " + (qsIcon == null ? "" : qsIcon.toString()) + ","
+                        + "statusType = " + statusType + ","
+                        + "qsType = " + qsType + ","
+                        + "activityIn = " + activityIn + ","
+                        + "activityOut = " + activityOut + ","
+                        + "typeContentDescription = " + typeContentDescription + ","
+                        + "typeContentDescriptionHtml = " + typeContentDescriptionHtml + ","
+                        + "description = " + description + ","
+                        + "isWide = " + isWide + ","
+                        + "subId = " + subId + ","
+                        + "roaming = " + roaming);
+            }
+            if (qsIcon == null) {
+                // Not data sim, don't display.
+                return;
+            }
+            mCellularInfo.mDataSubscriptionName = mController.getMobileDataNetworkName();
+            mCellularInfo.mDataContentDescription =
+                    (description != null) ? typeContentDescriptionHtml : null;
+            mCellularInfo.mMobileSignalIconId = qsIcon.icon;
+            mCellularInfo.mActivityIn = activityIn;
+            mCellularInfo.mActivityOut = activityOut;
+            mCellularInfo.mRoaming = roaming;
+            mCellularInfo.mMultipleSubs = mController.getNumberSubscriptions() > 1;
+            refreshState(mCellularInfo);
+        }
+
+        @Override
+        public void setNoSims(boolean show, boolean simDetected) {
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.d(TAG, "setNoSims: "
+                        + "show = " + show + ","
+                        + "simDetected = " + simDetected);
+            }
+            mCellularInfo.mNoSim = show;
+            if (mCellularInfo.mNoSim) {
+                // Make sure signal gets cleared out when no sims.
+                mCellularInfo.mMobileSignalIconId = 0;
+            }
+            refreshState(mCellularInfo);
+        }
+
+        @Override
+        public void setIsAirplaneMode(IconState icon) {
+            if (Log.isLoggable(TAG, Log.DEBUG)) {
+                Log.d(TAG, "InternetTile-setIsAirplaneMode: "
+                        + "icon = " + (icon == null ? "" : icon.toString()));
+            }
+            mCellularInfo.mAirplaneModeEnabled = icon.visible;
+            refreshState(mCellularInfo);
+        }
+    }
+
+    @Override
+    protected void handleUpdateState(SignalState state, Object arg) {
+        Log.d(TAG, "handleUpdateState: " + "arg = " + arg);
+        if (arg instanceof CellularCallbackInfo) {
+            mLastTileState = 0;
+            handleUpdateCellularState(state, arg);
+        } else if (arg instanceof WifiCallbackInfo) {
+            mLastTileState = 1;
+            handleUpdateWifiState(state, arg);
+        } else {
+            // handleUpdateState will be triggered when user expands the QuickSetting panel with
+            // arg = null, in this case the last updated CellularCallbackInfo or WifiCallbackInfo
+            // should be used to refresh the tile.
+            if (mLastTileState == 0) {
+                handleUpdateCellularState(state, mSignalCallback.mCellularInfo);
+            } else if (mLastTileState == 1) {
+                handleUpdateWifiState(state, mSignalCallback.mWifiInfo);
+            }
+        }
+    }
+
+    private void handleUpdateWifiState(SignalState state, Object arg) {
+        WifiCallbackInfo cb = (WifiCallbackInfo) arg;
+        boolean wifiConnected = cb.mEnabled && (cb.mWifiSignalIconId > 0) && (cb.mSsid != null);
+        boolean wifiNotConnected = (cb.mWifiSignalIconId > 0) && (cb.mSsid == null);
+        boolean enabledChanging = state.value != cb.mEnabled;
+        if (enabledChanging) {
+            fireToggleStateChanged(cb.mEnabled);
+        }
+        if (state.slash == null) {
+            state.slash = new SlashState();
+            state.slash.rotation = 6;
+        }
+        state.slash.isSlashed = false;
+        state.secondaryLabel = getSecondaryLabel(cb.mIsTransient, removeDoubleQuotes(cb.mSsid));
+        state.state = Tile.STATE_ACTIVE;
+        state.dualTarget = true;
+        state.value = cb.mEnabled;
+        state.activityIn = cb.mEnabled && cb.mActivityIn;
+        state.activityOut = cb.mEnabled && cb.mActivityOut;
+        final StringBuffer minimalContentDescription = new StringBuffer();
+        final StringBuffer minimalStateDescription = new StringBuffer();
+        final Resources r = mContext.getResources();
+        // TODO(b/174753536): Use the new "Internet" string as state.label once available.
+        if (cb.mIsTransient) {
+            state.icon = ResourceIcon.get(
+                com.android.internal.R.drawable.ic_signal_wifi_transient_animation);
+            state.label = r.getString(R.string.quick_settings_internet_label);
+        } else if (!state.value) {
+            state.slash.isSlashed = true;
+            state.state = Tile.STATE_INACTIVE;
+            state.icon = ResourceIcon.get(WifiIcons.QS_WIFI_DISABLED);
+            state.label = r.getString(R.string.quick_settings_internet_label);
+        } else if (wifiConnected) {
+            state.icon = ResourceIcon.get(cb.mWifiSignalIconId);
+            state.label = r.getString(R.string.quick_settings_internet_label);
+        } else if (wifiNotConnected) {
+            state.icon = ResourceIcon.get(WifiIcons.QS_WIFI_NO_NETWORK);
+            state.label = r.getString(R.string.quick_settings_internet_label);
+        } else {
+            state.icon = ResourceIcon.get(WifiIcons.QS_WIFI_NO_NETWORK);
+            state.label = r.getString(R.string.quick_settings_internet_label);
+        }
+        minimalContentDescription.append(
+            mContext.getString(R.string.quick_settings_internet_label)).append(",");
+        if (state.value) {
+            if (wifiConnected) {
+                minimalStateDescription.append(cb.mWifiSignalContentDescription);
+                minimalContentDescription.append(removeDoubleQuotes(cb.mSsid));
+                if (!TextUtils.isEmpty(state.secondaryLabel)) {
+                    minimalContentDescription.append(",").append(state.secondaryLabel);
+                }
+            }
+        }
+        state.stateDescription = minimalStateDescription.toString();
+        state.contentDescription = minimalContentDescription.toString();
+        state.dualLabelContentDescription = r.getString(
+                R.string.accessibility_quick_settings_open_settings, getTileLabel());
+        state.expandedAccessibilityClassName = Switch.class.getName();
+    }
+
+    private void handleUpdateCellularState(SignalState state, Object arg) {
+        CellularCallbackInfo cb = (CellularCallbackInfo) arg;
+        final Resources r = mContext.getResources();
+        // TODO(b/174753536): Use the new "Internet" string as state.label once available.
+        state.label = r.getString(R.string.quick_settings_internet_label);
+        boolean mobileDataEnabled = mDataController.isMobileDataSupported()
+                && mDataController.isMobileDataEnabled();
+        state.value = mobileDataEnabled;
+        state.activityIn = mobileDataEnabled && cb.mActivityIn;
+        state.activityOut = mobileDataEnabled && cb.mActivityOut;
+        state.expandedAccessibilityClassName = Switch.class.getName();
+        if (cb.mNoSim) {
+            state.icon = ResourceIcon.get(R.drawable.ic_qs_no_sim);
+        } else {
+            state.icon = new SignalIcon(cb.mMobileSignalIconId);
+        }
+
+        if (cb.mNoSim) {
+            state.state = Tile.STATE_UNAVAILABLE;
+            state.secondaryLabel = r.getString(R.string.keyguard_missing_sim_message_short);
+        } else if (cb.mAirplaneModeEnabled) {
+            state.state = Tile.STATE_UNAVAILABLE;
+            state.secondaryLabel = r.getString(R.string.status_bar_airplane);
+        } else if (mobileDataEnabled) {
+            state.state = Tile.STATE_ACTIVE;
+            state.secondaryLabel = appendMobileDataType(cb.mDataSubscriptionName,
+                    getMobileDataContentName(cb));
+        } else {
+            state.state = Tile.STATE_INACTIVE;
+            state.secondaryLabel = r.getString(R.string.cell_data_off);
+        }
+
+        state.contentDescription = state.label;
+        if (state.state == Tile.STATE_INACTIVE) {
+            // This information is appended later by converting the Tile.STATE_INACTIVE state.
+            state.stateDescription = "";
+        } else {
+            state.stateDescription = state.secondaryLabel;
+        }
+    }
+
+    private CharSequence appendMobileDataType(CharSequence current, CharSequence dataType) {
+        if (TextUtils.isEmpty(dataType)) {
+            return Html.fromHtml((current == null ? "" : current.toString()), 0);
+        }
+        if (TextUtils.isEmpty(current)) {
+            return Html.fromHtml((dataType == null ? "" : dataType.toString()), 0);
+        }
+        String concat = mContext.getString(R.string.mobile_carrier_text_format, current, dataType);
+        return Html.fromHtml(concat, 0);
+    }
+
+    private CharSequence getMobileDataContentName(CellularCallbackInfo cb) {
+        if (cb.mRoaming && !TextUtils.isEmpty(cb.mDataContentDescription)) {
+            String roaming = mContext.getString(R.string.data_connection_roaming);
+            String dataDescription =
+                    cb.mDataContentDescription == null ? ""
+                            : cb.mDataContentDescription.toString();
+            return mContext.getString(R.string.mobile_data_text_format, roaming, dataDescription);
+        }
+        if (cb.mRoaming) {
+            return mContext.getString(R.string.data_connection_roaming);
+        }
+        return cb.mDataContentDescription;
+    }
+
+    private static class SignalIcon extends Icon {
+        private final int mState;
+        SignalIcon(int state) {
+            mState = state;
+        }
+        public int getState() {
+            return mState;
+        }
+
+        @Override
+        public Drawable getDrawable(Context context) {
+            SignalDrawable d = new SignalDrawable(context);
+            d.setLevel(getState());
+            return d;
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ImageExporter.java b/packages/SystemUI/src/com/android/systemui/screenshot/ImageExporter.java
new file mode 100644
index 0000000..f77431a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ImageExporter.java
@@ -0,0 +1,356 @@
+/*
+ * 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.systemui.screenshot;
+
+import static android.os.FileUtils.closeQuietly;
+
+import android.annotation.IntRange;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.CompressFormat;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Environment;
+import android.os.ParcelFileDescriptor;
+import android.os.SystemClock;
+import android.os.Trace;
+import android.provider.MediaStore;
+import android.util.Log;
+
+import androidx.concurrent.futures.CallbackToFutureAdapter;
+import androidx.exifinterface.media.ExifInterface;
+
+import com.google.common.util.concurrent.ListenableFuture;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.time.Duration;
+import java.time.Instant;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.concurrent.Executor;
+
+import javax.inject.Inject;
+
+class ImageExporter {
+    private static final String TAG = LogConfig.logTag(ImageExporter.class);
+
+    static final Duration PENDING_ENTRY_TTL = Duration.ofHours(24);
+
+    // ex: 'Screenshot_20201215-090626.png'
+    private static final String FILENAME_PATTERN = "Screenshot_%1$tY%<tm%<td-%<tH%<tM%<tS.%2$s";
+    private static final String SCREENSHOTS_PATH = Environment.DIRECTORY_PICTURES
+            + File.separator + Environment.DIRECTORY_SCREENSHOTS;
+
+    private static final String RESOLVER_INSERT_RETURNED_NULL =
+            "ContentResolver#insert returned null.";
+    private static final String RESOLVER_OPEN_FILE_RETURNED_NULL =
+            "ContentResolver#openFile returned null.";
+    private static final String RESOLVER_OPEN_FILE_EXCEPTION =
+            "ContentResolver#openFile threw an exception.";
+    private static final String OPEN_OUTPUT_STREAM_EXCEPTION =
+            "ContentResolver#openOutputStream threw an exception.";
+    private static final String EXIF_READ_EXCEPTION =
+            "ExifInterface threw an exception reading from the file descriptor.";
+    private static final String EXIF_WRITE_EXCEPTION =
+            "ExifInterface threw an exception writing to the file descriptor.";
+    private static final String RESOLVER_UPDATE_ZERO_ROWS =
+            "Failed to publishEntry. ContentResolver#update reported no rows updated.";
+    private static final String IMAGE_COMPRESS_RETURNED_FALSE =
+            "Bitmap.compress returned false. (Failure unknown)";
+
+    private final ContentResolver mResolver;
+    private CompressFormat mCompressFormat = CompressFormat.PNG;
+    private int mQuality = 100;
+
+    @Inject
+    ImageExporter(ContentResolver resolver) {
+        mResolver = resolver;
+    }
+
+    /**
+     * Adjusts the output image format. This also determines extension of the filename created. The
+     * default is {@link CompressFormat#PNG PNG}.
+     *
+     * @see CompressFormat
+     *
+     * @param format the image format for export
+     */
+    void setFormat(CompressFormat format) {
+        mCompressFormat = format;
+    }
+
+    /**
+     * Sets the quality format. The exact meaning is dependent on the {@link CompressFormat} used.
+     *
+     * @param quality the 'quality' level between 0 and 100
+     */
+    void setQuality(@IntRange(from = 0, to = 100) int quality) {
+        mQuality = quality;
+    }
+
+    /**
+     * Export the image using the given executor.
+     *
+     * @param executor the thread for execution
+     * @param bitmap the bitmap to export
+     *
+     * @return a listenable future result
+     */
+    ListenableFuture<Uri> export(Executor executor, Bitmap bitmap) {
+        return export(executor, bitmap, ZonedDateTime.now());
+    }
+
+    /**
+     * Export the image using the given executor.
+     *
+     * @param executor the thread for execution
+     * @param bitmap the bitmap to export
+     *
+     * @return a listenable future result
+     */
+    ListenableFuture<Uri> export(Executor executor, Bitmap bitmap, ZonedDateTime captureTime) {
+        final Task task = new Task(mResolver, bitmap, captureTime, mCompressFormat, mQuality);
+        return CallbackToFutureAdapter.getFuture(
+                (completer) -> {
+                    executor.execute(() -> {
+                        try {
+                            completer.set(task.execute());
+                        } catch (ImageExportException | InterruptedException e) {
+                            completer.setException(e);
+                        }
+                    });
+                    return task;
+                }
+        );
+    }
+
+    private static class Task {
+        private final ContentResolver mResolver;
+        private final ZonedDateTime mCaptureTime;
+        private final CompressFormat mFormat;
+        private final int mQuality;
+        private final Bitmap mBitmap;
+
+        Task(ContentResolver resolver, Bitmap bitmap, ZonedDateTime captureTime,
+                CompressFormat format, int quality) {
+            mResolver = resolver;
+            mBitmap = bitmap;
+            mCaptureTime = captureTime;
+            mFormat = format;
+            mQuality = quality;
+        }
+
+        public Uri execute() throws ImageExportException, InterruptedException {
+            Trace.beginSection("ImageExporter_execute");
+            Uri uri = null;
+            Instant start = null;
+            try {
+                if (LogConfig.DEBUG_STORAGE) {
+                    Log.d(TAG, "image export started");
+                    start = Instant.now();
+                }
+                uri = createEntry(mFormat, mCaptureTime);
+                throwIfInterrupted();
+
+                writeImage(mBitmap, mFormat, mQuality, uri);
+                throwIfInterrupted();
+
+                writeExif(uri, mBitmap.getWidth(), mBitmap.getHeight(), mCaptureTime);
+                throwIfInterrupted();
+
+                publishEntry(uri);
+
+                if (LogConfig.DEBUG_STORAGE) {
+                    Log.d(TAG, "image export completed: "
+                            + Duration.between(start, Instant.now()).toMillis() + " ms");
+                }
+            } catch (ImageExportException e) {
+                if (uri != null) {
+                    mResolver.delete(uri, null);
+                }
+                throw e;
+            } finally {
+                Trace.endSection();
+            }
+            return uri;
+        }
+
+        Uri createEntry(CompressFormat format, ZonedDateTime time) throws ImageExportException {
+            Trace.beginSection("ImageExporter_createEntry");
+            try {
+                final ContentValues values = createMetadata(time, format);
+                Uri uri = mResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
+                if (uri == null) {
+                    throw new ImageExportException(RESOLVER_INSERT_RETURNED_NULL);
+                }
+                return uri;
+            } finally {
+                Trace.endSection();
+            }
+        }
+
+        void writeImage(Bitmap bitmap, CompressFormat format, int quality,
+                Uri contentUri) throws ImageExportException {
+            Trace.beginSection("ImageExporter_writeImage");
+            try (OutputStream out = mResolver.openOutputStream(contentUri)) {
+                long start = SystemClock.elapsedRealtime();
+                if (!bitmap.compress(format, quality, out)) {
+                    throw new ImageExportException(IMAGE_COMPRESS_RETURNED_FALSE);
+                } else if (LogConfig.DEBUG_STORAGE) {
+                    Log.d(TAG, "Bitmap.compress took "
+                            + (SystemClock.elapsedRealtime() - start) + " ms");
+                }
+            } catch (IOException ex) {
+                throw new ImageExportException(OPEN_OUTPUT_STREAM_EXCEPTION, ex);
+            } finally {
+                Trace.endSection();
+            }
+        }
+
+        void writeExif(Uri uri, int width, int height, ZonedDateTime captureTime)
+                throws ImageExportException {
+            Trace.beginSection("ImageExporter_writeExif");
+            ParcelFileDescriptor pfd = null;
+            try {
+                pfd = mResolver.openFile(uri, "rw", null);
+                if (pfd == null) {
+                    throw new ImageExportException(RESOLVER_OPEN_FILE_RETURNED_NULL);
+                }
+                ExifInterface exif;
+                try {
+                    exif = new ExifInterface(pfd.getFileDescriptor());
+                } catch (IOException e) {
+                    throw new ImageExportException(EXIF_READ_EXCEPTION, e);
+                }
+
+                updateExifAttributes(exif, width, height, captureTime);
+                try {
+                    exif.saveAttributes();
+                } catch (IOException e) {
+                    throw new ImageExportException(EXIF_WRITE_EXCEPTION, e);
+                }
+            } catch (FileNotFoundException e) {
+                throw new ImageExportException(RESOLVER_OPEN_FILE_EXCEPTION, e);
+            } finally {
+                closeQuietly(pfd);
+                Trace.endSection();
+            }
+        }
+
+        void publishEntry(Uri uri) throws ImageExportException {
+            Trace.beginSection("ImageExporter_publishEntry");
+            try {
+                ContentValues values = new ContentValues();
+                values.put(MediaStore.MediaColumns.IS_PENDING, 0);
+                values.putNull(MediaStore.MediaColumns.DATE_EXPIRES);
+                final int rowsUpdated = mResolver.update(uri, values, /* extras */ null);
+                if (rowsUpdated < 1) {
+                    throw new ImageExportException(RESOLVER_UPDATE_ZERO_ROWS);
+                }
+            } finally {
+                Trace.endSection();
+            }
+        }
+
+        @Override
+        public String toString() {
+            return "compress [" + mBitmap + "] to [" + mFormat + "] at quality " + mQuality;
+        }
+    }
+
+    static String createFilename(ZonedDateTime time, CompressFormat format) {
+        return String.format(FILENAME_PATTERN, time, fileExtension(format));
+    }
+
+    static ContentValues createMetadata(ZonedDateTime captureTime, CompressFormat format) {
+        ContentValues values = new ContentValues();
+        values.put(MediaStore.MediaColumns.RELATIVE_PATH, SCREENSHOTS_PATH);
+        values.put(MediaStore.MediaColumns.DISPLAY_NAME, createFilename(captureTime, format));
+        values.put(MediaStore.MediaColumns.MIME_TYPE, getMimeType(format));
+        values.put(MediaStore.MediaColumns.DATE_ADDED, captureTime.toEpochSecond());
+        values.put(MediaStore.MediaColumns.DATE_MODIFIED, captureTime.toEpochSecond());
+        values.put(MediaStore.MediaColumns.DATE_EXPIRES,
+                captureTime.plus(PENDING_ENTRY_TTL).toEpochSecond());
+        values.put(MediaStore.MediaColumns.IS_PENDING, 1);
+        return values;
+    }
+
+    static void updateExifAttributes(ExifInterface exif, int width, int height,
+            ZonedDateTime captureTime) {
+        exif.setAttribute(ExifInterface.TAG_SOFTWARE, "Android " + Build.DISPLAY);
+        exif.setAttribute(ExifInterface.TAG_IMAGE_WIDTH, Integer.toString(width));
+        exif.setAttribute(ExifInterface.TAG_IMAGE_LENGTH, Integer.toString(height));
+
+        String dateTime = DateTimeFormatter.ofPattern("yyyy:MM:dd HH:mm:ss").format(captureTime);
+        String subSec = DateTimeFormatter.ofPattern("SSS").format(captureTime);
+        String timeZone = DateTimeFormatter.ofPattern("xxx").format(captureTime);
+
+        exif.setAttribute(ExifInterface.TAG_DATETIME_ORIGINAL, dateTime);
+        exif.setAttribute(ExifInterface.TAG_SUBSEC_TIME_ORIGINAL, subSec);
+        exif.setAttribute(ExifInterface.TAG_OFFSET_TIME_ORIGINAL, timeZone);
+    }
+
+    static String getMimeType(CompressFormat format) {
+        switch (format) {
+            case JPEG:
+                return "image/jpeg";
+            case PNG:
+                return "image/png";
+            case WEBP:
+            case WEBP_LOSSLESS:
+            case WEBP_LOSSY:
+                return "image/webp";
+            default:
+                throw new IllegalArgumentException("Unknown CompressFormat!");
+        }
+    }
+
+    static String fileExtension(CompressFormat format) {
+        switch (format) {
+            case JPEG:
+                return "jpg";
+            case PNG:
+                return "png";
+            case WEBP:
+            case WEBP_LOSSY:
+            case WEBP_LOSSLESS:
+                return "webp";
+            default:
+                throw new IllegalArgumentException("Unknown CompressFormat!");
+        }
+    }
+
+    private static void throwIfInterrupted() throws InterruptedException {
+        if (Thread.currentThread().isInterrupted()) {
+            throw new InterruptedException();
+        }
+    }
+
+    static final class ImageExportException extends IOException {
+        ImageExportException(String message) {
+            super(message);
+        }
+
+        ImageExportException(String message, Throwable cause) {
+            super(message, cause);
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ImageTile.java b/packages/SystemUI/src/com/android/systemui/screenshot/ImageTile.java
new file mode 100644
index 0000000..143121a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ImageTile.java
@@ -0,0 +1,113 @@
+/*
+ * 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.systemui.screenshot;
+
+import static android.graphics.ColorSpace.Named.SRGB;
+
+import static java.util.Objects.requireNonNull;
+
+import android.graphics.Bitmap;
+import android.graphics.ColorSpace;
+import android.graphics.RecordingCanvas;
+import android.graphics.Rect;
+import android.graphics.RenderNode;
+import android.media.Image;
+
+/**
+ * Holds a hardware image, coordinates and render node to draw the tile. The tile manages clipping
+ * and dimensions. The tile must be drawn translated to the correct target position:
+ * <pre>
+ *     ImageTile tile = getTile();
+ *     canvas.save();
+ *     canvas.translate(tile.getLeft(), tile.getTop());
+ *     canvas.drawRenderNode(tile.getDisplayList());
+ *     canvas.restore();
+ * </pre>
+ */
+class ImageTile implements AutoCloseable {
+    private final Image mImage;
+    private final Rect mLocation;
+    private RenderNode mNode;
+
+    private static final ColorSpace COLOR_SPACE = ColorSpace.get(SRGB);
+
+    /**
+     * Create an image tile from the given image.
+     *
+     * @param image an image containing a hardware buffer
+     * @param location the captured area represented by image tile (virtual coordinates)
+     */
+    ImageTile(Image image, Rect location) {
+        mImage = requireNonNull(image, "image");
+        mLocation = location;
+
+        requireNonNull(mImage.getHardwareBuffer(), "image must be a hardware image");
+    }
+
+    RenderNode getDisplayList() {
+        if (mNode == null) {
+            mNode = new RenderNode("Tile{" + Integer.toHexString(mImage.hashCode()) + "}");
+        }
+        if (mNode.hasDisplayList()) {
+            return mNode;
+        }
+        final int w = Math.min(mImage.getWidth(), mLocation.width());
+        final int h = Math.min(mImage.getHeight(), mLocation.height());
+        mNode.setPosition(0, 0, w, h);
+
+        RecordingCanvas canvas = mNode.beginRecording(w, h);
+        Rect rect = new Rect(0, 0, w, h);
+        canvas.save();
+        canvas.clipRect(0, 0, mLocation.right, mLocation.bottom);
+        canvas.drawBitmap(Bitmap.wrapHardwareBuffer(mImage.getHardwareBuffer(), COLOR_SPACE),
+                0, 0, null);
+        canvas.restore();
+        mNode.endRecording();
+        return mNode;
+    }
+
+    Rect getLocation() {
+        return mLocation;
+    }
+
+    int getLeft() {
+        return mLocation.left;
+    }
+
+    int getTop() {
+        return mLocation.top;
+    }
+
+    int getRight() {
+        return mLocation.right;
+    }
+
+    int getBottom() {
+        return mLocation.bottom;
+    }
+
+    @Override
+    public void close() {
+        mImage.close();
+        mNode.discardDisplayList();
+    }
+
+    @Override
+    public String toString() {
+        return "{location=" + mLocation + ", source=" + mImage
+                + ", buffer=" + mImage.getHardwareBuffer() + "}";
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ImageTileSet.java b/packages/SystemUI/src/com/android/systemui/screenshot/ImageTileSet.java
new file mode 100644
index 0000000..8ff66f5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ImageTileSet.java
@@ -0,0 +1,163 @@
+/*
+ * 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.systemui.screenshot;
+
+import android.graphics.Bitmap;
+import android.graphics.HardwareRenderer;
+import android.graphics.RecordingCanvas;
+import android.graphics.Rect;
+import android.graphics.RenderNode;
+import android.graphics.drawable.Drawable;
+
+import androidx.annotation.UiThread;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Owns a series of partial screen captures (tiles).
+ * <p>
+ * To display on-screen, use {@link #getDrawable()}.
+ */
+@UiThread
+class ImageTileSet {
+
+    private static final String TAG = "ImageTileSet";
+
+    interface OnBoundsChangedListener {
+        /**
+         * Reports an update to the bounding box that contains all active tiles. These are virtual
+         * (capture) coordinates which can be either negative or positive.
+         */
+        void onBoundsChanged(int left, int top, int right, int bottom);
+    }
+
+    interface OnContentChangedListener {
+        /**
+         * Mark as dirty and rebuild display list.
+         */
+        void onContentChanged();
+    }
+
+    private final List<ImageTile> mTiles = new ArrayList<>();
+    private final Rect mBounds = new Rect();
+
+    private OnContentChangedListener mOnContentChangedListener;
+    private OnBoundsChangedListener mOnBoundsChangedListener;
+
+    void setOnBoundsChangedListener(OnBoundsChangedListener listener) {
+        mOnBoundsChangedListener = listener;
+    }
+
+    void setOnContentChangedListener(OnContentChangedListener listener) {
+        mOnContentChangedListener = listener;
+    }
+
+    void addTile(ImageTile tile) {
+        final Rect newBounds = new Rect(mBounds);
+        final Rect newRect = tile.getLocation();
+        mTiles.add(tile);
+        newBounds.union(newRect);
+        if (!newBounds.equals(mBounds)) {
+            mBounds.set(newBounds);
+            if (mOnBoundsChangedListener != null) {
+                mOnBoundsChangedListener.onBoundsChanged(
+                        newBounds.left, newBounds.top, newBounds.right, newBounds.bottom);
+            }
+        }
+        if (mOnContentChangedListener != null) {
+            mOnContentChangedListener.onContentChanged();
+        }
+    }
+
+    /**
+     * Returns a drawable to paint the combined contents of the tiles. Drawable dimensions are
+     * zero-based and map directly to {@link #getLeft()}, {@link #getTop()}, {@link #getRight()},
+     * and {@link #getBottom()} which are dimensions relative to the capture start position
+     * (positive or negative).
+     *
+     * @return a drawable to display the image content
+     */
+    Drawable getDrawable() {
+        return new TiledImageDrawable(this);
+    }
+
+    boolean  isEmpty() {
+        return mTiles.isEmpty();
+    }
+
+    int size() {
+        return mTiles.size();
+    }
+
+    ImageTile get(int i) {
+        return mTiles.get(i);
+    }
+
+    Bitmap toBitmap() {
+        if (mTiles.isEmpty()) {
+            return null;
+        }
+        final RenderNode output = new RenderNode("Bitmap Export");
+        output.setPosition(0, 0, getWidth(), getHeight());
+        RecordingCanvas canvas = output.beginRecording();
+        canvas.translate(-getLeft(), -getTop());
+        for (ImageTile tile : mTiles) {
+            canvas.save();
+            canvas.translate(tile.getLeft(), tile.getTop());
+            canvas.drawRenderNode(tile.getDisplayList());
+            canvas.restore();
+        }
+        output.endRecording();
+        return HardwareRenderer.createHardwareBitmap(output, getWidth(), getHeight());
+    }
+
+    int getLeft() {
+        return mBounds.left;
+    }
+
+    int getTop() {
+        return mBounds.top;
+    }
+
+    int getRight() {
+        return mBounds.right;
+    }
+
+    int getBottom() {
+        return mBounds.bottom;
+    }
+
+    int getWidth() {
+        return mBounds.width();
+    }
+
+    int getHeight() {
+        return mBounds.height();
+    }
+
+    void clear() {
+        mBounds.set(0, 0, 0, 0);
+        mTiles.forEach(ImageTile::close);
+        mTiles.clear();
+        if (mOnBoundsChangedListener != null) {
+            mOnBoundsChangedListener.onBoundsChanged(0, 0, 0, 0);
+        }
+        if (mOnContentChangedListener != null) {
+            mOnContentChangedListener.onContentChanged();
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
index 3346935..db2750b 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/SaveImageInBackgroundTask.java
@@ -27,8 +27,6 @@
 import android.content.ClipData;
 import android.content.ClipDescription;
 import android.content.ComponentName;
-import android.content.ContentResolver;
-import android.content.ContentValues;
 import android.content.Context;
 import android.content.Intent;
 import android.content.res.Resources;
@@ -36,43 +34,26 @@
 import android.graphics.drawable.Icon;
 import android.net.Uri;
 import android.os.AsyncTask;
-import android.os.Build;
 import android.os.Bundle;
-import android.os.Environment;
 import android.os.Handler;
-import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
 import android.provider.DeviceConfig;
-import android.provider.MediaStore;
-import android.provider.MediaStore.MediaColumns;
 import android.text.TextUtils;
-import android.text.format.DateUtils;
 import android.util.Log;
 
-import androidx.exifinterface.media.ExifInterface;
-
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
 import com.android.systemui.R;
 import com.android.systemui.SystemUIFactory;
 import com.android.systemui.screenshot.ScreenshotController.SavedImageData.ShareTransition;
 
-import java.io.File;
-import java.io.IOException;
-import java.io.OutputStream;
 import java.text.DateFormat;
 import java.text.SimpleDateFormat;
-import java.time.Instant;
-import java.time.ZoneId;
-import java.time.ZoneOffset;
-import java.time.ZonedDateTime;
-import java.time.format.DateTimeFormatter;
 import java.util.ArrayList;
 import java.util.Date;
 import java.util.List;
-import java.util.Objects;
 import java.util.Random;
 import java.util.UUID;
 import java.util.concurrent.CompletableFuture;
@@ -99,14 +80,17 @@
     private final boolean mSmartActionsEnabled;
     private final Random mRandom = new Random();
     private final Supplier<ShareTransition> mSharedElementTransition;
+    private final ImageExporter mImageExporter;
 
-    SaveImageInBackgroundTask(Context context, ScreenshotSmartActions screenshotSmartActions,
+    SaveImageInBackgroundTask(Context context, ImageExporter exporter,
+            ScreenshotSmartActions screenshotSmartActions,
             ScreenshotController.SaveImageInBackgroundData data,
             Supplier<ShareTransition> sharedElementTransition) {
         mContext = context;
         mScreenshotSmartActions = screenshotSmartActions;
         mImageData = new ScreenshotController.SavedImageData();
         mSharedElementTransition = sharedElementTransition;
+        mImageExporter = exporter;
 
         // Prepare all the output metadata
         mParams = data;
@@ -139,90 +123,17 @@
         }
         Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
 
-        ContentResolver resolver = mContext.getContentResolver();
         Bitmap image = mParams.image;
 
         try {
-            // Save the screenshot to the MediaStore
-            final ContentValues values = new ContentValues();
-            values.put(MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES
-                    + File.separator + Environment.DIRECTORY_SCREENSHOTS);
-            values.put(MediaColumns.DISPLAY_NAME, mImageFileName);
-            values.put(MediaColumns.MIME_TYPE, "image/png");
-            values.put(MediaColumns.DATE_ADDED, mImageTime / 1000);
-            values.put(MediaColumns.DATE_MODIFIED, mImageTime / 1000);
-            values.put(MediaColumns.DATE_EXPIRES, (mImageTime + DateUtils.DAY_IN_MILLIS) / 1000);
-            values.put(MediaColumns.IS_PENDING, 1);
-
-            final Uri uri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
+            // Call synchronously here since already on a background thread.
+            Uri uri = mImageExporter.export(Runnable::run, image).get();
 
             CompletableFuture<List<Notification.Action>> smartActionsFuture =
                     mScreenshotSmartActions.getSmartActionsFuture(
                             mScreenshotId, uri, image, mSmartActionsProvider,
                             mSmartActionsEnabled, getUserHandle(mContext));
 
-            try {
-                // First, write the actual data for our screenshot
-                try (OutputStream out = resolver.openOutputStream(uri)) {
-                    if (DEBUG_STORAGE) {
-                        Log.d(TAG, "Compressing PNG:"
-                                + " w=" + image.getWidth() + " h=" + image.getHeight());
-                    }
-                    if (!image.compress(Bitmap.CompressFormat.PNG, 100, out)) {
-                        if (DEBUG_STORAGE) {
-                            Log.d(TAG, "Bitmap.compress returned false");
-                        }
-                        throw new IOException("Failed to compress");
-                    }
-                    if (DEBUG_STORAGE) {
-                        Log.d(TAG, "Done compressing PNG");
-                    }
-                }
-
-                // Next, write metadata to help index the screenshot
-                try (ParcelFileDescriptor pfd = resolver.openFile(uri, "rw", null)) {
-                    final ExifInterface exif = new ExifInterface(pfd.getFileDescriptor());
-
-                    exif.setAttribute(ExifInterface.TAG_SOFTWARE,
-                            "Android " + Build.DISPLAY);
-
-                    exif.setAttribute(ExifInterface.TAG_IMAGE_WIDTH,
-                            Integer.toString(image.getWidth()));
-                    exif.setAttribute(ExifInterface.TAG_IMAGE_LENGTH,
-                            Integer.toString(image.getHeight()));
-
-                    final ZonedDateTime time = ZonedDateTime.ofInstant(
-                            Instant.ofEpochMilli(mImageTime), ZoneId.systemDefault());
-                    exif.setAttribute(ExifInterface.TAG_DATETIME_ORIGINAL,
-                            DateTimeFormatter.ofPattern("yyyy:MM:dd HH:mm:ss").format(time));
-                    exif.setAttribute(ExifInterface.TAG_SUBSEC_TIME_ORIGINAL,
-                            DateTimeFormatter.ofPattern("SSS").format(time));
-
-                    if (Objects.equals(time.getOffset(), ZoneOffset.UTC)) {
-                        exif.setAttribute(ExifInterface.TAG_OFFSET_TIME_ORIGINAL, "+00:00");
-                    } else {
-                        exif.setAttribute(ExifInterface.TAG_OFFSET_TIME_ORIGINAL,
-                                DateTimeFormatter.ofPattern("XXX").format(time));
-                    }
-                    if (DEBUG_STORAGE) {
-                        Log.d(TAG, "Writing EXIF metadata");
-                    }
-                    exif.saveAttributes();
-                }
-
-                // Everything went well above, publish it!
-                values.clear();
-                values.put(MediaColumns.IS_PENDING, 0);
-                values.putNull(MediaColumns.DATE_EXPIRES);
-                resolver.update(uri, values, null, null);
-                if (DEBUG_STORAGE) {
-                    Log.d(TAG, "Completed writing to ContentManager");
-                }
-            } catch (Exception e) {
-                resolver.delete(uri, null);
-                throw e;
-            }
-
             List<Notification.Action> smartActions = new ArrayList<>();
             if (mSmartActionsEnabled) {
                 int timeoutMs = DeviceConfig.getInt(
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index d2fe5d2..a60c241 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -78,10 +78,13 @@
 import com.android.internal.policy.PhoneWindow;
 import com.android.settingslib.applications.InterestingConfigChanges;
 import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.screenshot.ScreenshotController.SavedImageData.ShareTransition;
 import com.android.systemui.util.DeviceConfigProxy;
 
 import java.util.List;
+import java.util.concurrent.Executor;
 import java.util.function.Consumer;
 import java.util.function.Supplier;
 
@@ -166,6 +169,9 @@
     private final ScreenshotNotificationsController mNotificationsController;
     private final ScreenshotSmartActions mScreenshotSmartActions;
     private final UiEventLogger mUiEventLogger;
+    private final ImageExporter mImageExporter;
+    private final Executor mMainExecutor;
+    private final Executor mBgExecutor;
 
     private final WindowManager mWindowManager;
     private final WindowManager.LayoutParams mWindowLayoutParams;
@@ -219,11 +225,17 @@
             ScreenshotNotificationsController screenshotNotificationsController,
             ScrollCaptureClient scrollCaptureClient,
             UiEventLogger uiEventLogger,
-            DeviceConfigProxy configProxy) {
+            DeviceConfigProxy configProxy,
+            ImageExporter imageExporter,
+            @Main Executor mainExecutor,
+            @Background Executor bgExecutor) {
         mScreenshotSmartActions = screenshotSmartActions;
         mNotificationsController = screenshotNotificationsController;
         mScrollCaptureClient = scrollCaptureClient;
         mUiEventLogger = uiEventLogger;
+        mImageExporter = imageExporter;
+        mMainExecutor = mainExecutor;
+        mBgExecutor = bgExecutor;
 
         final DisplayManager dm = requireNonNull(context.getSystemService(DisplayManager.class));
         final Display display = dm.getDisplay(DEFAULT_DISPLAY);
@@ -502,9 +514,10 @@
         }
     }
 
-    private void runScrollCapture(ScrollCaptureClient.Connection connection,
-            Runnable after) {
-        new ScrollCaptureController(mContext, connection).run(after);
+    private void runScrollCapture(ScrollCaptureClient.Connection connection, Runnable andThen) {
+        ScrollCaptureController controller = new ScrollCaptureController(mContext, connection,
+                mMainExecutor, mBgExecutor, mImageExporter);
+        controller.run(andThen);
     }
 
     /**
@@ -604,8 +617,8 @@
             mSaveInBgTask.setActionsReadyListener(this::logSuccessOnActionsReady);
         }
 
-        mSaveInBgTask = new SaveImageInBackgroundTask(mContext, mScreenshotSmartActions, data,
-                getShareTransitionSupplier());
+        mSaveInBgTask = new SaveImageInBackgroundTask(mContext, mImageExporter,
+                mScreenshotSmartActions, data, getShareTransitionSupplier());
         mSaveInBgTask.execute();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java
index e159992..bb07012 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java
@@ -18,6 +18,7 @@
 
 import static com.android.systemui.screenshot.LogConfig.DEBUG_SCROLL;
 
+import static java.lang.Math.min;
 import static java.util.Objects.requireNonNull;
 
 import android.annotation.UiContext;
@@ -44,15 +45,20 @@
 import javax.inject.Inject;
 
 /**
- * High level interface to scroll capture API.
+ * High(er) level interface to scroll capture API.
  */
 public class ScrollCaptureClient {
+    private static final int TILE_SIZE_PX_MAX = 4 * (1024 * 1024);
+    private static final int TILES_PER_PAGE = 2; // increase once b/174571735 is addressed
+    private static final int MAX_PAGES = 5;
+    private static final int MAX_IMAGE_COUNT = MAX_PAGES * TILES_PER_PAGE;
 
     @VisibleForTesting
     static final int MATCH_ANY_TASK = ActivityTaskManager.INVALID_TASK_ID;
 
     private static final String TAG = LogConfig.logTag(ScrollCaptureClient.class);
 
+
     /**
      * A connection to a remote window. Starts a capture session.
      */
@@ -60,13 +66,10 @@
         /**
          * Session start should be deferred until UI is active because of resource allocation and
          * potential visible side effects in the target window.
-         *
-         * @param maxBuffers the maximum number of buffers (tiles) that may be in use at one
-         *                   time, tiles are not cached anywhere so set this to a large enough
-         *                   number to retain offscreen content until it is no longer needed
+
          * @param sessionConsumer listener to receive the session once active
          */
-        void start(int maxBuffers, Consumer<Session> sessionConsumer);
+        void start(Consumer<Session> sessionConsumer);
 
         /**
          * Close the connection.
@@ -100,26 +103,33 @@
      */
     interface Session {
         /**
-         * Request the given horizontal strip. Values are y-coordinates in captured space, relative
-         * to start position.
+         * Request an image tile at the given position, from top, to top + {@link #getTileHeight()},
+         * and from left 0, to {@link #getPageWidth()}
          *
-         * @param contentRect the area to capture, in content rect space, relative to scroll-bounds
+         * @param top the top (y) position of the tile to capture, in content rect space
          * @param consumer listener to be informed of the result
          */
-        void requestTile(Rect contentRect, Consumer<CaptureResult> consumer);
+        void requestTile(int top, Consumer<CaptureResult> consumer);
 
         /**
-         * End the capture session, return the target app to original state. The returned
-         * stage must be waited for to complete to allow the target app a chance to restore to
-         * original state before becoming visible.
+         * Returns the maximum number of tiles which may be requested and retained without
+         * being {@link Image#close() closed}.
          *
-         * @return a stage presenting the session shutdown
+         * @return the maximum number of open tiles allowed
+         */
+        int getMaxTiles();
+
+        int getTileHeight();
+
+        int getPageHeight();
+
+        int getPageWidth();
+
+        /**
+         * End the capture session, return the target app to original state. The listener
+         * will be called when the target app is ready to before visible and interactive.
          */
         void end(Runnable listener);
-
-        int getMaxTileHeight();
-
-        int getMaxTileWidth();
     }
 
     private final IWindowManager mWindowManagerService;
@@ -131,6 +141,12 @@
         mWindowManagerService = windowManagerService;
     }
 
+    /**
+     * Set the window token for the screenshot window/ This is required to avoid targeting our
+     * window or any above it.
+     *
+     * @param token the windowToken of the screenshot window
+     */
     public void setHostWindowToken(IBinder token) {
         mHostWindowToken = token;
     }
@@ -176,6 +192,8 @@
 
         private ImageReader mReader;
         private Rect mScrollBounds;
+        private int mTileHeight;
+        private int mTileWidth;
         private Rect mRequestRect;
         private boolean mStarted;
 
@@ -197,6 +215,15 @@
             mScrollBounds = scrollBounds;
             mConnectionConsumer.accept(this);
             mConnectionConsumer = null;
+
+            int pxPerPage = mScrollBounds.width() * mScrollBounds.height();
+            int pxPerTile = min(TILE_SIZE_PX_MAX, (pxPerPage / TILES_PER_PAGE));
+            mTileWidth = mScrollBounds.width();
+            mTileHeight = pxPerTile  / mScrollBounds.width();
+            if (DEBUG_SCROLL) {
+                Log.d(TAG, "scrollBounds: " + mScrollBounds);
+                Log.d(TAG, "tile dimen: " + mTileWidth + "x" + mTileHeight);
+            }
         }
 
         @Override
@@ -257,24 +284,19 @@
 
         // ScrollCaptureController.Connection
 
-        // -> Error handling: BiConsumer<Session, Throwable> ?
         @Override
-        public void start(int maxBufferCount, Consumer<Session> sessionConsumer) {
+        public void start(Consumer<Session> sessionConsumer) {
             if (DEBUG_SCROLL) {
-                Log.d(TAG, "start(maxBufferCount=" + maxBufferCount
-                        + ", sessionConsumer=" + sessionConsumer + ")");
+                Log.d(TAG, "start(sessionConsumer=" + sessionConsumer + ")");
             }
-            mReader = ImageReader.newInstance(mScrollBounds.width(), mScrollBounds.height(),
-                    PixelFormat.RGBA_8888, maxBufferCount, HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE);
+            mReader = ImageReader.newInstance(mTileWidth, mTileHeight, PixelFormat.RGBA_8888,
+                    MAX_IMAGE_COUNT, HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE);
             mSessionConsumer = sessionConsumer;
             try {
                 mConnection.startCapture(mReader.getSurface());
                 mStarted = true;
             } catch (RemoteException e) {
-                Log.w(TAG, "should not be happening :-(");
-                // ?
-                //mSessionListener.onError(e);
-                //mSessionListener = null;
+                Log.w(TAG, "Failed to start", e);
             }
         }
 
@@ -307,27 +329,36 @@
         }
 
         @Override
-        public int getMaxTileHeight() {
+        public int getPageHeight() {
             return mScrollBounds.height();
         }
 
         @Override
-        public int getMaxTileWidth() {
+        public int getPageWidth() {
             return mScrollBounds.width();
         }
 
         @Override
-        public void requestTile(Rect contentRect, Consumer<CaptureResult> consumer) {
+        public int getTileHeight() {
+            return mTileHeight;
+        }
+
+        @Override
+        public int getMaxTiles() {
+            return MAX_IMAGE_COUNT;
+        }
+
+        @Override
+        public void requestTile(int top, Consumer<CaptureResult> consumer) {
             if (DEBUG_SCROLL) {
-                Log.d(TAG, "requestTile(contentRect=" + contentRect + "consumer=" + consumer + ")");
+                Log.d(TAG, "requestTile(top=" + top + ", consumer=" + consumer + ")");
             }
-            mRequestRect = new Rect(contentRect);
+            mRequestRect = new Rect(0, top, mTileWidth, top + mTileHeight);
             mResultConsumer = consumer;
             try {
                 mConnection.requestImage(mRequestRect);
             } catch (RemoteException e) {
                 Log.e(TAG, "Caught remote exception from requestImage", e);
-                // ?
             }
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
index 47b4a50..c75efbc 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
@@ -16,44 +16,21 @@
 
 package com.android.systemui.screenshot;
 
-import static android.graphics.ColorSpace.Named.SRGB;
-
-import android.content.ContentResolver;
-import android.content.ContentValues;
 import android.content.Context;
 import android.content.Intent;
 import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.ColorSpace;
-import android.graphics.Picture;
-import android.graphics.Rect;
-import android.media.ExifInterface;
-import android.media.Image;
 import android.net.Uri;
-import android.os.Build;
-import android.os.Environment;
-import android.os.ParcelFileDescriptor;
 import android.os.UserHandle;
-import android.provider.MediaStore;
-import android.text.format.DateUtils;
 import android.util.Log;
 import android.widget.Toast;
 
 import com.android.systemui.screenshot.ScrollCaptureClient.Connection;
 import com.android.systemui.screenshot.ScrollCaptureClient.Session;
 
-import java.io.File;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.sql.Date;
-import java.text.SimpleDateFormat;
-import java.time.Instant;
-import java.time.ZoneId;
-import java.time.ZoneOffset;
-import java.time.ZonedDateTime;
-import java.time.format.DateTimeFormatter;
-import java.util.Objects;
-import java.util.UUID;
+import com.google.common.util.concurrent.ListenableFuture;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
 import java.util.function.Consumer;
 
 /**
@@ -62,16 +39,27 @@
 public class ScrollCaptureController {
     private static final String TAG = "ScrollCaptureController";
 
+    private static final boolean USE_TILED_IMAGE = false;
+
     public static final int MAX_PAGES = 5;
     public static final int MAX_HEIGHT = 12000;
 
     private final Connection mConnection;
     private final Context mContext;
-    private Picture mPicture;
 
-    public ScrollCaptureController(Context context, Connection connection) {
+    private final Executor mUiExecutor;
+    private final Executor mBgExecutor;
+    private final ImageExporter mImageExporter;
+    private final ImageTileSet mImageTileSet;
+
+    public ScrollCaptureController(Context context, Connection connection, Executor uiExecutor,
+            Executor bgExecutor, ImageExporter exporter) {
         mContext = context;
         mConnection = connection;
+        mUiExecutor = uiExecutor;
+        mBgExecutor = bgExecutor;
+        mImageExporter = exporter;
+        mImageTileSet = new ImageTileSet();
     }
 
     /**
@@ -80,159 +68,58 @@
      * @param after action to take after the flow is complete
      */
     public void run(final Runnable after) {
-        mConnection.start(MAX_PAGES, (session) -> startCapture(session, after));
+        mConnection.start((session) -> startCapture(session, after));
     }
 
-    private void startCapture(Session session, final Runnable after) {
-        Rect requestRect = new Rect(0, 0,
-                session.getMaxTileWidth(), session.getMaxTileHeight());
+    private void startCapture(Session session, final Runnable onDismiss) {
         Consumer<ScrollCaptureClient.CaptureResult> consumer =
                 new Consumer<ScrollCaptureClient.CaptureResult>() {
 
                     int mFrameCount = 0;
+                    int mTop = 0;
 
                     @Override
                     public void accept(ScrollCaptureClient.CaptureResult result) {
                         mFrameCount++;
+
                         boolean emptyFrame = result.captured.height() == 0;
                         if (!emptyFrame) {
-                            mPicture = stackBelow(mPicture, result.image, result.captured.width(),
-                                    result.captured.height());
+                            mImageTileSet.addTile(new ImageTile(result.image, result.captured));
                         }
+
                         if (emptyFrame || mFrameCount >= MAX_PAGES
-                                || requestRect.bottom > MAX_HEIGHT) {
-                            Uri uri = null;
-                            if (mPicture != null) {
-                                // This is probably on a binder thread right now ¯\_(ツ)_/¯
-                                uri = writeImage(Bitmap.createBitmap(mPicture));
-                                // Release those buffers!
-                                mPicture.close();
-                            }
-                            if (uri != null) {
-                                launchViewer(uri);
+                                || mTop + session.getTileHeight() > MAX_HEIGHT) {
+                            if (!mImageTileSet.isEmpty()) {
+                                exportToFile(mImageTileSet.toBitmap(), session, onDismiss);
+                                mImageTileSet.clear();
                             } else {
-                                Toast.makeText(mContext, "Failed to create tall screenshot",
-                                        Toast.LENGTH_SHORT).show();
+                                session.end(onDismiss);
                             }
-                            session.end(after); // end session, close connection, after.run()
                             return;
                         }
-                        requestRect.offset(0, session.getMaxTileHeight());
-                        session.requestTile(requestRect, /* consumer */ this);
+                        mTop += result.captured.height();
+                        session.requestTile(mTop, /* consumer */ this);
                     }
                 };
 
         // fire it up!
-        session.requestTile(requestRect, consumer);
+        session.requestTile(0, consumer);
     };
 
-
-    /**
-     * Combine the top {@link Picture} with an {@link Image} by appending the image directly
-     * below, creating a result that is the combined height of both.
-     * <p>
-     * Note: no pixel data is transferred here, only a record of drawing commands. Backing
-     * hardware buffers must not be modified/recycled until the picture is
-     * {@link Picture#close closed}.
-     *
-     * @param top the existing picture
-     * @param below the image to append below
-     * @param cropWidth the width of the pixel data to use from the image
-     * @param cropHeight the height of the pixel data to use from the image
-     *
-     * @return a new Picture which draws the previous picture with the image below it
-     */
-    private static Picture stackBelow(Picture top, Image below, int cropWidth, int cropHeight) {
-        int width = cropWidth;
-        int height = cropHeight;
-        if (top != null) {
-            height += top.getHeight();
-            width = Math.max(width, top.getWidth());
-        }
-        Picture combined = new Picture();
-        Canvas canvas = combined.beginRecording(width, height);
-        int y = 0;
-        if (top != null) {
-            canvas.drawPicture(top, new Rect(0, 0, top.getWidth(), top.getHeight()));
-            y += top.getHeight();
-        }
-        canvas.drawBitmap(Bitmap.wrapHardwareBuffer(
-                below.getHardwareBuffer(), ColorSpace.get(SRGB)), 0, y, null);
-        combined.endRecording();
-        return combined;
-    }
-
-    Uri writeImage(Bitmap image) {
-        ContentResolver resolver = mContext.getContentResolver();
-        long mImageTime = System.currentTimeMillis();
-        String imageDate = new SimpleDateFormat("yyyyMMdd-HHmmss").format(new Date(mImageTime));
-        String mImageFileName = String.format("tall_Screenshot_%s.png", imageDate);
-        String mScreenshotId = String.format("Screenshot_%s", UUID.randomUUID());
-        try {
-            // Save the screenshot to the MediaStore
-            final ContentValues values = new ContentValues();
-            values.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES
-                    + File.separator + Environment.DIRECTORY_SCREENSHOTS);
-            values.put(MediaStore.MediaColumns.DISPLAY_NAME, mImageFileName);
-            values.put(MediaStore.MediaColumns.MIME_TYPE, "image/png");
-            values.put(MediaStore.MediaColumns.DATE_ADDED, mImageTime / 1000);
-            values.put(MediaStore.MediaColumns.DATE_MODIFIED, mImageTime / 1000);
-            values.put(
-                    MediaStore.MediaColumns.DATE_EXPIRES,
-                    (mImageTime + DateUtils.DAY_IN_MILLIS) / 1000);
-            values.put(MediaStore.MediaColumns.IS_PENDING, 1);
-
-            final Uri uri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
-                    values);
+    void exportToFile(Bitmap bitmap, Session session, Runnable afterEnd) {
+        mImageExporter.setFormat(Bitmap.CompressFormat.PNG);
+        mImageExporter.setQuality(6);
+        ListenableFuture<Uri> future =
+                mImageExporter.export(mBgExecutor, bitmap);
+        future.addListener(() -> {
             try {
-                try (OutputStream out = resolver.openOutputStream(uri)) {
-                    if (!image.compress(Bitmap.CompressFormat.PNG, 100, out)) {
-                        throw new IOException("Failed to compress");
-                    }
-                }
-
-                // Next, write metadata to help index the screenshot
-                try (ParcelFileDescriptor pfd = resolver.openFile(uri, "rw", null)) {
-                    final ExifInterface exif = new ExifInterface(pfd.getFileDescriptor());
-
-                    exif.setAttribute(ExifInterface.TAG_SOFTWARE,
-                            "Android " + Build.DISPLAY);
-
-                    exif.setAttribute(ExifInterface.TAG_IMAGE_WIDTH,
-                            Integer.toString(image.getWidth()));
-                    exif.setAttribute(ExifInterface.TAG_IMAGE_LENGTH,
-                            Integer.toString(image.getHeight()));
-
-                    final ZonedDateTime time = ZonedDateTime.ofInstant(
-                            Instant.ofEpochMilli(mImageTime), ZoneId.systemDefault());
-                    exif.setAttribute(ExifInterface.TAG_DATETIME_ORIGINAL,
-                            DateTimeFormatter.ofPattern("yyyy:MM:dd HH:mm:ss").format(time));
-                    exif.setAttribute(ExifInterface.TAG_SUBSEC_TIME_ORIGINAL,
-                            DateTimeFormatter.ofPattern("SSS").format(time));
-
-                    if (Objects.equals(time.getOffset(), ZoneOffset.UTC)) {
-                        exif.setAttribute(ExifInterface.TAG_OFFSET_TIME_ORIGINAL, "+00:00");
-                    } else {
-                        exif.setAttribute(ExifInterface.TAG_OFFSET_TIME_ORIGINAL,
-                                DateTimeFormatter.ofPattern("XXX").format(time));
-                    }
-                    exif.saveAttributes();
-                }
-
-                // Everything went well above, publish it!
-                values.clear();
-                values.put(MediaStore.MediaColumns.IS_PENDING, 0);
-                values.putNull(MediaStore.MediaColumns.DATE_EXPIRES);
-                resolver.update(uri, values, null, null);
-                return uri;
-            } catch (Exception e) {
-                resolver.delete(uri, null);
-                throw e;
+                launchViewer(future.get());
+            } catch (InterruptedException | ExecutionException e) {
+                Toast.makeText(mContext, "Failed to write image", Toast.LENGTH_SHORT).show();
+                Log.e(TAG, "Error storing screenshot to media store", e.getCause());
             }
-        } catch (Exception e) {
-            Log.e(TAG, "unable to save screenshot", e);
-        }
-        return null;
+            session.end(afterEnd); // end session, close connection, afterEnd.run()
+        }, mUiExecutor);
     }
 
     void launchViewer(Uri uri) {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TiledImageDrawable.java b/packages/SystemUI/src/com/android/systemui/screenshot/TiledImageDrawable.java
new file mode 100644
index 0000000..72f489b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/TiledImageDrawable.java
@@ -0,0 +1,117 @@
+/*
+ * 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.systemui.screenshot;
+
+import android.annotation.Nullable;
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.RenderNode;
+import android.graphics.drawable.Drawable;
+import android.util.Log;
+
+/**
+ * Draws a set of hardware image tiles from a display list. The tiles exist in virtual coordinate
+ * space that may extend into positive or negative values. The origin is the upper-left-most corner
+ * of bounding box, which is drawn at 0,0 to the drawable (output) bounds.
+ */
+public class TiledImageDrawable extends Drawable {
+
+    private static final String TAG = "TiledImageDrawable";
+
+    private final ImageTileSet mTiles;
+    private RenderNode mNode;
+
+    public TiledImageDrawable(ImageTileSet tiles) {
+        mTiles = tiles;
+        mTiles.setOnContentChangedListener(this::onContentChanged);
+    }
+
+    private void onContentChanged() {
+        if (mNode != null && mNode.hasDisplayList()) {
+            mNode.discardDisplayList();
+        }
+        invalidateSelf();
+    }
+
+    private void rebuildDisplayListIfNeeded() {
+        if (mNode != null && mNode.hasDisplayList()) {
+            return;
+        }
+        if (mNode == null) {
+            mNode = new RenderNode("TiledImageDrawable");
+        }
+        mNode.setPosition(0, 0, mTiles.getWidth(), mTiles.getHeight());
+        Canvas canvas = mNode.beginRecording(mTiles.getWidth(), mTiles.getHeight());
+        // Align content (virtual) top/left with 0,0, within the render node
+        canvas.translate(-mTiles.getLeft(), -mTiles.getTop());
+        for (int i = 0; i < mTiles.size(); i++) {
+            ImageTile tile = mTiles.get(i);
+            canvas.save();
+            canvas.translate(tile.getLeft(), tile.getTop());
+            canvas.drawRenderNode(tile.getDisplayList());
+            canvas.restore();
+        }
+        mNode.endRecording();
+    }
+
+    /**
+     * Draws the tiled image to the canvas, with the top/left (virtual) coordinate aligned to 0,0
+     * placed at left/top of the drawable's bounds.
+     */
+    @Override
+    public void draw(Canvas canvas) {
+        rebuildDisplayListIfNeeded();
+        if (canvas.isHardwareAccelerated()) {
+            Rect bounds = getBounds();
+            canvas.save();
+            canvas.clipRect(bounds);
+            canvas.translate(bounds.left, bounds.top);
+            canvas.drawRenderNode(mNode);
+            canvas.restore();
+        } else {
+            Log.d(TAG, "Canvas is not hardware accelerated. Skipping draw!");
+        }
+    }
+
+    @Override
+    public int getIntrinsicWidth() {
+        return mTiles.getWidth();
+    }
+
+    @Override
+    public int getIntrinsicHeight() {
+        return mTiles.getHeight();
+    }
+
+    @Override
+    public void setAlpha(int alpha) {
+        if (mNode.setAlpha(alpha / 255f)) {
+            invalidateSelf();
+        }
+    }
+
+    @Override
+    public void setColorFilter(@Nullable ColorFilter colorFilter) {
+        throw new IllegalArgumentException("not implemented");
+    }
+
+    @Override
+    public int getOpacity() {
+        return PixelFormat.OPAQUE;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java
index be10c26..a3b5f27 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessController.java
@@ -316,8 +316,7 @@
                 PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM);
         mMaximumBacklight = pm.getBrightnessConstraint(
                 PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MAXIMUM);
-        mDefaultBacklight = pm.getBrightnessConstraint(
-                PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DEFAULT);
+        mDefaultBacklight = mContext.getDisplay().getBrightnessDefault();
         mMinimumBacklightForVr = pm.getBrightnessConstraint(
                 PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MINIMUM_VR);
         mMaximumBacklightForVr = pm.getBrightnessConstraint(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java
index ab0366e..eceac32 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenLockIconController.java
@@ -259,6 +259,7 @@
                         force = true;
                     }
                     if (!wasShowing && mKeyguardShowing) {
+                        setBouncerHideAmount(KeyguardBouncer.EXPANSION_HIDDEN);
                         mKeyguardJustShown = true;
                     }
                     update(force);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index a991d3615..8f23824 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -34,7 +34,6 @@
 import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_ASLEEP;
 import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWAKE;
 import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_WAKING;
-import static com.android.systemui.shared.system.WindowManagerWrapper.NAV_BAR_POS_INVALID;
 import static com.android.systemui.statusbar.LightRevealScrimKt.getEnableLightReveal;
 import static com.android.systemui.statusbar.NotificationLockscreenUserManager.PERMISSION_SELF;
 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT;
@@ -176,7 +175,6 @@
 import com.android.systemui.recents.ScreenPinningRequest;
 import com.android.systemui.settings.brightness.BrightnessSlider;
 import com.android.systemui.shared.plugins.PluginManager;
-import com.android.systemui.shared.system.WindowManagerWrapper;
 import com.android.systemui.statusbar.AutoHideUiElement;
 import com.android.systemui.statusbar.BackDropView;
 import com.android.systemui.statusbar.CommandQueue;
@@ -1582,11 +1580,6 @@
             return true;
         }
 
-        final int navbarPos = WindowManagerWrapper.getInstance().getNavBarPosition(mDisplayId);
-        if (navbarPos == NAV_BAR_POS_INVALID) {
-            return false;
-        }
-
         if (legacySplitScreen.splitPrimaryTask()) {
             if (metricsDockAction != -1) {
                 mMetricsLogger.action(metricsDockAction);
@@ -4044,10 +4037,6 @@
             return;
         }
 
-        if (mVibrator != null && mVibrator.hasVibrator()) {
-            mVibrator.vibrate(500L);
-        }
-
         emergencyIntent.setComponent(new ComponentName(resolveInfo.activityInfo.packageName,
                 resolveInfo.activityInfo.name));
         emergencyIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
index 08e70a9..6ae5e90 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
@@ -105,6 +105,9 @@
 
         default void onExtremeBatterySaverChanged(boolean isExtreme) {
         }
+
+        default void onWirelessChargingChanged(boolean isWirlessCharging) {
+        }
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
index 8c67072..288eb3d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryControllerImpl.java
@@ -77,7 +77,7 @@
     private boolean mCharged;
     protected boolean mPowerSave;
     private boolean mAodPowerSave;
-    protected boolean mWirelessCharging;
+    private boolean mWirelessCharging;
     private boolean mTestMode = false;
     @VisibleForTesting
     boolean mHasReceivedBattery = false;
@@ -155,6 +155,7 @@
         cb.onBatteryLevelChanged(mLevel, mPluggedIn, mCharging);
         cb.onPowerSaveChanged(mPowerSave);
         cb.onBatteryUnknownStateChanged(mStateUnknown);
+        cb.onWirelessChargingChanged(mWirelessCharging);
     }
 
     @Override
@@ -179,8 +180,12 @@
                     BatteryManager.BATTERY_STATUS_UNKNOWN);
             mCharged = status == BatteryManager.BATTERY_STATUS_FULL;
             mCharging = mCharged || status == BatteryManager.BATTERY_STATUS_CHARGING;
-            mWirelessCharging = mCharging && intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0)
-                    == BatteryManager.BATTERY_PLUGGED_WIRELESS;
+            if (mWirelessCharging != (mCharging
+                    && intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0)
+                    == BatteryManager.BATTERY_PLUGGED_WIRELESS)) {
+                mWirelessCharging = !mWirelessCharging;
+                fireWirelessChargingChanged();
+            }
 
             boolean present = intent.getBooleanExtra(EXTRA_PRESENT, true);
             boolean unknown = !present;
@@ -195,31 +200,31 @@
         } else if (action.equals(ACTION_LEVEL_TEST)) {
             mTestMode = true;
             mMainHandler.post(new Runnable() {
-                int curLevel = 0;
-                int incr = 1;
-                int saveLevel = mLevel;
-                boolean savePlugged = mPluggedIn;
+                int mCurrentLevel = 0;
+                int mIncrement = 1;
+                int mSavedLevel = mLevel;
+                boolean mSavedPluggedIn = mPluggedIn;
                 Intent mTestIntent = new Intent(Intent.ACTION_BATTERY_CHANGED);
                 @Override
                 public void run() {
-                    if (curLevel < 0) {
+                    if (mCurrentLevel < 0) {
                         mTestMode = false;
-                        mTestIntent.putExtra("level", saveLevel);
-                        mTestIntent.putExtra("plugged", savePlugged);
+                        mTestIntent.putExtra("level", mSavedLevel);
+                        mTestIntent.putExtra("plugged", mSavedPluggedIn);
                         mTestIntent.putExtra("testmode", false);
                     } else {
-                        mTestIntent.putExtra("level", curLevel);
-                        mTestIntent.putExtra("plugged", incr > 0 ? BatteryManager.BATTERY_PLUGGED_AC
-                                : 0);
+                        mTestIntent.putExtra("level", mCurrentLevel);
+                        mTestIntent.putExtra("plugged",
+                                mIncrement > 0 ? BatteryManager.BATTERY_PLUGGED_AC : 0);
                         mTestIntent.putExtra("testmode", true);
                     }
                     context.sendBroadcast(mTestIntent);
 
                     if (!mTestMode) return;
 
-                    curLevel += incr;
-                    if (curLevel == 100) {
-                        incr *= -1;
+                    mCurrentLevel += mIncrement;
+                    if (mCurrentLevel == 100) {
+                        mIncrement *= -1;
                     }
                     mMainHandler.postDelayed(this, 200);
                 }
@@ -227,6 +232,13 @@
         }
     }
 
+    private void fireWirelessChargingChanged() {
+        synchronized (mChangeCallbacks) {
+            mChangeCallbacks.forEach(batteryStateChangeCallback ->
+                    batteryStateChangeCallback.onWirelessChargingChanged(mWirelessCharging));
+        }
+    }
+
     @Override
     public boolean isPluggedIn() {
         return mPluggedIn;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
index 8722fec..f92860b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
@@ -104,6 +104,15 @@
                 Context context) {
             this(visible, icon, context.getString(contentDescription));
         }
+
+        @Override
+        public String toString() {
+            StringBuilder builder = new StringBuilder();
+            return builder.append("[visible=").append(visible).append(',')
+                .append("icon=").append(icon).append(',')
+                .append("contentDescription=").append(contentDescription).append(']')
+                .toString();
+        }
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index 0ca2b4a..291aefe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -359,8 +359,6 @@
 
         // broadcasts
         IntentFilter filter = new IntentFilter();
-        filter.addAction(WifiManager.RSSI_CHANGED_ACTION);
-        filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
         filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
         filter.addAction(Intent.ACTION_SIM_STATE_CHANGED);
         filter.addAction(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
index 72e8e38..9a8e26d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -691,7 +691,11 @@
             if (item.isAddUser) {
                 iconRes = R.drawable.ic_add_circle;
             } else if (item.isGuest) {
-                iconRes = R.drawable.ic_avatar_guest_user;
+                if (item.isCurrent) {
+                    iconRes = R.drawable.ic_exit_to_app;
+                } else {
+                    iconRes = R.drawable.ic_avatar_guest_user;
+                }
             } else {
                 iconRes = R.drawable.ic_avatar_user;
             }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
index 82ce4c8..6d109ac 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java
@@ -104,11 +104,7 @@
                 wifiDesc, mCurrentState.isTransient, mCurrentState.statusLabel);
     }
 
-    /**
-     * Fetches wifi initial state replacing the initial sticky broadcast.
-     */
-    public void fetchInitialState() {
-        mWifiTracker.fetchInitialState();
+    private void copyWifiStates() {
         mCurrentState.enabled = mWifiTracker.enabled;
         mCurrentState.isDefault = mWifiTracker.isDefaultNetwork;
         mCurrentState.connected = mWifiTracker.connected;
@@ -116,6 +112,14 @@
         mCurrentState.rssi = mWifiTracker.rssi;
         mCurrentState.level = mWifiTracker.level;
         mCurrentState.statusLabel = mWifiTracker.statusLabel;
+    }
+
+    /**
+     * Fetches wifi initial state replacing the initial sticky broadcast.
+     */
+    public void fetchInitialState() {
+        mWifiTracker.fetchInitialState();
+        copyWifiStates();
         notifyListenersIfNecessary();
     }
 
@@ -124,19 +128,12 @@
      */
     public void handleBroadcast(Intent intent) {
         mWifiTracker.handleBroadcast(intent);
-        mCurrentState.enabled = mWifiTracker.enabled;
-        mCurrentState.isDefault = mWifiTracker.isDefaultNetwork;
-        mCurrentState.connected = mWifiTracker.connected;
-        mCurrentState.ssid = mWifiTracker.ssid;
-        mCurrentState.rssi = mWifiTracker.rssi;
-        mCurrentState.level = mWifiTracker.level;
-        mCurrentState.statusLabel = mWifiTracker.statusLabel;
+        copyWifiStates();
         notifyListenersIfNecessary();
     }
 
     private void handleStatusUpdated() {
-        mCurrentState.statusLabel = mWifiTracker.statusLabel;
-        mCurrentState.isDefault = mWifiTracker.isDefaultNetwork;
+        copyWifiStates();
         notifyListenersIfNecessary();
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationPanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationPanel.java
index 477424c..c9bc7e6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/notifications/TvNotificationPanel.java
@@ -91,6 +91,7 @@
 
     private void openInternalNotificationPanel(String action) {
         Intent intent = new Intent(mContext, TvNotificationPanelActivity.class);
+        intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
         intent.setAction(action);
         mContext.startActivityAsUser(intent, UserHandle.SYSTEM);
     }
@@ -113,6 +114,7 @@
         if (ri != null && ri.activityInfo != null) {
             if (ri.activityInfo.permission != null && ri.activityInfo.permission.equals(
                     Manifest.permission.STATUS_BAR_SERVICE)) {
+                intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
                 mContext.startActivityAsUser(intent, UserHandle.CURRENT);
             } else {
                 Log.e(TAG,
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java
index b67574d..09335af 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java
@@ -109,7 +109,7 @@
         dialog.setButton(DialogInterface.BUTTON_NEGATIVE, context.getString(R.string.cancel),
                 (OnClickListener) null);
         dialog.setButton(DialogInterface.BUTTON_POSITIVE,
-                context.getString(R.string.guest_exit_guest_dialog_remove), new OnClickListener() {
+                context.getString(R.string.qs_customize_remove), new OnClickListener() {
                     @Override
                     public void onClick(DialogInterface dialog, int which) {
                         // Tell the tuner (in main SysUI process) to clear all its settings.
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java
index 125b5d4c..4d3af9c 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/TvPipModule.java
@@ -24,6 +24,7 @@
 import com.android.wm.shell.common.DisplayController;
 import com.android.wm.shell.common.SystemWindows;
 import com.android.wm.shell.common.TaskStackListenerImpl;
+import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
 import com.android.wm.shell.pip.Pip;
 import com.android.wm.shell.pip.PipBoundsAlgorithm;
 import com.android.wm.shell.pip.PipBoundsState;
@@ -32,11 +33,8 @@
 import com.android.wm.shell.pip.PipTaskOrganizer;
 import com.android.wm.shell.pip.PipUiEventLogger;
 import com.android.wm.shell.pip.tv.PipController;
-import com.android.wm.shell.pip.tv.PipControlsView;
-import com.android.wm.shell.pip.tv.PipControlsViewController;
 import com.android.wm.shell.pip.tv.PipNotification;
 import com.android.wm.shell.pip.tv.TvPipMenuController;
-import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
 
 import java.util.Optional;
 
@@ -75,19 +73,6 @@
 
     @WMSingleton
     @Provides
-    static PipControlsViewController providePipControlsViewController(
-            PipControlsView pipControlsView, PipController pipController) {
-        return new PipControlsViewController(pipControlsView, pipController);
-    }
-
-    @WMSingleton
-    @Provides
-    static PipControlsView providePipControlsView(Context context) {
-        return new PipControlsView(context, null);
-    }
-
-    @WMSingleton
-    @Provides
     static PipNotification providePipNotification(Context context,
             PipMediaController pipMediaController) {
         return new PipNotification(context, pipMediaController);
@@ -108,9 +93,12 @@
 
     @WMSingleton
     @Provides
-    static TvPipMenuController providesPipTvMenuController(Context context,
-            PipBoundsState pipBoundsState, SystemWindows systemWindows) {
-        return new TvPipMenuController(context, pipBoundsState, systemWindows);
+    static TvPipMenuController providesPipTvMenuController(
+            Context context,
+            PipBoundsState pipBoundsState,
+            SystemWindows systemWindows,
+            PipMediaController pipMediaController) {
+        return new TvPipMenuController(context, pipBoundsState, systemWindows, pipMediaController);
     }
 
     @WMSingleton
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
index bcb2a8c..88cbddd 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShellBaseModule.java
@@ -285,7 +285,7 @@
     @Provides
     static RootTaskDisplayAreaOrganizer provideRootTaskDisplayAreaOrganizer(
             @ShellMainThread ShellExecutor mainExecutor, Context context) {
-        return new RootTaskDisplayAreaOrganizer(mainExecutor);
+        return new RootTaskDisplayAreaOrganizer(mainExecutor, context);
     }
 
     @WMSingleton
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
index 4944284..31cc7bb 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinBasedInputViewControllerTest.java
@@ -31,6 +31,8 @@
 import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
 import com.android.systemui.R;
 import com.android.systemui.SysuiTestCase;
+import com.android.systemui.classifier.FalsingCollector;
+import com.android.systemui.classifier.FalsingCollectorFake;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -65,6 +67,7 @@
     private LatencyTracker mLatencyTracker;
     @Mock
     private LiftToActivateListener mLiftToactivateListener;
+    private FalsingCollector mFalsingCollector = new FalsingCollectorFake();
     @Mock
     private View mDeleteButton;
     @Mock
@@ -88,7 +91,8 @@
                 .thenReturn(mOkButton);
         mKeyguardPinViewController = new KeyguardPinBasedInputViewController(mPinBasedInputView,
                 mKeyguardUpdateMonitor, mSecurityMode, mLockPatternUtils, mKeyguardSecurityCallback,
-                mKeyguardMessageAreaControllerFactory, mLatencyTracker, mLiftToactivateListener) {
+                mKeyguardMessageAreaControllerFactory, mLatencyTracker, mLiftToactivateListener,
+                mFalsingCollector) {
             @Override
             public void onResume(int reason) {
                 super.onResume(reason);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationGestureDetectorTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationGestureDetectorTest.java
new file mode 100644
index 0000000..6f4846a
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationGestureDetectorTest.java
@@ -0,0 +1,173 @@
+/*
+ * 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.systemui.accessibility;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyFloat;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import android.os.Handler;
+import android.os.SystemClock;
+import android.testing.AndroidTestingRunner;
+import android.view.MotionEvent;
+import android.view.ViewConfiguration;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InOrder;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class MagnificationGestureDetectorTest extends SysuiTestCase {
+
+    private static final float ACTION_DOWN_X = 100;
+    private static final float ACTION_DOWN_Y = 200;
+    private int mTouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
+    private MagnificationGestureDetector mGestureDetector;
+    private MotionEventHelper mMotionEventHelper = new MotionEventHelper();
+    @Mock
+    private MagnificationGestureDetector.OnGestureListener mListener;
+    @Mock
+    private Handler mHandler;
+    private Runnable mCancelSingleTapRunnable;
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        doAnswer((invocation) -> {
+            mCancelSingleTapRunnable = invocation.getArgument(0);
+            return null;
+        }).when(mHandler).postAtTime(any(Runnable.class), anyLong());
+        mGestureDetector = new MagnificationGestureDetector(mContext, mHandler, mListener);
+    }
+
+    @After
+    public void tearDown() {
+        mMotionEventHelper.recycleEvents();
+    }
+
+    @Test
+    public void onActionDown_invokeDownCallback() {
+        final long downTime = SystemClock.uptimeMillis();
+        final MotionEvent downEvent = mMotionEventHelper.obtainMotionEvent(downTime, downTime,
+                MotionEvent.ACTION_DOWN, ACTION_DOWN_X, ACTION_DOWN_Y);
+
+        mGestureDetector.onTouch(downEvent);
+
+        mListener.onStart(ACTION_DOWN_X, ACTION_DOWN_Y);
+    }
+
+    @Test
+    public void performSingleTap_invokeCallbacksInOrder() {
+        final long downTime = SystemClock.uptimeMillis();
+        final MotionEvent downEvent = mMotionEventHelper.obtainMotionEvent(downTime, downTime,
+                MotionEvent.ACTION_DOWN, ACTION_DOWN_X, ACTION_DOWN_Y);
+        final MotionEvent upEvent = mMotionEventHelper.obtainMotionEvent(downTime, downTime,
+                MotionEvent.ACTION_UP, ACTION_DOWN_X, ACTION_DOWN_Y);
+
+        mGestureDetector.onTouch(downEvent);
+        mGestureDetector.onTouch(upEvent);
+
+        InOrder inOrder = Mockito.inOrder(mListener);
+        inOrder.verify(mListener).onStart(ACTION_DOWN_X, ACTION_DOWN_Y);
+        inOrder.verify(mListener).onSingleTap();
+        inOrder.verify(mListener).onFinish(ACTION_DOWN_X, ACTION_DOWN_Y);
+        verify(mListener, never()).onDrag(anyFloat(), anyFloat());
+    }
+
+    @Test
+    public void performSingleTapWithActionCancel_notInvokeOnSingleTapCallback() {
+        final long downTime = SystemClock.uptimeMillis();
+        final MotionEvent downEvent = mMotionEventHelper.obtainMotionEvent(downTime, downTime,
+                MotionEvent.ACTION_DOWN, ACTION_DOWN_X, ACTION_DOWN_Y);
+        final MotionEvent cancelEvent = mMotionEventHelper.obtainMotionEvent(downTime, downTime,
+                MotionEvent.ACTION_CANCEL, ACTION_DOWN_X, ACTION_DOWN_Y);
+
+        mGestureDetector.onTouch(downEvent);
+        mGestureDetector.onTouch(cancelEvent);
+
+        verify(mListener, never()).onSingleTap();
+    }
+
+    @Test
+    public void performSingleTapWithTwoPointers_notInvokeSingleTapCallback() {
+        final long downTime = SystemClock.uptimeMillis();
+        final MotionEvent downEvent = mMotionEventHelper.obtainMotionEvent(downTime, downTime,
+                MotionEvent.ACTION_DOWN, ACTION_DOWN_X, ACTION_DOWN_Y);
+        final MotionEvent upEvent = mMotionEventHelper.obtainMotionEvent(downTime, downTime,
+                MotionEvent.ACTION_POINTER_DOWN, ACTION_DOWN_X, ACTION_DOWN_Y);
+
+        mGestureDetector.onTouch(downEvent);
+        mGestureDetector.onTouch(upEvent);
+
+        verify(mListener, never()).onSingleTap();
+    }
+
+    @Test
+    public void performLongPress_invokeCallbacksInOrder() {
+        final long downTime = SystemClock.uptimeMillis();
+        final MotionEvent downEvent = mMotionEventHelper.obtainMotionEvent(downTime, downTime,
+                MotionEvent.ACTION_DOWN, ACTION_DOWN_X, ACTION_DOWN_Y);
+        final MotionEvent upEvent = mMotionEventHelper.obtainMotionEvent(downTime, downTime,
+                MotionEvent.ACTION_UP, ACTION_DOWN_X, ACTION_DOWN_Y);
+
+        mGestureDetector.onTouch(downEvent);
+        // Execute the pending message for stopping single-tap detection.
+        mCancelSingleTapRunnable.run();
+        mGestureDetector.onTouch(upEvent);
+
+        InOrder inOrder = Mockito.inOrder(mListener);
+        inOrder.verify(mListener).onStart(ACTION_DOWN_X, ACTION_DOWN_Y);
+        inOrder.verify(mListener).onFinish(ACTION_DOWN_X, ACTION_DOWN_Y);
+        verify(mListener, never()).onSingleTap();
+    }
+
+    @Test
+    public void performDrag_invokeCallbacksInOrder() {
+        final long downTime = SystemClock.uptimeMillis();
+        final float dragOffset = mTouchSlop + 10;
+        final MotionEvent downEvent = mMotionEventHelper.obtainMotionEvent(downTime, downTime,
+                MotionEvent.ACTION_DOWN, ACTION_DOWN_X, ACTION_DOWN_Y);
+        final MotionEvent moveEvent = mMotionEventHelper.obtainMotionEvent(downTime, downTime,
+                MotionEvent.ACTION_MOVE, ACTION_DOWN_X + dragOffset, ACTION_DOWN_Y);
+        final MotionEvent upEvent = mMotionEventHelper.obtainMotionEvent(downTime, downTime,
+                MotionEvent.ACTION_UP, ACTION_DOWN_X, ACTION_DOWN_Y);
+
+        mGestureDetector.onTouch(downEvent);
+        mGestureDetector.onTouch(moveEvent);
+        mGestureDetector.onTouch(upEvent);
+
+        InOrder inOrder = Mockito.inOrder(mListener);
+        inOrder.verify(mListener).onStart(ACTION_DOWN_X, ACTION_DOWN_Y);
+        inOrder.verify(mListener).onDrag(dragOffset, 0);
+        inOrder.verify(mListener).onFinish(ACTION_DOWN_X, ACTION_DOWN_Y);
+        verify(mListener, never()).onSingleTap();
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java
index 96f3c15..3d504fb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MagnificationModeSwitchTest.java
@@ -73,7 +73,6 @@
 import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 
-import java.util.ArrayList;
 import java.util.List;
 
 @SmallTest
@@ -91,8 +90,8 @@
     private ViewPropertyAnimator mViewPropertyAnimator;
     private MagnificationModeSwitch mMagnificationModeSwitch;
     private View.OnTouchListener mTouchListener;
-    private List<MotionEvent> mMotionEvents = new ArrayList<>();
     private Runnable mFadeOutAnimation;
+    private MotionEventHelper mMotionEventHelper = new MotionEventHelper();
 
     @Before
     public void setUp() throws Exception {
@@ -117,11 +116,8 @@
 
     @After
     public void tearDown() {
-        for (MotionEvent event:mMotionEvents) {
-            event.recycle();
-        }
-        mMotionEvents.clear();
         mFadeOutAnimation = null;
+        mMotionEventHelper.recycleEvents();
     }
 
     @Test
@@ -436,9 +432,7 @@
 
     private MotionEvent obtainMotionEvent(long downTime, long eventTime, int action, float x,
             float y) {
-        MotionEvent event = MotionEvent.obtain(downTime, eventTime, action, x, y, 0);
-        mMotionEvents.add(event);
-        return event;
+        return mMotionEventHelper.obtainMotionEvent(downTime, eventTime, action, x, y);
     }
 
     private void executeFadeOutAnimation() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/MotionEventHelper.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MotionEventHelper.java
new file mode 100644
index 0000000..92dad9b
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/MotionEventHelper.java
@@ -0,0 +1,47 @@
+/*
+ * 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.systemui.accessibility;
+
+import android.view.MotionEvent;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.ArrayList;
+import java.util.List;
+
+class MotionEventHelper {
+    @GuardedBy("this")
+    private final List<MotionEvent> mMotionEvents = new ArrayList<>();
+
+    void recycleEvents() {
+        for (MotionEvent event:mMotionEvents) {
+            event.recycle();
+        }
+        synchronized (this) {
+            mMotionEvents.clear();
+        }
+    }
+
+    MotionEvent obtainMotionEvent(long downTime, long eventTime, int action, float x,
+            float y) {
+        MotionEvent event = MotionEvent.obtain(downTime, eventTime, action, x, y, 0);
+        synchronized (this) {
+            mMotionEvents.add(event);
+        }
+        return event;
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
index f28322f..9659610e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
@@ -26,6 +26,7 @@
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertThat;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.anyLong;
@@ -40,6 +41,7 @@
 import android.content.pm.ActivityInfo;
 import android.content.res.Resources;
 import android.os.Handler;
+import android.os.SystemClock;
 import android.testing.AndroidTestingRunner;
 import android.testing.TestableResources;
 import android.view.Display;
@@ -347,4 +349,26 @@
         verify(mWindowManager).updateViewLayout(eq(mMirrorView), paramsArgumentCaptor.capture());
         assertEquals(newA11yWindowTitle, paramsArgumentCaptor.getValue().accessibilityTitle);
     }
+
+    @Test
+    public void onSingleTap_enabled_scaleIsChanged() {
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationController.enableWindowMagnification(Float.NaN, Float.NaN,
+                    Float.NaN);
+        });
+
+        mInstrumentation.runOnMainSync(() -> {
+            mWindowMagnificationController.onSingleTap();
+        });
+
+        final long timeout = SystemClock.uptimeMillis() + 1000;
+        while (SystemClock.uptimeMillis() < timeout) {
+            SystemClock.sleep(10);
+
+            if (Float.compare(1.0f, mMirrorView.getScaleX()) < 0) {
+                return;
+            }
+        }
+        fail("mMirrorView scale is not changed");
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index 7f8372e..a65c352 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -27,6 +27,7 @@
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.hardware.biometrics.SensorProperties;
+import android.hardware.display.DisplayManager;
 import android.hardware.fingerprint.FingerprintManager;
 import android.hardware.fingerprint.FingerprintSensorProperties;
 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
@@ -84,7 +85,7 @@
     @Mock
     private FingerprintManager mFingerprintManager;
     @Mock
-    private PowerManager mPowerManager;
+    private DisplayManager mDisplayManager;
     @Mock
     private WindowManager mWindowManager;
     @Mock
@@ -124,7 +125,7 @@
                 mResources,
                 mLayoutInflater,
                 mFingerprintManager,
-                mPowerManager,
+                mDisplayManager,
                 mWindowManager,
                 mSystemSettings,
                 mStatusBarStateController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java
index 8c547b1..5709ce30 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/BrightLineClassifierTest.java
@@ -16,12 +16,15 @@
 
 package com.android.systemui.classifier;
 
-import static com.android.systemui.util.mockito.KotlinMockitoHelpersKt.any;
-
 import static com.google.common.truth.Truth.assertThat;
 
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyCollection;
 import static org.mockito.ArgumentMatchers.anyDouble;
+import static org.mockito.ArgumentMatchers.anyLong;
 import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -35,6 +38,7 @@
 import com.android.systemui.SysuiTestCase;
 import com.android.systemui.classifier.FalsingDataProvider.GestureCompleteListener;
 import com.android.systemui.dock.DockManagerFake;
+import com.android.systemui.util.concurrency.FakeExecutor;
 import com.android.systemui.util.time.FakeSystemClock;
 
 import org.junit.Before;
@@ -45,7 +49,6 @@
 import org.mockito.MockitoAnnotations;
 
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
@@ -53,6 +56,8 @@
 @SmallTest
 @RunWith(AndroidTestingRunner.class)
 public class BrightLineClassifierTest extends SysuiTestCase {
+    private static final long DOUBLE_TAP_TIMEOUT_MS = 1000;
+
     private BrightLineFalsingManager mBrightLineFalsingManager;
     @Mock
     private FalsingDataProvider mFalsingDataProvider;
@@ -69,24 +74,35 @@
     private FalsingClassifier mClassifierB;
     private final List<MotionEvent> mMotionEventList = new ArrayList<>();
     @Mock
-    private HistoryTracker mHistoryTracker;
-    private FakeSystemClock mSystemClock = new FakeSystemClock();
+    private HistoryTracker mHistoryTracker;;
+    private final FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
 
     private final FalsingClassifier.Result mFalsedResult = FalsingClassifier.Result.falsed(1, "");
     private final FalsingClassifier.Result mPassedResult = FalsingClassifier.Result.passed(1);
+    private GestureCompleteListener mGestureCompleteListener;
 
     @Before
     public void setup() {
         MockitoAnnotations.initMocks(this);
         when(mClassifierA.classifyGesture(anyDouble(), anyDouble())).thenReturn(mPassedResult);
         when(mClassifierB.classifyGesture(anyDouble(), anyDouble())).thenReturn(mPassedResult);
+        when(mSingleTapClassfier.isTap(any(List.class))).thenReturn(mPassedResult);
+        when(mDoubleTapClassifier.classifyGesture()).thenReturn(mPassedResult);
         mClassifiers.add(mClassifierA);
         mClassifiers.add(mClassifierB);
-        when(mFalsingDataProvider.isDirty()).thenReturn(true);
         when(mFalsingDataProvider.getRecentMotionEvents()).thenReturn(mMotionEventList);
         mBrightLineFalsingManager = new BrightLineFalsingManager(mFalsingDataProvider, mDockManager,
                 mMetricsLogger, mClassifiers, mSingleTapClassfier, mDoubleTapClassifier,
-                mHistoryTracker, mSystemClock, false);
+                mHistoryTracker, mFakeExecutor, DOUBLE_TAP_TIMEOUT_MS, false);
+
+
+        ArgumentCaptor<GestureCompleteListener> gestureCompleteListenerCaptor =
+                ArgumentCaptor.forClass(GestureCompleteListener.class);
+
+        verify(mFalsingDataProvider).addGestureCompleteListener(
+                gestureCompleteListenerCaptor.capture());
+
+        mGestureCompleteListener = gestureCompleteListenerCaptor.getValue();
     }
 
     @Test
@@ -179,15 +195,57 @@
 
     @Test
     public void testHistory() {
-        ArgumentCaptor<GestureCompleteListener> gestureCompleteListenerCaptor =
-                ArgumentCaptor.forClass(GestureCompleteListener.class);
+        mGestureCompleteListener.onGestureComplete(1000);
 
-        verify(mFalsingDataProvider).addGestureCompleteListener(
-                gestureCompleteListenerCaptor.capture());
+        verify(mHistoryTracker).addResults(anyCollection(), eq(1000L));
+    }
 
-        GestureCompleteListener gestureCompleteListener = gestureCompleteListenerCaptor.getValue();
-        gestureCompleteListener.onGestureComplete();
+    @Test
+    public void testHistory_singleTap() {
+        // When trying to classify single taps, we don't immediately add results to history.
+        mBrightLineFalsingManager.isFalseTap(false);
+        mGestureCompleteListener.onGestureComplete(1000);
 
-        verify(mHistoryTracker).addResults(any(Collection.class), eq(mSystemClock.uptimeMillis()));
+        verify(mHistoryTracker, never()).addResults(any(), anyLong());
+
+        mFakeExecutor.advanceClockToNext();
+        mFakeExecutor.runAllReady();
+
+        verify(mHistoryTracker).addResults(anyCollection(), eq(1000L));
+    }
+
+    @Test
+    public void testHistory_multipleSingleTaps() {
+        // When trying to classify single taps, we don't immediately add results to history.
+        mBrightLineFalsingManager.isFalseTap(false);
+        mGestureCompleteListener.onGestureComplete(1000);
+        mBrightLineFalsingManager.isFalseTap(false);
+        mGestureCompleteListener.onGestureComplete(2000);
+
+        verify(mHistoryTracker, never()).addResults(any(), anyLong());
+
+        mFakeExecutor.advanceClockToNext();
+        mFakeExecutor.runNextReady();
+        verify(mHistoryTracker).addResults(anyCollection(), eq(1000L));
+        reset(mHistoryTracker);
+        mFakeExecutor.advanceClockToNext();
+        mFakeExecutor.runNextReady();
+        verify(mHistoryTracker).addResults(anyCollection(), eq(2000L));
+    }
+
+    @Test
+    public void testHistory_doubleTap() {
+        // When trying to classify single taps, we don't immediately add results to history.
+        mBrightLineFalsingManager.isFalseTap(false);
+        mGestureCompleteListener.onGestureComplete(1000);
+        // Before checking for double tap, we may check for single-tap on the second gesture.
+        mBrightLineFalsingManager.isFalseTap(false);
+        mBrightLineFalsingManager.isFalseDoubleTap();
+        mGestureCompleteListener.onGestureComplete(2000);
+
+        // Double tap is immediately added to history. Single tap is never added.
+        verify(mHistoryTracker).addResults(anyCollection(), eq(2000L));
+
+        assertThat(mFakeExecutor.numPending()).isEqualTo(0);
     }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java
index af5e789..23ef865 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/classifier/FalsingCollectorImplTest.java
@@ -17,12 +17,15 @@
 package com.android.systemui.classifier;
 
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.Mockito.inOrder;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
 import android.testing.AndroidTestingRunner;
+import android.view.MotionEvent;
 
 import androidx.test.filters.SmallTest;
 
@@ -38,6 +41,7 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.ArgumentCaptor;
+import org.mockito.InOrder;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
@@ -66,7 +70,6 @@
                 mKeyguardUpdateMonitor, mProximitySensor, mStatusBarStateController);
     }
 
-
     @Test
     public void testRegisterSensor() {
         mFalsingCollector.onScreenTurningOn();
@@ -110,7 +113,6 @@
 
     @Test
     public void testUnregisterSensor_StateTransition() {
-
         ArgumentCaptor<StatusBarStateController.StateListener> stateListenerArgumentCaptor =
                 ArgumentCaptor.forClass(StatusBarStateController.StateListener.class);
         verify(mStatusBarStateController).addCallback(stateListenerArgumentCaptor.capture());
@@ -120,4 +122,37 @@
         stateListenerArgumentCaptor.getValue().onStateChanged(StatusBarState.SHADE);
         verify(mProximitySensor).unregister(any(ThresholdSensor.Listener.class));
     }
+
+    @Test
+    public void testPassThroughGesture() {
+        MotionEvent down = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0);
+        MotionEvent up = MotionEvent.obtain(0, 0, MotionEvent.ACTION_UP, 0, 0, 0);
+
+        // Nothing passed initially
+        mFalsingCollector.onTouchEvent(down);
+        verify(mFalsingDataProvider, never()).onMotionEvent(any(MotionEvent.class));
+
+        // Up event flushes the down event.
+        mFalsingCollector.onTouchEvent(up);
+        InOrder orderedCalls = inOrder(mFalsingDataProvider);
+        // We can't simply use "eq" or similar because the collector makes a copy of "down".
+        orderedCalls.verify(mFalsingDataProvider).onMotionEvent(
+                argThat(argument -> argument.getActionMasked() == MotionEvent.ACTION_DOWN));
+        orderedCalls.verify(mFalsingDataProvider).onMotionEvent(up);
+    }
+
+    @Test
+    public void testAvoidGesture() {
+        MotionEvent down = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0);
+        MotionEvent up = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0);
+
+        // Nothing passed initially
+        mFalsingCollector.onTouchEvent(down);
+        verify(mFalsingDataProvider, never()).onMotionEvent(any(MotionEvent.class));
+
+        mFalsingCollector.avoidGesture();
+        // Up event would flush, but we were told to avoid.
+        mFalsingCollector.onTouchEvent(up);
+        verify(mFalsingDataProvider, never()).onMotionEvent(any(MotionEvent.class));
+    }
 }
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt
index 5d81de6..59c2b17 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt
@@ -33,6 +33,7 @@
 import androidx.test.filters.SmallTest
 import com.android.systemui.SysuiTestCase
 import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.dump.DumpManager
 import com.android.systemui.tuner.TunerService
 import com.android.systemui.util.concurrency.FakeExecutor
 import com.android.systemui.util.time.FakeSystemClock
@@ -85,6 +86,7 @@
     @Mock private lateinit var sharedPrefsEditor: SharedPreferences.Editor
     @Mock private lateinit var mockContext: Context
     @Mock private lateinit var pendingIntent: PendingIntent
+    @Mock private lateinit var dumpManager: DumpManager
 
     @Captor lateinit var callbackCaptor: ArgumentCaptor<ResumeMediaBrowser.Callback>
 
@@ -120,7 +122,7 @@
 
         executor = FakeExecutor(FakeSystemClock())
         resumeListener = MediaResumeListener(mockContext, broadcastDispatcher, executor,
-                tunerService, resumeBrowserFactory)
+                tunerService, resumeBrowserFactory, dumpManager)
         resumeListener.setManager(mediaDataManager)
         mediaDataManager.addListener(resumeListener)
 
@@ -159,7 +161,7 @@
 
         // When listener is created, we do NOT register a user change listener
         val listener = MediaResumeListener(context, broadcastDispatcher, executor, tunerService,
-                resumeBrowserFactory)
+                resumeBrowserFactory, dumpManager)
         listener.setManager(mediaDataManager)
         verify(broadcastDispatcher, never()).registerReceiver(eq(listener.userChangeReceiver),
             any(), any(), any())
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ImageExporterTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ImageExporterTest.java
new file mode 100644
index 0000000..f2bf7aa
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ImageExporterTest.java
@@ -0,0 +1,171 @@
+/*
+ * 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.systemui.screenshot;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import static java.nio.charset.StandardCharsets.US_ASCII;
+
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Bitmap.CompressFormat;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.net.Uri;
+import android.os.Build;
+import android.os.ParcelFileDescriptor;
+import android.provider.MediaStore;
+import android.testing.AndroidTestingRunner;
+
+import androidx.exifinterface.media.ExifInterface;
+import androidx.test.filters.MediumTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.systemui.SysuiTestCase;
+
+import com.google.common.util.concurrent.ListenableFuture;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
+
+@RunWith(AndroidTestingRunner.class)
+@MediumTest // file I/O
+public class ImageExporterTest extends SysuiTestCase {
+
+    /** Executes directly in the caller's thread */
+    private static final Executor DIRECT_EXECUTOR = Runnable::run;
+    private static final byte[] EXIF_FILE_TAG = "Exif\u0000\u0000".getBytes(US_ASCII);
+
+    private static final ZonedDateTime CAPTURE_TIME =
+            ZonedDateTime.of(LocalDateTime.of(2020, 12, 15, 13, 15), ZoneId.of("EST"));
+
+    @Test
+    public void testImageFilename() {
+        assertEquals("image file name", "Screenshot_20201215-131500.png",
+                ImageExporter.createFilename(CAPTURE_TIME, CompressFormat.PNG));
+    }
+
+    @Test
+    public void testUpdateExifAttributes_timeZoneUTC() throws IOException {
+        ExifInterface exifInterface = new ExifInterface(new ByteArrayInputStream(EXIF_FILE_TAG),
+                ExifInterface.STREAM_TYPE_EXIF_DATA_ONLY);
+
+        ImageExporter.updateExifAttributes(exifInterface, 100, 100,
+                ZonedDateTime.of(LocalDateTime.of(2020, 12, 15, 18, 15), ZoneId.of("UTC")));
+
+        assertEquals("Exif " + ExifInterface.TAG_OFFSET_TIME_ORIGINAL, "+00:00",
+                exifInterface.getAttribute(ExifInterface.TAG_OFFSET_TIME_ORIGINAL));
+    }
+
+    @Test
+    public void testImageExport() throws ExecutionException, InterruptedException, IOException {
+        Context context = InstrumentationRegistry.getInstrumentation().getContext();
+        ContentResolver contentResolver = context.getContentResolver();
+        ImageExporter exporter = new ImageExporter(contentResolver);
+
+        Bitmap original = createCheckerBitmap(10, 10, 10);
+
+        ListenableFuture<Uri> direct = exporter.export(DIRECT_EXECUTOR, original, CAPTURE_TIME);
+        assertTrue("future should be done", direct.isDone());
+        assertFalse("future should not be canceled", direct.isCancelled());
+        Uri result = direct.get();
+
+        assertNotNull("Uri should not be null", result);
+        Bitmap decoded = null;
+        try (InputStream in = contentResolver.openInputStream(result)) {
+            decoded = BitmapFactory.decodeStream(in);
+            assertNotNull("decoded image should not be null", decoded);
+            assertTrue("original and decoded image should be identical", original.sameAs(decoded));
+
+            try (ParcelFileDescriptor pfd = contentResolver.openFile(result, "r", null)) {
+                assertNotNull(pfd);
+                ExifInterface exifInterface = new ExifInterface(pfd.getFileDescriptor());
+
+                assertEquals("Exif " + ExifInterface.TAG_SOFTWARE, "Android " + Build.DISPLAY,
+                        exifInterface.getAttribute(ExifInterface.TAG_SOFTWARE));
+
+                assertEquals("Exif " + ExifInterface.TAG_IMAGE_WIDTH, 100,
+                        exifInterface.getAttributeInt(ExifInterface.TAG_IMAGE_WIDTH, 0));
+                assertEquals("Exif " + ExifInterface.TAG_IMAGE_LENGTH, 100,
+                        exifInterface.getAttributeInt(ExifInterface.TAG_IMAGE_LENGTH, 0));
+
+                assertEquals("Exif " + ExifInterface.TAG_DATETIME_ORIGINAL, "2020:12:15 13:15:00",
+                        exifInterface.getAttribute(ExifInterface.TAG_DATETIME_ORIGINAL));
+                assertEquals("Exif " + ExifInterface.TAG_SUBSEC_TIME_ORIGINAL, "000",
+                        exifInterface.getAttribute(ExifInterface.TAG_SUBSEC_TIME_ORIGINAL));
+                assertEquals("Exif " + ExifInterface.TAG_OFFSET_TIME_ORIGINAL, "-05:00",
+                        exifInterface.getAttribute(ExifInterface.TAG_OFFSET_TIME_ORIGINAL));
+            }
+        } finally {
+            if (decoded != null) {
+                decoded.recycle();
+            }
+            contentResolver.delete(result, null);
+        }
+    }
+
+    @Test
+    public void testMediaStoreMetadata() {
+        ContentValues values = ImageExporter.createMetadata(CAPTURE_TIME, CompressFormat.PNG);
+        assertEquals("Pictures/Screenshots",
+                values.getAsString(MediaStore.MediaColumns.RELATIVE_PATH));
+        assertEquals("Screenshot_20201215-131500.png",
+                values.getAsString(MediaStore.MediaColumns.DISPLAY_NAME));
+        assertEquals("image/png", values.getAsString(MediaStore.MediaColumns.MIME_TYPE));
+        assertEquals(Long.valueOf(1608056100L),
+                values.getAsLong(MediaStore.MediaColumns.DATE_ADDED));
+        assertEquals(Long.valueOf(1608056100L),
+                values.getAsLong(MediaStore.MediaColumns.DATE_MODIFIED));
+        assertEquals(Integer.valueOf(1), values.getAsInteger(MediaStore.MediaColumns.IS_PENDING));
+        assertEquals(Long.valueOf(1608056100L + 86400L), // +1 day
+                values.getAsLong(MediaStore.MediaColumns.DATE_EXPIRES));
+    }
+
+    @SuppressWarnings("SameParameterValue")
+    private Bitmap createCheckerBitmap(int tileSize, int w, int h) {
+        Bitmap bitmap = Bitmap.createBitmap(w * tileSize, h * tileSize, Bitmap.Config.ARGB_8888);
+        Canvas c = new Canvas(bitmap);
+        Paint paint = new Paint();
+        paint.setStyle(Paint.Style.FILL);
+
+        for (int i = 0; i < h; i++) {
+            int top = i * tileSize;
+            for (int j = 0; j < w; j++) {
+                int left = j * tileSize;
+                paint.setColor(paint.getColor() == Color.WHITE ? Color.BLACK : Color.WHITE);
+                c.drawRect(left, top, left + tileSize, top + tileSize, paint);
+            }
+        }
+        return bitmap;
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java
index 6759c90..ced8428 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScreenshotNotificationSmartActionsTest.java
@@ -176,7 +176,7 @@
         data.finisher = null;
         data.mActionsReadyListener = null;
         SaveImageInBackgroundTask task =
-                new SaveImageInBackgroundTask(mContext, mScreenshotSmartActions, data,
+                new SaveImageInBackgroundTask(mContext, null, mScreenshotSmartActions, data,
                         ShareTransition::new);
 
         Notification.Action shareAction = task.createShareAction(mContext, mContext.getResources(),
@@ -204,7 +204,7 @@
         data.finisher = null;
         data.mActionsReadyListener = null;
         SaveImageInBackgroundTask task =
-                new SaveImageInBackgroundTask(mContext, mScreenshotSmartActions, data,
+                new SaveImageInBackgroundTask(mContext, null, mScreenshotSmartActions, data,
                         ShareTransition::new);
 
         Notification.Action editAction = task.createEditAction(mContext, mContext.getResources(),
@@ -232,7 +232,7 @@
         data.finisher = null;
         data.mActionsReadyListener = null;
         SaveImageInBackgroundTask task =
-                new SaveImageInBackgroundTask(mContext, mScreenshotSmartActions, data,
+                new SaveImageInBackgroundTask(mContext, null, mScreenshotSmartActions, data,
                         ShareTransition::new);
 
         Notification.Action deleteAction = task.createDeleteAction(mContext,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureClientTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureClientTest.java
index 4aa730e..c1c6371 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureClientTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureClientTest.java
@@ -96,13 +96,13 @@
 
         Connection conn = mConnectionConsumer.getValue();
 
-        conn.start(5, mSessionConsumer);
+        conn.start(mSessionConsumer);
         verify(mSessionConsumer, timeout(100)).accept(any(Session.class));
 
         Session session = mSessionConsumer.getValue();
-        Rect request = new Rect(0, 0, session.getMaxTileWidth(), session.getMaxTileHeight());
+        Rect request = new Rect(0, 0, session.getPageWidth(), session.getTileHeight());
 
-        session.requestTile(request, mResultConsumer);
+        session.requestTile(0, mResultConsumer);
         verify(mResultConsumer, timeout(100)).accept(any(CaptureResult.class));
 
         CaptureResult result = mResultConsumer.getValue();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/InflatedSmartRepliesTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/InflatedSmartRepliesTest.java
index 8ee15bb..32675c9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/InflatedSmartRepliesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/InflatedSmartRepliesTest.java
@@ -425,7 +425,8 @@
     private void setupAppGeneratedReplies(
             CharSequence[] smartReplies, boolean allowSystemGeneratedReplies) {
         PendingIntent pendingIntent =
-                PendingIntent.getBroadcast(mContext, 0, TEST_INTENT, 0);
+                PendingIntent.getBroadcast(mContext, 0, TEST_INTENT,
+                        PendingIntent.FLAG_MUTABLE);
         Notification.Action action =
                 new Notification.Action.Builder(null, "Test Action", pendingIntent).build();
         when(mRemoteInput.getChoices()).thenReturn(smartReplies);
@@ -456,7 +457,8 @@
     }
 
     private Notification.Action.Builder createActionBuilder(String actionTitle, Intent intent) {
-        PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
+        PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0, intent,
+                PendingIntent.FLAG_MUTABLE);
         return new Notification.Action.Builder(mActionIcon, actionTitle, pendingIntent);
     }
 
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
index 6adf94e..8f5d308 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerBaseTest.java
@@ -40,7 +40,9 @@
 import android.net.ConnectivityManager;
 import android.net.Network;
 import android.net.NetworkCapabilities;
+import android.net.NetworkRequest;
 import android.net.NetworkScoreManager;
+import android.net.wifi.WifiInfo;
 import android.net.wifi.WifiManager;
 import android.os.Handler;
 import android.provider.Settings;
@@ -119,6 +121,7 @@
     protected int mSubId;
 
     private NetworkCapabilities mNetCapabilities;
+    private ConnectivityManager.NetworkCallback mDefaultNetworkCallback;
     private ConnectivityManager.NetworkCallback mNetworkCallback;
 
     @Rule
@@ -232,6 +235,10 @@
             ArgumentCaptor.forClass(ConnectivityManager.NetworkCallback.class);
         verify(mMockCm, atLeastOnce())
             .registerDefaultNetworkCallback(callbackArg.capture(), isA(Handler.class));
+        mDefaultNetworkCallback = callbackArg.getValue();
+        assertNotNull(mDefaultNetworkCallback);
+        verify(mMockCm, atLeastOnce()).registerNetworkCallback(
+                isA(NetworkRequest.class), callbackArg.capture(), isA(Handler.class));
         mNetworkCallback = callbackArg.getValue();
         assertNotNull(mNetworkCallback);
     }
@@ -288,10 +295,19 @@
     }
 
     public void setConnectivityViaCallback(
-        int networkType, boolean validated, boolean isConnected){
+            int networkType, boolean validated, boolean isConnected, WifiInfo wifiInfo) {
+        mNetCapabilities.setTransportInfo(wifiInfo);
         setConnectivityCommon(networkType, validated, isConnected);
-        mNetworkCallback.onCapabilitiesChanged(
+        mDefaultNetworkCallback.onCapabilitiesChanged(
             mock(Network.class), new NetworkCapabilities(mNetCapabilities));
+        if (networkType == NetworkCapabilities.TRANSPORT_WIFI) {
+            if (isConnected) {
+                mNetworkCallback.onCapabilitiesChanged(
+                        mock(Network.class), new NetworkCapabilities(mNetCapabilities));
+            } else {
+                mNetworkCallback.onLost(mock(Network.class));
+            }
+        }
     }
 
     private void setConnectivityCommon(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
index 988e022..9c9d6ea 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/NetworkControllerWifiTest.java
@@ -32,6 +32,7 @@
     // These match the constants in WifiManager and need to be kept up to date.
     private static final int MIN_RSSI = -100;
     private static final int MAX_RSSI = -55;
+    private WifiInfo mWifiInfo = mock(WifiInfo.class);
 
     @Test
     public void testWifiIcon() {
@@ -41,15 +42,16 @@
 
         setWifiState(true, testSsid);
         setWifiLevel(0);
+        setConnectivityViaCallback(NetworkCapabilities.TRANSPORT_WIFI, false, true, mWifiInfo);
         // Connected, but still not validated - does not show
         verifyLastWifiIcon(false, WifiIcons.WIFI_SIGNAL_STRENGTH[0][0]);
 
         for (int testLevel = 0; testLevel < WifiIcons.WIFI_LEVEL_COUNT; testLevel++) {
             setWifiLevel(testLevel);
 
-            setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_WIFI, true, true);
+            setConnectivityViaCallback(NetworkCapabilities.TRANSPORT_WIFI, true, true, mWifiInfo);
             verifyLastWifiIcon(true, WifiIcons.WIFI_SIGNAL_STRENGTH[1][testLevel]);
-            setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_WIFI, false, true);
+            setConnectivityViaCallback(NetworkCapabilities.TRANSPORT_WIFI, false, true, mWifiInfo);
             // Icon does not show if not validated
             verifyLastWifiIcon(false, WifiIcons.WIFI_SIGNAL_STRENGTH[0][testLevel]);
         }
@@ -69,10 +71,10 @@
         for (int testLevel = 0; testLevel < WifiIcons.WIFI_LEVEL_COUNT; testLevel++) {
             setWifiLevel(testLevel);
 
-            setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_WIFI, true, true);
+            setConnectivityViaCallback(NetworkCapabilities.TRANSPORT_WIFI, true, true, mWifiInfo);
             verifyLastQsWifiIcon(true, true, WifiIcons.QS_WIFI_SIGNAL_STRENGTH[1][testLevel],
                     testSsid);
-            setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_WIFI, false, true);
+            setConnectivityViaCallback(NetworkCapabilities.TRANSPORT_WIFI, false, true, mWifiInfo);
             verifyLastQsWifiIcon(true, true, WifiIcons.QS_WIFI_SIGNAL_STRENGTH[0][testLevel],
                     testSsid);
         }
@@ -86,7 +88,7 @@
         setWifiEnabled(true);
         setWifiState(true, testSsid);
         setWifiLevel(testLevel);
-        setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_WIFI, true, true);
+        setConnectivityViaCallback(NetworkCapabilities.TRANSPORT_WIFI, true, true, mWifiInfo);
         verifyLastQsWifiIcon(true, true,
                 WifiIcons.QS_WIFI_SIGNAL_STRENGTH[1][testLevel], testSsid);
 
@@ -111,14 +113,14 @@
         setWifiEnabled(true);
         setWifiState(true, testSsid);
         setWifiLevel(testLevel);
-        setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_WIFI, true, true);
+        setConnectivityViaCallback(NetworkCapabilities.TRANSPORT_WIFI, true, true, mWifiInfo);
         verifyLastWifiIcon(true, WifiIcons.WIFI_SIGNAL_STRENGTH[1][testLevel]);
 
         setupDefaultSignal();
         setGsmRoaming(true);
         // Still be on wifi though.
-        setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_WIFI, true, true);
-        setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_CELLULAR, false, false);
+        setConnectivityViaCallback(NetworkCapabilities.TRANSPORT_WIFI, true, true, mWifiInfo);
+        setConnectivityViaCallback(NetworkCapabilities.TRANSPORT_CELLULAR, false, false, mWifiInfo);
         verifyLastMobileDataIndicators(true,
                 DEFAULT_LEVEL,
                 0, true);
@@ -132,10 +134,10 @@
         setWifiEnabled(true);
         setWifiState(true, testSsid);
         setWifiLevel(testLevel);
-        setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_WIFI, true, true);
+        setConnectivityViaCallback(NetworkCapabilities.TRANSPORT_WIFI, true, true, mWifiInfo);
         verifyLastWifiIcon(true, WifiIcons.WIFI_SIGNAL_STRENGTH[1][testLevel]);
 
-        setConnectivityViaCallback(NetworkCapabilities.TRANSPORT_WIFI, false, true);
+        setConnectivityViaCallback(NetworkCapabilities.TRANSPORT_WIFI, false, true, mWifiInfo);
         verifyLastWifiIcon(false, WifiIcons.WIFI_SIGNAL_STRENGTH[0][testLevel]);
     }
 
@@ -147,11 +149,11 @@
         setWifiEnabled(true);
         setWifiState(true, testSsid);
         setWifiLevel(testLevel);
-        setConnectivityViaBroadcast(NetworkCapabilities.TRANSPORT_WIFI, true, true);
+        setConnectivityViaCallback(NetworkCapabilities.TRANSPORT_WIFI, true, true, mWifiInfo);
         verifyLastWifiIcon(true, WifiIcons.WIFI_SIGNAL_STRENGTH[1][testLevel]);
 
         setWifiState(false, testSsid);
-        setConnectivityViaCallback(NetworkCapabilities.TRANSPORT_WIFI, false, false);
+        setConnectivityViaCallback(NetworkCapabilities.TRANSPORT_WIFI, false, false, mWifiInfo);
         verifyLastWifiIcon(false, WifiIcons.WIFI_NO_NETWORK);
     }
 
@@ -162,14 +164,14 @@
         setWifiEnabled(true);
         verifyLastWifiIcon(false, WifiIcons.WIFI_NO_NETWORK);
 
-        setConnectivityViaCallback(NetworkCapabilities.TRANSPORT_VPN, false, true);
-        setConnectivityViaCallback(NetworkCapabilities.TRANSPORT_VPN, true, true);
+        setConnectivityViaCallback(NetworkCapabilities.TRANSPORT_VPN, false, true, mWifiInfo);
+        setConnectivityViaCallback(NetworkCapabilities.TRANSPORT_VPN, true, true, mWifiInfo);
         verifyLastWifiIcon(false, WifiIcons.WIFI_NO_NETWORK);
 
         // Mock calling setUnderlyingNetworks.
         setWifiState(true, testSsid);
         setWifiLevel(testLevel);
-        setConnectivityViaCallback(NetworkCapabilities.TRANSPORT_WIFI, true, true);
+        setConnectivityViaCallback(NetworkCapabilities.TRANSPORT_WIFI, true, true, mWifiInfo);
         verifyLastWifiIcon(true, WifiIcons.WIFI_SIGNAL_STRENGTH[1][testLevel]);
     }
 
@@ -209,6 +211,7 @@
         int rssi = (int)(MIN_RSSI + level * amountPerLevel);
         // Put RSSI in the middle of the range.
         rssi += amountPerLevel / 2;
+        when(mWifiInfo.getRssi()).thenReturn(rssi);
         Intent i = new Intent(WifiManager.RSSI_CHANGED_ACTION);
         i.putExtra(WifiManager.EXTRA_NEW_RSSI, rssi);
         mNetworkController.onReceive(mContext, i);
@@ -224,10 +227,8 @@
         Intent i = new Intent(WifiManager.NETWORK_STATE_CHANGED_ACTION);
         NetworkInfo networkInfo = mock(NetworkInfo.class);
         when(networkInfo.isConnected()).thenReturn(connected);
-
-        WifiInfo wifiInfo = mock(WifiInfo.class);
-        when(wifiInfo.getSSID()).thenReturn(ssid);
-        when(mMockWm.getConnectionInfo()).thenReturn(wifiInfo);
+        when(mWifiInfo.getSSID()).thenReturn(ssid);
+        when(mMockWm.getConnectionInfo()).thenReturn(mWifiInfo);
 
         i.putExtra(WifiManager.EXTRA_NETWORK_INFO, networkInfo);
         mNetworkController.onReceive(mContext, i);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
index 4fb85ad..a844d09 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/RemoteInputViewTest.java
@@ -102,7 +102,7 @@
 
     private void setTestPendingIntent(RemoteInputView view) {
         PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0,
-                new Intent(TEST_ACTION), 0);
+                new Intent(TEST_ACTION), PendingIntent.FLAG_MUTABLE);
         RemoteInput input = new RemoteInput.Builder(TEST_RESULT_KEY).build();
 
         view.setPendingIntent(pendingIntent);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
index 836a81e..5de62b9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SmartReplyViewTest.java
@@ -492,7 +492,8 @@
     private SmartReplyView.SmartReplies createSmartReplies(CharSequence[] choices,
             boolean fromAssistant) {
         PendingIntent pendingIntent =
-                PendingIntent.getBroadcast(mContext, 0, new Intent(TEST_ACTION), 0);
+                PendingIntent.getBroadcast(mContext, 0, new Intent(TEST_ACTION),
+                        PendingIntent.FLAG_MUTABLE);
         RemoteInput input = new RemoteInput.Builder(TEST_RESULT_KEY).setChoices(choices).build();
         return new SmartReplyView.SmartReplies(
                 Arrays.asList(choices), input, pendingIntent, fromAssistant);
@@ -508,7 +509,7 @@
 
     private Notification.Action createAction(String actionTitle) {
         PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0,
-                new Intent(TEST_ACTION), 0);
+                new Intent(TEST_ACTION), PendingIntent.FLAG_MUTABLE);
         return new Notification.Action.Builder(mActionIcon, actionTitle, pendingIntent).build();
     }
 
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index c6919ad..9aa0aed 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -508,6 +508,7 @@
                                 }
                                 it.remove();
                                 userState.getBindingServicesLocked().remove(comp);
+                                userState.getCrashedServicesLocked().remove(comp);
                                 persistComponentNamesToSettingLocked(
                                         Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES,
                                         userState.mEnabledServices, userId);
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 4e75a9e..397eeb2 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -74,7 +74,6 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
-import android.content.res.Configuration;
 import android.database.ContentObserver;
 import android.net.CaptivePortal;
 import android.net.CaptivePortalData;
@@ -90,12 +89,10 @@
 import android.net.IDnsResolver;
 import android.net.IIpConnectivityMetrics;
 import android.net.INetd;
-import android.net.INetdEventCallback;
 import android.net.INetworkManagementEventObserver;
 import android.net.INetworkMonitor;
 import android.net.INetworkMonitorCallbacks;
 import android.net.INetworkPolicyListener;
-import android.net.INetworkPolicyManager;
 import android.net.INetworkStatsService;
 import android.net.ISocketKeepaliveCallback;
 import android.net.InetAddresses;
@@ -133,6 +130,7 @@
 import android.net.Uri;
 import android.net.VpnManager;
 import android.net.VpnService;
+import android.net.metrics.INetdEventListener;
 import android.net.metrics.IpConnectivityLog;
 import android.net.metrics.NetworkEvent;
 import android.net.netlink.InetDiagMessage;
@@ -173,7 +171,6 @@
 import android.util.Pair;
 import android.util.SparseArray;
 import android.util.SparseIntArray;
-import android.util.Xml;
 
 import com.android.connectivity.aidl.INetworkAgent;
 import com.android.internal.R;
@@ -190,7 +187,6 @@
 import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.LocationPermissionChecker;
 import com.android.internal.util.MessageUtils;
-import com.android.internal.util.XmlUtils;
 import com.android.modules.utils.BasicShellCommandHandler;
 import com.android.net.module.util.LinkPropertiesUtils.CompareOrUpdateResult;
 import com.android.net.module.util.LinkPropertiesUtils.CompareResult;
@@ -211,7 +207,6 @@
 import com.android.server.connectivity.PermissionMonitor;
 import com.android.server.connectivity.ProxyTracker;
 import com.android.server.connectivity.Vpn;
-import com.android.server.net.BaseNetdEventCallback;
 import com.android.server.net.BaseNetworkObserver;
 import com.android.server.net.LockdownVpnTracker;
 import com.android.server.net.NetworkPolicyManagerInternal;
@@ -221,14 +216,7 @@
 
 import libcore.io.IoUtils;
 
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.File;
 import java.io.FileDescriptor;
-import java.io.FileNotFoundException;
-import java.io.FileReader;
-import java.io.IOException;
 import java.io.PrintWriter;
 import java.net.Inet4Address;
 import java.net.InetAddress;
@@ -337,7 +325,7 @@
     @VisibleForTesting
     protected INetd mNetd;
     private INetworkStatsService mStatsService;
-    private INetworkPolicyManager mPolicyManager;
+    private NetworkPolicyManager mPolicyManager;
     private NetworkPolicyManagerInternal mPolicyManagerInternal;
 
     /**
@@ -558,6 +546,13 @@
     private static final int EVENT_CAPPORT_DATA_CHANGED = 46;
 
     /**
+     * Used by setRequireVpnForUids.
+     * arg1 = whether the specified UID ranges are required to use a VPN.
+     * obj  = Array of UidRange objects.
+     */
+    private static final int EVENT_SET_REQUIRE_VPN_FOR_UIDS = 47;
+
+    /**
      * Argument for {@link #EVENT_PROVISIONING_NOTIFICATION} to indicate that the notification
      * should be shown.
      */
@@ -946,15 +941,15 @@
     }
 
     public ConnectivityService(Context context, INetworkManagementService netManager,
-            INetworkStatsService statsService, INetworkPolicyManager policyManager) {
-        this(context, netManager, statsService, policyManager, getDnsResolver(context),
-                new IpConnectivityLog(), NetdService.getInstance(), new Dependencies());
+            INetworkStatsService statsService) {
+        this(context, netManager, statsService, getDnsResolver(context), new IpConnectivityLog(),
+                NetdService.getInstance(), new Dependencies());
     }
 
     @VisibleForTesting
     protected ConnectivityService(Context context, INetworkManagementService netManager,
-            INetworkStatsService statsService, INetworkPolicyManager policyManager,
-            IDnsResolver dnsresolver, IpConnectivityLog logger, INetd netd, Dependencies deps) {
+            INetworkStatsService statsService, IDnsResolver dnsresolver, IpConnectivityLog logger,
+            INetd netd, Dependencies deps) {
         if (DBG) log("ConnectivityService starting up");
 
         mDeps = Objects.requireNonNull(deps, "missing Dependencies");
@@ -992,7 +987,7 @@
 
         mNMS = Objects.requireNonNull(netManager, "missing INetworkManagementService");
         mStatsService = Objects.requireNonNull(statsService, "missing INetworkStatsService");
-        mPolicyManager = Objects.requireNonNull(policyManager, "missing INetworkPolicyManager");
+        mPolicyManager = mContext.getSystemService(NetworkPolicyManager.class);
         mPolicyManagerInternal = Objects.requireNonNull(
                 LocalServices.getService(NetworkPolicyManagerInternal.class),
                 "missing NetworkPolicyManagerInternal");
@@ -1008,12 +1003,7 @@
         // To ensure uid rules are synchronized with Network Policy, register for
         // NetworkPolicyManagerService events must happen prior to NetworkPolicyManagerService
         // reading existing policy from disk.
-        try {
-            mPolicyManager.registerListener(mPolicyListener);
-        } catch (RemoteException e) {
-            // ouch, no rules updates means some processes may never get network
-            loge("unable to register INetworkPolicyListener" + e);
-        }
+        mPolicyManager.registerListener(mPolicyListener);
 
         final PowerManager powerManager = (PowerManager) context.getSystemService(
                 Context.POWER_SERVICE);
@@ -1290,19 +1280,28 @@
         }
     }
 
-    private Network[] getVpnUnderlyingNetworks(int uid) {
-        synchronized (mVpns) {
-            if (!mLockdownEnabled) {
-                int user = UserHandle.getUserId(uid);
-                Vpn vpn = mVpns.get(user);
-                if (vpn != null && vpn.appliesToUid(uid)) {
-                    return vpn.getUnderlyingNetworks();
+    // TODO: determine what to do when more than one VPN applies to |uid|.
+    private NetworkAgentInfo getVpnForUid(int uid) {
+        synchronized (mNetworkForNetId) {
+            for (int i = 0; i < mNetworkForNetId.size(); i++) {
+                final NetworkAgentInfo nai = mNetworkForNetId.valueAt(i);
+                if (nai.isVPN() && nai.everConnected && nai.networkCapabilities.appliesToUid(uid)) {
+                    return nai;
                 }
             }
         }
         return null;
     }
 
+    private Network[] getVpnUnderlyingNetworks(int uid) {
+        synchronized (mVpns) {
+            if (mLockdownEnabled) return null;
+        }
+        final NetworkAgentInfo nai = getVpnForUid(uid);
+        if (nai != null) return nai.declaredUnderlyingNetworks;
+        return null;
+    }
+
     private NetworkState getUnfilteredActiveNetworkState(int uid) {
         NetworkAgentInfo nai = getDefaultNetwork();
 
@@ -1328,7 +1327,7 @@
     }
 
     /**
-     * Check if UID should be blocked from using the network with the given LinkProperties.
+     * Check if UID should be blocked from using the specified network.
      */
     private boolean isNetworkWithLinkPropertiesBlocked(LinkProperties lp, int uid,
             boolean ignoreBlocked) {
@@ -1336,12 +1335,7 @@
         if (ignoreBlocked) {
             return false;
         }
-        synchronized (mVpns) {
-            final Vpn vpn = mVpns.get(UserHandle.getUserId(uid));
-            if (vpn != null && vpn.getLockdown() && vpn.isBlockingUid(uid)) {
-                return true;
-            }
-        }
+        if (isUidBlockedByVpn(uid, mVpnBlockedUidRanges)) return true;
         final String iface = (lp == null ? "" : lp.getInterfaceName());
         return mPolicyManagerInternal.isUidNetworkingBlocked(uid, iface);
     }
@@ -1567,22 +1561,14 @@
                             nc, mDeps.getCallingUid(), callingPackageName));
         }
 
-        synchronized (mVpns) {
-            if (!mLockdownEnabled) {
-                Vpn vpn = mVpns.get(userId);
-                if (vpn != null) {
-                    Network[] networks = vpn.getUnderlyingNetworks();
-                    if (networks != null) {
-                        for (Network network : networks) {
-                            nc = getNetworkCapabilitiesInternal(network);
-                            if (nc != null) {
-                                result.put(
-                                        network,
-                                        maybeSanitizeLocationInfoForCaller(
-                                                nc, mDeps.getCallingUid(), callingPackageName));
-                            }
-                        }
-                    }
+        // No need to check mLockdownEnabled. If it's true, getVpnUnderlyingNetworks returns null.
+        final Network[] networks = getVpnUnderlyingNetworks(Binder.getCallingUid());
+        if (networks != null) {
+            for (Network network : networks) {
+                nc = getNetworkCapabilitiesInternal(network);
+                if (nc != null) {
+                    result.put(network, maybeSanitizeLocationInfoForCaller(
+                                    nc, mDeps.getCallingUid(), callingPackageName));
                 }
             }
         }
@@ -1920,8 +1906,7 @@
         return true;
     }
 
-    @VisibleForTesting
-    protected final INetdEventCallback mNetdEventCallback = new BaseNetdEventCallback() {
+    private class NetdEventCallback extends INetdEventListener.Stub {
         @Override
         public void onPrivateDnsValidationEvent(int netId, String ipAddress,
                 String hostname, boolean validated) {
@@ -1937,8 +1922,8 @@
         }
 
         @Override
-        public void onDnsEvent(int netId, int eventType, int returnCode, String hostname,
-                String[] ipAddresses, int ipAddressesCount, long timestamp, int uid) {
+        public void onDnsEvent(int netId, int eventType, int returnCode, int latencyMs,
+                String hostname,  String[] ipAddresses, int ipAddressesCount, int uid) {
             NetworkAgentInfo nai = getNetworkAgentInfoForNetId(netId);
             // Netd event only allow registrants from system. Each NetworkMonitor thread is under
             // the caller thread of registerNetworkAgent. Thus, it's not allowed to register netd
@@ -1957,21 +1942,42 @@
                                        String prefixString, int prefixLength) {
             mHandler.post(() -> handleNat64PrefixEvent(netId, added, prefixString, prefixLength));
         }
-    };
 
-    private void registerNetdEventCallback() {
-        final IIpConnectivityMetrics ipConnectivityMetrics = mDeps.getIpConnectivityMetrics();
-        if (ipConnectivityMetrics == null) {
-            Log.wtf(TAG, "Missing IIpConnectivityMetrics");
-            return;
+        @Override
+        public void onConnectEvent(int netId, int error, int latencyMs, String ipAddr, int port,
+                int uid) {
         }
 
+        @Override
+        public void onWakeupEvent(String prefix, int uid, int ethertype, int ipNextHeader,
+                byte[] dstHw, String srcIp, String dstIp, int srcPort, int dstPort,
+                long timestampNs) {
+        }
+
+        @Override
+        public void onTcpSocketStatsEvent(int[] networkIds, int[] sentPackets, int[] lostPackets,
+                int[] rttsUs, int[] sentAckDiffsMs) {
+        }
+
+        @Override
+        public int getInterfaceVersion() throws RemoteException {
+            return this.VERSION;
+        }
+
+        @Override
+        public String getInterfaceHash() {
+            return this.HASH;
+        }
+    };
+
+    @VisibleForTesting
+    protected final INetdEventListener mNetdEventCallback = new NetdEventCallback();
+
+    private void registerNetdEventCallback() {
         try {
-            ipConnectivityMetrics.addNetdEventCallback(
-                    INetdEventCallback.CALLBACK_CALLER_CONNECTIVITY_SERVICE,
-                    mNetdEventCallback);
+            mDnsResolver.registerEventListener(mNetdEventCallback);
         } catch (Exception e) {
-            loge("Error registering netd callback: " + e);
+            loge("Error registering DnsResolver callback: " + e);
         }
     }
 
@@ -2008,29 +2014,18 @@
     void handleRestrictBackgroundChanged(boolean restrictBackground) {
         if (mRestrictBackground == restrictBackground) return;
 
+        final List<UidRange> blockedRanges = mVpnBlockedUidRanges;
         for (final NetworkAgentInfo nai : mNetworkAgentInfos) {
             final boolean curMetered = nai.networkCapabilities.isMetered();
             maybeNotifyNetworkBlocked(nai, curMetered, curMetered, mRestrictBackground,
-                    restrictBackground);
+                    restrictBackground, blockedRanges, blockedRanges);
         }
 
         mRestrictBackground = restrictBackground;
     }
 
-    private boolean isUidNetworkingWithVpnBlocked(int uid, int uidRules, boolean isNetworkMetered,
+    private boolean isUidBlockedByRules(int uid, int uidRules, boolean isNetworkMetered,
             boolean isBackgroundRestricted) {
-        synchronized (mVpns) {
-            final Vpn vpn = mVpns.get(UserHandle.getUserId(uid));
-            // Because the return value of this function depends on the list of UIDs the
-            // always-on VPN blocks when in lockdown mode, when the always-on VPN changes that
-            // list all state depending on the return value of this function has to be recomputed.
-            // TODO: add a trigger when the always-on VPN sets its blocked UIDs to reevaluate and
-            // send the necessary onBlockedStatusChanged callbacks.
-            if (vpn != null && vpn.getLockdown() && vpn.isBlockingUid(uid)) {
-                return true;
-            }
-        }
-
         return NetworkPolicyManagerInternal.isUidNetworkingBlocked(uid, uidRules,
                 isNetworkMetered, isBackgroundRestricted);
     }
@@ -3597,8 +3592,8 @@
     private boolean isNetworkPotentialSatisfier(
             @NonNull final NetworkAgentInfo candidate, @NonNull final NetworkRequestInfo nri) {
         // listen requests won't keep up a network satisfying it. If this is not a multilayer
-        // request, we can return immediately. For multilayer requests, we have to check to see if
-        // any of the multilayer requests may have a potential satisfier.
+        // request, return immediately. For multilayer requests, check to see if any of the
+        // multilayer requests may have a potential satisfier.
         if (!nri.isMultilayerRequest() && nri.mRequests.get(0).isListen()) {
             return false;
         }
@@ -4305,6 +4300,9 @@
                 case EVENT_DATA_SAVER_CHANGED:
                     handleRestrictBackgroundChanged(toBool(msg.arg1));
                     break;
+                case EVENT_SET_REQUIRE_VPN_FOR_UIDS:
+                    handleSetRequireVpnForUids(toBool(msg.arg1), (UidRange[]) msg.obj);
+                    break;
             }
         }
     }
@@ -4473,8 +4471,7 @@
         if (!nai.everConnected) {
             return;
         }
-        LinkProperties lp = getLinkProperties(nai);
-        if (isNetworkWithLinkPropertiesBlocked(lp, uid, false)) {
+        if (isNetworkWithLinkPropertiesBlocked(nai.linkProperties, uid, false)) {
             return;
         }
         nai.networkMonitor().forceReevaluation(uid);
@@ -4901,6 +4898,56 @@
         }
     }
 
+    private boolean isUidBlockedByVpn(int uid, List<UidRange> blockedUidRanges) {
+        // Determine whether this UID is blocked because of always-on VPN lockdown. If a VPN applies
+        // to the UID, then the UID is not blocked because always-on VPN lockdown applies only when
+        // a VPN is not up.
+        final NetworkAgentInfo vpnNai = getVpnForUid(uid);
+        if (vpnNai != null && !vpnNai.networkAgentConfig.allowBypass) return false;
+        for (UidRange range : blockedUidRanges) {
+            if (range.contains(uid)) return true;
+        }
+        return false;
+    }
+
+    @Override
+    public void setRequireVpnForUids(boolean requireVpn, UidRange[] ranges) {
+        NetworkStack.checkNetworkStackPermission(mContext);
+        mHandler.sendMessage(mHandler.obtainMessage(EVENT_SET_REQUIRE_VPN_FOR_UIDS,
+                encodeBool(requireVpn), 0 /* arg2 */, ranges));
+    }
+
+    private void handleSetRequireVpnForUids(boolean requireVpn, UidRange[] ranges) {
+        if (DBG) {
+            Log.d(TAG, "Setting VPN " + (requireVpn ? "" : "not ") + "required for UIDs: "
+                    + Arrays.toString(ranges));
+        }
+        // Cannot use a Set since the list of UID ranges might contain duplicates.
+        final List<UidRange> newVpnBlockedUidRanges = new ArrayList(mVpnBlockedUidRanges);
+        for (int i = 0; i < ranges.length; i++) {
+            if (requireVpn) {
+                newVpnBlockedUidRanges.add(ranges[i]);
+            } else {
+                newVpnBlockedUidRanges.remove(ranges[i]);
+            }
+        }
+
+        try {
+            mNetd.networkRejectNonSecureVpn(requireVpn, toUidRangeStableParcels(ranges));
+        } catch (RemoteException | ServiceSpecificException e) {
+            Log.e(TAG, "setRequireVpnForUids(" + requireVpn + ", "
+                    + Arrays.toString(ranges) + "): netd command failed: " + e);
+        }
+
+        for (final NetworkAgentInfo nai : mNetworkAgentInfos) {
+            final boolean curMetered = nai.networkCapabilities.isMetered();
+            maybeNotifyNetworkBlocked(nai, curMetered, curMetered, mRestrictBackground,
+                    mRestrictBackground, mVpnBlockedUidRanges, newVpnBlockedUidRanges);
+        }
+
+        mVpnBlockedUidRanges = newVpnBlockedUidRanges;
+    }
+
     @Override
     public boolean updateLockdownVpn() {
         if (mDeps.getCallingUid() != Process.SYSTEM_UID) {
@@ -5085,101 +5132,6 @@
     }
 
     @Override
-    public int checkMobileProvisioning(int suggestedTimeOutMs) {
-        // TODO: Remove?  Any reason to trigger a provisioning check?
-        return -1;
-    }
-
-    /** Location to an updatable file listing carrier provisioning urls.
-     *  An example:
-     *
-     * <?xml version="1.0" encoding="utf-8"?>
-     *  <provisioningUrls>
-     *   <provisioningUrl mcc="310" mnc="4">http://myserver.com/foo?mdn=%3$s&amp;iccid=%1$s&amp;imei=%2$s</provisioningUrl>
-     *  </provisioningUrls>
-     */
-    private static final String PROVISIONING_URL_PATH =
-            "/data/misc/radio/provisioning_urls.xml";
-    private final File mProvisioningUrlFile = new File(PROVISIONING_URL_PATH);
-
-    /** XML tag for root element. */
-    private static final String TAG_PROVISIONING_URLS = "provisioningUrls";
-    /** XML tag for individual url */
-    private static final String TAG_PROVISIONING_URL = "provisioningUrl";
-    /** XML attribute for mcc */
-    private static final String ATTR_MCC = "mcc";
-    /** XML attribute for mnc */
-    private static final String ATTR_MNC = "mnc";
-
-    private String getProvisioningUrlBaseFromFile() {
-        XmlPullParser parser;
-        Configuration config = mContext.getResources().getConfiguration();
-
-        try (FileReader fileReader = new FileReader(mProvisioningUrlFile)) {
-            parser = Xml.newPullParser();
-            parser.setInput(fileReader);
-            XmlUtils.beginDocument(parser, TAG_PROVISIONING_URLS);
-
-            while (true) {
-                XmlUtils.nextElement(parser);
-
-                String element = parser.getName();
-                if (element == null) break;
-
-                if (element.equals(TAG_PROVISIONING_URL)) {
-                    String mcc = parser.getAttributeValue(null, ATTR_MCC);
-                    try {
-                        if (mcc != null && Integer.parseInt(mcc) == config.mcc) {
-                            String mnc = parser.getAttributeValue(null, ATTR_MNC);
-                            if (mnc != null && Integer.parseInt(mnc) == config.mnc) {
-                                parser.next();
-                                if (parser.getEventType() == XmlPullParser.TEXT) {
-                                    return parser.getText();
-                                }
-                            }
-                        }
-                    } catch (NumberFormatException e) {
-                        loge("NumberFormatException in getProvisioningUrlBaseFromFile: " + e);
-                    }
-                }
-            }
-            return null;
-        } catch (FileNotFoundException e) {
-            loge("Carrier Provisioning Urls file not found");
-        } catch (XmlPullParserException e) {
-            loge("Xml parser exception reading Carrier Provisioning Urls file: " + e);
-        } catch (IOException e) {
-            loge("I/O exception reading Carrier Provisioning Urls file: " + e);
-        }
-        return null;
-    }
-
-    @Override
-    public String getMobileProvisioningUrl() {
-        enforceSettingsPermission();
-        String url = getProvisioningUrlBaseFromFile();
-        if (TextUtils.isEmpty(url)) {
-            url = mContext.getResources().getString(R.string.mobile_provisioning_url);
-            log("getMobileProvisioningUrl: mobile_provisioining_url from resource =" + url);
-        } else {
-            log("getMobileProvisioningUrl: mobile_provisioning_url from File =" + url);
-        }
-        // populate the iccid, imei and phone number in the provisioning url.
-        if (!TextUtils.isEmpty(url)) {
-            String phoneNumber = mTelephonyManager.getLine1Number();
-            if (TextUtils.isEmpty(phoneNumber)) {
-                phoneNumber = "0000000000";
-            }
-            url = String.format(url,
-                    mTelephonyManager.getSimSerialNumber() /* ICCID */,
-                    mTelephonyManager.getDeviceId() /* IMEI */,
-                    phoneNumber /* Phone number */);
-        }
-
-        return url;
-    }
-
-    @Override
     public void setProvisioningNotificationVisible(boolean visible, int networkType,
             String action) {
         enforceSettingsPermission();
@@ -5981,6 +5933,12 @@
     // NOTE: Only should be accessed on ConnectivityServiceThread, except dump().
     private final ArraySet<NetworkAgentInfo> mNetworkAgentInfos = new ArraySet<>();
 
+    // UID ranges for users that are currently blocked by VPNs.
+    // This array is accessed and iterated on multiple threads without holding locks, so its
+    // contents must never be mutated. When the ranges change, the array is replaced with a new one
+    // (on the handler thread).
+    private volatile List<UidRange> mVpnBlockedUidRanges = new ArrayList<>();
+
     @GuardedBy("mBlockedAppUids")
     private final HashSet<Integer> mBlockedAppUids = new HashSet<>();
 
@@ -6635,7 +6593,7 @@
 
             if (meteredChanged) {
                 maybeNotifyNetworkBlocked(nai, oldMetered, newMetered, mRestrictBackground,
-                        mRestrictBackground);
+                        mRestrictBackground, mVpnBlockedUidRanges, mVpnBlockedUidRanges);
             }
 
             final boolean roamingChanged = prevNc.hasCapability(NET_CAPABILITY_NOT_ROAMING) !=
@@ -6700,6 +6658,15 @@
         return stableRanges;
     }
 
+    private static UidRangeParcel[] toUidRangeStableParcels(UidRange[] ranges) {
+        final UidRangeParcel[] stableRanges = new UidRangeParcel[ranges.length];
+        for (int i = 0; i < ranges.length; i++) {
+            stableRanges[i] = new UidRangeParcel(ranges[i].start, ranges[i].stop);
+        }
+        return stableRanges;
+    }
+
+
     private void updateUids(NetworkAgentInfo nai, NetworkCapabilities prevNc,
             NetworkCapabilities newNc) {
         Set<UidRange> prevRanges = null == prevNc ? null : prevNc.getUids();
@@ -7527,7 +7494,9 @@
         }
 
         final boolean metered = nai.networkCapabilities.isMetered();
-        final boolean blocked = isUidNetworkingWithVpnBlocked(nri.mUid, mUidRules.get(nri.mUid),
+        boolean blocked;
+        blocked = isUidBlockedByVpn(nri.mUid, mVpnBlockedUidRanges);
+        blocked |= isUidBlockedByRules(nri.mUid, mUidRules.get(nri.mUid),
                 metered, mRestrictBackground);
         callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_AVAILABLE, blocked ? 1 : 0);
     }
@@ -7549,21 +7518,25 @@
      * @param newRestrictBackground True if data saver is enabled.
      */
     private void maybeNotifyNetworkBlocked(NetworkAgentInfo nai, boolean oldMetered,
-            boolean newMetered, boolean oldRestrictBackground, boolean newRestrictBackground) {
+            boolean newMetered, boolean oldRestrictBackground, boolean newRestrictBackground,
+            List<UidRange> oldBlockedUidRanges, List<UidRange> newBlockedUidRanges) {
 
         for (int i = 0; i < nai.numNetworkRequests(); i++) {
             NetworkRequest nr = nai.requestAt(i);
             NetworkRequestInfo nri = mNetworkRequests.get(nr);
             final int uidRules = mUidRules.get(nri.mUid);
-            final boolean oldBlocked, newBlocked;
-            // mVpns lock needs to be hold here to ensure that the active VPN cannot be changed
-            // between these two calls.
-            synchronized (mVpns) {
-                oldBlocked = isUidNetworkingWithVpnBlocked(nri.mUid, uidRules, oldMetered,
-                        oldRestrictBackground);
-                newBlocked = isUidNetworkingWithVpnBlocked(nri.mUid, uidRules, newMetered,
-                        newRestrictBackground);
-            }
+            final boolean oldBlocked, newBlocked, oldVpnBlocked, newVpnBlocked;
+
+            oldVpnBlocked = isUidBlockedByVpn(nri.mUid, oldBlockedUidRanges);
+            newVpnBlocked = (oldBlockedUidRanges != newBlockedUidRanges)
+                    ? isUidBlockedByVpn(nri.mUid, newBlockedUidRanges)
+                    : oldVpnBlocked;
+
+            oldBlocked = oldVpnBlocked || isUidBlockedByRules(nri.mUid, uidRules, oldMetered,
+                    oldRestrictBackground);
+            newBlocked = newVpnBlocked || isUidBlockedByRules(nri.mUid, uidRules, newMetered,
+                    newRestrictBackground);
+
             if (oldBlocked != newBlocked) {
                 callCallbackForRequest(nri, nai, ConnectivityManager.CALLBACK_BLK_CHANGED,
                         encodeBool(newBlocked));
@@ -7579,17 +7552,12 @@
     private void maybeNotifyNetworkBlockedForNewUidRules(int uid, int newRules) {
         for (final NetworkAgentInfo nai : mNetworkAgentInfos) {
             final boolean metered = nai.networkCapabilities.isMetered();
+            final boolean vpnBlocked = isUidBlockedByVpn(uid, mVpnBlockedUidRanges);
             final boolean oldBlocked, newBlocked;
-            // TODO: Consider that doze mode or turn on/off battery saver would deliver lots of uid
-            // rules changed event. And this function actually loop through all connected nai and
-            // its requests. It seems that mVpns lock will be grabbed frequently in this case.
-            // Reduce the number of locking or optimize the use of lock are likely needed in future.
-            synchronized (mVpns) {
-                oldBlocked = isUidNetworkingWithVpnBlocked(
-                        uid, mUidRules.get(uid), metered, mRestrictBackground);
-                newBlocked = isUidNetworkingWithVpnBlocked(
-                        uid, newRules, metered, mRestrictBackground);
-            }
+            oldBlocked = vpnBlocked || isUidBlockedByRules(
+                    uid, mUidRules.get(uid), metered, mRestrictBackground);
+            newBlocked = vpnBlocked || isUidBlockedByRules(
+                    uid, newRules, metered, mRestrictBackground);
             if (oldBlocked == newBlocked) {
                 continue;
             }
@@ -8234,6 +8202,13 @@
         final IBinder iCb = cb.asBinder();
         final NetworkRequestInfo nri = cbInfo.mRequestInfo;
 
+        // Connectivity Diagnostics are meant to be used with a single network request. It would be
+        // confusing for these networks to change when an NRI is satisfied in another layer.
+        if (nri.isMultilayerRequest()) {
+            throw new IllegalArgumentException("Connectivity Diagnostics do not support multilayer "
+                + "network requests.");
+        }
+
         // This means that the client registered the same callback multiple times. Do
         // not override the previous entry, and exit silently.
         if (mConnectivityDiagnosticsCallbacks.containsKey(iCb)) {
@@ -8260,7 +8235,8 @@
         synchronized (mNetworkForNetId) {
             for (int i = 0; i < mNetworkForNetId.size(); i++) {
                 final NetworkAgentInfo nai = mNetworkForNetId.valueAt(i);
-                if (nai.satisfies(nri.request)) {
+                // Connectivity Diagnostics rejects multilayer requests at registration hence get(0)
+                if (nai.satisfies(nri.mRequests.get(0))) {
                     matchingNetworks.add(nai);
                 }
             }
@@ -8388,7 +8364,8 @@
                 mConnectivityDiagnosticsCallbacks.entrySet()) {
             final ConnectivityDiagnosticsCallbackInfo cbInfo = entry.getValue();
             final NetworkRequestInfo nri = cbInfo.mRequestInfo;
-            if (nai.satisfies(nri.request)) {
+            // Connectivity Diagnostics rejects multilayer requests at registration hence get(0).
+            if (nai.satisfies(nri.mRequests.get(0))) {
                 if (checkConnectivityDiagnosticsPermissions(
                         nri.mPid, nri.mUid, nai, cbInfo.mCallingPackageName)) {
                     results.add(entry.getValue().mCb);
diff --git a/services/core/java/com/android/server/ConnectivityServiceInitializer.java b/services/core/java/com/android/server/ConnectivityServiceInitializer.java
index 2bc8925..f701688 100644
--- a/services/core/java/com/android/server/ConnectivityServiceInitializer.java
+++ b/services/core/java/com/android/server/ConnectivityServiceInitializer.java
@@ -20,7 +20,6 @@
 import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL;
 
 import android.content.Context;
-import android.net.INetworkPolicyManager;
 import android.net.INetworkStatsService;
 import android.os.INetworkManagementService;
 import android.os.ServiceManager;
@@ -38,7 +37,7 @@
         super(context);
         // TODO: Define formal APIs to get the needed services.
         mConnectivity = new ConnectivityService(context, getNetworkManagementService(),
-                getNetworkStatsService(), getNetworkPolicyManager());
+                getNetworkStatsService());
     }
 
     @Override
@@ -57,10 +56,4 @@
         return INetworkStatsService.Stub.asInterface(
                 ServiceManager.getService(Context.NETWORK_STATS_SERVICE));
     }
-
-    private INetworkPolicyManager getNetworkPolicyManager() {
-        return INetworkPolicyManager.Stub.asInterface(
-                ServiceManager.getService(Context.NETWORK_POLICY_SERVICE));
-    }
-
 }
diff --git a/services/core/java/com/android/server/DynamicSystemService.java b/services/core/java/com/android/server/DynamicSystemService.java
index 500e768..f2b63a6 100644
--- a/services/core/java/com/android/server/DynamicSystemService.java
+++ b/services/core/java/com/android/server/DynamicSystemService.java
@@ -236,4 +236,9 @@
             throw new RuntimeException(e.toString());
         }
     }
+
+    @Override
+    public long suggestScratchSize() throws RemoteException {
+        return getGsiService().suggestScratchSize();
+    }
 }
diff --git a/services/core/java/com/android/server/IntentResolver.java b/services/core/java/com/android/server/IntentResolver.java
index ea1ac0c..2906cee 100644
--- a/services/core/java/com/android/server/IntentResolver.java
+++ b/services/core/java/com/android/server/IntentResolver.java
@@ -839,14 +839,22 @@
         }
     };
 
-    // Make <this> a copy of <orig>.  The presumption is that <this> is empty.
-    protected void doCopy(IntentResolver orig) {
+    // Make <this> a copy of <orig>.  The presumption is that <this> is empty but all
+    // arrays are cleared out explicitly, just to be sure.
+    protected void copyFrom(IntentResolver orig) {
+        mFilters.clear();
         mFilters.addAll(orig.mFilters);
+        mTypeToFilter.clear();
         mTypeToFilter.putAll(orig.mTypeToFilter);
+        mBaseTypeToFilter.clear();
         mBaseTypeToFilter.putAll(orig.mBaseTypeToFilter);
+        mWildTypeToFilter.clear();
         mWildTypeToFilter.putAll(orig.mWildTypeToFilter);
+        mSchemeToFilter.clear();
         mSchemeToFilter.putAll(orig.mSchemeToFilter);
+        mActionToFilter.clear();
         mActionToFilter.putAll(orig.mActionToFilter);
+        mTypedActionToFilter.clear();
         mTypedActionToFilter.putAll(orig.mTypedActionToFilter);
     }
 
diff --git a/services/core/java/com/android/server/OWNERS b/services/core/java/com/android/server/OWNERS
index 046d927..dfcc325 100644
--- a/services/core/java/com/android/server/OWNERS
+++ b/services/core/java/com/android/server/OWNERS
@@ -1,5 +1,5 @@
 # Connectivity / Networking
-per-file ConnectivityService.java,NetworkManagementService.java,NsdService.java = codewiz@google.com, ek@google.com, jchalard@google.com, junyulai@google.com, lorenzo@google.com, reminv@google.com, satk@google.com
+per-file ConnectivityService.java,ConnectivityServiceInitializer.java,NetworkManagementService.java,NsdService.java = file:/services/core/java/com/android/server/net/OWNERS
 
 # Vibrator / Threads
 per-file VibratorManagerService.java, VibratorService.java, DisplayThread.java = michaelwr@google.com, ogunwale@google.com
@@ -13,6 +13,9 @@
 # Sensor Privacy
 per-file SensorPrivacyService.java = file:platform/frameworks/native:/libs/sensorprivacy/OWNERS
 
+# ServiceWatcher
+per-file ServiceWatcher.java = sooniln@google.com
+
 per-file *Alarm* = file:/apex/jobscheduler/OWNERS
 per-file *AppOp* = file:/core/java/android/permission/OWNERS
 per-file *Bluetooth* = file:/core/java/android/bluetooth/OWNERS
@@ -21,7 +24,6 @@
 per-file *Network* = file:/services/core/java/com/android/server/net/OWNERS
 per-file *Storage* = file:/core/java/android/os/storage/OWNERS
 per-file *TimeUpdate* = file:/core/java/android/app/timezone/OWNERS
-per-file ConnectivityService.java = file:/services/core/java/com/android/server/net/OWNERS
 per-file DynamicSystemService.java = file:/packages/DynamicSystemInstallationService/OWNERS
 per-file GestureLauncherService.java = file:platform/packages/apps/EmergencyInfo:/OWNERS
 per-file IpSecService.java = file:/services/core/java/com/android/server/net/OWNERS
diff --git a/services/core/java/com/android/server/ServiceWatcher.java b/services/core/java/com/android/server/ServiceWatcher.java
index 3ccb6e5..c2d8fa2 100644
--- a/services/core/java/com/android/server/ServiceWatcher.java
+++ b/services/core/java/com/android/server/ServiceWatcher.java
@@ -26,6 +26,7 @@
 import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
 
 import android.annotation.BoolRes;
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.StringRes;
 import android.annotation.UserIdInt;
@@ -53,6 +54,7 @@
 import java.io.PrintWriter;
 import java.util.List;
 import java.util.Objects;
+import java.util.function.Predicate;
 
 /**
  * Maintains a binding to the best service that matches the given intent information. Bind and
@@ -68,6 +70,8 @@
 
     private static final long RETRY_DELAY_MS = 15 * 1000;
 
+    private static final Predicate<ResolveInfo> DEFAULT_SERVICE_CHECK_PREDICATE = x -> true;
+
     /** Function to run on binder interface. */
     public interface BinderRunner {
         /** Called to run client code with the binder. */
@@ -184,6 +188,7 @@
     private final Context mContext;
     private final Handler mHandler;
     private final Intent mIntent;
+    private final Predicate<ResolveInfo> mServiceCheckPredicate;
 
     private final PackageMonitor mPackageMonitor = new PackageMonitor() {
         @Override
@@ -239,15 +244,24 @@
             @Nullable OnBindRunner onBind, @Nullable Runnable onUnbind,
             @BoolRes int enableOverlayResId, @StringRes int nonOverlayPackageResId) {
         this(context, FgThread.getHandler(), action, onBind, onUnbind, enableOverlayResId,
-                nonOverlayPackageResId);
+                nonOverlayPackageResId, DEFAULT_SERVICE_CHECK_PREDICATE);
     }
 
     public ServiceWatcher(Context context, Handler handler, String action,
             @Nullable OnBindRunner onBind, @Nullable Runnable onUnbind,
             @BoolRes int enableOverlayResId, @StringRes int nonOverlayPackageResId) {
+        this(context, handler, action, onBind, onUnbind, enableOverlayResId, nonOverlayPackageResId,
+                DEFAULT_SERVICE_CHECK_PREDICATE);
+    }
+
+    public ServiceWatcher(Context context, Handler handler, String action,
+            @Nullable OnBindRunner onBind, @Nullable Runnable onUnbind,
+            @BoolRes int enableOverlayResId, @StringRes int nonOverlayPackageResId,
+            @NonNull Predicate<ResolveInfo> serviceCheckPredicate) {
         mContext = context;
         mHandler = handler;
         mIntent = new Intent(Objects.requireNonNull(action));
+        mServiceCheckPredicate = Objects.requireNonNull(serviceCheckPredicate);
 
         Resources resources = context.getResources();
         boolean enableOverlay = resources.getBoolean(enableOverlayResId);
@@ -269,9 +283,16 @@
      * constraints.
      */
     public boolean checkServiceResolves() {
-        return !mContext.getPackageManager().queryIntentServicesAsUser(mIntent,
-                MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE | MATCH_SYSTEM_ONLY,
-                UserHandle.USER_SYSTEM).isEmpty();
+        List<ResolveInfo> resolveInfos = mContext.getPackageManager()
+                .queryIntentServicesAsUser(mIntent,
+                        MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE | MATCH_SYSTEM_ONLY,
+                        UserHandle.USER_SYSTEM);
+        for (ResolveInfo resolveInfo : resolveInfos) {
+            if (mServiceCheckPredicate.test(resolveInfo)) {
+                return true;
+            }
+        }
+        return false;
     }
 
     /**
@@ -320,6 +341,9 @@
                     GET_META_DATA | MATCH_DIRECT_BOOT_AUTO | MATCH_SYSTEM_ONLY,
                     mCurrentUserId);
             for (ResolveInfo resolveInfo : resolveInfos) {
+                if (!mServiceCheckPredicate.test(resolveInfo)) {
+                    continue;
+                }
                 ServiceInfo serviceInfo = new ServiceInfo(resolveInfo, mCurrentUserId);
                 if (serviceInfo.compareTo(bestServiceInfo) > 0) {
                     bestServiceInfo = serviceInfo;
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index c476666..b76f3278 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -310,7 +310,7 @@
 
     private final LocalLog mLocalLog = new LocalLog(200);
 
-    private final LocalLog mListenLog = new LocalLog(00);
+    private final LocalLog mListenLog = new LocalLog(200);
 
     private List<PhysicalChannelConfig> mPhysicalChannelConfigs;
 
@@ -2455,7 +2455,9 @@
             pw.println("local logs:");
             pw.increaseIndent();
             mLocalLog.dump(fd, pw, args);
+            pw.decreaseIndent();
             pw.println("listen logs:");
+            pw.increaseIndent();
             mListenLog.dump(fd, pw, args);
             pw.decreaseIndent();
             pw.println("registrations: count=" + recordCount);
diff --git a/services/core/java/com/android/server/utils/WatchableIntentResolver.java b/services/core/java/com/android/server/WatchableIntentResolver.java
similarity index 81%
rename from services/core/java/com/android/server/utils/WatchableIntentResolver.java
rename to services/core/java/com/android/server/WatchableIntentResolver.java
index 767fc07..2ef94f1 100644
--- a/services/core/java/com/android/server/utils/WatchableIntentResolver.java
+++ b/services/core/java/com/android/server/WatchableIntentResolver.java
@@ -14,12 +14,14 @@
  * limitations under the License.
  */
 
-package com.android.server.utils;
+package com.android.server;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 
-import com.android.server.IntentResolver;
+import com.android.server.utils.Watchable;
+import com.android.server.utils.WatchableImpl;
+import com.android.server.utils.Watcher;
 
 import java.util.List;
 
@@ -37,28 +39,45 @@
      * Watchable machinery
      */
     private final Watchable mWatchable = new WatchableImpl();
+
     /**
      * Register an observer to receive change notifications.
      * @param observer The observer to register.
      */
+    @Override
     public void registerObserver(@NonNull Watcher observer) {
         mWatchable.registerObserver(observer);
     }
+
     /**
      * Unregister the observer, which will no longer receive change notifications.
      * @param observer The observer to unregister.
      */
+    @Override
     public void unregisterObserver(@NonNull Watcher observer) {
         mWatchable.unregisterObserver(observer);
     }
+
+    /**
+     * Return true if the {@link Watcher) is a registered observer.
+     * @param observer A {@link Watcher} that might be registered
+     * @return true if the observer is registered with this {@link Watchable}.
+     */
+    @Override
+    public boolean isRegisteredObserver(@NonNull Watcher observer) {
+        return mWatchable.isRegisteredObserver(observer);
+    }
+
     /**
      * Notify listeners that the object has changd.  The argument is a hint as to the
      * source of the change.
      * @param what The attribute or sub-object that changed, if not null.
      */
+    @Override
     public void dispatchChange(@Nullable Watchable what) {
         mWatchable.dispatchChange(what);
     }
+
     /**
      * Notify listeners that this object has changed.
      */
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 5fde046..bb07ee6 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -61,7 +61,6 @@
 import android.app.compat.CompatChanges;
 import android.appwidget.AppWidgetManagerInternal;
 import android.compat.annotation.ChangeId;
-import android.compat.annotation.Disabled;
 import android.compat.annotation.EnabledSince;
 import android.content.ComponentName;
 import android.content.ComponentName.WithComponentName;
@@ -108,6 +107,7 @@
 
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.app.procstats.ServiceState;
 import com.android.internal.messages.nano.SystemMessageProto;
 import com.android.internal.notification.SystemNotificationChannels;
@@ -169,6 +169,7 @@
     public static final int FGS_FEATURE_ALLOWED_BY_DEVICE_DEMO_MODE = 18;
     public static final int FGS_FEATURE_ALLOWED_BY_PROCESS_RECORD = 19;
     public static final int FGS_FEATURE_ALLOWED_BY_EXEMPTED_PACKAGES = 20;
+    public static final int FGS_FEATURE_ALLOWED_BY_ACTIVITY_STARTER = 21;
 
     @IntDef(flag = true, prefix = { "FGS_FEATURE_" }, value = {
             FGS_FEATURE_DENIED,
@@ -191,6 +192,7 @@
             FGS_FEATURE_ALLOWED_BY_DEVICE_DEMO_MODE,
             FGS_FEATURE_ALLOWED_BY_PROCESS_RECORD,
             FGS_FEATURE_ALLOWED_BY_EXEMPTED_PACKAGES,
+            FGS_FEATURE_ALLOWED_BY_ACTIVITY_STARTER
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface FgsFeatureRetCode {}
@@ -273,7 +275,7 @@
      * is higher than R.
      */
     @ChangeId
-    @Disabled
+    @EnabledSince(targetSdkVersion = android.os.Build.VERSION_CODES.S)
     static final long FGS_BG_START_RESTRICTION_CHANGE_ID = 170668199L;
 
     /**
@@ -305,9 +307,40 @@
      */
     private static final ArraySet<String> sFgsBgStartExemptedPackages = new ArraySet<>();
 
+    private static final ArrayList<String> sFgsBgStartExemptedPackagePrefixes = new ArrayList<>();
+
+    /**
+     * List of packages that are exempted from the FGS restriction *for now*.
+     *
+     * STOPSHIP(/b/176844961) Remove it. Also update ActiveServicesTest.java.
+     */
+    private static final String[] FGS_BG_START_EXEMPTED_PACKAGES = {
+            "com.google.pixel.exo.bootstrapping",
+    };
+
+    /**
+     * List of packages that are exempted from the FGS restriction *for now*. We also allow
+     * any packages that
+     *
+     * STOPSHIP(/b/176844961) Remove it. Also update ActiveServicesTest.java.
+     */
+    private static final String[] FGS_BG_START_EXEMPTED_PACKAGES_PREFIXED_ALLOWED = {
+            "com.android.webview",
+            "com.google.android.webview",
+            "com.android.chrome",
+            "com.google.android.apps.chrome",
+            "com.chrome",
+    };
+
     static {
-        sFgsBgStartExemptedPackages.add("com.google.pixel.exo.bootstrapping"); //STOPSHIP Remove it.
-        sFgsBgStartExemptedPackages.add("com.android.chrome"); // STOPSHIP Remove it.
+        for (String s : FGS_BG_START_EXEMPTED_PACKAGES) {
+            sFgsBgStartExemptedPackages.add(s);
+        }
+
+        for (String s : FGS_BG_START_EXEMPTED_PACKAGES_PREFIXED_ALLOWED) {
+            sFgsBgStartExemptedPackages.add(s); // Add it for an exact match.
+            sFgsBgStartExemptedPackagePrefixes.add(s + "."); // Add it for an prefix match.
+        }
     }
 
     final Runnable mLastAnrDumpClearer = new Runnable() {
@@ -1700,12 +1733,13 @@
     // TODO: remove as part of fixing b/173627642
     @SuppressWarnings("AndroidFrameworkCompatChange")
     private void postFgsNotificationLocked(ServiceRecord r) {
+        final boolean isLegacyApp = (r.appInfo.targetSdkVersion < Build.VERSION_CODES.S);
         boolean showNow = !mAm.mConstants.mFlagFgsNotificationDeferralEnabled;
         if (!showNow) {
             // Legacy apps' FGS notifications are not deferred unless the relevant
             // DeviceConfig element has been set
             showNow = mAm.mConstants.mFlagFgsNotificationDeferralApiGated
-                    && r.appInfo.targetSdkVersion < Build.VERSION_CODES.S;
+                    && isLegacyApp;
         }
         if (!showNow) {
             // is the notification such that it should show right away?
@@ -1760,6 +1794,11 @@
             Slog.d(TAG_SERVICE, "FGS " + r
                     + " notification in " + (when - now) + " ms");
         }
+        if (isLegacyApp) {
+            Slog.i(TAG_SERVICE, "Deferring FGS notification in legacy app "
+                    + r.appInfo.packageName + "/" + UserHandle.formatUid(r.appInfo.uid)
+                    + " : " + r.foregroundNoti);
+        }
         mAm.mHandler.postAtTime(mPostDeferredFGSNotifications, when);
     }
 
@@ -5202,8 +5241,8 @@
             for (int i = mAm.mProcessList.mLruProcesses.size() - 1; i >= 0; i--) {
                 final ProcessRecord pr = mAm.mProcessList.mLruProcesses.get(i);
                 if (pr.uid == callingUid) {
-                    if (pr.areBackgroundActivityStartsAllowedByToken()) {
-                        ret = FGS_FEATURE_ALLOWED_BY_ACTIVITY_TOKEN;
+                    if (pr.getWindowProcessController().areBackgroundActivityStartsAllowed()) {
+                        ret = FGS_FEATURE_ALLOWED_BY_ACTIVITY_STARTER;
                         break;
                     }
                 }
@@ -5357,10 +5396,25 @@
         return ret;
     }
 
-    private boolean isPackageExemptedFromFgsRestriction(String packageName, int uid) {
-        if (!sFgsBgStartExemptedPackages.contains(packageName)) {
-            return false;
+    @VisibleForTesting
+    static boolean isPackageExemptedFromFgsRestriction(String packageName, int uid) {
+        boolean exempted = false;
+        if (sFgsBgStartExemptedPackages.contains(packageName)) {
+            exempted = true;
+        } else {
+            for (String pkg : sFgsBgStartExemptedPackagePrefixes) {
+                if (packageName.startsWith(pkg)) {
+                    exempted = true;
+                    break;
+                }
+            }
         }
+        if (!exempted) {
+            return false; // Package isn't exempted.
+        }
+        // Allow exempted packages to be subject to the restriction using this compat ID.
+        // (so that, for example, the webview developer will be able to test the restriction
+        // locally.)
         return CompatChanges.isChangeEnabled(FGS_BG_START_USE_EXEMPTION_LIST_CHANGE_ID, uid);
     }
 
@@ -5406,6 +5460,8 @@
                 return "ALLOWED_BY_PROCESS_RECORD";
             case FGS_FEATURE_ALLOWED_BY_EXEMPTED_PACKAGES:
                 return "FGS_FEATURE_ALLOWED_BY_EXEMPTED_PACKAGES";
+            case FGS_FEATURE_ALLOWED_BY_ACTIVITY_STARTER:
+                return "ALLOWED_BY_ACTIVITY_STARTER";
             default:
                 return "";
         }
@@ -5441,9 +5497,7 @@
     }
 
     private boolean isBgFgsRestrictionEnabled(ServiceRecord r) {
-        if (mAm.mConstants.mFlagFgsStartRestrictionEnabled) {
-            return true;
-        }
-        return CompatChanges.isChangeEnabled(FGS_BG_START_RESTRICTION_CHANGE_ID, r.appInfo.uid);
+        return mAm.mConstants.mFlagFgsStartRestrictionEnabled
+                && CompatChanges.isChangeEnabled(FGS_BG_START_RESTRICTION_CHANGE_ID, r.appInfo.uid);
     }
 }
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index b0f296f..94643f1 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -360,19 +360,20 @@
     // started, the restriction is on while-in-use permissions.)
     volatile boolean mFlagBackgroundFgsStartRestrictionEnabled = true;
 
-    // Indicates whether the foreground service background start restriction is enabled.
+    // Indicates whether the foreground service background start restriction is enabled for
+    // apps targeting S+.
     // When the restriction is enabled, service is not allowed to startForeground from background
     // at all.
-    volatile boolean mFlagFgsStartRestrictionEnabled = false;
+    volatile boolean mFlagFgsStartRestrictionEnabled = true;
 
     // Whether we defer FGS notifications a few seconds following their transition to
     // the foreground state.  Applies only to S+ apps; enabled by default.
     volatile boolean mFlagFgsNotificationDeferralEnabled = true;
 
     // Restrict FGS notification deferral policy to only those apps that target
-    // API version S or higher.  Enabled by default; set to "false" to defer FGS
-    // notifications from legacy apps as well.
-    volatile boolean mFlagFgsNotificationDeferralApiGated = true;
+    // API version S or higher.  Disabled by default; set to "true" to force
+    // legacy app FGS notifications to display immediately in all cases.
+    volatile boolean mFlagFgsNotificationDeferralApiGated = false;
 
     // Time in milliseconds to defer FGS notifications after their transition to
     // the foreground state.
@@ -792,7 +793,7 @@
         mFlagFgsStartRestrictionEnabled = DeviceConfig.getBoolean(
                 DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                 KEY_DEFAULT_FGS_STARTS_RESTRICTION_ENABLED,
-                /*defaultValue*/ false);
+                /*defaultValue*/ true);
     }
 
     private void updateFgsNotificationDeferralEnable() {
@@ -806,7 +807,7 @@
         mFlagFgsNotificationDeferralApiGated = DeviceConfig.getBoolean(
                 DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
                 KEY_DEFERRED_FGS_NOTIFICATIONS_API_GATED,
-                /*default value*/ true);
+                /*default value*/ false);
     }
 
     private void updateFgsNotificationDeferralInterval() {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 1841d67..a094eac 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -7241,6 +7241,7 @@
         final long waitForNetworkTimeoutMs = Settings.Global.getLong(resolver,
                 NETWORK_ACCESS_TIMEOUT_MS, NETWORK_ACCESS_TIMEOUT_DEFAULT_MS);
         mHiddenApiBlacklist.registerObserver();
+        mPlatformCompat.registerContentObserver();
 
         mAppProfiler.retrieveSettings();
 
@@ -14052,18 +14053,25 @@
                 callerApp = mPidsSelfLocked.get(pid);
             }
         }
-        // Check if the instrumentation of the process has the permission. This covers the usual
-        // test started from the shell (which has the permission) case. This is needed for apps
-        // targeting SDK level < S but we are also allowing for targetSdk S+ as a convenience to
-        // avoid breaking a bunch of existing tests and asking them to adopt shell permissions to do
-        // this.
+
         if (callerApp != null) {
+            // Check if the instrumentation of the process has the permission. This covers the usual
+            // test started from the shell (which has the permission) case. This is needed for apps
+            // targeting SDK level < S but we are also allowing for targetSdk S+ as a convenience to
+            // avoid breaking a bunch of existing tests and asking them to adopt shell permissions
+            // to do this.
             ActiveInstrumentation instrumentation = callerApp.getActiveInstrumentation();
             if (instrumentation != null && checkPermission(
                     permission.BROADCAST_CLOSE_SYSTEM_DIALOGS, -1, instrumentation.mSourceUid)
                     == PERMISSION_GRANTED) {
                 return true;
             }
+            // This is the notification trampoline use-case for example, where apps use Intent.ACSD
+            // to close the shade prior to starting an activity.
+            WindowProcessController wmApp = callerApp.getWindowProcessController();
+            if (wmApp.canCloseSystemDialogsByToken()) {
+                return true;
+            }
         }
         return false;
     }
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 255badd..e90423c 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -1381,10 +1381,6 @@
         mWindowProcessController.removeAllowBackgroundActivityStartsToken(entity);
     }
 
-    boolean areBackgroundActivityStartsAllowedByToken() {
-        return mWindowProcessController.areBackgroundActivityStartsAllowedByToken();
-    }
-
     void addBoundClientUid(int clientUid) {
         mBoundClientUids.add(clientUid);
         mWindowProcessController.setBoundClientUids(mBoundClientUids);
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index e129561..d9c83da 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -749,9 +749,15 @@
                         // There are other callbacks in the queue, let's just update the originating
                         // token
                         if (mIsAllowedBgActivityStartsByStart) {
-                            mAppForAllowingBgActivityStartsByStart
-                                    .addOrUpdateAllowBackgroundActivityStartsToken(
-                                            this, getExclusiveOriginatingToken());
+                            // mAppForAllowingBgActivityStartsByStart can be null here for example
+                            // if get 2 calls to allowBgActivityStartsOnServiceStart() without a
+                            // process attached to this ServiceRecord, so we need to perform a null
+                            // check here.
+                            if (mAppForAllowingBgActivityStartsByStart != null) {
+                                mAppForAllowingBgActivityStartsByStart
+                                        .addOrUpdateAllowBackgroundActivityStartsToken(
+                                                this, getExclusiveOriginatingToken());
+                            }
                         } else {
                             Slog.wtf(TAG,
                                     "Service callback to revoke bg activity starts by service "
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 360ed9d..5d6454b 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -6143,6 +6143,12 @@
     /** Pulls current AppOps access report and resamples package and app op to watch */
     @Override
     public @Nullable RuntimeAppOpAccessMessage collectRuntimeAppOpAccessMessage() {
+        ActivityManagerInternal ami = LocalServices.getService(ActivityManagerInternal.class);
+        boolean isCallerInstrumented = ami.isUidCurrentlyInstrumented(Binder.getCallingUid());
+        boolean isCallerSystem = Binder.getCallingPid() == Process.myPid();
+        if (!isCallerSystem && !isCallerInstrumented) {
+            return null;
+        }
         mContext.enforcePermission(android.Manifest.permission.GET_APP_OPS_STATS,
                 Binder.getCallingPid(), Binder.getCallingUid(), null);
         RuntimeAppOpAccessMessage result;
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 2ab5aa6..391a64c 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -3467,8 +3467,6 @@
     /** @see AudioManager#getStreamVolume(int) */
     public int getStreamVolume(int streamType) {
         ensureValidStreamType(streamType);
-        Log.e(TAG, "AudioSystem.getDevicesForStream In AudioService from u/pid"
-                + Binder.getCallingUid() + "/" + Binder.getCallingPid());
         int device = getDeviceForStream(streamType);
         synchronized (VolumeStreamState.class) {
             int index = mStreamStates[streamType].getIndex(device);
@@ -8910,8 +8908,14 @@
         mPlaybackMonitor.playerAttributes(piid, attr, Binder.getCallingUid());
     }
 
-    public void playerEvent(int piid, int event) {
-        mPlaybackMonitor.playerEvent(piid, event, Binder.getCallingUid());
+    /**
+     * Update player event
+     * @param piid Player id to update
+     * @param event The new player event
+     * @param deviceId The new player device id
+     */
+    public void playerEvent(int piid, int event, int deviceId) {
+        mPlaybackMonitor.playerEvent(piid, event, deviceId, Binder.getCallingUid());
     }
 
     public void playerHasOpPlayAudio(int piid, boolean hasOpPlayAudio) {
diff --git a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
index a577883..36c67cd 100644
--- a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
@@ -233,15 +233,25 @@
         }
     }
 
-    public void playerEvent(int piid, int event, int binderUid) {
-        if (DEBUG) { Log.v(TAG, String.format("playerEvent(piid=%d, event=%d)", piid, event)); }
+    /**
+     * Update player event
+     * @param piid Player id to update
+     * @param event The new player event
+     * @param deviceId The new player device id
+     * @param binderUid Calling binder uid
+     */
+    public void playerEvent(int piid, int event, int deviceId, int binderUid) {
+        if (DEBUG) {
+            Log.v(TAG, String.format("playerEvent(piid=%d, deviceId=%d, event=%d)",
+                    piid, deviceId, event));
+        }
         final boolean change;
         synchronized(mPlayerLock) {
             final AudioPlaybackConfiguration apc = mPlayers.get(new Integer(piid));
             if (apc == null) {
                 return;
             }
-            sEventLogger.log(new PlayerEvent(piid, event));
+            sEventLogger.log(new PlayerEvent(piid, event, deviceId));
             if (event == AudioPlaybackConfiguration.PLAYER_STATE_STARTED) {
                 for (Integer uidInteger: mBannedUids) {
                     if (checkBanPlayer(apc, uidInteger.intValue())) {
@@ -259,7 +269,7 @@
             if (checkConfigurationCaller(piid, apc, binderUid)) {
                 //TODO add generation counter to only update to the latest state
                 checkVolumeForPrivilegedAlarm(apc, event);
-                change = apc.handleStateEvent(event);
+                change = apc.handleStateEvent(event, deviceId);
             } else {
                 Log.e(TAG, "Error handling event " + event);
                 change = false;
@@ -289,7 +299,8 @@
                 mPlayers.remove(new Integer(piid));
                 mDuckingManager.removeReleased(apc);
                 checkVolumeForPrivilegedAlarm(apc, AudioPlaybackConfiguration.PLAYER_STATE_RELEASED);
-                change = apc.handleStateEvent(AudioPlaybackConfiguration.PLAYER_STATE_RELEASED);
+                change = apc.handleStateEvent(AudioPlaybackConfiguration.PLAYER_STATE_RELEASED,
+                        AudioPlaybackConfiguration.PLAYER_DEVICEID_INVALID);
             }
         }
         if (change) {
@@ -868,16 +879,19 @@
         // only keeping the player interface ID as it uniquely identifies the player in the event
         final int mPlayerIId;
         final int mState;
+        final int mDeviceId;
 
-        PlayerEvent(int piid, int state) {
+        PlayerEvent(int piid, int state, int deviceId) {
             mPlayerIId = piid;
             mState = state;
+            mDeviceId = deviceId;
         }
 
         @Override
         public String eventToString() {
             return new StringBuilder("player piid:").append(mPlayerIId).append(" state:")
-                    .append(AudioPlaybackConfiguration.toLogFriendlyPlayerState(mState)).toString();
+                    .append(AudioPlaybackConfiguration.toLogFriendlyPlayerState(mState))
+                    .append(" DeviceId:").append(mDeviceId).toString();
         }
     }
 
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index 75e1938..a81abcd 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -608,8 +608,9 @@
         }
 
         @Override
-        public void registerAuthenticator(int id, int modality, @Authenticators.Types int strength,
-                IBiometricAuthenticator authenticator) {
+        public synchronized void registerAuthenticator(int id, int modality,
+                @Authenticators.Types int strength,
+                @NonNull IBiometricAuthenticator authenticator) {
             checkInternalPermission();
 
             Slog.d(TAG, "Registering ID: " + id
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java
index cbffdd3..9dc8439 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java
@@ -198,10 +198,8 @@
             final Intent intent = new Intent("android.settings.FACE_SETTINGS");
             intent.setPackage("com.android.settings");
 
-            // TODO(b/174187097) Please replace FLAG_MUTABLE_UNAUDITED below
-            // with either FLAG_IMMUTABLE (recommended) or FLAG_MUTABLE.
             final PendingIntent pendingIntent = PendingIntent.getActivityAsUser(getContext(),
-                    0 /* requestCode */, intent, PendingIntent.FLAG_MUTABLE_UNAUDITED /* flags */,
+                    0 /* requestCode */, intent, PendingIntent.FLAG_IMMUTABLE /* flags */,
                     null /* options */, UserHandle.CURRENT);
 
             final String channelName = "FaceEnrollNotificationChannel";
diff --git a/services/core/java/com/android/server/compat/CompatChange.java b/services/core/java/com/android/server/compat/CompatChange.java
index a8aa9aa..c4ff99b 100644
--- a/services/core/java/com/android/server/compat/CompatChange.java
+++ b/services/core/java/com/android/server/compat/CompatChange.java
@@ -61,6 +61,7 @@
     ChangeListener mListener = null;
 
     private Map<String, Boolean> mPackageOverrides;
+    private Map<String, Boolean> mDeferredOverrides;
 
     public CompatChange(long changeId) {
         this(changeId, null, -1, -1, false, false, null);
@@ -121,6 +122,56 @@
     }
 
     /**
+     * Tentatively set the state of this change for a given package name.
+     * The override will only take effect after that package is installed, if applicable.
+     *
+     * <p>Note, this method is not thread safe so callers must ensure thread safety.
+     *
+     * @param packageName Package name to tentatively enable the change for.
+     * @param enabled Whether or not to enable the change.
+     */
+    void addPackageDeferredOverride(String packageName, boolean enabled) {
+        if (getLoggingOnly()) {
+            throw new IllegalArgumentException(
+                    "Can't add overrides for a logging only change " + toString());
+        }
+        if (mDeferredOverrides == null) {
+            mDeferredOverrides = new HashMap<>();
+        }
+        mDeferredOverrides.put(packageName, enabled);
+    }
+
+    /**
+     * Rechecks an existing (and possibly deferred) override.
+     *
+     * <p>For deferred overrides, check if they can be promoted to a regular override. For regular
+     * overrides, check if they need to be demoted to deferred.</p>
+     *
+     * @param packageName Package name to apply deferred overrides for.
+     * @param allowed Whether the override is allowed.
+     *
+     * @return {@code true} if the recheck yielded a result that requires invalidating caches
+     *         (a deferred override was consolidated or a regular override was removed).
+     */
+    boolean recheckOverride(String packageName, boolean allowed) {
+        // A deferred override now is allowed by the policy, so promote it to a regular override.
+        if (hasDeferredOverride(packageName) && allowed) {
+            boolean overrideValue = mDeferredOverrides.remove(packageName);
+            addPackageOverride(packageName, overrideValue);
+            return true;
+        }
+        // A previously set override is no longer allowed by the policy, so make it deferred.
+        if (hasOverride(packageName) && !allowed) {
+            boolean overrideValue = mPackageOverrides.remove(packageName);
+            addPackageDeferredOverride(packageName, overrideValue);
+            // Notify because the override was removed.
+            notifyListener(packageName);
+            return true;
+        }
+        return false;
+    }
+
+    /**
      * Remove any package override for the given package name, restoring the default behaviour.
      *
      * <p>Note, this method is not thread safe so callers must ensure thread safety.
@@ -133,6 +184,9 @@
                 notifyListener(pname);
             }
         }
+        if (mDeferredOverrides != null) {
+            mDeferredOverrides.remove(pname);
+        }
     }
 
     /**
@@ -176,6 +230,15 @@
         return mPackageOverrides != null && mPackageOverrides.containsKey(packageName);
     }
 
+    /**
+     * Checks whether a change has a deferred override for a package.
+     * @param packageName name of the package
+     * @return true if there is such a deferred override
+     */
+    boolean hasDeferredOverride(String packageName) {
+        return mDeferredOverrides != null && mDeferredOverrides.containsKey(packageName);
+    }
+
     @Override
     public String toString() {
         StringBuilder sb = new StringBuilder("ChangeId(")
@@ -195,6 +258,9 @@
         if (mPackageOverrides != null && mPackageOverrides.size() > 0) {
             sb.append("; packageOverrides=").append(mPackageOverrides);
         }
+        if (mDeferredOverrides != null && mDeferredOverrides.size() > 0) {
+            sb.append("; deferredOverrides=").append(mDeferredOverrides);
+        }
         return sb.append(")").toString();
     }
 
diff --git a/services/core/java/com/android/server/compat/CompatConfig.java b/services/core/java/com/android/server/compat/CompatConfig.java
index 8511118..f03a608 100644
--- a/services/core/java/com/android/server/compat/CompatConfig.java
+++ b/services/core/java/com/android/server/compat/CompatConfig.java
@@ -65,7 +65,7 @@
     @GuardedBy("mChanges")
     private final LongSparseArray<CompatChange> mChanges = new LongSparseArray<>();
 
-    private IOverrideValidator mOverrideValidator;
+    private OverrideValidatorImpl mOverrideValidator;
 
     @VisibleForTesting
     CompatConfig(AndroidBuildClassifier androidBuildClassifier, Context context) {
@@ -161,7 +161,7 @@
      * @return {@code true} if the change existed before adding the override.
      */
     boolean addOverride(long changeId, String packageName, boolean enabled)
-            throws RemoteException, SecurityException {
+            throws SecurityException {
         boolean alreadyKnown = true;
         OverrideAllowedState allowedState =
                 mOverrideValidator.getOverrideAllowedState(changeId, packageName);
@@ -173,7 +173,17 @@
                 c = new CompatChange(changeId);
                 addChange(c);
             }
-            c.addPackageOverride(packageName, enabled);
+            switch (allowedState.state) {
+                case OverrideAllowedState.ALLOWED:
+                    c.addPackageOverride(packageName, enabled);
+                    break;
+                case OverrideAllowedState.DEFERRED_VERIFICATION:
+                    c.addPackageDeferredOverride(packageName, enabled);
+                    break;
+                default:
+                    throw new IllegalStateException("Should only be able to override changes that "
+                                                    + "are allowed or can be deferred.");
+            }
             invalidateCache();
         }
         return alreadyKnown;
@@ -244,26 +254,26 @@
      * @return {@code true} if an override existed;
      */
     boolean removeOverride(long changeId, String packageName)
-            throws RemoteException, SecurityException {
+            throws SecurityException {
         boolean overrideExists = false;
         synchronized (mChanges) {
             CompatChange c = mChanges.get(changeId);
-            try {
-                if (c != null) {
-                    overrideExists = c.hasOverride(packageName);
-                    if (overrideExists) {
-                        OverrideAllowedState allowedState =
-                                mOverrideValidator.getOverrideAllowedState(changeId, packageName);
-                        allowedState.enforce(changeId, packageName);
-                        c.removePackageOverride(packageName);
-                    }
+            if (c != null) {
+                // Always allow removing a deferred override.
+                if (c.hasDeferredOverride(packageName)) {
+                    c.removePackageOverride(packageName);
+                    overrideExists = true;
+                } else if (c.hasOverride(packageName)) {
+                    // Regular overrides need to pass the policy.
+                    overrideExists = true;
+                    OverrideAllowedState allowedState =
+                            mOverrideValidator.getOverrideAllowedState(changeId, packageName);
+                    allowedState.enforce(changeId, packageName);
+                    c.removePackageOverride(packageName);
                 }
-            } catch (RemoteException e) {
-                // Should never occur, since validator is in the same process.
-                throw new RuntimeException("Unable to call override validator!", e);
             }
-            invalidateCache();
         }
+        invalidateCache();
         return overrideExists;
     }
 
@@ -293,29 +303,15 @@
      * Removes all overrides previously added via {@link #addOverride(long, String, boolean)} or
      * {@link #addOverrides(CompatibilityChangeConfig, String)} for a certain package.
      *
-     * <p>This restores the default behaviour for the given change and app, once any app
-     * processes have been restarted.
+     * <p>This restores the default behaviour for the given app.
      *
      * @param packageName The package for which the overrides should be purged.
      */
-    void removePackageOverrides(String packageName) throws RemoteException, SecurityException {
+    void removePackageOverrides(String packageName) throws SecurityException {
         synchronized (mChanges) {
             for (int i = 0; i < mChanges.size(); ++i) {
-                try {
-                    CompatChange change = mChanges.valueAt(i);
-                    if (change.hasOverride(packageName)) {
-                        OverrideAllowedState allowedState =
-                                mOverrideValidator.getOverrideAllowedState(change.getId(),
-                                        packageName);
-                        allowedState.enforce(change.getId(), packageName);
-                        if (change != null) {
-                            mChanges.valueAt(i).removePackageOverride(packageName);
-                        }
-                    }
-                } catch (RemoteException e) {
-                    // Should never occur, since validator is in the same process.
-                    throw new RuntimeException("Unable to call override validator!", e);
-                }
+                CompatChange change = mChanges.valueAt(i);
+                removeOverride(change.getId(), packageName);
             }
             invalidateCache();
         }
@@ -327,20 +323,15 @@
         LongArray allowed = new LongArray();
         synchronized (mChanges) {
             for (int i = 0; i < mChanges.size(); ++i) {
-                try {
-                    CompatChange change = mChanges.valueAt(i);
-                    if (change.getEnableSinceTargetSdk() != targetSdkVersion) {
-                        continue;
-                    }
-                    OverrideAllowedState allowedState =
-                            mOverrideValidator.getOverrideAllowedState(change.getId(),
-                                                                       packageName);
-                    if (allowedState.state == OverrideAllowedState.ALLOWED) {
-                        allowed.add(change.getId());
-                    }
-                } catch (RemoteException e) {
-                    // Should never occur, since validator is in the same process.
-                    throw new RuntimeException("Unable to call override validator!", e);
+                CompatChange change = mChanges.valueAt(i);
+                if (change.getEnableSinceTargetSdk() != targetSdkVersion) {
+                    continue;
+                }
+                OverrideAllowedState allowedState =
+                        mOverrideValidator.getOverrideAllowedState(change.getId(),
+                                                                    packageName);
+                if (allowedState.state == OverrideAllowedState.ALLOWED) {
+                    allowed.add(change.getId());
                 }
             }
         }
@@ -401,6 +392,11 @@
     }
 
     @VisibleForTesting
+    void forceNonDebuggableFinalForTest(boolean value) {
+        mOverrideValidator.forceNonDebuggableFinalForTest(value);
+    }
+
+    @VisibleForTesting
     void clearChanges() {
         synchronized (mChanges) {
             mChanges.clear();
@@ -511,4 +507,26 @@
     private void invalidateCache() {
         ChangeIdStateCache.invalidate();
     }
+    /**
+     * Rechecks all the existing overrides for a package.
+     */
+    void recheckOverrides(String packageName) {
+        synchronized (mChanges) {
+            boolean shouldInvalidateCache = false;
+            for (int idx = 0; idx < mChanges.size(); ++idx) {
+                CompatChange c = mChanges.valueAt(idx);
+                OverrideAllowedState allowedState =
+                        mOverrideValidator.getOverrideAllowedState(c.getId(), packageName);
+                boolean allowedOverride = (allowedState.state == OverrideAllowedState.ALLOWED);
+                shouldInvalidateCache |= c.recheckOverride(packageName, allowedOverride);
+            }
+            if (shouldInvalidateCache) {
+                invalidateCache();
+            }
+        }
+    }
+
+    void registerContentObserver() {
+        mOverrideValidator.registerContentObserver();
+    }
 }
diff --git a/services/core/java/com/android/server/compat/OverrideValidatorImpl.java b/services/core/java/com/android/server/compat/OverrideValidatorImpl.java
index 79a13ca..fe5b4a9 100644
--- a/services/core/java/com/android/server/compat/OverrideValidatorImpl.java
+++ b/services/core/java/com/android/server/compat/OverrideValidatorImpl.java
@@ -17,16 +17,19 @@
 package com.android.server.compat;
 
 import static com.android.internal.compat.OverrideAllowedState.ALLOWED;
+import static com.android.internal.compat.OverrideAllowedState.DEFERRED_VERIFICATION;
 import static com.android.internal.compat.OverrideAllowedState.DISABLED_NON_TARGET_SDK;
 import static com.android.internal.compat.OverrideAllowedState.DISABLED_NOT_DEBUGGABLE;
 import static com.android.internal.compat.OverrideAllowedState.DISABLED_TARGET_SDK_TOO_HIGH;
 import static com.android.internal.compat.OverrideAllowedState.LOGGING_ONLY_CHANGE;
-import static com.android.internal.compat.OverrideAllowedState.PACKAGE_DOES_NOT_EXIST;
 
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
+import android.database.ContentObserver;
+import android.os.Handler;
+import android.provider.Settings;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.compat.AndroidBuildClassifier;
@@ -41,6 +44,20 @@
     private AndroidBuildClassifier mAndroidBuildClassifier;
     private Context mContext;
     private CompatConfig mCompatConfig;
+    private boolean mForceNonDebuggableFinalBuild;
+
+    private class SettingsObserver extends ContentObserver {
+        SettingsObserver() {
+            super(new Handler());
+        }
+        @Override
+        public void onChange(boolean selfChange) {
+            mForceNonDebuggableFinalBuild = Settings.Global.getInt(
+                mContext.getContentResolver(),
+                Settings.Global.FORCE_NON_DEBUGGABLE_FINAL_BUILD_FOR_COMPAT,
+                0) == 1;
+        }
+    }
 
     @VisibleForTesting
     OverrideValidatorImpl(AndroidBuildClassifier androidBuildClassifier,
@@ -48,6 +65,7 @@
         mAndroidBuildClassifier = androidBuildClassifier;
         mContext = context;
         mCompatConfig = config;
+        mForceNonDebuggableFinalBuild = false;
     }
 
     @Override
@@ -56,8 +74,10 @@
             return new OverrideAllowedState(LOGGING_ONLY_CHANGE, -1, -1);
         }
 
-        boolean debuggableBuild = mAndroidBuildClassifier.isDebuggableBuild();
-        boolean finalBuild = mAndroidBuildClassifier.isFinalBuild();
+        boolean debuggableBuild = mAndroidBuildClassifier.isDebuggableBuild()
+                                    && !mForceNonDebuggableFinalBuild;
+        boolean finalBuild = mAndroidBuildClassifier.isFinalBuild()
+                                || mForceNonDebuggableFinalBuild;
         int maxTargetSdk = mCompatConfig.maxTargetSdkForChangeIdOptIn(changeId);
         boolean disabled = mCompatConfig.isDisabled(changeId);
 
@@ -73,7 +93,7 @@
         try {
             applicationInfo = packageManager.getApplicationInfo(packageName, 0);
         } catch (NameNotFoundException e) {
-            return new OverrideAllowedState(PACKAGE_DOES_NOT_EXIST, -1, -1);
+            return new OverrideAllowedState(DEFERRED_VERIFICATION, -1, -1);
         }
         int appTargetSdk = applicationInfo.targetSdkVersion;
         // Only allow overriding debuggable apps.
@@ -94,4 +114,17 @@
         }
         return new OverrideAllowedState(DISABLED_TARGET_SDK_TOO_HIGH, appTargetSdk, maxTargetSdk);
     }
+
+    void registerContentObserver() {
+        mContext.getContentResolver().registerContentObserver(
+                Settings.Global.getUriFor(
+                    Settings.Global.FORCE_NON_DEBUGGABLE_FINAL_BUILD_FOR_COMPAT),
+                false,
+                new SettingsObserver());
+    }
+
+    void forceNonDebuggableFinalForTest(boolean value) {
+        mForceNonDebuggableFinalBuild = value;
+    }
+
 }
diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java
index aa85f7f..283dba7 100644
--- a/services/core/java/com/android/server/compat/PlatformCompat.java
+++ b/services/core/java/com/android/server/compat/PlatformCompat.java
@@ -25,9 +25,13 @@
 import android.annotation.UserIdInt;
 import android.app.ActivityManager;
 import android.app.IActivityManager;
+import android.content.BroadcastReceiver;
 import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManagerInternal;
+import android.net.Uri;
 import android.os.Binder;
 import android.os.Build;
 import android.os.RemoteException;
@@ -74,6 +78,7 @@
         mChangeReporter = new ChangeReporter(
                 ChangeReporter.SOURCE_SYSTEM_SERVER);
         mCompatConfig = compatConfig;
+        registerPackageReceiver(context);
     }
 
     @Override
@@ -389,4 +394,42 @@
         }
         return true;
     }
+
+    /**
+     * Registers a broadcast receiver that listens for package install, replace or remove.
+     * @param context the context where the receiver should be registered.
+     */
+    public void registerPackageReceiver(Context context) {
+        final BroadcastReceiver receiver = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                if (intent == null) {
+                    return;
+                }
+                final Uri packageData = intent.getData();
+                if (packageData == null) {
+                    return;
+                }
+                final String packageName = packageData.getSchemeSpecificPart();
+                if (packageName == null) {
+                    return;
+                }
+                mCompatConfig.recheckOverrides(packageName);
+            }
+        };
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(Intent.ACTION_PACKAGE_ADDED);
+        filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
+        filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+        filter.addDataScheme("package");
+        context.registerReceiver(receiver, filter);
+    }
+
+    /**
+     * Register the observer for
+     * {@link android.provider.Settings.Global#FORCE_NON_DEBUGGABLE_FINAL_BUILD_FOR_COMPAT}
+     */
+    public void registerContentObserver() {
+        mCompatConfig.registerContentObserver();
+    }
 }
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index 841c970..7bde4d5 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -146,7 +146,11 @@
     // Underlying networks declared by the agent. Only set if supportsUnderlyingNetworks is true.
     // The networks in this list might be declared by a VPN app using setUnderlyingNetworks and are
     // not guaranteed to be current or correct, or even to exist.
-    public @Nullable Network[] declaredUnderlyingNetworks;
+    //
+    // This array is read and iterated on multiple threads with no locking so its contents must
+    // never be modified. When the list of networks changes, replace with a new array, on the
+    // handler thread.
+    public @Nullable volatile Network[] declaredUnderlyingNetworks;
 
     // The capabilities originally announced by the NetworkAgent, regardless of any capabilities
     // that were added or removed due to this network's underlying networks.
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 228ad588..07a4b89 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -101,6 +101,7 @@
 import android.text.TextUtils;
 import android.util.ArraySet;
 import android.util.Log;
+import android.util.Range;
 
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
@@ -223,7 +224,7 @@
     private @NonNull List<String> mLockdownAllowlist = Collections.emptyList();
 
      /**
-     * A memory of what UIDs this class told netd to block for the lockdown feature.
+     * A memory of what UIDs this class told ConnectivityService to block for the lockdown feature.
      *
      * Netd maintains ranges of UIDs for which network should be restricted to using only the VPN
      * for the lockdown feature. This class manages these UIDs and sends this information to netd.
@@ -237,7 +238,7 @@
      * @see mLockdown
      */
     @GuardedBy("this")
-    private final Set<UidRangeParcel> mBlockedUidsAsToldToNetd = new ArraySet<>();
+    private final Set<UidRangeParcel> mBlockedUidsAsToldToConnectivity = new ArraySet<>();
 
     // The user id of initiating VPN.
     private final int mUserId;
@@ -1588,7 +1589,7 @@
      *                {@link Vpn} goes through a VPN connection or is blocked until one is
      *                available, {@code false} to lift the requirement.
      *
-     * @see #mBlockedUidsAsToldToNetd
+     * @see #mBlockedUidsAsToldToConnectivity
      */
     @GuardedBy("this")
     private void setVpnForcedLocked(boolean enforce) {
@@ -1599,10 +1600,8 @@
             exemptedPackages = new ArrayList<>(mLockdownAllowlist);
             exemptedPackages.add(mPackage);
         }
-        final Set<UidRangeParcel> rangesToTellNetdToRemove =
-                new ArraySet<>(mBlockedUidsAsToldToNetd);
-
-        final Set<UidRangeParcel> rangesToTellNetdToAdd;
+        final Set<UidRangeParcel> rangesToRemove = new ArraySet<>(mBlockedUidsAsToldToConnectivity);
+        final Set<UidRangeParcel> rangesToAdd;
         if (enforce) {
             final Set<UidRange> restrictedProfilesRanges =
                     createUserAndRestrictedProfilesRanges(mUserId,
@@ -1621,26 +1620,27 @@
                 }
             }
 
-            rangesToTellNetdToRemove.removeAll(rangesThatShouldBeBlocked);
-            rangesToTellNetdToAdd = rangesThatShouldBeBlocked;
-            // The ranges to tell netd to add are the ones that should be blocked minus the
-            // ones it already knows to block. Note that this will change the contents of
+            rangesToRemove.removeAll(rangesThatShouldBeBlocked);
+            rangesToAdd = rangesThatShouldBeBlocked;
+            // The ranges to tell ConnectivityService to add are the ones that should be blocked
+            // minus the ones it already knows to block. Note that this will change the contents of
             // rangesThatShouldBeBlocked, but the list of ranges that should be blocked is
             // not used after this so it's fine to destroy it.
-            rangesToTellNetdToAdd.removeAll(mBlockedUidsAsToldToNetd);
+            rangesToAdd.removeAll(mBlockedUidsAsToldToConnectivity);
         } else {
-            rangesToTellNetdToAdd = Collections.emptySet();
+            rangesToAdd = Collections.emptySet();
         }
 
         // If mBlockedUidsAsToldToNetd used to be empty, this will always be a no-op.
-        setAllowOnlyVpnForUids(false, rangesToTellNetdToRemove);
+        setAllowOnlyVpnForUids(false, rangesToRemove);
         // If nothing should be blocked now, this will now be a no-op.
-        setAllowOnlyVpnForUids(true, rangesToTellNetdToAdd);
+        setAllowOnlyVpnForUids(true, rangesToAdd);
     }
 
     /**
-     * Tell netd to add or remove a list of {@link UidRange}s to the list of UIDs that are only
-     * allowed to make connections through sockets that have had {@code protect()} called on them.
+     * Tell ConnectivityService to add or remove a list of {@link UidRange}s to the list of UIDs
+     * that are only allowed to make connections through sockets that have had {@code protect()}
+     * called on them.
      *
      * @param enforce {@code true} to add to the denylist, {@code false} to remove.
      * @param ranges {@link Collection} of {@link UidRange}s to add (if {@param enforce} is
@@ -1653,18 +1653,22 @@
         if (ranges.size() == 0) {
             return true;
         }
-        final UidRangeParcel[] stableRanges = ranges.toArray(new UidRangeParcel[ranges.size()]);
+        // Convert to Collection<Range> which is what the ConnectivityManager API takes.
+        ArrayList<Range<Integer>> integerRanges = new ArrayList<>(ranges.size());
+        for (UidRangeParcel uidRange : ranges) {
+            integerRanges.add(new Range<>(uidRange.start, uidRange.stop));
+        }
         try {
-            mNetd.networkRejectNonSecureVpn(enforce, stableRanges);
-        } catch (RemoteException | RuntimeException e) {
+            mConnectivityManager.setRequireVpnForUids(enforce, integerRanges);
+        } catch (RuntimeException e) {
             Log.e(TAG, "Updating blocked=" + enforce
                     + " for UIDs " + Arrays.toString(ranges.toArray()) + " failed", e);
             return false;
         }
         if (enforce) {
-            mBlockedUidsAsToldToNetd.addAll(ranges);
+            mBlockedUidsAsToldToConnectivity.addAll(ranges);
         } else {
-            mBlockedUidsAsToldToNetd.removeAll(ranges);
+            mBlockedUidsAsToldToConnectivity.removeAll(ranges);
         }
         return true;
     }
@@ -1783,9 +1787,6 @@
 
     /**
      * Updates underlying network set.
-     *
-     * <p>Note: Does not updates capabilities. Call {@link #updateCapabilities} from
-     * ConnectivityService thread to get updated capabilities.
      */
     public synchronized boolean setUnderlyingNetworks(Network[] networks) {
         if (!isCallerEstablishedOwnerLocked()) {
@@ -1808,13 +1809,6 @@
         return true;
     }
 
-    public synchronized Network[] getUnderlyingNetworks() {
-        if (!isRunningLocked()) {
-            return null;
-        }
-        return mConfig.underlyingNetworks;
-    }
-
     /**
      * This method should only be called by ConnectivityService because it doesn't
      * have enough data to fill VpnInfo.primaryUnderlyingIface field.
@@ -1864,13 +1858,13 @@
      * the {@code uid}.
      *
      * @apiNote This method don't check VPN lockdown status.
-     * @see #mBlockedUidsAsToldToNetd
+     * @see #mBlockedUidsAsToldToConnectivity
      */
     public synchronized boolean isBlockingUid(int uid) {
         if (mNetworkInfo.isConnected()) {
             return !appliesToUid(uid);
         } else {
-            return containsUid(mBlockedUidsAsToldToNetd, uid);
+            return containsUid(mBlockedUidsAsToldToConnectivity, uid);
         }
     }
 
diff --git a/services/core/java/com/android/server/content/SyncManager.java b/services/core/java/com/android/server/content/SyncManager.java
index 6ae410a..fe89903 100644
--- a/services/core/java/com/android/server/content/SyncManager.java
+++ b/services/core/java/com/android/server/content/SyncManager.java
@@ -3838,12 +3838,10 @@
             }
 
             UserHandle user = new UserHandle(userId);
-            // TODO(b/174186839) Please replace FLAG_MUTABLE_UNAUDITED below
-            // with either FLAG_IMMUTABLE (recommended) or FLAG_MUTABLE.
             final PendingIntent pendingIntent = PendingIntent
                     .getActivityAsUser(mContext, 0, clickIntent,
-                            PendingIntent.FLAG_CANCEL_CURRENT
-                            | PendingIntent.FLAG_MUTABLE_UNAUDITED, null, user);
+                            PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE,
+                            null, user);
 
             CharSequence tooManyDeletesDescFormat = mContext.getResources().getText(
                     R.string.contentServiceTooManyDeletesNotificationDesc);
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index eb61a1c..fa063b2 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -46,6 +46,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.BackgroundThread;
 import com.android.server.EventLogTags;
+import com.android.server.display.DisplayDeviceConfig.HighBrightnessModeData;
 
 import java.io.PrintWriter;
 
@@ -195,6 +196,9 @@
     private boolean mShortTermModelValid;
     private float mShortTermModelAnchor;
 
+    // Controls High Brightness Mode.
+    private HighBrightnessModeController mHbmController;
+
     // Context-sensitive brightness configurations require keeping track of the foreground app's
     // package name and category, which is done by registering a TaskStackListener to call back to
     // us onTaskStackChanged, and then using the ActivityTaskManager to get the foreground app's
@@ -216,12 +220,12 @@
             float dozeScaleFactor, int lightSensorRate, int initialLightSensorRate,
             long brighteningLightDebounceConfig, long darkeningLightDebounceConfig,
             boolean resetAmbientLuxAfterWarmUpConfig, HysteresisLevels ambientBrightnessThresholds,
-            HysteresisLevels screenBrightnessThresholds, Context context) {
+            HysteresisLevels screenBrightnessThresholds, LogicalDisplay display, Context context) {
         this(new Injector(), callbacks, looper, sensorManager, lightSensor, mapper,
                 lightSensorWarmUpTime, brightnessMin, brightnessMax, dozeScaleFactor,
                 lightSensorRate, initialLightSensorRate, brighteningLightDebounceConfig,
                 darkeningLightDebounceConfig, resetAmbientLuxAfterWarmUpConfig,
-                ambientBrightnessThresholds, screenBrightnessThresholds, context
+                ambientBrightnessThresholds, screenBrightnessThresholds, display, context
         );
     }
 
@@ -232,7 +236,7 @@
             float dozeScaleFactor, int lightSensorRate, int initialLightSensorRate,
             long brighteningLightDebounceConfig, long darkeningLightDebounceConfig,
             boolean resetAmbientLuxAfterWarmUpConfig, HysteresisLevels ambientBrightnessThresholds,
-            HysteresisLevels screenBrightnessThresholds, Context context) {
+            HysteresisLevels screenBrightnessThresholds, LogicalDisplay display, Context context) {
         mInjector = injector;
         mContext = context;
         mCallbacks = callbacks;
@@ -269,6 +273,20 @@
         mPendingForegroundAppPackageName = null;
         mForegroundAppCategory = ApplicationInfo.CATEGORY_UNDEFINED;
         mPendingForegroundAppCategory = ApplicationInfo.CATEGORY_UNDEFINED;
+
+        final DisplayDeviceConfig ddConfig =
+                display.getPrimaryDisplayDeviceLocked().getDisplayDeviceConfig();
+        HighBrightnessModeData hbmData =
+                ddConfig != null ? ddConfig.getHighBrightnessModeData() : null;
+
+        final Runnable hbmChangeCallback = () -> {
+            updateAutoBrightness(true /*sendUpdate*/, false /*userInitiatedChange*/);
+            // TODO: b/175937645 - Callback to DisplayManagerService to indicate a change to the HBM
+            // allowance has been made so that the brightness limits can be calculated
+            // appropriately.
+        };
+        mHbmController = new HighBrightnessModeController(mHandler, brightnessMin, brightnessMax,
+                hbmData, hbmChangeCallback);
     }
 
     /**
@@ -538,6 +556,7 @@
         mAmbientLux = lux;
         mAmbientBrighteningThreshold = mAmbientBrightnessThresholds.getBrighteningThreshold(lux);
         mAmbientDarkeningThreshold = mAmbientBrightnessThresholds.getDarkeningThreshold(lux);
+        mHbmController.onAmbientLuxChange(mAmbientLux);
 
         // If the short term model was invalidated and the change is drastic enough, reset it.
         if (!mShortTermModelValid && mShortTermModelAnchor != -1) {
@@ -751,6 +770,7 @@
                     mScreenBrightnessThresholds.getBrighteningThreshold(newScreenAutoBrightness));
             mScreenDarkeningThreshold = clampScreenBrightness(
                     mScreenBrightnessThresholds.getDarkeningThreshold(newScreenAutoBrightness));
+            mHbmController.onAutoBrightnessChanged(mScreenAutoBrightness);
 
             if (sendUpdate) {
                 mCallbacks.updateBrightness();
@@ -761,7 +781,7 @@
     // Clamps values with float range [0.0-1.0]
     private float clampScreenBrightness(float value) {
         return MathUtils.constrain(value,
-                mScreenBrightnessRangeMinimum, mScreenBrightnessRangeMaximum);
+                mHbmController.getCurrentBrightnessMin(), mHbmController.getCurrentBrightnessMax());
     }
 
     private void prepareBrightnessAdjustmentSample() {
diff --git a/services/core/java/com/android/server/display/DisplayDevice.java b/services/core/java/com/android/server/display/DisplayDevice.java
index 3516981..cd17cfe 100644
--- a/services/core/java/com/android/server/display/DisplayDevice.java
+++ b/services/core/java/com/android/server/display/DisplayDevice.java
@@ -37,7 +37,6 @@
     private final DisplayAdapter mDisplayAdapter;
     private final IBinder mDisplayToken;
     private final String mUniqueId;
-    private DisplayDeviceConfig mDisplayDeviceConfig;
 
     // The display device does not manage these properties itself, they are set by
     // the display manager service.  The display device shouldn't really be looking at these.
@@ -71,12 +70,11 @@
 
     /*
      * Gets the DisplayDeviceConfig for this DisplayDevice.
-     * Returns null for this device but is overridden in LocalDisplayDevice.
      *
-     * @return The DisplayDeviceConfig.
+     * @return The DisplayDeviceConfig; {@code null} if not overridden.
      */
     public DisplayDeviceConfig getDisplayDeviceConfig() {
-        return mDisplayDeviceConfig;
+        return null;
     }
 
     /**
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index 574d8c6..68708d36 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -16,12 +16,18 @@
 
 package com.android.server.display;
 
+import android.annotation.NonNull;
+import android.content.Context;
 import android.os.Environment;
+import android.os.PowerManager;
 import android.util.Slog;
 import android.view.DisplayAddress;
 
+import com.android.internal.BrightnessSynchronizer;
 import com.android.server.display.config.DisplayConfiguration;
 import com.android.server.display.config.DisplayQuirks;
+import com.android.server.display.config.HbmTiming;
+import com.android.server.display.config.HighBrightnessMode;
 import com.android.server.display.config.NitsMap;
 import com.android.server.display.config.Point;
 import com.android.server.display.config.XmlParser;
@@ -33,6 +39,7 @@
 import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.math.BigDecimal;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
@@ -49,6 +56,7 @@
 
     public static final String QUIRK_CAN_SET_BRIGHTNESS_VIA_HWC = "canSetBrightnessViaHwc";
 
+    private static final float BRIGHTNESS_DEFAULT = 0.5f;
     private static final String ETC_DIR = "etc";
     private static final String DISPLAY_CONFIG_DIR = "displayconfig";
     private static final String CONFIG_FILE_FORMAT = "display_%s.xml";
@@ -56,12 +64,23 @@
     private static final String STABLE_ID_SUFFIX_FORMAT = "id_%d";
     private static final String NO_SUFFIX_FORMAT = "%d";
     private static final long STABLE_FLAG = 1L << 62;
+    // Float.NaN (used as invalid for brightness) cannot be stored in config.xml
+    // so -2 is used instead
+    private static final float INVALID_BRIGHTNESS_IN_CONFIG = -2f;
+
+    private final Context mContext;
 
     private float[] mNits;
     private float[] mBrightness;
+    private float mBrightnessMinimum = Float.NaN;
+    private float mBrightnessMaximum = Float.NaN;
+    private float mBrightnessDefault = Float.NaN;
     private List<String> mQuirks;
+    private boolean mIsHighBrightnessModeEnabled = false;
+    private HighBrightnessModeData mHbmData;
 
-    private DisplayDeviceConfig() {
+    private DisplayDeviceConfig(Context context) {
+        mContext = context;
     }
 
     /**
@@ -72,37 +91,50 @@
      *     <li>physicalDisplayId without a stable flag (old system)</li>
      *     <li>portId</li>
      * </ol>
+     *
      * @param physicalDisplayId The display ID for which to load the configuration.
      * @return A configuration instance for the specified display.
      */
-    public static DisplayDeviceConfig create(long physicalDisplayId) {
+    public static DisplayDeviceConfig create(Context context, long physicalDisplayId,
+            boolean isDefaultDisplay) {
         DisplayDeviceConfig config;
 
-        config = loadConfigFromDirectory(Environment.getProductDirectory(), physicalDisplayId);
+        config = loadConfigFromDirectory(context, Environment.getProductDirectory(),
+                physicalDisplayId);
         if (config != null) {
             return config;
         }
 
-        config = loadConfigFromDirectory(Environment.getVendorDirectory(), physicalDisplayId);
+        config = loadConfigFromDirectory(context, Environment.getVendorDirectory(),
+                physicalDisplayId);
         if (config != null) {
             return config;
         }
 
-        return null;
+        // If no config can be loaded from any ddc xml at all,
+        // prepare a whole config using the global config.xml.
+        // Guaranteed not null
+        if (isDefaultDisplay) {
+            config = getConfigFromGlobalXml(context);
+        } else {
+            config = getConfigFromPmValues(context);
+        }
+        return config;
     }
 
-    private static DisplayDeviceConfig loadConfigFromDirectory(
+    private static DisplayDeviceConfig loadConfigFromDirectory(Context context,
             File baseDirectory, long physicalDisplayId) {
         DisplayDeviceConfig config;
         // Create config using filename from physical ID (including "stable" bit).
-        config = getConfigFromSuffix(baseDirectory, STABLE_ID_SUFFIX_FORMAT, physicalDisplayId);
+        config = getConfigFromSuffix(context, baseDirectory, STABLE_ID_SUFFIX_FORMAT,
+                physicalDisplayId);
         if (config != null) {
             return config;
         }
 
         // Create config using filename from physical ID (excluding "stable" bit).
         final long withoutStableFlag = physicalDisplayId & ~STABLE_FLAG;
-        config = getConfigFromSuffix(baseDirectory, NO_SUFFIX_FORMAT, withoutStableFlag);
+        config = getConfigFromSuffix(context, baseDirectory, NO_SUFFIX_FORMAT, withoutStableFlag);
         if (config != null) {
             return config;
         }
@@ -111,14 +143,8 @@
         final DisplayAddress.Physical physicalAddress =
                 DisplayAddress.fromPhysicalDisplayId(physicalDisplayId);
         int port = physicalAddress.getPort();
-        config = getConfigFromSuffix(baseDirectory, PORT_SUFFIX_FORMAT, port);
-        if (config != null) {
-            return config;
-        }
-
-        // None of these files exist.
-        return null;
-
+        config = getConfigFromSuffix(context, baseDirectory, PORT_SUFFIX_FORMAT, port);
+        return config;
     }
 
     /**
@@ -139,6 +165,18 @@
         return mBrightness;
     }
 
+    public float getBrightnessMinimum() {
+        return mBrightnessMinimum;
+    }
+
+    public float getBrightnessMaximum() {
+        return mBrightnessMaximum;
+    }
+
+    public float getBrightnessDefault() {
+        return mBrightnessDefault;
+    }
+
     /**
      * @param quirkValue The quirk to test.
      * @return {@code true} if the specified quirk is present in this configuration,
@@ -148,17 +186,39 @@
         return mQuirks != null && mQuirks.contains(quirkValue);
     }
 
+    /**
+     * @return high brightness mode configuration data for the display.
+     */
+    public HighBrightnessModeData getHighBrightnessModeData() {
+        if (!mIsHighBrightnessModeEnabled || mHbmData == null) {
+            return null;
+        }
+
+        HighBrightnessModeData hbmData = new HighBrightnessModeData();
+        mHbmData.copyTo(hbmData);
+        return hbmData;
+    }
+
     @Override
     public String toString() {
         String str = "DisplayDeviceConfig{"
                 + "mBrightness=" + Arrays.toString(mBrightness)
                 + ", mNits=" + Arrays.toString(mNits)
+                + ", mBrightnessMinimum=" + mBrightnessMinimum
+                + ", mBrightnessMaximum=" + mBrightnessMaximum
+                + ", mBrightnessDefault=" + mBrightnessDefault
                 + ", mQuirks=" + mQuirks
+                + ", isHbmEnabled=" + mIsHighBrightnessModeEnabled
+                + ", mHbmData=" + mHbmData
                 + "}";
         return str;
     }
 
-    private static DisplayDeviceConfig getConfigFromSuffix(File baseDirectory,
+    private float getMaxBrightness() {
+        return mBrightness[mBrightness.length - 1];
+    }
+
+    private static DisplayDeviceConfig getConfigFromSuffix(Context context, File baseDirectory,
             String suffixFormat, long idNumber) {
 
         final String suffix = String.format(suffixFormat, idNumber);
@@ -167,13 +227,25 @@
                 baseDirectory, ETC_DIR, DISPLAY_CONFIG_DIR, filename);
 
         if (filePath.exists()) {
-            final DisplayDeviceConfig config = new DisplayDeviceConfig();
+            final DisplayDeviceConfig config = new DisplayDeviceConfig(context);
             config.initFromFile(filePath);
             return config;
         }
         return null;
     }
 
+    private static DisplayDeviceConfig getConfigFromGlobalXml(Context context) {
+        DisplayDeviceConfig config = new DisplayDeviceConfig(context);
+        config.initFromGlobalXml();
+        return config;
+    }
+
+    private static DisplayDeviceConfig getConfigFromPmValues(Context context) {
+        DisplayDeviceConfig config = new DisplayDeviceConfig(context);
+        config.initFromPmValues();
+        return config;
+    }
+
     private void initFromFile(File configFile) {
         if (!configFile.exists()) {
             // Display configuration files aren't required to exist.
@@ -187,16 +259,88 @@
 
         try (InputStream in = new BufferedInputStream(new FileInputStream(configFile))) {
             final DisplayConfiguration config = XmlParser.read(in);
-            loadBrightnessMap(config);
-            loadQuirks(config);
+            if (config != null) {
+                loadBrightnessMap(config);
+                loadBrightnessDefaultFromDdcXml(config);
+                loadBrightnessConstraintsFromConfigXml();
+                loadHighBrightnessModeData(config);
+                loadQuirks(config);
+            } else {
+                Slog.w(TAG, "DisplayDeviceConfig file is null");
+            }
         } catch (IOException | DatatypeConfigurationException | XmlPullParserException e) {
             Slog.e(TAG, "Encountered an error while reading/parsing display config file: "
                     + configFile, e);
         }
     }
 
+    private void initFromGlobalXml() {
+        // If no ddc exists, use config.xml
+        loadBrightnessDefaultFromConfigXml();
+        loadBrightnessConstraintsFromConfigXml();
+    }
+
+    private void initFromPmValues() {
+        mBrightnessMinimum = PowerManager.BRIGHTNESS_MIN;
+        mBrightnessMaximum = PowerManager.BRIGHTNESS_MAX;
+        mBrightnessDefault = BRIGHTNESS_DEFAULT;
+    }
+
+    private void loadBrightnessDefaultFromDdcXml(DisplayConfiguration config) {
+        // Default brightness values are stored in the displayDeviceConfig file,
+        // Or we fallback standard values if not.
+        // Priority 1: Value in the displayDeviceConfig
+        // Priority 2: Value in the config.xml (float)
+        // Priority 3: Value in the config.xml (int)
+        if (config != null) {
+            BigDecimal configBrightnessDefault = config.getScreenBrightnessDefault();
+            if (configBrightnessDefault != null) {
+                mBrightnessDefault = configBrightnessDefault.floatValue();
+            } else {
+                mBrightnessDefault = BRIGHTNESS_DEFAULT;
+            }
+        }
+    }
+
+    private void loadBrightnessDefaultFromConfigXml() {
+        // Priority 1: Value in the config.xml (float)
+        // Priority 2: Value in the config.xml (int)
+        final float def = mContext.getResources().getFloat(com.android.internal.R.dimen
+                .config_screenBrightnessSettingDefaultFloat);
+        if (def == INVALID_BRIGHTNESS_IN_CONFIG) {
+            mBrightnessDefault = BrightnessSynchronizer.brightnessIntToFloat(
+                    mContext.getResources().getInteger(com.android.internal.R.integer
+                            .config_screenBrightnessSettingDefault));
+        } else {
+            mBrightnessDefault = def;
+        }
+    }
+
+    private void loadBrightnessConstraintsFromConfigXml() {
+        // TODO(b/175373898) add constraints (min / max) to ddc.
+        final float min = mContext.getResources().getFloat(com.android.internal.R.dimen
+                .config_screenBrightnessSettingMinimumFloat);
+        final float max = mContext.getResources().getFloat(com.android.internal.R.dimen
+                .config_screenBrightnessSettingMaximumFloat);
+        if (min == INVALID_BRIGHTNESS_IN_CONFIG || max == INVALID_BRIGHTNESS_IN_CONFIG) {
+            mBrightnessMinimum = BrightnessSynchronizer.brightnessIntToFloat(
+                    mContext.getResources().getInteger(com.android.internal.R.integer
+                            .config_screenBrightnessSettingMinimum));
+            mBrightnessMaximum = BrightnessSynchronizer.brightnessIntToFloat(
+                    mContext.getResources().getInteger(com.android.internal.R.integer
+                            .config_screenBrightnessSettingMaximum));
+        } else {
+            mBrightnessMinimum = min;
+            mBrightnessMaximum = max;
+        }
+    }
+
     private void loadBrightnessMap(DisplayConfiguration config) {
         final NitsMap map = config.getScreenBrightnessMap();
+        // Map may not exist in config file
+        if (map == null) {
+            return;
+        }
         final List<Point> points = map.getPoint();
         final int size = points.size();
 
@@ -233,4 +377,66 @@
             mQuirks = new ArrayList<>(quirks.getQuirk());
         }
     }
+
+    private void loadHighBrightnessModeData(DisplayConfiguration config) {
+        final HighBrightnessMode hbm = config.getHighBrightnessMode();
+        if (hbm != null) {
+            mIsHighBrightnessModeEnabled = hbm.getEnabled();
+            mHbmData = new HighBrightnessModeData();
+            mHbmData.minimumLux = hbm.getMinimumLux_all().floatValue();
+            mHbmData.transitionPoint = hbm.getTransitionPoint_all().floatValue();
+            if (mHbmData.transitionPoint >= getMaxBrightness()) {
+                throw new IllegalArgumentException("HBM transition point invalid. "
+                        + mHbmData.transitionPoint + " is not less than "
+                        + getMaxBrightness());
+            }
+            final HbmTiming hbmTiming = hbm.getTiming_all();
+            mHbmData.timeWindowMillis = hbmTiming.getTimeWindowSecs_all().longValue() * 1000;
+            mHbmData.timeMaxMillis = hbmTiming.getTimeMaxSecs_all().longValue() * 1000;
+            mHbmData.timeMinMillis = hbmTiming.getTimeMinSecs_all().longValue() * 1000;
+        }
+    }
+
+    /**
+     * Container for high brightness mode configuration data.
+     */
+    static class HighBrightnessModeData {
+        /** Minimum lux needed to enter high brightness mode */
+        public float minimumLux;
+
+        /** Brightness level at which we transition from normal to high-brightness. */
+        public float transitionPoint;
+
+        /** Time window for HBM. */
+        public long timeWindowMillis;
+
+        /** Maximum time HBM is allowed to be during in a {@code timeWindowMillis}. */
+        public long timeMaxMillis;
+
+        /** Minimum time that HBM can be on before being enabled. */
+        public long timeMinMillis;
+
+        /**
+         * Copies the HBM data to the specified parameter instance.
+         * @param other the instance to copy data to.
+         */
+        public void copyTo(@NonNull HighBrightnessModeData other) {
+            other.minimumLux = minimumLux;
+            other.transitionPoint = transitionPoint;
+            other.timeWindowMillis = timeWindowMillis;
+            other.timeMaxMillis = timeMaxMillis;
+            other.timeMinMillis = timeMinMillis;
+        }
+
+        @Override
+        public String toString() {
+            return "HBM{"
+                    + "minLux: " + minimumLux
+                    + ", transition: " + transitionPoint
+                    + ", timeWindow: " + timeWindowMillis + "ms"
+                    + ", timeMax: " + timeMaxMillis + "ms"
+                    + ", timeMin: " + timeMinMillis
+                    + "} ";
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/display/DisplayDeviceInfo.java b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
index 468d825..2641ee7 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceInfo.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
@@ -25,6 +25,8 @@
 import android.view.DisplayEventReceiver;
 import android.view.Surface;
 
+import com.android.internal.BrightnessSynchronizer;
+
 import java.util.Arrays;
 import java.util.Objects;
 
@@ -337,6 +339,10 @@
     public DisplayEventReceiver.FrameRateOverride[] frameRateOverrides =
             new DisplayEventReceiver.FrameRateOverride[0];
 
+    public float brightnessMinimum;
+    public float brightnessMaximum;
+    public float brightnessDefault;
+
     public void setAssumedDensityForExternalDisplay(int width, int height) {
         densityDpi = Math.min(width, height) * DisplayMetrics.DENSITY_XHIGH / 1080;
         // Technically, these values should be smaller than the apparent density
@@ -391,7 +397,11 @@
                 || !Objects.equals(deviceProductInfo, other.deviceProductInfo)
                 || ownerUid != other.ownerUid
                 || !Objects.equals(ownerPackageName, other.ownerPackageName)
-                || !Objects.equals(frameRateOverrides, other.frameRateOverrides)) {
+                || !Objects.equals(frameRateOverrides, other.frameRateOverrides)
+                || !BrightnessSynchronizer.floatEquals(brightnessMinimum, other.brightnessMinimum)
+                || !BrightnessSynchronizer.floatEquals(brightnessMaximum, other.brightnessMaximum)
+                || !BrightnessSynchronizer.floatEquals(brightnessDefault,
+                other.brightnessDefault)) {
             diff |= DIFF_OTHER;
         }
         return diff;
@@ -431,6 +441,9 @@
         ownerUid = other.ownerUid;
         ownerPackageName = other.ownerPackageName;
         frameRateOverrides = other.frameRateOverrides;
+        brightnessMinimum = other.brightnessMinimum;
+        brightnessMaximum = other.brightnessMaximum;
+        brightnessDefault = other.brightnessDefault;
     }
 
     // For debugging purposes
@@ -471,6 +484,9 @@
         for (DisplayEventReceiver.FrameRateOverride frameRateOverride : frameRateOverrides) {
             sb.append(frameRateOverride).append(" ");
         }
+        sb.append(", brightnessMinimum ").append(brightnessMinimum);
+        sb.append(", brightnessMaximum ").append(brightnessMaximum);
+        sb.append(", brightnessDefault ").append(brightnessDefault);
         sb.append(flagsToString(flags));
         sb.append("}");
         return sb.toString();
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 2c7cd5b..60e4595 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -288,9 +288,6 @@
     @GuardedBy("mSyncRoot")
     private final SparseArray<Float> mDisplayBrightnesses = new SparseArray<>();
 
-    // The default brightness.
-    private final float mDisplayDefaultBrightness;
-
     // Set to true when there are pending display changes that have yet to be applied
     // to the surface flinger state.
     private boolean mPendingTraversal;
@@ -416,9 +413,6 @@
         mMinimumBrightnessCurve = new Curve(lux, nits);
         mMinimumBrightnessSpline = Spline.createSpline(lux, nits);
 
-        PowerManager pm = mContext.getSystemService(PowerManager.class);
-        mDisplayDefaultBrightness = pm.getBrightnessConstraint(
-                PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DEFAULT);
         mCurrentUserId = UserHandle.USER_SYSTEM;
         ColorSpace[] colorSpaces = SurfaceControl.getCompositionColorSpaces();
         mWideColorSpace = colorSpaces[1];
@@ -1106,9 +1100,9 @@
             recordStableDisplayStatsIfNeededLocked(display);
             recordTopInsetLocked(display);
         }
-        addDisplayPowerControllerLocked(displayId);
+        addDisplayPowerControllerLocked(display);
         mDisplayStates.append(displayId, Display.STATE_OFF);
-        mDisplayBrightnesses.append(displayId, mDisplayDefaultBrightness);
+        mDisplayBrightnesses.append(displayId, display.getDisplayInfoLocked().brightnessDefault);
 
         DisplayManagerGlobal.invalidateLocalDisplayInfoCaches();
 
@@ -1138,6 +1132,11 @@
         // this point.
         sendDisplayEventLocked(displayId, DisplayManagerGlobal.EVENT_DISPLAY_CHANGED);
         scheduleTraversalLocked(false);
+
+        DisplayPowerController dpc = mDisplayPowerControllers.get(displayId);
+        if (dpc != null) {
+            dpc.onDisplayChanged();
+        }
     }
 
     private void handleLogicalDisplayFrameRateOverridesChangedLocked(
@@ -1866,18 +1865,18 @@
 
     private void initializeDisplayPowerControllersLocked() {
         mLogicalDisplayMapper.forEachLocked((logicalDisplay) -> addDisplayPowerControllerLocked(
-                logicalDisplay.getDisplayIdLocked()));
+                logicalDisplay));
     }
 
-    private void addDisplayPowerControllerLocked(int displayId) {
+    private void addDisplayPowerControllerLocked(LogicalDisplay display) {
         if (mPowerHandler == null) {
             // initPowerManagement has not yet been called.
             return;
         }
         final DisplayPowerController displayPowerController = new DisplayPowerController(
                 mContext, mDisplayPowerCallbacks, mPowerHandler, mSensorManager,
-                mDisplayBlanker, displayId);
-        mDisplayPowerControllers.append(displayId, displayPowerController);
+                mDisplayBlanker, display);
+        mDisplayPowerControllers.append(display.getDisplayIdLocked(), displayPowerController);
     }
 
     private final class DisplayManagerHandler extends Handler {
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index f488260..811625b 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -163,6 +163,9 @@
     // The display blanker.
     private final DisplayBlanker mBlanker;
 
+    // The LogicalDisplay tied to this DisplayPowerController.
+    private final LogicalDisplay mLogicalDisplay;
+
     // The ID of the LogicalDisplay tied to this DisplayPowerController.
     private final int mDisplayId;
 
@@ -406,7 +409,7 @@
      */
     public DisplayPowerController(Context context,
             DisplayPowerCallbacks callbacks, Handler handler,
-            SensorManager sensorManager, DisplayBlanker blanker, int displayId) {
+            SensorManager sensorManager, DisplayBlanker blanker, LogicalDisplay logicalDisplay) {
         mHandler = new DisplayControllerHandler(handler.getLooper());
         mBrightnessTracker = new BrightnessTracker(context, null);
         mSettingsObserver = new SettingsObserver(mHandler);
@@ -418,9 +421,10 @@
         mContext = context;
         mBrightnessSynchronizer = new BrightnessSynchronizer(context);
         mBrightnessSynchronizer.startSynchronizing();
-        mDisplayId = displayId;
+        mLogicalDisplay = logicalDisplay;
+        mDisplayId = mLogicalDisplay.getDisplayIdLocked();
 
-        PowerManager pm =  context.getSystemService(PowerManager.class);
+        PowerManager pm = context.getSystemService(PowerManager.class);
 
         final Resources resources = context.getResources();
 
@@ -439,7 +443,7 @@
         mScreenBrightnessRangeMaximum = clampAbsoluteBrightness(
                 pm.getBrightnessConstraint(PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_MAXIMUM));
         mScreenBrightnessDefault = clampAbsoluteBrightness(
-                pm.getBrightnessConstraint(PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DEFAULT));
+                mLogicalDisplay.getDisplayInfoLocked().brightnessDefault);
 
         // VR SETTINGS
         mScreenBrightnessForVrDefault = clampAbsoluteBrightness(
@@ -519,7 +523,7 @@
                         mScreenBrightnessRangeMaximum, dozeScaleFactor, lightSensorRate,
                         initialLightSensorRate, brighteningLightDebounce, darkeningLightDebounce,
                         autoBrightnessResetAmbientLuxAfterWarmUp, ambientBrightnessThresholds,
-                        screenBrightnessThresholds, context);
+                        screenBrightnessThresholds, logicalDisplay, context);
             } else {
                 mUseSoftwareAutoBrightnessConfig = false;
             }
@@ -670,6 +674,15 @@
         return mAutomaticBrightnessController.getDefaultConfig();
     }
 
+    /**
+     * Notified when the display is changed. We use this to apply any changes that might be needed
+     * when displays get swapped on foldable devices.  For example, different brightness properties
+     * of each display need to be properly reflected in AutomaticBrightnessController.
+     */
+    public void onDisplayChanged() {
+        // TODO: b/175821789 - Support high brightness on multiple (folding) displays
+    }
+
     private void sendUpdatePowerState() {
         synchronized (mLock) {
             sendUpdatePowerStateLocked();
@@ -1833,6 +1846,9 @@
 
         pw.println();
         pw.println("Display Power Controller Configuration:");
+        pw.println("  mScreenBrightnessRangeMinimum=" + mScreenBrightnessRangeMinimum);
+        pw.println("  mScreenBrightnessRangeMaximum=" + mScreenBrightnessRangeMaximum);
+        pw.println("  mScreenBrightnessRangeDefault=" + mScreenBrightnessDefault);
         pw.println("  mScreenBrightnessDozeConfig=" + mScreenBrightnessDozeConfig);
         pw.println("  mScreenBrightnessDimConfig=" + mScreenBrightnessDimConfig);
         pw.println("  mScreenBrightnessDefault=" + mScreenBrightnessDefault);
diff --git a/services/core/java/com/android/server/display/HighBrightnessModeController.java b/services/core/java/com/android/server/display/HighBrightnessModeController.java
new file mode 100644
index 0000000..12b810f
--- /dev/null
+++ b/services/core/java/com/android/server/display/HighBrightnessModeController.java
@@ -0,0 +1,260 @@
+/*
+ * 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.display;
+
+import android.os.Handler;
+import android.os.PowerManager;
+import android.os.SystemClock;
+import android.util.Slog;
+
+import com.android.server.display.DisplayDeviceConfig.HighBrightnessModeData;
+
+import java.util.Iterator;
+import java.util.LinkedList;
+
+/**
+ * Controls the status of high-brightness mode for devices that support it. This class assumes that
+ * an instance is always created even if a device does not support high-brightness mode (HBM); in
+ * the case where it is not supported, the majority of the logic is skipped. On devices that support
+ * HBM, we keep track of the ambient lux as well as historical usage of HBM to determine when HBM is
+ * allowed and not. This class's output is simply a brightness-range maximum value (queried via
+ * {@link #getCurrentBrightnessMax}) that changes depending on whether HBM is enabled or not.
+ */
+class HighBrightnessModeController {
+    private static final String TAG = "HighBrightnessModeController";
+
+    private static final boolean DEBUG_HBM = false;
+
+    private final float mBrightnessMin;
+    private final float mBrightnessMax;
+    private final HighBrightnessModeData mHbmData;
+    private final Handler mHandler;
+    private final Runnable mHbmChangeCallback;
+    private final Runnable mRecalcRunnable;
+
+    private boolean mIsInAllowedAmbientRange = false;
+    private boolean mIsTimeAvailable = false;
+    private float mAutoBrightness;
+
+    /**
+     * If HBM is currently running, this is the start time for the current HBM session.
+     */
+    private long mRunningStartTimeMillis = -1;
+
+    /**
+     * List of previous HBM-events ordered from most recent to least recent.
+     * Meant to store only the events that fall into the most recent
+     * {@link mHbmData.timeWindowSecs}.
+     */
+    private LinkedList<HbmEvent> mEvents = new LinkedList<>();
+
+    HighBrightnessModeController(Handler handler, float brightnessMin, float brightnessMax,
+            HighBrightnessModeData hbmData, Runnable hbmChangeCallback) {
+        mHandler = handler;
+        mBrightnessMin = brightnessMin;
+        mBrightnessMax = brightnessMax;
+        mHbmData = hbmData;
+        mHbmChangeCallback = hbmChangeCallback;
+        mAutoBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
+
+        mRecalcRunnable = () -> {
+            boolean oldIsAllowed = isCurrentlyAllowed();
+            recalculateTimeAllowance();
+            if (oldIsAllowed != isCurrentlyAllowed()) {
+                // Our allowed state has changed; tell AutomaticBrightnessController
+                // to update the brightness.
+                if (mHbmChangeCallback != null) {
+                    mHbmChangeCallback.run();
+                }
+            }
+        };
+    }
+
+    float getCurrentBrightnessMin() {
+        return mBrightnessMin;
+    }
+
+    float getCurrentBrightnessMax() {
+        if (!deviceSupportsHbm() || isCurrentlyAllowed()) {
+            // Either the device doesn't support HBM, or HBM range is currently allowed (device
+            // it in a high-lux environment). In either case, return the highest brightness
+            // level supported by the device.
+            return mBrightnessMax;
+        } else {
+            // Hbm is not allowed, only allow up to the brightness where we
+            // transition to high brightness mode.
+            return mHbmData.transitionPoint;
+        }
+    }
+
+    void onAmbientLuxChange(float ambientLux) {
+        if (!deviceSupportsHbm()) {
+            return;
+        }
+
+        final boolean isHighLux = (ambientLux >= mHbmData.minimumLux);
+        if (isHighLux != mIsInAllowedAmbientRange) {
+            mIsInAllowedAmbientRange = isHighLux;
+            recalculateTimeAllowance();
+        }
+    }
+
+    void onAutoBrightnessChanged(float autoBrightness) {
+        if (!deviceSupportsHbm()) {
+            return;
+        }
+        final float oldAutoBrightness = mAutoBrightness;
+        mAutoBrightness = autoBrightness;
+
+        // If we are starting or ending a high brightness mode session, store the current
+        // session in mRunningStartTimeMillis, or the old one in mEvents.
+        final boolean wasOldBrightnessHigh = oldAutoBrightness > mHbmData.transitionPoint;
+        final boolean isNewBrightnessHigh = mAutoBrightness > mHbmData.transitionPoint;
+        if (wasOldBrightnessHigh != isNewBrightnessHigh) {
+            final long currentTime = SystemClock.uptimeMillis();
+            if (isNewBrightnessHigh) {
+                mRunningStartTimeMillis = currentTime;
+            } else {
+                mEvents.addFirst(new HbmEvent(mRunningStartTimeMillis, currentTime));
+                mRunningStartTimeMillis = -1;
+
+                if (DEBUG_HBM) {
+                    Slog.d(TAG, "New HBM event: " + mEvents.getFirst());
+                }
+            }
+        }
+
+        recalculateTimeAllowance();
+    }
+
+    private boolean isCurrentlyAllowed() {
+        return mIsTimeAvailable && mIsInAllowedAmbientRange;
+    }
+
+    private boolean deviceSupportsHbm() {
+        return mHbmData != null;
+    }
+
+    /**
+     * Recalculates the allowable HBM time.
+     */
+    private void recalculateTimeAllowance() {
+        final long currentTime = SystemClock.uptimeMillis();
+        long timeAlreadyUsed = 0;
+
+        // First, lets see how much time we've taken for any currently running
+        // session of HBM.
+        if (mRunningStartTimeMillis > 0) {
+            if (mRunningStartTimeMillis > currentTime) {
+                Slog.e(TAG, "Start time set to the future. curr: " + currentTime
+                        + ", start: " + mRunningStartTimeMillis);
+                mRunningStartTimeMillis = currentTime;
+            }
+            timeAlreadyUsed = currentTime - mRunningStartTimeMillis;
+        }
+
+        if (DEBUG_HBM) {
+            Slog.d(TAG, "Time already used after current session: " + timeAlreadyUsed);
+        }
+
+        // Next, lets iterate through the history of previous sessions and add those times.
+        final long windowstartTimeMillis = currentTime - mHbmData.timeWindowMillis;
+        Iterator<HbmEvent> it = mEvents.iterator();
+        while (it.hasNext()) {
+            final HbmEvent event = it.next();
+
+            // If this event ended before the current Timing window, discard forever and ever.
+            if (event.endTimeMillis < windowstartTimeMillis) {
+                it.remove();
+                continue;
+            }
+
+            final long startTimeMillis = Math.max(event.startTimeMillis, windowstartTimeMillis);
+            timeAlreadyUsed += event.endTimeMillis - startTimeMillis;
+        }
+
+        if (DEBUG_HBM) {
+            Slog.d(TAG, "Time already used after all sessions: " + timeAlreadyUsed);
+        }
+
+        // See how much allowable time we have left.
+        final long remainingTime = Math.max(0, mHbmData.timeMaxMillis - timeAlreadyUsed);
+
+        // We allow HBM if there is more than the minimum required time available
+        // or if brightness is already in the high range, if there is any time left at all.
+        final boolean isAllowedWithoutRestrictions = remainingTime >= mHbmData.timeMinMillis;
+        final boolean isOnlyAllowedToStayOn = !isAllowedWithoutRestrictions
+                && remainingTime > 0 && mAutoBrightness > mHbmData.transitionPoint;
+        mIsTimeAvailable = isAllowedWithoutRestrictions || isOnlyAllowedToStayOn;
+
+        // Calculate the time at which we want to recalculate mIsTimeAvailable in case a lux or
+        // brightness change doesn't happen before then.
+        long nextTimeout = -1;
+        if (mAutoBrightness > mHbmData.transitionPoint) {
+            // if we're in high-lux now, timeout when we run out of allowed time.
+            nextTimeout = currentTime + remainingTime;
+        } else if (!mIsTimeAvailable && mEvents.size() > 0) {
+            // If we are not allowed...timeout when the oldest event moved outside of the timing
+            // window by at least minTime. Basically, we're calculating the soonest time we can
+            // get {@code timeMinMillis} back to us.
+            final HbmEvent lastEvent = mEvents.getLast();
+            final long startTimePlusMinMillis =
+                    Math.max(windowstartTimeMillis, lastEvent.startTimeMillis)
+                    + mHbmData.timeMinMillis;
+            final long timeWhenMinIsGainedBack =
+                    currentTime + (startTimePlusMinMillis - windowstartTimeMillis) - remainingTime;
+            nextTimeout = timeWhenMinIsGainedBack;
+        }
+
+        if (DEBUG_HBM) {
+            Slog.d(TAG, "HBM recalculated.  IsAllowedWithoutRestrictions: "
+                    + isAllowedWithoutRestrictions
+                    + ", isOnlyAllowedToStayOn: " + isOnlyAllowedToStayOn
+                    + ", remainingAllowedTime: " + remainingTime
+                    + ", isLuxHigh: " + mIsInAllowedAmbientRange
+                    + ", isHBMCurrentlyAllowed: " + isCurrentlyAllowed()
+                    + ", brightness: " + mAutoBrightness
+                    + ", RunningStartTimeMillis: " + mRunningStartTimeMillis
+                    + ", nextTimeout: " + (nextTimeout != -1 ? (nextTimeout - currentTime) : -1)
+                    + ", events: " + mEvents);
+        }
+
+        if (nextTimeout != -1) {
+            mHandler.removeCallbacks(mRecalcRunnable);
+            mHandler.postAtTime(mRecalcRunnable, nextTimeout);
+        }
+    }
+
+    /**
+     * Represents an event in which High Brightness Mode was enabled.
+     */
+    private static class HbmEvent {
+        public long startTimeMillis;
+        public long endTimeMillis;
+
+        HbmEvent(long startTimeMillis, long endTimeMillis) {
+            this.startTimeMillis = startTimeMillis;
+            this.endTimeMillis = endTimeMillis;
+        }
+
+        @Override
+        public String toString() {
+            return "[Event: {" + startTimeMillis + ", " + endTimeMillis + "}, total: "
+                    + ((endTimeMillis - startTimeMillis) / 1000) + "]";
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 9b8ed3a..8198e51 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -415,7 +415,9 @@
             Spline sysToNits = null;
 
             // Load the mapping from nits to HAL brightness range (display-device-config.xml)
-            mDisplayDeviceConfig = DisplayDeviceConfig.create(mPhysicalDisplayId);
+            final Context context = getOverlayContext();
+            mDisplayDeviceConfig = DisplayDeviceConfig.create(context, mPhysicalDisplayId,
+                    mIsDefaultDisplay);
             if (mDisplayDeviceConfig == null) {
                 return;
             }
@@ -431,9 +433,9 @@
             nitsToHal = Spline.createSpline(halNits, halBrightness);
 
             // Load the mapping from system brightness range to nits (config.xml)
-            final Resources res = getOverlayContext().getResources();
+            final Resources res = context.getResources();
             final float[] sysNits = BrightnessMappingStrategy.getFloatArray(res.obtainTypedArray(
-                            com.android.internal.R.array.config_screenBrightnessNits));
+                    com.android.internal.R.array.config_screenBrightnessNits));
             final int[] sysBrightness = res.getIntArray(
                     com.android.internal.R.array.config_screenBrightnessBacklight);
             if (sysNits.length == 0 || sysBrightness.length != sysNits.length) {
@@ -630,6 +632,15 @@
 
                 // The display is trusted since it is created by system.
                 mInfo.flags |= DisplayDeviceInfo.FLAG_TRUSTED;
+                if (mDisplayDeviceConfig != null) {
+                    mInfo.brightnessMinimum = mDisplayDeviceConfig.getBrightnessMinimum();
+                    mInfo.brightnessMaximum = mDisplayDeviceConfig.getBrightnessMaximum();
+                    mInfo.brightnessDefault = mDisplayDeviceConfig.getBrightnessDefault();
+                } else {
+                    mInfo.brightnessMinimum = PowerManager.BRIGHTNESS_MIN;
+                    mInfo.brightnessMaximum = PowerManager.BRIGHTNESS_MAX;
+                    mInfo.brightnessDefault = 0.5f;
+                }
             }
             return mInfo;
         }
@@ -997,7 +1008,7 @@
                 pw.println("  " + mSupportedModes.valueAt(i));
             }
             pw.println("mSupportedColorModes=" + mSupportedColorModes.toString());
-            pw.print("mDisplayDeviceConfig=" + mDisplayDeviceConfig);
+            pw.println("mDisplayDeviceConfig=" + mDisplayDeviceConfig);
         }
 
         private int findDisplayConfigIdLocked(int modeId, int configGroup) {
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index d80e168..5bf83db 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -366,7 +366,9 @@
             mBaseDisplayInfo.displayCutout = maskCutout ? null : deviceInfo.displayCutout;
             mBaseDisplayInfo.displayId = mDisplayId;
             updateFrameRateOverrides(deviceInfo);
-
+            mBaseDisplayInfo.brightnessMinimum = deviceInfo.brightnessMinimum;
+            mBaseDisplayInfo.brightnessMaximum = deviceInfo.brightnessMaximum;
+            mBaseDisplayInfo.brightnessDefault = deviceInfo.brightnessDefault;
             mPrimaryDisplayDeviceInfo = deviceInfo;
             mInfo.set(null);
         }
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index bff81e6..71fcd1d 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -161,6 +161,9 @@
             "com.snapchat.android" // b/173297887
     };
 
+    /** TODO(b/169067926): Remove this. */
+    private static final boolean UNTRUSTED_TOUCHES_TOAST = false;
+
     // Pointer to native input manager service object.
     private final long mPtr;
 
@@ -2307,7 +2310,8 @@
     // Native callback
     private void notifyUntrustedTouch(String packageName) {
         // TODO(b/169067926): Remove toast after gathering feedback on dogfood.
-        if (ArrayUtils.contains(PACKAGE_BLOCKLIST_FOR_UNTRUSTED_TOUCHES_TOAST, packageName)) {
+        if (!UNTRUSTED_TOUCHES_TOAST || ArrayUtils.contains(
+                PACKAGE_BLOCKLIST_FOR_UNTRUSTED_TOUCHES_TOAST, packageName)) {
             Log.i(TAG, "Suppressing untrusted touch toast for " + packageName);
             return;
         }
diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java
index 9068287..9ab30b4 100644
--- a/services/core/java/com/android/server/location/LocationManagerService.java
+++ b/services/core/java/com/android/server/location/LocationManagerService.java
@@ -45,15 +45,15 @@
 import android.location.Criteria;
 import android.location.GeocoderParams;
 import android.location.Geofence;
+import android.location.GnssAntennaInfo;
 import android.location.GnssCapabilities;
 import android.location.GnssMeasurementCorrections;
 import android.location.GnssMeasurementRequest;
 import android.location.IGeocodeListener;
-import android.location.IGnssAntennaInfoListener;
 import android.location.IGnssMeasurementsListener;
 import android.location.IGnssNavigationMessageListener;
+import android.location.IGnssNmeaListener;
 import android.location.IGnssStatusListener;
-import android.location.IGpsGeofenceHardware;
 import android.location.ILocationCallback;
 import android.location.ILocationListener;
 import android.location.ILocationManager;
@@ -64,6 +64,7 @@
 import android.location.LocationProvider;
 import android.location.LocationRequest;
 import android.location.LocationTime;
+import android.location.ProviderProperties;
 import android.location.util.identity.CallerIdentity;
 import android.os.Binder;
 import android.os.Bundle;
@@ -80,17 +81,19 @@
 import android.util.Log;
 
 import com.android.internal.annotations.GuardedBy;
-import com.android.internal.location.ProviderProperties;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.Preconditions;
 import com.android.server.LocalServices;
 import com.android.server.SystemService;
 import com.android.server.location.geofence.GeofenceManager;
 import com.android.server.location.geofence.GeofenceProxy;
+import com.android.server.location.gnss.GnssConfiguration;
 import com.android.server.location.gnss.GnssManagerService;
+import com.android.server.location.gnss.hal.GnssNative;
 import com.android.server.location.injector.AlarmHelper;
 import com.android.server.location.injector.AppForegroundHelper;
 import com.android.server.location.injector.AppOpsHelper;
+import com.android.server.location.injector.EmergencyHelper;
 import com.android.server.location.injector.Injector;
 import com.android.server.location.injector.LocationAttributionHelper;
 import com.android.server.location.injector.LocationEventLog;
@@ -102,6 +105,7 @@
 import com.android.server.location.injector.SystemAlarmHelper;
 import com.android.server.location.injector.SystemAppForegroundHelper;
 import com.android.server.location.injector.SystemAppOpsHelper;
+import com.android.server.location.injector.SystemEmergencyHelper;
 import com.android.server.location.injector.SystemLocationPermissionsHelper;
 import com.android.server.location.injector.SystemLocationPowerSaveModeHelper;
 import com.android.server.location.injector.SystemScreenInteractiveHelper;
@@ -367,8 +371,10 @@
 
         // initialize gnss last because it has no awareness of boot phases and blindly assumes that
         // all other location providers are loaded at initialization
-        if (GnssManagerService.isGnssSupported()) {
-            mGnssManagerService = new GnssManagerService(mContext, mInjector);
+        if (GnssNative.isSupported()) {
+            GnssConfiguration gnssConfiguration = new GnssConfiguration(mContext);
+            GnssNative gnssNative = GnssNative.create(mInjector, gnssConfiguration);
+            mGnssManagerService = new GnssManagerService(mContext, mInjector, gnssNative);
             mGnssManagerService.onSystemReady();
 
             LocationProviderManager gnssManager = new LocationProviderManager(mContext, mInjector,
@@ -390,13 +396,11 @@
         }
 
         // bind to gnss geofence proxy
-        if (GnssManagerService.isGnssSupported()) {
-            IGpsGeofenceHardware gpsGeofenceHardware = mGnssManagerService.getGpsGeofenceProxy();
-            if (gpsGeofenceHardware != null) {
-                GeofenceProxy provider = GeofenceProxy.createAndBind(mContext, gpsGeofenceHardware);
-                if (provider == null) {
-                    Log.e(TAG, "unable to bind to GeofenceProxy");
-                }
+        if (mGnssManagerService != null) {
+            GeofenceProxy provider = GeofenceProxy.createAndBind(mContext,
+                    mGnssManagerService.getGnssGeofenceProxy());
+            if (provider == null) {
+                Log.e(TAG, "unable to bind to GeofenceProxy");
             }
         }
 
@@ -414,7 +418,7 @@
                     Boolean.parseBoolean(fragments[5]) /* supportsAltitude */,
                     Boolean.parseBoolean(fragments[6]) /* supportsSpeed */,
                     Boolean.parseBoolean(fragments[7]) /* supportsBearing */,
-                    Integer.parseInt(fragments[8]) /* powerRequirement */,
+                    Integer.parseInt(fragments[8]) /* powerUsage */,
                     Integer.parseInt(fragments[9]) /* accuracy */);
             getOrAddLocationProviderManager(name).setMockProvider(
                     new MockLocationProvider(properties, CallerIdentity.fromContext(mContext)));
@@ -516,6 +520,11 @@
     }
 
     @Override
+    public boolean hasProvider(String provider) {
+        return getLocationProviderManager(provider) != null;
+    }
+
+    @Override
     public List<String> getAllProviders() {
         ArrayList<String> providers = new ArrayList<>(mProviderManagers.size());
         for (LocationProviderManager manager : mProviderManagers) {
@@ -859,6 +868,21 @@
     }
 
     @Override
+    public void registerGnssNmeaCallback(IGnssNmeaListener listener, String packageName,
+            String attributionTag) {
+        if (mGnssManagerService != null) {
+            mGnssManagerService.registerGnssNmeaCallback(listener, packageName, attributionTag);
+        }
+    }
+
+    @Override
+    public void unregisterGnssNmeaCallback(IGnssNmeaListener listener) {
+        if (mGnssManagerService != null) {
+            mGnssManagerService.unregisterGnssNmeaCallback(listener);
+        }
+    }
+
+    @Override
     public void addGnssMeasurementsListener(@Nullable GnssMeasurementRequest request,
             IGnssMeasurementsListener listener, String packageName, String attributionTag) {
         if (mGnssManagerService != null) {
@@ -883,24 +907,14 @@
     }
 
     @Override
-    public long getGnssCapabilities() {
-        return mGnssManagerService == null ? GnssCapabilities.INVALID_CAPABILITIES
+    public GnssCapabilities getGnssCapabilities() {
+        return mGnssManagerService == null ? new GnssCapabilities.Builder().build()
                 : mGnssManagerService.getGnssCapabilities();
     }
 
     @Override
-    public void addGnssAntennaInfoListener(IGnssAntennaInfoListener listener,
-            String packageName, String attributionTag) {
-        if (mGnssManagerService != null) {
-            mGnssManagerService.addGnssAntennaInfoListener(listener, packageName, attributionTag);
-        }
-    }
-
-    @Override
-    public void removeGnssAntennaInfoListener(IGnssAntennaInfoListener listener) {
-        if (mGnssManagerService != null) {
-            mGnssManagerService.removeGnssAntennaInfoListener(listener);
-        }
+    public List<GnssAntennaInfo> getGnssAntennaInfos() {
+        return mGnssManagerService == null ? null : mGnssManagerService.getGnssAntennaInfos();
     }
 
     @Override
@@ -944,11 +958,10 @@
     }
 
     @Override
-    public ProviderProperties getProviderProperties(String providerName) {
-        LocationProviderManager manager = getLocationProviderManager(providerName);
-        if (manager == null) {
-            return null;
-        }
+    public ProviderProperties getProviderProperties(String provider) {
+        LocationProviderManager manager = getLocationProviderManager(provider);
+        Preconditions.checkArgument(manager != null,
+                "provider \"" + provider + "\" does not exist");
         return manager.getProperties();
     }
 
@@ -1285,8 +1298,10 @@
 
     private static class SystemInjector implements Injector {
 
-        private final LocationEventLog mLocationEventLog;
+        private final Context mContext;
+
         private final UserInfoHelper mUserInfoHelper;
+        private final LocationEventLog mLocationEventLog;
         private final AlarmHelper mAlarmHelper;
         private final SystemAppOpsHelper mAppOpsHelper;
         private final SystemLocationPermissionsHelper mLocationPermissionsHelper;
@@ -1297,9 +1312,19 @@
         private final LocationAttributionHelper mLocationAttributionHelper;
         private final LocationUsageLogger mLocationUsageLogger;
 
+        // lazily instantiated since they may not always be used
+
+        @GuardedBy("this")
+        private @Nullable SystemEmergencyHelper mEmergencyCallHelper;
+
+        @GuardedBy("this")
+        private boolean mSystemReady;
+
         SystemInjector(Context context, UserInfoHelper userInfoHelper) {
-            mLocationEventLog = new LocationEventLog();
+            mContext = context;
+
             mUserInfoHelper = userInfoHelper;
+            mLocationEventLog = new LocationEventLog();
             mAlarmHelper = new SystemAlarmHelper(context);
             mAppOpsHelper = new SystemAppOpsHelper(context);
             mLocationPermissionsHelper = new SystemLocationPermissionsHelper(context,
@@ -1313,13 +1338,19 @@
             mLocationUsageLogger = new LocationUsageLogger();
         }
 
-        void onSystemReady() {
+        synchronized void onSystemReady() {
             mAppOpsHelper.onSystemReady();
             mLocationPermissionsHelper.onSystemReady();
             mSettingsHelper.onSystemReady();
             mAppForegroundHelper.onSystemReady();
             mLocationPowerSaveModeHelper.onSystemReady();
             mScreenInteractiveHelper.onSystemReady();
+
+            if (mEmergencyCallHelper != null) {
+                mEmergencyCallHelper.onSystemReady();
+            }
+
+            mSystemReady = true;
         }
 
         @Override
@@ -1353,11 +1384,6 @@
         }
 
         @Override
-        public LocationUsageLogger getLocationUsageLogger() {
-            return mLocationUsageLogger;
-        }
-
-        @Override
         public LocationPowerSaveModeHelper getLocationPowerSaveModeHelper() {
             return mLocationPowerSaveModeHelper;
         }
@@ -1373,8 +1399,25 @@
         }
 
         @Override
+        public synchronized EmergencyHelper getEmergencyHelper() {
+            if (mEmergencyCallHelper == null) {
+                mEmergencyCallHelper = new SystemEmergencyHelper(mContext);
+                if (mSystemReady) {
+                    mEmergencyCallHelper.onSystemReady();
+                }
+            }
+
+            return mEmergencyCallHelper;
+        }
+
+        @Override
         public LocationEventLog getLocationEventLog() {
             return mLocationEventLog;
         }
+
+        @Override
+        public LocationUsageLogger getLocationUsageLogger() {
+            return mLocationUsageLogger;
+        }
     }
 }
diff --git a/services/core/java/com/android/server/location/gnss/GnssAntennaInfoProvider.java b/services/core/java/com/android/server/location/gnss/GnssAntennaInfoProvider.java
deleted file mode 100644
index 9961d27..0000000
--- a/services/core/java/com/android/server/location/gnss/GnssAntennaInfoProvider.java
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * 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.location.gnss;
-
-import static com.android.server.location.gnss.GnssManagerService.D;
-import static com.android.server.location.gnss.GnssManagerService.TAG;
-
-import android.location.GnssAntennaInfo;
-import android.location.IGnssAntennaInfoListener;
-import android.location.util.identity.CallerIdentity;
-import android.util.Log;
-
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.Preconditions;
-import com.android.server.location.injector.Injector;
-
-import java.util.Collection;
-import java.util.List;
-
-/**
- * Provides GNSS antenna information to clients.
- */
-public class GnssAntennaInfoProvider extends
-        GnssListenerMultiplexer<Void, IGnssAntennaInfoListener, Void> {
-
-    private final GnssAntennaInfoProviderNative mNative;
-
-    public GnssAntennaInfoProvider(Injector injector) {
-        this(injector, new GnssAntennaInfoProviderNative());
-    }
-
-    @VisibleForTesting
-    public GnssAntennaInfoProvider(Injector injector, GnssAntennaInfoProviderNative aNative) {
-        super(injector);
-        mNative = aNative;
-    }
-
-    @Override
-    protected boolean isServiceSupported() {
-        return mNative.isAntennaInfoSupported();
-    }
-
-    @Override
-    public void addListener(CallerIdentity identity, IGnssAntennaInfoListener listener) {
-        super.addListener(identity, listener);
-    }
-
-    @Override
-    protected boolean registerWithService(Void ignored,
-            Collection<GnssListenerRegistration> registrations) {
-        Preconditions.checkState(mNative.isAntennaInfoSupported());
-
-        if (mNative.startAntennaInfoListening()) {
-            if (D) {
-                Log.d(TAG, "starting gnss antenna info");
-            }
-            return true;
-        } else {
-            Log.e(TAG, "error starting gnss antenna info");
-            return false;
-        }
-    }
-
-    @Override
-    protected void unregisterWithService() {
-        if (mNative.stopAntennaInfoListening()) {
-            if (D) {
-                Log.d(TAG, "stopping gnss antenna info");
-            }
-        } else {
-            Log.e(TAG, "error stopping gnss antenna info");
-        }
-    }
-
-    /**
-     * Called by GnssLocationProvider.
-     */
-    public void onGnssAntennaInfoAvailable(List<GnssAntennaInfo> gnssAntennaInfos) {
-        deliverToListeners((listener) -> {
-            listener.onGnssAntennaInfoReceived(gnssAntennaInfos);
-        });
-    }
-
-    /**
-     * Wrapper class for native methods. This is mocked for testing.
-     */
-    @VisibleForTesting
-    public static class GnssAntennaInfoProviderNative {
-
-        public boolean isAntennaInfoSupported() {
-            return native_is_antenna_info_supported();
-        }
-
-        /** Start antenna info listening. */
-        public boolean startAntennaInfoListening() {
-            return native_start_antenna_info_listening();
-        }
-
-        /** Stop antenna info listening. */
-        public boolean stopAntennaInfoListening() {
-            return native_stop_antenna_info_listening();
-        }
-    }
-
-    static native boolean native_is_antenna_info_supported();
-
-    static native boolean native_start_antenna_info_listening();
-
-    static native boolean native_stop_antenna_info_listening();
-}
diff --git a/services/core/java/com/android/server/location/gnss/GnssCapabilitiesProvider.java b/services/core/java/com/android/server/location/gnss/GnssCapabilitiesProvider.java
deleted file mode 100644
index 1c4fb10..0000000
--- a/services/core/java/com/android/server/location/gnss/GnssCapabilitiesProvider.java
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * 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.location.gnss;
-
-import android.location.GnssCapabilities;
-import android.util.Log;
-
-import com.android.internal.annotations.GuardedBy;
-
-/**
- * Provides GNSS capabilities supported by the GNSS HAL implementation.
- */
-public class GnssCapabilitiesProvider {
-    private static final String TAG = "GnssCapabilitiesProvider";
-    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
-
-    private static final long GNSS_CAPABILITIES_TOP_HAL =
-            GnssCapabilities.LOW_POWER_MODE | GnssCapabilities.SATELLITE_BLOCKLIST
-                    | GnssCapabilities.GEOFENCING | GnssCapabilities.MEASUREMENTS
-                    | GnssCapabilities.NAV_MESSAGES;
-
-    private static final long GNSS_CAPABILITIES_SUB_HAL_MEASUREMENT_CORRECTIONS =
-            GnssCapabilities.MEASUREMENT_CORRECTIONS
-                    | GnssCapabilities.MEASUREMENT_CORRECTIONS_LOS_SATS
-                    | GnssCapabilities.MEASUREMENT_CORRECTIONS_EXCESS_PATH_LENGTH
-                    | GnssCapabilities.MEASUREMENT_CORRECTIONS_REFLECTING_PLANE;
-
-    // Capabilities in {@link android.location.GnssCapabilities} supported by GNSS chipset.
-    @GuardedBy("this")
-    private long mGnssCapabilities;
-
-    /**
-     * Returns the capabilities supported by the GNSS chipset.
-     *
-     * <p>The capabilities are described in {@link android.location.GnssCapabilities} and
-     * their integer values correspond to the bit positions in the returned {@code long} value.
-     */
-    public long getGnssCapabilities() {
-        synchronized (this) {
-            return mGnssCapabilities;
-        }
-    }
-
-    /**
-     * Updates the general capabilities exposed through {@link android.location.GnssCapabilities}.
-     */
-    void setTopHalCapabilities(int topHalCapabilities) {
-        long gnssCapabilities = 0;
-        if (hasCapability(topHalCapabilities,
-                GnssLocationProvider.GPS_CAPABILITY_LOW_POWER_MODE)) {
-            gnssCapabilities |= GnssCapabilities.LOW_POWER_MODE;
-        }
-        if (hasCapability(topHalCapabilities,
-                GnssLocationProvider.GPS_CAPABILITY_SATELLITE_BLOCKLIST)) {
-            gnssCapabilities |= GnssCapabilities.SATELLITE_BLOCKLIST;
-        }
-        if (hasCapability(topHalCapabilities, GnssLocationProvider.GPS_CAPABILITY_GEOFENCING)) {
-            gnssCapabilities |= GnssCapabilities.GEOFENCING;
-        }
-        if (hasCapability(topHalCapabilities, GnssLocationProvider.GPS_CAPABILITY_MEASUREMENTS)) {
-            gnssCapabilities |= GnssCapabilities.MEASUREMENTS;
-        }
-        if (hasCapability(topHalCapabilities, GnssLocationProvider.GPS_CAPABILITY_NAV_MESSAGES)) {
-            gnssCapabilities |= GnssCapabilities.NAV_MESSAGES;
-        }
-        if (hasCapability(topHalCapabilities, GnssLocationProvider.GPS_CAPABILITY_ANTENNA_INFO)) {
-            gnssCapabilities |= GnssCapabilities.ANTENNA_INFO;
-        }
-
-        synchronized (this) {
-            mGnssCapabilities &= ~GNSS_CAPABILITIES_TOP_HAL;
-            mGnssCapabilities |= gnssCapabilities;
-            if (DEBUG) {
-                Log.d(TAG, "setTopHalCapabilities, mGnssCapabilities=0x" + Long.toHexString(
-                        mGnssCapabilities) + ", " + GnssCapabilities.of(mGnssCapabilities));
-            }
-        }
-    }
-
-    /**
-     * Updates the measurement corrections related capabilities exposed through
-     * {@link android.location.GnssCapabilities}.
-     */
-    void setSubHalMeasurementCorrectionsCapabilities(int measurementCorrectionsCapabilities) {
-        long gnssCapabilities = GnssCapabilities.MEASUREMENT_CORRECTIONS;
-        if (hasCapability(measurementCorrectionsCapabilities,
-                GnssMeasurementCorrectionsProvider.CAPABILITY_LOS_SATS)) {
-            gnssCapabilities |= GnssCapabilities.MEASUREMENT_CORRECTIONS_LOS_SATS;
-        }
-        if (hasCapability(measurementCorrectionsCapabilities,
-                GnssMeasurementCorrectionsProvider.CAPABILITY_EXCESS_PATH_LENGTH)) {
-            gnssCapabilities |= GnssCapabilities.MEASUREMENT_CORRECTIONS_EXCESS_PATH_LENGTH;
-        }
-        if (hasCapability(measurementCorrectionsCapabilities,
-                GnssMeasurementCorrectionsProvider.CAPABILITY_REFLECTING_PLANE)) {
-            gnssCapabilities |= GnssCapabilities.MEASUREMENT_CORRECTIONS_REFLECTING_PLANE;
-        }
-
-        synchronized (this) {
-            mGnssCapabilities &= ~GNSS_CAPABILITIES_SUB_HAL_MEASUREMENT_CORRECTIONS;
-            mGnssCapabilities |= gnssCapabilities;
-            if (DEBUG) {
-                Log.d(TAG, "setSubHalMeasurementCorrectionsCapabilities, mGnssCapabilities=0x"
-                        + Long.toHexString(mGnssCapabilities) + ", " + GnssCapabilities.of(
-                        mGnssCapabilities));
-            }
-        }
-    }
-
-    private static boolean hasCapability(int halCapabilities, int capability) {
-        return (halCapabilities & capability) != 0;
-    }
-}
diff --git a/services/core/java/com/android/server/location/gnss/GnssConfiguration.java b/services/core/java/com/android/server/location/gnss/GnssConfiguration.java
index 2628372..60b7447 100644
--- a/services/core/java/com/android/server/location/gnss/GnssConfiguration.java
+++ b/services/core/java/com/android/server/location/gnss/GnssConfiguration.java
@@ -31,7 +31,7 @@
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.IOException;
-import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
@@ -48,7 +48,7 @@
  * Instances of this class are not thread-safe and should either be used from a single thread
  * or with external synchronization when used by multiple threads.
  */
-class GnssConfiguration {
+public class GnssConfiguration {
     private static final String TAG = "GnssConfiguration";
 
     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
@@ -98,13 +98,13 @@
     /**
      * Properties loaded from PROPERTIES_FILE.
      */
-    private Properties mProperties;
+    private final Properties mProperties;
 
     private int mEsExtensionSec = 0;
 
     private final Context mContext;
 
-    GnssConfiguration(Context context) {
+    public GnssConfiguration(Context context) {
         mContext = context;
         mProperties = new Properties();
     }
@@ -120,7 +120,7 @@
      * Returns the value of config parameter ES_EXTENSION_SEC. The value is range checked
      * and constrained to min/max limits.
      */
-    int getEsExtensionSec() {
+    public int getEsExtensionSec() {
         return mEsExtensionSec;
     }
 
@@ -168,8 +168,8 @@
      * Returns the value of config parameter SUPL_ES or {@code defaultSuplEs} if no value is
      * provided or if there is an error parsing the configured value.
      */
-    int getSuplEs(int defaulSuplEs) {
-        return getIntConfig(CONFIG_SUPL_ES, defaulSuplEs);
+    public int getSuplEs(int defaultSuplEs) {
+        return getIntConfig(CONFIG_SUPL_ES, defaultSuplEs);
     }
 
     /**
@@ -181,27 +181,21 @@
     }
 
     /**
-     * Returns the list of proxy apps from the value of config parameter NFW_PROXY_APPS or
-     * {@Collections.EMPTY_LIST} if no value is provided.
+     * Returns the list of proxy apps from the value of config parameter NFW_PROXY_APPS.
      */
     List<String> getProxyApps() {
         // Space separated list of Android proxy app package names.
         String proxyAppsStr = mProperties.getProperty(CONFIG_NFW_PROXY_APPS);
         if (TextUtils.isEmpty(proxyAppsStr)) {
-            return Collections.EMPTY_LIST;
+            return Collections.emptyList();
         }
 
         String[] proxyAppsArray = proxyAppsStr.trim().split("\\s+");
         if (proxyAppsArray.length == 0) {
-            return Collections.EMPTY_LIST;
+            return Collections.emptyList();
         }
 
-        ArrayList proxyApps = new ArrayList(proxyAppsArray.length);
-        for (String proxyApp : proxyAppsArray) {
-            proxyApps.add(proxyApp);
-        }
-
-        return proxyApps;
+        return Arrays.asList(proxyAppsArray);
     }
 
     /**
diff --git a/services/core/java/com/android/server/location/gnss/GnssGeofenceProvider.java b/services/core/java/com/android/server/location/gnss/GnssGeofenceProvider.java
deleted file mode 100644
index 53883b9..0000000
--- a/services/core/java/com/android/server/location/gnss/GnssGeofenceProvider.java
+++ /dev/null
@@ -1,189 +0,0 @@
-/*
- * 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.location.gnss;
-
-import android.location.IGpsGeofenceHardware;
-import android.util.Log;
-import android.util.SparseArray;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
-
-/**
- * Manages GNSS Geofence operations.
- */
-class GnssGeofenceProvider extends IGpsGeofenceHardware.Stub {
-
-    private static final String TAG = "GnssGeofenceProvider";
-    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
-
-    /** Holds the parameters of a geofence. */
-    private static class GeofenceEntry {
-        public int geofenceId;
-        public double latitude;
-        public double longitude;
-        public double radius;
-        public int lastTransition;
-        public int monitorTransitions;
-        public int notificationResponsiveness;
-        public int unknownTimer;
-        public boolean paused;
-    }
-
-    private final Object mLock = new Object();
-    @GuardedBy("mLock")
-    private final GnssGeofenceProviderNative mNative;
-    @GuardedBy("mLock")
-    private final SparseArray<GeofenceEntry> mGeofenceEntries = new SparseArray<>();
-
-    GnssGeofenceProvider() {
-        this(new GnssGeofenceProviderNative());
-    }
-
-    @VisibleForTesting
-    GnssGeofenceProvider(GnssGeofenceProviderNative gnssGeofenceProviderNative) {
-        mNative = gnssGeofenceProviderNative;
-    }
-
-    void resumeIfStarted() {
-        if (DEBUG) {
-            Log.d(TAG, "resumeIfStarted");
-        }
-        synchronized (mLock) {
-            for (int i = 0; i < mGeofenceEntries.size(); i++) {
-                GeofenceEntry entry = mGeofenceEntries.valueAt(i);
-                boolean added = mNative.addGeofence(entry.geofenceId, entry.latitude,
-                        entry.longitude,
-                        entry.radius,
-                        entry.lastTransition, entry.monitorTransitions,
-                        entry.notificationResponsiveness, entry.unknownTimer);
-                if (added && entry.paused) {
-                    mNative.pauseGeofence(entry.geofenceId);
-                }
-            }
-        }
-    }
-
-    @Override
-    public boolean isHardwareGeofenceSupported() {
-        synchronized (mLock) {
-            return mNative.isGeofenceSupported();
-        }
-    }
-
-    @Override
-    public boolean addCircularHardwareGeofence(int geofenceId, double latitude,
-            double longitude, double radius, int lastTransition, int monitorTransitions,
-            int notificationResponsiveness, int unknownTimer) {
-        synchronized (mLock) {
-            boolean added = mNative.addGeofence(geofenceId, latitude, longitude, radius,
-                    lastTransition, monitorTransitions, notificationResponsiveness,
-                    unknownTimer);
-            if (added) {
-                GeofenceEntry entry = new GeofenceEntry();
-                entry.geofenceId = geofenceId;
-                entry.latitude = latitude;
-                entry.longitude = longitude;
-                entry.radius = radius;
-                entry.lastTransition = lastTransition;
-                entry.monitorTransitions = monitorTransitions;
-                entry.notificationResponsiveness = notificationResponsiveness;
-                entry.unknownTimer = unknownTimer;
-                mGeofenceEntries.put(geofenceId, entry);
-            }
-            return added;
-        }
-    }
-
-    @Override
-    public boolean removeHardwareGeofence(int geofenceId) {
-        synchronized (mLock) {
-            boolean removed = mNative.removeGeofence(geofenceId);
-            if (removed) {
-                mGeofenceEntries.remove(geofenceId);
-            }
-            return removed;
-        }
-    }
-
-    @Override
-    public boolean pauseHardwareGeofence(int geofenceId) {
-        synchronized (mLock) {
-            boolean paused = mNative.pauseGeofence(geofenceId);
-            if (paused) {
-                GeofenceEntry entry = mGeofenceEntries.get(geofenceId);
-                if (entry != null) {
-                    entry.paused = true;
-                }
-            }
-            return paused;
-        }
-    }
-
-    @Override
-    public boolean resumeHardwareGeofence(int geofenceId, int monitorTransitions) {
-        synchronized (mLock) {
-            boolean resumed = mNative.resumeGeofence(geofenceId, monitorTransitions);
-            if (resumed) {
-                GeofenceEntry entry = mGeofenceEntries.get(geofenceId);
-                if (entry != null) {
-                    entry.paused = false;
-                    entry.monitorTransitions = monitorTransitions;
-                }
-            }
-            return resumed;
-        }
-    }
-
-    @VisibleForTesting
-    static class GnssGeofenceProviderNative {
-        public boolean isGeofenceSupported() {
-            return native_is_geofence_supported();
-        }
-
-        public boolean addGeofence(int geofenceId, double latitude, double longitude, double radius,
-                int lastTransition, int monitorTransitions, int notificationResponsiveness,
-                int unknownTimer) {
-            return native_add_geofence(geofenceId, latitude, longitude, radius, lastTransition,
-                    monitorTransitions, notificationResponsiveness, unknownTimer);
-        }
-
-        public boolean removeGeofence(int geofenceId) {
-            return native_remove_geofence(geofenceId);
-        }
-
-        public boolean resumeGeofence(int geofenceId, int transitions) {
-            return native_resume_geofence(geofenceId, transitions);
-        }
-
-        public boolean pauseGeofence(int geofenceId) {
-            return native_pause_geofence(geofenceId);
-        }
-    }
-
-    private static native boolean native_is_geofence_supported();
-
-    private static native boolean native_add_geofence(int geofenceId, double latitude,
-            double longitude, double radius, int lastTransition, int monitorTransitions,
-            int notificationResponsivenes, int unknownTimer);
-
-    private static native boolean native_remove_geofence(int geofenceId);
-
-    private static native boolean native_resume_geofence(int geofenceId, int transitions);
-
-    private static native boolean native_pause_geofence(int geofenceId);
-}
diff --git a/services/core/java/com/android/server/location/gnss/GnssGeofenceProxy.java b/services/core/java/com/android/server/location/gnss/GnssGeofenceProxy.java
new file mode 100644
index 0000000..f40ded9
--- /dev/null
+++ b/services/core/java/com/android/server/location/gnss/GnssGeofenceProxy.java
@@ -0,0 +1,143 @@
+/*
+ * 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.location.gnss;
+
+import android.location.IGpsGeofenceHardware;
+import android.util.SparseArray;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.location.gnss.hal.GnssNative;
+
+/**
+ * Manages GNSS Geofence operations.
+ */
+class GnssGeofenceProxy extends IGpsGeofenceHardware.Stub implements GnssNative.BaseCallbacks {
+
+    /** Holds the parameters of a geofence. */
+    private static class GeofenceEntry {
+        public int geofenceId;
+        public double latitude;
+        public double longitude;
+        public double radius;
+        public int lastTransition;
+        public int monitorTransitions;
+        public int notificationResponsiveness;
+        public int unknownTimer;
+        public boolean paused;
+    }
+
+    private final Object mLock = new Object();
+
+    private final GnssNative mGnssNative;
+
+    @GuardedBy("mLock")
+    private final SparseArray<GeofenceEntry> mGeofenceEntries = new SparseArray<>();
+
+    GnssGeofenceProxy(GnssNative gnssNative) {
+        mGnssNative = gnssNative;
+
+        mGnssNative.addBaseCallbacks(this);
+    }
+
+    @Override
+    public boolean isHardwareGeofenceSupported() {
+        synchronized (mLock) {
+            return mGnssNative.isGeofencingSupported();
+        }
+    }
+
+    @Override
+    public boolean addCircularHardwareGeofence(int geofenceId, double latitude,
+            double longitude, double radius, int lastTransition, int monitorTransitions,
+            int notificationResponsiveness, int unknownTimer) {
+        synchronized (mLock) {
+            boolean added = mGnssNative.addGeofence(geofenceId, latitude, longitude, radius,
+                    lastTransition, monitorTransitions, notificationResponsiveness,
+                    unknownTimer);
+            if (added) {
+                GeofenceEntry entry = new GeofenceEntry();
+                entry.geofenceId = geofenceId;
+                entry.latitude = latitude;
+                entry.longitude = longitude;
+                entry.radius = radius;
+                entry.lastTransition = lastTransition;
+                entry.monitorTransitions = monitorTransitions;
+                entry.notificationResponsiveness = notificationResponsiveness;
+                entry.unknownTimer = unknownTimer;
+                mGeofenceEntries.put(geofenceId, entry);
+            }
+            return added;
+        }
+    }
+
+    @Override
+    public boolean removeHardwareGeofence(int geofenceId) {
+        synchronized (mLock) {
+            boolean removed = mGnssNative.removeGeofence(geofenceId);
+            if (removed) {
+                mGeofenceEntries.remove(geofenceId);
+            }
+            return removed;
+        }
+    }
+
+    @Override
+    public boolean pauseHardwareGeofence(int geofenceId) {
+        synchronized (mLock) {
+            boolean paused = mGnssNative.pauseGeofence(geofenceId);
+            if (paused) {
+                GeofenceEntry entry = mGeofenceEntries.get(geofenceId);
+                if (entry != null) {
+                    entry.paused = true;
+                }
+            }
+            return paused;
+        }
+    }
+
+    @Override
+    public boolean resumeHardwareGeofence(int geofenceId, int monitorTransitions) {
+        synchronized (mLock) {
+            boolean resumed = mGnssNative.resumeGeofence(geofenceId, monitorTransitions);
+            if (resumed) {
+                GeofenceEntry entry = mGeofenceEntries.get(geofenceId);
+                if (entry != null) {
+                    entry.paused = false;
+                    entry.monitorTransitions = monitorTransitions;
+                }
+            }
+            return resumed;
+        }
+    }
+
+    @Override
+    public void onHalRestarted() {
+        synchronized (mLock) {
+            for (int i = 0; i < mGeofenceEntries.size(); i++) {
+                GeofenceEntry entry = mGeofenceEntries.valueAt(i);
+                boolean added = mGnssNative.addGeofence(entry.geofenceId, entry.latitude,
+                        entry.longitude,
+                        entry.radius,
+                        entry.lastTransition, entry.monitorTransitions,
+                        entry.notificationResponsiveness, entry.unknownTimer);
+                if (added && entry.paused) {
+                    mGnssNative.pauseGeofence(entry.geofenceId);
+                }
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java b/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java
index 7e848e0..87e6ef4 100644
--- a/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java
+++ b/services/core/java/com/android/server/location/gnss/GnssListenerMultiplexer.java
@@ -96,12 +96,29 @@
         }
 
         @Override
-        protected void onBinderListenerRegister() {
+        protected final void onBinderListenerRegister() {
             mPermitted = mLocationPermissionsHelper.hasLocationPermissions(PERMISSION_FINE,
                     getIdentity());
             mForeground = mAppForegroundHelper.isAppForeground(getIdentity().getUid());
+
+            onGnssListenerRegister();
         }
 
+        @Override
+        protected final void onBinderListenerUnregister() {
+            onGnssListenerUnregister();
+        }
+
+        /**
+         * May be overridden in place of {@link #onBinderListenerRegister()}.
+         */
+        protected void onGnssListenerRegister() {}
+
+        /**
+         * May be overridden in place of {@link #onBinderListenerUnregister()}.
+         */
+        protected void onGnssListenerUnregister() {}
+
         boolean onLocationPermissionsChanged(String packageName) {
             if (getIdentity().getPackageName().equals(packageName)) {
                 return onLocationPermissionsChanged();
diff --git a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
index 0c77c1e..477c037b 100644
--- a/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssLocationProvider.java
@@ -17,6 +17,28 @@
 package com.android.server.location.gnss;
 
 import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
+import static com.android.server.location.gnss.hal.GnssNative.AGPS_REF_LOCATION_TYPE_GSM_CELLID;
+import static com.android.server.location.gnss.hal.GnssNative.AGPS_REF_LOCATION_TYPE_UMTS_CELLID;
+import static com.android.server.location.gnss.hal.GnssNative.AGPS_SETID_TYPE_IMSI;
+import static com.android.server.location.gnss.hal.GnssNative.AGPS_SETID_TYPE_MSISDN;
+import static com.android.server.location.gnss.hal.GnssNative.AGPS_SETID_TYPE_NONE;
+import static com.android.server.location.gnss.hal.GnssNative.GNSS_AIDING_TYPE_ALL;
+import static com.android.server.location.gnss.hal.GnssNative.GNSS_AIDING_TYPE_ALMANAC;
+import static com.android.server.location.gnss.hal.GnssNative.GNSS_AIDING_TYPE_CELLDB_INFO;
+import static com.android.server.location.gnss.hal.GnssNative.GNSS_AIDING_TYPE_EPHEMERIS;
+import static com.android.server.location.gnss.hal.GnssNative.GNSS_AIDING_TYPE_HEALTH;
+import static com.android.server.location.gnss.hal.GnssNative.GNSS_AIDING_TYPE_IONO;
+import static com.android.server.location.gnss.hal.GnssNative.GNSS_AIDING_TYPE_POSITION;
+import static com.android.server.location.gnss.hal.GnssNative.GNSS_AIDING_TYPE_RTI;
+import static com.android.server.location.gnss.hal.GnssNative.GNSS_AIDING_TYPE_SADATA;
+import static com.android.server.location.gnss.hal.GnssNative.GNSS_AIDING_TYPE_SVDIR;
+import static com.android.server.location.gnss.hal.GnssNative.GNSS_AIDING_TYPE_SVSTEER;
+import static com.android.server.location.gnss.hal.GnssNative.GNSS_AIDING_TYPE_TIME;
+import static com.android.server.location.gnss.hal.GnssNative.GNSS_AIDING_TYPE_UTC;
+import static com.android.server.location.gnss.hal.GnssNative.GNSS_POSITION_MODE_MS_ASSISTED;
+import static com.android.server.location.gnss.hal.GnssNative.GNSS_POSITION_MODE_MS_BASED;
+import static com.android.server.location.gnss.hal.GnssNative.GNSS_POSITION_MODE_STANDALONE;
+import static com.android.server.location.gnss.hal.GnssNative.GNSS_POSITION_RECURRENCE_PERIODIC;
 
 import static java.util.concurrent.TimeUnit.MILLISECONDS;
 
@@ -28,21 +50,15 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.database.ContentObserver;
-import android.hardware.location.GeofenceHardware;
-import android.hardware.location.GeofenceHardwareImpl;
-import android.location.Criteria;
-import android.location.FusedBatchOptions;
-import android.location.GnssAntennaInfo;
-import android.location.GnssMeasurementsEvent;
-import android.location.GnssNavigationMessage;
+import android.location.GnssCapabilities;
 import android.location.GnssStatus;
-import android.location.IGpsGeofenceHardware;
 import android.location.INetInitiatedListener;
 import android.location.Location;
 import android.location.LocationListener;
 import android.location.LocationManager;
 import android.location.LocationRequest;
 import android.location.LocationResult;
+import android.location.ProviderProperties;
 import android.location.util.identity.CallerIdentity;
 import android.os.AsyncTask;
 import android.os.BatteryStats;
@@ -72,13 +88,12 @@
 import com.android.internal.app.IBatteryStats;
 import com.android.internal.location.GpsNetInitiatedHandler;
 import com.android.internal.location.GpsNetInitiatedHandler.GpsNiNotification;
-import com.android.internal.location.ProviderProperties;
 import com.android.internal.location.ProviderRequest;
-import com.android.internal.location.gnssmetrics.GnssMetrics;
 import com.android.internal.util.FrameworkStatsLog;
 import com.android.server.FgThread;
 import com.android.server.location.gnss.GnssSatelliteBlocklistHelper.GnssSatelliteBlocklistCallback;
 import com.android.server.location.gnss.NtpTimeHelper.InjectNtpTimeCallback;
+import com.android.server.location.gnss.hal.GnssNative;
 import com.android.server.location.injector.Injector;
 import com.android.server.location.provider.AbstractLocationProvider;
 
@@ -97,8 +112,10 @@
  * {@hide}
  */
 public class GnssLocationProvider extends AbstractLocationProvider implements
-        InjectNtpTimeCallback,
-        GnssSatelliteBlocklistCallback {
+        InjectNtpTimeCallback, GnssSatelliteBlocklistCallback, GnssNative.BaseCallbacks,
+        GnssNative.LocationCallbacks, GnssNative.SvStatusCallbacks, GnssNative.AGpsCallbacks,
+        GnssNative.PsdsCallbacks, GnssNative.NotificationCallbacks,
+        GnssNative.LocationRequestCallbacks, GnssNative.TimeCallbacks {
 
     private static final String TAG = "GnssLocationProvider";
 
@@ -113,104 +130,20 @@
             /* supportAltitude = */true,
             /* supportsSpeed = */true,
             /* supportsBearing = */true,
-            Criteria.POWER_HIGH,
-            Criteria.ACCURACY_FINE);
-
-    // these need to match GnssPositionMode enum in IGnss.hal
-    private static final int GPS_POSITION_MODE_STANDALONE = 0;
-    private static final int GPS_POSITION_MODE_MS_BASED = 1;
-    private static final int GPS_POSITION_MODE_MS_ASSISTED = 2;
-
-    // these need to match GnssPositionRecurrence enum in IGnss.hal
-    private static final int GPS_POSITION_RECURRENCE_PERIODIC = 0;
-    private static final int GPS_POSITION_RECURRENCE_SINGLE = 1;
-
-    // these need to match GnssStatusValue enum in IGnssCallback.hal
-    private static final int GPS_STATUS_NONE = 0;
-    private static final int GPS_STATUS_SESSION_BEGIN = 1;
-    private static final int GPS_STATUS_SESSION_END = 2;
-    private static final int GPS_STATUS_ENGINE_ON = 3;
-    private static final int GPS_STATUS_ENGINE_OFF = 4;
-
-    // these need to match GnssLocationFlags enum in types.hal
-    private static final int LOCATION_INVALID = 0;
-    private static final int LOCATION_HAS_LAT_LONG = 1;
-    private static final int LOCATION_HAS_ALTITUDE = 2;
-    private static final int LOCATION_HAS_SPEED = 4;
-    private static final int LOCATION_HAS_BEARING = 8;
-    private static final int LOCATION_HAS_HORIZONTAL_ACCURACY = 16;
-    private static final int LOCATION_HAS_VERTICAL_ACCURACY = 32;
-    private static final int LOCATION_HAS_SPEED_ACCURACY = 64;
-    private static final int LOCATION_HAS_BEARING_ACCURACY = 128;
-
-    // these need to match ElapsedRealtimeFlags enum in types.hal
-    private static final int ELAPSED_REALTIME_HAS_TIMESTAMP_NS = 1;
-    private static final int ELAPSED_REALTIME_HAS_TIME_UNCERTAINTY_NS = 2;
-
-    // IMPORTANT - the GPS_DELETE_* symbols here must match GnssAidingData enum in IGnss.hal
-    private static final int GPS_DELETE_EPHEMERIS = 0x0001;
-    private static final int GPS_DELETE_ALMANAC = 0x0002;
-    private static final int GPS_DELETE_POSITION = 0x0004;
-    private static final int GPS_DELETE_TIME = 0x0008;
-    private static final int GPS_DELETE_IONO = 0x0010;
-    private static final int GPS_DELETE_UTC = 0x0020;
-    private static final int GPS_DELETE_HEALTH = 0x0040;
-    private static final int GPS_DELETE_SVDIR = 0x0080;
-    private static final int GPS_DELETE_SVSTEER = 0x0100;
-    private static final int GPS_DELETE_SADATA = 0x0200;
-    private static final int GPS_DELETE_RTI = 0x0400;
-    private static final int GPS_DELETE_CELLDB_INFO = 0x8000;
-    private static final int GPS_DELETE_ALL = 0xFFFF;
-
-    // The GPS_CAPABILITY_* flags must match Capabilities enum in IGnssCallback.hal
-    private static final int GPS_CAPABILITY_SCHEDULING = 0x0000001;
-    private static final int GPS_CAPABILITY_MSB = 0x0000002;
-    private static final int GPS_CAPABILITY_MSA = 0x0000004;
-    private static final int GPS_CAPABILITY_SINGLE_SHOT = 0x0000008;
-    private static final int GPS_CAPABILITY_ON_DEMAND_TIME = 0x0000010;
-    public static final int GPS_CAPABILITY_GEOFENCING = 0x0000020;
-    public static final int GPS_CAPABILITY_MEASUREMENTS = 0x0000040;
-    public static final int GPS_CAPABILITY_NAV_MESSAGES = 0x0000080;
-    public static final int GPS_CAPABILITY_LOW_POWER_MODE = 0x0000100;
-    public static final int GPS_CAPABILITY_SATELLITE_BLOCKLIST = 0x0000200;
-    public static final int GPS_CAPABILITY_MEASUREMENT_CORRECTIONS = 0x0000400;
-    public static final int GPS_CAPABILITY_ANTENNA_INFO = 0x0000800;
+            ProviderProperties.POWER_USAGE_HIGH,
+            ProviderProperties.ACCURACY_FINE);
 
     // The AGPS SUPL mode
     private static final int AGPS_SUPL_MODE_MSA = 0x02;
     private static final int AGPS_SUPL_MODE_MSB = 0x01;
 
+    // handler messages
     private static final int INJECT_NTP_TIME = 5;
-    // PSDS stands for Predicted Satellite Data Service
     private static final int DOWNLOAD_PSDS_DATA = 6;
     private static final int REQUEST_LOCATION = 16;
     private static final int REPORT_LOCATION = 17; // HAL reports location
     private static final int REPORT_SV_STATUS = 18; // HAL reports SV status
 
-    // Request setid
-    private static final int AGPS_RIL_REQUEST_SETID_IMSI = 1;
-    private static final int AGPS_RIL_REQUEST_SETID_MSISDN = 2;
-
-    // ref. location info
-    private static final int AGPS_REF_LOCATION_TYPE_GSM_CELLID = 1;
-    private static final int AGPS_REF_LOCATION_TYPE_UMTS_CELLID = 2;
-
-    // set id info
-    private static final int AGPS_SETID_TYPE_NONE = 0;
-    private static final int AGPS_SETID_TYPE_IMSI = 1;
-    private static final int AGPS_SETID_TYPE_MSISDN = 2;
-
-    private static final int GPS_GEOFENCE_UNAVAILABLE = 1 << 0L;
-    private static final int GPS_GEOFENCE_AVAILABLE = 1 << 1L;
-
-    // GPS Geofence errors. Should match GeofenceStatus enum in IGnssGeofenceCallback.hal.
-    private static final int GPS_GEOFENCE_OPERATION_SUCCESS = 0;
-    private static final int GPS_GEOFENCE_ERROR_TOO_MANY_GEOFENCES = 100;
-    private static final int GPS_GEOFENCE_ERROR_ID_EXISTS = -101;
-    private static final int GPS_GEOFENCE_ERROR_ID_UNKNOWN = -102;
-    private static final int GPS_GEOFENCE_ERROR_INVALID_TRANSITION = -103;
-    private static final int GPS_GEOFENCE_ERROR_GENERIC = -149;
-
     // TCP/IP constants.
     // Valid TCP/UDP port range is (0, 65535].
     private static final int TCP_MIN_PORT = 0;
@@ -290,19 +223,13 @@
     private static final long LOCATION_OFF_DELAY_THRESHOLD_WARN_MILLIS = 2 * 1000;
     private static final long LOCATION_OFF_DELAY_THRESHOLD_ERROR_MILLIS = 15 * 1000;
 
-    private static final String DOWNLOAD_EXTRA_WAKELOCK_KEY = "GnssLocationProviderPsdsDownload";
-
-    // Set lower than the current ITAR limit of 600m/s to allow this to trigger even if GPS HAL
-    // stops output right at 600m/s, depriving this of the information of a device that reaches
-    // greater than 600m/s, and higher than the speed of sound to avoid impacting most use cases.
-    private static final float ITAR_SPEED_LIMIT_METERS_PER_SECOND = 400.0F;
-
-
     private final Object mLock = new Object();
 
     private final Context mContext;
     private final Handler mHandler;
 
+    private final GnssNative mGnssNative;
+
     @GuardedBy("mLock")
     private final ExponentialBackOff mPsdsBackOff = new ExponentialBackOff(RETRY_INTERVAL,
             MAX_RETRY_INTERVAL);
@@ -315,7 +242,6 @@
     private boolean mBatchingEnabled;
 
     private boolean mShutdown;
-    private boolean mNavigating;
     private boolean mStarted;
     private boolean mBatchingStarted;
     private long mStartedChangedElapsedRealtime;
@@ -335,9 +261,6 @@
 
     private final WorkSource mClientSource = new WorkSource();
 
-    // capabilities reported through the top level IGnssCallback.hal
-    private volatile int mTopHalCapabilities;
-
     // true if PSDS is supported
     private boolean mSupportsPsds;
     @GuardedBy("mLock")
@@ -358,15 +281,7 @@
     private boolean mSuplEsEnabled = false;
 
     private final LocationExtras mLocationExtras = new LocationExtras();
-    private final GnssStatusProvider mGnssStatusListenerHelper;
-    private final GnssMeasurementsProvider mGnssMeasurementsProvider;
-    private final GnssMeasurementCorrectionsProvider mGnssMeasurementCorrectionsProvider;
-    private final GnssAntennaInfoProvider mGnssAntennaInfoProvider;
-    private final GnssNavigationMessageProvider mGnssNavigationMessageProvider;
-    private final GnssPowerIndicationProvider mGnssPowerIndicationProvider;
     private final NtpTimeHelper mNtpTimeHelper;
-    private final GnssGeofenceProvider mGnssGeofenceProvider;
-    private final GnssCapabilitiesProvider mGnssCapabilitiesProvider;
     private final GnssSatelliteBlocklistHelper mGnssSatelliteBlocklistHelper;
 
     // Available only on GNSS HAL 2.0 implementations and later.
@@ -385,44 +300,12 @@
     private final AppOpsManager mAppOps;
     private final IBatteryStats mBatteryStats;
 
-    private GeofenceHardwareImpl mGeofenceHardwareImpl;
-
-    // Volatile for simple inter-thread sync on these values.
-    private volatile int mHardwareYear = 0;
-    private volatile String mHardwareModelName;
-
-    private volatile boolean mItarSpeedLimitExceeded = false;
-
     @GuardedBy("mLock")
     private final ArrayList<Runnable> mFlushListeners = new ArrayList<>(0);
 
     // GNSS Metrics
     private final GnssMetrics mGnssMetrics;
 
-    public GnssStatusProvider getGnssStatusProvider() {
-        return mGnssStatusListenerHelper;
-    }
-
-    public IGpsGeofenceHardware getGpsGeofenceProxy() {
-        return mGnssGeofenceProvider;
-    }
-
-    public GnssMeasurementsProvider getGnssMeasurementsProvider() {
-        return mGnssMeasurementsProvider;
-    }
-
-    public GnssMeasurementCorrectionsProvider getGnssMeasurementCorrectionsProvider() {
-        return mGnssMeasurementCorrectionsProvider;
-    }
-
-    public GnssAntennaInfoProvider getGnssAntennaInfoProvider() {
-        return mGnssAntennaInfoProvider;
-    }
-
-    public GnssNavigationMessageProvider getGnssNavigationMessageProvider() {
-        return mGnssNavigationMessageProvider;
-    }
-
     /**
      * Implements {@link GnssSatelliteBlocklistCallback#onUpdateSatelliteBlocklist}.
      */
@@ -486,20 +369,23 @@
         }
     }
 
-    public GnssLocationProvider(Context context, Injector injector) {
-        super(FgThread.getExecutor(), CallerIdentity.fromContext(context));
+    public GnssLocationProvider(Context context, Injector injector, GnssNative gnssNative,
+            GnssMetrics gnssMetrics) {
+        super(FgThread.getExecutor(), CallerIdentity.fromContext(context), PROPERTIES);
 
         mContext = context;
+        mGnssNative = gnssNative;
+        mGnssMetrics = gnssMetrics;
 
         // Create a wake lock
         PowerManager powerManager = Objects.requireNonNull(
                 mContext.getSystemService(PowerManager.class));
-        mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
+        mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*location*:" + TAG);
         mWakeLock.setReferenceCounted(true);
 
         // Create a separate wake lock for psds downloader as it may be released due to timeout.
         mDownloadPsdsWakeLock = powerManager.newWakeLock(
-                PowerManager.PARTIAL_WAKE_LOCK, DOWNLOAD_EXTRA_WAKELOCK_KEY);
+                PowerManager.PARTIAL_WAKE_LOCK, "*location*:PsdsDownload");
         mDownloadPsdsWakeLock.setReferenceCounted(true);
 
         mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
@@ -519,8 +405,7 @@
         // relative long time, so the ctor() is kept to create objects needed by this instance,
         // while IO initialization and registration is delegated to our internal handler
         // this approach is just fine because events are posted to our handler anyway
-        mGnssConfiguration = new GnssConfiguration(mContext);
-        mGnssCapabilitiesProvider = new GnssCapabilitiesProvider();
+        mGnssConfiguration = mGnssNative.getConfiguration();
         // Create a GPS net-initiated handler (also needed by handleInitialize)
         mNIHandler = new GpsNetInitiatedHandler(context,
                 mNetInitiatedListener,
@@ -530,22 +415,21 @@
         mNetworkConnectivityHandler = new GnssNetworkConnectivityHandler(context,
                 GnssLocationProvider.this::onNetworkAvailable, mHandler.getLooper(), mNIHandler);
 
-        mGnssStatusListenerHelper = new GnssStatusProvider(injector);
-        mGnssMeasurementsProvider = new GnssMeasurementsProvider(injector);
-        mGnssMeasurementCorrectionsProvider = new GnssMeasurementCorrectionsProvider(mHandler);
-        mGnssAntennaInfoProvider = new GnssAntennaInfoProvider(injector);
-        mGnssNavigationMessageProvider = new GnssNavigationMessageProvider(injector);
-        mGnssPowerIndicationProvider = new GnssPowerIndicationProvider();
-
-        mGnssMetrics = new GnssMetrics(mContext, mBatteryStats);
         mNtpTimeHelper = new NtpTimeHelper(mContext, mHandler.getLooper(), this);
         mGnssSatelliteBlocklistHelper =
                 new GnssSatelliteBlocklistHelper(mContext,
                         mHandler.getLooper(), this);
-        mGnssGeofenceProvider = new GnssGeofenceProvider();
 
-        setProperties(PROPERTIES);
         setAllowed(true);
+
+        mGnssNative.addBaseCallbacks(this);
+        mGnssNative.addLocationCallbacks(this);
+        mGnssNative.addSvStatusCallbacks(this);
+        mGnssNative.setAGpsCallbacks(this);
+        mGnssNative.setPsdsCallbacks(this);
+        mGnssNative.setNotificationCallbacks(this);
+        mGnssNative.setLocationRequestCallbacks(this);
+        mGnssNative.setTimeCallbacks(this);
     }
 
     /** Called when system is ready. */
@@ -575,12 +459,7 @@
     }
 
     private void handleInitialize() {
-        // it *appears* that native_init() needs to be called at least once before invoking any
-        // other gnss methods, so we cycle once on initialization.
-        native_init();
-        native_cleanup();
-
-        if (native_is_gnss_visibility_control_supported()) {
+        if (mGnssNative.isGnssVisibilityControlSupported()) {
             mGnssVisibilityControl = new GnssVisibilityControl(mContext, mHandler.getLooper(),
                     mNIHandler);
         }
@@ -635,7 +514,7 @@
      */
     @Override
     public void injectTime(long time, long timeReference, int uncertainty) {
-        native_inject_time(time, timeReference, uncertainty);
+        mGnssNative.injectTime(time, timeReference, uncertainty);
     }
 
     /**
@@ -647,7 +526,7 @@
         if (mSupportsPsds) {
             synchronized (mLock) {
                 for (int psdsType : mPendingDownloadPsdsTypes) {
-                    downloadPsdsData(psdsType);
+                    sendMessage(DOWNLOAD_PSDS_DATA, psdsType, null);
                 }
                 mPendingDownloadPsdsTypes.clear();
             }
@@ -724,38 +603,7 @@
             return;
         }
 
-        int gnssLocationFlags = LOCATION_HAS_LAT_LONG
-                | (location.hasAltitude() ? LOCATION_HAS_ALTITUDE : 0)
-                | (location.hasSpeed() ? LOCATION_HAS_SPEED : 0)
-                | (location.hasBearing() ? LOCATION_HAS_BEARING : 0)
-                | (location.hasAccuracy() ? LOCATION_HAS_HORIZONTAL_ACCURACY : 0)
-                | (location.hasVerticalAccuracy() ? LOCATION_HAS_VERTICAL_ACCURACY : 0)
-                | (location.hasSpeedAccuracy() ? LOCATION_HAS_SPEED_ACCURACY : 0)
-                | (location.hasBearingAccuracy() ? LOCATION_HAS_BEARING_ACCURACY : 0);
-
-        double latitudeDegrees = location.getLatitude();
-        double longitudeDegrees = location.getLongitude();
-        double altitudeMeters = location.getAltitude();
-        float speedMetersPerSec = location.getSpeed();
-        float bearingDegrees = location.getBearing();
-        float horizontalAccuracyMeters = location.getAccuracy();
-        float verticalAccuracyMeters = location.getVerticalAccuracyMeters();
-        float speedAccuracyMetersPerSecond = location.getSpeedAccuracyMetersPerSecond();
-        float bearingAccuracyDegrees = location.getBearingAccuracyDegrees();
-        long timestamp = location.getTime();
-
-        int elapsedRealtimeFlags = ELAPSED_REALTIME_HAS_TIMESTAMP_NS
-                | (location.hasElapsedRealtimeUncertaintyNanos()
-                ? ELAPSED_REALTIME_HAS_TIME_UNCERTAINTY_NS : 0);
-        long elapsedRealtimeNanos = location.getElapsedRealtimeNanos();
-        double elapsedRealtimeUncertaintyNanos = location.getElapsedRealtimeUncertaintyNanos();
-
-        native_inject_best_location(
-                gnssLocationFlags, latitudeDegrees, longitudeDegrees,
-                altitudeMeters, speedMetersPerSec, bearingDegrees,
-                horizontalAccuracyMeters, verticalAccuracyMeters,
-                speedAccuracyMetersPerSecond, bearingAccuracyDegrees, timestamp,
-                elapsedRealtimeFlags, elapsedRealtimeNanos, elapsedRealtimeUncertaintyNanos);
+        mGnssNative.injectBestLocation(location);
     }
 
     /** Returns true if the location request is too frequent. */
@@ -789,7 +637,7 @@
             if (data != null) {
                 mHandler.post(() -> {
                     if (DEBUG) Log.d(TAG, "calling native_inject_psds_data");
-                    native_inject_psds_data(data, data.length, psdsType);
+                    mGnssNative.injectPsdsData(data, data.length, psdsType);
                     synchronized (mLock) {
                         mPsdsBackOff.reset();
                     }
@@ -824,9 +672,8 @@
     }
 
     private void injectLocation(Location location) {
-        if (location.hasAccuracy() && !location.isFromMockProvider()) {
-            native_inject_location(location.getLatitude(), location.getLongitude(),
-                    location.getAccuracy());
+        if (!location.isFromMockProvider()) {
+            mGnssNative.injectLocation(location);
         }
     }
 
@@ -836,7 +683,7 @@
         if (mSuplServerHost != null
                 && mSuplServerPort > TCP_MIN_PORT
                 && mSuplServerPort <= TCP_MAX_PORT) {
-            native_set_agps_server(GnssNetworkConnectivityHandler.AGPS_TYPE_SUPL,
+            mGnssNative.setAgpsServer(GnssNetworkConnectivityHandler.AGPS_TYPE_SUPL,
                     mSuplServerHost, mSuplServerPort);
         }
     }
@@ -852,16 +699,16 @@
         if (agpsEnabled) {
             int suplMode = mGnssConfiguration.getSuplMode(0);
             if (suplMode == 0) {
-                return GPS_POSITION_MODE_STANDALONE;
+                return GNSS_POSITION_MODE_STANDALONE;
             }
 
             // MS-Based is the preferred mode for Assisted-GPS position computation, so we favor
             // such mode when it is available
-            if (hasCapability(GPS_CAPABILITY_MSB) && (suplMode & AGPS_SUPL_MODE_MSB) != 0) {
-                return GPS_POSITION_MODE_MS_BASED;
+            if (mGnssNative.getCapabilities().hasMsb() && (suplMode & AGPS_SUPL_MODE_MSB) != 0) {
+                return GNSS_POSITION_MODE_MS_BASED;
             }
         }
-        return GPS_POSITION_MODE_STANDALONE;
+        return GNSS_POSITION_MODE_STANDALONE;
     }
 
     private void setGpsEnabled(boolean enabled) {
@@ -873,23 +720,23 @@
     private void handleEnable() {
         if (DEBUG) Log.d(TAG, "handleEnable");
 
-        boolean inited = native_init();
+        boolean inited = mGnssNative.init();
 
         if (inited) {
             setGpsEnabled(true);
-            mSupportsPsds = native_supports_psds();
+            mSupportsPsds = mGnssNative.isPsdsSupported();
 
             // TODO: remove the following native calls if we can make sure they are redundant.
             if (mSuplServerHost != null) {
-                native_set_agps_server(GnssNetworkConnectivityHandler.AGPS_TYPE_SUPL,
+                mGnssNative.setAgpsServer(GnssNetworkConnectivityHandler.AGPS_TYPE_SUPL,
                         mSuplServerHost, mSuplServerPort);
             }
             if (mC2KServerHost != null) {
-                native_set_agps_server(GnssNetworkConnectivityHandler.AGPS_TYPE_C2K,
+                mGnssNative.setAgpsServer(GnssNetworkConnectivityHandler.AGPS_TYPE_C2K,
                         mC2KServerHost, mC2KServerPort);
             }
 
-            mBatchingEnabled = native_init_batching() && native_get_batch_size() > 1;
+            mBatchingEnabled = mGnssNative.initBatching() && mGnssNative.getBatchSize() > 1;
             if (mGnssVisibilityControl != null) {
                 mGnssVisibilityControl.onGpsEnabledChanged(/* isEnabled= */ true);
             }
@@ -911,8 +758,8 @@
             mGnssVisibilityControl.onGpsEnabledChanged(/* isEnabled= */ false);
         }
         // do this before releasing wakelock
-        native_cleanup_batching();
-        native_cleanup();
+        mGnssNative.cleanupBatching();
+        mGnssNative.cleanup();
     }
 
     private void updateEnabled() {
@@ -951,7 +798,7 @@
      * minimum size guaranteed to be available for batching operations.
      */
     public int getBatchSize() {
-        return native_get_batch_size();
+        return mGnssNative.getBatchSize();
     }
 
     @Override
@@ -965,7 +812,7 @@
         if (!added) {
             listener.run();
         } else {
-            native_flush_batch();
+            mGnssNative.flushBatch();
         }
     }
 
@@ -1009,9 +856,9 @@
             } else {
                 stopBatching();
 
-                if (mStarted && hasCapability(GPS_CAPABILITY_SCHEDULING)) {
+                if (mStarted && mGnssNative.getCapabilities().hasScheduling()) {
                     // change period and/or lowPowerMode
-                    if (!setPositionMode(mPositionMode, GPS_POSITION_RECURRENCE_PERIODIC,
+                    if (!setPositionMode(mPositionMode, GNSS_POSITION_RECURRENCE_PERIODIC,
                             mFixInterval, mProviderRequest.isLowPower())) {
                         Log.e(TAG, "set_position_mode failed in updateRequirements");
                     }
@@ -1045,8 +892,8 @@
             return true;
         }
 
-        boolean result = native_set_position_mode(mode, recurrence, minInterval,
-                0, 0, lowPowerMode);
+        boolean result = mGnssNative.setPositionMode(mode, recurrence, minInterval, 0, 0,
+                lowPowerMode);
         if (result) {
             mLastPositionMode = positionMode;
         } else {
@@ -1125,11 +972,11 @@
             requestUtcTime();
         } else if ("force_psds_injection".equals(command)) {
             if (mSupportsPsds) {
-                downloadPsdsData(/* psdsType= */
-                        GnssPsdsDownloader.LONG_TERM_PSDS_SERVER_INDEX);
+                sendMessage(DOWNLOAD_PSDS_DATA, GnssPsdsDownloader.LONG_TERM_PSDS_SERVER_INDEX,
+                        null);
             }
         } else if ("request_power_stats".equals(command)) {
-            GnssPowerIndicationProvider.requestPowerStats();
+            mGnssNative.requestPowerStats();
         } else {
             Log.w(TAG, "sendExtraCommand: unknown command " + command);
         }
@@ -1139,26 +986,26 @@
         int flags;
 
         if (extras == null) {
-            flags = GPS_DELETE_ALL;
+            flags = GNSS_AIDING_TYPE_ALL;
         } else {
             flags = 0;
-            if (extras.getBoolean("ephemeris")) flags |= GPS_DELETE_EPHEMERIS;
-            if (extras.getBoolean("almanac")) flags |= GPS_DELETE_ALMANAC;
-            if (extras.getBoolean("position")) flags |= GPS_DELETE_POSITION;
-            if (extras.getBoolean("time")) flags |= GPS_DELETE_TIME;
-            if (extras.getBoolean("iono")) flags |= GPS_DELETE_IONO;
-            if (extras.getBoolean("utc")) flags |= GPS_DELETE_UTC;
-            if (extras.getBoolean("health")) flags |= GPS_DELETE_HEALTH;
-            if (extras.getBoolean("svdir")) flags |= GPS_DELETE_SVDIR;
-            if (extras.getBoolean("svsteer")) flags |= GPS_DELETE_SVSTEER;
-            if (extras.getBoolean("sadata")) flags |= GPS_DELETE_SADATA;
-            if (extras.getBoolean("rti")) flags |= GPS_DELETE_RTI;
-            if (extras.getBoolean("celldb-info")) flags |= GPS_DELETE_CELLDB_INFO;
-            if (extras.getBoolean("all")) flags |= GPS_DELETE_ALL;
+            if (extras.getBoolean("ephemeris")) flags |= GNSS_AIDING_TYPE_EPHEMERIS;
+            if (extras.getBoolean("almanac")) flags |= GNSS_AIDING_TYPE_ALMANAC;
+            if (extras.getBoolean("position")) flags |= GNSS_AIDING_TYPE_POSITION;
+            if (extras.getBoolean("time")) flags |= GNSS_AIDING_TYPE_TIME;
+            if (extras.getBoolean("iono")) flags |= GNSS_AIDING_TYPE_IONO;
+            if (extras.getBoolean("utc")) flags |= GNSS_AIDING_TYPE_UTC;
+            if (extras.getBoolean("health")) flags |= GNSS_AIDING_TYPE_HEALTH;
+            if (extras.getBoolean("svdir")) flags |= GNSS_AIDING_TYPE_SVDIR;
+            if (extras.getBoolean("svsteer")) flags |= GNSS_AIDING_TYPE_SVSTEER;
+            if (extras.getBoolean("sadata")) flags |= GNSS_AIDING_TYPE_SADATA;
+            if (extras.getBoolean("rti")) flags |= GNSS_AIDING_TYPE_RTI;
+            if (extras.getBoolean("celldb-info")) flags |= GNSS_AIDING_TYPE_CELLDB_INFO;
+            if (extras.getBoolean("all")) flags |= GNSS_AIDING_TYPE_ALL;
         }
 
         if (flags != 0) {
-            native_delete_aiding_data(flags);
+            mGnssNative.deleteAidingData(flags);
         }
     }
 
@@ -1168,13 +1015,7 @@
             mTimeToFirstFix = 0;
             mLastFixTime = 0;
             setStarted(true);
-            mPositionMode = GPS_POSITION_MODE_STANDALONE;
-            // Notify about suppressed output, if speed limit was previously exceeded.
-            // Elsewhere, we check again with every speed output reported.
-            if (mItarSpeedLimitExceeded) {
-                Log.i(TAG, "startNavigating with ITAR limit in place. Output limited  "
-                        + "until slow enough speed reported.");
-            }
+            mPositionMode = GNSS_POSITION_MODE_STANDALONE;
 
             boolean agpsEnabled =
                     (Settings.Global.getInt(mContext.getContentResolver(),
@@ -1185,13 +1026,13 @@
                 String mode;
 
                 switch (mPositionMode) {
-                    case GPS_POSITION_MODE_STANDALONE:
+                    case GNSS_POSITION_MODE_STANDALONE:
                         mode = "standalone";
                         break;
-                    case GPS_POSITION_MODE_MS_ASSISTED:
+                    case GNSS_POSITION_MODE_MS_ASSISTED:
                         mode = "MS_ASSISTED";
                         break;
-                    case GPS_POSITION_MODE_MS_BASED:
+                    case GNSS_POSITION_MODE_MS_BASED:
                         mode = "MS_BASED";
                         break;
                     default:
@@ -1201,14 +1042,14 @@
                 Log.d(TAG, "setting position_mode to " + mode);
             }
 
-            int interval = (hasCapability(GPS_CAPABILITY_SCHEDULING) ? mFixInterval : 1000);
-            if (!setPositionMode(mPositionMode, GPS_POSITION_RECURRENCE_PERIODIC,
+            int interval = mGnssNative.getCapabilities().hasScheduling() ? mFixInterval : 1000;
+            if (!setPositionMode(mPositionMode, GNSS_POSITION_RECURRENCE_PERIODIC,
                     interval, mProviderRequest.isLowPower())) {
                 setStarted(false);
                 Log.e(TAG, "set_position_mode failed in startNavigating()");
                 return;
             }
-            if (!native_start()) {
+            if (!mGnssNative.start()) {
                 setStarted(false);
                 Log.e(TAG, "native_start failed in startNavigating()");
                 return;
@@ -1217,7 +1058,7 @@
             // reset SV count to zero
             mLocationExtras.reset();
             mFixRequestTime = SystemClock.elapsedRealtime();
-            if (!hasCapability(GPS_CAPABILITY_SCHEDULING)) {
+            if (!mGnssNative.getCapabilities().hasScheduling()) {
                 // set timer to give up if we do not receive a fix within NO_FIX_TIMEOUT
                 // and our fix interval is not short
                 if (mFixInterval >= NO_FIX_TIMEOUT) {
@@ -1233,7 +1074,7 @@
         if (DEBUG) Log.d(TAG, "stopNavigating");
         if (mStarted) {
             setStarted(false);
-            native_stop();
+            mGnssNative.stop();
             mLastFixTime = 0;
             // native_stop() may reset the position mode in hardware.
             mLastPositionMode = null;
@@ -1249,7 +1090,7 @@
         if (DEBUG) {
             Log.d(TAG, "startBatching " + mFixInterval);
         }
-        if (native_start_batch(MILLISECONDS.toNanos(mFixInterval), true)) {
+        if (mGnssNative.startBatch(MILLISECONDS.toNanos(mFixInterval), true)) {
             mBatchingStarted = true;
         } else {
             Log.e(TAG, "native_start_batch failed in startBatching()");
@@ -1259,7 +1100,7 @@
     private void stopBatching() {
         if (DEBUG) Log.d(TAG, "stopBatching");
         if (mBatchingStarted) {
-            native_stop_batch();
+            mGnssNative.stopBatch();
             mBatchingStarted = false;
         }
     }
@@ -1279,28 +1120,7 @@
                 mWakeupListener, mHandler);
     }
 
-    private boolean hasCapability(int capability) {
-        return (mTopHalCapabilities & capability) != 0;
-    }
-
-    void reportLocation(boolean hasLatLong, Location location) {
-        sendMessage(REPORT_LOCATION, hasLatLong ? 1 : 0, location);
-    }
-
     private void handleReportLocation(boolean hasLatLong, Location location) {
-        if (location.hasSpeed()) {
-            mItarSpeedLimitExceeded = location.getSpeed() > ITAR_SPEED_LIMIT_METERS_PER_SECOND;
-        }
-
-        if (mItarSpeedLimitExceeded) {
-            Log.i(TAG, "Hal reported a speed in excess of ITAR limit."
-                    + "  GPS/GNSS Navigation output blocked.");
-            if (mStarted) {
-                mGnssMetrics.logReceivedLocationStatus(false);
-            }
-            return;  // No output of location allowed
-        }
-
         if (VERBOSE) Log.v(TAG, "reportLocation " + location.toString());
 
         location.setExtras(mLocationExtras.getBundle());
@@ -1343,9 +1163,6 @@
             if (mStarted) {
                 mGnssMetrics.logTimeToFirstFixMilliSecs(mTimeToFirstFix);
             }
-
-            // notify status listeners
-            mGnssStatusListenerHelper.onFirstFix(mTimeToFirstFix);
         }
 
         if (mStarted) {
@@ -1353,51 +1170,19 @@
             // spend too much power searching for a location, when the requested update rate is
             // slow.
             // As we just recievied a location, we'll cancel that timer.
-            if (!hasCapability(GPS_CAPABILITY_SCHEDULING) && mFixInterval < NO_FIX_TIMEOUT) {
+            if (!mGnssNative.getCapabilities().hasScheduling() && mFixInterval < NO_FIX_TIMEOUT) {
                 mAlarmManager.cancel(mTimeoutListener);
             }
         }
 
-        if (!hasCapability(GPS_CAPABILITY_SCHEDULING) && mStarted
+        if (!mGnssNative.getCapabilities().hasScheduling() && mStarted
                 && mFixInterval > GPS_POLLING_THRESHOLD_INTERVAL) {
             if (DEBUG) Log.d(TAG, "got fix, hibernating");
             hibernate();
         }
     }
 
-    void reportStatus(int status) {
-        if (DEBUG) Log.v(TAG, "reportStatus status: " + status);
-
-        boolean wasNavigating = mNavigating;
-        switch (status) {
-            case GPS_STATUS_SESSION_BEGIN:
-                mNavigating = true;
-                break;
-            case GPS_STATUS_ENGINE_ON:
-                break;
-            case GPS_STATUS_SESSION_END:
-                // fall through
-            case GPS_STATUS_ENGINE_OFF:
-                mNavigating = false;
-                break;
-        }
-
-        if (wasNavigating != mNavigating) {
-            mGnssStatusListenerHelper.onStatusChanged(mNavigating);
-        }
-    }
-
-    void reportSvStatus(int svCount, int[] svidWithFlags, float[] cn0DbHzs,
-            float[] elevations, float[] azimuths, float[] carrierFrequencies,
-            float[] basebandCn0DbHzs) {
-        sendMessage(REPORT_SV_STATUS, 0,
-                GnssStatus.wrap(svCount, svidWithFlags, cn0DbHzs, elevations, azimuths,
-                        carrierFrequencies, basebandCn0DbHzs));
-    }
-
     private void handleReportSvStatus(GnssStatus gnssStatus) {
-        mGnssStatusListenerHelper.onSvStatusChanged(gnssStatus);
-
         // Log CN0 as part of GNSS metrics
         mGnssMetrics.logCn0(gnssStatus);
 
@@ -1427,289 +1212,12 @@
         mGnssMetrics.logSvStatus(gnssStatus);
     }
 
-    void reportAGpsStatus(int agpsType, int agpsStatus, byte[] suplIpAddr) {
-        mNetworkConnectivityHandler.onReportAGpsStatus(agpsType, agpsStatus, suplIpAddr);
-    }
-
-    void reportNmea(long timestamp) {
-        if (!mItarSpeedLimitExceeded) {
-            int length = native_read_nmea(mNmeaBuffer, mNmeaBuffer.length);
-            String nmea = new String(mNmeaBuffer, 0 /* offset */, length);
-            mGnssStatusListenerHelper.onNmeaReceived(timestamp, nmea);
-        }
-    }
-
-    void reportMeasurementData(GnssMeasurementsEvent event) {
-        if (!mItarSpeedLimitExceeded) {
-            // send to handler to allow native to return quickly
-            mHandler.post(() -> mGnssMeasurementsProvider.onMeasurementsAvailable(event));
-        }
-    }
-
-    void reportAntennaInfo(List<GnssAntennaInfo> antennaInfos) {
-        mHandler.post(() -> mGnssAntennaInfoProvider.onGnssAntennaInfoAvailable(antennaInfos));
-    }
-
-    void reportNavigationMessage(GnssNavigationMessage event) {
-        if (!mItarSpeedLimitExceeded) {
-            // send to handler to allow native to return quickly
-            mHandler.post(() -> mGnssNavigationMessageProvider.onNavigationMessageAvailable(event));
-        }
-    }
-
-    void reportGnssPowerStats(GnssPowerStats powerStats) {
-        mHandler.post(() -> mGnssPowerIndicationProvider.onGnssPowerStatsAvailable(powerStats));
-    }
-
-    void setTopHalCapabilities(int topHalCapabilities) {
-        mHandler.post(() -> {
-            mTopHalCapabilities = topHalCapabilities;
-
-            if (hasCapability(GPS_CAPABILITY_ON_DEMAND_TIME)) {
-                mNtpTimeHelper.enablePeriodicTimeInjection();
-                requestUtcTime();
-            }
-
-            restartRequests();
-
-            mGnssCapabilitiesProvider.setTopHalCapabilities(mTopHalCapabilities);
-        });
-    }
-
-    void setSubHalMeasurementCorrectionsCapabilities(int subHalCapabilities) {
-        mHandler.post(() -> {
-            if (!mGnssMeasurementCorrectionsProvider.onCapabilitiesUpdated(subHalCapabilities)) {
-                return;
-            }
-
-            mGnssCapabilitiesProvider.setSubHalMeasurementCorrectionsCapabilities(
-                    subHalCapabilities);
-        });
-    }
-
-    /**
-     * Sets the capabilities bits for IGnssPowerIndication HAL.
-     *
-     * These capabilities are defined in IGnssPowerIndicationCallback.aidl.
-     */
-    void setSubHalPowerIndicationCapabilities(int subHalCapabilities) {
-        mHandler.post(() -> mGnssPowerIndicationProvider.onCapabilitiesUpdated(subHalCapabilities));
-    }
-
-    private void restartRequests() {
-        Log.i(TAG, "restartRequests");
-
-        restartLocationRequest();
-        mGnssGeofenceProvider.resumeIfStarted();
-    }
-
     private void restartLocationRequest() {
         if (DEBUG) Log.d(TAG, "restartLocationRequest");
         setStarted(false);
         updateRequirements();
     }
 
-    void setGnssYearOfHardware(final int yearOfHardware) {
-        // mHardwareYear is simply set here, to be read elsewhere, and is volatile for safe sync
-        if (DEBUG) Log.d(TAG, "setGnssYearOfHardware called with " + yearOfHardware);
-        mHardwareYear = yearOfHardware;
-    }
-
-    void setGnssHardwareModelName(final String modelName) {
-        // mHardwareModelName is simply set here, to be read elsewhere, and volatile for safe sync
-        if (DEBUG) Log.d(TAG, "setGnssModelName called with " + modelName);
-        mHardwareModelName = modelName;
-    }
-
-    void reportGnssServiceRestarted() {
-        if (DEBUG) Log.d(TAG, "reportGnssServiceDied");
-
-        // it *appears* that native_init() needs to be called at least once before invoking any
-        // other gnss methods, so we cycle once on initialization.
-        native_init();
-        native_cleanup();
-
-        // resend configuration into the restarted HAL service.
-        reloadGpsProperties();
-        if (isGpsEnabled()) {
-            setGpsEnabled(false);
-            updateEnabled();
-        }
-    }
-
-    /**
-     * Interface for GnssSystemInfo methods.
-     */
-    public interface GnssSystemInfoProvider {
-        /**
-         * Returns the year of underlying GPS hardware.
-         */
-        int getGnssYearOfHardware();
-
-        /**
-         * Returns the model name of underlying GPS hardware.
-         */
-        String getGnssHardwareModelName();
-    }
-
-    /**
-     * @hide
-     */
-    public GnssSystemInfoProvider getGnssSystemInfoProvider() {
-        return new GnssSystemInfoProvider() {
-            @Override
-            public int getGnssYearOfHardware() {
-                return mHardwareYear;
-            }
-
-            @Override
-            public String getGnssHardwareModelName() {
-                return mHardwareModelName;
-            }
-        };
-    }
-
-    /**
-     * Interface for GnssMetrics methods.
-     */
-    public interface GnssMetricsProvider {
-        /**
-         * Returns GNSS metrics as proto string
-         */
-        String getGnssMetricsAsProtoString();
-    }
-
-    /**
-     * @hide
-     */
-    public GnssMetricsProvider getGnssMetricsProvider() {
-        return mGnssMetrics::dumpGnssMetricsAsProtoString;
-    }
-
-    /**
-     * @hide
-     */
-    public GnssCapabilitiesProvider getGnssCapabilitiesProvider() {
-        return mGnssCapabilitiesProvider;
-    }
-
-    void reportLocationBatch(Location[] locations) {
-        if (DEBUG) {
-            Log.d(TAG, "Location batch of size " + locations.length + " reported");
-        }
-
-        Runnable[] listeners;
-        synchronized (mLock) {
-            listeners = mFlushListeners.toArray(new Runnable[0]);
-            mFlushListeners.clear();
-        }
-
-        if (locations.length > 0) {
-            reportLocation(LocationResult.create(Arrays.asList(locations)).validate());
-        }
-
-        for (Runnable listener : listeners) {
-            listener.run();
-        }
-    }
-
-    void downloadPsdsData(int psdsType) {
-        if (DEBUG) Log.d(TAG, "downloadPsdsData. psdsType: " + psdsType);
-        sendMessage(DOWNLOAD_PSDS_DATA, psdsType, null);
-    }
-
-    /**
-     * Converts the GPS HAL status to the internal Geofence Hardware status.
-     */
-    private static int getGeofenceStatus(int status) {
-        switch (status) {
-            case GPS_GEOFENCE_OPERATION_SUCCESS:
-                return GeofenceHardware.GEOFENCE_SUCCESS;
-            case GPS_GEOFENCE_ERROR_GENERIC:
-                return GeofenceHardware.GEOFENCE_FAILURE;
-            case GPS_GEOFENCE_ERROR_ID_EXISTS:
-                return GeofenceHardware.GEOFENCE_ERROR_ID_EXISTS;
-            case GPS_GEOFENCE_ERROR_INVALID_TRANSITION:
-                return GeofenceHardware.GEOFENCE_ERROR_INVALID_TRANSITION;
-            case GPS_GEOFENCE_ERROR_TOO_MANY_GEOFENCES:
-                return GeofenceHardware.GEOFENCE_ERROR_TOO_MANY_GEOFENCES;
-            case GPS_GEOFENCE_ERROR_ID_UNKNOWN:
-                return GeofenceHardware.GEOFENCE_ERROR_ID_UNKNOWN;
-            default:
-                return -1;
-        }
-    }
-
-    void reportGeofenceTransition(int geofenceId, Location location, int transition,
-            long transitionTimestamp) {
-        mHandler.post(() -> {
-            if (mGeofenceHardwareImpl == null) {
-                mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
-            }
-
-            mGeofenceHardwareImpl.reportGeofenceTransition(
-                    geofenceId,
-                    location,
-                    transition,
-                    transitionTimestamp,
-                    GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE,
-                    FusedBatchOptions.SourceTechnologies.GNSS);
-        });
-    }
-
-    void reportGeofenceStatus(int status, Location location) {
-        mHandler.post(() -> {
-            if (mGeofenceHardwareImpl == null) {
-                mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
-            }
-            int monitorStatus = GeofenceHardware.MONITOR_CURRENTLY_UNAVAILABLE;
-            if (status == GPS_GEOFENCE_AVAILABLE) {
-                monitorStatus = GeofenceHardware.MONITOR_CURRENTLY_AVAILABLE;
-            }
-            mGeofenceHardwareImpl.reportGeofenceMonitorStatus(
-                    GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE,
-                    monitorStatus,
-                    location,
-                    FusedBatchOptions.SourceTechnologies.GNSS);
-        });
-    }
-
-    void reportGeofenceAddStatus(int geofenceId, int status) {
-        mHandler.post(() -> {
-            if (mGeofenceHardwareImpl == null) {
-                mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
-            }
-            mGeofenceHardwareImpl.reportGeofenceAddStatus(geofenceId, getGeofenceStatus(status));
-        });
-    }
-
-    void reportGeofenceRemoveStatus(int geofenceId, int status) {
-        mHandler.post(() -> {
-            if (mGeofenceHardwareImpl == null) {
-                mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
-            }
-            mGeofenceHardwareImpl.reportGeofenceRemoveStatus(geofenceId, getGeofenceStatus(status));
-        });
-    }
-
-    void reportGeofencePauseStatus(int geofenceId, int status) {
-        mHandler.post(() -> {
-            if (mGeofenceHardwareImpl == null) {
-                mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
-            }
-            mGeofenceHardwareImpl.reportGeofencePauseStatus(geofenceId, getGeofenceStatus(status));
-        });
-    }
-
-    void reportGeofenceResumeStatus(int geofenceId, int status) {
-        mHandler.post(() -> {
-            if (mGeofenceHardwareImpl == null) {
-                mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
-            }
-            mGeofenceHardwareImpl.reportGeofenceResumeStatus(geofenceId, getGeofenceStatus(status));
-        });
-    }
-
     //=============================================================
     // NI Client support
     //=============================================================
@@ -1723,7 +1231,7 @@
                 Log.d(TAG, "sendNiResponse, notifId: " + notificationId
                         + ", response: " + userResponse);
             }
-            native_send_ni_response(notificationId, userResponse);
+            mGnssNative.sendNiResponse(notificationId, userResponse);
 
             FrameworkStatsLog.write(FrameworkStatsLog.GNSS_NI_EVENT_REPORTED,
                     FrameworkStatsLog.GNSS_NI_EVENT_REPORTED__EVENT_TYPE__NI_RESPONSE,
@@ -1751,7 +1259,7 @@
     }
 
     /** Reports a NI notification. */
-    void reportNiNotification(int notificationId, int niType, int notifyFlags, int timeout,
+    private void reportNiNotification(int notificationId, int niType, int notifyFlags, int timeout,
             int defaultResponse, String requestorId, String text, int requestorIdEncoding,
             int textEncoding) {
         Log.i(TAG, "reportNiNotification: entered");
@@ -1800,52 +1308,12 @@
                 /* userResponse= */ 0);
     }
 
-    /**
-     * We should be careful about receiving null string from the TelephonyManager,
-     * because sending null String to JNI function would cause a crash.
-     */
-    void requestSetID(int flags) {
-        TelephonyManager phone = (TelephonyManager)
-                mContext.getSystemService(Context.TELEPHONY_SERVICE);
-        int type = AGPS_SETID_TYPE_NONE;
-        String setId = null;
-
-        int ddSubId = SubscriptionManager.getDefaultDataSubscriptionId();
-        if (SubscriptionManager.isValidSubscriptionId(ddSubId)) {
-            phone = phone.createForSubscriptionId(ddSubId);
-        }
-        if ((flags & AGPS_RIL_REQUEST_SETID_IMSI) == AGPS_RIL_REQUEST_SETID_IMSI) {
-            setId = phone.getSubscriberId();
-            if (setId != null) {
-                // This means the framework has the SIM card.
-                type = AGPS_SETID_TYPE_IMSI;
-            }
-        } else if ((flags & AGPS_RIL_REQUEST_SETID_MSISDN) == AGPS_RIL_REQUEST_SETID_MSISDN) {
-            setId = phone.getLine1Number();
-            if (setId != null) {
-                // This means the framework has the SIM card.
-                type = AGPS_SETID_TYPE_MSISDN;
-            }
-        }
-
-        native_agps_set_id(type, (setId == null) ? "" : setId);
-    }
-
-    void requestLocation(boolean independentFromGnss, boolean isUserEmergency) {
-        if (DEBUG) {
-            Log.d(TAG, "requestLocation. independentFromGnss: " + independentFromGnss
-                    + ", isUserEmergency: "
-                    + isUserEmergency);
-        }
-        sendMessage(REQUEST_LOCATION, independentFromGnss ? 1 : 0, isUserEmergency);
-    }
-
-    void requestUtcTime() {
+    private void requestUtcTime() {
         if (DEBUG) Log.d(TAG, "utcTimeRequest");
         sendMessage(INJECT_NTP_TIME, 0, null);
     }
 
-    void requestRefLocation() {
+    private void requestRefLocation() {
         TelephonyManager phone = (TelephonyManager)
                 mContext.getSystemService(Context.TELEPHONY_SERVICE);
         final int phoneType = phone.getPhoneType();
@@ -1866,8 +1334,8 @@
                 } else {
                     type = AGPS_REF_LOCATION_TYPE_GSM_CELLID;
                 }
-                native_agps_set_ref_location_cellid(type, mcc, mnc,
-                        gsm_cell.getLac(), gsm_cell.getCid());
+                mGnssNative.setAgpsReferenceLocationCellId(type, mcc, mnc, gsm_cell.getLac(),
+                        gsm_cell.getCid());
             } else {
                 Log.e(TAG, "Error getting cell location info.");
             }
@@ -1876,21 +1344,6 @@
         }
     }
 
-    // Implements method nfwNotifyCb() in IGnssVisibilityControlCallback.hal.
-    void reportNfwNotification(String proxyAppPackageName, byte protocolStack,
-            String otherProtocolStackName, byte requestor, String requestorId, byte responseType,
-            boolean inEmergencyMode, boolean isCachedLocation) {
-        if (mGnssVisibilityControl == null) {
-            Log.e(TAG, "reportNfwNotification: mGnssVisibilityControl is not initialized.");
-            return;
-        }
-
-        mGnssVisibilityControl.reportNfwNotification(proxyAppPackageName, protocolStack,
-                otherProtocolStackName, requestor, requestorId, responseType, inEmergencyMode,
-                isCachedLocation);
-    }
-
-    // Implements method isInEmergencySession() in IGnssVisibilityControlCallback.hal.
     boolean isInEmergencySession() {
         return mNIHandler.getInEmergency();
     }
@@ -1988,97 +1441,143 @@
         pw.println("mBatchingStarted=" + mBatchingStarted);
         pw.println("mBatchSize=" + getBatchSize());
         pw.println("mFixInterval=" + mFixInterval);
-        mGnssPowerIndicationProvider.dump(fd, pw, args);
-        pw.print("mTopHalCapabilities=0x" + Integer.toHexString(mTopHalCapabilities) + " ( ");
-        if (hasCapability(GPS_CAPABILITY_SCHEDULING)) pw.print("SCHEDULING ");
-        if (hasCapability(GPS_CAPABILITY_MSB)) pw.print("MSB ");
-        if (hasCapability(GPS_CAPABILITY_MSA)) pw.print("MSA ");
-        if (hasCapability(GPS_CAPABILITY_SINGLE_SHOT)) pw.print("SINGLE_SHOT ");
-        if (hasCapability(GPS_CAPABILITY_ON_DEMAND_TIME)) pw.print("ON_DEMAND_TIME ");
-        if (hasCapability(GPS_CAPABILITY_GEOFENCING)) pw.print("GEOFENCING ");
-        if (hasCapability(GPS_CAPABILITY_MEASUREMENTS)) pw.print("MEASUREMENTS ");
-        if (hasCapability(GPS_CAPABILITY_NAV_MESSAGES)) pw.print("NAV_MESSAGES ");
-        if (hasCapability(GPS_CAPABILITY_LOW_POWER_MODE)) pw.print("LOW_POWER_MODE ");
-        if (hasCapability(GPS_CAPABILITY_SATELLITE_BLOCKLIST)) pw.print("SATELLITE_BLOCKLIST ");
-        if (hasCapability(GPS_CAPABILITY_MEASUREMENT_CORRECTIONS)) {
-            pw.print("MEASUREMENT_CORRECTIONS ");
-        }
-        if (hasCapability(GPS_CAPABILITY_ANTENNA_INFO)) pw.print("ANTENNA_INFO ");
-        pw.println(")");
-        if (hasCapability(GPS_CAPABILITY_MEASUREMENT_CORRECTIONS)) {
-            pw.println("SubHal=MEASUREMENT_CORRECTIONS["
-                    + mGnssMeasurementCorrectionsProvider.toStringCapabilities() + "]");
-        }
         pw.print(mGnssMetrics.dumpGnssMetricsAsText());
         if (dumpAll) {
             pw.println("native internal state: ");
-            pw.println("  " + native_get_internal_state());
+            pw.println("  " + mGnssNative.getInternalState());
         }
     }
 
-    // preallocated to avoid memory allocation in reportNmea()
-    private final byte[] mNmeaBuffer = new byte[120];
+    @Override
+    public void onHalRestarted() {
+        reloadGpsProperties();
+        if (isGpsEnabled()) {
+            setGpsEnabled(false);
+            updateEnabled();
+        }
+    }
 
-    private static native boolean native_is_gnss_visibility_control_supported();
+    @Override
+    public void onCapabilitiesChanged(GnssCapabilities oldCapabilities,
+            GnssCapabilities newCapabilities) {
+        mHandler.post(() -> {
+            if (mGnssNative.getCapabilities().hasOnDemandTime()) {
+                mNtpTimeHelper.enablePeriodicTimeInjection();
+                requestUtcTime();
+            }
 
-    private native boolean native_init();
+            restartLocationRequest();
+        });
+    }
 
-    private native void native_cleanup();
+    @Override
+    public void onReportLocation(boolean hasLatLong, Location location) {
+        sendMessage(REPORT_LOCATION, hasLatLong ? 1 : 0, location);
+    }
 
-    private native boolean native_set_position_mode(int mode, int recurrence, int minInterval,
-            int preferredAccuracy, int preferredTime, boolean lowPowerMode);
+    @Override
+    public void onReportLocations(Location[] locations) {
+        if (DEBUG) {
+            Log.d(TAG, "Location batch of size " + locations.length + " reported");
+        }
 
-    private native boolean native_start();
+        Runnable[] listeners;
+        synchronized (mLock) {
+            listeners = mFlushListeners.toArray(new Runnable[0]);
+            mFlushListeners.clear();
+        }
 
-    private native boolean native_stop();
+        if (locations.length > 0) {
+            reportLocation(LocationResult.create(Arrays.asList(locations)).validate());
+        }
 
-    private native void native_delete_aiding_data(int flags);
+        for (Runnable listener : listeners) {
+            listener.run();
+        }
+    }
 
-    private native int native_read_nmea(byte[] buffer, int bufferSize);
+    @Override
+    public void onReportSvStatus(GnssStatus gnssStatus) {
+        sendMessage(REPORT_SV_STATUS, 0, gnssStatus);
+    }
 
-    private native void native_inject_best_location(
-            int gnssLocationFlags, double latitudeDegrees, double longitudeDegrees,
-            double altitudeMeters, float speedMetersPerSec, float bearingDegrees,
-            float horizontalAccuracyMeters, float verticalAccuracyMeters,
-            float speedAccuracyMetersPerSecond, float bearingAccuracyDegrees,
-            long timestamp, int elapsedRealtimeFlags, long elapsedRealtimeNanos,
-            double elapsedRealtimeUncertaintyNanos);
+    @Override
+    public void onReportAGpsStatus(int agpsType, int agpsStatus, byte[] suplIpAddr) {
+        mNetworkConnectivityHandler.onReportAGpsStatus(agpsType, agpsStatus, suplIpAddr);
+    }
 
-    private native void native_inject_location(double latitude, double longitude, float accuracy);
+    @Override
+    public void onRequestPsdsDownload(int psdsType) {
+        sendMessage(DOWNLOAD_PSDS_DATA, psdsType, null);
+    }
 
-    // PSDS Support
-    private native void native_inject_time(long time, long timeReference, int uncertainty);
+    @Override
+    public void onReportNiNotification(int notificationId, int niType, int notifyFlags,
+            int timeout, int defaultResponse, String requestorId, String text,
+            int requestorIdEncoding, int textEncoding) {
+        reportNiNotification(notificationId, niType, notifyFlags, timeout,
+                defaultResponse, requestorId, text, requestorIdEncoding, textEncoding);
+    }
 
-    private native boolean native_supports_psds();
+    @Override
+    public void onRequestSetID(@GnssNative.AGpsCallbacks.AgpsSetIdFlags int flags) {
+        TelephonyManager phone = (TelephonyManager)
+                mContext.getSystemService(Context.TELEPHONY_SERVICE);
+        int type = AGPS_SETID_TYPE_NONE;
+        String setId = null;
 
-    private native void native_inject_psds_data(byte[] data, int length, int psdsType);
+        int ddSubId = SubscriptionManager.getDefaultDataSubscriptionId();
+        if (SubscriptionManager.isValidSubscriptionId(ddSubId)) {
+            phone = phone.createForSubscriptionId(ddSubId);
+        }
+        if ((flags & AGPS_REQUEST_SETID_IMSI) == AGPS_REQUEST_SETID_IMSI) {
+            setId = phone.getSubscriberId();
+            if (setId != null) {
+                // This means the framework has the SIM card.
+                type = AGPS_SETID_TYPE_IMSI;
+            }
+        } else if ((flags & AGPS_REQUEST_SETID_MSISDN) == AGPS_REQUEST_SETID_MSISDN) {
+            setId = phone.getLine1Number();
+            if (setId != null) {
+                // This means the framework has the SIM card.
+                type = AGPS_SETID_TYPE_MSISDN;
+            }
+        }
 
-    // DEBUG Support
-    private native String native_get_internal_state();
+        mGnssNative.setAgpsSetId(type, (setId == null) ? "" : setId);
+    }
 
-    // AGPS Support
-    private native void native_agps_ni_message(byte[] msg, int length);
+    @Override
+    public void onRequestLocation(boolean independentFromGnss, boolean isUserEmergency) {
+        if (DEBUG) {
+            Log.d(TAG, "requestLocation. independentFromGnss: " + independentFromGnss
+                    + ", isUserEmergency: "
+                    + isUserEmergency);
+        }
+        sendMessage(REQUEST_LOCATION, independentFromGnss ? 1 : 0, isUserEmergency);
+    }
 
-    private native void native_set_agps_server(int type, String hostname, int port);
+    @Override
+    public void onRequestUtcTime() {
+        requestUtcTime();
+    }
 
-    // Network-initiated (NI) Support
-    private native void native_send_ni_response(int notificationId, int userResponse);
+    @Override
+    public void onRequestRefLocation() {
+        requestRefLocation();
+    }
 
-    // AGPS ril support
-    private native void native_agps_set_ref_location_cellid(int type, int mcc, int mnc,
-            int lac, int cid);
+    @Override
+    public void onReportNfwNotification(String proxyAppPackageName, byte protocolStack,
+            String otherProtocolStackName, byte requestor, String requestorId,
+            byte responseType, boolean inEmergencyMode, boolean isCachedLocation) {
+        if (mGnssVisibilityControl == null) {
+            Log.e(TAG, "reportNfwNotification: mGnssVisibilityControl uninitialized.");
+            return;
+        }
 
-    private native void native_agps_set_id(int type, String setid);
-
-    private static native boolean native_init_batching();
-
-    private static native void native_cleanup_batching();
-
-    private static native int native_get_batch_size();
-
-    private static native boolean native_start_batch(long periodNanos, boolean wakeOnFifoFull);
-
-    private static native void native_flush_batch();
-
-    private static native boolean native_stop_batch();
+        mGnssVisibilityControl.reportNfwNotification(proxyAppPackageName, protocolStack,
+                otherProtocolStackName, requestor, requestorId, responseType, inEmergencyMode,
+                isCachedLocation);
+    }
 }
diff --git a/services/core/java/com/android/server/location/gnss/GnssManagerService.java b/services/core/java/com/android/server/location/gnss/GnssManagerService.java
index fa137aa..92957aa 100644
--- a/services/core/java/com/android/server/location/gnss/GnssManagerService.java
+++ b/services/core/java/com/android/server/location/gnss/GnssManagerService.java
@@ -19,99 +19,88 @@
 import android.Manifest;
 import android.annotation.Nullable;
 import android.content.Context;
+import android.content.Intent;
+import android.hardware.location.GeofenceHardware;
+import android.hardware.location.GeofenceHardwareImpl;
+import android.location.FusedBatchOptions;
 import android.location.GnssAntennaInfo;
+import android.location.GnssCapabilities;
 import android.location.GnssMeasurementCorrections;
 import android.location.GnssMeasurementRequest;
-import android.location.GnssMeasurementsEvent;
-import android.location.GnssNavigationMessage;
-import android.location.IGnssAntennaInfoListener;
 import android.location.IGnssMeasurementsListener;
 import android.location.IGnssNavigationMessageListener;
+import android.location.IGnssNmeaListener;
 import android.location.IGnssStatusListener;
 import android.location.IGpsGeofenceHardware;
-import android.location.INetInitiatedListener;
 import android.location.Location;
-import android.location.LocationManagerInternal;
+import android.location.LocationManager;
 import android.location.util.identity.CallerIdentity;
+import android.os.BatteryStats;
+import android.os.Binder;
 import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.UserHandle;
 import android.util.IndentingPrintWriter;
 import android.util.Log;
 
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.Preconditions;
-import com.android.server.LocalServices;
-import com.android.server.location.injector.AppOpsHelper;
+import com.android.internal.app.IBatteryStats;
+import com.android.server.FgThread;
+import com.android.server.location.gnss.hal.GnssNative;
 import com.android.server.location.injector.Injector;
 
 import java.io.FileDescriptor;
+import java.util.ArrayList;
 import java.util.List;
 
 /** Manages Gnss providers and related Gnss functions for LocationManagerService. */
-public class GnssManagerService implements GnssNative.Callbacks {
+public class GnssManagerService {
 
     public static final String TAG = "GnssManager";
     public static final boolean D = Log.isLoggable(TAG, Log.DEBUG);
 
     private static final String ATTRIBUTION_ID = "GnssService";
 
-    public static boolean isGnssSupported() {
-        return GnssNative.isSupported();
-    }
-
     private final Context mContext;
-    private final AppOpsHelper mAppOpsHelper;
-    private final LocationManagerInternal mLocationManagerInternal;
+    private final GnssNative mGnssNative;
 
     private final GnssLocationProvider mGnssLocationProvider;
     private final GnssStatusProvider mGnssStatusProvider;
+    private final GnssNmeaProvider mGnssNmeaProvider;
     private final GnssMeasurementsProvider mGnssMeasurementsProvider;
-    private final GnssMeasurementCorrectionsProvider mGnssMeasurementCorrectionsProvider;
-    private final GnssAntennaInfoProvider mGnssAntennaInfoProvider;
     private final GnssNavigationMessageProvider mGnssNavigationMessageProvider;
-    private final GnssLocationProvider.GnssSystemInfoProvider mGnssSystemInfoProvider;
-    private final GnssLocationProvider.GnssMetricsProvider mGnssMetricsProvider;
-    private final GnssCapabilitiesProvider mGnssCapabilitiesProvider;
-    private final INetInitiatedListener mNetInitiatedListener;
-    private final IGpsGeofenceHardware mGpsGeofenceProxy;
+    private final IGpsGeofenceHardware mGnssGeofenceProxy;
 
-    public GnssManagerService(Context context, Injector injector) {
-        this(context, injector, null);
-    }
+    private final GnssGeofenceHalModule mGeofenceHalModule;
+    private final GnssCapabilitiesHalModule mCapabilitiesHalModule;
+    private final GnssAntennaInfoHalModule mAntennaInfoHalModule;
 
-    @VisibleForTesting
-    GnssManagerService(Context context, Injector injector,
-            GnssLocationProvider gnssLocationProvider) {
-        Preconditions.checkState(isGnssSupported());
+    private final GnssMetrics mGnssMetrics;
 
-        GnssNative.initialize();
-
+    public GnssManagerService(Context context, Injector injector, GnssNative gnssNative) {
         mContext = context.createAttributionContext(ATTRIBUTION_ID);
-        mAppOpsHelper = injector.getAppOpsHelper();
-        mLocationManagerInternal = LocalServices.getService(LocationManagerInternal.class);
+        mGnssNative = gnssNative;
 
-        if (gnssLocationProvider == null) {
-            gnssLocationProvider = new GnssLocationProvider(mContext, injector);
-        }
+        mGnssMetrics = new GnssMetrics(mContext, IBatteryStats.Stub.asInterface(
+                ServiceManager.getService(BatteryStats.SERVICE_NAME)));
 
-        mGnssLocationProvider = gnssLocationProvider;
-        mGnssStatusProvider = mGnssLocationProvider.getGnssStatusProvider();
-        mGnssMeasurementsProvider = mGnssLocationProvider.getGnssMeasurementsProvider();
-        mGnssAntennaInfoProvider = mGnssLocationProvider.getGnssAntennaInfoProvider();
-        mGnssMeasurementCorrectionsProvider =
-                mGnssLocationProvider.getGnssMeasurementCorrectionsProvider();
-        mGnssNavigationMessageProvider = mGnssLocationProvider.getGnssNavigationMessageProvider();
-        mGnssSystemInfoProvider = mGnssLocationProvider.getGnssSystemInfoProvider();
-        mGnssMetricsProvider = mGnssLocationProvider.getGnssMetricsProvider();
-        mGnssCapabilitiesProvider = mGnssLocationProvider.getGnssCapabilitiesProvider();
-        mNetInitiatedListener = mGnssLocationProvider.getNetInitiatedListener();
-        mGpsGeofenceProxy = mGnssLocationProvider.getGpsGeofenceProxy();
+        mGnssLocationProvider = new GnssLocationProvider(mContext, injector, mGnssNative,
+                mGnssMetrics);
+        mGnssStatusProvider = new GnssStatusProvider(injector, mGnssNative);
+        mGnssNmeaProvider = new GnssNmeaProvider(injector, mGnssNative);
+        mGnssMeasurementsProvider = new GnssMeasurementsProvider(injector, mGnssNative);
+        mGnssNavigationMessageProvider = new GnssNavigationMessageProvider(injector, mGnssNative);
+        mGnssGeofenceProxy = new GnssGeofenceProxy(mGnssNative);
+
+        mGeofenceHalModule = new GnssGeofenceHalModule(mGnssNative);
+        mCapabilitiesHalModule = new GnssCapabilitiesHalModule(mGnssNative);
+        mAntennaInfoHalModule = new GnssAntennaInfoHalModule(mGnssNative);
 
         // allow gnss access to begin - we must assume that callbacks can start immediately
-        GnssNative.register(this);
+        mGnssNative.register();
     }
 
     /** Called when system is ready. */
-    public synchronized void onSystemReady() {
+    public void onSystemReady() {
         mGnssLocationProvider.onSystemReady();
     }
 
@@ -121,15 +110,15 @@
     }
 
     /** Retrieve the IGpsGeofenceHardware. */
-    public IGpsGeofenceHardware getGpsGeofenceProxy() {
-        return mGpsGeofenceProxy;
+    public IGpsGeofenceHardware getGnssGeofenceProxy() {
+        return mGnssGeofenceProxy;
     }
 
     /**
      * Get year of GNSS hardware.
      */
     public int getGnssYearOfHardware() {
-        return mGnssSystemInfoProvider.getGnssYearOfHardware();
+        return mGnssNative.getHardwareYear();
     }
 
     /**
@@ -137,15 +126,21 @@
      */
     @Nullable
     public String getGnssHardwareModelName() {
-        return mGnssSystemInfoProvider.getGnssHardwareModelName();
+        return mGnssNative.getHardwareModelName();
     }
 
     /**
-     * Get GNSS hardware capabilities. The capabilities returned are a bitfield as described in
-     * {@link android.location.GnssCapabilities}.
+     * Get GNSS hardware capabilities.
      */
-    public long getGnssCapabilities() {
-        return mGnssCapabilitiesProvider.getGnssCapabilities();
+    public GnssCapabilities getGnssCapabilities() {
+        return mGnssNative.getCapabilities();
+    }
+
+    /**
+     * Get GNSS antenna information.
+     */
+    public @Nullable List<GnssAntennaInfo> getGnssAntennaInfos() {
+        return mAntennaInfoHalModule.getAntennaInfos();
     }
 
     /**
@@ -174,6 +169,24 @@
     }
 
     /**
+     * Registers listener for GNSS NMEA messages.
+     */
+    public void registerGnssNmeaCallback(IGnssNmeaListener listener, String packageName,
+            @Nullable String attributionTag) {
+        mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION, null);
+
+        CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag);
+        mGnssNmeaProvider.addListener(identity, listener);
+    }
+
+    /**
+     * Unregisters listener for GNSS NMEA messages.
+     */
+    public void unregisterGnssNmeaCallback(IGnssNmeaListener listener) {
+        mGnssNmeaProvider.removeListener(listener);
+    }
+
+    /**
      * Adds a GNSS measurements listener.
      */
     public void addGnssMeasurementsListener(GnssMeasurementRequest request,
@@ -192,7 +205,9 @@
         mContext.enforceCallingOrSelfPermission(Manifest.permission.LOCATION_HARDWARE, null);
         mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION, null);
 
-        mGnssMeasurementCorrectionsProvider.injectGnssMeasurementCorrections(corrections);
+        if (!mGnssNative.injectMeasurementCorrections(corrections)) {
+            Log.w(TAG, "failed to inject GNSS measurement corrections");
+        }
     }
 
     /**
@@ -203,29 +218,6 @@
     }
 
     /**
-     * Adds a GNSS Antenna Info listener.
-     *
-     * @param listener    called when GNSS antenna info is received
-     * @param packageName name of requesting package
-     */
-    public void addGnssAntennaInfoListener(IGnssAntennaInfoListener listener, String packageName,
-            @Nullable String attributionTag) {
-        mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION, null);
-
-        CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag);
-        mGnssAntennaInfoProvider.addListener(identity, listener);
-    }
-
-    /**
-     * Removes a GNSS Antenna Info listener.
-     *
-     * @param listener called when GNSS antenna info is received
-     */
-    public void removeGnssAntennaInfoListener(IGnssAntennaInfoListener listener) {
-        mGnssAntennaInfoProvider.removeListener(listener);
-    }
-
-    /**
      * Adds a GNSS navigation message listener.
      */
     public void addGnssNavigationMessageListener(IGnssNavigationMessageListener listener,
@@ -248,9 +240,9 @@
      */
     public void sendNiResponse(int notifId, int userResponse) {
         try {
-            mNetInitiatedListener.sendNiResponse(notifId, userResponse);
+            mGnssLocationProvider.getNetInitiatedListener().sendNiResponse(notifId, userResponse);
         } catch (RemoteException e) {
-            Log.e(TAG, "RemoteException in LocationManagerService.sendNiResponse");
+            throw e.rethrowFromSystemServer();
         }
     }
 
@@ -259,18 +251,18 @@
      */
     public void dump(FileDescriptor fd, IndentingPrintWriter ipw, String[] args) {
         if (args.length > 0 && args[0].equals("--gnssmetrics")) {
-            if (mGnssMetricsProvider != null) {
-                ipw.append(mGnssMetricsProvider.getGnssMetricsAsProtoString());
-            }
+            ipw.append(mGnssMetrics.dumpGnssMetricsAsProtoString());
             return;
         }
 
-        ipw.println("Antenna Info Provider:");
-        ipw.increaseIndent();
-        mGnssAntennaInfoProvider.dump(fd, ipw, args);
-        ipw.decreaseIndent();
+        ipw.println("Capabilities: " + mGnssNative.getCapabilities());
 
-        ipw.println("Measurement Provider:");
+        List<GnssAntennaInfo> infos = mAntennaInfoHalModule.getAntennaInfos();
+        if (infos != null) {
+            ipw.println("Antenna Infos: " + infos);
+        }
+
+        ipw.println("Measurements Provider:");
         ipw.increaseIndent();
         mGnssMeasurementsProvider.dump(fd, ipw, args);
         ipw.decreaseIndent();
@@ -284,169 +276,170 @@
         ipw.increaseIndent();
         mGnssStatusProvider.dump(fd, ipw, args);
         ipw.decreaseIndent();
+
+        GnssPowerStats powerStats = mGnssNative.getPowerStats();
+        if (powerStats != null) {
+            ipw.println("Last Power Stats:");
+            ipw.increaseIndent();
+            powerStats.dump(fd, ipw, args, mGnssNative.getCapabilities());
+            ipw.decreaseIndent();
+        }
     }
 
-    // all native callbacks - to be funneled to various locations as appropriate
+    private class GnssCapabilitiesHalModule implements GnssNative.BaseCallbacks {
 
-    @Override
-    public void reportLocation(boolean hasLatLong, Location location) {
-        mGnssLocationProvider.reportLocation(hasLatLong, location);
+        GnssCapabilitiesHalModule(GnssNative gnssNative) {
+            gnssNative.addBaseCallbacks(this);
+        }
+
+        @Override
+        public void onHalRestarted() {}
+
+        @Override
+        public void onCapabilitiesChanged(GnssCapabilities oldCapabilities,
+                GnssCapabilities newCapabilities) {
+            long ident = Binder.clearCallingIdentity();
+            try {
+                Intent intent = new Intent(LocationManager.ACTION_GNSS_CAPABILITIES_CHANGED)
+                        .putExtra(LocationManager.EXTRA_GNSS_CAPABILITIES, newCapabilities)
+                        .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY)
+                        .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+                mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
     }
 
-    @Override
-    public void reportStatus(int status) {
-        mGnssLocationProvider.reportStatus(status);
+    private class GnssGeofenceHalModule implements GnssNative.GeofenceCallbacks {
+
+        private GeofenceHardwareImpl mGeofenceHardwareImpl;
+
+        GnssGeofenceHalModule(GnssNative gnssNative) {
+            gnssNative.setGeofenceCallbacks(this);
+        }
+
+        private synchronized GeofenceHardwareImpl getGeofenceHardware() {
+            if (mGeofenceHardwareImpl == null) {
+                mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
+            }
+            return mGeofenceHardwareImpl;
+        }
+
+        @Override
+        public void onReportGeofenceTransition(int geofenceId, Location location,
+                @GeofenceTransition int transition, long timestamp) {
+            FgThread.getHandler().post(() -> getGeofenceHardware().reportGeofenceTransition(
+                    geofenceId, location, transition, timestamp,
+                    GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE,
+                    FusedBatchOptions.SourceTechnologies.GNSS));
+        }
+
+        @Override
+        public void onReportGeofenceStatus(@GeofenceAvailability int status, Location location) {
+            FgThread.getHandler().post(() -> {
+                int monitorStatus = GeofenceHardware.MONITOR_CURRENTLY_UNAVAILABLE;
+                if (status == GEOFENCE_AVAILABILITY_AVAILABLE) {
+                    monitorStatus = GeofenceHardware.MONITOR_CURRENTLY_AVAILABLE;
+                }
+                getGeofenceHardware().reportGeofenceMonitorStatus(
+                        GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE,
+                        monitorStatus,
+                        location,
+                        FusedBatchOptions.SourceTechnologies.GNSS);
+            });
+        }
+
+        @Override
+        public void onReportGeofenceAddStatus(int geofenceId, @GeofenceStatus int status) {
+            FgThread.getHandler().post(() -> getGeofenceHardware().reportGeofenceAddStatus(
+                    geofenceId, translateGeofenceStatus(status)));
+        }
+
+        @Override
+        public void onReportGeofenceRemoveStatus(int geofenceId, @GeofenceStatus int status) {
+            FgThread.getHandler().post(() -> getGeofenceHardware().reportGeofenceRemoveStatus(
+                    geofenceId, translateGeofenceStatus(status)));
+        }
+
+        @Override
+        public void onReportGeofencePauseStatus(int geofenceId, @GeofenceStatus int status) {
+            FgThread.getHandler().post(() -> getGeofenceHardware().reportGeofencePauseStatus(
+                    geofenceId, translateGeofenceStatus(status)));
+        }
+
+        @Override
+        public void onReportGeofenceResumeStatus(int geofenceId, @GeofenceStatus int status) {
+            FgThread.getHandler().post(() -> getGeofenceHardware().reportGeofenceResumeStatus(
+                    geofenceId, translateGeofenceStatus(status)));
+        }
+
+        private int translateGeofenceStatus(@GeofenceStatus int status) {
+            switch (status) {
+                case GEOFENCE_STATUS_OPERATION_SUCCESS:
+                    return GeofenceHardware.GEOFENCE_SUCCESS;
+                case GEOFENCE_STATUS_ERROR_GENERIC:
+                    return GeofenceHardware.GEOFENCE_FAILURE;
+                case GEOFENCE_STATUS_ERROR_ID_EXISTS:
+                    return GeofenceHardware.GEOFENCE_ERROR_ID_EXISTS;
+                case GEOFENCE_STATUS_ERROR_INVALID_TRANSITION:
+                    return GeofenceHardware.GEOFENCE_ERROR_INVALID_TRANSITION;
+                case GEOFENCE_STATUS_ERROR_TOO_MANY_GEOFENCES:
+                    return GeofenceHardware.GEOFENCE_ERROR_TOO_MANY_GEOFENCES;
+                case GEOFENCE_STATUS_ERROR_ID_UNKNOWN:
+                    return GeofenceHardware.GEOFENCE_ERROR_ID_UNKNOWN;
+                default:
+                    return -1;
+            }
+        }
     }
 
-    @Override
-    public void reportSvStatus(int svCount, int[] svidWithFlags, float[] cn0DbHzs,
-            float[] elevations, float[] azimuths, float[] carrierFrequencies,
-            float[] basebandCn0DbHzs) {
-        mGnssLocationProvider.reportSvStatus(svCount, svidWithFlags, cn0DbHzs, elevations, azimuths,
-                carrierFrequencies, basebandCn0DbHzs);
-    }
+    private class GnssAntennaInfoHalModule implements GnssNative.BaseCallbacks,
+            GnssNative.AntennaInfoCallbacks {
 
-    @Override
-    public void reportAGpsStatus(int agpsType, int agpsStatus, byte[] suplIpAddr) {
-        mGnssLocationProvider.reportAGpsStatus(agpsType, agpsStatus, suplIpAddr);
-    }
+        private final GnssNative mGnssNative;
 
-    @Override
-    public void reportNmea(long timestamp) {
-        mGnssLocationProvider.reportNmea(timestamp);
-    }
+        private volatile @Nullable List<GnssAntennaInfo> mAntennaInfos;
 
-    @Override
-    public void reportMeasurementData(GnssMeasurementsEvent event) {
-        mGnssLocationProvider.reportMeasurementData(event);
-    }
+        GnssAntennaInfoHalModule(GnssNative gnssNative) {
+            mGnssNative = gnssNative;
+            mGnssNative.addBaseCallbacks(this);
+            mGnssNative.addAntennaInfoCallbacks(this);
+        }
 
-    @Override
-    public void reportAntennaInfo(List<GnssAntennaInfo> antennaInfos) {
-        mGnssLocationProvider.reportAntennaInfo(antennaInfos);
-    }
+        @Nullable List<GnssAntennaInfo> getAntennaInfos() {
+            return mAntennaInfos;
+        }
 
-    @Override
-    public void reportNavigationMessage(GnssNavigationMessage event) {
-        mGnssLocationProvider.reportNavigationMessage(event);
-    }
+        @Override
+        public void onHalStarted() {
+            mGnssNative.startAntennaInfoListening();
+        }
 
-    @Override
-    public void reportGnssPowerStats(GnssPowerStats powerStats) {
-        mGnssLocationProvider.reportGnssPowerStats(powerStats);
-    }
+        @Override
+        public void onHalRestarted() {
+            mGnssNative.startAntennaInfoListening();
+        }
 
-    @Override
-    public void setTopHalCapabilities(int topHalCapabilities) {
-        mGnssLocationProvider.setTopHalCapabilities(topHalCapabilities);
-    }
+        @Override
+        public void onReportAntennaInfo(List<GnssAntennaInfo> antennaInfos) {
+            if (antennaInfos.equals(mAntennaInfos)) {
+                return;
+            }
 
-    @Override
-    public void setSubHalMeasurementCorrectionsCapabilities(int subHalCapabilities) {
-        mGnssLocationProvider.setSubHalMeasurementCorrectionsCapabilities(subHalCapabilities);
-    }
+            mAntennaInfos = antennaInfos;
 
-    @Override
-    public void setSubHalPowerIndicationCapabilities(int subHalCapabilities) {
-        mGnssLocationProvider.setSubHalPowerIndicationCapabilities(subHalCapabilities);
-    }
-
-    @Override
-    public void setGnssYearOfHardware(int yearOfHardware) {
-        mGnssLocationProvider.setGnssYearOfHardware(yearOfHardware);
-    }
-
-    @Override
-    public void setGnssHardwareModelName(String modelName) {
-        mGnssLocationProvider.setGnssHardwareModelName(modelName);
-    }
-
-    @Override
-    public void reportGnssServiceRestarted() {
-        mGnssLocationProvider.reportGnssServiceRestarted();
-    }
-
-    @Override
-    public void reportLocationBatch(Location[] locationArray) {
-        mGnssLocationProvider.reportLocationBatch(locationArray);
-    }
-
-    @Override
-    public void psdsDownloadRequest(int psdsType) {
-        mGnssLocationProvider.downloadPsdsData(psdsType);
-    }
-
-    @Override
-    public void reportGeofenceTransition(int geofenceId, Location location, int transition,
-            long transitionTimestamp) {
-        mGnssLocationProvider.reportGeofenceTransition(geofenceId, location, transition,
-                transitionTimestamp);
-    }
-
-    @Override
-    public void reportGeofenceStatus(int status, Location location) {
-        mGnssLocationProvider.reportGeofenceStatus(status, location);
-    }
-
-    @Override
-    public void reportGeofenceAddStatus(int geofenceId, int status) {
-        mGnssLocationProvider.reportGeofenceAddStatus(geofenceId, status);
-    }
-
-    @Override
-    public void reportGeofenceRemoveStatus(int geofenceId, int status) {
-        mGnssLocationProvider.reportGeofenceRemoveStatus(geofenceId, status);
-    }
-
-    @Override
-    public void reportGeofencePauseStatus(int geofenceId, int status) {
-        mGnssLocationProvider.reportGeofencePauseStatus(geofenceId, status);
-    }
-
-    @Override
-    public void reportGeofenceResumeStatus(int geofenceId, int status) {
-        mGnssLocationProvider.reportGeofenceResumeStatus(geofenceId, status);
-    }
-
-    @Override
-    public void reportNiNotification(int notificationId, int niType, int notifyFlags,
-            int timeout, int defaultResponse, String requestorId, String text,
-            int requestorIdEncoding, int textEncoding) {
-        mGnssLocationProvider.reportNiNotification(notificationId, niType, notifyFlags, timeout,
-                defaultResponse, requestorId, text, requestorIdEncoding, textEncoding);
-    }
-
-    @Override
-    public void requestSetID(int flags) {
-        mGnssLocationProvider.requestSetID(flags);
-    }
-
-    @Override
-    public void requestLocation(boolean independentFromGnss, boolean isUserEmergency) {
-        mGnssLocationProvider.requestLocation(independentFromGnss, isUserEmergency);
-    }
-
-    @Override
-    public void requestUtcTime() {
-        mGnssLocationProvider.requestUtcTime();
-    }
-
-    @Override
-    public void requestRefLocation() {
-        mGnssLocationProvider.requestRefLocation();
-    }
-
-    @Override
-    public void reportNfwNotification(String proxyAppPackageName, byte protocolStack,
-            String otherProtocolStackName, byte requestor, String requestorId,
-            byte responseType, boolean inEmergencyMode, boolean isCachedLocation) {
-        mGnssLocationProvider.reportNfwNotification(proxyAppPackageName, protocolStack,
-                otherProtocolStackName, requestor, requestorId, responseType, inEmergencyMode,
-                isCachedLocation);
-    }
-
-    @Override
-    public boolean isInEmergencySession() {
-        return mGnssLocationProvider.isInEmergencySession();
+            long ident = Binder.clearCallingIdentity();
+            try {
+                Intent intent = new Intent(LocationManager.ACTION_GNSS_ANTENNA_INFOS_CHANGED)
+                        .putParcelableArrayListExtra(LocationManager.EXTRA_GNSS_ANTENNA_INFOS,
+                                new ArrayList<>(antennaInfos))
+                        .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY)
+                        .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+                mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
     }
 }
diff --git a/services/core/java/com/android/server/location/gnss/GnssMeasurementCorrectionsProvider.java b/services/core/java/com/android/server/location/gnss/GnssMeasurementCorrectionsProvider.java
deleted file mode 100644
index 4401f29..0000000
--- a/services/core/java/com/android/server/location/gnss/GnssMeasurementCorrectionsProvider.java
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * 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.location.gnss;
-
-import android.location.GnssMeasurementCorrections;
-import android.os.Handler;
-import android.util.Log;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-/**
- * Manages GNSS measurement corrections.
- *
- * <p>Implements the framework side of the GNSS HAL interfaces {@code IMeasurementCorrections.hal}
- * and {@code IMeasurementCorrectionsCallback.hal).
- *
- * @hide
- */
-public class GnssMeasurementCorrectionsProvider {
-    private static final String TAG = "GnssMeasurementCorrectionsProvider";
-
-    // These must match with the Capabilities enum in IMeasurementCorrectionsCallback.hal.
-    static final int CAPABILITY_LOS_SATS = 0x0000001;
-    static final int CAPABILITY_EXCESS_PATH_LENGTH = 0x0000002;
-    static final int CAPABILITY_REFLECTING_PLANE = 0x0000004;
-
-    private static final int INVALID_CAPABILITIES = 1 << 31;
-
-    private final Handler mHandler;
-    private final GnssMeasurementCorrectionsProviderNative mNative;
-    private volatile int mCapabilities = INVALID_CAPABILITIES;
-
-    GnssMeasurementCorrectionsProvider(Handler handler) {
-        this(handler, new GnssMeasurementCorrectionsProviderNative());
-    }
-
-    @VisibleForTesting
-    GnssMeasurementCorrectionsProvider(Handler handler,
-            GnssMeasurementCorrectionsProviderNative aNative) {
-        mHandler = handler;
-        mNative = aNative;
-    }
-
-    /**
-     * Returns {@code true} if the GNSS HAL implementation supports measurement corrections.
-     */
-    public boolean isAvailableInPlatform() {
-        return mNative.isMeasurementCorrectionsSupported();
-    }
-
-    /**
-     * Injects GNSS measurement corrections into the GNSS chipset.
-     *
-     * @param measurementCorrections a {@link GnssMeasurementCorrections} object with the GNSS
-     *                               measurement corrections to be injected into the GNSS chipset.
-     */
-    public void injectGnssMeasurementCorrections(
-            GnssMeasurementCorrections measurementCorrections) {
-        if (!isCapabilitiesReceived()) {
-            Log.w(TAG, "Failed to inject GNSS measurement corrections. Capabilities "
-                    + "not received yet.");
-            return;
-        }
-        mHandler.post(() -> {
-            if (!mNative.injectGnssMeasurementCorrections(measurementCorrections)) {
-                Log.e(TAG, "Failure in injecting GNSS corrections.");
-            }
-        });
-    }
-
-    /** Handle measurement corrections capabilities update from the GNSS HAL implementation. */
-    boolean onCapabilitiesUpdated(int capabilities) {
-        if (hasCapability(capabilities, CAPABILITY_LOS_SATS) || hasCapability(capabilities,
-                CAPABILITY_EXCESS_PATH_LENGTH)) {
-            mCapabilities = capabilities;
-            return true;
-        } else {
-            Log.e(TAG, "Failed to set capabilities. Received capabilities 0x"
-                    + Integer.toHexString(capabilities) + " does not contain the mandatory "
-                    + "LOS_SATS or the EXCESS_PATH_LENGTH capability.");
-            return false;
-        }
-    }
-
-    /**
-     * Returns the measurement corrections specific capabilities of the GNSS HAL implementation.
-     */
-    int getCapabilities() {
-        return mCapabilities;
-    }
-
-    /**
-     * Returns the string representation of the GNSS measurement capabilities.
-     */
-    String toStringCapabilities() {
-        final int capabilities = getCapabilities();
-        StringBuilder s = new StringBuilder();
-        s.append("mCapabilities=0x").append(Integer.toHexString(capabilities));
-        s.append(" ( ");
-        if (hasCapability(capabilities, CAPABILITY_LOS_SATS)) {
-            s.append("LOS_SATS ");
-        }
-        if (hasCapability(capabilities, CAPABILITY_EXCESS_PATH_LENGTH)) {
-            s.append("EXCESS_PATH_LENGTH ");
-        }
-        if (hasCapability(capabilities, CAPABILITY_REFLECTING_PLANE)) {
-            s.append("REFLECTING_PLANE ");
-        }
-        s.append(")");
-        return s.toString();
-    }
-
-    private static boolean hasCapability(int halCapabilities, int capability) {
-        return (halCapabilities & capability) != 0;
-    }
-
-    private boolean isCapabilitiesReceived() {
-        return mCapabilities != INVALID_CAPABILITIES;
-    }
-
-    @VisibleForTesting
-    static class GnssMeasurementCorrectionsProviderNative {
-        public boolean isMeasurementCorrectionsSupported() {
-            return native_is_measurement_corrections_supported();
-        }
-
-        public boolean injectGnssMeasurementCorrections(
-                GnssMeasurementCorrections measurementCorrections) {
-            return native_inject_gnss_measurement_corrections(measurementCorrections);
-        }
-    }
-
-    static native boolean native_is_measurement_corrections_supported();
-
-    static native boolean native_inject_gnss_measurement_corrections(
-            GnssMeasurementCorrections measurementCorrections);
-}
diff --git a/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java b/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
index fa8e2a6..b623e27 100644
--- a/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
@@ -29,8 +29,7 @@
 import android.stats.location.LocationStatsEnums;
 import android.util.Log;
 
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.Preconditions;
+import com.android.server.location.gnss.hal.GnssNative;
 import com.android.server.location.injector.AppOpsHelper;
 import com.android.server.location.injector.Injector;
 import com.android.server.location.injector.LocationAttributionHelper;
@@ -38,7 +37,6 @@
 import com.android.server.location.injector.SettingsHelper;
 
 import java.util.Collection;
-import java.util.Objects;
 
 /**
  * An base implementation for GNSS measurements provider. It abstracts out the responsibility of
@@ -47,8 +45,10 @@
  * @hide
  */
 public final class GnssMeasurementsProvider extends
-        GnssListenerMultiplexer<GnssMeasurementRequest, IGnssMeasurementsListener, Boolean>
-        implements SettingsHelper.GlobalSettingChangedListener {
+        GnssListenerMultiplexer<GnssMeasurementRequest, IGnssMeasurementsListener,
+                GnssMeasurementRequest> implements
+        SettingsHelper.GlobalSettingChangedListener, GnssNative.BaseCallbacks,
+        GnssNative.MeasurementCallbacks {
 
     private class GnssMeasurementListenerRegistration extends GnssListenerRegistration {
 
@@ -57,8 +57,14 @@
         protected GnssMeasurementListenerRegistration(
                 @Nullable GnssMeasurementRequest request,
                 CallerIdentity callerIdentity,
-                IGnssMeasurementsListener iGnssMeasurementsListener) {
-            super(request, callerIdentity, iGnssMeasurementsListener);
+                IGnssMeasurementsListener listener) {
+            super(request, callerIdentity, listener);
+        }
+
+        @Override
+        protected void onGnssListenerRegister() {
+            executeOperation(listener -> listener.onStatusChanged(
+                    GnssMeasurementsEvent.Callback.STATUS_READY));
         }
 
         @Nullable
@@ -79,24 +85,22 @@
     private final AppOpsHelper mAppOpsHelper;
     private final LocationAttributionHelper mLocationAttributionHelper;
     private final LocationUsageLogger mLogger;
-    private final GnssMeasurementProviderNative mNative;
+    private final GnssNative mGnssNative;
 
-    public GnssMeasurementsProvider(Injector injector) {
-        this(injector, new GnssMeasurementProviderNative());
-    }
-
-    @VisibleForTesting
-    public GnssMeasurementsProvider(Injector injector, GnssMeasurementProviderNative aNative) {
+    public GnssMeasurementsProvider(Injector injector, GnssNative gnssNative) {
         super(injector);
         mAppOpsHelper = injector.getAppOpsHelper();
         mLocationAttributionHelper = injector.getLocationAttributionHelper();
         mLogger = injector.getLocationUsageLogger();
-        mNative = aNative;
+        mGnssNative = gnssNative;
+
+        mGnssNative.addBaseCallbacks(this);
+        mGnssNative.addMeasurementCallbacks(this);
     }
 
     @Override
     protected boolean isServiceSupported() {
-        return mNative.isMeasurementSupported();
+        return mGnssNative.isMeasurementSupported();
     }
 
     @Override
@@ -112,17 +116,14 @@
     }
 
     @Override
-    protected boolean registerWithService(Boolean fullTrackingRequest,
+    protected boolean registerWithService(GnssMeasurementRequest request,
             Collection<GnssListenerRegistration> registrations) {
-        Preconditions.checkState(mNative.isMeasurementSupported());
-
-        if (mNative.startMeasurementCollection(fullTrackingRequest)) {
+        if (mGnssNative.startMeasurementCollection(request.isFullTracking())) {
             if (D) {
-                Log.d(TAG, "starting gnss measurements (" + fullTrackingRequest + ")");
+                Log.d(TAG, "starting gnss measurements (" + request + ")");
             }
             return true;
         } else {
-
             Log.e(TAG, "error starting gnss measurements");
             return false;
         }
@@ -130,14 +131,12 @@
 
     @Override
     protected void unregisterWithService() {
-        if (mNative.isMeasurementSupported()) {
-            if (mNative.stopMeasurementCollection()) {
-                if (D) {
-                    Log.d(TAG, "stopping gnss measurements");
-                }
-            } else {
-                Log.e(TAG, "error stopping gnss measurements");
+        if (mGnssNative.stopMeasurementCollection()) {
+            if (D) {
+                Log.d(TAG, "stopping gnss measurements");
             }
+        } else {
+            Log.e(TAG, "error stopping gnss measurements");
         }
     }
 
@@ -158,18 +157,21 @@
     }
 
     @Override
-    protected Boolean mergeRegistrations(Collection<GnssListenerRegistration> registrations) {
+    protected GnssMeasurementRequest mergeRegistrations(
+            Collection<GnssListenerRegistration> registrations) {
+        boolean fullTracking = false;
         if (mSettingsHelper.isGnssMeasurementsFullTrackingEnabled()) {
-            return true;
-        }
-
-        for (GnssListenerRegistration registration : registrations) {
-            if (Objects.requireNonNull(registration.getRequest()).isFullTracking()) {
-                return true;
+            fullTracking = true;
+        } else {
+            for (GnssListenerRegistration registration : registrations) {
+                if (registration.getRequest().isFullTracking()) {
+                    fullTracking = true;
+                    break;
+                }
             }
         }
 
-        return false;
+        return new GnssMeasurementRequest.Builder().setFullTracking(fullTracking).build();
     }
 
     @Override
@@ -198,10 +200,13 @@
                 null, registration.isForeground());
     }
 
-    /**
-     * Called by GnssLocationProvider.
-     */
-    public void onMeasurementsAvailable(GnssMeasurementsEvent event) {
+    @Override
+    public void onHalRestarted() {
+        resetService();
+    }
+
+    @Override
+    public void onReportMeasurements(GnssMeasurementsEvent event) {
         deliverToListeners(registration -> {
             if (mAppOpsHelper.noteOpNoThrow(AppOpsManager.OP_FINE_LOCATION,
                     registration.getIdentity())) {
@@ -211,25 +216,4 @@
             }
         });
     }
-
-    @VisibleForTesting
-    static class GnssMeasurementProviderNative {
-        boolean isMeasurementSupported() {
-            return native_is_measurement_supported();
-        }
-
-        boolean startMeasurementCollection(boolean enableFullTracking) {
-            return native_start_measurement_collection(enableFullTracking);
-        }
-
-        boolean stopMeasurementCollection() {
-            return native_stop_measurement_collection();
-        }
-    }
-
-    static native boolean native_is_measurement_supported();
-
-    static native boolean native_start_measurement_collection(boolean enableFullTracking);
-
-    static native boolean native_stop_measurement_collection();
 }
diff --git a/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java b/services/core/java/com/android/server/location/gnss/GnssMetrics.java
similarity index 95%
rename from location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java
rename to services/core/java/com/android/server/location/gnss/GnssMetrics.java
index dd8a8c3..c7d8144 100644
--- a/location/java/com/android/internal/location/gnssmetrics/GnssMetrics.java
+++ b/services/core/java/com/android/server/location/gnss/GnssMetrics.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016 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.
@@ -14,15 +14,11 @@
  * limitations under the License.
  */
 
-package com.android.internal.location.gnssmetrics;
-
-import static android.location.GnssSignalQuality.GNSS_SIGNAL_QUALITY_GOOD;
-import static android.location.GnssSignalQuality.GNSS_SIGNAL_QUALITY_POOR;
-import static android.location.GnssSignalQuality.GNSS_SIGNAL_QUALITY_UNKNOWN;
-import static android.location.GnssSignalQuality.NUM_GNSS_SIGNAL_QUALITY_LEVELS;
+package com.android.server.location.gnss;
 
 import android.app.StatsManager;
 import android.content.Context;
+import android.location.GnssSignalQuality;
 import android.location.GnssStatus;
 import android.os.RemoteException;
 import android.os.SystemClock;
@@ -67,11 +63,11 @@
 
     // A boolean array indicating whether the constellation types have been used in fix.
     private boolean[] mConstellationTypes;
-    private Statistics mLocationFailureStatistics;
-    private Statistics mTimeToFirstFixSecStatistics;
-    private Statistics mPositionAccuracyMeterStatistics;
-    private Statistics mTopFourAverageCn0Statistics;
-    private Statistics mTopFourAverageCn0StatisticsL5;
+    private final Statistics mLocationFailureStatistics;
+    private final Statistics mTimeToFirstFixSecStatistics;
+    private final Statistics mPositionAccuracyMeterStatistics;
+    private final Statistics mTopFourAverageCn0Statistics;
+    private final Statistics mTopFourAverageCn0StatisticsL5;
     // Total number of sv status messages processed
     private int mNumSvStatus;
     // Total number of L5 sv status messages processed
@@ -91,7 +87,7 @@
     long mSvStatusReportsUsedInFix;
     long mL5SvStatusReportsUsedInFix;
 
-    private StatsManager mStatsManager;
+    private final StatsManager mStatsManager;
 
     public GnssMetrics(Context context, IBatteryStats stats) {
         mGnssPowerMetrics = new GnssPowerMetrics(stats);
@@ -390,7 +386,7 @@
                     stats.getLoggingDurationMs() / ((double) DateUtils.MINUTE_IN_MILLIS)).append(
                     "\n");
             long[] t = stats.getTimeInGpsSignalQualityLevel();
-            if (t != null && t.length == NUM_GNSS_SIGNAL_QUALITY_LEVELS) {
+            if (t != null && t.length == GnssSignalQuality.NUM_GNSS_SIGNAL_QUALITY_LEVELS) {
                 s.append("  Amount of time (while on battery) Top 4 Avg CN0 > "
                         + GnssPowerMetrics.POOR_TOP_FOUR_AVG_CN0_THRESHOLD_DB_HZ
                         + " dB-Hz (min): ").append(
@@ -505,7 +501,7 @@
           // so that
             // the first CNO report will trigger an update to BatteryStats
             mLastAverageCn0 = -100.0;
-            mLastSignalLevel = GNSS_SIGNAL_QUALITY_UNKNOWN;
+            mLastSignalLevel = GnssSignalQuality.GNSS_SIGNAL_QUALITY_UNKNOWN;
         }
 
         /**
@@ -577,9 +573,9 @@
          */
         private int getSignalLevel(double cn0) {
             if (cn0 > POOR_TOP_FOUR_AVG_CN0_THRESHOLD_DB_HZ) {
-                return GNSS_SIGNAL_QUALITY_GOOD;
+                return GnssSignalQuality.GNSS_SIGNAL_QUALITY_GOOD;
             }
-            return GNSS_SIGNAL_QUALITY_POOR;
+            return GnssSignalQuality.GNSS_SIGNAL_QUALITY_POOR;
         }
     }
 
diff --git a/services/core/java/com/android/server/location/gnss/GnssNative.java b/services/core/java/com/android/server/location/gnss/GnssNative.java
deleted file mode 100644
index 4494c19..0000000
--- a/services/core/java/com/android/server/location/gnss/GnssNative.java
+++ /dev/null
@@ -1,324 +0,0 @@
-/*
- * 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.location.gnss;
-
-import android.location.GnssAntennaInfo;
-import android.location.GnssMeasurementsEvent;
-import android.location.GnssNavigationMessage;
-import android.location.Location;
-
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.Preconditions;
-import com.android.server.FgThread;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-import java.util.List;
-
-/**
- * Entry point for all GNSS native callbacks, and responsible for initializing the GNSS HAL.
- */
-class GnssNative {
-
-    interface Callbacks {
-        void reportLocation(boolean hasLatLong, Location location);
-        void reportStatus(int status);
-        void reportSvStatus(int svCount, int[] svidWithFlags, float[] cn0DbHzs,
-                float[] elevations, float[] azimuths, float[] carrierFrequencies,
-                float[] basebandCn0DbHzs);
-        void reportAGpsStatus(int agpsType, int agpsStatus, byte[] suplIpAddr);
-        void reportNmea(long timestamp);
-        void reportMeasurementData(GnssMeasurementsEvent event);
-        void reportAntennaInfo(List<GnssAntennaInfo> antennaInfos);
-        void reportNavigationMessage(GnssNavigationMessage event);
-        void reportGnssPowerStats(GnssPowerStats powerStats);
-        void setTopHalCapabilities(int topHalCapabilities);
-        void setSubHalMeasurementCorrectionsCapabilities(int subHalCapabilities);
-        void setSubHalPowerIndicationCapabilities(int subHalCapabilities);
-        void setGnssYearOfHardware(int yearOfHardware);
-        void setGnssHardwareModelName(String modelName);
-        void reportGnssServiceRestarted();
-        void reportLocationBatch(Location[] locationArray);
-        void psdsDownloadRequest(int psdsType);
-        void reportGeofenceTransition(int geofenceId, Location location, int transition,
-                long transitionTimestamp);
-        void reportGeofenceStatus(int status, Location location);
-        void reportGeofenceAddStatus(int geofenceId, int status);
-        void reportGeofenceRemoveStatus(int geofenceId, int status);
-        void reportGeofencePauseStatus(int geofenceId, int status);
-        void reportGeofenceResumeStatus(int geofenceId, int status);
-        void reportNiNotification(
-                int notificationId,
-                int niType,
-                int notifyFlags,
-                int timeout,
-                int defaultResponse,
-                String requestorId,
-                String text,
-                int requestorIdEncoding,
-                int textEncoding
-        );
-        void requestSetID(int flags);
-        void requestLocation(boolean independentFromGnss, boolean isUserEmergency);
-        void requestUtcTime();
-        void requestRefLocation();
-        void reportNfwNotification(String proxyAppPackageName, byte protocolStack,
-                String otherProtocolStackName, byte requestor, String requestorId,
-                byte responseType, boolean inEmergencyMode, boolean isCachedLocation);
-        boolean isInEmergencySession();
-    }
-
-    /**
-     * Indicates that this method is a native entry point. Useful purely for IDEs which can
-     * understand entry points, and thus eliminate incorrect warnings about methods not used.
-     */
-    @Target(ElementType.METHOD)
-    @Retention(RetentionPolicy.SOURCE)
-    private @interface NativeEntryPoint {}
-
-    @GuardedBy("GnssNative.class")
-    private static boolean sInitialized;
-
-    @GuardedBy("GnssNative.class")
-    private static GnssNativeInitNative sInitNative = new GnssNativeInitNative();
-
-    @GuardedBy("GnssNative.class")
-    private static GnssNative sInstance;
-
-    @VisibleForTesting
-    public static synchronized void setInitNativeForTest(GnssNativeInitNative initNative) {
-        sInitNative = initNative;
-    }
-
-    public static synchronized boolean isSupported() {
-        initialize();
-        return sInitNative.isSupported();
-    }
-
-    static synchronized void initialize() {
-        if (!sInitialized) {
-            sInitNative.classInitOnce();
-            sInitialized = true;
-        }
-    }
-
-    @VisibleForTesting
-    public static synchronized void resetCallbacksForTest() {
-        sInstance = null;
-    }
-
-    static synchronized void register(Callbacks callbacks) {
-        Preconditions.checkState(sInstance == null);
-        initialize();
-        sInstance = new GnssNative(callbacks);
-    }
-
-    private final Callbacks mCallbacks;
-
-    private GnssNative(Callbacks callbacks) {
-        mCallbacks = callbacks;
-        sInitNative.initOnce(this, false);
-    }
-
-    @NativeEntryPoint
-    private void reportLocation(boolean hasLatLong, Location location) {
-        mCallbacks.reportLocation(hasLatLong, location);
-    }
-
-    @NativeEntryPoint
-    private void reportStatus(int status) {
-        mCallbacks.reportStatus(status);
-    }
-
-    @NativeEntryPoint
-    private void reportSvStatus(int svCount, int[] svidWithFlags, float[] cn0DbHzs,
-            float[] elevations, float[] azimuths, float[] carrierFrequencies,
-            float[] basebandCn0DbHzs) {
-        mCallbacks.reportSvStatus(svCount, svidWithFlags, cn0DbHzs, elevations, azimuths,
-                carrierFrequencies, basebandCn0DbHzs);
-    }
-
-    @NativeEntryPoint
-    private void reportAGpsStatus(int agpsType, int agpsStatus, byte[] suplIpAddr) {
-        mCallbacks.reportAGpsStatus(agpsType, agpsStatus, suplIpAddr);
-    }
-
-    @NativeEntryPoint
-    private void reportNmea(long timestamp) {
-        mCallbacks.reportNmea(timestamp);
-    }
-
-    @NativeEntryPoint
-    private void reportMeasurementData(GnssMeasurementsEvent event) {
-        mCallbacks.reportMeasurementData(event);
-    }
-
-    @NativeEntryPoint
-    private void reportAntennaInfo(List<GnssAntennaInfo> antennaInfos) {
-        mCallbacks.reportAntennaInfo(antennaInfos);
-    }
-
-    @NativeEntryPoint
-    private void reportNavigationMessage(GnssNavigationMessage event) {
-        mCallbacks.reportNavigationMessage(event);
-    }
-
-    @NativeEntryPoint
-    private void reportGnssPowerStats(GnssPowerStats powerStats) {
-        mCallbacks.reportGnssPowerStats(powerStats);
-    }
-
-    @NativeEntryPoint
-    private void setTopHalCapabilities(int topHalCapabilities) {
-        mCallbacks.setTopHalCapabilities(topHalCapabilities);
-    }
-
-    @NativeEntryPoint
-    private void setSubHalMeasurementCorrectionsCapabilities(int subHalCapabilities) {
-        mCallbacks.setSubHalMeasurementCorrectionsCapabilities(subHalCapabilities);
-    }
-
-    @NativeEntryPoint
-    private void setSubHalPowerIndicationCapabilities(int subHalCapabilities) {
-        mCallbacks.setSubHalPowerIndicationCapabilities(subHalCapabilities);
-    }
-
-    @NativeEntryPoint
-    private void setGnssYearOfHardware(int yearOfHardware) {
-        mCallbacks.setGnssYearOfHardware(yearOfHardware);
-    }
-
-    @NativeEntryPoint
-    private void setGnssHardwareModelName(String modelName) {
-        mCallbacks.setGnssHardwareModelName(modelName);
-    }
-
-    @NativeEntryPoint
-    private void reportGnssServiceDied() {
-        FgThread.getExecutor().execute(() -> {
-            sInitNative.initOnce(GnssNative.this, true);
-            mCallbacks.reportGnssServiceRestarted();
-        });
-    }
-
-    @NativeEntryPoint
-    private void reportLocationBatch(Location[] locationArray) {
-        mCallbacks.reportLocationBatch(locationArray);
-    }
-
-    @NativeEntryPoint
-    private void psdsDownloadRequest(int psdsType) {
-        mCallbacks.psdsDownloadRequest(psdsType);
-    }
-
-    @NativeEntryPoint
-    private void reportGeofenceTransition(int geofenceId, Location location, int transition,
-            long transitionTimestamp) {
-        mCallbacks.reportGeofenceTransition(geofenceId, location, transition, transitionTimestamp);
-    }
-
-    @NativeEntryPoint
-    private void reportGeofenceStatus(int status, Location location) {
-        mCallbacks.reportGeofenceStatus(status, location);
-    }
-
-    @NativeEntryPoint
-    private void reportGeofenceAddStatus(int geofenceId, int status) {
-        mCallbacks.reportGeofenceAddStatus(geofenceId, status);
-    }
-
-    @NativeEntryPoint
-    private void reportGeofenceRemoveStatus(int geofenceId, int status) {
-        mCallbacks.reportGeofenceRemoveStatus(geofenceId, status);
-    }
-
-    @NativeEntryPoint
-    private void reportGeofencePauseStatus(int geofenceId, int status) {
-        mCallbacks.reportGeofencePauseStatus(geofenceId, status);
-    }
-
-    @NativeEntryPoint
-    private void reportGeofenceResumeStatus(int geofenceId, int status) {
-        mCallbacks.reportGeofenceResumeStatus(geofenceId, status);
-    }
-
-    @NativeEntryPoint
-    private void reportNiNotification(int notificationId, int niType, int notifyFlags,
-            int timeout, int defaultResponse, String requestorId, String text,
-            int requestorIdEncoding, int textEncoding) {
-        mCallbacks.reportNiNotification(notificationId, niType, notifyFlags, timeout,
-                defaultResponse, requestorId, text, requestorIdEncoding, textEncoding);
-    }
-
-    @NativeEntryPoint
-    private void requestSetID(int flags) {
-        mCallbacks.requestSetID(flags);
-    }
-
-    @NativeEntryPoint
-    private void requestLocation(boolean independentFromGnss, boolean isUserEmergency) {
-        mCallbacks.requestLocation(independentFromGnss, isUserEmergency);
-    }
-
-    @NativeEntryPoint
-    private void requestUtcTime() {
-        mCallbacks.requestUtcTime();
-    }
-
-    @NativeEntryPoint
-    private void requestRefLocation() {
-        mCallbacks.requestRefLocation();
-    }
-
-    @NativeEntryPoint
-    private void reportNfwNotification(String proxyAppPackageName, byte protocolStack,
-            String otherProtocolStackName, byte requestor, String requestorId,
-            byte responseType, boolean inEmergencyMode, boolean isCachedLocation) {
-        mCallbacks.reportNfwNotification(proxyAppPackageName, protocolStack, otherProtocolStackName,
-                requestor, requestorId, responseType, inEmergencyMode, isCachedLocation);
-    }
-
-    @NativeEntryPoint
-    private boolean isInEmergencySession() {
-        return mCallbacks.isInEmergencySession();
-    }
-
-    @VisibleForTesting
-    public static class GnssNativeInitNative {
-
-        public void classInitOnce() {
-            native_class_init_once();
-        }
-
-        public boolean isSupported() {
-            return native_is_supported();
-        }
-
-        public void initOnce(GnssNative gnssNative, boolean reinitializeGnssServiceHandle) {
-            gnssNative.native_init_once(reinitializeGnssServiceHandle);
-        }
-    }
-
-    static native void native_class_init_once();
-
-    static native boolean native_is_supported();
-
-    native void native_init_once(boolean reinitializeGnssServiceHandle);
-}
diff --git a/services/core/java/com/android/server/location/gnss/GnssNavigationMessageProvider.java b/services/core/java/com/android/server/location/gnss/GnssNavigationMessageProvider.java
index 92491f7..ff9ad65 100644
--- a/services/core/java/com/android/server/location/gnss/GnssNavigationMessageProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssNavigationMessageProvider.java
@@ -25,8 +25,7 @@
 import android.location.util.identity.CallerIdentity;
 import android.util.Log;
 
-import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.util.Preconditions;
+import com.android.server.location.gnss.hal.GnssNative;
 import com.android.server.location.injector.AppOpsHelper;
 import com.android.server.location.injector.Injector;
 
@@ -40,21 +39,39 @@
  * @hide
  */
 public class GnssNavigationMessageProvider extends
-        GnssListenerMultiplexer<Void, IGnssNavigationMessageListener, Void> {
+        GnssListenerMultiplexer<Void, IGnssNavigationMessageListener, Void> implements
+        GnssNative.BaseCallbacks, GnssNative.NavigationMessageCallbacks {
 
-    private final AppOpsHelper mAppOpsHelper;
-    private final GnssNavigationMessageProviderNative mNative;
+    private class GnssNavigationMessageListenerRegistration extends GnssListenerRegistration {
 
-    public GnssNavigationMessageProvider(Injector injector) {
-        this(injector, new GnssNavigationMessageProviderNative());
+        protected GnssNavigationMessageListenerRegistration(
+                CallerIdentity callerIdentity,
+                IGnssNavigationMessageListener listener) {
+            super(null, callerIdentity, listener);
+        }
+
+        @Override
+        protected void onGnssListenerRegister() {
+            executeOperation(listener -> listener.onStatusChanged(
+                    GnssNavigationMessage.Callback.STATUS_READY));
+        }
     }
 
-    @VisibleForTesting
-    public GnssNavigationMessageProvider(Injector injector,
-            GnssNavigationMessageProviderNative aNative) {
+    private final AppOpsHelper mAppOpsHelper;
+    private final GnssNative mGnssNative;
+
+    public GnssNavigationMessageProvider(Injector injector, GnssNative gnssNative) {
         super(injector);
         mAppOpsHelper = injector.getAppOpsHelper();
-        mNative = aNative;
+        mGnssNative = gnssNative;
+
+        mGnssNative.addBaseCallbacks(this);
+        mGnssNative.addNavigationMessageCallbacks(this);
+    }
+
+    @Override
+    protected boolean isServiceSupported() {
+        return mGnssNative.isNavigationMessageCollectionSupported();
     }
 
     @Override
@@ -63,11 +80,15 @@
     }
 
     @Override
+    protected GnssListenerRegistration createRegistration(Void request,
+            CallerIdentity callerIdentity, IGnssNavigationMessageListener listener) {
+        return new GnssNavigationMessageListenerRegistration(callerIdentity, listener);
+    }
+
+    @Override
     protected boolean registerWithService(Void ignored,
             Collection<GnssListenerRegistration> registrations) {
-        Preconditions.checkState(mNative.isNavigationMessageSupported());
-
-        if (mNative.startNavigationMessageCollection()) {
+        if (mGnssNative.startNavigationMessageCollection()) {
             if (D) {
                 Log.d(TAG, "starting gnss navigation messages");
             }
@@ -80,21 +101,22 @@
 
     @Override
     protected void unregisterWithService() {
-        if (mNative.isNavigationMessageSupported()) {
-            if (mNative.stopNavigationMessageCollection()) {
-                if (D) {
-                    Log.d(TAG, "stopping gnss navigation messages");
-                }
-            } else {
-                Log.e(TAG, "error stopping gnss navigation messages");
+        if (mGnssNative.stopNavigationMessageCollection()) {
+            if (D) {
+                Log.d(TAG, "stopping gnss navigation messages");
             }
+        } else {
+            Log.e(TAG, "error stopping gnss navigation messages");
         }
     }
 
-    /**
-     * Called by GnssLocationProvider.
-     */
-    public void onNavigationMessageAvailable(GnssNavigationMessage event) {
+    @Override
+    public void onHalRestarted() {
+        resetService();
+    }
+
+    @Override
+    public void onReportNavigationMessage(GnssNavigationMessage event) {
         deliverToListeners(registration -> {
             if (mAppOpsHelper.noteOpNoThrow(AppOpsManager.OP_FINE_LOCATION,
                     registration.getIdentity())) {
@@ -104,30 +126,4 @@
             }
         });
     }
-
-    @Override
-    protected boolean isServiceSupported() {
-        return mNative.isNavigationMessageSupported();
-    }
-
-    @VisibleForTesting
-    static class GnssNavigationMessageProviderNative {
-        boolean isNavigationMessageSupported() {
-            return native_is_navigation_message_supported();
-        }
-
-        boolean startNavigationMessageCollection() {
-            return native_start_navigation_message_collection();
-        }
-
-        boolean stopNavigationMessageCollection() {
-            return native_stop_navigation_message_collection();
-        }
-    }
-
-    static native boolean native_is_navigation_message_supported();
-
-    static native boolean native_start_navigation_message_collection();
-
-    static native boolean native_stop_navigation_message_collection();
 }
diff --git a/services/core/java/com/android/server/location/gnss/GnssNmeaProvider.java b/services/core/java/com/android/server/location/gnss/GnssNmeaProvider.java
new file mode 100644
index 0000000..5036a6e
--- /dev/null
+++ b/services/core/java/com/android/server/location/gnss/GnssNmeaProvider.java
@@ -0,0 +1,110 @@
+/*
+ * 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.location.gnss;
+
+import static com.android.server.location.gnss.GnssManagerService.D;
+import static com.android.server.location.gnss.GnssManagerService.TAG;
+
+import android.annotation.Nullable;
+import android.app.AppOpsManager;
+import android.location.IGnssNmeaListener;
+import android.location.util.identity.CallerIdentity;
+import android.util.Log;
+
+import com.android.internal.listeners.ListenerExecutor;
+import com.android.server.location.gnss.hal.GnssNative;
+import com.android.server.location.injector.AppOpsHelper;
+import com.android.server.location.injector.Injector;
+
+import java.util.Collection;
+import java.util.function.Function;
+
+/**
+ * Implementation of a handler for {@link IGnssNmeaListener}.
+ */
+class GnssNmeaProvider extends GnssListenerMultiplexer<Void, IGnssNmeaListener, Void> implements
+        GnssNative.BaseCallbacks, GnssNative.NmeaCallbacks {
+
+    private final AppOpsHelper mAppOpsHelper;
+    private final GnssNative mGnssNative;
+
+    // preallocated to avoid memory allocation in onReportNmea()
+    private final byte[] mNmeaBuffer = new byte[120];
+
+    GnssNmeaProvider(Injector injector, GnssNative gnssNative) {
+        super(injector);
+
+        mAppOpsHelper = injector.getAppOpsHelper();
+        mGnssNative = gnssNative;
+
+        mGnssNative.addBaseCallbacks(this);
+        mGnssNative.addNmeaCallbacks(this);
+    }
+
+    @Override
+    public void addListener(CallerIdentity identity, IGnssNmeaListener listener) {
+        super.addListener(identity, listener);
+    }
+
+    @Override
+    protected boolean registerWithService(Void ignored,
+            Collection<GnssListenerRegistration> registrations) {
+        if (D) {
+            Log.d(TAG, "starting gnss nmea messages");
+        }
+        return true;
+    }
+
+    @Override
+    protected void unregisterWithService() {
+        if (D) {
+            Log.d(TAG, "stopping gnss nmea messages");
+        }
+    }
+
+    @Override
+    public void onHalRestarted() {
+        resetService();
+    }
+
+    @Override
+    public void onReportNmea(long timestamp) {
+        deliverToListeners(
+                new Function<GnssListenerRegistration,
+                        ListenerExecutor.ListenerOperation<IGnssNmeaListener>>() {
+
+                    // only read in the nmea string if we need to
+                    private @Nullable String mNmea;
+
+                    @Override
+                    public ListenerExecutor.ListenerOperation<IGnssNmeaListener> apply(
+                            GnssListenerRegistration registration) {
+                        if (mAppOpsHelper.noteOpNoThrow(AppOpsManager.OP_FINE_LOCATION,
+                                registration.getIdentity())) {
+                            if (mNmea == null) {
+                                int length = mGnssNative.readNmea(mNmeaBuffer,
+                                        mNmeaBuffer.length);
+                                mNmea = new String(mNmeaBuffer, 0, length);
+                            }
+                            return listener -> listener.onNmeaReceived(timestamp, mNmea);
+                        } else {
+                            return null;
+                        }
+                    }
+                });
+    }
+}
diff --git a/services/core/java/com/android/server/location/gnss/GnssPowerIndicationProvider.java b/services/core/java/com/android/server/location/gnss/GnssPowerIndicationProvider.java
deleted file mode 100644
index 5941a33..0000000
--- a/services/core/java/com/android/server/location/gnss/GnssPowerIndicationProvider.java
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * 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.location.gnss;
-
-import static android.hardware.gnss.IGnssPowerIndicationCallback.CAPABILITY_MULTIBAND_ACQUISITION;
-import static android.hardware.gnss.IGnssPowerIndicationCallback.CAPABILITY_MULTIBAND_TRACKING;
-import static android.hardware.gnss.IGnssPowerIndicationCallback.CAPABILITY_OTHER_MODES;
-import static android.hardware.gnss.IGnssPowerIndicationCallback.CAPABILITY_SINGLEBAND_ACQUISITION;
-import static android.hardware.gnss.IGnssPowerIndicationCallback.CAPABILITY_SINGLEBAND_TRACKING;
-import static android.hardware.gnss.IGnssPowerIndicationCallback.CAPABILITY_TOTAL;
-
-import android.util.Log;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-
-/**
- * Manages GNSS Power Indication operations.
- */
-class GnssPowerIndicationProvider {
-    private static final String TAG = "GnssPowerIndPdr";
-    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
-    private volatile int mCapabilities;
-    private GnssPowerStats mGnssPowerStats;
-
-    /**
-     * Handles GNSS Power Indication capabilities update from the GNSS HAL callback.
-     */
-    public void onCapabilitiesUpdated(int capabilities) {
-        mCapabilities = capabilities;
-    }
-
-    public void onGnssPowerStatsAvailable(GnssPowerStats powerStats) {
-        if (DEBUG) {
-            Log.d(TAG, "onGnssPowerStatsAvailable: " + powerStats.toString());
-        }
-        powerStats.validate();
-        mGnssPowerStats = powerStats;
-    }
-
-    /**
-     * Returns the GNSS Power Indication specific capabilities.
-     */
-    public int getCapabilities() {
-        return mCapabilities;
-    }
-
-    /**
-     * Requests the GNSS HAL to report {@link GnssPowerStats}.
-     */
-    public static void requestPowerStats() {
-        native_request_power_stats();
-    }
-
-    private boolean hasCapability(int capability) {
-        return (mCapabilities & capability) != 0;
-    }
-
-    /**
-     * Dump info for debugging.
-     */
-    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        if (mGnssPowerStats == null) {
-            return;
-        }
-        pw.print("GnssPowerStats[");
-        if (mGnssPowerStats.hasElapsedRealtimeNanos()) {
-            pw.print("ElapsedRealtime=" + mGnssPowerStats.getElapsedRealtimeNanos());
-        }
-        if (mGnssPowerStats.hasElapsedRealtimeUncertaintyNanos()) {
-            pw.print(", ElapsedRealtimeUncertaintyNanos="
-                    + mGnssPowerStats.getElapsedRealtimeUncertaintyNanos());
-        }
-        if (hasCapability(CAPABILITY_TOTAL)) {
-            pw.print(", TotalEnergyMilliJoule=" + mGnssPowerStats.getTotalEnergyMilliJoule());
-        }
-        if (hasCapability(CAPABILITY_SINGLEBAND_TRACKING)) {
-            pw.print(", SinglebandTrackingModeEnergyMilliJoule="
-                    + mGnssPowerStats.getSinglebandTrackingModeEnergyMilliJoule());
-        }
-        if (hasCapability(CAPABILITY_MULTIBAND_TRACKING)) {
-            pw.print(", MultibandTrackingModeEnergyMilliJoule="
-                    + mGnssPowerStats.getMultibandTrackingModeEnergyMilliJoule());
-        }
-        if (hasCapability(CAPABILITY_SINGLEBAND_ACQUISITION)) {
-            pw.print(", SinglebandAcquisitionModeEnergyMilliJoule="
-                    + mGnssPowerStats.getSinglebandAcquisitionModeEnergyMilliJoule());
-        }
-        if (hasCapability(CAPABILITY_MULTIBAND_ACQUISITION)) {
-            pw.print(", MultibandAcquisitionModeEnergyMilliJoule="
-                    + mGnssPowerStats.getMultibandAcquisitionModeEnergyMilliJoule());
-        }
-        if (hasCapability(CAPABILITY_OTHER_MODES)) {
-            pw.print(", OtherModesEnergyMilliJoule=[");
-            double[] otherModes = mGnssPowerStats.getOtherModesEnergyMilliJoule();
-            for (int i = 0; i < otherModes.length; i++) {
-                pw.print(otherModes[i]);
-                if (i < otherModes.length - 1) {
-                    pw.print(", ");
-                }
-            }
-            pw.print("] ");
-        }
-        pw.println(']');
-    }
-
-    private static native void native_request_power_stats();
-}
diff --git a/services/core/java/com/android/server/location/gnss/GnssPowerStats.java b/services/core/java/com/android/server/location/gnss/GnssPowerStats.java
index b924d1f..924ffe1 100644
--- a/services/core/java/com/android/server/location/gnss/GnssPowerStats.java
+++ b/services/core/java/com/android/server/location/gnss/GnssPowerStats.java
@@ -19,13 +19,23 @@
 import static android.hardware.gnss.ElapsedRealtime.HAS_TIMESTAMP_NS;
 import static android.hardware.gnss.ElapsedRealtime.HAS_TIME_UNCERTAINTY_NS;
 
+import static java.util.concurrent.TimeUnit.NANOSECONDS;
+
+import android.location.GnssCapabilities;
+import android.util.IndentingPrintWriter;
+import android.util.TimeUtils;
+
 import com.android.internal.util.Preconditions;
+import com.android.server.location.gnss.hal.GnssNative.GnssRealtimeFlags;
+
+import java.io.FileDescriptor;
 
 /**
  * Represents Cumulative GNSS power statistics since boot.
  */
-class GnssPowerStats {
-    private final int mElapsedRealtimeFlags;
+public class GnssPowerStats {
+
+    private final @GnssRealtimeFlags int mElapsedRealtimeFlags;
     private final long mElapsedRealtimeNanos;
     private final double mElapsedRealtimeUncertaintyNanos;
     private final double mTotalEnergyMilliJoule;
@@ -35,7 +45,7 @@
     private final double mMultibandAcquisitionModeEnergyMilliJoule;
     private final double[] mOtherModesEnergyMilliJoule;
 
-    GnssPowerStats(int elapsedRealtimeFlags,
+    public GnssPowerStats(@GnssRealtimeFlags int elapsedRealtimeFlags,
             long elapsedRealtimeNanos,
             double elapsedRealtimeUncertaintyNanos,
             double totalEnergyMilliJoule,
@@ -131,4 +141,51 @@
     public void validate() {
         Preconditions.checkArgument(hasElapsedRealtimeNanos());
     }
+
+    /**
+     * Dumps power stat information filtered by the given capabilities.
+     */
+    public void dump(FileDescriptor fd, IndentingPrintWriter ipw, String[] args,
+            GnssCapabilities capabilities) {
+        if (hasElapsedRealtimeNanos()) {
+            ipw.print("time: ");
+            ipw.print(TimeUtils.formatRealtime(NANOSECONDS.toMillis(mElapsedRealtimeNanos)));
+            if (hasElapsedRealtimeUncertaintyNanos() && mElapsedRealtimeUncertaintyNanos != 0) {
+                ipw.print(" +/- ");
+                ipw.print(NANOSECONDS.toMillis((long) mElapsedRealtimeUncertaintyNanos));
+            }
+        }
+        if (capabilities.hasPowerTotal()) {
+            ipw.print("total power: ");
+            ipw.print(mTotalEnergyMilliJoule);
+            ipw.println("mJ");
+        }
+        if (capabilities.hasPowerSinglebandTracking()) {
+            ipw.print("single-band tracking power: ");
+            ipw.print(mSinglebandTrackingModeEnergyMilliJoule);
+            ipw.println("mJ");
+        }
+        if (capabilities.hasPowerMultibandTracking()) {
+            ipw.print("multi-band tracking power: ");
+            ipw.print(mMultibandTrackingModeEnergyMilliJoule);
+            ipw.println("mJ");
+        }
+        if (capabilities.hasPowerSinglebandAcquisition()) {
+            ipw.print("single-band acquisition power: ");
+            ipw.print(mSinglebandAcquisitionModeEnergyMilliJoule);
+            ipw.println("mJ");
+        }
+        if (capabilities.hasPowerMultibandAcquisition()) {
+            ipw.print("multi-band acquisition power: ");
+            ipw.print(mMultibandAcquisitionModeEnergyMilliJoule);
+            ipw.println("mJ");
+        }
+        if (capabilities.hasPowerOtherModes()) {
+            for (int i = 1; i <= mOtherModesEnergyMilliJoule.length; i++) {
+                ipw.print("other mode [" + i + "] power: ");
+                ipw.print(mOtherModesEnergyMilliJoule[i]);
+                ipw.println("mJ");
+            }
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/location/gnss/GnssStatusProvider.java b/services/core/java/com/android/server/location/gnss/GnssStatusProvider.java
index 49aa235..1eb1618 100644
--- a/services/core/java/com/android/server/location/gnss/GnssStatusProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssStatusProvider.java
@@ -27,6 +27,7 @@
 import android.stats.location.LocationStatsEnums;
 import android.util.Log;
 
+import com.android.server.location.gnss.hal.GnssNative;
 import com.android.server.location.injector.AppOpsHelper;
 import com.android.server.location.injector.Injector;
 import com.android.server.location.injector.LocationUsageLogger;
@@ -36,15 +37,23 @@
 /**
  * Implementation of a handler for {@link IGnssStatusListener}.
  */
-public class GnssStatusProvider extends GnssListenerMultiplexer<Void, IGnssStatusListener, Void> {
+public class GnssStatusProvider extends
+        GnssListenerMultiplexer<Void, IGnssStatusListener, Void> implements
+        GnssNative.BaseCallbacks, GnssNative.StatusCallbacks, GnssNative.SvStatusCallbacks {
 
     private final AppOpsHelper mAppOpsHelper;
     private final LocationUsageLogger mLogger;
 
-    public GnssStatusProvider(Injector injector) {
+    private boolean mIsNavigating = false;
+
+    public GnssStatusProvider(Injector injector, GnssNative gnssNative) {
         super(injector);
         mAppOpsHelper = injector.getAppOpsHelper();
         mLogger = injector.getLocationUsageLogger();
+
+        gnssNative.addBaseCallbacks(this);
+        gnssNative.addStatusCallbacks(this);
+        gnssNative.addSvStatusCallbacks(this);
     }
 
     @Override
@@ -95,30 +104,46 @@
                 registration.isForeground());
     }
 
-    /**
-     * Called by GnssLocationProvider.
-     */
-    public void onStatusChanged(boolean isNavigating) {
-        if (isNavigating) {
-            deliverToListeners(IGnssStatusListener::onGnssStarted);
-        } else {
-            deliverToListeners(IGnssStatusListener::onGnssStopped);
+    @Override
+    public void onHalRestarted() {
+        resetService();
+    }
+
+    @Override
+    public void onReportStatus(@GnssNative.StatusCallbacks.GnssStatusValue int gnssStatus) {
+        boolean isNavigating;
+        switch (gnssStatus) {
+            case GNSS_STATUS_SESSION_BEGIN:
+                isNavigating = true;
+                break;
+            case GNSS_STATUS_SESSION_END:
+                // fall through
+            case GNSS_STATUS_ENGINE_OFF:
+                isNavigating = false;
+                break;
+            default:
+                isNavigating = mIsNavigating;
+        }
+
+        if (isNavigating != mIsNavigating) {
+            mIsNavigating = isNavigating;
+            if (isNavigating) {
+                deliverToListeners(IGnssStatusListener::onGnssStarted);
+            } else {
+                deliverToListeners(IGnssStatusListener::onGnssStopped);
+            }
         }
     }
 
-    /**
-     * Called by GnssLocationProvider.
-     */
-    public void onFirstFix(int ttff) {
+    @Override
+    public void onReportFirstFix(int ttff) {
         deliverToListeners(listener -> {
             listener.onFirstFix(ttff);
         });
     }
 
-    /**
-     * Called by GnssLocationProvider.
-     */
-    public void onSvStatusChanged(GnssStatus gnssStatus) {
+    @Override
+    public void onReportSvStatus(GnssStatus gnssStatus) {
         deliverToListeners(registration -> {
             if (mAppOpsHelper.noteOpNoThrow(AppOpsManager.OP_FINE_LOCATION,
                     registration.getIdentity())) {
@@ -128,18 +153,4 @@
             }
         });
     }
-
-    /**
-     * Called by GnssLocationProvider.
-     */
-    public void onNmeaReceived(long timestamp, String nmea) {
-        deliverToListeners(registration -> {
-            if (mAppOpsHelper.noteOpNoThrow(AppOpsManager.OP_FINE_LOCATION,
-                    registration.getIdentity())) {
-                return listener -> listener.onNmeaReceived(timestamp, nmea);
-            } else {
-                return null;
-            }
-        });
-    }
 }
diff --git a/services/core/java/com/android/server/location/gnss/hal/GnssNative.java b/services/core/java/com/android/server/location/gnss/hal/GnssNative.java
new file mode 100644
index 0000000..402e84b
--- /dev/null
+++ b/services/core/java/com/android/server/location/gnss/hal/GnssNative.java
@@ -0,0 +1,1504 @@
+/*
+ * 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.location.gnss.hal;
+
+import static com.android.server.location.gnss.GnssManagerService.TAG;
+
+import android.annotation.IntDef;
+import android.annotation.Nullable;
+import android.location.GnssAntennaInfo;
+import android.location.GnssCapabilities;
+import android.location.GnssMeasurementCorrections;
+import android.location.GnssMeasurementsEvent;
+import android.location.GnssNavigationMessage;
+import android.location.GnssStatus;
+import android.location.Location;
+import android.os.SystemClock;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.Preconditions;
+import com.android.server.FgThread;
+import com.android.server.location.gnss.GnssConfiguration;
+import com.android.server.location.gnss.GnssPowerStats;
+import com.android.server.location.injector.EmergencyHelper;
+import com.android.server.location.injector.Injector;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Entry point for most GNSS HAL commands and callbacks.
+ */
+public class GnssNative {
+
+    // IMPORTANT - must match GnssPositionMode enum in IGnss.hal
+    public static final int GNSS_POSITION_MODE_STANDALONE = 0;
+    public static final int GNSS_POSITION_MODE_MS_BASED = 1;
+    public static final int GNSS_POSITION_MODE_MS_ASSISTED = 2;
+
+    @IntDef(prefix = "GNSS_POSITION_MODE_", value = {GNSS_POSITION_MODE_STANDALONE,
+            GNSS_POSITION_MODE_MS_BASED, GNSS_POSITION_MODE_MS_ASSISTED})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface GnssPositionMode {}
+
+    // IMPORTANT - must match GnssPositionRecurrence enum in IGnss.hal
+    public static final int GNSS_POSITION_RECURRENCE_PERIODIC = 0;
+    public static final int GNSS_POSITION_RECURRENCE_SINGLE = 1;
+
+    @IntDef(prefix = "GNSS_POSITION_RECURRENCE_", value = {GNSS_POSITION_RECURRENCE_PERIODIC,
+            GNSS_POSITION_RECURRENCE_SINGLE})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface GnssPositionRecurrence {}
+
+    // IMPORTANT - must match the GnssLocationFlags enum in types.hal
+    public static final int GNSS_LOCATION_HAS_LAT_LONG = 1;
+    public static final int GNSS_LOCATION_HAS_ALTITUDE = 2;
+    public static final int GNSS_LOCATION_HAS_SPEED = 4;
+    public static final int GNSS_LOCATION_HAS_BEARING = 8;
+    public static final int GNSS_LOCATION_HAS_HORIZONTAL_ACCURACY = 16;
+    public static final int GNSS_LOCATION_HAS_VERTICAL_ACCURACY = 32;
+    public static final int GNSS_LOCATION_HAS_SPEED_ACCURACY = 64;
+    public static final int GNSS_LOCATION_HAS_BEARING_ACCURACY = 128;
+
+    @IntDef(flag = true, prefix = "GNSS_LOCATION_", value = {GNSS_LOCATION_HAS_LAT_LONG,
+            GNSS_LOCATION_HAS_ALTITUDE, GNSS_LOCATION_HAS_SPEED, GNSS_LOCATION_HAS_BEARING,
+            GNSS_LOCATION_HAS_HORIZONTAL_ACCURACY, GNSS_LOCATION_HAS_VERTICAL_ACCURACY,
+            GNSS_LOCATION_HAS_SPEED_ACCURACY, GNSS_LOCATION_HAS_BEARING_ACCURACY})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface GnssLocationFlags {}
+
+    // IMPORTANT - must match the ElapsedRealtimeFlags enum in types.hal
+    public static final int GNSS_REALTIME_HAS_TIMESTAMP_NS = 1;
+    public static final int GNSS_REALTIME_HAS_TIME_UNCERTAINTY_NS = 2;
+
+    @IntDef(flag = true, value = {GNSS_REALTIME_HAS_TIMESTAMP_NS,
+            GNSS_REALTIME_HAS_TIME_UNCERTAINTY_NS})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface GnssRealtimeFlags {}
+
+    // IMPORTANT - must match the GnssAidingData enum in IGnss.hal
+    public static final int GNSS_AIDING_TYPE_EPHEMERIS = 0x0001;
+    public static final int GNSS_AIDING_TYPE_ALMANAC = 0x0002;
+    public static final int GNSS_AIDING_TYPE_POSITION = 0x0004;
+    public static final int GNSS_AIDING_TYPE_TIME = 0x0008;
+    public static final int GNSS_AIDING_TYPE_IONO = 0x0010;
+    public static final int GNSS_AIDING_TYPE_UTC = 0x0020;
+    public static final int GNSS_AIDING_TYPE_HEALTH = 0x0040;
+    public static final int GNSS_AIDING_TYPE_SVDIR = 0x0080;
+    public static final int GNSS_AIDING_TYPE_SVSTEER = 0x0100;
+    public static final int GNSS_AIDING_TYPE_SADATA = 0x0200;
+    public static final int GNSS_AIDING_TYPE_RTI = 0x0400;
+    public static final int GNSS_AIDING_TYPE_CELLDB_INFO = 0x8000;
+    public static final int GNSS_AIDING_TYPE_ALL = 0xFFFF;
+
+    @IntDef(flag = true, prefix = "GNSS_AIDING_", value = {GNSS_AIDING_TYPE_EPHEMERIS,
+            GNSS_AIDING_TYPE_ALMANAC, GNSS_AIDING_TYPE_POSITION, GNSS_AIDING_TYPE_TIME,
+            GNSS_AIDING_TYPE_IONO, GNSS_AIDING_TYPE_UTC, GNSS_AIDING_TYPE_HEALTH,
+            GNSS_AIDING_TYPE_SVDIR, GNSS_AIDING_TYPE_SVSTEER, GNSS_AIDING_TYPE_SADATA,
+            GNSS_AIDING_TYPE_RTI, GNSS_AIDING_TYPE_CELLDB_INFO, GNSS_AIDING_TYPE_ALL})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface GnssAidingTypeFlags {}
+
+    // IMPORTANT - must match OEM definitions, this isn't part of a hal for some reason
+    public static final int AGPS_REF_LOCATION_TYPE_GSM_CELLID = 1;
+    public static final int AGPS_REF_LOCATION_TYPE_UMTS_CELLID = 2;
+
+    @IntDef(prefix = "AGPS_REF_LOCATION_TYPE_", value = {AGPS_REF_LOCATION_TYPE_GSM_CELLID,
+            AGPS_REF_LOCATION_TYPE_UMTS_CELLID})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface AgpsReferenceLocationType {}
+
+    // IMPORTANT - must match OEM definitions, this isn't part of a hal for some reason
+    public static final int AGPS_SETID_TYPE_NONE = 0;
+    public static final int AGPS_SETID_TYPE_IMSI = 1;
+    public static final int AGPS_SETID_TYPE_MSISDN = 2;
+
+    @IntDef(prefix = "AGPS_SETID_TYPE_", value = {AGPS_SETID_TYPE_NONE, AGPS_SETID_TYPE_IMSI,
+            AGPS_SETID_TYPE_MSISDN})
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface AgpsSetIdType {}
+
+    /** Callbacks relevant to the entire HAL. */
+    public interface BaseCallbacks {
+        default void onHalStarted() {}
+        void onHalRestarted();
+        default void onCapabilitiesChanged(GnssCapabilities oldCapabilities,
+                GnssCapabilities newCapabilities) {}
+    }
+
+    /** Callbacks for status events. */
+    public interface StatusCallbacks {
+
+        // IMPORTANT - must match GnssStatusValue enum in IGnssCallback.hal
+        int GNSS_STATUS_NONE = 0;
+        int GNSS_STATUS_SESSION_BEGIN = 1;
+        int GNSS_STATUS_SESSION_END = 2;
+        int GNSS_STATUS_ENGINE_ON = 3;
+        int GNSS_STATUS_ENGINE_OFF = 4;
+
+        @IntDef(prefix = "GNSS_STATUS_", value = {GNSS_STATUS_NONE, GNSS_STATUS_SESSION_BEGIN,
+                GNSS_STATUS_SESSION_END, GNSS_STATUS_ENGINE_ON, GNSS_STATUS_ENGINE_OFF})
+        @Retention(RetentionPolicy.SOURCE)
+        @interface GnssStatusValue {}
+
+        void onReportStatus(@GnssStatusValue int status);
+        void onReportFirstFix(int ttff);
+    }
+
+    /** Callbacks for SV status events. */
+    public interface SvStatusCallbacks {
+        void onReportSvStatus(GnssStatus gnssStatus);
+    }
+
+    /** Callbacks for NMEA events. */
+    public interface NmeaCallbacks {
+        void onReportNmea(long timestamp);
+    }
+
+    /** Callbacks for location events. */
+    public interface LocationCallbacks {
+        void onReportLocation(boolean hasLatLong, Location location);
+        void onReportLocations(Location[] locations);
+    }
+
+    /** Callbacks for measurement events. */
+    public interface MeasurementCallbacks {
+        void onReportMeasurements(GnssMeasurementsEvent event);
+    }
+
+    /** Callbacks for antenna info events. */
+    public interface AntennaInfoCallbacks {
+        void onReportAntennaInfo(List<GnssAntennaInfo> antennaInfos);
+    }
+
+    /** Callbacks for navigation message events. */
+    public interface NavigationMessageCallbacks {
+        void onReportNavigationMessage(GnssNavigationMessage event);
+    }
+
+    /** Callbacks for geofence events. */
+    public interface GeofenceCallbacks {
+
+        // IMPORTANT - must match GeofenceTransition enum in IGnssGeofenceCallback.hal
+        int GEOFENCE_TRANSITION_ENTERED = 1 << 0L;
+        int GEOFENCE_TRANSITION_EXITED = 1 << 1L;
+        int GEOFENCE_TRANSITION_UNCERTAIN = 1 << 2L;
+
+        @IntDef(prefix = "GEOFENCE_TRANSITION_", value = {GEOFENCE_TRANSITION_ENTERED,
+                GEOFENCE_TRANSITION_EXITED, GEOFENCE_TRANSITION_UNCERTAIN})
+        @Retention(RetentionPolicy.SOURCE)
+        @interface GeofenceTransition {}
+
+        // IMPORTANT - must match GeofenceAvailability enum in IGnssGeofenceCallback.hal
+        int GEOFENCE_AVAILABILITY_UNAVAILABLE = 1 << 0L;
+        int GEOFENCE_AVAILABILITY_AVAILABLE = 1 << 1L;
+
+        @IntDef(prefix = "GEOFENCE_AVAILABILITY_", value = {GEOFENCE_AVAILABILITY_UNAVAILABLE,
+                GEOFENCE_AVAILABILITY_AVAILABLE})
+        @Retention(RetentionPolicy.SOURCE)
+        @interface GeofenceAvailability {}
+
+        // IMPORTANT - must match GeofenceStatus enum in IGnssGeofenceCallback.hal
+        int GEOFENCE_STATUS_OPERATION_SUCCESS = 0;
+        int GEOFENCE_STATUS_ERROR_TOO_MANY_GEOFENCES = 100;
+        int GEOFENCE_STATUS_ERROR_ID_EXISTS = -101;
+        int GEOFENCE_STATUS_ERROR_ID_UNKNOWN = -102;
+        int GEOFENCE_STATUS_ERROR_INVALID_TRANSITION = -103;
+        int GEOFENCE_STATUS_ERROR_GENERIC = -149;
+
+        @IntDef(prefix = "GEOFENCE_STATUS_", value = {GEOFENCE_STATUS_OPERATION_SUCCESS,
+                GEOFENCE_STATUS_ERROR_TOO_MANY_GEOFENCES, GEOFENCE_STATUS_ERROR_ID_EXISTS,
+                GEOFENCE_STATUS_ERROR_ID_UNKNOWN, GEOFENCE_STATUS_ERROR_INVALID_TRANSITION,
+                GEOFENCE_STATUS_ERROR_GENERIC})
+        @Retention(RetentionPolicy.SOURCE)
+        @interface GeofenceStatus {}
+
+        void onReportGeofenceTransition(int geofenceId, Location location,
+                @GeofenceTransition int transition, long timestamp);
+        void onReportGeofenceStatus(@GeofenceAvailability int availabilityStatus,
+                Location location);
+        void onReportGeofenceAddStatus(int geofenceId, @GeofenceStatus int status);
+        void onReportGeofenceRemoveStatus(int geofenceId, @GeofenceStatus int status);
+        void onReportGeofencePauseStatus(int geofenceId, @GeofenceStatus int status);
+        void onReportGeofenceResumeStatus(int geofenceId, @GeofenceStatus int status);
+    }
+
+    /** Callbacks for the HAL requesting time. */
+    public interface TimeCallbacks {
+        void onRequestUtcTime();
+    }
+
+    /** Callbacks for the HAL requesting locations. */
+    public interface LocationRequestCallbacks {
+        void onRequestLocation(boolean independentFromGnss, boolean isUserEmergency);
+        void onRequestRefLocation();
+    }
+
+    /** Callbacks for HAL requesting PSDS download. */
+    public interface PsdsCallbacks {
+        void onRequestPsdsDownload(int psdsType);
+    }
+
+    /** Callbacks for AGPS functionality. */
+    public interface AGpsCallbacks {
+
+        // IMPORTANT - must match OEM definitions, this isn't part of a hal for some reason
+        int AGPS_REQUEST_SETID_IMSI = 1 << 0L;
+        int AGPS_REQUEST_SETID_MSISDN = 1 << 1L;
+
+        @IntDef(flag = true, prefix = "AGPS_REQUEST_SETID_", value = {AGPS_REQUEST_SETID_IMSI,
+                AGPS_REQUEST_SETID_MSISDN})
+        @Retention(RetentionPolicy.SOURCE)
+        @interface AgpsSetIdFlags {}
+
+        void onReportAGpsStatus(int agpsType, int agpsStatus, byte[] suplIpAddr);
+        void onRequestSetID(@AgpsSetIdFlags int flags);
+    }
+
+    /** Callbacks for notifications. */
+    public interface NotificationCallbacks {
+        void onReportNiNotification(int notificationId, int niType, int notifyFlags,
+                int timeout, int defaultResponse, String requestorId, String text,
+                int requestorIdEncoding, int textEncoding);
+        void onReportNfwNotification(String proxyAppPackageName, byte protocolStack,
+                String otherProtocolStackName, byte requestor, String requestorId,
+                byte responseType, boolean inEmergencyMode, boolean isCachedLocation);
+    }
+
+    // set lower than the current ITAR limit of 600m/s to allow this to trigger even if GPS HAL
+    // stops output right at 600m/s, depriving this of the information of a device that reaches
+    // greater than 600m/s, and higher than the speed of sound to avoid impacting most use cases.
+    private static final float ITAR_SPEED_LIMIT_METERS_PER_SECOND = 400.0f;
+
+    /**
+     * Indicates that this method is a native entry point. Useful purely for IDEs which can
+     * understand entry points, and thus eliminate incorrect warnings about methods not used.
+     */
+    @Target(ElementType.METHOD)
+    @Retention(RetentionPolicy.SOURCE)
+    private @interface NativeEntryPoint {}
+
+    @GuardedBy("GnssNative.class")
+    private static GnssHal sGnssHal;
+
+    @GuardedBy("GnssNative.class")
+    private static boolean sGnssHalInitialized;
+
+    @GuardedBy("GnssNative.class")
+    private static GnssNative sInstance;
+
+    /**
+     * Sets GnssHal instance to use for testing.
+     */
+    @VisibleForTesting
+    public static synchronized void setGnssHalForTest(GnssHal gnssHal) {
+        sGnssHal = Objects.requireNonNull(gnssHal);
+        sGnssHalInitialized = false;
+        sInstance = null;
+    }
+
+    private static synchronized void initializeHal() {
+        if (!sGnssHalInitialized) {
+            if (sGnssHal == null) {
+                sGnssHal = new GnssHal();
+            }
+            sGnssHal.classInitOnce();
+            sGnssHalInitialized = true;
+        }
+    }
+
+    /**
+     * Returns true if GNSS is supported on this device. If true, then
+     * {@link #create(Injector, GnssConfiguration)} may be invoked.
+     */
+    public static synchronized boolean isSupported() {
+        initializeHal();
+        return sGnssHal.isSupported();
+    }
+
+    /**
+     * Creates a new instance of GnssNative. Should only be invoked if {@link #isSupported()} is
+     * true. May only be invoked once.
+     */
+    public static synchronized GnssNative create(Injector injector,
+            GnssConfiguration configuration) {
+        // side effect - ensures initialization
+        Preconditions.checkState(isSupported());
+        Preconditions.checkState(sInstance == null);
+        return (sInstance = new GnssNative(sGnssHal, injector, configuration));
+    }
+
+    private final GnssHal mGnssHal;
+    private final EmergencyHelper mEmergencyHelper;
+    private final GnssConfiguration mConfiguration;
+
+    // these callbacks may have multiple implementations
+    private BaseCallbacks[] mBaseCallbacks = new BaseCallbacks[0];
+    private StatusCallbacks[] mStatusCallbacks = new StatusCallbacks[0];
+    private SvStatusCallbacks[] mSvStatusCallbacks = new SvStatusCallbacks[0];
+    private NmeaCallbacks[] mNmeaCallbacks = new NmeaCallbacks[0];
+    private LocationCallbacks[] mLocationCallbacks = new LocationCallbacks[0];
+    private MeasurementCallbacks[] mMeasurementCallbacks = new MeasurementCallbacks[0];
+    private AntennaInfoCallbacks[] mAntennaInfoCallbacks = new AntennaInfoCallbacks[0];
+    private NavigationMessageCallbacks[] mNavigationMessageCallbacks =
+            new NavigationMessageCallbacks[0];
+
+    // these callbacks may only have a single implementation
+    private GeofenceCallbacks mGeofenceCallbacks;
+    private TimeCallbacks mTimeCallbacks;
+    private LocationRequestCallbacks mLocationRequestCallbacks;
+    private PsdsCallbacks mPsdsCallbacks;
+    private AGpsCallbacks mAGpsCallbacks;
+    private NotificationCallbacks mNotificationCallbacks;
+
+    private boolean mRegistered;
+
+    private volatile boolean mItarSpeedLimitExceeded;
+
+    private GnssCapabilities mCapabilities = new GnssCapabilities.Builder().build();
+    private @GnssCapabilities.TopHalCapabilityFlags int mTopFlags;
+    private @Nullable GnssPowerStats mPowerStats = null;
+    private int mHardwareYear = 0;
+    private @Nullable String mHardwareModelName = null;
+    private long mStartRealtimeMs = 0;
+    private boolean mHasFirstFix = false;
+
+    private GnssNative(GnssHal gnssHal, Injector injector, GnssConfiguration configuration) {
+        mGnssHal = Objects.requireNonNull(gnssHal);
+        mEmergencyHelper = injector.getEmergencyHelper();
+        mConfiguration = configuration;
+    }
+
+    public void addBaseCallbacks(BaseCallbacks callbacks) {
+        Preconditions.checkState(!mRegistered);
+        mBaseCallbacks = ArrayUtils.appendElement(BaseCallbacks.class, mBaseCallbacks, callbacks);
+    }
+
+    public void addStatusCallbacks(StatusCallbacks callbacks) {
+        Preconditions.checkState(!mRegistered);
+        mStatusCallbacks = ArrayUtils.appendElement(StatusCallbacks.class, mStatusCallbacks,
+                callbacks);
+    }
+
+    public void addSvStatusCallbacks(SvStatusCallbacks callbacks) {
+        Preconditions.checkState(!mRegistered);
+        mSvStatusCallbacks = ArrayUtils.appendElement(SvStatusCallbacks.class, mSvStatusCallbacks,
+                callbacks);
+    }
+
+    public void addNmeaCallbacks(NmeaCallbacks callbacks) {
+        Preconditions.checkState(!mRegistered);
+        mNmeaCallbacks = ArrayUtils.appendElement(NmeaCallbacks.class, mNmeaCallbacks,
+                callbacks);
+    }
+
+    public void addLocationCallbacks(LocationCallbacks callbacks) {
+        Preconditions.checkState(!mRegistered);
+        mLocationCallbacks = ArrayUtils.appendElement(LocationCallbacks.class, mLocationCallbacks,
+                callbacks);
+    }
+
+    public void addMeasurementCallbacks(MeasurementCallbacks callbacks) {
+        Preconditions.checkState(!mRegistered);
+        mMeasurementCallbacks = ArrayUtils.appendElement(MeasurementCallbacks.class,
+                mMeasurementCallbacks, callbacks);
+    }
+
+    public void addAntennaInfoCallbacks(AntennaInfoCallbacks callbacks) {
+        Preconditions.checkState(!mRegistered);
+        mAntennaInfoCallbacks = ArrayUtils.appendElement(AntennaInfoCallbacks.class,
+                mAntennaInfoCallbacks, callbacks);
+    }
+
+    public void addNavigationMessageCallbacks(NavigationMessageCallbacks callbacks) {
+        Preconditions.checkState(!mRegistered);
+        mNavigationMessageCallbacks = ArrayUtils.appendElement(NavigationMessageCallbacks.class,
+                mNavigationMessageCallbacks, callbacks);
+    }
+
+    public void setGeofenceCallbacks(GeofenceCallbacks callbacks) {
+        Preconditions.checkState(!mRegistered);
+        Preconditions.checkState(mGeofenceCallbacks == null);
+        mGeofenceCallbacks = Objects.requireNonNull(callbacks);
+    }
+
+    public void setTimeCallbacks(TimeCallbacks callbacks) {
+        Preconditions.checkState(!mRegistered);
+        Preconditions.checkState(mTimeCallbacks == null);
+        mTimeCallbacks = Objects.requireNonNull(callbacks);
+    }
+
+    public void setLocationRequestCallbacks(LocationRequestCallbacks callbacks) {
+        Preconditions.checkState(!mRegistered);
+        Preconditions.checkState(mLocationRequestCallbacks == null);
+        mLocationRequestCallbacks = Objects.requireNonNull(callbacks);
+    }
+
+    public void setPsdsCallbacks(PsdsCallbacks callbacks) {
+        Preconditions.checkState(!mRegistered);
+        Preconditions.checkState(mPsdsCallbacks == null);
+        mPsdsCallbacks = Objects.requireNonNull(callbacks);
+    }
+
+    public void setAGpsCallbacks(AGpsCallbacks callbacks) {
+        Preconditions.checkState(!mRegistered);
+        Preconditions.checkState(mAGpsCallbacks == null);
+        mAGpsCallbacks = Objects.requireNonNull(callbacks);
+    }
+
+    public void setNotificationCallbacks(NotificationCallbacks callbacks) {
+        Preconditions.checkState(!mRegistered);
+        Preconditions.checkState(mNotificationCallbacks == null);
+        mNotificationCallbacks = Objects.requireNonNull(callbacks);
+    }
+
+    /**
+     * Registers with the HAL and allows callbacks to begin. Once registered with the native HAL,
+     * no more callbacks can be added or set. Must only be called once.
+     */
+    public void register() {
+        Preconditions.checkState(!mRegistered);
+        mRegistered = true;
+
+        initializeGnss(false);
+        Log.i(TAG, "gnss hal started");
+
+        for (int i = 0; i < mBaseCallbacks.length; i++) {
+            mBaseCallbacks[i].onHalStarted();
+        }
+    }
+
+    private void initializeGnss(boolean restart) {
+        Preconditions.checkState(mRegistered);
+        mTopFlags = 0;
+        mGnssHal.initOnce(GnssNative.this, restart);
+
+        // gnss chipset appears to require an init/cleanup cycle on startup in order to properly
+        // initialize - undocumented and no idea why this is the case
+        if (mGnssHal.init()) {
+            mGnssHal.cleanup();
+            Log.i(TAG, "gnss hal initialized");
+        } else {
+            Log.e(TAG, "gnss hal initialization failed");
+        }
+    }
+
+    public GnssConfiguration getConfiguration() {
+        return mConfiguration;
+    }
+
+    /**
+     * Starts up GNSS HAL, and has undocumented side effect of informing HAL that location is
+     * allowed by settings.
+     */
+    public boolean init() {
+        Preconditions.checkState(mRegistered);
+        return mGnssHal.init();
+    }
+
+    /**
+     * Shuts down GNSS HAL, and has undocumented side effect of informing HAL that location is not
+     * allowed by settings.
+     */
+    public void cleanup() {
+        Preconditions.checkState(mRegistered);
+        mGnssHal.cleanup();
+    }
+
+    /**
+     * Returns the latest power stats from the GNSS HAL.
+     */
+    public @Nullable GnssPowerStats getPowerStats() {
+        return mPowerStats;
+    }
+
+    /**
+     * Returns current capabilities of the GNSS HAL.
+     */
+    public GnssCapabilities getCapabilities() {
+        return mCapabilities;
+    }
+
+    /**
+     * Returns hardware year of GNSS chipset.
+     */
+    public int getHardwareYear() {
+        return mHardwareYear;
+    }
+
+    /**
+     * Returns hardware model name of GNSS chipset.
+     */
+    public @Nullable String getHardwareModelName() {
+        return mHardwareModelName;
+    }
+
+    /**
+     * Returns true if the ITAR speed limit is currently being exceeded, and thus location
+     * information may be blocked.
+     */
+    public boolean isItarSpeedLimitExceeded() {
+        return mItarSpeedLimitExceeded;
+    }
+
+    /**
+     * Starts the GNSS HAL.
+     */
+    public boolean start() {
+        Preconditions.checkState(mRegistered);
+        mStartRealtimeMs = SystemClock.elapsedRealtime();
+        mHasFirstFix = false;
+        return mGnssHal.start();
+    }
+
+    /**
+     * Stops the GNSS HAL.
+     */
+    public boolean stop() {
+        Preconditions.checkState(mRegistered);
+        return mGnssHal.stop();
+    }
+
+    /**
+     * Sets the position mode.
+     */
+    public boolean setPositionMode(@GnssPositionMode int mode,
+            @GnssPositionRecurrence int recurrence, int minInterval, int preferredAccuracy,
+            int preferredTime, boolean lowPowerMode) {
+        Preconditions.checkState(mRegistered);
+        return mGnssHal.setPositionMode(mode, recurrence, minInterval, preferredAccuracy,
+                preferredTime, lowPowerMode);
+    }
+
+    /**
+     * Returns a debug string from the GNSS HAL.
+     */
+    public String getInternalState() {
+        Preconditions.checkState(mRegistered);
+        return mGnssHal.getInternalState();
+    }
+
+    /**
+     * Deletes any aiding data specified by the given flags.
+     */
+    public void deleteAidingData(@GnssAidingTypeFlags int flags) {
+        Preconditions.checkState(mRegistered);
+        mGnssHal.deleteAidingData(flags);
+    }
+
+    /**
+     * Reads an NMEA message into the given buffer, returning the number of bytes loaded into the
+     * buffer.
+     */
+    public int readNmea(byte[] buffer, int bufferSize) {
+        Preconditions.checkState(mRegistered);
+        return mGnssHal.readNmea(buffer, bufferSize);
+    }
+
+    /**
+     * Injects location information into the GNSS HAL.
+     */
+    public void injectLocation(Location location) {
+        Preconditions.checkState(mRegistered);
+        if (location.hasAccuracy()) {
+            mGnssHal.injectLocation(location.getLatitude(), location.getLongitude(),
+                    location.getAccuracy());
+        }
+    }
+
+    /**
+     * Injects a location into the GNSS HAL in response to a HAL request for location.
+     */
+    public void injectBestLocation(Location location) {
+        Preconditions.checkState(mRegistered);
+
+        int gnssLocationFlags = GNSS_LOCATION_HAS_LAT_LONG
+                | (location.hasAltitude() ? GNSS_LOCATION_HAS_ALTITUDE : 0)
+                | (location.hasSpeed() ? GNSS_LOCATION_HAS_SPEED : 0)
+                | (location.hasBearing() ? GNSS_LOCATION_HAS_BEARING : 0)
+                | (location.hasAccuracy() ? GNSS_LOCATION_HAS_HORIZONTAL_ACCURACY : 0)
+                | (location.hasVerticalAccuracy() ? GNSS_LOCATION_HAS_VERTICAL_ACCURACY : 0)
+                | (location.hasSpeedAccuracy() ? GNSS_LOCATION_HAS_SPEED_ACCURACY : 0)
+                | (location.hasBearingAccuracy() ? GNSS_LOCATION_HAS_BEARING_ACCURACY : 0);
+
+        double latitudeDegrees = location.getLatitude();
+        double longitudeDegrees = location.getLongitude();
+        double altitudeMeters = location.getAltitude();
+        float speedMetersPerSec = location.getSpeed();
+        float bearingDegrees = location.getBearing();
+        float horizontalAccuracyMeters = location.getAccuracy();
+        float verticalAccuracyMeters = location.getVerticalAccuracyMeters();
+        float speedAccuracyMetersPerSecond = location.getSpeedAccuracyMetersPerSecond();
+        float bearingAccuracyDegrees = location.getBearingAccuracyDegrees();
+        long timestamp = location.getTime();
+
+        int elapsedRealtimeFlags = GNSS_REALTIME_HAS_TIMESTAMP_NS
+                | (location.hasElapsedRealtimeUncertaintyNanos()
+                ? GNSS_REALTIME_HAS_TIME_UNCERTAINTY_NS : 0);
+        long elapsedRealtimeNanos = location.getElapsedRealtimeNanos();
+        double elapsedRealtimeUncertaintyNanos = location.getElapsedRealtimeUncertaintyNanos();
+
+        mGnssHal.injectBestLocation(gnssLocationFlags, latitudeDegrees, longitudeDegrees,
+                altitudeMeters, speedMetersPerSec, bearingDegrees, horizontalAccuracyMeters,
+                verticalAccuracyMeters, speedAccuracyMetersPerSecond, bearingAccuracyDegrees,
+                timestamp, elapsedRealtimeFlags, elapsedRealtimeNanos,
+                elapsedRealtimeUncertaintyNanos);
+    }
+
+    /**
+     * Injects time information into the GNSS HAL.
+     */
+    public void injectTime(long time, long timeReference, int uncertainty) {
+        Preconditions.checkState(mRegistered);
+        mGnssHal.injectTime(time, timeReference, uncertainty);
+    }
+
+    /**
+     * Returns true if navigation message collection is supported.
+     */
+    public boolean isNavigationMessageCollectionSupported() {
+        Preconditions.checkState(mRegistered);
+        return mGnssHal.isNavigationMessageCollectionSupported();
+    }
+
+    /**
+     * Starts navigation message collection.
+     */
+    public boolean startNavigationMessageCollection() {
+        Preconditions.checkState(mRegistered);
+        return mGnssHal.startNavigationMessageCollection();
+    }
+
+    /**
+     * Stops navigation message collection.
+     */
+    public boolean stopNavigationMessageCollection() {
+        Preconditions.checkState(mRegistered);
+        return mGnssHal.stopNavigationMessageCollection();
+    }
+
+    /**
+     * Returns true if antenna info listening is supported.
+     */
+    public boolean isAntennaInfoListeningSupported() {
+        Preconditions.checkState(mRegistered);
+        return mGnssHal.isAntennaInfoListeningSupported();
+    }
+
+    /**
+     * Starts antenna info listening.
+     */
+    public boolean startAntennaInfoListening() {
+        Preconditions.checkState(mRegistered);
+        return mGnssHal.startAntennaInfoListening();
+    }
+
+    /**
+     * Stops antenna info listening.
+     */
+    public boolean stopAntennaInfoListening() {
+        Preconditions.checkState(mRegistered);
+        return mGnssHal.stopAntennaInfoListening();
+    }
+
+    /**
+     * Returns true if measurement collection is supported.
+     */
+    public boolean isMeasurementSupported() {
+        Preconditions.checkState(mRegistered);
+        return mGnssHal.isMeasurementSupported();
+    }
+
+    /**
+     * Starts measurement collection.
+     */
+    public boolean startMeasurementCollection(boolean enableFullTracking) {
+        Preconditions.checkState(mRegistered);
+        return mGnssHal.startMeasurementCollection(enableFullTracking);
+    }
+
+    /**
+     * Stops measurement collection.
+     */
+    public boolean stopMeasurementCollection() {
+        Preconditions.checkState(mRegistered);
+        return mGnssHal.stopMeasurementCollection();
+    }
+
+    /**
+     * Returns true if measurement corrections are supported.
+     */
+    public boolean isMeasurementCorrectionsSupported() {
+        Preconditions.checkState(mRegistered);
+        return mGnssHal.isMeasurementCorrectionsSupported();
+    }
+
+    /**
+     * Injects measurement corrections into the GNSS HAL.
+     */
+    public boolean injectMeasurementCorrections(GnssMeasurementCorrections corrections) {
+        Preconditions.checkState(mRegistered);
+        return mGnssHal.injectMeasurementCorrections(corrections);
+    }
+
+    /**
+     * Initialize batching.
+     */
+    public boolean initBatching() {
+        Preconditions.checkState(mRegistered);
+        return mGnssHal.initBatching();
+    }
+
+    /**
+     * Cleanup batching.
+     */
+    public void cleanupBatching() {
+        Preconditions.checkState(mRegistered);
+        mGnssHal.cleanupBatching();
+    }
+
+    /**
+     * Start batching.
+     */
+    public boolean startBatch(long periodNanos, boolean wakeOnFifoFull) {
+        Preconditions.checkState(mRegistered);
+        return mGnssHal.startBatch(periodNanos, wakeOnFifoFull);
+    }
+
+    /**
+     * Flush batching.
+     */
+    public void flushBatch() {
+        Preconditions.checkState(mRegistered);
+        mGnssHal.flushBatch();
+    }
+
+    /**
+     * Stop batching.
+     */
+    public void stopBatch() {
+        Preconditions.checkState(mRegistered);
+        mGnssHal.stopBatch();
+    }
+
+    /**
+     * Get current batching size.
+     */
+    public int getBatchSize() {
+        Preconditions.checkState(mRegistered);
+        return mGnssHal.getBatchSize();
+    }
+
+    /**
+     * Check if GNSS geofencing is supported.
+     */
+    public boolean isGeofencingSupported() {
+        Preconditions.checkState(mRegistered);
+        return mGnssHal.isGeofencingSupported();
+    }
+
+    /**
+     * Add geofence.
+     */
+    public boolean addGeofence(int geofenceId, double latitude, double longitude, double radius,
+            int lastTransition, int monitorTransitions, int notificationResponsiveness,
+            int unknownTimer) {
+        Preconditions.checkState(mRegistered);
+        return mGnssHal.addGeofence(geofenceId, latitude, longitude, radius, lastTransition,
+                monitorTransitions, notificationResponsiveness, unknownTimer);
+    }
+
+    /**
+     * Resume geofence.
+     */
+    public boolean resumeGeofence(int geofenceId, int monitorTransitions) {
+        Preconditions.checkState(mRegistered);
+        return mGnssHal.resumeGeofence(geofenceId, monitorTransitions);
+    }
+
+    /**
+     * Pause geofence.
+     */
+    public boolean pauseGeofence(int geofenceId) {
+        Preconditions.checkState(mRegistered);
+        return mGnssHal.pauseGeofence(geofenceId);
+    }
+
+    /**
+     * Remove geofence.
+     */
+    public boolean removeGeofence(int geofenceId) {
+        Preconditions.checkState(mRegistered);
+        return mGnssHal.removeGeofence(geofenceId);
+    }
+
+    /**
+     * Returns true if visibility control is supported.
+     */
+    public boolean isGnssVisibilityControlSupported() {
+        Preconditions.checkState(mRegistered);
+        return mGnssHal.isGnssVisibilityControlSupported();
+    }
+
+    /**
+     * Send a network initiated respnse.
+     */
+    public void sendNiResponse(int notificationId, int userResponse) {
+        Preconditions.checkState(mRegistered);
+        mGnssHal.sendNiResponse(notificationId, userResponse);
+    }
+
+    /**
+     * Request an eventual update of GNSS power statistics.
+     */
+    public void requestPowerStats() {
+        Preconditions.checkState(mRegistered);
+        mGnssHal.requestPowerStats();
+    }
+
+    /**
+     * Sets AGPS server information.
+     */
+    public void setAgpsServer(int type, String hostname, int port) {
+        Preconditions.checkState(mRegistered);
+        mGnssHal.setAgpsServer(type, hostname, port);
+    }
+
+    /**
+     * Sets AGPS set id.
+     */
+    public void setAgpsSetId(@AgpsSetIdType int type, String setId) {
+        Preconditions.checkState(mRegistered);
+        mGnssHal.setAgpsSetId(type, setId);
+    }
+
+    /**
+     * Sets AGPS reference cell id location.
+     */
+    public void setAgpsReferenceLocationCellId(@AgpsReferenceLocationType int type, int mcc,
+            int mnc, int lac, int cid) {
+        Preconditions.checkState(mRegistered);
+        mGnssHal.setAgpsReferenceLocationCellId(type, mcc, mnc, lac, cid);
+    }
+
+    /**
+     * Returns true if Predicted Satellite Data Service APIs are supported.
+     */
+    public boolean isPsdsSupported() {
+        Preconditions.checkState(mRegistered);
+        return mGnssHal.isPsdsSupported();
+    }
+
+    /**
+     * Injects Predicited Satellite Data Service data into the GNSS HAL.
+     */
+    public void injectPsdsData(byte[] data, int length, int psdsType) {
+        Preconditions.checkState(mRegistered);
+        mGnssHal.injectPsdsData(data, length, psdsType);
+    }
+
+    @NativeEntryPoint
+    void reportGnssServiceDied() {
+        Log.e(TAG, "gnss hal died - restarting shortly...");
+
+        // move to another thread just in case there is some awkward gnss thread dependency with
+        // the death notification. there shouldn't be, but you never know with gnss...
+        FgThread.getExecutor().execute(this::restartHal);
+    }
+
+    @VisibleForTesting
+    void restartHal() {
+        initializeGnss(true);
+        Log.e(TAG, "gnss hal restarted");
+
+        for (int i = 0; i < mBaseCallbacks.length; i++) {
+            mBaseCallbacks[i].onHalRestarted();
+        }
+    }
+
+    @NativeEntryPoint
+    void reportLocation(boolean hasLatLong, Location location) {
+        if (hasLatLong && !mHasFirstFix) {
+            mHasFirstFix = true;
+
+            // notify status listeners
+            int ttff = (int) (SystemClock.elapsedRealtime() - mStartRealtimeMs);
+            for (int i = 0; i < mStatusCallbacks.length; i++) {
+                mStatusCallbacks[i].onReportFirstFix(ttff);
+            }
+        }
+
+        if (location.hasSpeed()) {
+            boolean exceeded = location.getSpeed() > ITAR_SPEED_LIMIT_METERS_PER_SECOND;
+            if (!mItarSpeedLimitExceeded && exceeded) {
+                Log.w(TAG, "speed nearing ITAR threshold - blocking further GNSS output");
+            } else if (mItarSpeedLimitExceeded && !exceeded) {
+                Log.w(TAG, "speed leaving ITAR threshold - allowing further GNSS output");
+            }
+            mItarSpeedLimitExceeded = exceeded;
+        }
+
+        if (mItarSpeedLimitExceeded) {
+            return;
+        }
+
+        for (int i = 0; i < mLocationCallbacks.length; i++) {
+            mLocationCallbacks[i].onReportLocation(hasLatLong, location);
+        }
+    }
+
+    @NativeEntryPoint
+    void reportStatus(@StatusCallbacks.GnssStatusValue int gnssStatus) {
+        for (int i = 0; i < mStatusCallbacks.length; i++) {
+            mStatusCallbacks[i].onReportStatus(gnssStatus);
+        }
+    }
+
+    @NativeEntryPoint
+    void reportSvStatus(int svCount, int[] svidWithFlags, float[] cn0DbHzs,
+            float[] elevations, float[] azimuths, float[] carrierFrequencies,
+            float[] basebandCn0DbHzs) {
+        GnssStatus gnssStatus = GnssStatus.wrap(svCount, svidWithFlags, cn0DbHzs, elevations,
+                azimuths, carrierFrequencies, basebandCn0DbHzs);
+        for (int i = 0; i < mSvStatusCallbacks.length; i++) {
+            mSvStatusCallbacks[i].onReportSvStatus(gnssStatus);
+        }
+    }
+
+    @NativeEntryPoint
+    void reportAGpsStatus(int agpsType, int agpsStatus, byte[] suplIpAddr) {
+        mAGpsCallbacks.onReportAGpsStatus(agpsType, agpsStatus, suplIpAddr);
+    }
+
+    @NativeEntryPoint
+    void reportNmea(long timestamp) {
+        if (mItarSpeedLimitExceeded) {
+            return;
+        }
+
+        for (int i = 0; i < mNmeaCallbacks.length; i++) {
+            mNmeaCallbacks[i].onReportNmea(timestamp);
+        }
+    }
+
+    @NativeEntryPoint
+    void reportMeasurementData(GnssMeasurementsEvent event) {
+        if (mItarSpeedLimitExceeded) {
+            return;
+        }
+
+        for (int i = 0; i < mMeasurementCallbacks.length; i++) {
+            mMeasurementCallbacks[i].onReportMeasurements(event);
+        }
+    }
+
+    @NativeEntryPoint
+    void reportAntennaInfo(List<GnssAntennaInfo> antennaInfos) {
+        for (int i = 0; i < mAntennaInfoCallbacks.length; i++) {
+            mAntennaInfoCallbacks[i].onReportAntennaInfo(antennaInfos);
+        }
+    }
+
+    @NativeEntryPoint
+    void reportNavigationMessage(GnssNavigationMessage event) {
+        if (mItarSpeedLimitExceeded) {
+            return;
+        }
+
+        for (int i = 0; i < mNavigationMessageCallbacks.length; i++) {
+            mNavigationMessageCallbacks[i].onReportNavigationMessage(event);
+        }
+    }
+
+    @NativeEntryPoint
+    void setTopHalCapabilities(@GnssCapabilities.TopHalCapabilityFlags int capabilities) {
+        // Here the bits specified by 'capabilities' are turned on. It is handled differently from
+        // sub hal because top hal capabilities could be set by HIDL HAL and/or AIDL HAL. Each of
+        // them possesses a different set of capabilities.
+        mTopFlags |= capabilities;
+        GnssCapabilities oldCapabilities = mCapabilities;
+        mCapabilities = oldCapabilities.withTopHalFlags(mTopFlags);
+        onCapabilitiesChanged(oldCapabilities, mCapabilities);
+    }
+
+    @NativeEntryPoint
+    void setSubHalMeasurementCorrectionsCapabilities(
+            @GnssCapabilities.SubHalMeasurementCorrectionsCapabilityFlags int capabilities) {
+        GnssCapabilities oldCapabilities = mCapabilities;
+        mCapabilities = oldCapabilities.withSubHalMeasurementCorrectionsFlags(capabilities);
+        onCapabilitiesChanged(oldCapabilities, mCapabilities);
+    }
+
+    @NativeEntryPoint
+    void setSubHalPowerIndicationCapabilities(
+            @GnssCapabilities.SubHalPowerCapabilityFlags int capabilities) {
+        GnssCapabilities oldCapabilities = mCapabilities;
+        mCapabilities = oldCapabilities.withSubHalPowerFlags(capabilities);
+        onCapabilitiesChanged(oldCapabilities, mCapabilities);
+    }
+
+    private void onCapabilitiesChanged(GnssCapabilities oldCapabilities,
+            GnssCapabilities newCapabilities) {
+        if (newCapabilities.equals(oldCapabilities)) {
+            return;
+        }
+
+        Log.i(TAG, "gnss capabilities changed to " + newCapabilities);
+
+        for (int i = 0; i < mBaseCallbacks.length; i++) {
+            mBaseCallbacks[i].onCapabilitiesChanged(oldCapabilities, newCapabilities);
+        }
+    }
+
+    @NativeEntryPoint
+    void reportGnssPowerStats(GnssPowerStats powerStats) {
+        mPowerStats = powerStats;
+    }
+
+    @NativeEntryPoint
+    void setGnssYearOfHardware(int year) {
+        mHardwareYear = year;
+    }
+
+    @NativeEntryPoint
+    private void setGnssHardwareModelName(String modelName) {
+        mHardwareModelName = modelName;
+    }
+
+    @NativeEntryPoint
+    void reportLocationBatch(Location[] locations) {
+        for (int i = 0; i < mLocationCallbacks.length; i++) {
+            mLocationCallbacks[i].onReportLocations(locations);
+        }
+    }
+
+    @NativeEntryPoint
+    void psdsDownloadRequest(int psdsType) {
+        mPsdsCallbacks.onRequestPsdsDownload(psdsType);
+    }
+
+    @NativeEntryPoint
+    void reportGeofenceTransition(int geofenceId, Location location, int transition,
+            long transitionTimestamp) {
+        mGeofenceCallbacks.onReportGeofenceTransition(geofenceId, location, transition,
+                transitionTimestamp);
+    }
+
+    @NativeEntryPoint
+    void reportGeofenceStatus(int status, Location location) {
+        mGeofenceCallbacks.onReportGeofenceStatus(status, location);
+    }
+
+    @NativeEntryPoint
+    void reportGeofenceAddStatus(int geofenceId, @GeofenceCallbacks.GeofenceStatus int status) {
+        mGeofenceCallbacks.onReportGeofenceAddStatus(geofenceId, status);
+    }
+
+    @NativeEntryPoint
+    void reportGeofenceRemoveStatus(int geofenceId, @GeofenceCallbacks.GeofenceStatus int status) {
+        mGeofenceCallbacks.onReportGeofenceRemoveStatus(geofenceId, status);
+    }
+
+    @NativeEntryPoint
+    void reportGeofencePauseStatus(int geofenceId, @GeofenceCallbacks.GeofenceStatus int status) {
+        mGeofenceCallbacks.onReportGeofencePauseStatus(geofenceId, status);
+    }
+
+    @NativeEntryPoint
+    void reportGeofenceResumeStatus(int geofenceId, @GeofenceCallbacks.GeofenceStatus int status) {
+        mGeofenceCallbacks.onReportGeofenceResumeStatus(geofenceId, status);
+    }
+
+    @NativeEntryPoint
+    void reportNiNotification(int notificationId, int niType, int notifyFlags,
+            int timeout, int defaultResponse, String requestorId, String text,
+            int requestorIdEncoding, int textEncoding) {
+        mNotificationCallbacks.onReportNiNotification(notificationId, niType, notifyFlags, timeout,
+                    defaultResponse, requestorId, text, requestorIdEncoding, textEncoding);
+    }
+
+    @NativeEntryPoint
+    void requestSetID(int flags) {
+        mAGpsCallbacks.onRequestSetID(flags);
+    }
+
+    @NativeEntryPoint
+    void requestLocation(boolean independentFromGnss, boolean isUserEmergency) {
+        mLocationRequestCallbacks.onRequestLocation(independentFromGnss, isUserEmergency);
+    }
+
+    @NativeEntryPoint
+    void requestUtcTime() {
+        mTimeCallbacks.onRequestUtcTime();
+    }
+
+    @NativeEntryPoint
+    void requestRefLocation() {
+        mLocationRequestCallbacks.onRequestRefLocation();
+    }
+
+    @NativeEntryPoint
+    void reportNfwNotification(String proxyAppPackageName, byte protocolStack,
+            String otherProtocolStackName, byte requestor, String requestorId,
+            byte responseType, boolean inEmergencyMode, boolean isCachedLocation) {
+        mNotificationCallbacks.onReportNfwNotification(proxyAppPackageName, protocolStack,
+                    otherProtocolStackName, requestor, requestorId, responseType, inEmergencyMode,
+                    isCachedLocation);
+    }
+
+    @NativeEntryPoint
+    boolean isInEmergencySession() {
+        return mEmergencyHelper.isInEmergency(mConfiguration.getEsExtensionSec());
+    }
+
+    /**
+     * Encapsulates actual HAL methods for testing purposes.
+     */
+    @VisibleForTesting
+    public static class GnssHal {
+
+        protected GnssHal() {}
+
+        protected void classInitOnce() {
+            native_class_init_once();
+        }
+
+        protected boolean isSupported() {
+            return native_is_supported();
+        }
+
+        protected void initOnce(GnssNative gnssNative, boolean reinitializeGnssServiceHandle) {
+            gnssNative.native_init_once(reinitializeGnssServiceHandle);
+        }
+
+        protected boolean init() {
+            return native_init();
+        }
+
+        protected void cleanup() {
+            native_cleanup();
+        }
+
+        protected boolean start() {
+            return native_start();
+        }
+
+        protected boolean stop() {
+            return native_stop();
+        }
+
+        protected boolean setPositionMode(@GnssPositionMode int mode,
+                @GnssPositionRecurrence int recurrence, int minInterval, int preferredAccuracy,
+                int preferredTime, boolean lowPowerMode) {
+            return native_set_position_mode(mode, recurrence, minInterval, preferredAccuracy,
+                    preferredTime, lowPowerMode);
+        }
+
+        protected String getInternalState() {
+            return native_get_internal_state();
+        }
+
+        protected void deleteAidingData(@GnssAidingTypeFlags int flags) {
+            native_delete_aiding_data(flags);
+        }
+
+        protected int readNmea(byte[] buffer, int bufferSize) {
+            return native_read_nmea(buffer, bufferSize);
+        }
+
+        protected void injectLocation(double latitude, double longitude, float accuracy) {
+            native_inject_location(latitude, longitude, accuracy);
+        }
+
+        protected void injectBestLocation(@GnssLocationFlags int gnssLocationFlags, double latitude,
+                double longitude, double altitude, float speed, float bearing,
+                float horizontalAccuracy, float verticalAccuracy, float speedAccuracy,
+                float bearingAccuracy, long timestamp, @GnssRealtimeFlags int elapsedRealtimeFlags,
+                long elapsedRealtimeNanos, double elapsedRealtimeUncertaintyNanos) {
+            native_inject_best_location(gnssLocationFlags, latitude, longitude, altitude, speed,
+                    bearing, horizontalAccuracy, verticalAccuracy, speedAccuracy, bearingAccuracy,
+                    timestamp, elapsedRealtimeFlags, elapsedRealtimeNanos,
+                    elapsedRealtimeUncertaintyNanos);
+        }
+
+        protected void injectTime(long time, long timeReference, int uncertainty) {
+            native_inject_time(time, timeReference, uncertainty);
+        }
+
+        protected boolean isNavigationMessageCollectionSupported() {
+            return native_is_navigation_message_supported();
+        }
+
+        protected boolean startNavigationMessageCollection() {
+            return native_start_navigation_message_collection();
+        }
+
+        protected boolean stopNavigationMessageCollection() {
+            return native_stop_navigation_message_collection();
+        }
+
+        protected boolean isAntennaInfoListeningSupported() {
+            return native_is_antenna_info_supported();
+        }
+
+        protected boolean startAntennaInfoListening() {
+            return native_start_antenna_info_listening();
+        }
+
+        protected boolean stopAntennaInfoListening() {
+            return native_stop_antenna_info_listening();
+        }
+
+        protected boolean isMeasurementSupported() {
+            return native_is_measurement_supported();
+        }
+
+        protected boolean startMeasurementCollection(boolean enableFullTracking) {
+            return native_start_measurement_collection(enableFullTracking);
+        }
+
+        protected boolean stopMeasurementCollection() {
+            return native_stop_measurement_collection();
+        }
+
+        protected boolean isMeasurementCorrectionsSupported() {
+            return native_is_measurement_corrections_supported();
+        }
+
+        protected boolean injectMeasurementCorrections(GnssMeasurementCorrections corrections) {
+            return native_inject_measurement_corrections(corrections);
+        }
+
+        protected int getBatchSize() {
+            return native_get_batch_size();
+        }
+
+        protected boolean initBatching() {
+            return native_init_batching();
+        }
+
+        protected void cleanupBatching() {
+            native_cleanup_batching();
+        }
+
+        protected boolean startBatch(long periodNanos, boolean wakeOnFifoFull) {
+            return native_start_batch(periodNanos, wakeOnFifoFull);
+        }
+
+        protected void flushBatch() {
+            native_flush_batch();
+        }
+
+        protected void stopBatch() {
+            native_stop_batch();
+        }
+
+        protected boolean isGeofencingSupported() {
+            return native_is_geofence_supported();
+        }
+
+        protected boolean addGeofence(int geofenceId, double latitude, double longitude,
+                double radius, int lastTransition, int monitorTransitions,
+                int notificationResponsiveness, int unknownTimer) {
+            return native_add_geofence(geofenceId, latitude, longitude, radius, lastTransition,
+                    monitorTransitions, notificationResponsiveness, unknownTimer);
+        }
+
+        protected boolean resumeGeofence(int geofenceId, int monitorTransitions) {
+            return native_resume_geofence(geofenceId, monitorTransitions);
+        }
+
+        protected boolean pauseGeofence(int geofenceId) {
+            return native_pause_geofence(geofenceId);
+        }
+
+        protected boolean removeGeofence(int geofenceId) {
+            return native_remove_geofence(geofenceId);
+        }
+
+        protected boolean isGnssVisibilityControlSupported() {
+            return native_is_gnss_visibility_control_supported();
+        }
+
+        protected void sendNiResponse(int notificationId, int userResponse) {
+            native_send_ni_response(notificationId, userResponse);
+        }
+
+        protected void requestPowerStats() {
+            native_request_power_stats();
+        }
+
+        protected void setAgpsServer(int type, String hostname, int port) {
+            native_set_agps_server(type, hostname, port);
+        }
+
+        protected void setAgpsSetId(@AgpsSetIdType int type, String setId) {
+            native_agps_set_id(type, setId);
+        }
+
+        protected void setAgpsReferenceLocationCellId(@AgpsReferenceLocationType int type, int mcc,
+                int mnc, int lac, int cid) {
+            native_agps_set_ref_location_cellid(type, mcc, mnc, lac, cid);
+        }
+
+        protected boolean isPsdsSupported() {
+            return native_supports_psds();
+        }
+
+        protected void injectPsdsData(byte[] data, int length, int psdsType) {
+            native_inject_psds_data(data, length, psdsType);
+        }
+    }
+
+    // basic APIs
+
+    private static native void native_class_init_once();
+
+    private static native boolean native_is_supported();
+
+    private native void native_init_once(boolean reinitializeGnssServiceHandle);
+
+    private static native boolean native_init();
+
+    private static native void native_cleanup();
+
+    private static native boolean native_start();
+
+    private static native boolean native_stop();
+
+    private static native boolean native_set_position_mode(int mode, int recurrence,
+            int minInterval, int preferredAccuracy, int preferredTime, boolean lowPowerMode);
+
+    private static native String native_get_internal_state();
+
+    private static native void native_delete_aiding_data(int flags);
+
+    // NMEA APIs
+
+    private static native int native_read_nmea(byte[] buffer, int bufferSize);
+
+    // location injection APIs
+
+    private static native void native_inject_location(double latitude, double longitude,
+            float accuracy);
+
+
+    private static native void native_inject_best_location(
+            int gnssLocationFlags, double latitudeDegrees, double longitudeDegrees,
+            double altitudeMeters, float speedMetersPerSec, float bearingDegrees,
+            float horizontalAccuracyMeters, float verticalAccuracyMeters,
+            float speedAccuracyMetersPerSecond, float bearingAccuracyDegrees,
+            long timestamp, int elapsedRealtimeFlags, long elapsedRealtimeNanos,
+            double elapsedRealtimeUncertaintyNanos);
+
+    // time injection APIs
+
+    private static native void native_inject_time(long time, long timeReference, int uncertainty);
+
+    // navigation message APIs
+
+    private static native boolean native_is_navigation_message_supported();
+
+    private static native boolean native_start_navigation_message_collection();
+
+    private static native boolean native_stop_navigation_message_collection();
+
+    // antenna info APIS
+    // TODO: in a next version of the HAL, consider removing the necessity for listening to antenna
+    //   info changes, and simply report them always, same as capabilities.
+
+    private static native boolean native_is_antenna_info_supported();
+
+    private static native boolean native_start_antenna_info_listening();
+
+    private static native boolean native_stop_antenna_info_listening();
+
+    // measurement APIs
+
+    private static native boolean native_is_measurement_supported();
+
+    private static native boolean native_start_measurement_collection(boolean enableFullTracking);
+
+    private static native boolean native_stop_measurement_collection();
+
+    // measurement corrections APIs
+
+    private static native boolean native_is_measurement_corrections_supported();
+
+    private static native boolean native_inject_measurement_corrections(
+            GnssMeasurementCorrections corrections);
+
+    // batching APIs
+
+    private static native boolean native_init_batching();
+
+    private static native void native_cleanup_batching();
+
+    private static native boolean native_start_batch(long periodNanos, boolean wakeOnFifoFull);
+
+    private static native void native_flush_batch();
+
+    private static native boolean native_stop_batch();
+
+    private static native int native_get_batch_size();
+
+    // geofence APIs
+
+    private static native boolean native_is_geofence_supported();
+
+    private static native boolean native_add_geofence(int geofenceId, double latitude,
+            double longitude, double radius, int lastTransition, int monitorTransitions,
+            int notificationResponsivenes, int unknownTimer);
+
+    private static native boolean native_resume_geofence(int geofenceId, int monitorTransitions);
+
+    private static native boolean native_pause_geofence(int geofenceId);
+
+    private static native boolean native_remove_geofence(int geofenceId);
+
+    // network initiated (NI) APIs
+
+    private static native boolean native_is_gnss_visibility_control_supported();
+
+    private static native void native_send_ni_response(int notificationId, int userResponse);
+
+    // power stats APIs
+
+    private static native void native_request_power_stats();
+
+    // AGPS APIs
+
+    private static native void native_set_agps_server(int type, String hostname, int port);
+
+    private static native void native_agps_set_id(int type, String setid);
+
+    private static native void native_agps_set_ref_location_cellid(int type, int mcc, int mnc,
+            int lac, int cid);
+
+    // PSDS APIs
+
+    private static native boolean native_supports_psds();
+
+    private static native void native_inject_psds_data(byte[] data, int length, int psdsType);
+}
diff --git a/location/java/com/android/internal/location/timezone/ILocationTimeZoneProviderManager.aidl b/services/core/java/com/android/server/location/injector/EmergencyHelper.java
similarity index 64%
rename from location/java/com/android/internal/location/timezone/ILocationTimeZoneProviderManager.aidl
rename to services/core/java/com/android/server/location/injector/EmergencyHelper.java
index b5450b7..be4bf50 100644
--- a/location/java/com/android/internal/location/timezone/ILocationTimeZoneProviderManager.aidl
+++ b/services/core/java/com/android/server/location/injector/EmergencyHelper.java
@@ -14,14 +14,16 @@
  * limitations under the License.
  */
 
-package com.android.internal.location.timezone;
-
-import com.android.internal.location.timezone.LocationTimeZoneEvent;
+package com.android.server.location.injector;
 
 /**
- * Binder interface for the manager of location time zone provider implementations.
- * @hide
+ * Provides helpers for emergency sessions.
  */
-interface ILocationTimeZoneProviderManager {
-    void onLocationTimeZoneEvent(in LocationTimeZoneEvent locationTimeZoneEvent);
+public abstract class EmergencyHelper {
+
+    /**
+     * Returns true if the device is in an emergency session, or if an emergency session ended
+     * within the given extension time.
+     */
+    public abstract boolean isInEmergency(long extensionTimeMs);
 }
diff --git a/services/core/java/com/android/server/location/injector/Injector.java b/services/core/java/com/android/server/location/injector/Injector.java
index c42396d..03938b2 100644
--- a/services/core/java/com/android/server/location/injector/Injector.java
+++ b/services/core/java/com/android/server/location/injector/Injector.java
@@ -51,6 +51,9 @@
     /** Returns a LocationAttributionHelper. */
     LocationAttributionHelper getLocationAttributionHelper();
 
+    /** Returns an EmergencyHelper. */
+    EmergencyHelper getEmergencyHelper();
+
     /** Returns a LocationUsageLogger. */
     LocationUsageLogger getLocationUsageLogger();
 
diff --git a/services/core/java/com/android/server/location/injector/SystemEmergencyHelper.java b/services/core/java/com/android/server/location/injector/SystemEmergencyHelper.java
new file mode 100644
index 0000000..05d0aef
--- /dev/null
+++ b/services/core/java/com/android/server/location/injector/SystemEmergencyHelper.java
@@ -0,0 +1,94 @@
+/*
+ * 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.location.injector;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.SystemClock;
+import android.telephony.PhoneStateListener;
+import android.telephony.TelephonyManager;
+
+import com.android.server.FgThread;
+
+import java.util.Objects;
+
+/**
+ * Provides helpers for emergency sessions.
+ */
+public class SystemEmergencyHelper extends EmergencyHelper {
+
+    private final Context mContext;
+
+    private TelephonyManager mTelephonyManager;
+
+    private boolean mIsInEmergencyCall;
+    private long mEmergencyCallEndRealtimeMs = Long.MIN_VALUE;
+
+    public SystemEmergencyHelper(Context context) {
+        mContext = context;
+    }
+
+    /** Called when system is ready. */
+    public void onSystemReady() {
+        if (mTelephonyManager != null) {
+            return;
+        }
+
+        mTelephonyManager = Objects.requireNonNull(
+                mContext.getSystemService(TelephonyManager.class));
+
+        // TODO: this doesn't account for multisim phones
+
+        mTelephonyManager.registerPhoneStateListener(FgThread.getExecutor(),
+                new EmergencyCallPhoneStateListener());
+        mContext.registerReceiver(new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                if (!Intent.ACTION_NEW_OUTGOING_CALL.equals(intent.getAction())) {
+                    return;
+                }
+
+                mIsInEmergencyCall = mTelephonyManager.isEmergencyNumber(
+                        intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER));
+            }
+        }, new IntentFilter(Intent.ACTION_NEW_OUTGOING_CALL));
+    }
+
+    @Override
+    public boolean isInEmergency(long extensionTimeMs) {
+        return mIsInEmergencyCall
+                || ((SystemClock.elapsedRealtime() - mEmergencyCallEndRealtimeMs) < extensionTimeMs)
+                || mTelephonyManager.getEmergencyCallbackMode()
+                || mTelephonyManager.isInEmergencySmsMode();
+    }
+
+    private class EmergencyCallPhoneStateListener extends PhoneStateListener implements
+            PhoneStateListener.CallStateChangedListener {
+
+        @Override
+        public void onCallStateChanged(int state, String incomingNumber) {
+            if (state == TelephonyManager.CALL_STATE_IDLE) {
+                if (mIsInEmergencyCall) {
+                    mEmergencyCallEndRealtimeMs = SystemClock.elapsedRealtime();
+                    mIsInEmergencyCall = false;
+                }
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/location/provider/AbstractLocationProvider.java b/services/core/java/com/android/server/location/provider/AbstractLocationProvider.java
index d06f54d..5364feb 100644
--- a/services/core/java/com/android/server/location/provider/AbstractLocationProvider.java
+++ b/services/core/java/com/android/server/location/provider/AbstractLocationProvider.java
@@ -18,11 +18,11 @@
 
 import android.annotation.Nullable;
 import android.location.LocationResult;
+import android.location.ProviderProperties;
 import android.location.util.identity.CallerIdentity;
 import android.os.Binder;
 import android.os.Bundle;
 
-import com.android.internal.location.ProviderProperties;
 import com.android.internal.location.ProviderRequest;
 import com.android.internal.util.Preconditions;
 
@@ -90,7 +90,10 @@
             this.identity = identity;
         }
 
-        State withAllowed(boolean allowed) {
+        /**
+         * Returns a state the same as the current but with allowed set as specified.
+         */
+        public State withAllowed(boolean allowed) {
             if (allowed == this.allowed) {
                 return this;
             } else {
@@ -98,7 +101,10 @@
             }
         }
 
-        State withProperties(@Nullable ProviderProperties properties) {
+        /**
+         * Returns a state the same as the current but with properties set as specified.
+         */
+        public State withProperties(@Nullable ProviderProperties properties) {
             if (Objects.equals(properties, this.properties)) {
                 return this;
             } else {
@@ -106,7 +112,10 @@
             }
         }
 
-        State withIdentity(@Nullable CallerIdentity identity) {
+        /**
+         * Returns a state the same as the current but with an identity set as specified.
+         */
+        public State withIdentity(@Nullable CallerIdentity identity) {
             if (Objects.equals(identity, this.identity)) {
                 return this;
             } else {
@@ -175,28 +184,21 @@
 
     private final LocationProviderController mController;
 
-
-    /**
-     * See {@link #AbstractLocationProvider(Executor, CallerIdentity)}.
-     */
-    protected AbstractLocationProvider(Executor executor) {
-        this(executor, null);
-    }
-
     /**
      * Creates a new location provider.
      *
      * All callback methods will be invoked on the given executor. A direct executor may be provided
      * only if the provider can guarantee that all callback methods will never synchronously invoke
-     * any command method (that changes provider state, or reports a location, etc...). If this
-     * invariant is not held, use a normal executor or risk deadlock.
+     * any {@link LocationProviderController} methods. If this invariant is not held, use a normal
+     * executor or risk deadlock.
      *
-     * An optional identity may be provided to initialize the location provider.
+     * An optional identity and properties may be provided to initialize the location provider.
      */
-    protected AbstractLocationProvider(Executor executor, CallerIdentity identity) {
+    protected AbstractLocationProvider(Executor executor, @Nullable CallerIdentity identity,
+            @Nullable ProviderProperties properties) {
         mExecutor = executor;
-        mInternalState = new AtomicReference<>(
-                new InternalState(null, State.EMPTY_STATE.withIdentity(identity)));
+        mInternalState = new AtomicReference<>(new InternalState(null,
+                State.EMPTY_STATE.withIdentity(identity).withProperties(properties)));
         mController = new Controller();
     }
 
@@ -209,46 +211,23 @@
         return mController;
     }
 
-    /**
-     * Sets the state of the provider to the new state.
-     */
-    protected void setState(State newState) {
-        InternalState oldInternalState = mInternalState.getAndUpdate(
-                internalState -> internalState.withState(newState));
-        if (newState.equals(oldInternalState.state)) {
+    protected void setState(UnaryOperator<State> operator) {
+        AtomicReference<State> oldStateRef = new AtomicReference<>();
+        InternalState newInternalState = mInternalState.updateAndGet(
+                internalState -> {
+                    oldStateRef.set(internalState.state);
+                    return internalState.withState(operator);
+                });
+        State oldState = oldStateRef.get();
+
+        if (oldState.equals(newInternalState.state)) {
             return;
         }
 
-        // we know that we only updated the state, so the listener for the old state is the same as
-        // the listener for the new state.
-        if (oldInternalState.listener != null) {
+        if (newInternalState.listener != null) {
             final long identity = Binder.clearCallingIdentity();
             try {
-                oldInternalState.listener.onStateChanged(oldInternalState.state, newState);
-            } finally {
-                Binder.restoreCallingIdentity(identity);
-            }
-        }
-    }
-
-    private void setState(UnaryOperator<State> operator) {
-        InternalState oldInternalState = mInternalState.getAndUpdate(
-                internalState -> internalState.withState(operator));
-
-        // recreate the new state from our knowledge of the old state - unfortunately may result in
-        // an extra allocation, but oh well...
-        State newState = operator.apply(oldInternalState.state);
-
-        if (newState.equals(oldInternalState.state)) {
-            return;
-        }
-
-        // we know that we only updated the state, so the listener for the old state is the same as
-        // the listener for the new state.
-        if (oldInternalState.listener != null) {
-            final long identity = Binder.clearCallingIdentity();
-            try {
-                oldInternalState.listener.onStateChanged(oldInternalState.state, newState);
+                newInternalState.listener.onStateChanged(oldState, newInternalState.state);
             } finally {
                 Binder.restoreCallingIdentity(identity);
             }
@@ -279,7 +258,7 @@
     /**
      * Call this method to report a change in provider properties.
      */
-    protected void setProperties(ProviderProperties properties) {
+    protected void setProperties(@Nullable ProviderProperties properties) {
         setState(state -> state.withProperties(properties));
     }
 
@@ -293,7 +272,7 @@
     /**
      * Call this method to report a change in provider packages.
      */
-    protected void setIdentity(CallerIdentity identity) {
+    protected void setIdentity(@Nullable CallerIdentity identity) {
         setState(state -> state.withIdentity(identity));
     }
 
diff --git a/services/core/java/com/android/server/location/provider/LocationProviderManager.java b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
index 2fe8bcc..858b762 100644
--- a/services/core/java/com/android/server/location/provider/LocationProviderManager.java
+++ b/services/core/java/com/android/server/location/provider/LocationProviderManager.java
@@ -46,7 +46,6 @@
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.Intent;
-import android.location.Criteria;
 import android.location.ILocationCallback;
 import android.location.ILocationListener;
 import android.location.LastLocationRequest;
@@ -56,6 +55,7 @@
 import android.location.LocationManagerInternal.ProviderEnabledListener;
 import android.location.LocationRequest;
 import android.location.LocationResult;
+import android.location.ProviderProperties;
 import android.location.util.identity.CallerIdentity;
 import android.os.Binder;
 import android.os.Build;
@@ -82,7 +82,6 @@
 import android.util.TimeUtils;
 
 import com.android.internal.annotations.GuardedBy;
-import com.android.internal.location.ProviderProperties;
 import com.android.internal.location.ProviderRequest;
 import com.android.internal.util.Preconditions;
 import com.android.server.FgThread;
@@ -452,7 +451,7 @@
 
             return isActive()
                     && getRequest().getIntervalMillis() < MAX_HIGH_POWER_INTERVAL_MS
-                    && getProperties().getPowerRequirement() == Criteria.POWER_HIGH;
+                    && getProperties().getPowerUsage() == ProviderProperties.POWER_USAGE_HIGH;
         }
 
         @GuardedBy("mLock")
@@ -1358,6 +1357,8 @@
     public boolean isEnabled(int userId) {
         if (userId == UserHandle.USER_NULL) {
             return false;
+        } else if (userId == UserHandle.USER_CURRENT) {
+            return isEnabled(mUserHelper.getCurrentUserId());
         }
 
         Preconditions.checkArgument(userId >= 0);
@@ -1519,6 +1520,9 @@
                 }
             }
             return lastLocation;
+        } else if (userId == UserHandle.USER_CURRENT) {
+            return getLastLocationUnsafe(mUserHelper.getCurrentUserId(), permissionLevel,
+                    ignoreLocationSettings, maximumAgeMs);
         }
 
         Preconditions.checkArgument(userId >= 0);
@@ -1561,6 +1565,9 @@
                 setLastLocation(location, runningUserIds[i]);
             }
             return;
+        } else if (userId == UserHandle.USER_CURRENT) {
+            setLastLocation(location, mUserHelper.getCurrentUserId());
+            return;
         }
 
         Preconditions.checkArgument(userId >= 0);
diff --git a/services/core/java/com/android/server/location/provider/MockLocationProvider.java b/services/core/java/com/android/server/location/provider/MockLocationProvider.java
index 0c6d5dc..f9aa402 100644
--- a/services/core/java/com/android/server/location/provider/MockLocationProvider.java
+++ b/services/core/java/com/android/server/location/provider/MockLocationProvider.java
@@ -21,10 +21,10 @@
 import android.annotation.Nullable;
 import android.location.Location;
 import android.location.LocationResult;
+import android.location.ProviderProperties;
 import android.location.util.identity.CallerIdentity;
 import android.os.Bundle;
 
-import com.android.internal.location.ProviderProperties;
 import com.android.internal.location.ProviderRequest;
 
 import java.io.FileDescriptor;
@@ -41,8 +41,7 @@
 
     public MockLocationProvider(ProviderProperties properties, CallerIdentity identity) {
         // using a direct executor is ok because this class has no locks that could deadlock
-        super(DIRECT_EXECUTOR, identity);
-        setProperties(properties);
+        super(DIRECT_EXECUTOR, identity, properties);
     }
 
     /** Sets the allowed state of this mock provider. */
diff --git a/services/core/java/com/android/server/location/provider/MockableLocationProvider.java b/services/core/java/com/android/server/location/provider/MockableLocationProvider.java
index 79f641f..c1b0abf 100644
--- a/services/core/java/com/android/server/location/provider/MockableLocationProvider.java
+++ b/services/core/java/com/android/server/location/provider/MockableLocationProvider.java
@@ -21,11 +21,11 @@
 import android.annotation.Nullable;
 import android.location.Location;
 import android.location.LocationResult;
+import android.location.ProviderProperties;
 import android.location.util.identity.CallerIdentity;
 import android.os.Bundle;
 
 import com.android.internal.annotations.GuardedBy;
-import com.android.internal.location.ProviderProperties;
 import com.android.internal.location.ProviderRequest;
 import com.android.internal.util.Preconditions;
 
@@ -75,7 +75,7 @@
     public MockableLocationProvider(Object ownerLock) {
         // using a direct executor is acceptable because all inbound calls are delegated to the
         // actual provider implementations which will use their own executors
-        super(DIRECT_EXECUTOR);
+        super(DIRECT_EXECUTOR, null, null);
         mOwnerLock = ownerLock;
         mRequest = ProviderRequest.EMPTY_REQUEST;
     }
@@ -167,7 +167,7 @@
             newState = State.EMPTY_STATE;
         }
 
-        setState(newState);
+        setState(prevState -> newState);
     }
 
     /**
@@ -325,7 +325,7 @@
                     return;
                 }
 
-                setState(newState);
+                setState(prevState -> newState);
             }
         }
 
diff --git a/services/core/java/com/android/server/location/provider/PassiveLocationProvider.java b/services/core/java/com/android/server/location/provider/PassiveLocationProvider.java
index 0e8b40b..1f4c4cf 100644
--- a/services/core/java/com/android/server/location/provider/PassiveLocationProvider.java
+++ b/services/core/java/com/android/server/location/provider/PassiveLocationProvider.java
@@ -19,12 +19,11 @@
 import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
 
 import android.content.Context;
-import android.location.Criteria;
 import android.location.LocationResult;
+import android.location.ProviderProperties;
 import android.location.util.identity.CallerIdentity;
 import android.os.Bundle;
 
-import com.android.internal.location.ProviderProperties;
 import com.android.internal.location.ProviderRequest;
 
 import java.io.FileDescriptor;
@@ -47,14 +46,12 @@
             /* supportsAltitude = */false,
             /* supportsSpeed = */false,
             /* supportsBearing = */false,
-            Criteria.POWER_LOW,
-            Criteria.ACCURACY_COARSE);
+            ProviderProperties.POWER_USAGE_LOW,
+            ProviderProperties.ACCURACY_COARSE);
 
     public PassiveLocationProvider(Context context) {
         // using a direct executor is ok because this class has no locks that could deadlock
-        super(DIRECT_EXECUTOR, CallerIdentity.fromContext(context));
-
-        setProperties(PROPERTIES);
+        super(DIRECT_EXECUTOR, CallerIdentity.fromContext(context), PROPERTIES);
         setAllowed(true);
     }
 
diff --git a/services/core/java/com/android/server/location/provider/proxy/ProxyLocationProvider.java b/services/core/java/com/android/server/location/provider/proxy/ProxyLocationProvider.java
index 6e92c8d..345fdc0 100644
--- a/services/core/java/com/android/server/location/provider/proxy/ProxyLocationProvider.java
+++ b/services/core/java/com/android/server/location/provider/proxy/ProxyLocationProvider.java
@@ -22,6 +22,7 @@
 import android.content.ComponentName;
 import android.content.Context;
 import android.location.LocationResult;
+import android.location.ProviderProperties;
 import android.location.util.identity.CallerIdentity;
 import android.os.Binder;
 import android.os.Bundle;
@@ -31,7 +32,6 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.location.ILocationProvider;
 import com.android.internal.location.ILocationProviderManager;
-import com.android.internal.location.ProviderProperties;
 import com.android.internal.location.ProviderRequest;
 import com.android.internal.util.ArrayUtils;
 import com.android.server.ServiceWatcher;
@@ -82,7 +82,7 @@
             int nonOverlayPackageResId) {
         // safe to use direct executor since our locks are not acquired in a code path invoked by
         // our owning provider
-        super(DIRECT_EXECUTOR);
+        super(DIRECT_EXECUTOR, null, null);
 
         mContext = context;
         mServiceWatcher = new ServiceWatcher(context, action, this::onBind,
@@ -116,7 +116,7 @@
         synchronized (mLock) {
             mProxy = null;
             mService = null;
-            setState(State.EMPTY_STATE);
+            setState(prevState -> State.EMPTY_STATE);
             flushListeners = mFlushListeners.toArray(new Runnable[0]);
             mFlushListeners.clear();
         }
@@ -210,7 +210,8 @@
 
         // executed on binder thread
         @Override
-        public void onSetIdentity(@Nullable String packageName, @Nullable String attributionTag) {
+        public void onInitialize(boolean allowed, ProviderProperties properties,
+                @Nullable String packageName, @Nullable String attributionTag) {
             synchronized (mLock) {
                 if (mProxy != this) {
                     return;
@@ -226,7 +227,10 @@
                     identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag);
                 }
 
-                setIdentity(identity);
+                setState(prevState -> prevState
+                        .withAllowed(allowed)
+                        .withProperties(properties)
+                        .withIdentity(identity));
             }
         }
 
@@ -238,14 +242,6 @@
                     return;
                 }
 
-                // if no identity is set yet, set it now
-                if (getIdentity() == null) {
-                    String packageName = guessPackageName(mContext, Binder.getCallingUid(),
-                            Objects.requireNonNull(mService).getPackageName());
-                    // unsafe is ok since the package is coming direct from the package manager here
-                    setIdentity(CallerIdentity.fromBinderUnsafe(packageName, null));
-                }
-
                 setProperties(properties);
             }
         }
diff --git a/services/core/java/com/android/server/location/timezone/BinderLocationTimeZoneProvider.java b/services/core/java/com/android/server/location/timezone/BinderLocationTimeZoneProvider.java
index b9c23b7..210fb5c 100644
--- a/services/core/java/com/android/server/location/timezone/BinderLocationTimeZoneProvider.java
+++ b/services/core/java/com/android/server/location/timezone/BinderLocationTimeZoneProvider.java
@@ -17,6 +17,7 @@
 package com.android.server.location.timezone;
 
 import static com.android.server.location.timezone.LocationTimeZoneManagerService.debugLog;
+import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_DESTROYED;
 import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_PERM_FAILED;
 import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_CERTAIN;
 import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_INITIALIZING;
@@ -25,8 +26,8 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.os.RemoteCallback;
 import android.util.IndentingPrintWriter;
-import android.util.Slog;
 
 import java.time.Duration;
 import java.util.Objects;
@@ -71,6 +72,11 @@
         });
     }
 
+    @Override
+    void onDestroy() {
+        mProxy.destroy();
+    }
+
     private void handleProviderLost(String reason) {
         mThreadingDomain.assertCurrentThread();
 
@@ -100,11 +106,12 @@
                             + ": No state change required, provider is stopped.");
                     break;
                 }
-                case PROVIDER_STATE_PERM_FAILED: {
+                case PROVIDER_STATE_PERM_FAILED:
+                case PROVIDER_STATE_DESTROYED: {
                     debugLog("handleProviderLost reason=" + reason
                             + ", mProviderName=" + mProviderName
                             + ", currentState=" + currentState
-                            + ": No state change required, provider is perm failed.");
+                            + ": No state change required, provider is terminated.");
                     break;
                 }
                 default: {
@@ -132,11 +139,12 @@
                             + ", currentState=" + currentState + ": Provider is stopped.");
                     break;
                 }
-                case PROVIDER_STATE_PERM_FAILED: {
+                case PROVIDER_STATE_PERM_FAILED:
+                case PROVIDER_STATE_DESTROYED: {
                     debugLog("handleOnProviderBound"
                             + ", mProviderName=" + mProviderName
                             + ", currentState=" + currentState
-                            + ": No state change required, provider is perm failed.");
+                            + ": No state change required, provider is terminated.");
                     break;
                 }
                 default: {
@@ -161,9 +169,14 @@
         mProxy.setRequest(request);
     }
 
+    /**
+     * Passes the supplied test command to the current proxy.
+     */
     @Override
-    void logWarn(String msg) {
-        Slog.w(TAG, msg);
+    void handleTestCommand(@NonNull TestCommand testCommand, @Nullable RemoteCallback callback) {
+        mThreadingDomain.assertCurrentThread();
+
+        mProxy.handleTestCommand(testCommand, callback);
     }
 
     @Override
@@ -196,19 +209,4 @@
                     + '}';
         }
     }
-
-    /**
-     * Passes the supplied simulation / testing event to the current proxy iff the proxy is a
-     * {@link SimulatedLocationTimeZoneProviderProxy}. If not, the event is logged but discarded.
-     */
-    void simulateBinderProviderEvent(SimulatedBinderProviderEvent event) {
-        mThreadingDomain.assertCurrentThread();
-
-        if (!(mProxy instanceof SimulatedLocationTimeZoneProviderProxy)) {
-            Slog.w(TAG, mProxy + " is not a " + SimulatedLocationTimeZoneProviderProxy.class
-                    + ", event=" + event);
-            return;
-        }
-        ((SimulatedLocationTimeZoneProviderProxy) mProxy).simulate(event);
-    }
 }
diff --git a/services/core/java/com/android/server/location/timezone/ControllerEnvironmentImpl.java b/services/core/java/com/android/server/location/timezone/ControllerEnvironmentImpl.java
index b1e3306..d896f6e 100644
--- a/services/core/java/com/android/server/location/timezone/ControllerEnvironmentImpl.java
+++ b/services/core/java/com/android/server/location/timezone/ControllerEnvironmentImpl.java
@@ -19,6 +19,7 @@
 import android.annotation.NonNull;
 
 import com.android.server.LocalServices;
+import com.android.server.timezonedetector.ConfigurationChangeListener;
 import com.android.server.timezonedetector.ConfigurationInternal;
 import com.android.server.timezonedetector.TimeZoneDetectorInternal;
 
@@ -37,6 +38,7 @@
 
     @NonNull private final TimeZoneDetectorInternal mTimeZoneDetectorInternal;
     @NonNull private final LocationTimeZoneProviderController mController;
+    @NonNull private final ConfigurationChangeListener mConfigurationChangeListener;
 
     ControllerEnvironmentImpl(@NonNull ThreadingDomain threadingDomain,
             @NonNull LocationTimeZoneProviderController controller) {
@@ -45,8 +47,14 @@
         mTimeZoneDetectorInternal = LocalServices.getService(TimeZoneDetectorInternal.class);
 
         // Listen for configuration changes.
-        mTimeZoneDetectorInternal.addConfigurationListener(
-                () -> mThreadingDomain.post(mController::onConfigChanged));
+        mConfigurationChangeListener = () -> mThreadingDomain.post(mController::onConfigChanged);
+        mTimeZoneDetectorInternal.addConfigurationListener(mConfigurationChangeListener);
+    }
+
+
+    @Override
+    void destroy() {
+        mTimeZoneDetectorInternal.removeConfigurationListener(mConfigurationChangeListener);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/location/timezone/ControllerImpl.java b/services/core/java/com/android/server/location/timezone/ControllerImpl.java
index 7496168..03ce8ec 100644
--- a/services/core/java/com/android/server/location/timezone/ControllerImpl.java
+++ b/services/core/java/com/android/server/location/timezone/ControllerImpl.java
@@ -19,6 +19,7 @@
 import static com.android.server.location.timezone.LocationTimeZoneManagerService.debugLog;
 import static com.android.server.location.timezone.LocationTimeZoneManagerService.warnLog;
 import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState;
+import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_DESTROYED;
 import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_PERM_FAILED;
 import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_CERTAIN;
 import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_INITIALIZING;
@@ -31,6 +32,7 @@
 import android.annotation.DurationMillisLong;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.os.RemoteCallback;
 import android.util.IndentingPrintWriter;
 
 import com.android.internal.annotations.GuardedBy;
@@ -117,7 +119,7 @@
         mThreadingDomain.assertCurrentThread();
 
         synchronized (mSharedLock) {
-            debugLog("onEnvironmentConfigChanged()");
+            debugLog("onConfigChanged()");
 
             ConfigurationInternal oldConfig = mCurrentUserConfiguration;
             ConfigurationInternal newConfig = mEnvironment.getCurrentUserConfigurationInternal();
@@ -150,6 +152,23 @@
         return mUncertaintyTimeoutQueue.getQueuedDelayMillis();
     }
 
+    @Override
+    void destroy() {
+        mThreadingDomain.assertCurrentThread();
+
+        synchronized (mSharedLock) {
+            stopProviders();
+            mPrimaryProvider.destroy();
+            mSecondaryProvider.destroy();
+
+            // If the controller has made a "certain" suggestion, it should make an uncertain
+            // suggestion to cancel it.
+            if (mLastSuggestion != null && mLastSuggestion.getZoneIds() != null) {
+                makeSuggestion(createUncertainSuggestion("Controller is destroyed"));
+            }
+        }
+    }
+
     @GuardedBy("mSharedLock")
     private void stopProviders() {
         stopProviderIfStarted(mPrimaryProvider);
@@ -181,8 +200,9 @@
                 provider.stopUpdates();
                 break;
             }
-            case PROVIDER_STATE_PERM_FAILED: {
-                debugLog("Unable to stop " + provider + ": it is perm failed");
+            case PROVIDER_STATE_PERM_FAILED:
+            case PROVIDER_STATE_DESTROYED: {
+                debugLog("Unable to stop " + provider + ": it is terminated.");
                 break;
             }
             default: {
@@ -284,8 +304,9 @@
                 debugLog("No need to start " + provider + ": already started");
                 break;
             }
-            case PROVIDER_STATE_PERM_FAILED: {
-                debugLog("Unable to start " + provider + ": it is perm failed");
+            case PROVIDER_STATE_PERM_FAILED:
+            case PROVIDER_STATE_DESTROYED: {
+                debugLog("Unable to start " + provider + ": it is terminated");
                 break;
             }
             default: {
@@ -302,17 +323,20 @@
 
         synchronized (mSharedLock) {
             switch (providerState.stateEnum) {
-                case PROVIDER_STATE_STOPPED: {
-                    // This should never happen: entering stopped does not trigger a state change.
-                    warnLog("onProviderStateChange: Unexpected state change for stopped provider,"
+                case PROVIDER_STATE_STARTED_INITIALIZING:
+                case PROVIDER_STATE_STOPPED:
+                case PROVIDER_STATE_DESTROYED: {
+                    // This should never happen: entering initializing, stopped or destroyed are
+                    // triggered by the controller so and should not trigger a state change
+                    // callback.
+                    warnLog("onProviderStateChange: Unexpected state change for provider,"
                             + " provider=" + provider);
                     break;
                 }
-                case PROVIDER_STATE_STARTED_INITIALIZING:
                 case PROVIDER_STATE_STARTED_CERTAIN:
                 case PROVIDER_STATE_STARTED_UNCERTAIN: {
-                    // Entering started does not trigger a state change, so this only happens if an
-                    // event is received while the provider is started.
+                    // These are valid and only happen if an event is received while the provider is
+                    // started.
                     debugLog("onProviderStateChange: Received notification of a state change while"
                             + " started, provider=" + provider);
                     handleProviderStartedStateChange(providerState);
@@ -348,17 +372,18 @@
 
         // If a provider has failed, the other may need to be started.
         if (failedProvider == mPrimaryProvider) {
-            if (secondaryCurrentState.stateEnum != PROVIDER_STATE_PERM_FAILED) {
-                // The primary must have failed. Try to start the secondary. This does nothing if
-                // the provider is already started, and will leave the provider in
-                // {started initializing} if the provider is stopped.
+            if (!secondaryCurrentState.isTerminated()) {
+                // Try to start the secondary. This does nothing if the provider is already
+                // started, and will leave the provider in {started initializing} if the provider is
+                // stopped.
                 tryStartProvider(mSecondaryProvider, mCurrentUserConfiguration);
             }
         } else if (failedProvider == mSecondaryProvider) {
-            // No-op: The secondary will only be active if the primary is uncertain or is failed.
-            // So, there the primary should not need to be started when the secondary fails.
+            // No-op: The secondary will only be active if the primary is uncertain or is
+            // terminated. So, there the primary should not need to be started when the secondary
+            // fails.
             if (primaryCurrentState.stateEnum != PROVIDER_STATE_STARTED_UNCERTAIN
-                    && primaryCurrentState.stateEnum != PROVIDER_STATE_PERM_FAILED) {
+                    && !primaryCurrentState.isTerminated()) {
                 warnLog("Secondary provider unexpected reported a failure:"
                         + " failed provider=" + failedProvider.getName()
                         + ", primary provider=" + mPrimaryProvider
@@ -366,19 +391,18 @@
             }
         }
 
-        // If both providers are now failed, the controller needs to tell the next component in the
-        // time zone detection process.
-        if (primaryCurrentState.stateEnum == PROVIDER_STATE_PERM_FAILED
-                && secondaryCurrentState.stateEnum == PROVIDER_STATE_PERM_FAILED) {
+        // If both providers are now terminated, the controller needs to tell the next component in
+        // the time zone detection process.
+        if (primaryCurrentState.isTerminated() && secondaryCurrentState.isTerminated()) {
 
-            // If both providers are newly failed then the controller is uncertain by definition
+            // If both providers are newly terminated then the controller is uncertain by definition
             // and it will never recover so it can send a suggestion immediately.
             cancelUncertaintyTimeout();
 
-            // If both providers are now failed, then a suggestion must be sent informing the time
-            // zone detector that there are no further updates coming in future.
+            // If both providers are now terminated, then a suggestion must be sent informing the
+            // time zone detector that there are no further updates coming in future.
             GeolocationTimeZoneSuggestion suggestion = createUncertainSuggestion(
-                    "Both providers are permanently failed:"
+                    "Both providers are terminated:"
                             + " primary=" + primaryCurrentState.provider
                             + ", secondary=" + secondaryCurrentState.provider);
             makeSuggestion(suggestion);
@@ -553,6 +577,7 @@
         }
     }
 
+    @NonNull
     private static GeolocationTimeZoneSuggestion createUncertainSuggestion(@NonNull String reason) {
         GeolocationTimeZoneSuggestion suggestion = new GeolocationTimeZoneSuggestion(null);
         suggestion.addDebugInfo(reason);
@@ -560,30 +585,42 @@
     }
 
     /**
-     * Passes a {@link SimulatedBinderProviderEvent] to the appropriate provider.
-     * If the provider name does not match a known provider, then the event is logged and discarded.
+     * Passes a test command to the specified provider. If the provider name does not match a
+     * known provider, then the command is logged and discarded.
      */
-    void simulateBinderProviderEvent(@NonNull SimulatedBinderProviderEvent event) {
+    void handleProviderTestCommand(
+            @NonNull String providerName, @NonNull TestCommand testCommand,
+            @Nullable RemoteCallback callback) {
         mThreadingDomain.assertCurrentThread();
 
-        String targetProviderName = event.getProviderName();
+        LocationTimeZoneProvider targetProvider = getLocationTimeZoneProvider(providerName);
+        if (targetProvider == null) {
+            warnLog("Unable to process test command:"
+                    + " providerName=" + providerName + ", testCommand=" + testCommand);
+            return;
+        }
+
+        synchronized (mSharedLock) {
+            try {
+                targetProvider.handleTestCommand(testCommand, callback);
+            } catch (Exception e) {
+                warnLog("Unable to process test command:"
+                        + " providerName=" + providerName + ", testCommand=" + testCommand, e);
+            }
+        }
+    }
+
+    @Nullable
+    private LocationTimeZoneProvider getLocationTimeZoneProvider(@NonNull String providerName) {
         LocationTimeZoneProvider targetProvider;
-        if (Objects.equals(mPrimaryProvider.getName(), targetProviderName)) {
+        if (Objects.equals(mPrimaryProvider.getName(), providerName)) {
             targetProvider = mPrimaryProvider;
-        } else if (Objects.equals(mSecondaryProvider.getName(), targetProviderName)) {
+        } else if (Objects.equals(mSecondaryProvider.getName(), providerName)) {
             targetProvider = mSecondaryProvider;
         } else {
-            warnLog("Unable to process simulated binder provider event,"
-                    + " unknown providerName in event=" + event);
-            return;
+            warnLog("Bad providerName=" + providerName);
+            targetProvider = null;
         }
-        if (!(targetProvider instanceof BinderLocationTimeZoneProvider)) {
-            warnLog("Unable to process simulated binder provider event,"
-                    + " provider=" + targetProvider
-                    + " is not a " + BinderLocationTimeZoneProvider.class
-                    + ", event=" + event);
-            return;
-        }
-        ((BinderLocationTimeZoneProvider) targetProvider).simulateBinderProviderEvent(event);
+        return targetProvider;
     }
 }
diff --git a/services/core/java/com/android/server/location/timezone/HandlerThreadingDomain.java b/services/core/java/com/android/server/location/timezone/HandlerThreadingDomain.java
index b59898a..3055ff8 100644
--- a/services/core/java/com/android/server/location/timezone/HandlerThreadingDomain.java
+++ b/services/core/java/com/android/server/location/timezone/HandlerThreadingDomain.java
@@ -21,6 +21,10 @@
 import android.os.Handler;
 
 import java.util.Objects;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
 
 /**
  * The real implementation of {@link ThreadingDomain} that uses a {@link Handler}.
@@ -58,6 +62,38 @@
     }
 
     @Override
+    <V> V postAndWait(@NonNull Callable<V> callable, @DurationMillisLong long durationMillis)
+            throws Exception {
+        // Calling this on this domain's thread would lead to deadlock.
+        assertNotCurrentThread();
+
+        AtomicReference<V> resultReference = new AtomicReference<>();
+        AtomicReference<Exception> exceptionReference = new AtomicReference<>();
+        CountDownLatch latch = new CountDownLatch(1);
+        post(() -> {
+            try {
+                resultReference.set(callable.call());
+            } catch (Exception e) {
+                exceptionReference.set(e);
+            } finally {
+                latch.countDown();
+            }
+        });
+
+        try {
+            if (!latch.await(durationMillis, TimeUnit.MILLISECONDS)) {
+                throw new RuntimeException("Timed out");
+            }
+        } catch (InterruptedException e) {
+            throw new RuntimeException(e);
+        }
+        if (exceptionReference.get() != null) {
+            throw exceptionReference.get();
+        }
+        return resultReference.get();
+    }
+
+    @Override
     void postDelayed(@NonNull Runnable r, @DurationMillisLong long delayMillis) {
         getHandler().postDelayed(r, delayMillis);
     }
diff --git a/services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerService.java b/services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerService.java
index ab64f97..9f159fb 100644
--- a/services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerService.java
+++ b/services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerService.java
@@ -16,12 +16,21 @@
 
 package com.android.server.location.timezone;
 
+import static android.app.time.LocationTimeZoneManager.PRIMARY_PROVIDER_NAME;
+import static android.app.time.LocationTimeZoneManager.SECONDARY_PROVIDER_NAME;
+import static android.app.time.LocationTimeZoneManager.SYSTEM_PROPERTY_KEY_PROVIDER_MODE_OVERRIDE_PRIMARY;
+import static android.app.time.LocationTimeZoneManager.SYSTEM_PROPERTY_KEY_PROVIDER_MODE_OVERRIDE_SECONDARY;
+import static android.app.time.LocationTimeZoneManager.SYSTEM_PROPERTY_VALUE_PROVIDER_MODE_DISABLED;
+import static android.app.time.LocationTimeZoneManager.SYSTEM_PROPERTY_VALUE_PROVIDER_MODE_SIMULATED;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
 import android.content.res.Resources;
 import android.os.Binder;
+import android.os.Bundle;
 import android.os.Handler;
+import android.os.RemoteCallback;
 import android.os.ResultReceiver;
 import android.os.ShellCallback;
 import android.os.SystemProperties;
@@ -40,7 +49,11 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
+import java.time.Duration;
 import java.util.Objects;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
 
 /**
  * A service class that acts as a container for the {@link LocationTimeZoneProviderController},
@@ -113,11 +126,7 @@
 
     static final String TAG = "LocationTZDetector";
 
-    static final String PRIMARY_PROVIDER_NAME = "primary";
-    static final String SECONDARY_PROVIDER_NAME = "secondary";
-
-    private static final String SIMULATION_MODE_SYSTEM_PROPERTY_PREFIX =
-            "persist.sys.location_tz_simulation_mode.";
+    private static final long BLOCKING_OP_WAIT_DURATION_MILLIS = Duration.ofSeconds(20).toMillis();
 
     private static final String ATTRIBUTION_TAG = "LocationTimeZoneService";
 
@@ -144,10 +153,14 @@
     /** The shared lock from {@link #mThreadingDomain}. */
     @NonNull private final Object mSharedLock;
 
-    // Lazily initialized. Non-null and effectively final after onSystemThirdPartyAppsCanStart().
+    // Lazily initialized. Can be null if the service has been stopped.
     @GuardedBy("mSharedLock")
     private ControllerImpl mLocationTimeZoneDetectorController;
 
+    // Lazily initialized. Can be null if the service has been stopped.
+    @GuardedBy("mSharedLock")
+    private ControllerEnvironmentImpl mEnvironment;
+
     LocationTimeZoneManagerService(Context context) {
         mContext = context.createAttributionContext(ATTRIBUTION_TAG);
         mHandler = FgThread.getHandler();
@@ -169,32 +182,59 @@
     }
 
     void onSystemThirdPartyAppsCanStart() {
-        // Called on an arbitrary thread during initialization.
-        synchronized (mSharedLock) {
-            LocationTimeZoneProvider primary = createPrimaryProvider();
-            LocationTimeZoneProvider secondary = createSecondaryProvider();
-            mLocationTimeZoneDetectorController =
-                    new ControllerImpl(mThreadingDomain, primary, secondary);
-            ControllerCallbackImpl callback = new ControllerCallbackImpl(mThreadingDomain);
-            ControllerEnvironmentImpl environment = new ControllerEnvironmentImpl(
-                    mThreadingDomain, mLocationTimeZoneDetectorController);
+        // Called on an arbitrary thread during initialization. We do not want to wait for
+        // completion as it would delay boot.
+        final boolean waitForCompletion = false;
+        startInternal(waitForCompletion);
+    }
 
-            // Initialize the controller on the mThreadingDomain thread: this ensures that the
-            // ThreadingDomain requirements for the controller / environment methods are honored.
-            mThreadingDomain.post(() ->
-                    mLocationTimeZoneDetectorController.initialize(environment, callback));
+    /**
+     * Starts the service during server initialization or during tests after a call to
+     * {@link #stop()}.
+     */
+    void start() {
+        enforceManageTimeZoneDetectorPermission();
+
+        final boolean waitForCompletion = true;
+        startInternal(waitForCompletion);
+    }
+
+    /**
+     * Starts the service during server initialization or during tests after a call to
+     * {@link #stop()}.
+     *
+     * <p>To avoid tests needing to sleep, when {@code waitForCompletion} is {@code true}, this
+     * method will not return until all the system server components have started.
+     */
+    private void startInternal(boolean waitForCompletion) {
+        Runnable runnable = () -> {
+            synchronized (mSharedLock) {
+                if (mLocationTimeZoneDetectorController == null) {
+                    LocationTimeZoneProvider primary = createPrimaryProvider();
+                    LocationTimeZoneProvider secondary = createSecondaryProvider();
+                    mLocationTimeZoneDetectorController =
+                            new ControllerImpl(mThreadingDomain, primary, secondary);
+                    ControllerCallbackImpl callback = new ControllerCallbackImpl(
+                            mThreadingDomain);
+                    mEnvironment = new ControllerEnvironmentImpl(
+                            mThreadingDomain, mLocationTimeZoneDetectorController);
+                    mLocationTimeZoneDetectorController.initialize(mEnvironment, callback);
+                }
+            }
+        };
+        if (waitForCompletion) {
+            mThreadingDomain.postAndWait(runnable, BLOCKING_OP_WAIT_DURATION_MILLIS);
+        } else {
+            mThreadingDomain.post(runnable);
         }
     }
 
     private LocationTimeZoneProvider createPrimaryProvider() {
-        Resources resources = mContext.getResources();
-        if (!resources.getBoolean(R.bool.config_enablePrimaryLocationTimeZoneProvider)) {
-            return new NullLocationTimeZoneProvider(mThreadingDomain, PRIMARY_PROVIDER_NAME);
-        }
-
         LocationTimeZoneProviderProxy proxy;
         if (isInSimulationMode(PRIMARY_PROVIDER_NAME)) {
             proxy = new SimulatedLocationTimeZoneProviderProxy(mContext, mThreadingDomain);
+        } else if (isDisabled(PRIMARY_PROVIDER_NAME)) {
+            proxy = new NullLocationTimeZoneProviderProxy(mContext, mThreadingDomain);
         } else {
             proxy = new RealLocationTimeZoneProviderProxy(
                     mContext,
@@ -209,14 +249,11 @@
     }
 
     private LocationTimeZoneProvider createSecondaryProvider() {
-        Resources resources = mContext.getResources();
-        if (!resources.getBoolean(R.bool.config_enableSecondaryLocationTimeZoneProvider)) {
-            return new NullLocationTimeZoneProvider(mThreadingDomain, SECONDARY_PROVIDER_NAME);
-        }
-
         LocationTimeZoneProviderProxy proxy;
         if (isInSimulationMode(SECONDARY_PROVIDER_NAME)) {
             proxy = new SimulatedLocationTimeZoneProviderProxy(mContext, mThreadingDomain);
+        } else if (isDisabled(SECONDARY_PROVIDER_NAME)) {
+            proxy = new NullLocationTimeZoneProviderProxy(mContext, mThreadingDomain);
         } else {
             proxy = new RealLocationTimeZoneProviderProxy(
                     mContext,
@@ -230,9 +267,76 @@
         return new BinderLocationTimeZoneProvider(mThreadingDomain, SECONDARY_PROVIDER_NAME, proxy);
     }
 
-    private boolean isInSimulationMode(String providerName) {
-        return SystemProperties.getBoolean(
-                SIMULATION_MODE_SYSTEM_PROPERTY_PREFIX + providerName, false);
+    /** Used for bug triage and in tests to simulate provider events. */
+    private static boolean isInSimulationMode(String providerName) {
+        return isProviderModeSetInSystemProperties(providerName,
+                SYSTEM_PROPERTY_VALUE_PROVIDER_MODE_SIMULATED);
+    }
+
+    /** Used for bug triage, tests and experiments to remove a provider. */
+    private boolean isDisabled(String providerName) {
+        return !isProviderEnabledInConfig(providerName)
+                || isProviderModeSetInSystemProperties(
+                        providerName, SYSTEM_PROPERTY_VALUE_PROVIDER_MODE_DISABLED);
+    }
+
+    private boolean isProviderEnabledInConfig(String providerName) {
+        int providerEnabledConfigId;
+        switch (providerName) {
+            case PRIMARY_PROVIDER_NAME: {
+                providerEnabledConfigId = R.bool.config_enablePrimaryLocationTimeZoneProvider;
+                break;
+            }
+            case SECONDARY_PROVIDER_NAME: {
+                providerEnabledConfigId = R.bool.config_enableSecondaryLocationTimeZoneProvider;
+                break;
+            }
+            default: {
+                throw new IllegalArgumentException(providerName);
+            }
+        }
+        Resources resources = mContext.getResources();
+        return resources.getBoolean(providerEnabledConfigId);
+    }
+
+    private static boolean isProviderModeSetInSystemProperties(
+            @NonNull String providerName, @NonNull String mode) {
+        String systemPropertyKey;
+        switch (providerName) {
+            case PRIMARY_PROVIDER_NAME: {
+                systemPropertyKey = SYSTEM_PROPERTY_KEY_PROVIDER_MODE_OVERRIDE_PRIMARY;
+                break;
+            }
+            case SECONDARY_PROVIDER_NAME: {
+                systemPropertyKey = SYSTEM_PROPERTY_KEY_PROVIDER_MODE_OVERRIDE_SECONDARY;
+                break;
+            }
+            default: {
+                throw new IllegalArgumentException(providerName);
+            }
+        }
+
+        String systemPropertyProviderMode = SystemProperties.get(systemPropertyKey, null);
+        return Objects.equals(systemPropertyProviderMode, mode);
+    }
+
+    /**
+     * Stops the service for tests. To avoid tests needing to sleep, this method will not return
+     * until all the system server components have stopped.
+     */
+    void stop() {
+        enforceManageTimeZoneDetectorPermission();
+
+        mThreadingDomain.postAndWait(() -> {
+            synchronized (mSharedLock) {
+                if (mLocationTimeZoneDetectorController != null) {
+                    mLocationTimeZoneDetectorController.destroy();
+                    mLocationTimeZoneDetectorController = null;
+                    mEnvironment.destroy();
+                    mEnvironment = null;
+                }
+            }
+        }, BLOCKING_OP_WAIT_DURATION_MILLIS);
     }
 
     @Override
@@ -244,19 +348,45 @@
     }
 
     /**
-     * Asynchronously passes a {@link SimulatedBinderProviderEvent] to the appropriate provider.
-     * The device must be in simulation mode, otherwise an {@link IllegalStateException} will be
-     * thrown.
+     * Passes a {@link TestCommand} to the specified provider and waits for the response.
      */
-    void simulateBinderProviderEvent(SimulatedBinderProviderEvent event)
-            throws IllegalStateException {
-        if (!isInSimulationMode(event.getProviderName())) {
-            throw new IllegalStateException("Use \"setprop "
-                    + SIMULATION_MODE_SYSTEM_PROPERTY_PREFIX + event.getProviderName()
-                    + " 1\" and reboot before injecting simulated binder events.");
+    @NonNull
+    Bundle handleProviderTestCommand(
+            @NonNull String providerName, @NonNull TestCommand testCommand) {
+        enforceManageTimeZoneDetectorPermission();
+
+        // Because this method blocks and posts work to the threading domain thread, it would cause
+        // a deadlock if it were called by the threading domain thread.
+        mThreadingDomain.assertNotCurrentThread();
+
+        AtomicReference<Bundle> resultReference = new AtomicReference<>();
+        CountDownLatch latch = new CountDownLatch(1);
+        RemoteCallback remoteCallback = new RemoteCallback(x -> {
+            resultReference.set(x);
+            latch.countDown();
+        });
+
+        mThreadingDomain.post(() -> {
+            synchronized (mSharedLock) {
+                if (mLocationTimeZoneDetectorController == null) {
+                    remoteCallback.sendResult(null);
+                    return;
+                }
+                mLocationTimeZoneDetectorController.handleProviderTestCommand(
+                        providerName, testCommand, remoteCallback);
+            }
+        });
+
+        try {
+            // Wait, but not indefinitely.
+            if (!latch.await(BLOCKING_OP_WAIT_DURATION_MILLIS, TimeUnit.MILLISECONDS)) {
+                throw new RuntimeException("Command did not complete in time");
+            }
+        } catch (InterruptedException e) {
+            throw new AssertionError(e);
         }
-        mThreadingDomain.post(
-                () -> mLocationTimeZoneDetectorController.simulateBinderProviderEvent(event));
+
+        return resultReference.get();
     }
 
     @Override
@@ -270,7 +400,7 @@
             ipw.println("LocationTimeZoneManagerService:");
             ipw.increaseIndent();
             if (mLocationTimeZoneDetectorController == null) {
-                ipw.println("{Uninitialized}");
+                ipw.println("{Stopped}");
             } else {
                 mLocationTimeZoneDetectorController.dump(ipw, args);
             }
@@ -285,8 +415,18 @@
     }
 
     static void warnLog(String msg) {
+        warnLog(msg, null);
+    }
+
+    static void warnLog(String msg, @Nullable Throwable t) {
         if (Log.isLoggable(TAG, Log.WARN)) {
-            Slog.w(TAG, msg);
+            Slog.w(TAG, msg, t);
         }
     }
+
+    private void enforceManageTimeZoneDetectorPermission() {
+        mContext.enforceCallingPermission(
+                android.Manifest.permission.MANAGE_TIME_AND_ZONE_DETECTION,
+                "manage time and time zone detection");
+    }
 }
diff --git a/services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerShellCommand.java b/services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerShellCommand.java
index 7c3b891..554df07 100644
--- a/services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerShellCommand.java
+++ b/services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerShellCommand.java
@@ -15,13 +15,30 @@
  */
 package com.android.server.location.timezone;
 
+import static android.app.time.LocationTimeZoneManager.PRIMARY_PROVIDER_NAME;
+import static android.app.time.LocationTimeZoneManager.SECONDARY_PROVIDER_NAME;
+import static android.app.time.LocationTimeZoneManager.SHELL_COMMAND_SEND_PROVIDER_TEST_COMMAND;
+import static android.app.time.LocationTimeZoneManager.SHELL_COMMAND_START;
+import static android.app.time.LocationTimeZoneManager.SHELL_COMMAND_STOP;
+import static android.app.time.LocationTimeZoneManager.SYSTEM_PROPERTY_KEY_PROVIDER_MODE_OVERRIDE_PRIMARY;
+import static android.app.time.LocationTimeZoneManager.SYSTEM_PROPERTY_KEY_PROVIDER_MODE_OVERRIDE_SECONDARY;
+import static android.app.time.LocationTimeZoneManager.SYSTEM_PROPERTY_VALUE_PROVIDER_MODE_DISABLED;
+import static android.app.time.LocationTimeZoneManager.SYSTEM_PROPERTY_VALUE_PROVIDER_MODE_SIMULATED;
+
+import android.annotation.NonNull;
+import android.os.Bundle;
 import android.os.ShellCommand;
 
 import java.io.PrintWriter;
+import java.util.Arrays;
+import java.util.List;
 
 /** Implements the shell command interface for {@link LocationTimeZoneManagerService}. */
 class LocationTimeZoneManagerShellCommand extends ShellCommand {
 
+    private static final List<String> VALID_PROVIDER_NAMES =
+            Arrays.asList(PRIMARY_PROVIDER_NAME, SECONDARY_PROVIDER_NAME);
+
     private final LocationTimeZoneManagerService mService;
 
     LocationTimeZoneManagerShellCommand(LocationTimeZoneManagerService service) {
@@ -35,8 +52,14 @@
         }
 
         switch (cmd) {
-            case "simulate_binder": {
-                return runSimulateBinderEvent();
+            case SHELL_COMMAND_START: {
+                return runStart();
+            }
+            case SHELL_COMMAND_STOP: {
+                return runStop();
+            }
+            case SHELL_COMMAND_SEND_PROVIDER_TEST_COMMAND: {
+                return runSendProviderTestCommand();
             }
             default: {
                 return handleDefaultCommands(cmd);
@@ -44,37 +67,112 @@
         }
     }
 
-    private int runSimulateBinderEvent() {
-        PrintWriter outPrintWriter = getOutPrintWriter();
-
-        SimulatedBinderProviderEvent simulatedProviderBinderEvent;
-        try {
-            simulatedProviderBinderEvent = SimulatedBinderProviderEvent.createFromArgs(this);
-        } catch (IllegalArgumentException e) {
-            outPrintWriter.println("Error: " + e.getMessage());
-            return 1;
-        }
-
-        outPrintWriter.println("Injecting: " + simulatedProviderBinderEvent);
-        try {
-            mService.simulateBinderProviderEvent(simulatedProviderBinderEvent);
-        } catch (IllegalStateException e) {
-            outPrintWriter.println("Error: " + e.getMessage());
-            return 2;
-        }
-        return 0;
-    }
-
     @Override
     public void onHelp() {
         final PrintWriter pw = getOutPrintWriter();
         pw.println("Location Time Zone Manager (location_time_zone_manager) commands:");
         pw.println("  help");
         pw.println("    Print this help text.");
-        pw.println("  simulate_binder");
-        pw.println("    <simulated provider binder event>");
+        pw.printf("  %s\n", SHELL_COMMAND_START);
+        pw.println("    Starts the location_time_zone_manager, creating time zone providers.");
+        pw.printf("  %s\n", SHELL_COMMAND_STOP);
+        pw.println("    Stops the location_time_zone_manager, destroying time zone providers.");
+        pw.printf("  %s <provider name> <test command>\n",
+                SHELL_COMMAND_SEND_PROVIDER_TEST_COMMAND);
+        pw.println("    Passes a test command to the named provider.");
         pw.println();
-        SimulatedBinderProviderEvent.printCommandLineOpts(pw);
+        pw.printf("%s details:\n", SHELL_COMMAND_SEND_PROVIDER_TEST_COMMAND);
         pw.println();
+        pw.printf("<provider name> = One of %s\n", VALID_PROVIDER_NAMES);
+        pw.println();
+        pw.println("<test command> encoding:");
+        pw.println();
+        TestCommand.printShellCommandEncodingHelp(pw);
+        pw.println();
+        pw.printf("Provider modes can be modified by setting the \"%s\" or \"%s\"\n system"
+                        + " property and restarting the service or rebooting the device.\n",
+                SYSTEM_PROPERTY_KEY_PROVIDER_MODE_OVERRIDE_PRIMARY,
+                SYSTEM_PROPERTY_KEY_PROVIDER_MODE_OVERRIDE_SECONDARY);
+        pw.println("Values are:");
+        pw.printf("  %s - simulation mode (see below for commands)\n",
+                SYSTEM_PROPERTY_VALUE_PROVIDER_MODE_SIMULATED);
+        pw.printf("  %s - disabled mode\n", SYSTEM_PROPERTY_VALUE_PROVIDER_MODE_DISABLED);
+        pw.println();
+        pw.println("Simulated providers can be used to test the system server behavior or to"
+                + " reproduce bugs without the complexity of using real providers.");
+        pw.println();
+        pw.println("The test commands for simulated providers are:");
+        SimulatedLocationTimeZoneProviderProxy.printTestCommandShellHelp(pw);
+        pw.println();
+        pw.println("Test commands cannot currently be passed to real provider implementations.");
+        pw.println();
+    }
+
+    private int runStart() {
+        try {
+            mService.start();
+        } catch (RuntimeException e) {
+            reportError(e);
+            return 1;
+        }
+        PrintWriter outPrintWriter = getOutPrintWriter();
+        outPrintWriter.println("Service started");
+        return 0;
+    }
+
+    private int runStop() {
+        try {
+            mService.stop();
+        } catch (RuntimeException e) {
+            reportError(e);
+            return 1;
+        }
+        PrintWriter outPrintWriter = getOutPrintWriter();
+        outPrintWriter.println("Service stopped");
+        return 0;
+    }
+
+    private int runSendProviderTestCommand() {
+        PrintWriter outPrintWriter = getOutPrintWriter();
+
+        String providerName;
+        TestCommand testCommand;
+        try {
+            providerName = validateProviderName(getNextArgRequired());
+            testCommand = createTestCommandFromNextShellArg();
+        } catch (RuntimeException e) {
+            reportError(e);
+            return 1;
+        }
+
+        outPrintWriter.println("Injecting testCommand=" + testCommand
+                + " to providerName=" + providerName);
+        try {
+            Bundle result = mService.handleProviderTestCommand(providerName, testCommand);
+            outPrintWriter.println(result);
+        } catch (RuntimeException e) {
+            reportError(e);
+            return 2;
+        }
+        return 0;
+    }
+
+    @NonNull
+    private TestCommand createTestCommandFromNextShellArg() {
+        return TestCommand.createFromShellCommandArgs(this);
+    }
+
+    private void reportError(Throwable e) {
+        PrintWriter errPrintWriter = getErrPrintWriter();
+        errPrintWriter.println("Error: ");
+        e.printStackTrace(errPrintWriter);
+    }
+
+    @NonNull
+    static String validateProviderName(@NonNull String value) {
+        if (!VALID_PROVIDER_NAMES.contains(value)) {
+            throw new IllegalArgumentException("Unknown provider name=" + value);
+        }
+        return value;
     }
 }
diff --git a/services/core/java/com/android/server/location/timezone/LocationTimeZoneProvider.java b/services/core/java/com/android/server/location/timezone/LocationTimeZoneProvider.java
index 8b51ab4..132c167 100644
--- a/services/core/java/com/android/server/location/timezone/LocationTimeZoneProvider.java
+++ b/services/core/java/com/android/server/location/timezone/LocationTimeZoneProvider.java
@@ -16,7 +16,12 @@
 
 package com.android.server.location.timezone;
 
+import static android.service.timezone.TimeZoneProviderService.TEST_COMMAND_RESULT_ERROR_KEY;
+import static android.service.timezone.TimeZoneProviderService.TEST_COMMAND_RESULT_SUCCESS_KEY;
+
 import static com.android.server.location.timezone.LocationTimeZoneManagerService.debugLog;
+import static com.android.server.location.timezone.LocationTimeZoneManagerService.warnLog;
+import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_DESTROYED;
 import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_PERM_FAILED;
 import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_CERTAIN;
 import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_INITIALIZING;
@@ -30,7 +35,9 @@
 import android.annotation.IntDef;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.os.Bundle;
 import android.os.Handler;
+import android.os.RemoteCallback;
 import android.os.SystemClock;
 
 import com.android.internal.annotations.GuardedBy;
@@ -46,8 +53,7 @@
 
 /**
  * A facade used by the {@link LocationTimeZoneProviderController} to interact with a location time
- * zone provider. The provider could have a binder implementation with logic running in another
- * process, or could be a stubbed instance when no real provider is registered.
+ * zone provider. The provider implementation will typically have logic running in another process.
  *
  * <p>The provider is supplied with a {@link ProviderListener} via {@link
  * #initialize(ProviderListener)}. This starts communication of asynchronous detection / error
@@ -55,6 +61,10 @@
  * ProviderListener#onProviderStateChange} method. This call must be made on the
  * {@link Handler} thread from the {@link ThreadingDomain} passed to the constructor.
  *
+ * <p>This class is also responsible for monitoring the initialization timeout for a provider. i.e.
+ * if the provider fails to send its first suggestion within a certain time, this is the component
+ * responsible for generating the necessary "uncertain" event.
+ *
  * <p>All incoming calls from the controller except for {@link
  * LocationTimeZoneProvider#dump(android.util.IndentingPrintWriter, String[])} will be made on the
  * {@link Handler} thread of the {@link ThreadingDomain} passed to the constructor.
@@ -80,7 +90,7 @@
         @IntDef(prefix = "PROVIDER_STATE_",
                 value = { PROVIDER_STATE_UNKNOWN, PROVIDER_STATE_STARTED_INITIALIZING,
                 PROVIDER_STATE_STARTED_CERTAIN, PROVIDER_STATE_STARTED_UNCERTAIN,
-                PROVIDER_STATE_STOPPED, PROVIDER_STATE_PERM_FAILED })
+                PROVIDER_STATE_STOPPED, PROVIDER_STATE_PERM_FAILED, PROVIDER_STATE_DESTROYED })
         @interface ProviderStateEnum {}
 
         /**
@@ -111,12 +121,19 @@
         static final int PROVIDER_STATE_STOPPED = 4;
 
         /**
-         * The provider has failed and cannot be re-started.
+         * The provider has failed and cannot be restarted. This is a terminated state triggered by
+         * the provider itself.
          *
-         * Providers may enter this state after a provider is started.
+         * Providers may enter this state any time after a provider is started.
          */
         static final int PROVIDER_STATE_PERM_FAILED = 5;
 
+        /**
+         * The provider has been destroyed by the controller and cannot be restarted. Similar to
+         * {@link #PROVIDER_STATE_PERM_FAILED} except that a provider is set into this state.
+         */
+        static final int PROVIDER_STATE_DESTROYED = 6;
+
         /** The {@link LocationTimeZoneProvider} the state is for. */
         public final @NonNull LocationTimeZoneProvider provider;
 
@@ -195,12 +212,14 @@
                 case PROVIDER_STATE_STARTED_INITIALIZING:
                 case PROVIDER_STATE_STARTED_CERTAIN:
                 case PROVIDER_STATE_STARTED_UNCERTAIN: {
-                    // These can go to each other or PROVIDER_STATE_PERM_FAILED.
+                    // These can go to each other or either of PROVIDER_STATE_PERM_FAILED and
+                    // PROVIDER_STATE_DESTROYED.
                     break;
                 }
-                case PROVIDER_STATE_PERM_FAILED: {
+                case PROVIDER_STATE_PERM_FAILED:
+                case PROVIDER_STATE_DESTROYED: {
                     throw new IllegalArgumentException("Illegal transition out of "
-                            + prettyPrintStateEnum(PROVIDER_STATE_UNKNOWN));
+                            + prettyPrintStateEnum(this.stateEnum));
                 }
                 default: {
                     throw new IllegalArgumentException("Invalid this.stateEnum=" + this.stateEnum);
@@ -231,10 +250,12 @@
                     }
                     break;
                 }
-                case PROVIDER_STATE_PERM_FAILED: {
+                case PROVIDER_STATE_PERM_FAILED:
+                case PROVIDER_STATE_DESTROYED: {
                     if (event != null || currentUserConfig != null) {
                         throw new IllegalArgumentException(
-                                "Perf failed state: event and currentUserConfig must be null"
+                                "Terminal state: event and currentUserConfig must be null"
+                                        + ", newStateEnum=" + newStateEnum
                                         + ", event=" + event
                                         + ", currentUserConfig=" + currentUserConfig);
                     }
@@ -254,6 +275,12 @@
                     || stateEnum == PROVIDER_STATE_STARTED_UNCERTAIN;
         }
 
+        /** Returns {@code true} if {@link #stateEnum} is one of the terminated states. */
+        boolean isTerminated() {
+            return stateEnum == PROVIDER_STATE_PERM_FAILED
+                    || stateEnum == PROVIDER_STATE_DESTROYED;
+        }
+
         @Override
         public String toString() {
             // this.provider is omitted deliberately to avoid recursion, since the provider holds
@@ -298,6 +325,8 @@
                     return "Started uncertain (" + PROVIDER_STATE_STARTED_UNCERTAIN + ")";
                 case PROVIDER_STATE_PERM_FAILED:
                     return "Perm failure (" + PROVIDER_STATE_PERM_FAILED + ")";
+                case PROVIDER_STATE_DESTROYED:
+                    return "Destroyed (" + PROVIDER_STATE_DESTROYED + ")";
                 case PROVIDER_STATE_UNKNOWN:
                 default:
                     return "Unknown (" + state + ")";
@@ -334,7 +363,7 @@
     }
 
     /**
-     * Called before the provider is first used.
+     * Initializes the provider. Called before the provider is first used.
      */
     final void initialize(@NonNull ProviderListener providerListener) {
         mThreadingDomain.assertCurrentThread();
@@ -345,21 +374,55 @@
             }
             mProviderListener = Objects.requireNonNull(providerListener);
             ProviderState currentState = ProviderState.createStartingState(this);
-            ProviderState newState = currentState.newState(
+            currentState = currentState.newState(
                     PROVIDER_STATE_STOPPED, null, null,
                     "initialize() called");
-            setCurrentState(newState, false);
+            setCurrentState(currentState, false);
 
-            onInitialize();
+            // Guard against uncaught exceptions due to initialization problems.
+            try {
+                onInitialize();
+            } catch (RuntimeException e) {
+                warnLog("Unable to initialize the provider", e);
+                currentState = currentState
+                        .newState(PROVIDER_STATE_PERM_FAILED, null, null,
+                                "Provider failed to initialize");
+                setCurrentState(currentState, true);
+            }
         }
     }
 
     /**
      * Implemented by subclasses to do work during {@link #initialize}.
      */
+    @GuardedBy("mSharedLock")
     abstract void onInitialize();
 
     /**
+     * Destroys the provider. Called after the provider is stopped. This instance will not be called
+     * again by the {@link LocationTimeZoneProviderController}.
+     */
+    final void destroy() {
+        mThreadingDomain.assertCurrentThread();
+
+        synchronized (mSharedLock) {
+            ProviderState currentState = mCurrentState.get();
+            if (!currentState.isTerminated()) {
+                ProviderState destroyedState = currentState
+                        .newState(PROVIDER_STATE_DESTROYED, null, null, "destroy() called");
+                setCurrentState(destroyedState, false);
+                onDestroy();
+            }
+        }
+    }
+
+    /**
+     * Implemented by subclasses to do work during {@link #destroy()}.
+     */
+    @GuardedBy("mSharedLock")
+    abstract void onDestroy();
+
+    /**
      * Set the current state, for use by this class and subclasses only. If {@code #notifyChanges}
      * is {@code true} and {@code newState} is not equal to the old state, then {@link
      * ProviderListener#onProviderStateChange(ProviderState)} must be called on
@@ -445,17 +508,23 @@
                         PROVIDER_STATE_STARTED_UNCERTAIN, null /* event */,
                         currentState.currentUserConfiguration, "initialization timeout");
                 setCurrentState(newState, true);
+            } else {
+                warnLog("handleInitializationTimeout: Initialization timeout triggered when in"
+                        + " an unexpected state=" + currentState);
             }
         }
     }
 
     /**
-     * Implemented by subclasses to do work during {@link #startUpdates}.
+     * Implemented by subclasses to do work during {@link #startUpdates}. This is where the logic
+     * to start the real provider should be implemented.
+     *
+     * @param initializationTimeout the initialization timeout to pass to the real provider
      */
     abstract void onStartUpdates(@NonNull Duration initializationTimeout);
 
     /**
-     * Stops the provider. It is an error* to call this method except when the {@link
+     * Stops the provider. It is an error to call this method except when the {@link
      * #getCurrentState()} is one of the started states. This method must be
      * called using the handler thread from the {@link ThreadingDomain}.
      */
@@ -483,6 +552,23 @@
      */
     abstract void onStopUpdates();
 
+    /**
+     * Overridden by subclasses to handle the supplied {@link TestCommand}. If {@code callback} is
+     * non-null, the default implementation sends a result {@link Bundle} with {@link
+     * android.service.timezone.TimeZoneProviderService#TEST_COMMAND_RESULT_SUCCESS_KEY} set to
+     * {@code false} and a "Not implemented" error message.
+     */
+    void handleTestCommand(@NonNull TestCommand testCommand, @Nullable RemoteCallback callback) {
+        Objects.requireNonNull(testCommand);
+
+        if (callback != null) {
+            Bundle result = new Bundle();
+            result.putBoolean(TEST_COMMAND_RESULT_SUCCESS_KEY, false);
+            result.putString(TEST_COMMAND_RESULT_ERROR_KEY, "Not implemented");
+            callback.sendResult(result);
+        }
+    }
+
     /** For subclasses to invoke when a {@link TimeZoneProviderEvent} has been received. */
     final void handleTimeZoneProviderEvent(@NonNull TimeZoneProviderEvent timeZoneProviderEvent) {
         mThreadingDomain.assertCurrentThread();
@@ -495,11 +581,12 @@
             ProviderState currentState = mCurrentState.get();
             int eventType = timeZoneProviderEvent.getType();
             switch (currentState.stateEnum) {
+                case PROVIDER_STATE_DESTROYED:
                 case PROVIDER_STATE_PERM_FAILED: {
-                    // After entering perm failed, there is nothing to do. The remote peer is
+                    // After entering a terminated state, there is nothing to do. The remote peer is
                     // supposed to stop sending events after it has reported perm failure.
-                    logWarn("handleTimeZoneProviderEvent: Event=" + timeZoneProviderEvent
-                            + " received for provider=" + this + " when in failed state");
+                    warnLog("handleTimeZoneProviderEvent: Event=" + timeZoneProviderEvent
+                            + " received for provider=" + this + " when in terminated state");
                     return;
                 }
                 case PROVIDER_STATE_STOPPED: {
@@ -509,7 +596,7 @@
                                     + " Failure event=" + timeZoneProviderEvent
                                     + " received for stopped provider=" + this
                                     + ", entering permanently failed state";
-                            logWarn(msg);
+                            warnLog(msg);
                             ProviderState newState = currentState.newState(
                                     PROVIDER_STATE_PERM_FAILED, null, null, msg);
                             setCurrentState(newState, true);
@@ -522,7 +609,7 @@
                         case EVENT_TYPE_UNCERTAIN: {
                             // Any geolocation-related events received for a stopped provider are
                             // ignored: they should not happen.
-                            logWarn("handleTimeZoneProviderEvent:"
+                            warnLog("handleTimeZoneProviderEvent:"
                                     + " event=" + timeZoneProviderEvent
                                     + " received for stopped provider=" + this
                                     + ", ignoring");
@@ -544,7 +631,7 @@
                                     + " Failure event=" + timeZoneProviderEvent
                                     + " received for provider=" + this
                                     + ", entering permanently failed state";
-                            logWarn(msg);
+                            warnLog(msg);
                             ProviderState newState = currentState.newState(
                                     PROVIDER_STATE_PERM_FAILED, null, null, msg);
                             setCurrentState(newState, true);
@@ -584,11 +671,6 @@
         }
     }
 
-    /**
-     * Implemented by subclasses.
-     */
-    abstract void logWarn(String msg);
-
     @GuardedBy("mSharedLock")
     private void assertIsStarted() {
         ProviderState currentState = mCurrentState.get();
diff --git a/services/core/java/com/android/server/location/timezone/LocationTimeZoneProviderController.java b/services/core/java/com/android/server/location/timezone/LocationTimeZoneProviderController.java
index 45ec400..ec2bc13 100644
--- a/services/core/java/com/android/server/location/timezone/LocationTimeZoneProviderController.java
+++ b/services/core/java/com/android/server/location/timezone/LocationTimeZoneProviderController.java
@@ -94,6 +94,9 @@
     @DurationMillisLong
     abstract long getUncertaintyTimeoutDelayMillis();
 
+    /** Called if the geolocation time zone detection is being reconfigured. */
+    abstract void destroy();
+
     /**
      * Used by {@link LocationTimeZoneProviderController} to obtain information from the surrounding
      * service. It can easily be faked for tests.
@@ -108,6 +111,9 @@
             mSharedLock = threadingDomain.getLockObject();
         }
 
+        /** Destroys the environment, i.e. deregisters listeners, etc. */
+        abstract void destroy();
+
         /** Returns the {@link ConfigurationInternal} for the current user of the device. */
         abstract ConfigurationInternal getCurrentUserConfigurationInternal();
 
diff --git a/services/core/java/com/android/server/location/timezone/LocationTimeZoneProviderProxy.java b/services/core/java/com/android/server/location/timezone/LocationTimeZoneProviderProxy.java
index 91b52f1..8368b5e 100644
--- a/services/core/java/com/android/server/location/timezone/LocationTimeZoneProviderProxy.java
+++ b/services/core/java/com/android/server/location/timezone/LocationTimeZoneProviderProxy.java
@@ -20,6 +20,7 @@
 import android.annotation.Nullable;
 import android.content.Context;
 import android.os.Handler;
+import android.os.RemoteCallback;
 import android.util.IndentingPrintWriter;
 
 import com.android.internal.annotations.GuardedBy;
@@ -69,7 +70,7 @@
 
     /**
      * Initializes the proxy. The supplied listener can expect to receive all events after this
-     * point. This method also calls {@link #onInitialize()} for subclasses to handle their own
+     * point. This method calls {@link #onInitialize()} for subclasses to handle their own
      * initialization.
      */
     void initialize(@NonNull Listener listener) {
@@ -79,21 +80,46 @@
                 throw new IllegalStateException("listener already set");
             }
             this.mListener = listener;
+            onInitialize();
         }
-        onInitialize();
     }
 
     /**
-     * Initializes the proxy. This is called after {@link #mListener} is set.
+     * Implemented by subclasses to initializes the proxy. This is called after {@link #mListener}
+     * is set.
      */
+    @GuardedBy("mSharedLock")
     abstract void onInitialize();
 
     /**
+     * Destroys the proxy. This method calls {@link #onDestroy()} for subclasses to handle their own
+     * destruction.
+     */
+    void destroy() {
+        synchronized (mSharedLock) {
+            onDestroy();
+        }
+    }
+
+    /**
+     * Implemented by subclasses to destroy the proxy.
+     */
+    @GuardedBy("mSharedLock")
+    abstract void onDestroy();
+
+    /**
      * Sets a new request for the provider.
      */
     abstract void setRequest(@NonNull TimeZoneProviderRequest request);
 
     /**
+     * Processes the supplied test command. An optional callback can be supplied to listen for a
+     * response.
+     */
+    abstract void handleTestCommand(@NonNull TestCommand testCommand,
+            @Nullable RemoteCallback callback);
+
+    /**
      * Handles a {@link TimeZoneProviderEvent} from a remote process.
      */
     final void handleTimeZoneProviderEvent(@NonNull TimeZoneProviderEvent timeZoneProviderEvent) {
diff --git a/services/core/java/com/android/server/location/timezone/NullLocationTimeZoneProvider.java b/services/core/java/com/android/server/location/timezone/NullLocationTimeZoneProvider.java
deleted file mode 100644
index e11a7ce..0000000
--- a/services/core/java/com/android/server/location/timezone/NullLocationTimeZoneProvider.java
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * 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.location.timezone;
-
-import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_PERM_FAILED;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.util.IndentingPrintWriter;
-import android.util.Slog;
-
-import java.time.Duration;
-
-/**
- * A {@link LocationTimeZoneProvider} that provides minimal responses needed for the {@link
- * LocationTimeZoneProviderController} to operate correctly when there is no "real" provider
- * configured. This can be used during development / testing, or in a production build when the
- * platform supports more providers than are needed for an Android deployment.
- *
- * <p>For example, if the {@link LocationTimeZoneProviderController} supports a primary
- * and a secondary {@link LocationTimeZoneProvider}, but only a primary is configured, the secondary
- * config will be left null and the {@link LocationTimeZoneProvider} implementation will be
- * defaulted to a {@link NullLocationTimeZoneProvider}. The {@link NullLocationTimeZoneProvider}
- * enters a {@link ProviderState#PROVIDER_STATE_PERM_FAILED} state immediately after being started
- * for the first time and sends the appropriate event, which ensures the {@link
- * LocationTimeZoneProviderController} won't expect any further {@link
- * TimeZoneProviderEvent}s to come from it, and won't attempt to use it
- * again.
- */
-class NullLocationTimeZoneProvider extends LocationTimeZoneProvider {
-
-    private static final String TAG = "NullLocationTimeZoneProvider";
-
-    /** Creates the instance. */
-    NullLocationTimeZoneProvider(@NonNull ThreadingDomain threadingDomain,
-            @NonNull String providerName) {
-        super(threadingDomain, providerName);
-    }
-
-    @Override
-    void onInitialize() {
-        // No-op
-    }
-
-    @Override
-    void onStartUpdates(@NonNull Duration initializationTimeout) {
-        // Report a failure (asynchronously using the mThreadingDomain thread to avoid recursion).
-        mThreadingDomain.post(()-> {
-            // Enter the perm-failed state.
-            ProviderState currentState = mCurrentState.get();
-            ProviderState failedState = currentState.newState(
-                    PROVIDER_STATE_PERM_FAILED, null, null, "Stubbed provider");
-            setCurrentState(failedState, true);
-        });
-    }
-
-    @Override
-    void onStopUpdates() {
-        // Ignored - this implementation is always permanently failed.
-    }
-
-    @Override
-    void logWarn(String msg) {
-        Slog.w(TAG, msg);
-    }
-
-    @Override
-    public void dump(@NonNull IndentingPrintWriter ipw, @Nullable String[] args) {
-        synchronized (mSharedLock) {
-            ipw.println("{Stubbed LocationTimeZoneProvider}");
-            ipw.println("mProviderName=" + mProviderName);
-            ipw.println("mCurrentState=" + mCurrentState);
-        }
-    }
-
-    @Override
-    public String toString() {
-        synchronized (mSharedLock) {
-            return "NullLocationTimeZoneProvider{"
-                    + "mProviderName='" + mProviderName + '\''
-                    + ", mCurrentState='" + mCurrentState + '\''
-                    + '}';
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/location/timezone/NullLocationTimeZoneProviderProxy.java b/services/core/java/com/android/server/location/timezone/NullLocationTimeZoneProviderProxy.java
new file mode 100644
index 0000000..c2abbf9
--- /dev/null
+++ b/services/core/java/com/android/server/location/timezone/NullLocationTimeZoneProviderProxy.java
@@ -0,0 +1,86 @@
+/*
+ * 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.location.timezone;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Context;
+import android.os.Bundle;
+import android.os.RemoteCallback;
+import android.service.timezone.TimeZoneProviderService;
+import android.util.IndentingPrintWriter;
+
+/**
+ * A {@link LocationTimeZoneProviderProxy} that provides minimal responses needed for the {@link
+ * BinderLocationTimeZoneProvider} to operate correctly when there is no "real" provider
+ * configured / enabled. This can be used during development / testing, or in a production build
+ * when the platform supports more providers than are needed for an Android deployment.
+ *
+ * <p>For example, if the {@link LocationTimeZoneProviderController} supports a primary
+ * and a secondary {@link LocationTimeZoneProvider}, but only a primary is configured, the secondary
+ * config will be left null and the {@link LocationTimeZoneProviderProxy} implementation will be
+ * defaulted to a {@link NullLocationTimeZoneProviderProxy}. The {@link
+ * NullLocationTimeZoneProviderProxy} sends a "permanent failure" event immediately after being
+ * started for the first time, which ensures the {@link LocationTimeZoneProviderController} won't
+ * expect any further {@link TimeZoneProviderEvent}s to come from it, and won't attempt to use it
+ * again.
+ */
+class NullLocationTimeZoneProviderProxy extends LocationTimeZoneProviderProxy {
+
+    /** Creates the instance. */
+    NullLocationTimeZoneProviderProxy(
+            @NonNull Context context, @NonNull ThreadingDomain threadingDomain) {
+        super(context, threadingDomain);
+    }
+
+    @Override
+    void onInitialize() {
+        // No-op
+    }
+
+    @Override
+    void onDestroy() {
+        // No-op
+    }
+
+    @Override
+    void setRequest(@NonNull TimeZoneProviderRequest request) {
+        if (request.sendUpdates()) {
+            TimeZoneProviderEvent event = TimeZoneProviderEvent.createPermanentFailureEvent(
+                    "Provider is disabled");
+            handleTimeZoneProviderEvent(event);
+        }
+    }
+
+    @Override
+    void handleTestCommand(@NonNull TestCommand testCommand, @Nullable RemoteCallback callback) {
+        if (callback != null) {
+            Bundle result = new Bundle();
+            result.putBoolean(TimeZoneProviderService.TEST_COMMAND_RESULT_SUCCESS_KEY, false);
+            result.putString(TimeZoneProviderService.TEST_COMMAND_RESULT_ERROR_KEY,
+                    "Provider is disabled");
+            callback.sendResult(result);
+        }
+    }
+
+    @Override
+    public void dump(@NonNull IndentingPrintWriter ipw, @Nullable String[] args) {
+        synchronized (mSharedLock) {
+            ipw.println("{NullLocationTimeZoneProviderProxy}");
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/location/timezone/RealLocationTimeZoneProviderProxy.java b/services/core/java/com/android/server/location/timezone/RealLocationTimeZoneProviderProxy.java
index 6cc148a..0904ba4 100644
--- a/services/core/java/com/android/server/location/timezone/RealLocationTimeZoneProviderProxy.java
+++ b/services/core/java/com/android/server/location/timezone/RealLocationTimeZoneProviderProxy.java
@@ -16,13 +16,24 @@
 
 package com.android.server.location.timezone;
 
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.service.timezone.TimeZoneProviderService.TEST_COMMAND_RESULT_ERROR_KEY;
+import static android.service.timezone.TimeZoneProviderService.TEST_COMMAND_RESULT_SUCCESS_KEY;
+
+import static com.android.server.location.timezone.LocationTimeZoneManagerService.warnLog;
+
+import android.Manifest;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
-import android.os.RemoteException;
+import android.os.RemoteCallback;
 import android.service.timezone.ITimeZoneProvider;
 import android.service.timezone.ITimeZoneProviderManager;
 import android.service.timezone.TimeZoneProviderSuggestion;
@@ -32,6 +43,7 @@
 import com.android.server.ServiceWatcher;
 
 import java.util.Objects;
+import java.util.function.Predicate;
 
 /**
  * System server-side proxy for ITimeZoneProvider implementations, i.e. this provides the
@@ -57,8 +69,38 @@
         super(context, threadingDomain);
         mManagerProxy = null;
         mRequest = TimeZoneProviderRequest.createStopUpdatesRequest();
+
+        // A predicate that is used to confirm that an intent service can be used as a
+        // location-based TimeZoneProvider. The service must:
+        // 1) Declare android:permission="android.permission.BIND_TIME_ZONE_PROVIDER_SERVICE" - this
+        //    ensures that the provider will only communicate with the system server.
+        // 2) Be in an application that has been granted the
+        //    android.permission.INSTALL_LOCATION_TIME_ZONE_PROVIDER_SERVICE permission. This
+        //    ensures only trusted time zone providers will be discovered.
+        final String requiredClientPermission = Manifest.permission.BIND_TIME_ZONE_PROVIDER_SERVICE;
+        final String requiredPermission =
+                Manifest.permission.INSTALL_LOCATION_TIME_ZONE_PROVIDER_SERVICE;
+        Predicate<ResolveInfo> intentServiceCheckPredicate = resolveInfo -> {
+            ServiceInfo serviceInfo = resolveInfo.serviceInfo;
+
+            boolean hasClientPermissionRequirement =
+                    requiredClientPermission.equals(serviceInfo.permission);
+
+            String packageName = serviceInfo.packageName;
+            PackageManager packageManager = context.getPackageManager();
+            int checkResult = packageManager.checkPermission(requiredPermission, packageName);
+            boolean hasRequiredPermission = checkResult == PERMISSION_GRANTED;
+
+            boolean result = hasClientPermissionRequirement && hasRequiredPermission;
+            if (!result) {
+                warnLog("resolveInfo=" + resolveInfo + " does not meet requirements:"
+                        + " hasClientPermissionRequirement=" + hasClientPermissionRequirement
+                        + ", hasRequiredPermission=" + hasRequiredPermission);
+            }
+            return result;
+        };
         mServiceWatcher = new ServiceWatcher(context, handler, action, this::onBind, this::onUnbind,
-                enableOverlayResId, nonOverlayPackageResId);
+                enableOverlayResId, nonOverlayPackageResId, intentServiceCheckPredicate);
     }
 
     @Override
@@ -68,6 +110,11 @@
         }
     }
 
+    @Override
+    void onDestroy() {
+        mServiceWatcher.unregister();
+    }
+
     private boolean register() {
         boolean resolves = mServiceWatcher.checkServiceResolves();
         if (resolves) {
@@ -79,18 +126,15 @@
     private void onBind(IBinder binder, ComponentName componentName) {
         mThreadingDomain.assertCurrentThread();
 
-        ITimeZoneProvider provider = ITimeZoneProvider.Stub.asInterface(binder);
-
         synchronized (mSharedLock) {
-            try {
-                mManagerProxy = new ManagerProxy();
-                provider.setTimeZoneProviderManager(mManagerProxy);
-                trySendCurrentRequest();
-                mListener.onProviderBound();
-            } catch (RemoteException e) {
-                // This is not expected to happen.
-                throw new RuntimeException(e);
-            }
+            // When a new remote is first bound we create the ManagerProxy that will be passed to
+            // it. By creating a new one for each bind the ManagerProxy can check whether it is
+            // still the current proxy and if not it can ignore incoming calls.
+            mManagerProxy = new ManagerProxy();
+            mListener.onProviderBound();
+
+            // Send the current request to the remote.
+            trySendCurrentRequest();
         }
     }
 
@@ -98,6 +142,8 @@
         mThreadingDomain.assertCurrentThread();
 
         synchronized (mSharedLock) {
+            // Clear the ManagerProxy used with the old remote so we will ignore calls from any old
+            // remotes that somehow hold a reference to it.
             mManagerProxy = null;
             mListener.onProviderUnbound();
         }
@@ -111,26 +157,47 @@
         synchronized (mSharedLock) {
             mRequest = request;
 
+            // Two possible outcomes here: Either we are already bound to a remote service, in
+            // which case trySendCurrentRequest() will communicate the request immediately, or we
+            // are not bound to the remote service yet, in which case it will be sent during
+            // onBindOnHandlerThread() instead.
             trySendCurrentRequest();
         }
     }
 
     @GuardedBy("mSharedLock")
     private void trySendCurrentRequest() {
+        ManagerProxy managerProxy = mManagerProxy;
         TimeZoneProviderRequest request = mRequest;
         mServiceWatcher.runOnBinder(binder -> {
             ITimeZoneProvider service = ITimeZoneProvider.Stub.asInterface(binder);
             if (request.sendUpdates()) {
-                service.startUpdates(request.getInitializationTimeout().toMillis());
+                service.startUpdates(managerProxy, request.getInitializationTimeout().toMillis());
             } else {
                 service.stopUpdates();
             }
         });
     }
 
+    /**
+     * A stubbed implementation.
+     */
+    @Override
+    void handleTestCommand(@NonNull TestCommand testCommand, @Nullable RemoteCallback callback) {
+        mThreadingDomain.assertCurrentThread();
+
+        if (callback != null) {
+            Bundle result = new Bundle();
+            result.putBoolean(TEST_COMMAND_RESULT_SUCCESS_KEY, false);
+            result.putString(TEST_COMMAND_RESULT_ERROR_KEY, "Not implemented");
+            callback.sendResult(result);
+        }
+    }
+
     @Override
     public void dump(@NonNull IndentingPrintWriter ipw, @Nullable String[] args) {
         synchronized (mSharedLock) {
+            ipw.println("{RealLocationTimeZoneProviderProxy}");
             ipw.println("mRequest=" + mRequest);
             mServiceWatcher.dump(null, ipw, args);
         }
@@ -166,6 +233,8 @@
         private void onTimeZoneProviderEvent(TimeZoneProviderEvent event) {
             synchronized (mSharedLock) {
                 if (mManagerProxy != this) {
+                    // Ignore incoming calls if this instance is no longer the current
+                    // mManagerProxy.
                     return;
                 }
             }
diff --git a/services/core/java/com/android/server/location/timezone/SimulatedBinderProviderEvent.java b/services/core/java/com/android/server/location/timezone/SimulatedBinderProviderEvent.java
deleted file mode 100644
index 0987ee5..0000000
--- a/services/core/java/com/android/server/location/timezone/SimulatedBinderProviderEvent.java
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
- * 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.location.timezone;
-
-import static com.android.server.location.timezone.LocationTimeZoneManagerService.PRIMARY_PROVIDER_NAME;
-import static com.android.server.location.timezone.LocationTimeZoneManagerService.SECONDARY_PROVIDER_NAME;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.os.ShellCommand;
-import android.os.SystemClock;
-import android.service.timezone.TimeZoneProviderSuggestion;
-
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Objects;
-
-/**
- * An event used for simulating real binder proxy behavior using a {@link
- * SimulatedLocationTimeZoneProviderProxy}.
- */
-final class SimulatedBinderProviderEvent {
-
-    private static final List<String> VALID_PROVIDER_NAMES =
-            Arrays.asList(PRIMARY_PROVIDER_NAME, SECONDARY_PROVIDER_NAME);
-
-    static final int INJECTED_EVENT_TYPE_ON_BIND = 1;
-    static final int INJECTED_EVENT_TYPE_ON_UNBIND = 2;
-    static final int INJECTED_EVENT_TYPE_LOCATION_TIME_ZONE_EVENT = 3;
-
-
-    @NonNull private final String mProviderName;
-    private final int mType;
-    @Nullable private final TimeZoneProviderEvent mTimeZoneProviderEvent;
-
-    private SimulatedBinderProviderEvent(@NonNull String providerName, int eventType,
-            @Nullable TimeZoneProviderEvent timeZoneProviderEvent) {
-        this.mProviderName = Objects.requireNonNull(providerName);
-        this.mType = eventType;
-        this.mTimeZoneProviderEvent = timeZoneProviderEvent;
-    }
-
-    @NonNull
-    String getProviderName() {
-        return mProviderName;
-    }
-
-    @Nullable
-    TimeZoneProviderEvent getTimeZoneProviderEvent() {
-        return mTimeZoneProviderEvent;
-    }
-
-    int getType() {
-        return mType;
-    }
-
-    /** Prints the command line options that {@link #createFromArgs(ShellCommand)} understands. */
-    static void printCommandLineOpts(PrintWriter pw) {
-        pw.println("Simulated provider binder event:");
-        pw.println();
-        pw.println("<provider name> [onBind|onUnbind|timeZoneProviderEvent"
-                + " <location time zone event args>]");
-        pw.println();
-        pw.println("<provider name> = " + VALID_PROVIDER_NAMES);
-        pw.println("<time zone provider event args> ="
-                + " [PERMANENT_FAILURE|UNCERTAIN|SUGGESTION <time zone ids>*]");
-    }
-
-    /**
-     * Constructs a {@link SimulatedBinderProviderEvent} from the arguments of {@code shellCommand}.
-     */
-    static SimulatedBinderProviderEvent createFromArgs(ShellCommand shellCommand) {
-        String providerName = shellCommand.getNextArgRequired();
-        if (!VALID_PROVIDER_NAMES.contains(providerName)) {
-            throw new IllegalArgumentException("Unknown provider name=" + providerName);
-        }
-        String injectedEvent = shellCommand.getNextArgRequired();
-        switch (injectedEvent) {
-            case "onBind": {
-                return new SimulatedBinderProviderEvent(
-                        providerName, INJECTED_EVENT_TYPE_ON_BIND, null);
-            }
-            case "onUnbind": {
-                return new SimulatedBinderProviderEvent(
-                        providerName, INJECTED_EVENT_TYPE_ON_UNBIND, null);
-            }
-            case "timeZoneProviderEvent": {
-                TimeZoneProviderEvent event = parseTimeZoneProviderEventArgs(shellCommand);
-                return new SimulatedBinderProviderEvent(providerName,
-                        INJECTED_EVENT_TYPE_LOCATION_TIME_ZONE_EVENT, event);
-            }
-            default: {
-                throw new IllegalArgumentException("Unknown simulated event type=" + injectedEvent);
-            }
-        }
-    }
-
-    private static TimeZoneProviderEvent parseTimeZoneProviderEventArgs(ShellCommand shellCommand) {
-        TimeZoneProviderEvent event;
-        String eventTypeString = shellCommand.getNextArgRequired();
-        switch (eventTypeString.toUpperCase()) {
-            case "PERMANENT_FAILURE": {
-                event = TimeZoneProviderEvent.createPermanentFailureEvent("Simulated");
-                break;
-            }
-            case "UNCERTAIN": {
-                event = TimeZoneProviderEvent.createUncertainEvent();
-                break;
-            }
-            case "SUGGESTION": {
-                TimeZoneProviderSuggestion suggestion = new TimeZoneProviderSuggestion.Builder()
-                        .setElapsedRealtimeMillis(SystemClock.elapsedRealtime())
-                        .setTimeZoneIds(parseTimeZoneArgs(shellCommand))
-                        .build();
-                event = TimeZoneProviderEvent.createSuggestionEvent(suggestion);
-                break;
-            }
-            default: {
-                throw new IllegalArgumentException("Error: Unknown eventType: " + eventTypeString);
-            }
-        }
-        return event;
-    }
-
-    private static List<String> parseTimeZoneArgs(ShellCommand shellCommand) {
-        List<String> timeZoneIds = new ArrayList<>();
-        String timeZoneId;
-        while ((timeZoneId = shellCommand.getNextArg()) != null) {
-            timeZoneIds.add(timeZoneId);
-        }
-        return timeZoneIds;
-    }
-
-    @Override
-    public String toString() {
-        return "SimulatedBinderProviderEvent{"
-                + "mProviderName=" + mProviderName
-                + ", mType=" + mType
-                + ", mTimeZoneProviderEvent=" + mTimeZoneProviderEvent
-                + '}';
-    }
-}
diff --git a/services/core/java/com/android/server/location/timezone/SimulatedLocationTimeZoneProviderProxy.java b/services/core/java/com/android/server/location/timezone/SimulatedLocationTimeZoneProviderProxy.java
index cac5621..66ccaed 100644
--- a/services/core/java/com/android/server/location/timezone/SimulatedLocationTimeZoneProviderProxy.java
+++ b/services/core/java/com/android/server/location/timezone/SimulatedLocationTimeZoneProviderProxy.java
@@ -16,18 +16,29 @@
 
 package com.android.server.location.timezone;
 
-import static com.android.server.location.timezone.SimulatedBinderProviderEvent.INJECTED_EVENT_TYPE_LOCATION_TIME_ZONE_EVENT;
-import static com.android.server.location.timezone.SimulatedBinderProviderEvent.INJECTED_EVENT_TYPE_ON_BIND;
-import static com.android.server.location.timezone.SimulatedBinderProviderEvent.INJECTED_EVENT_TYPE_ON_UNBIND;
+import static android.app.time.LocationTimeZoneManager.SIMULATED_PROVIDER_TEST_COMMAND_ON_BIND;
+import static android.app.time.LocationTimeZoneManager.SIMULATED_PROVIDER_TEST_COMMAND_ON_UNBIND;
+import static android.app.time.LocationTimeZoneManager.SIMULATED_PROVIDER_TEST_COMMAND_PERM_FAILURE;
+import static android.app.time.LocationTimeZoneManager.SIMULATED_PROVIDER_TEST_COMMAND_SUCCESS;
+import static android.app.time.LocationTimeZoneManager.SIMULATED_PROVIDER_TEST_COMMAND_SUCCESS_ARG_KEY_TZ;
+import static android.app.time.LocationTimeZoneManager.SIMULATED_PROVIDER_TEST_COMMAND_UNCERTAIN;
+import static android.service.timezone.TimeZoneProviderService.TEST_COMMAND_RESULT_ERROR_KEY;
+import static android.service.timezone.TimeZoneProviderService.TEST_COMMAND_RESULT_SUCCESS_KEY;
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
+import android.os.Bundle;
+import android.os.RemoteCallback;
+import android.os.SystemClock;
+import android.service.timezone.TimeZoneProviderSuggestion;
 import android.util.IndentingPrintWriter;
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.server.timezonedetector.ReferenceWithHistory;
 
+import java.io.PrintWriter;
+import java.util.Arrays;
 import java.util.Objects;
 
 /**
@@ -53,38 +64,61 @@
         // No-op - nothing to do for the simulated provider.
     }
 
-    void simulate(@NonNull SimulatedBinderProviderEvent event) {
+    @Override
+    void onDestroy() {
+        // No-op - nothing to do for the simulated provider.
+    }
+
+    void handleTestCommand(@NonNull TestCommand testCommand, @Nullable RemoteCallback callback) {
         mThreadingDomain.assertCurrentThread();
 
-        Objects.requireNonNull(event);
+        Objects.requireNonNull(testCommand);
 
         synchronized (mSharedLock) {
-            switch (event.getType()) {
-                case INJECTED_EVENT_TYPE_ON_BIND: {
-                    mLastEvent.set("Simulating onProviderBound(), event=" + event);
+            Bundle resultBundle = new Bundle();
+            switch (testCommand.getName()) {
+                case SIMULATED_PROVIDER_TEST_COMMAND_ON_BIND: {
+                    mLastEvent.set("Simulating onProviderBound(), testCommand=" + testCommand);
                     mThreadingDomain.post(this::onBindOnHandlerThread);
+                    resultBundle.putBoolean(TEST_COMMAND_RESULT_SUCCESS_KEY, true);
                     break;
                 }
-                case INJECTED_EVENT_TYPE_ON_UNBIND: {
-                    mLastEvent.set("Simulating onProviderUnbound(), event=" + event);
+                case SIMULATED_PROVIDER_TEST_COMMAND_ON_UNBIND: {
+                    mLastEvent.set("Simulating onProviderUnbound(), testCommand=" + testCommand);
                     mThreadingDomain.post(this::onUnbindOnHandlerThread);
+                    resultBundle.putBoolean(TEST_COMMAND_RESULT_SUCCESS_KEY, true);
                     break;
                 }
-                case INJECTED_EVENT_TYPE_LOCATION_TIME_ZONE_EVENT: {
+                case SIMULATED_PROVIDER_TEST_COMMAND_PERM_FAILURE:
+                case SIMULATED_PROVIDER_TEST_COMMAND_UNCERTAIN:
+                case SIMULATED_PROVIDER_TEST_COMMAND_SUCCESS: {
                     if (!mRequest.sendUpdates()) {
-                        mLastEvent.set("Test event=" + event + " is testing an invalid case:"
-                                + " reporting is off. mRequest=" + mRequest);
+                        String errorMsg = "testCommand=" + testCommand
+                                + " is testing an invalid case:"
+                                + " updates are off. mRequest=" + mRequest;
+                        mLastEvent.set(errorMsg);
+                        resultBundle.putBoolean(TEST_COMMAND_RESULT_SUCCESS_KEY, false);
+                        resultBundle.putString(TEST_COMMAND_RESULT_ERROR_KEY, errorMsg);
+                        break;
                     }
-                    mLastEvent.set("Simulating TimeZoneProviderResult, event=" + event);
-                    handleTimeZoneProviderEvent(event.getTimeZoneProviderEvent());
+                    mLastEvent.set("Simulating TimeZoneProviderEvent, testCommand=" + testCommand);
+                    TimeZoneProviderEvent timeZoneProviderEvent =
+                            createTimeZoneProviderEventFromTestCommand(testCommand);
+                    handleTimeZoneProviderEvent(timeZoneProviderEvent);
+                    resultBundle.putBoolean(TEST_COMMAND_RESULT_SUCCESS_KEY, true);
                     break;
                 }
                 default: {
-                    mLastEvent.set("Unknown simulated event type. event=" + event);
-                    throw new IllegalArgumentException(
-                            "Unknown simulated event type. event=" + event);
+                    String errorMsg = "Unknown test event type. testCommand=" + testCommand;
+                    mLastEvent.set(errorMsg);
+                    resultBundle.putBoolean(TEST_COMMAND_RESULT_SUCCESS_KEY, false);
+                    resultBundle.putString(TEST_COMMAND_RESULT_ERROR_KEY, errorMsg);
+                    break;
                 }
             }
+            if (callback != null) {
+                callback.sendResult(resultBundle);
+            }
         }
     }
 
@@ -118,6 +152,7 @@
     @Override
     public void dump(@NonNull IndentingPrintWriter ipw, @Nullable String[] args) {
         synchronized (mSharedLock) {
+            ipw.println("{SimulatedLocationTimeZoneProviderProxy}");
             ipw.println("mRequest=" + mRequest);
             ipw.println("mLastEvent=" + mLastEvent);
 
@@ -127,4 +162,50 @@
             ipw.decreaseIndent();
         }
     }
+
+    /**
+     * Prints the command line options that to create a {@link TestCommand} that can be passed to
+     * {@link #createTimeZoneProviderEventFromTestCommand(TestCommand)}.
+     */
+    static void printTestCommandShellHelp(@NonNull PrintWriter pw) {
+        pw.printf("%s\n", SIMULATED_PROVIDER_TEST_COMMAND_ON_BIND);
+        pw.printf("%s\n", SIMULATED_PROVIDER_TEST_COMMAND_ON_UNBIND);
+        pw.printf("%s\n", SIMULATED_PROVIDER_TEST_COMMAND_PERM_FAILURE);
+        pw.printf("%s\n", SIMULATED_PROVIDER_TEST_COMMAND_UNCERTAIN);
+        pw.printf("%s %s=string_array:<time zone id>[&<time zone id>]+\n",
+                SIMULATED_PROVIDER_TEST_COMMAND_SUCCESS,
+                SIMULATED_PROVIDER_TEST_COMMAND_SUCCESS_ARG_KEY_TZ);
+    }
+
+    @NonNull
+    private static TimeZoneProviderEvent createTimeZoneProviderEventFromTestCommand(
+            @NonNull TestCommand testCommand) {
+        String name = testCommand.getName();
+        switch (name) {
+            case SIMULATED_PROVIDER_TEST_COMMAND_PERM_FAILURE: {
+                return TimeZoneProviderEvent.createPermanentFailureEvent("Simulated failure");
+            }
+            case SIMULATED_PROVIDER_TEST_COMMAND_UNCERTAIN: {
+                return TimeZoneProviderEvent.createUncertainEvent();
+            }
+            case SIMULATED_PROVIDER_TEST_COMMAND_SUCCESS: {
+                Bundle args = testCommand.getArgs();
+                String[] timeZoneIds = args.getStringArray(
+                        SIMULATED_PROVIDER_TEST_COMMAND_SUCCESS_ARG_KEY_TZ);
+                if (timeZoneIds == null) {
+                    throw new IllegalArgumentException("No "
+                            + SIMULATED_PROVIDER_TEST_COMMAND_SUCCESS_ARG_KEY_TZ + " arg found");
+                }
+                TimeZoneProviderSuggestion suggestion = new TimeZoneProviderSuggestion.Builder()
+                        .setTimeZoneIds(Arrays.asList(timeZoneIds))
+                        .setElapsedRealtimeMillis(SystemClock.elapsedRealtime())
+                        .build();
+                return TimeZoneProviderEvent.createSuggestionEvent(suggestion);
+            }
+            default: {
+                String msg = String.format("Error: Unknown command name %s", name);
+                throw new IllegalArgumentException(msg);
+            }
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/location/timezone/TestCommand.java b/services/core/java/com/android/server/location/timezone/TestCommand.java
new file mode 100644
index 0000000..0df3ca0
--- /dev/null
+++ b/services/core/java/com/android/server/location/timezone/TestCommand.java
@@ -0,0 +1,194 @@
+/*
+ * 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.location.timezone;
+
+import android.annotation.NonNull;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.ShellCommand;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.io.PrintWriter;
+import java.util.Arrays;
+import java.util.Objects;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * A command used to trigger behaviors in a component during tests. Routing to the correct
+ * component is not handled by this class. The meaning of the {@code name} and {@code args}
+ * properties are component-specific.
+ *
+ * <p>{@link TestCommand}s can be encoded as arguments in a shell command. See
+ * {@link #createFromShellCommandArgs(ShellCommand)} and {@link
+ * #printShellCommandEncodingHelp(PrintWriter)}.
+ */
+final class TestCommand {
+
+    private static final Pattern SHELL_ARG_PATTERN = Pattern.compile("([^=]+)=([^:]+):(.*)");
+    private static final Pattern SHELL_ARG_VALUE_SPLIT_PATTERN = Pattern.compile("&");
+
+    @NonNull private final String mName;
+    @NonNull private final Bundle mArgs;
+
+    /** Creates a {@link TestCommand} from components. */
+    private TestCommand(@NonNull String type, @NonNull Bundle args) {
+        mName = Objects.requireNonNull(type);
+        mArgs = Objects.requireNonNull(args);
+    }
+
+    @VisibleForTesting
+    @NonNull
+    public static TestCommand createForTests(@NonNull String type, @NonNull Bundle args) {
+        return new TestCommand(type, args);
+    }
+
+    /**
+     * Creates a {@link TestCommand} from a {@link ShellCommand}'s remaining arguments.
+     *
+     * See {@link #printShellCommandEncodingHelp(PrintWriter)} for encoding details.
+     */
+    @NonNull
+    public static TestCommand createFromShellCommandArgs(@NonNull ShellCommand shellCommand) {
+        String name = shellCommand.getNextArgRequired();
+        Bundle args = new Bundle();
+        String argKeyAndValue;
+        while ((argKeyAndValue = shellCommand.getNextArg()) != null) {
+            Matcher matcher = SHELL_ARG_PATTERN.matcher(argKeyAndValue);
+            if (!matcher.matches()) {
+                throw new IllegalArgumentException(
+                        argKeyAndValue + " does not match " + SHELL_ARG_PATTERN);
+            }
+            String key = matcher.group(1);
+            String type = matcher.group(2);
+            String encodedValue = matcher.group(3);
+            Object value = getTypedValue(type, encodedValue);
+            args.putObject(key, value);
+        }
+        return new TestCommand(name, args);
+    }
+
+    /**
+     * Returns the command's name.
+     */
+    @NonNull
+    public String getName() {
+        return mName;
+    }
+
+    /**
+     * Returns the arg values. Returns an empty bundle if there are no args.
+     */
+    @NonNull
+    public Bundle getArgs() {
+        return mArgs.deepCopy();
+    }
+
+    @Override
+    public String toString() {
+        return "TestCommand{"
+                + "mName=" + mName
+                + ", mArgs=" + mArgs
+                + '}';
+    }
+
+    /**
+     * Prints the text format that {@link #createFromShellCommandArgs(ShellCommand)} understands.
+     */
+    public static void printShellCommandEncodingHelp(@NonNull PrintWriter pw) {
+        pw.println("Test commands are encoded on the command line as: <name> <arg>*");
+        pw.println();
+        pw.println("The <name> is a string");
+        pw.println("The <arg> encoding is: \"key=type:value\"");
+        pw.println();
+        pw.println("e.g. \"myKey=string:myValue\" represents an argument with the key \"myKey\""
+                + " and a string value of \"myValue\"");
+        pw.println("Values are one or more URI-encoded strings separated by & characters. Only some"
+                + " types support multiple values, e.g. string arrays.");
+        pw.println();
+        pw.println("Recognized types are: string, boolean, double, long, string_array.");
+        pw.println();
+        pw.println("When passing test commands via adb shell, the & can be escaped by quoting the"
+                + " <arg> and escaping the & with \\");
+        pw.println("For example:");
+        pw.println("  $ adb shell ... my-command \"key1=string_array:value1\\&value2\"");
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        TestCommand that = (TestCommand) o;
+        return mName.equals(that.mName)
+                && mArgs.kindofEquals(that.mArgs);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mName, mArgs);
+    }
+
+
+    private static Object getTypedValue(String type, String encodedValue) {
+        // The value is stored in a URL encoding. Multiple value types have values separated with
+        // a & character.
+        String[] values = SHELL_ARG_VALUE_SPLIT_PATTERN.split(encodedValue);
+
+        // URI decode the values.
+        for (int i = 0; i < values.length; i++) {
+            values[i] = Uri.decode(values[i]);
+        }
+
+        switch (type) {
+            case "boolean": {
+                checkSingleValue(values);
+                return Boolean.parseBoolean(values[0]);
+            }
+            case "double": {
+                checkSingleValue(values);
+                return Double.parseDouble(values[0]);
+            }
+            case "long": {
+                checkSingleValue(values);
+                return Long.parseLong(values[0]);
+            }
+            case "string": {
+                checkSingleValue(values);
+                return values[0];
+            }
+            case "string_array": {
+                return values;
+            }
+            default: {
+                throw new IllegalArgumentException("Unknown type: " + type);
+            }
+        }
+
+    }
+
+    private static void checkSingleValue(String[] values) {
+        if (values.length != 1) {
+            throw new IllegalArgumentException("Expected a single value, but there were multiple: "
+                    + Arrays.toString(values));
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/location/timezone/ThreadingDomain.java b/services/core/java/com/android/server/location/timezone/ThreadingDomain.java
index a50f8ad..4ada6f5 100644
--- a/services/core/java/com/android/server/location/timezone/ThreadingDomain.java
+++ b/services/core/java/com/android/server/location/timezone/ThreadingDomain.java
@@ -21,6 +21,8 @@
 
 import com.android.internal.util.Preconditions;
 
+import java.util.concurrent.Callable;
+
 /**
  * A class that can be used to enforce / indicate a set of components that need to share threading
  * behavior such as a shared lock object and a common thread, with async execution support.
@@ -58,7 +60,15 @@
      * being used within a set of components, a lot of races can be avoided.
      */
     void assertCurrentThread() {
-        Preconditions.checkArgument(Thread.currentThread() == getThread());
+        Preconditions.checkState(Thread.currentThread() == getThread());
+    }
+
+    /**
+     * Asserts the currently executing thread is not the one associated with this threading domain.
+     * Generally useful for documenting expectations in the code and avoiding deadlocks.
+     */
+    void assertNotCurrentThread() {
+        Preconditions.checkState(Thread.currentThread() != getThread());
     }
 
     /**
@@ -67,6 +77,37 @@
     abstract void post(@NonNull Runnable runnable);
 
     /**
+     * Executes the supplied runnable and waits for up to the duration specified for it to be
+     * executed. This is only intended for use by test and/or shell command code as it consumes
+     * multiple threads and could lead to deadlocks.
+     *
+     * <p>An {@link IllegalStateException} will be thrown if calling this method would cause a
+     * deadlock, e.g. if it is called using the threading domain's own thread.
+     */
+    final void postAndWait(@NonNull Runnable runnable, @DurationMillisLong long durationMillis) {
+        try {
+            postAndWait(() -> {
+                runnable.run();
+                return null;
+            }, durationMillis);
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /**
+     * Executes the supplied callable and waits for up to the duration specified for it to be
+     * executed. This is only intended for use by test and/or shell command code as it consumes
+     * multiple threads and could lead to deadlocks.
+     *
+     * <p>An {@link IllegalStateException} will be thrown if calling this method would cause a
+     * deadlock, e.g. if it is called using the threading domain's own thread.
+     */
+    abstract <V> V postAndWait(
+            @NonNull Callable<V> callable, @DurationMillisLong long durationMillis)
+            throws Exception;
+
+    /**
      * Execute the supplied runnable on the threading domain's thread with a delay.
      */
     abstract void postDelayed(@NonNull Runnable runnable, @DurationMillisLong long delayMillis);
diff --git a/services/core/java/com/android/server/media/MediaShellCommand.java b/services/core/java/com/android/server/media/MediaShellCommand.java
index 69c57a9..103cdd9 100644
--- a/services/core/java/com/android/server/media/MediaShellCommand.java
+++ b/services/core/java/com/android/server/media/MediaShellCommand.java
@@ -67,7 +67,7 @@
         }
         if (sThread == null) {
             Looper.prepare();
-            sThread = ActivityThread.systemMain();
+            sThread = ActivityThread.currentActivityThread();
             Context context = sThread.getSystemContext();
             sMediaSessionManager =
                     (MediaSessionManager) context.getSystemService(Context.MEDIA_SESSION_SERVICE);
diff --git a/services/core/java/com/android/server/net/NetworkIdentitySet.java b/services/core/java/com/android/server/net/NetworkIdentitySet.java
index 2326ad3..bce8069 100644
--- a/services/core/java/com/android/server/net/NetworkIdentitySet.java
+++ b/services/core/java/com/android/server/net/NetworkIdentitySet.java
@@ -20,8 +20,8 @@
 import android.service.NetworkIdentitySetProto;
 import android.util.proto.ProtoOutputStream;
 
-import java.io.DataInputStream;
-import java.io.DataOutputStream;
+import java.io.DataInput;
+import java.io.DataOutput;
 import java.io.IOException;
 import java.util.HashSet;
 
@@ -44,7 +44,7 @@
     public NetworkIdentitySet() {
     }
 
-    public NetworkIdentitySet(DataInputStream in) throws IOException {
+    public NetworkIdentitySet(DataInput in) throws IOException {
         final int version = in.readInt();
         final int size = in.readInt();
         for (int i = 0; i < size; i++) {
@@ -89,7 +89,7 @@
         }
     }
 
-    public void writeToStream(DataOutputStream out) throws IOException {
+    public void writeToStream(DataOutput out) throws IOException {
         out.writeInt(VERSION_ADD_DEFAULT_NETWORK);
         out.writeInt(size());
         for (NetworkIdentity ident : this) {
@@ -143,7 +143,7 @@
         return true;
     }
 
-    private static void writeOptionalString(DataOutputStream out, String value) throws IOException {
+    private static void writeOptionalString(DataOutput out, String value) throws IOException {
         if (value != null) {
             out.writeByte(1);
             out.writeUTF(value);
@@ -152,7 +152,7 @@
         }
     }
 
-    private static String readOptionalString(DataInputStream in) throws IOException {
+    private static String readOptionalString(DataInput in) throws IOException {
         if (in.readByte() != 0) {
             return in.readUTF();
         } else {
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 38ffccd..0e7b4b8 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -27,6 +27,8 @@
 import static android.Manifest.permission.READ_NETWORK_USAGE_HISTORY;
 import static android.Manifest.permission.READ_PHONE_STATE;
 import static android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE;
+import static android.app.PendingIntent.FLAG_IMMUTABLE;
+import static android.app.PendingIntent.FLAG_UPDATE_CURRENT;
 import static android.content.Intent.ACTION_PACKAGE_ADDED;
 import static android.content.Intent.ACTION_UID_REMOVED;
 import static android.content.Intent.ACTION_USER_ADDED;
@@ -1429,17 +1431,17 @@
 
                 final Intent snoozeIntent = buildSnoozeWarningIntent(policy.template);
                 builder.setDeleteIntent(PendingIntent.getBroadcast(
-                        mContext, 0, snoozeIntent, PendingIntent.FLAG_UPDATE_CURRENT));
+                        mContext, 0, snoozeIntent, FLAG_UPDATE_CURRENT | FLAG_IMMUTABLE));
 
                 final Intent viewIntent = buildViewDataUsageIntent(res, policy.template);
                 // TODO: Resolve to single code path.
                 if (UserManager.isHeadlessSystemUserMode()) {
                     builder.setContentIntent(PendingIntent.getActivityAsUser(
-                            mContext, 0, viewIntent, PendingIntent.FLAG_UPDATE_CURRENT,
+                            mContext, 0, viewIntent, FLAG_UPDATE_CURRENT | FLAG_IMMUTABLE,
                             /* options= */ null, UserHandle.CURRENT));
                 } else {
                     builder.setContentIntent(PendingIntent.getActivity(
-                            mContext, 0, viewIntent, PendingIntent.FLAG_UPDATE_CURRENT));
+                            mContext, 0, viewIntent, FLAG_UPDATE_CURRENT | FLAG_IMMUTABLE));
                 }
                 break;
             }
@@ -1463,11 +1465,11 @@
                 // TODO: Resolve to single code path.
                 if (UserManager.isHeadlessSystemUserMode()) {
                     builder.setContentIntent(PendingIntent.getActivityAsUser(
-                            mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT,
+                            mContext, 0, intent, FLAG_UPDATE_CURRENT | FLAG_IMMUTABLE,
                             /* options= */ null, UserHandle.CURRENT));
                 } else {
                     builder.setContentIntent(PendingIntent.getActivity(
-                            mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT));
+                            mContext, 0, intent, FLAG_UPDATE_CURRENT | FLAG_IMMUTABLE));
                 }
                 break;
             }
@@ -1494,11 +1496,11 @@
                 // TODO: Resolve to single code path.
                 if (UserManager.isHeadlessSystemUserMode()) {
                     builder.setContentIntent(PendingIntent.getActivityAsUser(
-                            mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT,
+                            mContext, 0, intent, FLAG_UPDATE_CURRENT | FLAG_IMMUTABLE,
                             /* options= */ null, UserHandle.CURRENT));
                 } else {
                     builder.setContentIntent(PendingIntent.getActivity(
-                            mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT));
+                            mContext, 0, intent, FLAG_UPDATE_CURRENT | FLAG_IMMUTABLE));
                 }
                 break;
             }
@@ -1515,17 +1517,17 @@
 
                 final Intent snoozeIntent = buildSnoozeRapidIntent(policy.template);
                 builder.setDeleteIntent(PendingIntent.getBroadcast(
-                        mContext, 0, snoozeIntent, PendingIntent.FLAG_UPDATE_CURRENT));
+                        mContext, 0, snoozeIntent, FLAG_UPDATE_CURRENT | FLAG_IMMUTABLE));
 
                 final Intent viewIntent = buildViewDataUsageIntent(res, policy.template);
                 // TODO: Resolve to single code path.
                 if (UserManager.isHeadlessSystemUserMode()) {
                     builder.setContentIntent(PendingIntent.getActivityAsUser(
-                            mContext, 0, viewIntent, PendingIntent.FLAG_UPDATE_CURRENT,
+                            mContext, 0, viewIntent, FLAG_UPDATE_CURRENT | FLAG_IMMUTABLE,
                             /* options= */ null, UserHandle.CURRENT));
                 } else {
                     builder.setContentIntent(PendingIntent.getActivity(
-                            mContext, 0, viewIntent, PendingIntent.FLAG_UPDATE_CURRENT));
+                            mContext, 0, viewIntent, FLAG_UPDATE_CURRENT | FLAG_IMMUTABLE));
                 }
                 break;
             }
diff --git a/services/core/java/com/android/server/net/NetworkStatsCollection.java b/services/core/java/com/android/server/net/NetworkStatsCollection.java
index c4beddd4..6aefe41 100644
--- a/services/core/java/com/android/server/net/NetworkStatsCollection.java
+++ b/services/core/java/com/android/server/net/NetworkStatsCollection.java
@@ -63,12 +63,15 @@
 import com.google.android.collect.Maps;
 
 import java.io.BufferedInputStream;
+import java.io.DataInput;
 import java.io.DataInputStream;
+import java.io.DataOutput;
 import java.io.DataOutputStream;
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.OutputStream;
 import java.io.PrintWriter;
 import java.net.ProtocolException;
 import java.time.ZonedDateTime;
@@ -82,7 +85,7 @@
  * Collection of {@link NetworkStatsHistory}, stored based on combined key of
  * {@link NetworkIdentitySet}, UID, set, and tag. Knows how to persist itself.
  */
-public class NetworkStatsCollection implements FileRotator.Reader {
+public class NetworkStatsCollection implements FileRotator.Reader, FileRotator.Writer {
     /** File header magic number: "ANET" */
     private static final int FILE_MAGIC = 0x414E4554;
 
@@ -431,10 +434,10 @@
 
     @Override
     public void read(InputStream in) throws IOException {
-        read(new DataInputStream(in));
+        read((DataInput) new DataInputStream(in));
     }
 
-    public void read(DataInputStream in) throws IOException {
+    private void read(DataInput in) throws IOException {
         // verify file magic header intact
         final int magic = in.readInt();
         if (magic != FILE_MAGIC) {
@@ -468,7 +471,13 @@
         }
     }
 
-    public void write(DataOutputStream out) throws IOException {
+    @Override
+    public void write(OutputStream out) throws IOException {
+        write((DataOutput) new DataOutputStream(out));
+        out.flush();
+    }
+
+    private void write(DataOutput out) throws IOException {
         // cluster key lists grouped by ident
         final HashMap<NetworkIdentitySet, ArrayList<Key>> keysByIdent = Maps.newHashMap();
         for (Key key : mStats.keySet()) {
@@ -497,8 +506,6 @@
                 history.writeToStream(out);
             }
         }
-
-        out.flush();
     }
 
     @Deprecated
diff --git a/services/core/java/com/android/server/net/NetworkStatsRecorder.java b/services/core/java/com/android/server/net/NetworkStatsRecorder.java
index ce74169..978ae87 100644
--- a/services/core/java/com/android/server/net/NetworkStatsRecorder.java
+++ b/services/core/java/com/android/server/net/NetworkStatsRecorder.java
@@ -42,7 +42,6 @@
 import libcore.io.IoUtils;
 
 import java.io.ByteArrayOutputStream;
-import java.io.DataOutputStream;
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
@@ -375,7 +374,7 @@
 
         @Override
         public void write(OutputStream out) throws IOException {
-            mCollection.write(new DataOutputStream(out));
+            mCollection.write(out);
             mCollection.reset();
         }
     }
@@ -412,7 +411,7 @@
 
         @Override
         public void write(OutputStream out) throws IOException {
-            mTemp.write(new DataOutputStream(out));
+            mTemp.write(out);
         }
     }
 
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index 54e9b37..21537e6 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -73,7 +73,6 @@
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
 
 import java.io.IOException;
 import java.io.PrintWriter;
@@ -544,7 +543,8 @@
     /**
      * This is called to process tags other than {@link #TAG_MANAGED_SERVICES}.
      */
-    protected void readExtraTag(String tag, TypedXmlPullParser parser) throws IOException {}
+    protected void readExtraTag(String tag, TypedXmlPullParser parser)
+            throws IOException, XmlPullParserException {}
 
     protected final void migrateToXml() {
         for (UserInfo user : mUm.getUsers()) {
@@ -1613,6 +1613,7 @@
         public boolean isSystem;
         public ServiceConnection connection;
         public int targetSdkVersion;
+        public Pair<ComponentName, Integer> mKey;
 
         public ManagedServiceInfo(IInterface service, ComponentName component,
                 int userid, boolean isSystem, ServiceConnection connection, int targetSdkVersion) {
@@ -1622,6 +1623,7 @@
             this.isSystem = isSystem;
             this.connection = connection;
             this.targetSdkVersion = targetSdkVersion;
+            mKey = Pair.create(component, userid);
         }
 
         public boolean isGuest(ManagedServices host) {
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index c9ed518..cc5dfdc 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -66,6 +66,10 @@
 import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL;
 import static android.os.UserHandle.USER_NULL;
 import static android.os.UserHandle.USER_SYSTEM;
+import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ALERTING;
+import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_CONVERSATIONS;
+import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ONGOING;
+import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_SILENT;
 import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_CALL_EFFECTS;
 import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_EFFECTS;
 import static android.service.notification.NotificationListenerService.HINT_HOST_DISABLE_NOTIFICATION_EFFECTS;
@@ -102,6 +106,7 @@
 import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES;
 import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES;
 import static com.android.internal.util.FrameworkStatsLog.PACKAGE_NOTIFICATION_PREFERENCES;
+import static com.android.internal.util.Preconditions.checkArgument;
 import static com.android.server.am.PendingIntentRecord.FLAG_ACTIVITY_SENDER;
 import static com.android.server.am.PendingIntentRecord.FLAG_BROADCAST_SENDER;
 import static com.android.server.am.PendingIntentRecord.FLAG_SERVICE_SENDER;
@@ -163,6 +168,8 @@
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.PackageManagerInternal;
 import android.content.pm.ParceledListSlice;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
 import android.content.pm.ShortcutInfo;
 import android.content.pm.ShortcutServiceInternal;
 import android.content.pm.UserInfo;
@@ -209,6 +216,7 @@
 import android.service.notification.IStatusBarNotificationHolder;
 import android.service.notification.ListenersDisablingEffectsProto;
 import android.service.notification.NotificationAssistantService;
+import android.service.notification.NotificationListenerFilter;
 import android.service.notification.NotificationListenerService;
 import android.service.notification.NotificationRankingUpdate;
 import android.service.notification.NotificationRecordProto;
@@ -301,6 +309,7 @@
 import java.time.Duration;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.LinkedList;
@@ -1021,7 +1030,7 @@
                     nv.recycle();
                 }
                 reportUserInteraction(r);
-                mAssistants.notifyAssistantActionClicked(r.getSbn(), action, generatedByAssistant);
+                mAssistants.notifyAssistantActionClicked(r, action, generatedByAssistant);
             }
         }
 
@@ -1110,7 +1119,7 @@
                         reportSeen(r);
                     }
                     r.setVisibility(true, nv.rank, nv.count, mNotificationRecordLogger);
-                    mAssistants.notifyAssistantVisibilityChangedLocked(r.getSbn(), true);
+                    mAssistants.notifyAssistantVisibilityChangedLocked(r, true);
                     boolean isHun = (nv.location
                             == NotificationVisibility.NotificationLocation.LOCATION_FIRST_HEADS_UP);
                     // hasBeenVisiblyExpanded must be called after updating the expansion state of
@@ -1129,7 +1138,7 @@
                     NotificationRecord r = mNotificationsByKey.get(nv.key);
                     if (r == null) continue;
                     r.setVisibility(false, nv.rank, nv.count, mNotificationRecordLogger);
-                    mAssistants.notifyAssistantVisibilityChangedLocked(r.getSbn(), false);
+                    mAssistants.notifyAssistantVisibilityChangedLocked(r, false);
                     nv.recycle();
                 }
             }
@@ -1161,7 +1170,7 @@
                         reportUserInteraction(r);
                     }
                     mAssistants.notifyAssistantExpansionChangedLocked(
-                            r.getSbn(), userAction, expanded);
+                            r.getSbn(), r.getNotificationType(), userAction, expanded);
                 }
             }
         }
@@ -1180,7 +1189,7 @@
                             NotificationRecordLogger.NotificationEvent.NOTIFICATION_DIRECT_REPLIED,
                             r);
                     reportUserInteraction(r);
-                    mAssistants.notifyAssistantNotificationDirectReplyLocked(r.getSbn());
+                    mAssistants.notifyAssistantNotificationDirectReplyLocked(r);
                 }
             }
         }
@@ -1227,7 +1236,8 @@
                     // Treat clicking on a smart reply as a user interaction.
                     reportUserInteraction(r);
                     mAssistants.notifyAssistantSuggestedReplySent(
-                            r.getSbn(), reply, r.getSuggestionsGeneratedByAssistant());
+                            r.getSbn(), r.getNotificationType(), reply,
+                            r.getSuggestionsGeneratedByAssistant());
                 }
             }
         }
@@ -2241,7 +2251,8 @@
         init(handler, new RankingHandlerWorker(mRankingThread.getLooper()),
                 AppGlobals.getPackageManager(), getContext().getPackageManager(),
                 getLocalService(LightsManager.class),
-                new NotificationListeners(AppGlobals.getPackageManager()),
+                new NotificationListeners(getContext(), mNotificationLock, mUserProfiles,
+                        AppGlobals.getPackageManager()),
                 new NotificationAssistants(getContext(), mNotificationLock, mUserProfiles,
                         AppGlobals.getPackageManager()),
                 new ConditionProviders(getContext(), mUserProfiles, AppGlobals.getPackageManager()),
@@ -3253,6 +3264,21 @@
         }
 
         @Override
+        public NotificationListenerFilter getListenerFilter(ComponentName cn, int userId) {
+            checkCallerIsSystem();
+            return mListeners.getNotificationListenerFilter(Pair.create(cn, userId));
+        }
+
+        @Override
+        public void setListenerFilter(ComponentName cn, int userId,
+                NotificationListenerFilter nlf) {
+            checkCallerIsSystem();
+            mListeners.setNotificationListenerFilter(Pair.create(cn, userId), nlf);
+            // TODO (b/173052211): cancel notifications for listeners that can no longer see them
+            handleSavePolicyFile();
+        }
+
+        @Override
         public int getPackageImportance(String pkg) {
             checkCallerIsSystemOrSameApp(pkg);
             return mPreferencesHelper.getImportance(pkg, Binder.getCallingUid());
@@ -4268,7 +4294,7 @@
                             : mNotificationList.get(i);
                     if (r == null) continue;
                     StatusBarNotification sbn = r.getSbn();
-                    if (!isVisibleToListener(sbn, info)) continue;
+                    if (!isVisibleToListener(sbn, r.getNotificationType(), info)) continue;
                     StatusBarNotification sbnToSend =
                             (trim == TRIM_FULL) ? sbn : sbn.cloneLight();
                     list.add(sbnToSend);
@@ -4298,7 +4324,7 @@
                     final NotificationRecord r = snoozedRecords.get(i);
                     if (r == null) continue;
                     StatusBarNotification sbn = r.getSbn();
-                    if (!isVisibleToListener(sbn, info)) continue;
+                    if (!isVisibleToListener(sbn, r.getNotificationType(), info)) continue;
                     StatusBarNotification sbnToSend =
                             (trim == TRIM_FULL) ? sbn : sbn.cloneLight();
                     list.add(sbnToSend);
@@ -6339,7 +6365,7 @@
             cancelNotificationLocked(r, false, REASON_SNOOZED, wasPosted, null);
             updateLightsLocked();
             if (mSnoozeCriterionId != null) {
-                mAssistants.notifyAssistantSnoozedLocked(r.getSbn(), mSnoozeCriterionId);
+                mAssistants.notifyAssistantSnoozedLocked(r, mSnoozeCriterionId);
                 mSnoozeHelper.snooze(r, mSnoozeCriterionId);
             } else {
                 mSnoozeHelper.snooze(r, mDuration);
@@ -8812,7 +8838,7 @@
 
         for (int i = 0; i < N; i++) {
             NotificationRecord record = mNotificationList.get(i);
-            if (!isVisibleToListener(record.getSbn(), info)) {
+            if (!isVisibleToListener(record.getSbn(), record.getNotificationType(), info)) {
                 continue;
             }
             final String key = record.getSbn().getKey();
@@ -8886,11 +8912,21 @@
     }
 
     @VisibleForTesting
-    boolean isVisibleToListener(StatusBarNotification sbn, ManagedServiceInfo listener) {
+    boolean isVisibleToListener(StatusBarNotification sbn, int notificationType,
+            ManagedServiceInfo listener) {
         if (!listener.enabledAndUserMatches(sbn.getUserId())) {
             return false;
         }
-        return isInteractionVisibleToListener(listener, sbn.getUserId());
+        if (!isInteractionVisibleToListener(listener, sbn.getUserId())) {
+            return false;
+        }
+        NotificationListenerFilter nls = mListeners.getNotificationListenerFilter(listener.mKey);
+        if (nls != null
+                && (!nls.isTypeAllowed(notificationType)
+                || !nls.isPackageAllowed(sbn.getPackageName()))) {
+            return false;
+        }
+        return true;
     }
 
     /**
@@ -9126,7 +9162,8 @@
             for (final ManagedServiceInfo info : NotificationAssistants.this.getServices()) {
                 ArrayList<String> keys = new ArrayList<>(records.size());
                 for (NotificationRecord r : records) {
-                    boolean sbnVisible = isVisibleToListener(r.getSbn(), info)
+                    boolean sbnVisible = isVisibleToListener(
+                            r.getSbn(), r.getNotificationType(), info)
                             && info.isSameUser(r.getUserId());
                     if (sbnVisible) {
                         keys.add(r.getKey());
@@ -9241,6 +9278,7 @@
             final StatusBarNotification sbn = r.getSbn();
             notifyAssistantLocked(
                     sbn,
+                    r.getNotificationType(),
                     true /* sameUserOnly */,
                     (assistant, sbnHolder) -> {
                         try {
@@ -9257,14 +9295,15 @@
 
         @GuardedBy("mNotificationLock")
         void notifyAssistantVisibilityChangedLocked(
-                final StatusBarNotification sbn,
+                final NotificationRecord r,
                 final boolean isVisible) {
-            final String key = sbn.getKey();
+            final String key = r.getSbn().getKey();
             if (DBG) {
                 Slog.d(TAG, "notifyAssistantVisibilityChangedLocked: " + key);
             }
             notifyAssistantLocked(
-                    sbn,
+                    r.getSbn(),
+                    r.getNotificationType(),
                     true /* sameUserOnly */,
                     (assistant, sbnHolder) -> {
                         try {
@@ -9278,11 +9317,13 @@
         @GuardedBy("mNotificationLock")
         void notifyAssistantExpansionChangedLocked(
                 final StatusBarNotification sbn,
+                final int notificationType,
                 final boolean isUserAction,
                 final boolean isExpanded) {
             final String key = sbn.getKey();
             notifyAssistantLocked(
                     sbn,
+                    notificationType,
                     true /* sameUserOnly */,
                     (assistant, sbnHolder) -> {
                         try {
@@ -9295,10 +9336,11 @@
 
         @GuardedBy("mNotificationLock")
         void notifyAssistantNotificationDirectReplyLocked(
-                final StatusBarNotification sbn) {
-            final String key = sbn.getKey();
+                final NotificationRecord r) {
+            final String key = r.getKey();
             notifyAssistantLocked(
-                    sbn,
+                    r.getSbn(),
+                    r.getNotificationType(),
                     true /* sameUserOnly */,
                     (assistant, sbnHolder) -> {
                         try {
@@ -9311,10 +9353,12 @@
 
         @GuardedBy("mNotificationLock")
         void notifyAssistantSuggestedReplySent(
-                final StatusBarNotification sbn, CharSequence reply, boolean generatedByAssistant) {
+                final StatusBarNotification sbn, int notificationType,
+                CharSequence reply, boolean generatedByAssistant) {
             final String key = sbn.getKey();
             notifyAssistantLocked(
                     sbn,
+                    notificationType,
                     true /* sameUserOnly */,
                     (assistant, sbnHolder) -> {
                         try {
@@ -9332,11 +9376,12 @@
 
         @GuardedBy("mNotificationLock")
         void notifyAssistantActionClicked(
-                final StatusBarNotification sbn, Notification.Action action,
+                final NotificationRecord r, Notification.Action action,
                 boolean generatedByAssistant) {
-            final String key = sbn.getKey();
+            final String key = r.getSbn().getKey();
             notifyAssistantLocked(
-                    sbn,
+                    r.getSbn(),
+                    r.getNotificationType(),
                     true /* sameUserOnly */,
                     (assistant, sbnHolder) -> {
                         try {
@@ -9358,9 +9403,10 @@
          */
         @GuardedBy("mNotificationLock")
         private void notifyAssistantSnoozedLocked(
-                final StatusBarNotification sbn, final String snoozeCriterionId) {
+                final NotificationRecord r, final String snoozeCriterionId) {
             notifyAssistantLocked(
-                    sbn,
+                    r.getSbn(),
+                    r.getNotificationType(),
                     true /* sameUserOnly */,
                     (assistant, sbnHolder) -> {
                         try {
@@ -9384,6 +9430,7 @@
         @GuardedBy("mNotificationLock")
         private void notifyAssistantLocked(
                 final StatusBarNotification sbn,
+                int notificationType,
                 boolean sameUserOnly,
                 BiConsumer<INotificationListener, StatusBarNotificationHolder> callback) {
             TrimCache trimCache = new TrimCache(sbn);
@@ -9397,7 +9444,7 @@
                                 + sameUserOnly + "], callback = [" + callback + "]");
             }
             for (final ManagedServiceInfo info : NotificationAssistants.this.getServices()) {
-                boolean sbnVisible = isVisibleToListener(sbn, info)
+                boolean sbnVisible = isVisibleToListener(sbn, notificationType, info)
                         && (!sameUserOnly || info.isSameUser(sbn.getUserId()));
                 if (debug) {
                     Slog.v(TAG, "notifyAssistantLocked info=" + info + " snbVisible=" + sbnVisible);
@@ -9451,11 +9498,22 @@
 
     public class NotificationListeners extends ManagedServices {
         static final String TAG_ENABLED_NOTIFICATION_LISTENERS = "enabled_listeners";
+        static final String TAG_REQUESTED_LISTENERS = "req_listeners";
+        static final String TAG_REQUESTED_LISTENER = "listener";
+        static final String ATT_COMPONENT = "component";
+        static final String ATT_TYPES = "types";
+        static final String ATT_PKGS = "pkgs";
+        static final String TAG_APPROVED = "allowed";
+        static final String TAG_DISALLOWED= "disallowed";
+        static final String XML_SEPARATOR = ",";
 
         private final ArraySet<ManagedServiceInfo> mLightTrimListeners = new ArraySet<>();
+        ArrayMap<Pair<ComponentName, Integer>, NotificationListenerFilter>
+                mRequestedNotificationListeners = new ArrayMap<>();
 
-        public NotificationListeners(IPackageManager pm) {
-            super(getContext(), mNotificationLock, mUserProfiles, pm);
+        public NotificationListeners(Context context, Object lock, UserProfiles userProfiles,
+                IPackageManager pm) {
+            super(context, lock, userProfiles, pm);
         }
 
         @Override
@@ -9552,6 +9610,59 @@
         }
 
         @Override
+        public void onUserRemoved(int user) {
+            super.onUserRemoved(user);
+            for (int i = mRequestedNotificationListeners.size() - 1; i >= 0; i--) {
+                if (mRequestedNotificationListeners.keyAt(i).second == user) {
+                    mRequestedNotificationListeners.removeAt(i);
+                }
+            }
+        }
+
+        @Override
+        public void onUserUnlocked(int user) {
+            int flags = PackageManager.GET_SERVICES | PackageManager.GET_META_DATA;
+
+            final PackageManager pmWrapper = mContext.getPackageManager();
+            List<ResolveInfo> installedServices = pmWrapper.queryIntentServicesAsUser(
+                    new Intent(getConfig().serviceInterface), flags, user);
+
+            for (ResolveInfo resolveInfo : installedServices) {
+                ServiceInfo info = resolveInfo.serviceInfo;
+
+                if (!getConfig().bindPermission.equals(info.permission)) {
+                    continue;
+                }
+                Pair key = Pair.create(info.getComponentName(), user);
+                if (!mRequestedNotificationListeners.containsKey(key)) {
+                    mRequestedNotificationListeners.put(key, new NotificationListenerFilter());
+                }
+            }
+            super.onUserUnlocked(user);
+        }
+
+        @Override
+        public void onPackagesChanged(boolean removingPackage, String[] pkgList, int[] uidList) {
+            super.onPackagesChanged(removingPackage, pkgList, uidList);
+
+            // Since the default behavior is to allow everything, we don't need to explicitly
+            // handle package add or update. they will be added to the xml file on next boot or
+            // when the user tries to change the settings.
+            if (removingPackage) {
+                for (int i = 0; i < pkgList.length; i++) {
+                    String pkg = pkgList[i];
+                    int userId = UserHandle.getUserId(uidList[i]);
+                    for (int j = mRequestedNotificationListeners.size() - 1; j >= 0; j--) {
+                        Pair<ComponentName, Integer> key = mRequestedNotificationListeners.keyAt(j);
+                        if (key.second == userId && key.first.getPackageName().equals(pkg)) {
+                            mRequestedNotificationListeners.removeAt(j);
+                        }
+                    }
+                }
+            }
+        }
+
+        @Override
         protected String getRequiredPermission() {
             return null;
         }
@@ -9563,6 +9674,75 @@
             return true;
         }
 
+        @Override
+        protected void readExtraTag(String tag, TypedXmlPullParser parser)
+                throws IOException, XmlPullParserException {
+            if (TAG_REQUESTED_LISTENERS.equals(tag)) {
+                final int listenersOuterDepth = parser.getDepth();
+                while (XmlUtils.nextElementWithin(parser, listenersOuterDepth)) {
+                    if (!TAG_REQUESTED_LISTENER.equals(parser.getName())) {
+                        continue;
+                    }
+                    final int userId = XmlUtils.readIntAttribute(parser, ATT_USER_ID);
+                    final ComponentName cn = ComponentName.unflattenFromString(
+                            XmlUtils.readStringAttribute(parser, ATT_COMPONENT));
+                    int approved = FLAG_FILTER_TYPE_CONVERSATIONS | FLAG_FILTER_TYPE_ALERTING
+                            | FLAG_FILTER_TYPE_SILENT | FLAG_FILTER_TYPE_ONGOING;
+
+                    ArraySet<String> disallowedPkgs = new ArraySet<>();
+                    final int listenerOuterDepth = parser.getDepth();
+                    while (XmlUtils.nextElementWithin(parser, listenerOuterDepth)) {
+                        if (TAG_APPROVED.equals(parser.getName())) {
+                            approved = XmlUtils.readIntAttribute(parser, ATT_TYPES);
+                        } else if (TAG_DISALLOWED.equals(parser.getName())) {
+                            String pkgs = XmlUtils.readStringAttribute(parser, ATT_PKGS);
+                            if (!TextUtils.isEmpty(pkgs)) {
+                                disallowedPkgs = new ArraySet<>(pkgs.split(XML_SEPARATOR));
+                            }
+                        }
+                    }
+                    NotificationListenerFilter nlf =
+                            new NotificationListenerFilter(approved, disallowedPkgs);
+                    mRequestedNotificationListeners.put(Pair.create(cn, userId), nlf);
+                }
+            }
+        }
+
+        @Override
+        protected void writeExtraXmlTags(TypedXmlSerializer out) throws IOException {
+            out.startTag(null, TAG_REQUESTED_LISTENERS);
+            for (Pair<ComponentName, Integer> listener : mRequestedNotificationListeners.keySet()) {
+                NotificationListenerFilter nlf = mRequestedNotificationListeners.get(listener);
+                out.startTag(null, TAG_REQUESTED_LISTENER);
+                XmlUtils.writeStringAttribute(
+                        out, ATT_COMPONENT, listener.first.flattenToString());
+                XmlUtils.writeIntAttribute(out, ATT_USER_ID, listener.second);
+
+                out.startTag(null, TAG_APPROVED);
+                XmlUtils.writeIntAttribute(out, ATT_TYPES, nlf.getTypes());
+                out.endTag(null, TAG_APPROVED);
+
+                out.startTag(null, TAG_DISALLOWED);
+                XmlUtils.writeStringAttribute(
+                        out, ATT_PKGS, String.join(XML_SEPARATOR, nlf.getDisallowedPackages()));
+                out.endTag(null, TAG_DISALLOWED);
+
+                out.endTag(null, TAG_REQUESTED_LISTENER);
+            }
+
+            out.endTag(null, TAG_REQUESTED_LISTENERS);
+        }
+
+        protected @Nullable NotificationListenerFilter getNotificationListenerFilter(
+                Pair<ComponentName, Integer> pair) {
+            return mRequestedNotificationListeners.get(pair);
+        }
+
+        protected void setNotificationListenerFilter(Pair<ComponentName, Integer> pair,
+                NotificationListenerFilter nlf) {
+            mRequestedNotificationListeners.put(pair, nlf);
+        }
+
         @GuardedBy("mNotificationLock")
         public void setOnNotificationPostedTrimLocked(ManagedServiceInfo info, int trim) {
             if (trim == TRIM_LIGHT) {
@@ -9618,8 +9798,9 @@
                 TrimCache trimCache = new TrimCache(sbn);
 
                 for (final ManagedServiceInfo info : getServices()) {
-                    boolean sbnVisible = isVisibleToListener(sbn, info);
-                    boolean oldSbnVisible = (oldSbn != null) && isVisibleToListener(oldSbn, info);
+                    boolean sbnVisible = isVisibleToListener(sbn, r. getNotificationType(), info);
+                    boolean oldSbnVisible = (oldSbn != null)
+                            && isVisibleToListener(oldSbn, old.getNotificationType(), info);
                     // This notification hasn't been and still isn't visible -> ignore.
                     if (!oldSbnVisible && !sbnVisible) {
                         continue;
@@ -9672,7 +9853,7 @@
                 for (final NotificationRecord r : mNotificationList) {
                     // When granting permissions, ignore notifications which are invisible.
                     // When revoking permissions, all notifications are invisible, so process all.
-                    if (grant && !isVisibleToListener(r.getSbn(), info)) {
+                    if (grant && !isVisibleToListener(r.getSbn(), r.getNotificationType(), info)) {
                         continue;
                     }
                     // If the notification is hidden, permissions are not required by the listener.
@@ -9714,7 +9895,7 @@
             // notification
             final StatusBarNotification sbnLight = sbn.cloneLight();
             for (final ManagedServiceInfo info : getServices()) {
-                if (!isVisibleToListener(sbn, info)) {
+                if (!isVisibleToListener(sbn, r.getNotificationType(), info)) {
                     continue;
                 }
 
@@ -9754,7 +9935,8 @@
         public void notifyRankingUpdateLocked(List<NotificationRecord> changedHiddenNotifications) {
             boolean isHiddenRankingUpdate = changedHiddenNotifications != null
                     && changedHiddenNotifications.size() > 0;
-
+            // TODO (b/73052211): if the ranking update changed the notification type,
+            // cancel notifications for NLSes that can't see them anymore
             for (final ManagedServiceInfo serviceInfo : getServices()) {
                 if (!serviceInfo.isEnabledForCurrentProfiles() || !isInteractionVisibleToListener(
                         serviceInfo, ActivityManager.getCurrentUser())) {
@@ -9765,7 +9947,8 @@
                 if (isHiddenRankingUpdate && serviceInfo.targetSdkVersion >=
                         Build.VERSION_CODES.P) {
                     for (NotificationRecord rec : changedHiddenNotifications) {
-                        if (isVisibleToListener(rec.getSbn(), serviceInfo)) {
+                        if (isVisibleToListener(
+                                rec.getSbn(), rec.getNotificationType(), serviceInfo)) {
                             notifyThisListener = true;
                             break;
                         }
@@ -9979,6 +10162,7 @@
         }
     }
 
+
     class RoleObserver implements OnRoleHoldersChangedListener {
         // Role name : user id : list of approved packages
         private ArrayMap<String, ArrayMap<Integer, ArraySet<String>>> mNonBlockableDefaultApps;
@@ -10233,12 +10417,15 @@
         private final Set<String> mPackagesShown = new ArraySet<>();
 
         @Override
-        public IBinder getToken() {
-            return ALLOWLIST_TOKEN;
-        }
-
-        @Override
-        public boolean isActivityStartAllowed(int uid, String packageName) {
+        public boolean isActivityStartAllowed(Collection<IBinder> tokens, int uid,
+                String packageName) {
+            checkArgument(!tokens.isEmpty());
+            for (IBinder token : tokens) {
+                if (token != ALLOWLIST_TOKEN) {
+                    // We only block or warn if the start is exclusively due to notification
+                    return true;
+                }
+            }
             String toastMessage = "Indirect activity start from " + packageName;
             String logcatMessage =
                     "Indirect notification activity start (trampoline) from " + packageName;
@@ -10256,6 +10443,15 @@
             }
         }
 
+        @Override
+        public boolean canCloseSystemDialogs(Collection<IBinder> tokens, int uid) {
+            // If the start is allowed via notification, we allow the app to close system dialogs
+            // only if their targetSdk < S, otherwise they have no valid reason to do this since
+            // trampolines are blocked.
+            return tokens.contains(ALLOWLIST_TOKEN)
+                    && !CompatChanges.isChangeEnabled(NOTIFICATION_TRAMPOLINE_BLOCK, uid);
+        }
+
         private void toast(String message) {
             mUiHandler.post(() ->
                     Toast.makeText(getUiContext(), message + "\nSee go/s-trampolines.",
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index cff4f07..6bc4f7e 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -1257,6 +1257,16 @@
         return !Objects.equals(getSbn().getPackageName(), getSbn().getOpPkg());
     }
 
+    public int getNotificationType() {
+        if (isConversation()) {
+            return NotificationListenerService.FLAG_FILTER_TYPE_CONVERSATIONS;
+        } else if (getImportance() >= IMPORTANCE_DEFAULT) {
+            return NotificationListenerService.FLAG_FILTER_TYPE_ALERTING;
+        } else {
+            return NotificationListenerService.FLAG_FILTER_TYPE_SILENT;
+        }
+    }
+
     /**
      * @return all {@link Uri} that should have permission granted to whoever
      *         will be rendering it. This list has already been vetted to only
diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java
index 5cd22e0..de77372 100644
--- a/services/core/java/com/android/server/notification/ZenModeHelper.java
+++ b/services/core/java/com/android/server/notification/ZenModeHelper.java
@@ -88,7 +88,6 @@
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
 
 import java.io.IOException;
 import java.io.PrintWriter;
@@ -106,6 +105,9 @@
     // The amount of time rules instances can exist without their owning app being installed.
     private static final int RULE_INSTANCE_GRACE_PERIOD = 1000 * 60 * 60 * 72;
 
+    // pkg|userId => uid
+    protected final ArrayMap<String, Integer> mRulesUidCache = new ArrayMap<>();
+
     private final Context mContext;
     private final H mHandler;
     private final SettingsObserver mSettingsObserver;
@@ -145,7 +147,7 @@
         mHandler = new H(looper);
         addCallback(mMetrics);
         mAppOps = context.getSystemService(AppOpsManager.class);
-        mNotificationManager =  context.getSystemService(NotificationManager.class);
+        mNotificationManager = context.getSystemService(NotificationManager.class);
 
         mDefaultConfig = readDefaultConfig(mContext.getResources());
         updateDefaultAutomaticRuleNames();
@@ -384,17 +386,25 @@
         synchronized (mConfig) {
             if (mConfig == null) return false;
             newConfig = mConfig.copy();
-            ZenRule rule = newConfig.automaticRules.get(id);
-            if (rule == null) return false;
-            if (canManageAutomaticZenRule(rule)) {
+            ZenRule ruleToRemove = newConfig.automaticRules.get(id);
+            if (ruleToRemove == null) return false;
+            if (canManageAutomaticZenRule(ruleToRemove)) {
                 newConfig.automaticRules.remove(id);
+                if (ruleToRemove.pkg != null && !"android".equals(ruleToRemove.pkg)) {
+                    for (ZenRule currRule : newConfig.automaticRules.values()) {
+                        if (currRule.pkg != null && currRule.pkg.equals(ruleToRemove.pkg)) {
+                            break; // no need to remove from cache
+                        }
+                    }
+                    mRulesUidCache.remove(getPackageUserKey(ruleToRemove.pkg, newConfig.user));
+                }
                 if (DEBUG) Log.d(TAG, "removeZenRule zenRule=" + id + " reason=" + reason);
             } else {
                 throw new SecurityException(
                         "Cannot delete rules not owned by your condition provider");
             }
             dispatchOnAutomaticRuleStatusChanged(
-                    mConfig.user, rule.pkg, id, AUTOMATIC_RULE_STATUS_REMOVED);
+                    mConfig.user, ruleToRemove.pkg, id, AUTOMATIC_RULE_STATUS_REMOVED);
             return setConfigLocked(newConfig, reason, null, true);
         }
     }
@@ -1192,7 +1202,6 @@
     public void pullRules(List<StatsEvent> events) {
         synchronized (mConfig) {
             final int numConfigs = mConfigs.size();
-            int id = 0;
             for (int i = 0; i < numConfigs; i++) {
                 final int user = mConfigs.keyAt(i);
                 final ZenModeConfig config = mConfigs.valueAt(i);
@@ -1208,16 +1217,16 @@
                         .writeByteArray(config.toZenPolicy().toProto());
                 events.add(data.build());
                 if (config.manualRule != null && config.manualRule.enabler != null) {
-                    ruleToProto(user, config.manualRule, events);
+                    ruleToProtoLocked(user, config.manualRule, events);
                 }
                 for (ZenRule rule : config.automaticRules.values()) {
-                    ruleToProto(user, rule, events);
+                    ruleToProtoLocked(user, rule, events);
                 }
             }
         }
     }
 
-    private void ruleToProto(int user, ZenRule rule, List<StatsEvent> events) {
+    private void ruleToProtoLocked(int user, ZenRule rule, List<StatsEvent> events) {
         // Make the ID safe.
         String id = rule.id == null ? "" : rule.id;
         if (!ZenModeConfig.DEFAULT_RULE_IDS.contains(id)) {
@@ -1231,9 +1240,6 @@
             id = ZenModeConfig.MANUAL_RULE_ID;
         }
 
-        // TODO: fetch the uid from the package manager
-        int uid = "android".equals(pkg) ? Process.SYSTEM_UID : 0;
-
         SysUiStatsEvent.Builder data;
         data = mStatsEventBuilderFactory.newBuilder()
                 .setAtomId(DND_MODE_RULE)
@@ -1242,7 +1248,7 @@
                 .writeBoolean(false) // channels_bypassing unused for rules
                 .writeInt(rule.zenMode)
                 .writeString(id)
-                .writeInt(uid)
+                .writeInt(getPackageUid(pkg, user))
                 .addBooleanAnnotation(ANNOTATION_ID_IS_UID, true);
         byte[] policyProto = new byte[]{};
         if (rule.zenPolicy != null) {
@@ -1252,6 +1258,24 @@
         events.add(data.build());
     }
 
+    private int getPackageUid(String pkg, int user) {
+        if ("android".equals(pkg)) {
+            return Process.SYSTEM_UID;
+        }
+        final String key = getPackageUserKey(pkg, user);
+        if (mRulesUidCache.get(key) == null) {
+            try {
+                mRulesUidCache.put(key, mPm.getPackageUidAsUser(pkg, user));
+            } catch (PackageManager.NameNotFoundException e) {
+            }
+        }
+        return mRulesUidCache.getOrDefault(key, -1);
+    }
+
+    private static String getPackageUserKey(String pkg, int user) {
+        return pkg + "|" + user;
+    }
+
     @VisibleForTesting
     protected final class RingerModeDelegate implements AudioManagerInternal.RingerModeDelegate {
         @Override
diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
index d7a1ba2..4d4a6c1 100644
--- a/services/core/java/com/android/server/om/OverlayManagerService.java
+++ b/services/core/java/com/android/server/om/OverlayManagerService.java
@@ -24,11 +24,15 @@
 import static android.content.Intent.ACTION_USER_ADDED;
 import static android.content.Intent.ACTION_USER_REMOVED;
 import static android.content.Intent.EXTRA_REASON;
+import static android.content.om.OverlayManagerTransaction.Request.TYPE_SET_DISABLED;
+import static android.content.om.OverlayManagerTransaction.Request.TYPE_SET_ENABLED;
 import static android.content.pm.PackageManager.SIGNATURE_MATCH;
 import static android.os.Trace.TRACE_TAG_RRO;
 import static android.os.Trace.traceBegin;
 import static android.os.Trace.traceEnd;
 
+import static com.android.server.om.OverlayManagerServiceImpl.OperationFailedException;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.UserIdInt;
@@ -40,6 +44,7 @@
 import android.content.IntentFilter;
 import android.content.om.IOverlayManager;
 import android.content.om.OverlayInfo;
+import android.content.om.OverlayManagerTransaction;
 import android.content.om.OverlayableInfo;
 import android.content.pm.IPackageManager;
 import android.content.pm.PackageInfo;
@@ -49,6 +54,7 @@
 import android.net.Uri;
 import android.os.Binder;
 import android.os.Environment;
+import android.os.HandlerThread;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.ResultReceiver;
@@ -65,7 +71,6 @@
 
 import com.android.internal.content.om.OverlayConfig;
 import com.android.server.FgThread;
-import com.android.server.IoThread;
 import com.android.server.LocalServices;
 import com.android.server.SystemConfig;
 import com.android.server.SystemService;
@@ -84,12 +89,15 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
-import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.Optional;
+import java.util.Set;
+import java.util.function.Consumer;
 
 /**
  * Service to manage asset overlays.
@@ -238,7 +246,14 @@
 
     private final OverlayActorEnforcer mActorEnforcer;
 
-    private final AtomicBoolean mPersistSettingsScheduled = new AtomicBoolean(false);
+    private final Consumer<PackageAndUser> mPropagateOverlayChange = (pair) -> {
+        persistSettings();
+        FgThread.getHandler().post(() -> {
+            List<String> affectedTargets = updatePackageManager(pair.packageName, pair.userId);
+            updateActivityManager(affectedTargets, pair.userId);
+            broadcastActionOverlayChanged(affectedTargets, pair.userId);
+        });
+    };
 
     public OverlayManagerService(@NonNull final Context context) {
         super(context);
@@ -251,17 +266,19 @@
             IdmapManager im = new IdmapManager(IdmapDaemon.getInstance(), mPackageManager);
             mSettings = new OverlayManagerSettings();
             mImpl = new OverlayManagerServiceImpl(mPackageManager, im, mSettings,
-                    OverlayConfig.getSystemInstance(), getDefaultOverlayPackages(),
-                    new OverlayChangeListener());
+                    OverlayConfig.getSystemInstance(), getDefaultOverlayPackages());
             mActorEnforcer = new OverlayActorEnforcer(mPackageManager);
 
+            HandlerThread packageReceiverThread = new HandlerThread(TAG);
+            packageReceiverThread.start();
+
             final IntentFilter packageFilter = new IntentFilter();
             packageFilter.addAction(ACTION_PACKAGE_ADDED);
             packageFilter.addAction(ACTION_PACKAGE_CHANGED);
             packageFilter.addAction(ACTION_PACKAGE_REMOVED);
             packageFilter.addDataScheme("package");
             getContext().registerReceiverAsUser(new PackageReceiver(), UserHandle.ALL,
-                    packageFilter, null, null);
+                    packageFilter, null, packageReceiverThread.getThreadHandler());
 
             final IntentFilter userFilter = new IntentFilter();
             userFilter.addAction(ACTION_USER_ADDED);
@@ -294,11 +311,11 @@
             for (int i = 0; i < userCount; i++) {
                 final UserInfo userInfo = users.get(i);
                 if (!userInfo.supportsSwitchTo() && userInfo.id != UserHandle.USER_SYSTEM) {
-                    // Initialize any users that can't be switched to, as there state would
+                    // Initialize any users that can't be switched to, as their state would
                     // never be setup in onSwitchUser(). We will switch to the system user right
                     // after this, and its state will be setup there.
                     final List<String> targets = mImpl.updateOverlaysForUser(users.get(i).id);
-                    updateOverlayPaths(users.get(i).id, targets);
+                    updatePackageManager(targets, users.get(i).id);
                 }
             }
         }
@@ -316,9 +333,10 @@
             // any asset changes to the rest of the system
             synchronized (mLock) {
                 final List<String> targets = mImpl.updateOverlaysForUser(newUserId);
-                updateAssets(newUserId, targets);
+                final List<String> affectedTargets = updatePackageManager(targets, newUserId);
+                updateActivityManager(affectedTargets, newUserId);
             }
-            schedulePersistSettings();
+            persistSettings();
         } finally {
             traceEnd(TRACE_TAG_RRO);
         }
@@ -402,10 +420,17 @@
                                 false);
                         if (pi != null && !pi.applicationInfo.isInstantApp()) {
                             mPackageManager.cachePackageInfo(packageName, userId, pi);
-                            if (pi.isOverlayPackage()) {
-                                mImpl.onOverlayPackageAdded(packageName, userId);
-                            } else {
-                                mImpl.onTargetPackageAdded(packageName, userId);
+
+                            try {
+                                if (pi.isOverlayPackage()) {
+                                    mImpl.onOverlayPackageAdded(packageName, userId)
+                                        .ifPresent(mPropagateOverlayChange);
+                                } else {
+                                    mImpl.onTargetPackageAdded(packageName, userId)
+                                        .ifPresent(mPropagateOverlayChange);
+                                }
+                            } catch (OperationFailedException e) {
+                                Slog.e(TAG, "onPackageAdded internal error", e);
                             }
                         }
                     }
@@ -425,10 +450,17 @@
                                 false);
                         if (pi != null && pi.applicationInfo.isInstantApp()) {
                             mPackageManager.cachePackageInfo(packageName, userId, pi);
-                            if (pi.isOverlayPackage()) {
-                                mImpl.onOverlayPackageChanged(packageName, userId);
-                            }  else {
-                                mImpl.onTargetPackageChanged(packageName, userId);
+
+                            try {
+                                if (pi.isOverlayPackage()) {
+                                    mImpl.onOverlayPackageChanged(packageName, userId)
+                                        .ifPresent(mPropagateOverlayChange);
+                                }  else {
+                                    mImpl.onTargetPackageChanged(packageName, userId)
+                                        .ifPresent(mPropagateOverlayChange);
+                                }
+                            } catch (OperationFailedException e) {
+                                Slog.e(TAG, "onPackageChanged internal error", e);
                             }
                         }
                     }
@@ -447,7 +479,12 @@
                         mPackageManager.forgetPackageInfo(packageName, userId);
                         final OverlayInfo oi = mImpl.getOverlayInfo(packageName, userId);
                         if (oi != null) {
-                            mImpl.onOverlayPackageReplacing(packageName, userId);
+                            try {
+                                mImpl.onOverlayPackageReplacing(packageName, userId)
+                                    .ifPresent(mPropagateOverlayChange);
+                            } catch (OperationFailedException e) {
+                                Slog.e(TAG, "onPackageReplacing internal error", e);
+                            }
                         }
                     }
                 }
@@ -466,10 +503,16 @@
                                 false);
                         if (pi != null && !pi.applicationInfo.isInstantApp()) {
                             mPackageManager.cachePackageInfo(packageName, userId, pi);
-                            if (pi.isOverlayPackage()) {
-                                mImpl.onOverlayPackageReplaced(packageName, userId);
-                            } else {
-                                mImpl.onTargetPackageReplaced(packageName, userId);
+                            try {
+                                if (pi.isOverlayPackage()) {
+                                    mImpl.onOverlayPackageReplaced(packageName, userId)
+                                        .ifPresent(mPropagateOverlayChange);
+                                } else {
+                                    mImpl.onTargetPackageReplaced(packageName, userId)
+                                        .ifPresent(mPropagateOverlayChange);
+                                }
+                            } catch (OperationFailedException e) {
+                                Slog.e(TAG, "onPackageReplaced internal error", e);
                             }
                         }
                     }
@@ -487,10 +530,17 @@
                     synchronized (mLock) {
                         mPackageManager.forgetPackageInfo(packageName, userId);
                         final OverlayInfo oi = mImpl.getOverlayInfo(packageName, userId);
-                        if (oi != null) {
-                            mImpl.onOverlayPackageRemoved(packageName, userId);
-                        } else {
-                            mImpl.onTargetPackageRemoved(packageName, userId);
+
+                        try {
+                            if (oi != null) {
+                                mImpl.onOverlayPackageRemoved(packageName, userId)
+                                    .ifPresent(mPropagateOverlayChange);
+                            } else {
+                                mImpl.onTargetPackageRemoved(packageName, userId)
+                                    .ifPresent(mPropagateOverlayChange);
+                            }
+                        } catch (OperationFailedException e) {
+                            Slog.e(TAG, "onPackageRemoved internal error", e);
                         }
                     }
                 }
@@ -513,7 +563,7 @@
                             synchronized (mLock) {
                                 targets = mImpl.updateOverlaysForUser(userId);
                             }
-                            updateOverlayPaths(userId, targets);
+                            updatePackageManager(targets, userId);
                         } finally {
                             traceEnd(TRACE_TAG_RRO);
                         }
@@ -608,7 +658,13 @@
                 final long ident = Binder.clearCallingIdentity();
                 try {
                     synchronized (mLock) {
-                        return mImpl.setEnabled(packageName, enable, realUserId);
+                        try {
+                            mImpl.setEnabled(packageName, enable, realUserId)
+                                .ifPresent(mPropagateOverlayChange);
+                            return true;
+                        } catch (OperationFailedException e) {
+                            return false;
+                        }
                     }
                 } finally {
                     Binder.restoreCallingIdentity(ident);
@@ -633,8 +689,14 @@
                 final long ident = Binder.clearCallingIdentity();
                 try {
                     synchronized (mLock) {
-                        return mImpl.setEnabledExclusive(packageName, false /* withinCategory */,
-                                realUserId);
+                        try {
+                            mImpl.setEnabledExclusive(packageName,
+                                    false /* withinCategory */, realUserId)
+                                .ifPresent(mPropagateOverlayChange);
+                            return true;
+                        } catch (OperationFailedException e) {
+                            return false;
+                        }
                     }
                 } finally {
                     Binder.restoreCallingIdentity(ident);
@@ -660,8 +722,14 @@
                 final long ident = Binder.clearCallingIdentity();
                 try {
                     synchronized (mLock) {
-                        return mImpl.setEnabledExclusive(packageName, true /* withinCategory */,
-                                realUserId);
+                        try {
+                            mImpl.setEnabledExclusive(packageName,
+                                    true /* withinCategory */, realUserId)
+                                .ifPresent(mPropagateOverlayChange);
+                            return true;
+                        } catch (OperationFailedException e) {
+                            return false;
+                        }
                     }
                 } finally {
                     Binder.restoreCallingIdentity(ident);
@@ -687,7 +755,13 @@
                 final long ident = Binder.clearCallingIdentity();
                 try {
                     synchronized (mLock) {
-                        return mImpl.setPriority(packageName, parentPackageName, realUserId);
+                        try {
+                            mImpl.setPriority(packageName, parentPackageName, realUserId)
+                                .ifPresent(mPropagateOverlayChange);
+                            return true;
+                        } catch (OperationFailedException e) {
+                            return false;
+                        }
                     }
                 } finally {
                     Binder.restoreCallingIdentity(ident);
@@ -711,7 +785,13 @@
                 final long ident = Binder.clearCallingIdentity();
                 try {
                     synchronized (mLock) {
-                        return mImpl.setHighestPriority(packageName, realUserId);
+                        try {
+                            mImpl.setHighestPriority(packageName, realUserId)
+                                .ifPresent(mPropagateOverlayChange);
+                            return true;
+                        } catch (OperationFailedException e) {
+                            return false;
+                        }
                     }
                 } finally {
                     Binder.restoreCallingIdentity(ident);
@@ -735,7 +815,13 @@
                 final long ident = Binder.clearCallingIdentity();
                 try {
                     synchronized (mLock) {
-                        return mImpl.setLowestPriority(packageName, realUserId);
+                        try {
+                            mImpl.setLowestPriority(packageName, realUserId)
+                                .ifPresent(mPropagateOverlayChange);
+                            return true;
+                        } catch (OperationFailedException e) {
+                            return false;
+                        }
                     }
                 } finally {
                     Binder.restoreCallingIdentity(ident);
@@ -784,6 +870,120 @@
         }
 
         @Override
+        public void commit(@NonNull final OverlayManagerTransaction transaction)
+                throws RemoteException {
+            try {
+                traceBegin(TRACE_TAG_RRO, "OMS#commit " + transaction);
+                try {
+                    executeAllRequests(transaction);
+                } catch (Exception e) {
+                    final long ident = Binder.clearCallingIdentity();
+                    try {
+                        restoreSettings();
+                    } finally {
+                        Binder.restoreCallingIdentity(ident);
+                    }
+                    Slog.d(TAG, "commit failed: " + e.getMessage(), e);
+                    throw new SecurityException("commit failed"
+                            + (DEBUG ? ": " + e.getMessage() : ""));
+                }
+            } finally {
+                traceEnd(TRACE_TAG_RRO);
+            }
+        }
+
+        private Optional<PackageAndUser> executeRequest(
+                @NonNull final OverlayManagerTransaction.Request request) throws Exception {
+            final int realUserId = handleIncomingUser(request.userId, request.typeToString());
+            enforceActor(request.packageName, request.typeToString(), realUserId);
+
+            final long ident = Binder.clearCallingIdentity();
+            try {
+                switch (request.type) {
+                    case TYPE_SET_ENABLED:
+                        Optional<PackageAndUser> opt1 =
+                                mImpl.setEnabled(request.packageName, true, request.userId);
+                        Optional<PackageAndUser> opt2 =
+                                mImpl.setHighestPriority(request.packageName, request.userId);
+                        // Both setEnabled and setHighestPriority affected the same
+                        // target package and user: if both return non-empty
+                        // Optionals, they are identical
+                        return opt1.isPresent() ? opt1 : opt2;
+                    case TYPE_SET_DISABLED:
+                        return mImpl.setEnabled(request.packageName, false, request.userId);
+                    default:
+                        throw new IllegalArgumentException("unsupported request: " + request);
+                }
+            } finally {
+                Binder.restoreCallingIdentity(ident);
+            }
+        }
+
+        private void executeAllRequests(@NonNull final OverlayManagerTransaction transaction)
+                throws Exception {
+            if (DEBUG) {
+                Slog.d(TAG, "commit " + transaction);
+            }
+            if (transaction == null) {
+                throw new IllegalArgumentException("null transaction");
+            }
+
+            // map: userId -> list<targetPackageName>
+            SparseArray<List<String>> affectedTargetsToUpdate = new SparseArray<>();
+
+            synchronized (mLock) {
+                // map: userId -> set<targetPackageName>
+                SparseArray<Set<String>> targetsToUpdate = new SparseArray<>();
+
+                // execute the requests (as calling user)
+                for (final OverlayManagerTransaction.Request request : transaction) {
+                    executeRequest(request).ifPresent(target -> {
+                        Set<String> userTargets = targetsToUpdate.get(target.userId);
+                        if (userTargets == null) {
+                            userTargets = new ArraySet<String>();
+                            targetsToUpdate.put(target.userId, userTargets);
+                        }
+                        userTargets.add(target.packageName);
+                    });
+                }
+
+                // past the point of no return: the entire transaction has been
+                // processed successfully, we can no longer fail: continue as
+                // system_server
+                final long ident = Binder.clearCallingIdentity();
+                try {
+                    persistSettings();
+
+                    // inform the package manager about the new paths
+                    for (int index = 0; index < targetsToUpdate.size(); index++) {
+                        final int userId = targetsToUpdate.keyAt(index);
+                        final List<String> affectedTargets =
+                                updatePackageManager(targetsToUpdate.valueAt(index), userId);
+                        affectedTargetsToUpdate.put(userId, affectedTargets);
+                    }
+                } finally {
+                    Binder.restoreCallingIdentity(ident);
+                }
+            } // synchronized (mLock)
+
+            FgThread.getHandler().post(() -> {
+                final long ident = Binder.clearCallingIdentity();
+                try {
+                    // schedule apps to refresh + broadcast the ACTION_OVERLAY_CHANGED intents
+                    for (int index = 0; index < affectedTargetsToUpdate.size(); index++) {
+                        final int userId = affectedTargetsToUpdate.keyAt(index);
+                        final List<String> packageNames = affectedTargetsToUpdate.valueAt(index);
+
+                        updateActivityManager(packageNames, userId);
+                        broadcastActionOverlayChanged(packageNames, userId);
+                    }
+                } finally {
+                    Binder.restoreCallingIdentity(ident);
+                }
+            });
+        }
+
+        @Override
         public void onShellCommand(@NonNull final FileDescriptor in,
                 @NonNull final FileDescriptor out, @NonNull final FileDescriptor err,
                 @NonNull final String[] args, @NonNull final ShellCallback callback,
@@ -904,162 +1104,7 @@
         }
     };
 
-    private final class OverlayChangeListener
-            implements OverlayManagerServiceImpl.OverlayChangeListener {
-        @Override
-        public void onOverlaysChanged(@NonNull final String targetPackageName, final int userId) {
-            schedulePersistSettings();
-            FgThread.getHandler().post(() -> {
-                updateAssets(userId, targetPackageName);
-
-                final Intent intent = new Intent(ACTION_OVERLAY_CHANGED,
-                        Uri.fromParts("package", targetPackageName, null));
-                intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
-
-                if (DEBUG) {
-                    Slog.d(TAG, "send broadcast " + intent);
-                }
-
-                try {
-                    ActivityManager.getService().broadcastIntentWithFeature(null, null, intent,
-                            null, null, 0, null, null, null, android.app.AppOpsManager.OP_NONE,
-                            null, false, false, userId);
-                } catch (RemoteException e) {
-                    // Intentionally left empty.
-                }
-            });
-        }
-    }
-
-    /**
-     * Updates the target packages' set of enabled overlays in PackageManager.
-     */
-    private ArrayList<String> updateOverlayPaths(int userId, List<String> targetPackageNames) {
-        try {
-            traceBegin(TRACE_TAG_RRO, "OMS#updateOverlayPaths " + targetPackageNames);
-            if (DEBUG) {
-                Slog.d(TAG, "Updating overlay assets");
-            }
-            final PackageManagerInternal pm =
-                    LocalServices.getService(PackageManagerInternal.class);
-            final boolean updateFrameworkRes = targetPackageNames.contains("android");
-            if (updateFrameworkRes) {
-                targetPackageNames = pm.getTargetPackageNames(userId);
-            }
-
-            final Map<String, List<String>> pendingChanges =
-                    new ArrayMap<>(targetPackageNames.size());
-            synchronized (mLock) {
-                final List<String> frameworkOverlays =
-                        mImpl.getEnabledOverlayPackageNames("android", userId);
-                final int n = targetPackageNames.size();
-                for (int i = 0; i < n; i++) {
-                    final String targetPackageName = targetPackageNames.get(i);
-                    List<String> list = new ArrayList<>();
-                    if (!"android".equals(targetPackageName)) {
-                        list.addAll(frameworkOverlays);
-                    }
-                    list.addAll(mImpl.getEnabledOverlayPackageNames(targetPackageName, userId));
-                    pendingChanges.put(targetPackageName, list);
-                }
-            }
-
-            final HashSet<String> updatedPackages = new HashSet<>();
-            final int n = targetPackageNames.size();
-            for (int i = 0; i < n; i++) {
-                final String targetPackageName = targetPackageNames.get(i);
-                if (DEBUG) {
-                    Slog.d(TAG, "-> Updating overlay: target=" + targetPackageName + " overlays=["
-                            + TextUtils.join(",", pendingChanges.get(targetPackageName))
-                            + "] userId=" + userId);
-                }
-
-                if (!pm.setEnabledOverlayPackages(
-                        userId, targetPackageName, pendingChanges.get(targetPackageName),
-                        updatedPackages)) {
-                    Slog.e(TAG, String.format("Failed to change enabled overlays for %s user %d",
-                            targetPackageName, userId));
-                }
-            }
-            return new ArrayList<>(updatedPackages);
-        } finally {
-            traceEnd(TRACE_TAG_RRO);
-        }
-    }
-
-    private void updateAssets(final int userId, final String targetPackageName) {
-        updateAssets(userId, Collections.singletonList(targetPackageName));
-    }
-
-    private void updateAssets(final int userId, List<String> targetPackageNames) {
-        final IActivityManager am = ActivityManager.getService();
-        try {
-            final ArrayList<String> updatedPaths = updateOverlayPaths(userId, targetPackageNames);
-            am.scheduleApplicationInfoChanged(updatedPaths, userId);
-        } catch (RemoteException e) {
-            // Intentionally left empty.
-        }
-    }
-
-    private void schedulePersistSettings() {
-        if (mPersistSettingsScheduled.getAndSet(true)) {
-            return;
-        }
-        IoThread.getHandler().post(() -> {
-            mPersistSettingsScheduled.set(false);
-            if (DEBUG) {
-                Slog.d(TAG, "Writing overlay settings");
-            }
-            synchronized (mLock) {
-                FileOutputStream stream = null;
-                try {
-                    stream = mSettingsFile.startWrite();
-                    mSettings.persist(stream);
-                    mSettingsFile.finishWrite(stream);
-                } catch (IOException | XmlPullParserException e) {
-                    mSettingsFile.failWrite(stream);
-                    Slog.e(TAG, "failed to persist overlay state", e);
-                }
-            }
-        });
-    }
-
-    private void restoreSettings() {
-        try {
-            traceBegin(TRACE_TAG_RRO, "OMS#restoreSettings");
-            synchronized (mLock) {
-                if (!mSettingsFile.getBaseFile().exists()) {
-                    return;
-                }
-                try (FileInputStream stream = mSettingsFile.openRead()) {
-                    mSettings.restore(stream);
-
-                    // We might have data for dying users if the device was
-                    // restarted before we received USER_REMOVED. Remove data for
-                    // users that will not exist after the system is ready.
-
-                    final List<UserInfo> liveUsers = mUserManager.getUsers(true /*excludeDying*/);
-                    final int[] liveUserIds = new int[liveUsers.size()];
-                    for (int i = 0; i < liveUsers.size(); i++) {
-                        liveUserIds[i] = liveUsers.get(i).getUserHandle().getIdentifier();
-                    }
-                    Arrays.sort(liveUserIds);
-
-                    for (int userId : mSettings.getUsers()) {
-                        if (Arrays.binarySearch(liveUserIds, userId) < 0) {
-                            mSettings.removeUser(userId);
-                        }
-                    }
-                } catch (IOException | XmlPullParserException e) {
-                    Slog.e(TAG, "failed to restore overlay state", e);
-                }
-            }
-        } finally {
-            traceEnd(TRACE_TAG_RRO);
-        }
-    }
-
-    private static final class PackageManagerHelperImpl implements PackageManagerHelper  {
+    private static final class PackageManagerHelperImpl implements PackageManagerHelper {
 
         private final Context mContext;
         private final IPackageManager mPackageManager;
@@ -1269,4 +1314,151 @@
             }
         }
     }
+
+    // Helper methods to update other parts of the system or read/write
+    // settings: these methods should never call into each other!
+
+    private void broadcastActionOverlayChanged(@NonNull final Collection<String> packageNames,
+            final int userId) {
+        for (final String packageName : packageNames) {
+            broadcastActionOverlayChanged(packageName, userId);
+        }
+    }
+
+    private void broadcastActionOverlayChanged(@NonNull final String targetPackageName,
+            final int userId) {
+        final Intent intent = new Intent(ACTION_OVERLAY_CHANGED,
+                Uri.fromParts("package", targetPackageName, null));
+        intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+        try {
+            ActivityManager.getService().broadcastIntent(null, intent, null, null, 0, null, null,
+                    null, android.app.AppOpsManager.OP_NONE, null, false, false, userId);
+        } catch (RemoteException e) {
+            // Intentionally left empty.
+        }
+    }
+
+    /**
+     * Tell the activity manager to tell a set of packages to reload their
+     * resources.
+     */
+    private void updateActivityManager(List<String> targetPackageNames, final int userId) {
+        final IActivityManager am = ActivityManager.getService();
+        try {
+            am.scheduleApplicationInfoChanged(targetPackageNames, userId);
+        } catch (RemoteException e) {
+            // Intentionally left empty.
+        }
+    }
+
+    private ArrayList<String> updatePackageManager(String targetPackageNames, final int userId) {
+        return updatePackageManager(Collections.singletonList(targetPackageNames), userId);
+    }
+
+    /**
+     * Updates the target packages' set of enabled overlays in PackageManager.
+     * @return the package names of affected targets (a superset of
+     *         targetPackageNames: the target themserlves and shared libraries)
+     */
+    private ArrayList<String> updatePackageManager(@NonNull Collection<String> targetPackageNames,
+            final int userId) {
+        try {
+            traceBegin(TRACE_TAG_RRO, "OMS#updatePackageManager " + targetPackageNames);
+            if (DEBUG) {
+                Slog.d(TAG, "Update package manager about changed overlays");
+            }
+            final PackageManagerInternal pm =
+                    LocalServices.getService(PackageManagerInternal.class);
+            final boolean updateFrameworkRes = targetPackageNames.contains("android");
+            if (updateFrameworkRes) {
+                targetPackageNames = pm.getTargetPackageNames(userId);
+            }
+
+            final Map<String, List<String>> pendingChanges =
+                    new ArrayMap<>(targetPackageNames.size());
+            synchronized (mLock) {
+                final List<String> frameworkOverlays =
+                        mImpl.getEnabledOverlayPackageNames("android", userId);
+                for (final String targetPackageName : targetPackageNames) {
+                    List<String> list = new ArrayList<>();
+                    if (!"android".equals(targetPackageName)) {
+                        list.addAll(frameworkOverlays);
+                    }
+                    list.addAll(mImpl.getEnabledOverlayPackageNames(targetPackageName, userId));
+                    pendingChanges.put(targetPackageName, list);
+                }
+            }
+
+            final HashSet<String> updatedPackages = new HashSet<>();
+            for (final String targetPackageName : targetPackageNames) {
+                if (DEBUG) {
+                    Slog.d(TAG, "-> Updating overlay: target=" + targetPackageName + " overlays=["
+                            + TextUtils.join(",", pendingChanges.get(targetPackageName))
+                            + "] userId=" + userId);
+                }
+
+                if (!pm.setEnabledOverlayPackages(
+                        userId, targetPackageName, pendingChanges.get(targetPackageName),
+                        updatedPackages)) {
+                    Slog.e(TAG, String.format("Failed to change enabled overlays for %s user %d",
+                            targetPackageName, userId));
+                }
+            }
+            return new ArrayList<>(updatedPackages);
+        } finally {
+            traceEnd(TRACE_TAG_RRO);
+        }
+    }
+
+    private void persistSettings() {
+        if (DEBUG) {
+            Slog.d(TAG, "Writing overlay settings");
+        }
+        synchronized (mLock) {
+            FileOutputStream stream = null;
+            try {
+                stream = mSettingsFile.startWrite();
+                mSettings.persist(stream);
+                mSettingsFile.finishWrite(stream);
+            } catch (IOException | XmlPullParserException e) {
+                mSettingsFile.failWrite(stream);
+                Slog.e(TAG, "failed to persist overlay state", e);
+            }
+        }
+    }
+
+    private void restoreSettings() {
+        try {
+            traceBegin(TRACE_TAG_RRO, "OMS#restoreSettings");
+            synchronized (mLock) {
+                if (!mSettingsFile.getBaseFile().exists()) {
+                    return;
+                }
+                try (FileInputStream stream = mSettingsFile.openRead()) {
+                    mSettings.restore(stream);
+
+                    // We might have data for dying users if the device was
+                    // restarted before we received USER_REMOVED. Remove data for
+                    // users that will not exist after the system is ready.
+
+                    final List<UserInfo> liveUsers = mUserManager.getUsers(true /*excludeDying*/);
+                    final int[] liveUserIds = new int[liveUsers.size()];
+                    for (int i = 0; i < liveUsers.size(); i++) {
+                        liveUserIds[i] = liveUsers.get(i).getUserHandle().getIdentifier();
+                    }
+                    Arrays.sort(liveUserIds);
+
+                    for (int userId : mSettings.getUsers()) {
+                        if (Arrays.binarySearch(liveUserIds, userId) < 0) {
+                            mSettings.removeUser(userId);
+                        }
+                    }
+                } catch (IOException | XmlPullParserException e) {
+                    Slog.e(TAG, "failed to restore overlay state", e);
+                }
+            }
+        } finally {
+            traceEnd(TRACE_TAG_RRO);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
index 05a4a38..e60411bb 100644
--- a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
+++ b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java
@@ -45,6 +45,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
+import java.util.Optional;
 import java.util.Set;
 
 /**
@@ -71,7 +72,6 @@
     private final OverlayManagerSettings mSettings;
     private final OverlayConfig mOverlayConfig;
     private final String[] mDefaultOverlays;
-    private final OverlayChangeListener mListener;
 
     /**
      * Helper method to merge the overlay manager's (as read from overlays.xml)
@@ -114,14 +114,12 @@
             @NonNull final IdmapManager idmapManager,
             @NonNull final OverlayManagerSettings settings,
             @NonNull final OverlayConfig overlayConfig,
-            @NonNull final String[] defaultOverlays,
-            @NonNull final OverlayChangeListener listener) {
+            @NonNull final String[] defaultOverlays) {
         mPackageManager = packageManager;
         mIdmapManager = idmapManager;
         mSettings = settings;
         mOverlayConfig = overlayConfig;
         mDefaultOverlays = defaultOverlays;
-        mListener = listener;
     }
 
     /**
@@ -259,52 +257,58 @@
         mSettings.removeUser(userId);
     }
 
-    void onTargetPackageAdded(@NonNull final String packageName, final int userId) {
+    Optional<PackageAndUser> onTargetPackageAdded(@NonNull final String packageName,
+            final int userId) throws OperationFailedException {
         if (DEBUG) {
             Slog.d(TAG, "onTargetPackageAdded packageName=" + packageName + " userId=" + userId);
         }
 
-        updateAndRefreshOverlaysForTarget(packageName, userId, 0);
+        return updateAndRefreshOverlaysForTarget(packageName, userId, 0);
     }
 
-    void onTargetPackageChanged(@NonNull final String packageName, final int userId) {
+    Optional<PackageAndUser> onTargetPackageChanged(@NonNull final String packageName,
+            final int userId) throws OperationFailedException {
         if (DEBUG) {
             Slog.d(TAG, "onTargetPackageChanged packageName=" + packageName + " userId=" + userId);
         }
 
-        updateAndRefreshOverlaysForTarget(packageName, userId, 0);
+        return updateAndRefreshOverlaysForTarget(packageName, userId, 0);
     }
 
-    void onTargetPackageReplacing(@NonNull final String packageName, final int userId) {
+    Optional<PackageAndUser> onTargetPackageReplacing(@NonNull final String packageName,
+            final int userId) throws OperationFailedException {
         if (DEBUG) {
             Slog.d(TAG, "onTargetPackageReplacing packageName=" + packageName + " userId="
                     + userId);
         }
 
-        updateAndRefreshOverlaysForTarget(packageName, userId, 0);
+        return updateAndRefreshOverlaysForTarget(packageName, userId, 0);
     }
 
-    void onTargetPackageReplaced(@NonNull final String packageName, final int userId) {
+    Optional<PackageAndUser> onTargetPackageReplaced(@NonNull final String packageName,
+            final int userId) throws OperationFailedException {
         if (DEBUG) {
             Slog.d(TAG, "onTargetPackageReplaced packageName=" + packageName + " userId=" + userId);
         }
 
-        updateAndRefreshOverlaysForTarget(packageName, userId, 0);
+        return updateAndRefreshOverlaysForTarget(packageName, userId, 0);
     }
 
-    void onTargetPackageRemoved(@NonNull final String packageName, final int userId) {
+    Optional<PackageAndUser> onTargetPackageRemoved(@NonNull final String packageName,
+            final int userId) throws OperationFailedException {
         if (DEBUG) {
             Slog.d(TAG, "onTargetPackageRemoved packageName=" + packageName + " userId=" + userId);
         }
 
-        updateAndRefreshOverlaysForTarget(packageName, userId, 0);
+        return updateAndRefreshOverlaysForTarget(packageName, userId, 0);
     }
 
     /**
      * Update the state of any overlays for this target.
      */
-    private void updateAndRefreshOverlaysForTarget(@NonNull final String targetPackageName,
-            final int userId, final int flags) {
+    private Optional<PackageAndUser> updateAndRefreshOverlaysForTarget(
+            @NonNull final String targetPackageName, final int userId, final int flags)
+            throws OperationFailedException {
         final List<OverlayInfo> targetOverlays = mSettings.getOverlaysForTarget(targetPackageName,
                 userId);
 
@@ -364,11 +368,13 @@
         }
 
         if (modified) {
-            mListener.onOverlaysChanged(targetPackageName, userId);
+            return Optional.of(new PackageAndUser(targetPackageName, userId));
         }
+        return Optional.empty();
     }
 
-    void onOverlayPackageAdded(@NonNull final String packageName, final int userId) {
+    Optional<PackageAndUser> onOverlayPackageAdded(@NonNull final String packageName,
+            final int userId) throws OperationFailedException {
         if (DEBUG) {
             Slog.d(TAG, "onOverlayPackageAdded packageName=" + packageName + " userId=" + userId);
         }
@@ -376,8 +382,7 @@
         final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId);
         if (overlayPackage == null) {
             Slog.w(TAG, "overlay package " + packageName + " was added, but couldn't be found");
-            onOverlayPackageRemoved(packageName, userId);
-            return;
+            return onOverlayPackageRemoved(packageName, userId);
         }
 
         mSettings.init(packageName, userId, overlayPackage.overlayTarget,
@@ -389,15 +394,17 @@
                 overlayPackage.overlayCategory);
         try {
             if (updateState(overlayPackage.overlayTarget, packageName, userId, 0)) {
-                mListener.onOverlaysChanged(overlayPackage.overlayTarget, userId);
+                return Optional.of(new PackageAndUser(overlayPackage.overlayTarget, userId));
             }
+            return Optional.empty();
         } catch (OverlayManagerSettings.BadKeyException e) {
-            Slog.e(TAG, "failed to update settings", e);
             mSettings.remove(packageName, userId);
+            throw new OperationFailedException("failed to update settings", e);
         }
     }
 
-    void onOverlayPackageChanged(@NonNull final String packageName, final int userId) {
+    Optional<PackageAndUser> onOverlayPackageChanged(@NonNull final String packageName,
+            final int userId) throws OperationFailedException {
         if (DEBUG) {
             Slog.d(TAG, "onOverlayPackageChanged packageName=" + packageName + " userId=" + userId);
         }
@@ -405,14 +412,16 @@
         try {
             final OverlayInfo oi = mSettings.getOverlayInfo(packageName, userId);
             if (updateState(oi.targetPackageName, packageName, userId, 0)) {
-                mListener.onOverlaysChanged(oi.targetPackageName, userId);
+                return Optional.of(new PackageAndUser(oi.targetPackageName, userId));
             }
+            return Optional.empty();
         } catch (OverlayManagerSettings.BadKeyException e) {
-            Slog.e(TAG, "failed to update settings", e);
+            throw new OperationFailedException("failed to update settings", e);
         }
     }
 
-    void onOverlayPackageReplacing(@NonNull final String packageName, final int userId) {
+    Optional<PackageAndUser> onOverlayPackageReplacing(@NonNull final String packageName,
+            final int userId) throws OperationFailedException {
         if (DEBUG) {
             Slog.d(TAG, "onOverlayPackageReplacing packageName=" + packageName + " userId="
                     + userId);
@@ -423,14 +432,16 @@
             if (updateState(oi.targetPackageName, packageName, userId,
                         FLAG_OVERLAY_IS_BEING_REPLACED)) {
                 removeIdmapIfPossible(oi);
-                mListener.onOverlaysChanged(oi.targetPackageName, userId);
+                return Optional.of(new PackageAndUser(oi.targetPackageName, userId));
             }
+            return Optional.empty();
         } catch (OverlayManagerSettings.BadKeyException e) {
-            Slog.e(TAG, "failed to update settings", e);
+            throw new OperationFailedException("failed to update settings", e);
         }
     }
 
-    void onOverlayPackageReplaced(@NonNull final String packageName, final int userId) {
+    Optional<PackageAndUser> onOverlayPackageReplaced(@NonNull final String packageName,
+            final int userId) throws OperationFailedException {
         if (DEBUG) {
             Slog.d(TAG, "onOverlayPackageReplaced packageName=" + packageName + " userId="
                     + userId);
@@ -439,16 +450,12 @@
         final PackageInfo pkg = mPackageManager.getPackageInfo(packageName, userId);
         if (pkg == null) {
             Slog.w(TAG, "overlay package " + packageName + " was replaced, but couldn't be found");
-            onOverlayPackageRemoved(packageName, userId);
-            return;
+            return onOverlayPackageRemoved(packageName, userId);
         }
 
         try {
             final OverlayInfo oldOi = mSettings.getOverlayInfo(packageName, userId);
             if (mustReinitializeOverlay(pkg, oldOi)) {
-                if (oldOi != null && !oldOi.targetPackageName.equals(pkg.overlayTarget)) {
-                    mListener.onOverlaysChanged(pkg.overlayTarget, userId);
-                }
                 mSettings.init(packageName, userId, pkg.overlayTarget, pkg.targetOverlayableName,
                         pkg.applicationInfo.getBaseCodePath(),
                         isPackageConfiguredMutable(pkg.packageName),
@@ -457,22 +464,25 @@
             }
 
             if (updateState(pkg.overlayTarget, packageName, userId, 0)) {
-                mListener.onOverlaysChanged(pkg.overlayTarget, userId);
+                return Optional.of(new PackageAndUser(pkg.overlayTarget, userId));
             }
+            return Optional.empty();
         } catch (OverlayManagerSettings.BadKeyException e) {
-            Slog.e(TAG, "failed to update settings", e);
+            throw new OperationFailedException("failed to update settings", e);
         }
     }
 
-    void onOverlayPackageRemoved(@NonNull final String packageName, final int userId) {
+    Optional<PackageAndUser> onOverlayPackageRemoved(@NonNull final String packageName,
+            final int userId) throws OperationFailedException {
         try {
             final OverlayInfo overlayInfo = mSettings.getOverlayInfo(packageName, userId);
             if (mSettings.remove(packageName, userId)) {
                 removeIdmapIfPossible(overlayInfo);
-                mListener.onOverlaysChanged(overlayInfo.targetPackageName, userId);
+                return Optional.of(new PackageAndUser(overlayInfo.targetPackageName, userId));
             }
+            return Optional.empty();
         } catch (OverlayManagerSettings.BadKeyException e) {
-            Slog.e(TAG, "failed to remove overlay", e);
+            throw new OperationFailedException("failed to remove overlay", e);
         }
     }
 
@@ -493,8 +503,8 @@
         return mSettings.getOverlaysForUser(userId);
     }
 
-    boolean setEnabled(@NonNull final String packageName, final boolean enable,
-            final int userId) {
+    Optional<PackageAndUser> setEnabled(@NonNull final String packageName, final boolean enable,
+            final int userId) throws OperationFailedException {
         if (DEBUG) {
             Slog.d(TAG, String.format("setEnabled packageName=%s enable=%s userId=%d",
                         packageName, enable, userId));
@@ -502,30 +512,33 @@
 
         final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId);
         if (overlayPackage == null) {
-            return false;
+            throw new OperationFailedException(
+                    String.format("failed to find overlay package %s for user %d",
+                        packageName, userId));
         }
 
         try {
             final OverlayInfo oi = mSettings.getOverlayInfo(packageName, userId);
             if (!oi.isMutable) {
                 // Ignore immutable overlays.
-                return false;
+                throw new OperationFailedException(
+                        "cannot enable immutable overlay packages in runtime");
             }
 
             boolean modified = mSettings.setEnabled(packageName, userId, enable);
             modified |= updateState(oi.targetPackageName, oi.packageName, userId, 0);
 
             if (modified) {
-                mListener.onOverlaysChanged(oi.targetPackageName, userId);
+                return Optional.of(new PackageAndUser(oi.targetPackageName, userId));
             }
-            return true;
+            return Optional.empty();
         } catch (OverlayManagerSettings.BadKeyException e) {
-            return false;
+            throw new OperationFailedException("failed to update settings", e);
         }
     }
 
-    boolean setEnabledExclusive(@NonNull final String packageName, boolean withinCategory,
-            final int userId) {
+    Optional<PackageAndUser> setEnabledExclusive(@NonNull final String packageName,
+            boolean withinCategory, final int userId) throws OperationFailedException {
         if (DEBUG) {
             Slog.d(TAG, String.format("setEnabledExclusive packageName=%s"
                     + " withinCategory=%s userId=%d", packageName, withinCategory, userId));
@@ -533,7 +546,8 @@
 
         final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId);
         if (overlayPackage == null) {
-            return false;
+            throw new OperationFailedException(String.format(
+                        "failed to find overlay package %s for user %d", packageName, userId));
         }
 
         try {
@@ -576,11 +590,11 @@
             modified |= updateState(targetPackageName, packageName, userId, 0);
 
             if (modified) {
-                mListener.onOverlaysChanged(targetPackageName, userId);
+                return Optional.of(new PackageAndUser(targetPackageName, userId));
             }
-            return true;
+            return Optional.empty();
         } catch (OverlayManagerSettings.BadKeyException e) {
-            return false;
+            throw new OperationFailedException("failed to update settings", e);
         }
     }
 
@@ -596,66 +610,75 @@
         return mOverlayConfig.isEnabled(packageName);
     }
 
-    boolean setPriority(@NonNull final String packageName,
-            @NonNull final String newParentPackageName, final int userId) {
+    Optional<PackageAndUser> setPriority(@NonNull final String packageName,
+            @NonNull final String newParentPackageName, final int userId)
+            throws OperationFailedException {
         if (DEBUG) {
             Slog.d(TAG, "setPriority packageName=" + packageName + " newParentPackageName="
                     + newParentPackageName + " userId=" + userId);
         }
 
         if (!isPackageConfiguredMutable(packageName)) {
-            return false;
+            throw new OperationFailedException(String.format(
+                        "overlay package %s user %d is not updatable", packageName, userId));
         }
 
         final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId);
         if (overlayPackage == null) {
-            return false;
+            throw new OperationFailedException(String.format(
+                        "failed to find overlay package %s for user %d", packageName, userId));
         }
 
         if (mSettings.setPriority(packageName, newParentPackageName, userId)) {
-            mListener.onOverlaysChanged(overlayPackage.overlayTarget, userId);
+            return Optional.of(new PackageAndUser(overlayPackage.overlayTarget, userId));
         }
-        return true;
+        return Optional.empty();
     }
 
-    boolean setHighestPriority(@NonNull final String packageName, final int userId) {
+    Optional<PackageAndUser> setHighestPriority(@NonNull final String packageName,
+            final int userId) throws OperationFailedException {
         if (DEBUG) {
             Slog.d(TAG, "setHighestPriority packageName=" + packageName + " userId=" + userId);
         }
 
         if (!isPackageConfiguredMutable(packageName)) {
-            return false;
+            throw new OperationFailedException(String.format(
+                        "overlay package %s user %d is not updatable", packageName, userId));
         }
 
         final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId);
         if (overlayPackage == null) {
-            return false;
+            throw new OperationFailedException(String.format(
+                        "failed to find overlay package %s for user %d", packageName, userId));
         }
 
         if (mSettings.setHighestPriority(packageName, userId)) {
-            mListener.onOverlaysChanged(overlayPackage.overlayTarget, userId);
+            return Optional.of(new PackageAndUser(overlayPackage.overlayTarget, userId));
         }
-        return true;
+        return Optional.empty();
     }
 
-    boolean setLowestPriority(@NonNull final String packageName, final int userId) {
+    Optional<PackageAndUser> setLowestPriority(@NonNull final String packageName, final int userId)
+            throws OperationFailedException {
         if (DEBUG) {
             Slog.d(TAG, "setLowestPriority packageName=" + packageName + " userId=" + userId);
         }
 
         if (!isPackageConfiguredMutable(packageName)) {
-            return false;
+            throw new OperationFailedException(String.format(
+                        "overlay package %s user %d is not updatable", packageName, userId));
         }
 
         final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId);
         if (overlayPackage == null) {
-            return false;
+            throw new OperationFailedException(String.format(
+                        "failed to find overlay package %s for user %d", packageName, userId));
         }
 
         if (mSettings.setLowestPriority(packageName, userId)) {
-            mListener.onOverlaysChanged(overlayPackage.overlayTarget, userId);
+            return Optional.of(new PackageAndUser(overlayPackage.overlayTarget, userId));
         }
-        return true;
+        return Optional.empty();
     }
 
     void dump(@NonNull final PrintWriter pw, @NonNull DumpState dumpState) {
@@ -797,12 +820,13 @@
         mIdmapManager.removeIdmap(oi, oi.userId);
     }
 
-    interface OverlayChangeListener {
+    static final class OperationFailedException extends Exception {
+        OperationFailedException(@NonNull final String message) {
+            super(message);
+        }
 
-        /**
-         * An event triggered by changes made to overlay state or settings as well as changes that
-         * add or remove target packages of overlays.
-         **/
-        void onOverlaysChanged(@NonNull String targetPackage, int userId);
+        OperationFailedException(@NonNull final String message, @NonNull Throwable cause) {
+            super(message, cause);
+        }
     }
 }
diff --git a/services/core/java/com/android/server/om/PackageAndUser.java b/services/core/java/com/android/server/om/PackageAndUser.java
new file mode 100644
index 0000000..5c38ba7
--- /dev/null
+++ b/services/core/java/com/android/server/om/PackageAndUser.java
@@ -0,0 +1,57 @@
+/*
+ * 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.om;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+
+final class PackageAndUser {
+    public final @NonNull String packageName;
+    public final @UserIdInt int userId;
+
+    PackageAndUser(@NonNull String packageName, @UserIdInt int userId) {
+        this.packageName = packageName;
+        this.userId = userId;
+    }
+
+    @Override
+    public boolean equals(@Nullable Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (!(obj instanceof PackageAndUser)) {
+            return false;
+        }
+        PackageAndUser other = (PackageAndUser) obj;
+        return packageName.equals(other.packageName) && userId == other.userId;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + packageName.hashCode();
+        result = prime * result + userId;
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        return String.format("PackageAndUser{packageName=%s, userId=%d}", packageName, userId);
+    }
+}
diff --git a/services/core/java/com/android/server/pm/AppsFilter.java b/services/core/java/com/android/server/pm/AppsFilter.java
index 094be06..f8990c0 100644
--- a/services/core/java/com/android/server/pm/AppsFilter.java
+++ b/services/core/java/com/android/server/pm/AppsFilter.java
@@ -167,6 +167,7 @@
      *
      * @param observer The {@link Watcher} to be notified when the {@link Watchable} changes.
      */
+    @Override
     public void registerObserver(@NonNull Watcher observer) {
         mWatchable.registerObserver(observer);
     }
@@ -177,17 +178,29 @@
      *
      * @param observer The {@link Watcher} that should not be in the notification list.
      */
+    @Override
     public void unregisterObserver(@NonNull Watcher observer) {
         mWatchable.unregisterObserver(observer);
     }
 
     /**
+     * Return true if the {@link Watcher) is a registered observer.
+     * @param observer A {@link Watcher} that might be registered
+     * @return true if the observer is registered with this {@link Watchable}.
+     */
+    @Override
+    public boolean isRegisteredObserver(@NonNull Watcher observer) {
+        return mWatchable.isRegisteredObserver(observer);
+    }
+
+    /**
      * Invokes {@link Watcher#onChange} on each registered observer.  The method can be called
      * with the {@link Watchable} that generated the event.  In a tree of {@link Watchable}s, this
      * is generally the first (deepest) {@link Watchable} to detect a change.
      *
      * @param what The {@link Watchable} that generated the event.
      */
+    @Override
     public void dispatchChange(@Nullable Watchable what) {
         mSnapshot = null;
         mWatchable.dispatchChange(what);
@@ -443,7 +456,7 @@
         }
         final StateProvider stateProvider = command -> {
             synchronized (injector.getLock()) {
-                command.currentState(injector.getSettings().getPackagesLocked().untrackedMap(),
+                command.currentState(injector.getSettings().getPackagesLocked().untrackedStorage(),
                         injector.getUserManagerInternal().getUserInfos());
             }
         };
@@ -979,7 +992,7 @@
     @Nullable
     SparseArray<int[]> getVisibilityAllowList(PackageSetting setting, int[] users,
             WatchedArrayMap<String, PackageSetting> existingSettings) {
-        return getVisibilityAllowList(setting, users, existingSettings.untrackedMap());
+        return getVisibilityAllowList(setting, users, existingSettings.untrackedStorage());
     }
 
     /**
diff --git a/services/core/java/com/android/server/pm/CrossProfileIntentResolver.java b/services/core/java/com/android/server/pm/CrossProfileIntentResolver.java
index bf7f466..aae6ce4 100644
--- a/services/core/java/com/android/server/pm/CrossProfileIntentResolver.java
+++ b/services/core/java/com/android/server/pm/CrossProfileIntentResolver.java
@@ -19,8 +19,8 @@
 import android.annotation.NonNull;
 import android.content.IntentFilter;
 
+import com.android.server.WatchableIntentResolver;
 import com.android.server.utils.Snappable;
-import com.android.server.utils.WatchableIntentResolver;
 
 import java.util.List;
 
@@ -57,7 +57,7 @@
      */
     public CrossProfileIntentResolver snapshot() {
         CrossProfileIntentResolver result = new CrossProfileIntentResolver();
-        result.doCopy(this);
+        result.copyFrom(this);
         return result;
     }
 }
diff --git a/services/core/java/com/android/server/pm/InstantAppRegistry.java b/services/core/java/com/android/server/pm/InstantAppRegistry.java
index 69d3e5c..c3bca28 100644
--- a/services/core/java/com/android/server/pm/InstantAppRegistry.java
+++ b/services/core/java/com/android/server/pm/InstantAppRegistry.java
@@ -154,6 +154,9 @@
     public void unregisterObserver(@NonNull Watcher observer) {
         mWatchable.unregisterObserver(observer);
     }
+    public boolean isRegisteredObserver(@NonNull Watcher observer) {
+        return mWatchable.isRegisteredObserver(observer);
+    }
     public void dispatchChange(@Nullable Watchable what) {
         mSnapshot = null;
         mWatchable.dispatchChange(what);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 22f4a92..cf9867c 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -295,7 +295,7 @@
 import android.os.storage.StorageManagerInternal;
 import android.os.storage.VolumeInfo;
 import android.os.storage.VolumeRecord;
-import android.permission.IPermissionManager;
+import android.permission.PermissionManager;
 import android.provider.ContactsContract;
 import android.provider.DeviceConfig;
 import android.provider.Settings.Global;
@@ -388,7 +388,6 @@
 import com.android.server.pm.permission.Permission;
 import com.android.server.pm.permission.PermissionManagerService;
 import com.android.server.pm.permission.PermissionManagerServiceInternal;
-import com.android.server.policy.PermissionPolicyInternal;
 import com.android.server.rollback.RollbackManagerInternal;
 import com.android.server.security.VerityUtils;
 import com.android.server.storage.DeviceStorageMonitorInternal;
@@ -962,7 +961,6 @@
         private final Singleton<ArtManagerService> mArtManagerServiceProducer;
         private final Singleton<ApexManager> mApexManagerProducer;
         private final Singleton<ViewCompiler> mViewCompilerProducer;
-        private final Singleton<IPermissionManager> mPermissionManagerProducer;
         private final Singleton<IncrementalManager> mIncrementalManagerProducer;
         private final Singleton<DefaultAppProvider> mDefaultAppProviderProducer;
         private final Singleton<DisplayMetrics> mDisplayMetricsProducer;
@@ -994,7 +992,6 @@
                 Producer<DexManager> dexManagerProducer,
                 Producer<ArtManagerService> artManagerServiceProducer,
                 Producer<ApexManager> apexManagerProducer,
-                Producer<IPermissionManager> permissionManagerProducer,
                 Producer<ViewCompiler> viewCompilerProducer,
                 Producer<IncrementalManager> incrementalManagerProducer,
                 Producer<DefaultAppProvider> defaultAppProviderProducer,
@@ -1029,7 +1026,6 @@
             mDexManagerProducer = new Singleton<>(dexManagerProducer);
             mArtManagerServiceProducer = new Singleton<>(artManagerServiceProducer);
             mApexManagerProducer = new Singleton<>(apexManagerProducer);
-            mPermissionManagerProducer = new Singleton<>(permissionManagerProducer);
             mViewCompilerProducer = new Singleton<>(viewCompilerProducer);
             mIncrementalManagerProducer = new Singleton<>(incrementalManagerProducer);
             mDefaultAppProviderProducer = new Singleton<>(defaultAppProviderProducer);
@@ -1131,10 +1127,6 @@
             return mViewCompilerProducer.get(this, mPackageManager);
         }
 
-        public IPermissionManager getPermissionManagerService() {
-            return mPermissionManagerProducer.get(this, mPackageManager);
-        }
-
         public Handler getBackgroundHandler() {
             return mBackgroundHandler;
         }
@@ -1260,7 +1252,6 @@
         public OverlayConfig overlayConfig;
         public PackageDexOptimizer packageDexOptimizer;
         public PackageParser2.Callback packageParserCallback;
-        public IPermissionManager permissionManagerService;
         public PendingPackageBroadcasts pendingPackageBroadcasts;
         public PackageManagerInternal pmInternal;
         public TestUtilityService testUtilityService;
@@ -1381,8 +1372,6 @@
 
     // Internal interface for permission manager
     private final PermissionManagerServiceInternal mPermissionManager;
-    // Public interface for permission manager
-    private final IPermissionManager mPermissionManagerService;
 
     private final ComponentResolver mComponentResolver;
     // List of packages names to keep cached, even if they are uninstalled for all users
@@ -2857,7 +2846,6 @@
                 (i, pm) -> new ArtManagerService(i.getContext(), pm, i.getInstaller(),
                         i.getInstallLock()),
                 (i, pm) -> ApexManager.getInstance(),
-                (i, pm) -> (IPermissionManager) ServiceManager.getService("permissionmgr"),
                 (i, pm) -> new ViewCompiler(i.getInstallLock(), i.getInstaller()),
                 (i, pm) -> (IncrementalManager)
                         i.getContext().getSystemService(Context.INCREMENTAL_SERVICE),
@@ -3080,7 +3068,6 @@
         mPackageDexOptimizer = testParams.packageDexOptimizer;
         mPackageParserCallback = testParams.packageParserCallback;
         mPendingBroadcasts = testParams.pendingPackageBroadcasts;
-        mPermissionManagerService = testParams.permissionManagerService;
         mPmInternal = testParams.pmInternal;
         mTestUtilityService = testParams.testUtilityService;
         mProcessLoggingHandler = testParams.processLoggingHandler;
@@ -3158,7 +3145,6 @@
         mComponentResolver = injector.getComponentResolver();
         mPermissionManager = injector.getPermissionManagerServiceInternal();
         mSettings = injector.getSettings();
-        mPermissionManagerService = injector.getPermissionManagerService();
         mIncrementalManager = mInjector.getIncrementalManager();
         mDefaultAppProvider = mInjector.getDefaultAppProvider();
         mLegacyPermissionManager = mInjector.getLegacyPermissionManagerInternal();
@@ -3734,7 +3720,7 @@
                 Slog.i(TAG, "Platform changed from " + ver.sdkVersion + " to "
                         + mSdkVersion + "; regranting permissions for internal storage");
             }
-            mPermissionManager.updateAllPermissions(
+            mPermissionManager.onStorageVolumeMounted(
                     StorageManager.UUID_PRIVATE_INTERNAL, sdkUpdated);
             ver.sdkVersion = mSdkVersion;
 
@@ -5176,12 +5162,10 @@
     // NOTE: Can't remove due to unsupported app usage
     @Override
     public PermissionGroupInfo getPermissionGroupInfo(String groupName, int flags) {
-        try {
-            // Because this is accessed via the package manager service AIDL,
-            // go through the permission manager service AIDL
-            return mPermissionManagerService.getPermissionGroupInfo(groupName, flags);
-        } catch (RemoteException ignore) { }
-        return null;
+        // Because this is accessed via the package manager service AIDL,
+        // go through the permission manager service AIDL
+        return mContext.getSystemService(PermissionManager.class)
+                .getPermissionGroupInfo(groupName, flags);
     }
 
     @GuardedBy("mLock")
@@ -6213,23 +6197,13 @@
     // NOTE: Can't remove due to unsupported app usage
     @Override
     public int checkPermission(String permName, String pkgName, int userId) {
-        try {
-            // Because this is accessed via the package manager service AIDL,
-            // go through the permission manager service AIDL
-            return mPermissionManagerService.checkPermission(permName, pkgName, userId);
-        } catch (RemoteException ignore) { }
-        return PackageManager.PERMISSION_DENIED;
+        return mPermissionManager.checkPermission(pkgName, permName, userId);
     }
 
     // NOTE: Can't remove without a major refactor. Keep around for now.
     @Override
     public int checkUidPermission(String permName, int uid) {
-        try {
-            // Because this is accessed via the package manager service AIDL,
-            // go through the permission manager service AIDL
-            return mPermissionManagerService.checkUidPermission(permName, uid);
-        } catch (RemoteException ignore) { }
-        return PackageManager.PERMISSION_DENIED;
+        return mPermissionManager.checkUidPermission(uid, permName);
     }
 
     @Override
@@ -6248,43 +6222,34 @@
     // NOTE: Can't remove due to unsupported app usage
     @Override
     public boolean addPermission(PermissionInfo info) {
-        try {
-            // Because this is accessed via the package manager service AIDL,
-            // go through the permission manager service AIDL
-            return mPermissionManagerService.addPermission(info, false);
-        } catch (RemoteException ignore) { }
-        return false;
+        // Because this is accessed via the package manager service AIDL,
+        // go through the permission manager service AIDL
+        return mContext.getSystemService(PermissionManager.class).addPermission(info, false);
     }
 
     // NOTE: Can't remove due to unsupported app usage
     @Override
     public boolean addPermissionAsync(PermissionInfo info) {
-        try {
-            // Because this is accessed via the package manager service AIDL,
-            // go through the permission manager service AIDL
-            return mPermissionManagerService.addPermission(info, true);
-        } catch (RemoteException ignore) { }
-        return false;
+        // Because this is accessed via the package manager service AIDL,
+        // go through the permission manager service AIDL
+        return mContext.getSystemService(PermissionManager.class).addPermission(info, true);
     }
 
     // NOTE: Can't remove due to unsupported app usage
     @Override
     public void removePermission(String permName) {
-        try {
-            // Because this is accessed via the package manager service AIDL,
-            // go through the permission manager service AIDL
-            mPermissionManagerService.removePermission(permName);
-        } catch (RemoteException ignore) { }
+        // Because this is accessed via the package manager service AIDL,
+        // go through the permission manager service AIDL
+        mContext.getSystemService(PermissionManager.class).removePermission(permName);
     }
 
     // NOTE: Can't remove due to unsupported app usage
     @Override
     public void grantRuntimePermission(String packageName, String permName, final int userId) {
-        try {
-            // Because this is accessed via the package manager service AIDL,
-            // go through the permission manager service AIDL
-            mPermissionManagerService.grantRuntimePermission(packageName, permName, userId);
-        } catch (RemoteException ignore) { }
+        // Because this is accessed via the package manager service AIDL,
+        // go through the permission manager service AIDL
+        mContext.getSystemService(PermissionManager.class)
+                .grantRuntimePermission(packageName, permName, UserHandle.of(userId));
     }
 
     @Override
@@ -22388,23 +22353,6 @@
 
         mUserManager.systemReady();
 
-        // Now that we've scanned all packages, and granted any default
-        // permissions, ensure permissions are updated. Beware of dragons if you
-        // try optimizing this.
-        synchronized (mLock) {
-            mPermissionManager.updateAllPermissions(StorageManager.UUID_PRIVATE_INTERNAL, false);
-
-            final PermissionPolicyInternal permissionPolicyInternal =
-                    mInjector.getLocalService(PermissionPolicyInternal.class);
-            permissionPolicyInternal.setOnInitializedCallback(userId -> {
-                // The SDK updated case is already handled when we run during the ctor.
-                synchronized (mLock) {
-                    mPermissionManager.updateAllPermissions(
-                            StorageManager.UUID_PRIVATE_INTERNAL, false);
-                }
-            });
-        }
-
         // Watch for external volumes that come and go over time
         final StorageManager storage = mInjector.getSystemService(StorageManager.class);
         storage.registerListener(mStorageListener);
@@ -22521,7 +22469,7 @@
     public void onShellCommand(FileDescriptor in, FileDescriptor out,
             FileDescriptor err, String[] args, ShellCallback callback,
             ResultReceiver resultReceiver) {
-        (new PackageManagerShellCommand(this, mPermissionManagerService, mContext)).exec(
+        (new PackageManagerShellCommand(this, mContext)).exec(
                 this, in, out, err, args, callback, resultReceiver);
     }
 
@@ -23460,7 +23408,7 @@
                 logCriticalInfo(Log.INFO, "Platform changed from " + ver.sdkVersion + " to "
                         + mSdkVersion + "; regranting permissions for " + volumeUuid);
             }
-            mPermissionManager.updateAllPermissions(volumeUuid, sdkUpdated);
+            mPermissionManager.onStorageVolumeMounted(volumeUuid, sdkUpdated);
 
             // Yay, everything is now upgraded
             ver.forceCurrent();
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 2f6756d..9eae117 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -89,7 +89,7 @@
 import android.os.UserManager;
 import android.os.incremental.V4Signature;
 import android.os.storage.StorageManager;
-import android.permission.IPermissionManager;
+import android.permission.PermissionManager;
 import android.system.ErrnoException;
 import android.system.Os;
 import android.text.TextUtils;
@@ -108,6 +108,7 @@
 import com.android.server.LocalServices;
 import com.android.server.SystemConfig;
 import com.android.server.pm.PackageManagerShellCommandDataLoader.Metadata;
+import com.android.server.pm.permission.LegacyPermissionManagerInternal;
 
 import dalvik.system.DexFile;
 
@@ -144,7 +145,8 @@
     private static final String TAG = "PackageManagerShellCommand";
 
     final IPackageManager mInterface;
-    final IPermissionManager mPermissionManager;
+    final LegacyPermissionManagerInternal mLegacyPermissionManager;
+    final PermissionManager mPermissionManager;
     final Context mContext;
     final private WeakHashMap<String, Resources> mResourceCache =
             new WeakHashMap<String, Resources>();
@@ -153,10 +155,10 @@
     boolean mComponents;
     int mQueryFlags;
 
-    PackageManagerShellCommand(
-            PackageManagerService service, IPermissionManager permissionManager, Context context) {
+    PackageManagerShellCommand(PackageManagerService service, Context context) {
         mInterface = service;
-        mPermissionManager = permissionManager;
+        mLegacyPermissionManager = LocalServices.getService(LegacyPermissionManagerInternal.class);
+        mPermissionManager = context.getSystemService(PermissionManager.class);
         mContext = context;
     }
 
@@ -887,8 +889,7 @@
 
     private int runListPermissionGroups() throws RemoteException {
         final PrintWriter pw = getOutPrintWriter();
-        final List<PermissionGroupInfo> pgs =
-                mPermissionManager.getAllPermissionGroups(0).getList();
+        final List<PermissionGroupInfo> pgs = mPermissionManager.getAllPermissionGroups(0);
 
         final int count = pgs.size();
         for (int p = 0; p < count ; p++) {
@@ -935,7 +936,7 @@
         final ArrayList<String> groupList = new ArrayList<String>();
         if (groups) {
             final List<PermissionGroupInfo> infos =
-                    mPermissionManager.getAllPermissionGroups(0 /*flags*/).getList();
+                    mPermissionManager.getAllPermissionGroups(0 /*flags*/);
             final int count = infos.size();
             for (int i = 0; i < count; i++) {
                 groupList.add(infos.get(i).name);
@@ -2297,18 +2298,18 @@
             getErrPrintWriter().println("Error: no permission specified");
             return 1;
         }
-        final int translatedUserId =
-                translateUserId(userId, UserHandle.USER_NULL, "runGrantRevokePermission");
+        final UserHandle translatedUser = UserHandle.of(translateUserId(userId,
+                UserHandle.USER_NULL, "runGrantRevokePermission"));
         if (grant) {
-            mPermissionManager.grantRuntimePermission(pkg, perm, translatedUserId);
+            mPermissionManager.grantRuntimePermission(pkg, perm, translatedUser);
         } else {
-            mPermissionManager.revokeRuntimePermission(pkg, perm, translatedUserId, null);
+            mPermissionManager.revokeRuntimePermission(pkg, perm, translatedUser, null);
         }
         return 0;
     }
 
     private int runResetPermissions() throws RemoteException {
-        mPermissionManager.resetRuntimePermissions();
+        mLegacyPermissionManager.resetRuntimePermissions();
         return 0;
     }
 
@@ -3483,7 +3484,7 @@
                 prefix = "  ";
             }
             List<PermissionInfo> ps = mPermissionManager
-                    .queryPermissionsByGroup(groupList.get(i), 0 /*flags*/).getList();
+                    .queryPermissionsByGroup(groupList.get(i), 0 /*flags*/);
             final int count = ps.size();
             boolean first = true;
             for (int p = 0 ; p < count ; p++) {
diff --git a/services/core/java/com/android/server/pm/PersistentPreferredIntentResolver.java b/services/core/java/com/android/server/pm/PersistentPreferredIntentResolver.java
index d0f9787..c1bfcac 100644
--- a/services/core/java/com/android/server/pm/PersistentPreferredIntentResolver.java
+++ b/services/core/java/com/android/server/pm/PersistentPreferredIntentResolver.java
@@ -19,8 +19,8 @@
 import android.annotation.NonNull;
 import android.content.IntentFilter;
 
+import com.android.server.WatchableIntentResolver;
 import com.android.server.utils.Snappable;
-import com.android.server.utils.WatchableIntentResolver;
 
 public class PersistentPreferredIntentResolver
         extends WatchableIntentResolver<PersistentPreferredActivity, PersistentPreferredActivity>
@@ -47,7 +47,7 @@
      */
     public PersistentPreferredIntentResolver snapshot() {
         PersistentPreferredIntentResolver result = new PersistentPreferredIntentResolver();
-        result.doCopy(this);
+        result.copyFrom(this);
         return result;
     }
 }
diff --git a/services/core/java/com/android/server/pm/PreferredIntentResolver.java b/services/core/java/com/android/server/pm/PreferredIntentResolver.java
index b62421e..0e3b85c 100644
--- a/services/core/java/com/android/server/pm/PreferredIntentResolver.java
+++ b/services/core/java/com/android/server/pm/PreferredIntentResolver.java
@@ -19,8 +19,8 @@
 import android.annotation.NonNull;
 import android.content.IntentFilter;
 
+import com.android.server.WatchableIntentResolver;
 import com.android.server.utils.Snappable;
-import com.android.server.utils.WatchableIntentResolver;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -76,7 +76,7 @@
      */
     public PreferredIntentResolver snapshot() {
         PreferredIntentResolver result = new PreferredIntentResolver();
-        result.doCopy(this);
+        result.copyFrom(this);
         return result;
     }
 }
diff --git a/services/core/java/com/android/server/pm/SettingBase.java b/services/core/java/com/android/server/pm/SettingBase.java
index 7924d5d..0e8a278 100644
--- a/services/core/java/com/android/server/pm/SettingBase.java
+++ b/services/core/java/com/android/server/pm/SettingBase.java
@@ -49,6 +49,7 @@
      *
      * @param observer The {@link Watcher} to be notified when the {@link Watchable} changes.
      */
+    @Override
     public void registerObserver(@NonNull Watcher observer) {
         mWatchable.registerObserver(observer);
     }
@@ -59,20 +60,33 @@
      *
      * @param observer The {@link Watcher} that should not be in the notification list.
      */
+    @Override
     public void unregisterObserver(@NonNull Watcher observer) {
         mWatchable.unregisterObserver(observer);
     }
 
     /**
+     * Return true if the {@link Watcher) is a registered observer.
+     * @param observer A {@link Watcher} that might be registered
+     * @return true if the observer is registered with this {@link Watchable}.
+     */
+    @Override
+    public boolean isRegisteredObserver(@NonNull Watcher observer) {
+        return mWatchable.isRegisteredObserver(observer);
+    }
+
+    /**
      * Invokes {@link Watcher#onChange} on each registered observer.  The method can be called
      * with the {@link Watchable} that generated the event.  In a tree of {@link Watchable}s, this
      * is generally the first (deepest) {@link Watchable} to detect a change.
      *
      * @param what The {@link Watchable} that generated the event.
      */
+    @Override
     public void dispatchChange(@Nullable Watchable what) {
         mWatchable.dispatchChange(what);
     }
+
     /**
      * Notify listeners that this object has changed.
      */
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 7c4dade..50c1065 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -115,12 +115,15 @@
 import com.android.server.pm.permission.LegacyPermissionState;
 import com.android.server.pm.permission.LegacyPermissionState.PermissionState;
 import com.android.server.utils.Snappable;
-import com.android.server.utils.Snapshots;
 import com.android.server.utils.TimingsTraceAndSlog;
 import com.android.server.utils.Watchable;
 import com.android.server.utils.WatchableImpl;
+import com.android.server.utils.Watched;
+import com.android.server.utils.WatchedArrayList;
 import com.android.server.utils.WatchedArrayMap;
+import com.android.server.utils.WatchedArraySet;
 import com.android.server.utils.WatchedSparseArray;
+import com.android.server.utils.WatchedSparseIntArray;
 import com.android.server.utils.Watcher;
 
 import libcore.io.IoUtils;
@@ -189,6 +192,16 @@
     }
 
     /**
+     * Return true if the {@link Watcher) is a registered observer.
+     * @param observer A {@link Watcher} that might be registered
+     * @return true if the observer is registered with this {@link Watchable}.
+     */
+    @Override
+    public boolean isRegisteredObserver(@NonNull Watcher observer) {
+        return mWatchable.isRegisteredObserver(observer);
+    }
+
+    /**
      * Invokes {@link Watcher#onChange} on each registered observer.  The method can be called
      * with the {@link Watchable} that generated the event.  In a tree of {@link Watchable}s, this
      * is generally the first (deepest) {@link Watchable} to detect a change.
@@ -350,6 +363,7 @@
     private final File mKernelMappingFilename;
 
     /** Map from package name to settings */
+    @Watched
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
     final WatchedArrayMap<String, PackageSetting> mPackages = new WatchedArrayMap<>();
 
@@ -357,21 +371,29 @@
      * List of packages that were involved in installing other packages, i.e. are listed
      * in at least one app's InstallSource.
      */
-    private final ArraySet<String> mInstallerPackages = new ArraySet<>();
+    @Watched
+    private final WatchedArraySet<String> mInstallerPackages = new WatchedArraySet<>();
 
     /** Map from package name to appId and excluded userids */
-    private final ArrayMap<String, KernelPackageState> mKernelMapping = new ArrayMap<>();
+    @Watched
+    private final WatchedArrayMap<String, KernelPackageState> mKernelMapping =
+            new WatchedArrayMap<>();
 
     // List of replaced system applications
+    @Watched
     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
-    final ArrayMap<String, PackageSetting> mDisabledSysPackages = new ArrayMap<>();
+    final WatchedArrayMap<String, PackageSetting> mDisabledSysPackages = new WatchedArrayMap<>();
 
     /** List of packages that are blocked for uninstall for specific users */
-    private final SparseArray<ArraySet<String>> mBlockUninstallPackages = new SparseArray<>();
+    @Watched
+    private final WatchedSparseArray<ArraySet<String>> mBlockUninstallPackages =
+            new WatchedSparseArray<>();
 
     // Set of restored intent-filter verification states
-    private final ArrayMap<String, IntentFilterVerificationInfo> mRestoredIntentFilterVerifications =
-            new ArrayMap<String, IntentFilterVerificationInfo>();
+    @Watched
+    private final WatchedArrayMap<String, IntentFilterVerificationInfo>
+            mRestoredIntentFilterVerifications =
+            new WatchedArrayMap<String, IntentFilterVerificationInfo>();
 
     private static final class KernelPackageState {
         int appId;
@@ -381,7 +403,8 @@
     private static int mFirstAvailableUid = 0;
 
     /** Map from volume UUID to {@link VersionInfo} */
-    private ArrayMap<String, VersionInfo> mVersion = new ArrayMap<>();
+    @Watched
+    private WatchedArrayMap<String, VersionInfo> mVersion = new WatchedArrayMap<>();
 
     /**
      * Version details for a storage volume that may hold apps.
@@ -423,21 +446,27 @@
 
     // The user's preferred activities associated with particular intent
     // filters.
+    @Watched
     private final WatchedSparseArray<PreferredIntentResolver>
             mPreferredActivities = new WatchedSparseArray<>();
 
     // The persistent preferred activities of the user's profile/device owner
     // associated with particular intent filters.
+    @Watched
     private final WatchedSparseArray<PersistentPreferredIntentResolver>
             mPersistentPreferredActivities = new WatchedSparseArray<>();
 
     // For every user, it is used to find to which other users the intent can be forwarded.
+    @Watched
     private final WatchedSparseArray<CrossProfileIntentResolver>
             mCrossProfileIntentResolvers = new WatchedSparseArray<>();
 
-    final ArrayMap<String, SharedUserSetting> mSharedUsers = new ArrayMap<>();
-    private final ArrayList<SettingBase> mAppIds;
-    private final SparseArray<SettingBase> mOtherAppIds;
+    @Watched
+    final WatchedArrayMap<String, SharedUserSetting> mSharedUsers = new WatchedArrayMap<>();
+    @Watched
+    private final WatchedArrayList<SettingBase> mAppIds;
+    @Watched
+    private final WatchedSparseArray<SettingBase> mOtherAppIds;
 
     // For reading/writing settings file.
     private final ArrayList<Signature> mPastSignatures =
@@ -449,13 +478,17 @@
     // Keys are the new names of the packages, values are the original
     // names.  The packages appear everywhere else under their original
     // names.
-    private final ArrayMap<String, String> mRenamedPackages = new ArrayMap<String, String>();
+    @Watched
+    private final WatchedArrayMap<String, String> mRenamedPackages =
+            new WatchedArrayMap<String, String>();
 
     // For every user, it is used to find the package name of the default Browser App.
-    final SparseArray<String> mDefaultBrowserApp = new SparseArray<String>();
+    @Watched
+    final WatchedSparseArray<String> mDefaultBrowserApp = new WatchedSparseArray<String>();
 
     // App-link priority tracking, per-user
-    final SparseIntArray mNextAppLinkGeneration = new SparseIntArray();
+    @Watched
+    final WatchedSparseIntArray mNextAppLinkGeneration = new WatchedSparseIntArray();
 
     final StringBuilder mReadMessages = new StringBuilder();
 
@@ -471,7 +504,7 @@
     private final File mSystemDir;
 
     public final KeySetManagerService mKeySetManagerService =
-            new KeySetManagerService(mPackages.untrackedMap());
+            new KeySetManagerService(mPackages.untrackedStorage());
 
     /** Settings and other information about permissions */
     final LegacyPermissionSettings mPermissions;
@@ -492,8 +525,8 @@
     public Settings(Map<String, PackageSetting> pkgSettings) {
         mLock = new Object();
         mPackages.putAll(pkgSettings);
-        mAppIds = new ArrayList<>();
-        mOtherAppIds = new SparseArray<>();
+        mAppIds = new WatchedArrayList<>();
+        mOtherAppIds = new WatchedSparseArray<>();
         mSystemDir = null;
         mPermissions = null;
         mRuntimePermissionsPersistence = null;
@@ -504,17 +537,32 @@
         mStoppedPackagesFilename = null;
         mBackupStoppedPackagesFilename = null;
         mKernelMappingFilename = null;
+
         mPackages.registerObserver(mObserver);
+        mInstallerPackages.registerObserver(mObserver);
+        mKernelMapping.registerObserver(mObserver);
+        mDisabledSysPackages.registerObserver(mObserver);
+        mBlockUninstallPackages.registerObserver(mObserver);
+        mRestoredIntentFilterVerifications.registerObserver(mObserver);
+        mVersion.registerObserver(mObserver);
         mPreferredActivities.registerObserver(mObserver);
         mPersistentPreferredActivities.registerObserver(mObserver);
         mCrossProfileIntentResolvers.registerObserver(mObserver);
+        mSharedUsers.registerObserver(mObserver);
+        mAppIds.registerObserver(mObserver);
+        mOtherAppIds.registerObserver(mObserver);
+        mRenamedPackages.registerObserver(mObserver);
+        mDefaultBrowserApp.registerObserver(mObserver);
+        mNextAppLinkGeneration.registerObserver(mObserver);
+
+        Watchable.verifyWatchedAttributes(this, mObserver);
     }
 
     Settings(File dataDir, RuntimePermissionsPersistence runtimePermissionsPersistence,
             LegacyPermissionDataProvider permissionDataProvider, Object lock) {
         mLock = lock;
-        mAppIds = new ArrayList<>();
-        mOtherAppIds = new SparseArray<>();
+        mAppIds = new WatchedArrayList<>();
+        mOtherAppIds = new WatchedSparseArray<>();
         mPermissions = new LegacyPermissionSettings(lock);
         mRuntimePermissionsPersistence = new RuntimePermissionPersistence(
                 runtimePermissionsPersistence);
@@ -537,10 +585,25 @@
         // Deprecated: Needed for migration
         mStoppedPackagesFilename = new File(mSystemDir, "packages-stopped.xml");
         mBackupStoppedPackagesFilename = new File(mSystemDir, "packages-stopped-backup.xml");
+
         mPackages.registerObserver(mObserver);
+        mInstallerPackages.registerObserver(mObserver);
+        mKernelMapping.registerObserver(mObserver);
+        mDisabledSysPackages.registerObserver(mObserver);
+        mBlockUninstallPackages.registerObserver(mObserver);
+        mRestoredIntentFilterVerifications.registerObserver(mObserver);
+        mVersion.registerObserver(mObserver);
         mPreferredActivities.registerObserver(mObserver);
         mPersistentPreferredActivities.registerObserver(mObserver);
         mCrossProfileIntentResolvers.registerObserver(mObserver);
+        mSharedUsers.registerObserver(mObserver);
+        mAppIds.registerObserver(mObserver);
+        mOtherAppIds.registerObserver(mObserver);
+        mRenamedPackages.registerObserver(mObserver);
+        mDefaultBrowserApp.registerObserver(mObserver);
+        mNextAppLinkGeneration.registerObserver(mObserver);
+
+        Watchable.verifyWatchedAttributes(this, mObserver);
     }
 
     /**
@@ -568,7 +631,7 @@
         mInstallerPackages.addAll(r.mInstallerPackages);
         mKernelMapping.putAll(r.mKernelMapping);
         mDisabledSysPackages.putAll(r.mDisabledSysPackages);
-        Snapshots.copy(mBlockUninstallPackages, r.mBlockUninstallPackages);
+        mBlockUninstallPackages.snapshot(r.mBlockUninstallPackages);
         mRestoredIntentFilterVerifications.putAll(r.mRestoredIntentFilterVerifications);
         mVersion.putAll(r.mVersion);
         mVerifierDeviceIdentity = r.mVerifierDeviceIdentity;
@@ -579,13 +642,13 @@
         WatchedSparseArray.snapshot(
                 mCrossProfileIntentResolvers, r.mCrossProfileIntentResolvers);
         mSharedUsers.putAll(r.mSharedUsers);
-        mAppIds = new ArrayList<>(r.mAppIds);
-        mOtherAppIds = r.mOtherAppIds.clone();
+        mAppIds = r.mAppIds.snapshot();
+        mOtherAppIds = r.mOtherAppIds.snapshot();
         mPastSignatures.addAll(r.mPastSignatures);
         mKeySetRefs.putAll(r.mKeySetRefs);
-        mRenamedPackages.putAll(r.mRenamedPackages);
-        Snapshots.copy(mDefaultBrowserApp, r.mDefaultBrowserApp);
-        Snapshots.snapshot(mNextAppLinkGeneration, r.mNextAppLinkGeneration);
+        mRenamedPackages.snapshot(r.mRenamedPackages);
+        mDefaultBrowserApp.snapshot(r.mDefaultBrowserApp);
+        mNextAppLinkGeneration.snapshot(r.mNextAppLinkGeneration);
         // mReadMessages
         mPendingPackages.addAll(r.mPendingPackages);
         mSystemDir = null;
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index ccbf73c..9347ce1 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -3728,7 +3728,7 @@
     UserData putUserInfo(UserInfo userInfo) {
         final UserData userData = new UserData();
         userData.info = userInfo;
-        synchronized (mUsers) {
+        synchronized (mUsersLock) {
             mUsers.put(userInfo.id, userData);
         }
         return userData;
@@ -3736,7 +3736,7 @@
 
     @VisibleForTesting
     void removeUserInfo(@UserIdInt int userId) {
-        synchronized (mUsers) {
+        synchronized (mUsersLock) {
             mUsers.remove(userId);
         }
     }
@@ -4140,7 +4140,7 @@
         userFile.delete();
         updateUserIds();
         if (RELEASE_DELETED_USER_ID) {
-            synchronized (mUsers) {
+            synchronized (mUsersLock) {
                 mRemovingUserIds.delete(userId);
             }
         }
@@ -5326,6 +5326,9 @@
                                 debugMsg + " for another profile "
                                         + targetUserId + " from " + callingUserId);
                     }
+                    Slog.w(LOG_TAG, debugMsg + " for another profile "
+                            + targetUserId + " from " + callingUserId);
+                    return false;
                 }
 
                 UserInfo targetUserInfo = getUserInfoLU(targetUserId);
diff --git a/services/core/java/com/android/server/pm/permission/LegacyPermissionManagerInternal.java b/services/core/java/com/android/server/pm/permission/LegacyPermissionManagerInternal.java
index 7ca9f05..446e20b7 100644
--- a/services/core/java/com/android/server/pm/permission/LegacyPermissionManagerInternal.java
+++ b/services/core/java/com/android/server/pm/permission/LegacyPermissionManagerInternal.java
@@ -24,6 +24,11 @@
  */
 public interface LegacyPermissionManagerInternal {
     /**
+     * Reset the runtime permission state for all users and packages.
+     */
+    void resetRuntimePermissions();
+
+    /**
      * Sets the dialer application packages provider.
      * @param provider The provider.
      */
diff --git a/services/core/java/com/android/server/pm/permission/LegacyPermissionManagerService.java b/services/core/java/com/android/server/pm/permission/LegacyPermissionManagerService.java
index f453d74..fd9aa3e 100644
--- a/services/core/java/com/android/server/pm/permission/LegacyPermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/LegacyPermissionManagerService.java
@@ -22,6 +22,7 @@
 import android.app.admin.DevicePolicyManager;
 import android.content.Context;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManagerInternal;
 import android.os.Binder;
 import android.os.Process;
 import android.os.ServiceManager;
@@ -32,6 +33,7 @@
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.server.LocalServices;
 import com.android.server.pm.PackageManagerServiceUtils;
+import com.android.server.pm.UserManagerService;
 
 /**
  * Legacy permission manager service.
@@ -43,6 +45,9 @@
     private final Injector mInjector;
 
     @NonNull
+    private final Context mContext;
+
+    @NonNull
     private final DefaultPermissionGrantPolicy mDefaultPermissionGrantPolicy;
 
     /**
@@ -74,6 +79,7 @@
 
     @VisibleForTesting
     LegacyPermissionManagerService(@NonNull Context context, @NonNull Injector injector) {
+        mContext = context;
         mInjector = injector;
         mDefaultPermissionGrantPolicy = new DefaultPermissionGrantPolicy(context);
     }
@@ -190,6 +196,29 @@
 
     private class Internal implements LegacyPermissionManagerInternal {
         @Override
+        public void resetRuntimePermissions() {
+            mContext.enforceCallingOrSelfPermission(
+                    android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS,
+                    "revokeRuntimePermission");
+
+            final int callingUid = Binder.getCallingUid();
+            if (callingUid != Process.SYSTEM_UID && callingUid != 0) {
+                mContext.enforceCallingOrSelfPermission(
+                        android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+                        "resetRuntimePermissions");
+            }
+
+            final PackageManagerInternal packageManagerInternal = LocalServices.getService(
+                    PackageManagerInternal.class);
+            final PermissionManagerServiceInternal permissionManagerInternal =
+                    LocalServices.getService(PermissionManagerServiceInternal.class);
+            for (final int userId : UserManagerService.getInstance().getUserIds()) {
+                packageManagerInternal.forEachPackage(pkg ->
+                        permissionManagerInternal.resetRuntimePermissions(pkg, userId));
+            }
+        }
+
+        @Override
         public void setDialerAppPackagesProvider(PackagesProvider provider) {
             mDefaultPermissionGrantPolicy.setDialerAppPackagesProvider(provider);
         }
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 2107536f..004c015 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -716,13 +716,13 @@
     }
 
     @Override
-    public int getPermissionFlags(String permName, String packageName, int userId) {
+    public int getPermissionFlags(String packageName, String permName, int userId) {
         final int callingUid = getCallingUid();
-        return getPermissionFlagsInternal(permName, packageName, callingUid, userId);
+        return getPermissionFlagsInternal(packageName, permName, callingUid, userId);
     }
 
     private int getPermissionFlagsInternal(
-            String permName, String packageName, int callingUid, int userId) {
+            String packageName, String permName, int callingUid, int userId) {
         if (!mUserManagerInt.exists(userId)) {
             return 0;
         }
@@ -757,7 +757,7 @@
     }
 
     @Override
-    public void updatePermissionFlags(String permName, String packageName, int flagMask,
+    public void updatePermissionFlags(String packageName, String permName, int flagMask,
             int flagValues, boolean checkAdjustPolicyFlagPermission, int userId) {
         final int callingUid = getCallingUid();
         boolean overridePolicy = false;
@@ -787,11 +787,11 @@
         }
 
         updatePermissionFlagsInternal(
-                permName, packageName, flagMask, flagValues, callingUid, userId,
+                packageName, permName, flagMask, flagValues, callingUid, userId,
                 overridePolicy, mDefaultPermissionCallback);
     }
 
-    private void updatePermissionFlagsInternal(String permName, String packageName, int flagMask,
+    private void updatePermissionFlagsInternal(String packageName, String permName, int flagMask,
             int flagValues, int callingUid, int userId, boolean overridePolicy,
             PermissionCallback callback) {
         if (PermissionManager.DEBUG_TRACE_PERMISSION_UPDATES
@@ -955,10 +955,9 @@
         }
     }
 
-    @Override
-    public int checkPermission(String permName, String pkgName, @UserIdInt int userId) {
+    private int checkPermission(String pkgName, String permName, @UserIdInt int userId) {
         // Not using Objects.requireNonNull() here for compatibility reasons.
-        if (permName == null || pkgName == null) {
+        if (pkgName == null || permName == null) {
             return PackageManager.PERMISSION_DENIED;
         }
         if (!mUserManagerInt.exists(userId)) {
@@ -970,13 +969,13 @@
             checkPermissionDelegate = mCheckPermissionDelegate;
         }
         if (checkPermissionDelegate == null) {
-            return checkPermissionImpl(permName, pkgName, userId);
+            return checkPermissionImpl(pkgName, permName, userId);
         }
-        return checkPermissionDelegate.checkPermission(permName, pkgName, userId,
+        return checkPermissionDelegate.checkPermission(pkgName, permName, userId,
                 this::checkPermissionImpl);
     }
 
-    private int checkPermissionImpl(String permName, String pkgName, int userId) {
+    private int checkPermissionImpl(String pkgName, String permName, int userId) {
         final AndroidPackage pkg = mPackageManagerInt.getPackage(pkgName);
         if (pkg == null) {
             return PackageManager.PERMISSION_DENIED;
@@ -1037,8 +1036,7 @@
         return true;
     }
 
-    @Override
-    public int checkUidPermission(String permName, int uid) {
+    private int checkUidPermission(int uid, String permName) {
         // Not using Objects.requireNonNull() here for compatibility reasons.
         if (permName == null) {
             return PackageManager.PERMISSION_DENIED;
@@ -1053,13 +1051,13 @@
             checkPermissionDelegate = mCheckPermissionDelegate;
         }
         if (checkPermissionDelegate == null)  {
-            return checkUidPermissionImpl(permName, uid);
+            return checkUidPermissionImpl(uid, permName);
         }
-        return checkPermissionDelegate.checkUidPermission(permName, uid,
+        return checkPermissionDelegate.checkUidPermission(uid, permName,
                 this::checkUidPermissionImpl);
     }
 
-    private int checkUidPermissionImpl(String permName, int uid) {
+    private int checkUidPermissionImpl(int uid, String permName) {
         final AndroidPackage pkg = mPackageManagerInt.getPackage(uid);
         return checkUidPermissionInternal(pkg, uid, permName);
     }
@@ -1446,15 +1444,14 @@
     public void grantRuntimePermission(String packageName, String permName, final int userId) {
         final int callingUid = Binder.getCallingUid();
         final boolean overridePolicy =
-                checkUidPermission(ADJUST_RUNTIME_PERMISSIONS_POLICY, callingUid)
+                checkUidPermission(callingUid, ADJUST_RUNTIME_PERMISSIONS_POLICY)
                         == PackageManager.PERMISSION_GRANTED;
 
-        grantRuntimePermissionInternal(permName, packageName, overridePolicy,
+        grantRuntimePermissionInternal(packageName, permName, overridePolicy,
                 callingUid, userId, mDefaultPermissionCallback);
     }
 
-    // TODO swap permission name and package name
-    private void grantRuntimePermissionInternal(String permName, String packageName,
+    private void grantRuntimePermissionInternal(String packageName, String permName,
             boolean overridePolicy, int callingUid, final int userId, PermissionCallback callback) {
         if (PermissionManager.DEBUG_TRACE_GRANTS
                 && PermissionManager.shouldTraceGrant(packageName, permName, userId)) {
@@ -1622,15 +1619,14 @@
             String reason) {
         final int callingUid = Binder.getCallingUid();
         final boolean overridePolicy =
-                checkUidPermission(ADJUST_RUNTIME_PERMISSIONS_POLICY, callingUid)
+                checkUidPermission(callingUid, ADJUST_RUNTIME_PERMISSIONS_POLICY)
                         == PackageManager.PERMISSION_GRANTED;
 
-        revokeRuntimePermissionInternal(permName, packageName, overridePolicy, callingUid, userId,
+        revokeRuntimePermissionInternal(packageName, permName, overridePolicy, callingUid, userId,
                 reason, mDefaultPermissionCallback);
     }
 
-    // TODO swap permission name and package name
-    private void revokeRuntimePermissionInternal(String permName, String packageName,
+    private void revokeRuntimePermissionInternal(String packageName, String permName,
             boolean overridePolicy, int callingUid, final int userId, String reason,
             PermissionCallback callback) {
         if (PermissionManager.DEBUG_TRACE_PERMISSION_UPDATES
@@ -1764,27 +1760,6 @@
         return Arrays.asList(packageNames).contains(permissionControllerPackageName);
     }
 
-    @Override
-    public void resetRuntimePermissions() {
-        mContext.enforceCallingOrSelfPermission(
-                android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS,
-                "revokeRuntimePermission");
-
-        final int callingUid = Binder.getCallingUid();
-        if (callingUid != Process.SYSTEM_UID && callingUid != 0) {
-            mContext.enforceCallingOrSelfPermission(
-                    android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
-                    "resetRuntimePermissions");
-        }
-
-        updateAllPermissions(
-                StorageManager.UUID_PRIVATE_INTERNAL, false, mDefaultPermissionCallback);
-        for (final int userId : UserManagerService.getInstance().getUserIds()) {
-            mPackageManagerInt.forEachPackage(
-                    (AndroidPackage pkg) -> resetRuntimePermissionsInternal(pkg, userId));
-        }
-    }
-
     /**
      * Reverts user permission state changes (permissions and flags).
      *
@@ -1908,7 +1883,7 @@
             }
 
             final int oldFlags =
-                    getPermissionFlagsInternal(permName, packageName, Process.SYSTEM_UID, userId);
+                    getPermissionFlagsInternal(packageName, permName, Process.SYSTEM_UID, userId);
 
             // Always clear the user settable flags.
             // If permission review is enabled and this is a legacy app, mark the
@@ -1920,7 +1895,7 @@
                     : 0;
 
             updatePermissionFlagsInternal(
-                    permName, packageName, userSettableMask, flags, Process.SYSTEM_UID, userId,
+                    packageName, permName, userSettableMask, flags, Process.SYSTEM_UID, userId,
                     false, delayingPermCallback);
 
             // Below is only runtime permission handling.
@@ -1937,13 +1912,13 @@
             if ((oldFlags & FLAG_PERMISSION_GRANTED_BY_DEFAULT) != 0
                     || (oldFlags & FLAG_PERMISSION_GRANTED_BY_ROLE) != 0) {
                 // PermissionPolicyService will handle the app op for runtime permissions later.
-                grantRuntimePermissionInternal(permName, packageName, false,
+                grantRuntimePermissionInternal(packageName, permName, false,
                         Process.SYSTEM_UID, userId, delayingPermCallback);
             // If permission review is enabled the permissions for a legacy apps
             // are represented as constantly granted runtime ones, so don't revoke.
             } else if ((flags & FLAG_PERMISSION_REVIEW_REQUIRED) == 0) {
                 // Otherwise, reset the permission.
-                revokeRuntimePermissionInternal(permName, packageName, false, Process.SYSTEM_UID,
+                revokeRuntimePermissionInternal(packageName, permName, false, Process.SYSTEM_UID,
                         userId, null, delayingPermCallback);
             }
         }
@@ -1982,8 +1957,8 @@
     private static final long BACKGROUND_RATIONALE_CHANGE_ID = 147316723L;
 
     @Override
-    public boolean shouldShowRequestPermissionRationale(String permName,
-            String packageName, int userId) {
+    public boolean shouldShowRequestPermissionRationale(String packageName, String permName,
+            @UserIdInt int userId) {
         final int callingUid = Binder.getCallingUid();
         if (UserHandle.getCallingUserId() != userId) {
             mContext.enforceCallingPermission(
@@ -1997,7 +1972,7 @@
             return false;
         }
 
-        if (checkPermission(permName, packageName, userId)
+        if (checkPermission(packageName, permName, userId)
                 == PackageManager.PERMISSION_GRANTED) {
             return false;
         }
@@ -2006,7 +1981,7 @@
 
         final long identity = Binder.clearCallingIdentity();
         try {
-            flags = getPermissionFlagsInternal(permName, packageName, callingUid, userId);
+            flags = getPermissionFlagsInternal(packageName, permName, callingUid, userId);
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
@@ -2047,14 +2022,14 @@
     }
 
     @Override
-    public boolean isPermissionRevokedByPolicy(String permName, String packageName, int userId) {
+    public boolean isPermissionRevokedByPolicy(String packageName, String permName, int userId) {
         if (UserHandle.getCallingUserId() != userId) {
             mContext.enforceCallingPermission(
                     android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
                     "isPermissionRevokedByPolicy for user " + userId);
         }
 
-        if (checkPermission(permName, packageName, userId) == PackageManager.PERMISSION_GRANTED) {
+        if (checkPermission(packageName, permName, userId) == PackageManager.PERMISSION_GRANTED) {
             return false;
         }
 
@@ -2065,7 +2040,7 @@
 
         final long identity = Binder.clearCallingIdentity();
         try {
-            final int flags = getPermissionFlagsInternal(permName, packageName, callingUid, userId);
+            final int flags = getPermissionFlagsInternal(packageName, permName, callingUid, userId);
             return (flags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0;
         } finally {
             Binder.restoreCallingIdentity(identity);
@@ -2238,7 +2213,7 @@
                             + downgradedSdk + " or newly requested legacy full storage "
                             + newlyRequestsLegacy);
 
-            revokeRuntimePermissionInternal(permInfo.name, newPackage.getPackageName(),
+            revokeRuntimePermissionInternal(newPackage.getPackageName(), permInfo.name,
                     false, callingUid, userId, null, mDefaultPermissionCallback);
         }
 
@@ -2289,7 +2264,7 @@
                     mPackageManagerInt.forEachPackage(pkg -> {
                         final String packageName = pkg.getPackageName();
                         for (final int userId : userIds) {
-                            final int permissionState = checkPermission(permissionName, packageName,
+                            final int permissionState = checkPermission(packageName, permissionName,
                                     userId);
                             if (permissionState == PackageManager.PERMISSION_GRANTED) {
                                 EventLog.writeEvent(0x534e4554, "72710897",
@@ -2300,7 +2275,7 @@
                                         " to " + newPermissionGroupName);
 
                                 try {
-                                    revokeRuntimePermissionInternal(permissionName, packageName,
+                                    revokeRuntimePermissionInternal(packageName, permissionName,
                                             false, callingUid, userId, null,
                                             mDefaultPermissionCallback);
                                 } catch (IllegalArgumentException e) {
@@ -2343,9 +2318,9 @@
                     return;
                 }
                 for (final int userId : userIds) {
-                    final int permissionState = checkPermissionImpl(permName, packageName,
+                    final int permissionState = checkPermissionImpl(packageName, permName,
                             userId);
-                    final int flags = getPermissionFlags(permName, packageName, userId);
+                    final int flags = getPermissionFlags(packageName, permName, userId);
                     final int flagMask = FLAG_PERMISSION_SYSTEM_FIXED
                             | FLAG_PERMISSION_POLICY_FIXED
                             | FLAG_PERMISSION_GRANTED_BY_DEFAULT
@@ -2362,7 +2337,7 @@
                         Slog.e(TAG, "Revoking permission " + permName + " from package "
                                 + packageName + " due to definition change");
                         try {
-                            revokeRuntimePermissionInternal(permName, packageName,
+                            revokeRuntimePermissionInternal(packageName, permName,
                                     false, callingUid, userId, null, mDefaultPermissionCallback);
                         } catch (Exception e) {
                             Slog.e(TAG, "Could not revoke " + permName + " from "
@@ -3717,19 +3692,19 @@
                         && (permissions == null || permissions.contains(permission));
             }
             if (shouldGrantPermission) {
-                final int flags = getPermissionFlagsInternal(permission, pkg.getPackageName(),
+                final int flags = getPermissionFlagsInternal(pkg.getPackageName(), permission,
                         myUid, userId);
                 if (supportsRuntimePermissions) {
                     // Installer cannot change immutable permissions.
                     if ((flags & immutableFlags) == 0) {
-                        grantRuntimePermissionInternal(permission, pkg.getPackageName(), false,
+                        grantRuntimePermissionInternal(pkg.getPackageName(), permission, false,
                                 myUid, userId, mDefaultPermissionCallback);
                     }
                 } else {
                     // In permission review mode we clear the review flag and the revoked compat
                     // flag when we are asked to install the app with all permissions granted.
                     if ((flags & compatFlags) != 0) {
-                        updatePermissionFlagsInternal(permission, pkg.getPackageName(), compatFlags,
+                        updatePermissionFlagsInternal(pkg.getPackageName(), permission, compatFlags,
                                 0, myUid, userId, false, mDefaultPermissionCallback);
                     }
                 }
@@ -3771,8 +3746,8 @@
                 oldGrantedRestrictedPermissions.add(permissionName);
             }
 
-            final int oldFlags = getPermissionFlagsInternal(permissionName,
-                    pkg.getPackageName(), myUid, userId);
+            final int oldFlags = getPermissionFlagsInternal(pkg.getPackageName(), permissionName,
+                    myUid, userId);
 
             int newFlags = oldFlags;
             int mask = 0;
@@ -3850,7 +3825,7 @@
                 newFlags |= PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
             }
 
-            updatePermissionFlagsInternal(permissionName, pkg.getPackageName(), mask, newFlags,
+            updatePermissionFlagsInternal(pkg.getPackageName(), permissionName, mask, newFlags,
                     myUid, userId, false, null /*callback*/);
         }
 
@@ -4029,19 +4004,18 @@
      *     <li>Update the state (grant, flags) of the permissions</li>
      * </ol>
      *
-     * @param volumeUuid The volume of the packages to be updated, {@code null} for all volumes
-     * @param allPackages All currently known packages
-     * @param callback Callback to call after permission changes
+     * @param volumeUuid The volume UUID of the packages to be updated
+     * @param sdkVersionChanged whether the current SDK version is different from what it was when
+     *                          this volume was last mounted
      */
-    private void updateAllPermissions(@Nullable String volumeUuid, boolean sdkUpdated,
-            @NonNull PermissionCallback callback) {
+    private void updateAllPermissions(@NonNull String volumeUuid, boolean sdkVersionChanged) {
         PackageManager.corkPackageInfoCache();  // Prevent invalidation storm
         try {
             final int flags = UPDATE_PERMISSIONS_ALL |
-                    (sdkUpdated
+                    (sdkVersionChanged
                             ? UPDATE_PERMISSIONS_REPLACE_PKG | UPDATE_PERMISSIONS_REPLACE_ALL
                             : 0);
-            updatePermissions(null, null, volumeUuid, flags, callback);
+            updatePermissions(null, null, volumeUuid, flags, mDefaultPermissionCallback);
         } finally {
             PackageManager.uncorkPackageInfoCache();
         }
@@ -4262,12 +4236,11 @@
             return;
         }
 
-        if (checkPermissionImpl(permissionName, pName, userId)
+        if (checkPermissionImpl(pName, permissionName, userId)
                 == PackageManager.PERMISSION_GRANTED) {
             try {
                 revokeRuntimePermissionInternal(
-                        permissionName,
-                        pName,
+                        pName, permissionName,
                         overridePolicy,
                         Process.SYSTEM_UID,
                         userId,
@@ -4483,6 +4456,20 @@
     }
 
     private void systemReady() {
+        // Now that we've scanned all packages, and granted any default
+        // permissions, ensure permissions are updated. Beware of dragons if you
+        // try optimizing this.
+        updateAllPermissions(StorageManager.UUID_PRIVATE_INTERNAL, false);
+
+        final PermissionPolicyInternal permissionPolicyInternal = LocalServices.getService(
+                PermissionPolicyInternal.class);
+        permissionPolicyInternal.setOnInitializedCallback(userId -> {
+            // The SDK updated case is already handled when we run during the ctor.
+            synchronized (mLock) {
+                updateAllPermissions(StorageManager.UUID_PRIVATE_INTERNAL, false);
+            }
+        });
+
         mSystemReady = true;
 
         synchronized (mLock) {
@@ -4948,6 +4935,18 @@
 
     private class PermissionManagerServiceInternalImpl implements PermissionManagerServiceInternal {
         @Override
+        public int checkPermission(@NonNull String packageName, @NonNull String permissionName,
+                @UserIdInt int userId) {
+            return PermissionManagerService.this.checkPermission(packageName, permissionName,
+                    userId);
+        }
+
+        @Override
+        public int checkUidPermission(int uid, @NonNull String permissionName) {
+            return PermissionManagerService.this.checkUidPermission(uid, permissionName);
+        }
+
+        @Override
         public void onSystemReady() {
             PermissionManagerService.this.systemReady();
         }
@@ -4996,9 +4995,8 @@
             return PermissionManagerService.this.getAppOpPermissionPackagesInternal(permissionName);
         }
         @Override
-        public void updateAllPermissions(@Nullable String volumeUuid, boolean sdkUpdated) {
-            PermissionManagerService.this
-                    .updateAllPermissions(volumeUuid, sdkUpdated, mDefaultPermissionCallback);
+        public void onStorageVolumeMounted(@Nullable String volumeUuid, boolean sdkVersionChanged) {
+            updateAllPermissions(volumeUuid, sdkVersionChanged);
         }
         @Override
         public void resetRuntimePermissions(@NonNull AndroidPackage pkg, @UserIdInt int userId) {
@@ -5098,8 +5096,7 @@
         public void onUserCreated(@UserIdInt int userId) {
             Preconditions.checkArgumentNonNegative(userId, "userId");
             // NOTE: This adds UPDATE_PERMISSIONS_REPLACE_PKG
-            PermissionManagerService.this.updateAllPermissions(StorageManager.UUID_PRIVATE_INTERNAL,
-                    true, mDefaultPermissionCallback);
+            updateAllPermissions(StorageManager.UUID_PRIVATE_INTERNAL, true);
         }
 
         @Override
@@ -5264,8 +5261,8 @@
         /**
          * Check whether the given package has been granted the specified permission.
          *
-         * @param permissionName the name of the permission to be checked
          * @param packageName the name of the package to be checked
+         * @param permissionName the name of the permission to be checked
          * @param userId the user ID
          * @param superImpl the original implementation that can be delegated to
          * @return {@link android.content.pm.PackageManager.PERMISSION_GRANTED} if the package has
@@ -5273,21 +5270,21 @@
          *
          * @see android.content.pm.PackageManager#checkPermission(String, String)
          */
-        int checkPermission(@NonNull String permissionName, @NonNull String packageName,
+        int checkPermission(@NonNull String packageName, @NonNull String permissionName,
                 @UserIdInt int userId,
                 @NonNull TriFunction<String, String, Integer, Integer> superImpl);
 
         /**
          * Check whether the given UID has been granted the specified permission.
          *
-         * @param permissionName the name of the permission to be checked
          * @param uid the UID to be checked
+         * @param permissionName the name of the permission to be checked
          * @param superImpl the original implementation that can be delegated to
          * @return {@link android.content.pm.PackageManager.PERMISSION_GRANTED} if the package has
          * the permission, or {@link android.content.pm.PackageManager.PERMISSION_DENITED} otherwise
          */
-        int checkUidPermission(@NonNull String permissionName, int uid,
-                BiFunction<String, Integer, Integer> superImpl);
+        int checkUidPermission(int uid, @NonNull String permissionName,
+                BiFunction<Integer, String, Integer> superImpl);
     }
 
     private class ShellDelegate implements CheckPermissionDelegate {
@@ -5310,32 +5307,32 @@
         }
 
         @Override
-        public int checkPermission(@NonNull String permissionName, @NonNull String packageName,
+        public int checkPermission(@NonNull String packageName, @NonNull String permissionName,
                 int userId, @NonNull TriFunction<String, String, Integer, Integer> superImpl) {
             if (mDelegatedPackageName.equals(packageName)
                     && isDelegatedPermission(permissionName)) {
                 final long identity = Binder.clearCallingIdentity();
                 try {
-                    return superImpl.apply(permissionName, "com.android.shell", userId);
+                    return superImpl.apply("com.android.shell", permissionName, userId);
                 } finally {
                     Binder.restoreCallingIdentity(identity);
                 }
             }
-            return superImpl.apply(permissionName, packageName, userId);
+            return superImpl.apply(packageName, permissionName, userId);
         }
 
         @Override
-        public int checkUidPermission(@NonNull String permissionName, int uid,
-                @NonNull BiFunction<String, Integer, Integer> superImpl) {
+        public int checkUidPermission(int uid, @NonNull String permissionName,
+                @NonNull BiFunction<Integer, String, Integer> superImpl) {
             if (uid == mDelegatedUid && isDelegatedPermission(permissionName)) {
                 final long identity = Binder.clearCallingIdentity();
                 try {
-                    return superImpl.apply(permissionName, Process.SHELL_UID);
+                    return superImpl.apply(Process.SHELL_UID, permissionName);
                 } finally {
                     Binder.restoreCallingIdentity(identity);
                 }
             }
-            return superImpl.apply(permissionName, uid);
+            return superImpl.apply(uid, permissionName);
         }
 
         private boolean isDelegatedPermission(@NonNull String permissionName) {
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
index 1cfae00..59682e1 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
@@ -39,6 +39,30 @@
 public interface PermissionManagerServiceInternal extends PermissionManagerInternal,
         LegacyPermissionDataProvider {
     /**
+     * Check whether a particular package has been granted a particular permission.
+     *
+     * @param packageName the name of the package you are checking against
+     * @param permissionName the name of the permission you are checking for
+     * @param userId the user ID
+     * @return {@code PERMISSION_GRANTED} if the permission is granted, or {@code PERMISSION_DENIED}
+     *         otherwise
+     */
+    //@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
+    int checkPermission(@NonNull String packageName, @NonNull String permissionName,
+            @UserIdInt int userId);
+
+    /**
+     * Check whether a particular UID has been granted a particular permission.
+     *
+     * @param uid the UID
+     * @param permissionName the name of the permission you are checking for
+     * @return {@code PERMISSION_GRANTED} if the permission is granted, or {@code PERMISSION_DENIED}
+     *         otherwise
+     */
+    //@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
+    int checkUidPermission(int uid, @NonNull String permissionName);
+
+    /**
      * Adds a listener for runtime permission state (permissions or flags) changes.
      *
      * @param listener The listener.
@@ -66,20 +90,6 @@
             @UserIdInt int userId);
 
     /**
-     * Update all permissions for all apps.
-     *
-     * <p><ol>
-     *     <li>Reconsider the ownership of permission</li>
-     *     <li>Update the state (grant, flags) of the permissions</li>
-     * </ol>
-     *
-     * @param volumeUuid The volume of the packages to be updated, {@code null} for all volumes
-     * @param allPackages All currently known packages
-     * @param callback Callback to call after permission changes
-     */
-    void updateAllPermissions(@Nullable String volumeUuid, boolean sdkUpdate);
-
-    /**
      * Reset the runtime permission state changes for a package.
      *
      * TODO(zhanghai): Turn this into package change callback?
@@ -197,6 +207,16 @@
     void onSystemReady();
 
     /**
+     * Callback when a storage volume is mounted, so that all packages on it become available.
+     *
+     * @param volumeUuid the UUID of the storage volume
+     * @param sdkVersionChanged whether the current SDK version is different from what it was when
+     *                          this volume was last mounted
+     */
+    //@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
+    void onStorageVolumeMounted(@NonNull String volumeUuid, boolean sdkVersionChanged);
+
+    /**
      * Callback when a user has been created.
      *
      * @param userId the created user ID
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 6919cea..6e4806f 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -2698,8 +2698,7 @@
                 float stepFloat = (maxFloat - minFloat) / BRIGHTNESS_STEPS * direction;
                 float brightnessFloat = Settings.System.getFloatForUser(
                         mContext.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS_FLOAT,
-                        mPowerManager.getBrightnessConstraint(
-                                PowerManager.BRIGHTNESS_CONSTRAINT_TYPE_DEFAULT),
+                        mContext.getDisplay().getBrightnessDefault(),
                         UserHandle.USER_CURRENT_OR_SELF);
                 brightnessFloat += stepFloat;
                 // Make sure we don't go beyond the limits.
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 966be99..db79ce4 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -3198,7 +3198,11 @@
     int pullFaceSettingsLocked(int atomTag, List<StatsEvent> pulledData) {
         final long callingToken = Binder.clearCallingIdentity();
         try {
-            List<UserInfo> users = mContext.getSystemService(UserManager.class).getUsers();
+            UserManager manager = mContext.getSystemService(UserManager.class);
+            if (manager == null) {
+                return StatsManager.PULL_SKIP;
+            }
+            List<UserInfo> users = manager.getUsers();
             int numUsers = users.size();
             for (int userNum = 0; userNum < numUsers; userNum++) {
                 int userId = users.get(userNum).getUserHandle().getIdentifier();
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java
index 2d50390..203a8a4 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternal.java
@@ -30,6 +30,12 @@
     /** Adds a listener that will be invoked when time zone detection configuration is changed. */
     void addConfigurationListener(ConfigurationChangeListener listener);
 
+    /**
+     * Removes a listener previously added via {@link
+     * #addConfigurationListener(ConfigurationChangeListener)}.
+     */
+    void removeConfigurationListener(ConfigurationChangeListener listener);
+
     /** Returns the {@link ConfigurationInternal} for the current user. */
     ConfigurationInternal getCurrentUserConfigurationInternal();
 
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternalImpl.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternalImpl.java
index f0ce827..2d5dacd 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternalImpl.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorInternalImpl.java
@@ -69,6 +69,13 @@
     }
 
     @Override
+    public void removeConfigurationListener(ConfigurationChangeListener listener) {
+        synchronized (mConfigurationListeners) {
+            mConfigurationListeners.remove(Objects.requireNonNull(listener));
+        }
+    }
+
+    @Override
     @NonNull
     public ConfigurationInternal getCurrentUserConfigurationInternal() {
         return mTimeZoneDetectorStrategy.getCurrentUserConfigurationInternal();
diff --git a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorShellCommand.java b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorShellCommand.java
index a8d5c02..13b4456 100644
--- a/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorShellCommand.java
+++ b/services/core/java/com/android/server/timezonedetector/TimeZoneDetectorShellCommand.java
@@ -15,6 +15,10 @@
  */
 package com.android.server.timezonedetector;
 
+import static android.app.timezonedetector.TimeZoneDetector.SHELL_COMMAND_SUGGEST_GEO_LOCATION_TIME_ZONE;
+import static android.app.timezonedetector.TimeZoneDetector.SHELL_COMMAND_SUGGEST_MANUAL_TIME_ZONE;
+import static android.app.timezonedetector.TimeZoneDetector.SHELL_COMMAND_SUGGEST_TELEPHONY_TIME_ZONE;
+
 import android.app.timezonedetector.ManualTimeZoneSuggestion;
 import android.app.timezonedetector.TelephonyTimeZoneSuggestion;
 import android.os.ShellCommand;
@@ -39,11 +43,11 @@
         }
 
         switch (cmd) {
-            case "suggest_geo_location_time_zone":
+            case SHELL_COMMAND_SUGGEST_GEO_LOCATION_TIME_ZONE:
                 return runSuggestGeolocationTimeZone();
-            case "suggest_manual_time_zone":
+            case SHELL_COMMAND_SUGGEST_MANUAL_TIME_ZONE:
                 return runSuggestManualTimeZone();
-            case "suggest_telephony_time_zone":
+            case SHELL_COMMAND_SUGGEST_TELEPHONY_TIME_ZONE:
                 return runSuggestTelephonyTimeZone();
             default: {
                 return handleDefaultCommands(cmd);
@@ -72,16 +76,7 @@
     private <T> int runSuggestTimeZone(Supplier<T> suggestionParser, Consumer<T> invoker) {
         final PrintWriter pw = getOutPrintWriter();
         try {
-            T suggestion = null;
-            String opt;
-            while ((opt = getNextArg()) != null) {
-                if ("--suggestion".equals(opt)) {
-                    suggestion = suggestionParser.get();
-                } else {
-                    pw.println("Error: Unknown option: " + opt);
-                    return 1;
-                }
-            }
+            T suggestion = suggestionParser.get();
             if (suggestion == null) {
                 pw.println("Error: suggestion not specified");
                 return 1;
@@ -101,12 +96,12 @@
         pw.println("Time Zone Detector (time_zone_detector) commands:");
         pw.println("  help");
         pw.println("    Print this help text.");
-        pw.println("  suggest_geolocation_time_zone");
-        pw.println("    --suggestion <geolocation suggestion opts>");
-        pw.println("  suggest_manual_time_zone");
-        pw.println("    --suggestion <manual suggestion opts>");
-        pw.println("  suggest_telephony_time_zone");
-        pw.println("    --suggestion <telephony suggestion opts>");
+        pw.printf("  %s <geolocation suggestion opts>\n",
+                SHELL_COMMAND_SUGGEST_GEO_LOCATION_TIME_ZONE);
+        pw.printf("  %s <manual suggestion opts>\n",
+                SHELL_COMMAND_SUGGEST_MANUAL_TIME_ZONE);
+        pw.printf("  %s <telephony suggestion opts>\n",
+                SHELL_COMMAND_SUGGEST_TELEPHONY_TIME_ZONE);
         pw.println();
         GeolocationTimeZoneSuggestion.printCommandLineOpts(pw);
         pw.println();
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java b/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java
index 036049f..edf007d 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/ClientProfile.java
@@ -63,9 +63,9 @@
     private int mNiceValue;
 
     /**
-     * List of the frontend ids that are used by the current client.
+     * List of the frontend handles that are used by the current client.
      */
-    private Set<Integer> mUsingFrontendIds = new HashSet<>();
+    private Set<Integer> mUsingFrontendHandles = new HashSet<>();
 
     /**
      * List of the client ids that share frontend with the current client.
@@ -73,9 +73,9 @@
     private Set<Integer> mShareFeClientIds = new HashSet<>();
 
     /**
-     * List of the Lnb ids that are used by the current client.
+     * List of the Lnb handles that are used by the current client.
      */
-    private Set<Integer> mUsingLnbIds = new HashSet<>();
+    private Set<Integer> mUsingLnbHandles = new HashSet<>();
 
     /**
      * List of the Cas system ids that are used by the current client.
@@ -139,10 +139,10 @@
     /**
      * Set when the client starts to use a frontend.
      *
-     * @param frontendId being used.
+     * @param frontendHandle being used.
      */
-    public void useFrontend(int frontendId) {
-        mUsingFrontendIds.add(frontendId);
+    public void useFrontend(int frontendHandle) {
+        mUsingFrontendHandles.add(frontendHandle);
     }
 
     /**
@@ -163,8 +163,8 @@
         mShareFeClientIds.remove(clientId);
     }
 
-    public Set<Integer> getInUseFrontendIds() {
-        return mUsingFrontendIds;
+    public Set<Integer> getInUseFrontendHandles() {
+        return mUsingFrontendHandles;
     }
 
     public Set<Integer> getShareFeClientIds() {
@@ -175,30 +175,30 @@
      * Called when the client released a frontend.
      */
     public void releaseFrontend() {
-        mUsingFrontendIds.clear();
+        mUsingFrontendHandles.clear();
         mShareFeClientIds.clear();
     }
 
     /**
      * Set when the client starts to use an Lnb.
      *
-     * @param lnbId being used.
+     * @param lnbHandle being used.
      */
-    public void useLnb(int lnbId) {
-        mUsingLnbIds.add(lnbId);
+    public void useLnb(int lnbHandle) {
+        mUsingLnbHandles.add(lnbHandle);
     }
 
-    public Set<Integer> getInUseLnbIds() {
-        return mUsingLnbIds;
+    public Set<Integer> getInUseLnbHandles() {
+        return mUsingLnbHandles;
     }
 
     /**
      * Called when the client released an lnb.
      *
-     * @param lnbId being released.
+     * @param lnbHandle being released.
      */
-    public void releaseLnb(int lnbId) {
-        mUsingLnbIds.remove(lnbId);
+    public void releaseLnb(int lnbHandle) {
+        mUsingLnbHandles.remove(lnbHandle);
     }
 
     /**
@@ -225,9 +225,9 @@
      * Called to reclaim all the resources being used by the current client.
      */
     public void reclaimAllResources() {
-        mUsingFrontendIds.clear();
+        mUsingFrontendHandles.clear();
         mShareFeClientIds.clear();
-        mUsingLnbIds.clear();
+        mUsingLnbHandles.clear();
         mUsingCasSystemId = INVALID_RESOURCE_ID;
     }
 
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/FrontendResource.java b/services/core/java/com/android/server/tv/tunerresourcemanager/FrontendResource.java
index 7ea62b2..7ef75e3 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/FrontendResource.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/FrontendResource.java
@@ -40,9 +40,9 @@
     private final int mExclusiveGroupId;
 
     /**
-     * An array to save all the FE ids under the same exclisive group.
+     * An array to save all the FE handles under the same exclisive group.
      */
-    private Set<Integer> mExclusiveGroupMemberFeIds = new HashSet<>();
+    private Set<Integer> mExclusiveGroupMemberHandles = new HashSet<>();
 
     private FrontendResource(Builder builder) {
         super(builder);
@@ -58,26 +58,26 @@
         return mExclusiveGroupId;
     }
 
-    public Set<Integer> getExclusiveGroupMemberFeIds() {
-        return mExclusiveGroupMemberFeIds;
+    public Set<Integer> getExclusiveGroupMemberFeHandles() {
+        return mExclusiveGroupMemberHandles;
     }
 
     /**
-     * Add one id into the exclusive group member id collection.
+     * Add one handle into the exclusive group member handle collection.
      *
-     * @param id the id to be added.
+     * @param handle the handle to be added.
      */
-    public void addExclusiveGroupMemberFeId(int id) {
-        mExclusiveGroupMemberFeIds.add(id);
+    public void addExclusiveGroupMemberFeHandle(int handle) {
+        mExclusiveGroupMemberHandles.add(handle);
     }
 
     /**
-     * Add one id collection to the exclusive group member id collection.
+     * Add one handle collection to the exclusive group member handle collection.
      *
-     * @param ids the id collection to be added.
+     * @param handles the handle collection to be added.
      */
-    public void addExclusiveGroupMemberFeIds(Collection<Integer> ids) {
-        mExclusiveGroupMemberFeIds.addAll(ids);
+    public void addExclusiveGroupMemberFeHandles(Collection<Integer> handles) {
+        mExclusiveGroupMemberHandles.addAll(handles);
     }
 
     /**
@@ -85,15 +85,15 @@
      *
      * @param id the id to be removed.
      */
-    public void removeExclusiveGroupMemberFeId(int id) {
-        mExclusiveGroupMemberFeIds.remove(id);
+    public void removeExclusiveGroupMemberFeId(int handle) {
+        mExclusiveGroupMemberHandles.remove(handle);
     }
 
     @Override
     public String toString() {
-        return "FrontendResource[id=" + this.mId + ", type=" + this.mType
-                + ", exclusiveGId=" + this.mExclusiveGroupId + ", exclusiveGMemeberIds="
-                + this.mExclusiveGroupMemberFeIds
+        return "FrontendResource[handle=" + this.mHandle + ", type=" + this.mType
+                + ", exclusiveGId=" + this.mExclusiveGroupId + ", exclusiveGMemeberHandles="
+                + this.mExclusiveGroupMemberHandles
                 + ", isInUse=" + this.mIsInUse + ", ownerClientId=" + this.mOwnerClientId + "]";
     }
 
@@ -104,8 +104,8 @@
         @Type private int mType;
         private int mExclusiveGroupId;
 
-        Builder(int id) {
-            super(id);
+        Builder(int handle) {
+            super(handle);
         }
 
         /**
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/LnbResource.java b/services/core/java/com/android/server/tv/tunerresourcemanager/LnbResource.java
index 345b4b2..41cacea 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/LnbResource.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/LnbResource.java
@@ -29,7 +29,7 @@
 
     @Override
     public String toString() {
-        return "LnbResource[id=" + this.mId
+        return "LnbResource[handle=" + this.mHandle
                 + ", isInUse=" + this.mIsInUse + ", ownerClientId=" + this.mOwnerClientId + "]";
     }
 
@@ -38,8 +38,8 @@
      */
     public static class Builder extends TunerResourceBasic.Builder {
 
-        Builder(int id) {
-            super(id);
+        Builder(int handle) {
+            super(handle);
         }
 
         /**
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceBasic.java b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceBasic.java
index 7f133c3..07853fc 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceBasic.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceBasic.java
@@ -25,10 +25,10 @@
  */
 public class TunerResourceBasic {
     /**
-     * Id of the current resource. Should not be changed and should be aligned with the driver level
-     * implementation.
+     * Handle of the current resource. Should not be changed and should be aligned with the driver
+     * level implementation.
      */
-    final int mId;
+    final int mHandle;
 
     /**
      * If the current resource is in use.
@@ -41,11 +41,11 @@
     int mOwnerClientId = INVALID_OWNER_ID;
 
     TunerResourceBasic(Builder builder) {
-        this.mId = builder.mId;
+        this.mHandle = builder.mHandle;
     }
 
-    public int getId() {
-        return mId;
+    public int getHandle() {
+        return mHandle;
     }
 
     public boolean isInUse() {
@@ -78,10 +78,10 @@
      * Builder class for {@link TunerResourceBasic}.
      */
     public static class Builder {
-        private final int mId;
+        private final int mHandle;
 
-        Builder(int id) {
-            this.mId = id;
+        Builder(int handle) {
+            this.mHandle = handle;
         }
 
         /**
diff --git a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
index fb2347e8..8c6e690 100644
--- a/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
+++ b/services/core/java/com/android/server/tv/tunerresourcemanager/TunerResourceManagerService.java
@@ -190,13 +190,13 @@
         }
 
         @Override
-        public void setLnbInfoList(int[] lnbIds) throws RemoteException {
+        public void setLnbInfoList(int[] lnbHandles) throws RemoteException {
             enforceTrmAccessPermission("setLnbInfoList");
-            if (lnbIds == null) {
-                throw new RemoteException("Lnb id list can't be null");
+            if (lnbHandles == null) {
+                throw new RemoteException("Lnb handle list can't be null");
             }
             synchronized (mLock) {
-                setLnbInfoListInternal(lnbIds);
+                setLnbInfoListInternal(lnbHandles);
             }
         }
 
@@ -214,7 +214,7 @@
                             + request.getClientId());
                 }
                 // If the request client is holding or sharing a frontend, throw an exception.
-                if (!getClientProfile(request.getClientId()).getInUseFrontendIds().isEmpty()) {
+                if (!getClientProfile(request.getClientId()).getInUseFrontendHandles().isEmpty()) {
                     throw new RemoteException("Release frontend before requesting another one. "
                             + "Client id: " + request.getClientId());
                 }
@@ -235,7 +235,7 @@
                     throw new RemoteException("Request to share frontend with an unregistered "
                             + "client:" + targetClientId);
                 }
-                if (getClientProfile(targetClientId).getInUseFrontendIds().isEmpty()) {
+                if (getClientProfile(targetClientId).getInUseFrontendHandles().isEmpty()) {
                     throw new RemoteException("Request to share frontend with a client that has no "
                             + "frontend resources. Target client id:" + targetClientId);
                 }
@@ -323,8 +323,7 @@
                     throw new RemoteException("Release frontend from unregistered client:"
                             + clientId);
                 }
-                int frontendId = getResourceIdFromHandle(frontendHandle);
-                FrontendResource fe = getFrontendResource(frontendId);
+                FrontendResource fe = getFrontendResource(frontendHandle);
                 if (fe == null) {
                     throw new RemoteException("Releasing frontend does not exist.");
                 }
@@ -388,8 +387,7 @@
             if (!checkClientExists(clientId)) {
                 throw new RemoteException("Release lnb from unregistered client:" + clientId);
             }
-            int lnbId = getResourceIdFromHandle(lnbHandle);
-            LnbResource lnb = getLnbResource(lnbId);
+            LnbResource lnb = getLnbResource(lnbHandle);
             if (lnb == null) {
                 throw new RemoteException("Releasing lnb does not exist.");
             }
@@ -518,18 +516,18 @@
         // A set to record the frontends pending on updating. Ids will be removed
         // from this set once its updating finished. Any frontend left in this set when all
         // the updates are done will be removed from mFrontendResources.
-        Set<Integer> updatingFrontendIds = new HashSet<>(getFrontendResources().keySet());
+        Set<Integer> updatingFrontendHandles = new HashSet<>(getFrontendResources().keySet());
 
         // Update frontendResources map and other mappings accordingly
         for (int i = 0; i < infos.length; i++) {
-            if (getFrontendResource(infos[i].getId()) != null) {
+            if (getFrontendResource(infos[i].getHandle()) != null) {
                 if (DEBUG) {
-                    Slog.d(TAG, "Frontend id=" + infos[i].getId() + "exists.");
+                    Slog.d(TAG, "Frontend handle=" + infos[i].getHandle() + "exists.");
                 }
-                updatingFrontendIds.remove(infos[i].getId());
+                updatingFrontendHandles.remove(infos[i].getHandle());
             } else {
                 // Add a new fe resource
-                FrontendResource newFe = new FrontendResource.Builder(infos[i].getId())
+                FrontendResource newFe = new FrontendResource.Builder(infos[i].getHandle())
                                                  .type(infos[i].getFrontendType())
                                                  .exclusiveGroupId(infos[i].getExclusiveGroupId())
                                                  .build();
@@ -537,41 +535,41 @@
             }
         }
 
-        for (int removingId : updatingFrontendIds) {
+        for (int removingHandle : updatingFrontendHandles) {
             // update the exclusive group id member list
-            removeFrontendResource(removingId);
+            removeFrontendResource(removingHandle);
         }
     }
 
     @VisibleForTesting
-    protected void setLnbInfoListInternal(int[] lnbIds) {
+    protected void setLnbInfoListInternal(int[] lnbHandles) {
         if (DEBUG) {
-            for (int i = 0; i < lnbIds.length; i++) {
-                Slog.d(TAG, "updateLnbInfo(lnbId=" + lnbIds[i] + ")");
+            for (int i = 0; i < lnbHandles.length; i++) {
+                Slog.d(TAG, "updateLnbInfo(lnbHanle=" + lnbHandles[i] + ")");
             }
         }
 
-        // A set to record the Lnbs pending on updating. Ids will be removed
+        // A set to record the Lnbs pending on updating. Handles will be removed
         // from this set once its updating finished. Any lnb left in this set when all
         // the updates are done will be removed from mLnbResources.
-        Set<Integer> updatingLnbIds = new HashSet<>(getLnbResources().keySet());
+        Set<Integer> updatingLnbHandles = new HashSet<>(getLnbResources().keySet());
 
         // Update lnbResources map and other mappings accordingly
-        for (int i = 0; i < lnbIds.length; i++) {
-            if (getLnbResource(lnbIds[i]) != null) {
+        for (int i = 0; i < lnbHandles.length; i++) {
+            if (getLnbResource(lnbHandles[i]) != null) {
                 if (DEBUG) {
-                    Slog.d(TAG, "Lnb id=" + lnbIds[i] + "exists.");
+                    Slog.d(TAG, "Lnb handle=" + lnbHandles[i] + "exists.");
                 }
-                updatingLnbIds.remove(lnbIds[i]);
+                updatingLnbHandles.remove(lnbHandles[i]);
             } else {
                 // Add a new lnb resource
-                LnbResource newLnb = new LnbResource.Builder(lnbIds[i]).build();
+                LnbResource newLnb = new LnbResource.Builder(lnbHandles[i]).build();
                 addLnbResource(newLnb);
             }
         }
 
-        for (int removingId : updatingLnbIds) {
-            removeLnbResource(removingId);
+        for (int removingHandle : updatingLnbHandles) {
+            removeLnbResource(removingHandle);
         }
     }
 
@@ -613,28 +611,29 @@
 
         frontendHandle[0] = TunerResourceManager.INVALID_RESOURCE_HANDLE;
         ClientProfile requestClient = getClientProfile(request.getClientId());
-        int grantingFrontendId = -1;
-        int inUseLowestPriorityFrId = -1;
+        int grantingFrontendHandle = TunerResourceManager.INVALID_RESOURCE_HANDLE;
+        int inUseLowestPriorityFrHandle = TunerResourceManager.INVALID_RESOURCE_HANDLE;
         // Priority max value is 1000
         int currentLowestPriority = MAX_CLIENT_PRIORITY + 1;
         for (FrontendResource fr : getFrontendResources().values()) {
             if (fr.getType() == request.getFrontendType()) {
                 if (!fr.isInUse()) {
                     // Grant unused frontend with no exclusive group members first.
-                    if (fr.getExclusiveGroupMemberFeIds().isEmpty()) {
-                        grantingFrontendId = fr.getId();
+                    if (fr.getExclusiveGroupMemberFeHandles().isEmpty()) {
+                        grantingFrontendHandle = fr.getHandle();
                         break;
-                    } else if (grantingFrontendId < 0) {
+                    } else if (grantingFrontendHandle
+                            == TunerResourceManager.INVALID_RESOURCE_HANDLE) {
                         // Grant the unused frontend with lower id first if all the unused
                         // frontends have exclusive group members.
-                        grantingFrontendId = fr.getId();
+                        grantingFrontendHandle = fr.getHandle();
                     }
-                } else if (grantingFrontendId < 0) {
+                } else if (grantingFrontendHandle == TunerResourceManager.INVALID_RESOURCE_HANDLE) {
                     // Record the frontend id with the lowest client priority among all the
                     // in use frontends when no available frontend has been found.
                     int priority = getOwnerClientPriority(fr.getOwnerClientId());
                     if (currentLowestPriority > priority) {
-                        inUseLowestPriorityFrId = fr.getId();
+                        inUseLowestPriorityFrHandle = fr.getHandle();
                         currentLowestPriority = priority;
                     }
                 }
@@ -642,23 +641,24 @@
         }
 
         // Grant frontend when there is unused resource.
-        if (grantingFrontendId > -1) {
-            frontendHandle[0] = generateResourceHandle(
-                    TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND, grantingFrontendId);
-            updateFrontendClientMappingOnNewGrant(grantingFrontendId, request.getClientId());
+        if (grantingFrontendHandle != TunerResourceManager.INVALID_RESOURCE_HANDLE) {
+            frontendHandle[0] = grantingFrontendHandle;
+            updateFrontendClientMappingOnNewGrant(grantingFrontendHandle, request.getClientId());
             return true;
         }
 
         // When all the resources are occupied, grant the lowest priority resource if the
         // request client has higher priority.
-        if (inUseLowestPriorityFrId > -1 && (requestClient.getPriority() > currentLowestPriority)) {
-            if (!reclaimResource(getFrontendResource(inUseLowestPriorityFrId).getOwnerClientId(),
+        if (inUseLowestPriorityFrHandle != TunerResourceManager.INVALID_RESOURCE_HANDLE
+                && (requestClient.getPriority() > currentLowestPriority)) {
+            if (!reclaimResource(
+                    getFrontendResource(inUseLowestPriorityFrHandle).getOwnerClientId(),
                     TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND)) {
                 return false;
             }
-            frontendHandle[0] = generateResourceHandle(
-                    TunerResourceManager.TUNER_RESOURCE_TYPE_FRONTEND, inUseLowestPriorityFrId);
-            updateFrontendClientMappingOnNewGrant(inUseLowestPriorityFrId, request.getClientId());
+            frontendHandle[0] = inUseLowestPriorityFrHandle;
+            updateFrontendClientMappingOnNewGrant(
+                    inUseLowestPriorityFrHandle, request.getClientId());
             return true;
         }
 
@@ -670,7 +670,7 @@
         if (DEBUG) {
             Slog.d(TAG, "shareFrontend from " + selfClientId + " with " + targetClientId);
         }
-        for (int feId : getClientProfile(targetClientId).getInUseFrontendIds()) {
+        for (int feId : getClientProfile(targetClientId).getInUseFrontendHandles()) {
             getClientProfile(selfClientId).useFrontend(feId);
         }
         getClientProfile(targetClientId).shareFrontend(selfClientId);
@@ -684,45 +684,43 @@
 
         lnbHandle[0] = TunerResourceManager.INVALID_RESOURCE_HANDLE;
         ClientProfile requestClient = getClientProfile(request.getClientId());
-        int grantingLnbId = -1;
-        int inUseLowestPriorityLnbId = -1;
+        int grantingLnbHandle = TunerResourceManager.INVALID_RESOURCE_HANDLE;
+        int inUseLowestPriorityLnbHandle = TunerResourceManager.INVALID_RESOURCE_HANDLE;
         // Priority max value is 1000
         int currentLowestPriority = MAX_CLIENT_PRIORITY + 1;
         for (LnbResource lnb : getLnbResources().values()) {
             if (!lnb.isInUse()) {
-                // Grant the unused lnb with lower id first
-                grantingLnbId = lnb.getId();
+                // Grant the unused lnb with lower handle first
+                grantingLnbHandle = lnb.getHandle();
                 break;
             } else {
                 // Record the lnb id with the lowest client priority among all the
                 // in use lnb when no available lnb has been found.
                 int priority = getOwnerClientPriority(lnb.getOwnerClientId());
                 if (currentLowestPriority > priority) {
-                    inUseLowestPriorityLnbId = lnb.getId();
+                    inUseLowestPriorityLnbHandle = lnb.getHandle();
                     currentLowestPriority = priority;
                 }
             }
         }
 
         // Grant Lnb when there is unused resource.
-        if (grantingLnbId > -1) {
-            lnbHandle[0] = generateResourceHandle(
-                    TunerResourceManager.TUNER_RESOURCE_TYPE_LNB, grantingLnbId);
-            updateLnbClientMappingOnNewGrant(grantingLnbId, request.getClientId());
+        if (grantingLnbHandle > -1) {
+            lnbHandle[0] = grantingLnbHandle;
+            updateLnbClientMappingOnNewGrant(grantingLnbHandle, request.getClientId());
             return true;
         }
 
         // When all the resources are occupied, grant the lowest priority resource if the
         // request client has higher priority.
-        if (inUseLowestPriorityLnbId > -1
+        if (inUseLowestPriorityLnbHandle > TunerResourceManager.INVALID_RESOURCE_HANDLE
                 && (requestClient.getPriority() > currentLowestPriority)) {
-            if (!reclaimResource(getLnbResource(inUseLowestPriorityLnbId).getOwnerClientId(),
+            if (!reclaimResource(getLnbResource(inUseLowestPriorityLnbHandle).getOwnerClientId(),
                     TunerResourceManager.TUNER_RESOURCE_TYPE_LNB)) {
                 return false;
             }
-            lnbHandle[0] = generateResourceHandle(
-                    TunerResourceManager.TUNER_RESOURCE_TYPE_LNB, inUseLowestPriorityLnbId);
-            updateLnbClientMappingOnNewGrant(inUseLowestPriorityLnbId, request.getClientId());
+            lnbHandle[0] = inUseLowestPriorityLnbHandle;
+            updateLnbClientMappingOnNewGrant(inUseLowestPriorityLnbHandle, request.getClientId());
             return true;
         }
 
@@ -807,7 +805,7 @@
     @VisibleForTesting
     protected void releaseFrontendInternal(FrontendResource fe, int clientId) {
         if (DEBUG) {
-            Slog.d(TAG, "releaseFrontend(id=" + fe.getId() + ", clientId=" + clientId + " )");
+            Slog.d(TAG, "releaseFrontend(id=" + fe.getHandle() + ", clientId=" + clientId + " )");
         }
         if (clientId == fe.getOwnerClientId()) {
             ClientProfile ownerClient = getClientProfile(fe.getOwnerClientId());
@@ -821,7 +819,7 @@
     @VisibleForTesting
     protected void releaseLnbInternal(LnbResource lnb) {
         if (DEBUG) {
-            Slog.d(TAG, "releaseLnb(lnbId=" + lnb.getId() + ")");
+            Slog.d(TAG, "releaseLnb(lnbHandle=" + lnb.getHandle() + ")");
         }
         updateLnbClientMappingOnRelease(lnb);
     }
@@ -965,28 +963,28 @@
         return false;
     }
 
-    private void updateFrontendClientMappingOnNewGrant(int grantingId, int ownerClientId) {
-        FrontendResource grantingFrontend = getFrontendResource(grantingId);
+    private void updateFrontendClientMappingOnNewGrant(int grantingHandle, int ownerClientId) {
+        FrontendResource grantingFrontend = getFrontendResource(grantingHandle);
         ClientProfile ownerProfile = getClientProfile(ownerClientId);
         grantingFrontend.setOwner(ownerClientId);
-        ownerProfile.useFrontend(grantingId);
-        for (int exclusiveGroupMember : grantingFrontend.getExclusiveGroupMemberFeIds()) {
+        ownerProfile.useFrontend(grantingHandle);
+        for (int exclusiveGroupMember : grantingFrontend.getExclusiveGroupMemberFeHandles()) {
             getFrontendResource(exclusiveGroupMember).setOwner(ownerClientId);
             ownerProfile.useFrontend(exclusiveGroupMember);
         }
     }
 
-    private void updateLnbClientMappingOnNewGrant(int grantingId, int ownerClientId) {
-        LnbResource grantingLnb = getLnbResource(grantingId);
+    private void updateLnbClientMappingOnNewGrant(int grantingHandle, int ownerClientId) {
+        LnbResource grantingLnb = getLnbResource(grantingHandle);
         ClientProfile ownerProfile = getClientProfile(ownerClientId);
         grantingLnb.setOwner(ownerClientId);
-        ownerProfile.useLnb(grantingId);
+        ownerProfile.useLnb(grantingHandle);
     }
 
     private void updateLnbClientMappingOnRelease(@NonNull LnbResource releasingLnb) {
         ClientProfile ownerProfile = getClientProfile(releasingLnb.getOwnerClientId());
         releasingLnb.removeOwner();
-        ownerProfile.releaseLnb(releasingLnb.getId());
+        ownerProfile.releaseLnb(releasingLnb.getHandle());
     }
 
     private void updateCasClientMappingOnNewGrant(int grantingId, int ownerClientId) {
@@ -1015,8 +1013,8 @@
 
     @VisibleForTesting
     @Nullable
-    protected FrontendResource getFrontendResource(int frontendId) {
-        return mFrontendResources.get(frontendId);
+    protected FrontendResource getFrontendResource(int frontendHandle) {
+        return mFrontendResources.get(frontendHandle);
     }
 
     @VisibleForTesting
@@ -1028,22 +1026,22 @@
         // Update the exclusive group member list in all the existing Frontend resource
         for (FrontendResource fe : getFrontendResources().values()) {
             if (fe.getExclusiveGroupId() == newFe.getExclusiveGroupId()) {
-                newFe.addExclusiveGroupMemberFeId(fe.getId());
-                newFe.addExclusiveGroupMemberFeIds(fe.getExclusiveGroupMemberFeIds());
-                for (int excGroupmemberFeId : fe.getExclusiveGroupMemberFeIds()) {
-                    getFrontendResource(excGroupmemberFeId)
-                            .addExclusiveGroupMemberFeId(newFe.getId());
+                newFe.addExclusiveGroupMemberFeHandle(fe.getHandle());
+                newFe.addExclusiveGroupMemberFeHandles(fe.getExclusiveGroupMemberFeHandles());
+                for (int excGroupmemberFeHandle : fe.getExclusiveGroupMemberFeHandles()) {
+                    getFrontendResource(excGroupmemberFeHandle)
+                            .addExclusiveGroupMemberFeHandle(newFe.getHandle());
                 }
-                fe.addExclusiveGroupMemberFeId(newFe.getId());
+                fe.addExclusiveGroupMemberFeHandle(newFe.getHandle());
                 break;
             }
         }
         // Update resource list and available id list
-        mFrontendResources.put(newFe.getId(), newFe);
+        mFrontendResources.put(newFe.getHandle(), newFe);
     }
 
-    private void removeFrontendResource(int removingId) {
-        FrontendResource fe = getFrontendResource(removingId);
+    private void removeFrontendResource(int removingHandle) {
+        FrontendResource fe = getFrontendResource(removingHandle);
         if (fe == null) {
             return;
         }
@@ -1054,17 +1052,17 @@
             }
             clearFrontendAndClientMapping(ownerClient);
         }
-        for (int excGroupmemberFeId : fe.getExclusiveGroupMemberFeIds()) {
-            getFrontendResource(excGroupmemberFeId)
-                    .removeExclusiveGroupMemberFeId(fe.getId());
+        for (int excGroupmemberFeHandle : fe.getExclusiveGroupMemberFeHandles()) {
+            getFrontendResource(excGroupmemberFeHandle)
+                    .removeExclusiveGroupMemberFeId(fe.getHandle());
         }
-        mFrontendResources.remove(removingId);
+        mFrontendResources.remove(removingHandle);
     }
 
     @VisibleForTesting
     @Nullable
-    protected LnbResource getLnbResource(int lnbId) {
-        return mLnbResources.get(lnbId);
+    protected LnbResource getLnbResource(int lnbHandle) {
+        return mLnbResources.get(lnbHandle);
     }
 
     @VisibleForTesting
@@ -1074,18 +1072,18 @@
 
     private void addLnbResource(LnbResource newLnb) {
         // Update resource list and available id list
-        mLnbResources.put(newLnb.getId(), newLnb);
+        mLnbResources.put(newLnb.getHandle(), newLnb);
     }
 
-    private void removeLnbResource(int removingId) {
-        LnbResource lnb = getLnbResource(removingId);
+    private void removeLnbResource(int removingHandle) {
+        LnbResource lnb = getLnbResource(removingHandle);
         if (lnb == null) {
             return;
         }
         if (lnb.isInUse()) {
             releaseLnbInternal(lnb);
         }
-        mLnbResources.remove(removingId);
+        mLnbResources.remove(removingHandle);
     }
 
     @VisibleForTesting
@@ -1143,7 +1141,7 @@
     }
 
     private void clearFrontendAndClientMapping(ClientProfile profile) {
-        for (Integer feId : profile.getInUseFrontendIds()) {
+        for (Integer feId : profile.getInUseFrontendHandles()) {
             FrontendResource fe = getFrontendResource(feId);
             if (fe.getOwnerClientId() == profile.getId()) {
                 fe.removeOwner();
@@ -1156,8 +1154,8 @@
 
     private void clearAllResourcesAndClientMapping(ClientProfile profile) {
         // Clear Lnb
-        for (Integer lnbId : profile.getInUseLnbIds()) {
-            getLnbResource(lnbId).removeOwner();
+        for (Integer lnbHandle : profile.getInUseLnbHandles()) {
+            getLnbResource(lnbHandle).removeOwner();
         }
         // Clear Cas
         if (profile.getInUseCasSystemId() != ClientProfile.INVALID_RESOURCE_ID) {
diff --git a/services/core/java/com/android/server/utils/Watchable.java b/services/core/java/com/android/server/utils/Watchable.java
index 7c99274..f936693 100644
--- a/services/core/java/com/android/server/utils/Watchable.java
+++ b/services/core/java/com/android/server/utils/Watchable.java
@@ -18,6 +18,10 @@
 
 import android.annotation.NonNull;
 import android.annotation.Nullable;
+import android.os.Build;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
 
 /**
  * Notify registered {@link Watcher}s when the content changes.
@@ -41,6 +45,13 @@
     public void unregisterObserver(@NonNull Watcher observer);
 
     /**
+     * Return true if the {@link Watcher) is a registered observer.
+     * @param observer A {@link Watcher} that might be registered
+     * @return true if the observer is registered with this {@link Watchable}.
+     */
+    public boolean isRegisteredObserver(@NonNull Watcher observer);
+
+    /**
      * Invokes {@link Watcher#onChange} on each registered observer.  The method can be called
      * with the {@link Watchable} that generated the event.  In a tree of {@link Watchable}s, this
      * is generally the first (deepest) {@link Watchable} to detect a change.
@@ -48,4 +59,42 @@
      * @param what The {@link Watchable} that generated the event.
      */
     public void dispatchChange(@Nullable Watchable what);
+
+    /**
+     * Return true if the field is tagged with @Watched
+     */
+    private static boolean isWatched(Field f) {
+        for (Annotation a : f.getDeclaredAnnotations()) {
+            if (a.annotationType().equals(Watched.class)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Verify that all @Watched {@link Watchable} attributes are being watched by this
+     * class.  This requires reflection and only runs in engineering or user debug
+     * builds.
+     */
+    static void verifyWatchedAttributes(Object base, Watcher observer) {
+        if (Build.IS_ENG || Build.IS_USERDEBUG) {
+            for (Field f : base.getClass().getDeclaredFields()) {
+                try {
+                    final boolean flagged = isWatched(f);
+                    final Object o = f.get(base);
+                    final boolean watchable = o instanceof Watchable;
+                    if (flagged && watchable) {
+                        Watchable attr = (Watchable) f.get(base);
+                        if (attr != null && !attr.isRegisteredObserver(observer)) {
+                            throw new RuntimeException(f.getName() + " missing an observer");
+                        }
+                    }
+                } catch (IllegalAccessException e) {
+                    // The field is protected; ignore it.  Other exceptions that may be thrown by
+                    // Field.get() are allowed to roll up.
+                }
+            }
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/utils/WatchableImpl.java b/services/core/java/com/android/server/utils/WatchableImpl.java
index 16400b1..527db54 100644
--- a/services/core/java/com/android/server/utils/WatchableImpl.java
+++ b/services/core/java/com/android/server/utils/WatchableImpl.java
@@ -66,6 +66,18 @@
     }
 
     /**
+     * Return true if the {@link Watcher) is a registered observer.
+     * @param observer A {@link Watcher} that might be registered
+     * @return true if the observer is registered with this {@link Watchable}.
+     */
+    @Override
+    public boolean isRegisteredObserver(@NonNull Watcher observer) {
+        synchronized (mObservers) {
+            return mObservers.contains(observer);
+        }
+    }
+
+    /**
      * Return the number of registered observers.
      *
      * @return The number of registered observers.
@@ -100,7 +112,7 @@
 
     /**
      * Freeze the {@link Watchable}.
-     **/
+     */
     public void seal() {
         synchronized (mObservers) {
             mSealed = true;
diff --git a/services/core/java/com/android/server/utils/Watched.java b/services/core/java/com/android/server/utils/Watched.java
new file mode 100644
index 0000000..4340d01
--- /dev/null
+++ b/services/core/java/com/android/server/utils/Watched.java
@@ -0,0 +1,32 @@
+/*
+ * 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.utils;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation type to mark an attribute that is monitored for change detection and
+ * snapshot creation.
+ * TODO(b/176923052) Automate validation of @Watchable attributes.
+ */
+@Target({ ElementType.FIELD })
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Watched {
+}
diff --git a/services/core/java/com/android/server/utils/WatchedArrayList.java b/services/core/java/com/android/server/utils/WatchedArrayList.java
new file mode 100644
index 0000000..bb0ba13
--- /dev/null
+++ b/services/core/java/com/android/server/utils/WatchedArrayList.java
@@ -0,0 +1,416 @@
+/*
+ * 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.utils;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+/**
+ * WatchedArrayMap is an {@link android.util.ArrayMap} that can report changes to itself.  If its
+ * values are {@link Watchable} then the WatchedArrayMap will also report changes to the values.
+ * A {@link Watchable} is notified only once, no matter how many times it is stored in the array.
+ * @param <E> The element type, stored in the array.
+ */
+public class WatchedArrayList<E> extends WatchableImpl
+        implements Snappable {
+
+    // The storage
+    private final ArrayList<E> mStorage;
+
+    // If true, the array is watching its children
+    private volatile boolean mWatching = false;
+
+    // The local observer
+    private final Watcher mObserver = new Watcher() {
+            @Override
+            public void onChange(@Nullable Watchable what) {
+                WatchedArrayList.this.dispatchChange(what);
+            }
+        };
+
+    /**
+     * A convenience function called when the elements are added to or removed from the storage.
+     * The watchable is always {@link this}.
+     */
+    private void onChanged() {
+        dispatchChange(this);
+    }
+
+    /**
+     * A convenience function.  Register the object if it is {@link Watchable} and if the
+     * array is currently watching.  Note that the watching flag must be true if this
+     * function is to succeed.  Also note that if this is called with the same object
+     * twice, <this> is only registered once.
+     */
+    private void registerChild(Object o) {
+        if (mWatching && o instanceof Watchable) {
+            ((Watchable) o).registerObserver(mObserver);
+        }
+    }
+
+    /**
+     * A convenience function.  Unregister the object if it is {@link Watchable} and if the
+     * array is currently watching.  This unconditionally removes the object from the
+     * registered list.
+     */
+    private void unregisterChild(Object o) {
+        if (mWatching && o instanceof Watchable) {
+            ((Watchable) o).unregisterObserver(mObserver);
+        }
+    }
+
+    /**
+     * A convenience function.  Unregister the object if it is {@link Watchable}, if the
+     * array is currently watching, and if there are no other instances of this object in
+     * the storage.  Note that the watching flag must be true if this function is to
+     * succeed.  The object must already have been removed from the storage before this
+     * method is called.
+     */
+    private void unregisterChildIf(Object o) {
+        if (mWatching && o instanceof Watchable) {
+            if (!mStorage.contains(o)) {
+                ((Watchable) o).unregisterObserver(mObserver);
+            }
+        }
+    }
+
+    /**
+     * Register a {@link Watcher} with the array.  If this is the first Watcher than any
+     * array values that are {@link Watchable} are registered to the array itself.
+     */
+    @Override
+    public void registerObserver(@NonNull Watcher observer) {
+        super.registerObserver(observer);
+        if (registeredObserverCount() == 1) {
+            // The watching flag must be set true before any children are registered.
+            mWatching = true;
+            final int end = mStorage.size();
+            for (int i = 0; i < end; i++) {
+                registerChild(mStorage.get(i));
+            }
+        }
+    }
+
+    /**
+     * Unregister a {@link Watcher} from the array.  If this is the last Watcher than any
+     * array values that are {@link Watchable} are unregistered to the array itself.
+     */
+    @Override
+    public void unregisterObserver(@NonNull Watcher observer) {
+        super.unregisterObserver(observer);
+        if (registeredObserverCount() == 0) {
+            final int end = mStorage.size();
+            for (int i = 0; i < end; i++) {
+                unregisterChild(mStorage.get(i));
+            }
+            // The watching flag must be true while children are unregistered.
+            mWatching = false;
+        }
+    }
+
+    /**
+     * Create a new empty {@link WatchedArrayList}.  The default capacity of an array map
+     * is 0, and will grow once items are added to it.
+     */
+    public WatchedArrayList() {
+        this(0);
+    }
+
+    /**
+     * Create a new {@link WatchedArrayList} with a given initial capacity.
+     */
+    public WatchedArrayList(int capacity) {
+        mStorage = new ArrayList<E>(capacity);
+    }
+
+    /**
+     * Create a new {@link WatchedArrayList} with the content of the collection.
+     */
+    public WatchedArrayList(@Nullable Collection<? extends E> c) {
+        mStorage = new ArrayList<E>();
+        if (c != null) {
+            // There is no need to register children because the WatchedArrayList starts
+            // life unobserved.
+            mStorage.addAll(c);
+        }
+    }
+
+    /**
+     * Create a {@link WatchedArrayList} from an {@link ArrayList}
+     */
+    public WatchedArrayList(@NonNull ArrayList<E> c) {
+        mStorage = new ArrayList<>(c);
+    }
+
+    /**
+     * Create a {@link WatchedArrayList} from an {@link WatchedArrayList}
+     */
+    public WatchedArrayList(@NonNull WatchedArrayList<E> c) {
+        mStorage = new ArrayList<>(c.mStorage);
+    }
+
+    /**
+     * Make <this> a copy of src.  Any data in <this> is discarded.
+     */
+    public void copyFrom(@NonNull ArrayList<E> src) {
+        clear();
+        final int end = src.size();
+        mStorage.ensureCapacity(end);
+        for (int i = 0; i < end; i++) {
+            add(src.get(i));
+        }
+    }
+
+    /**
+     * Make dst a copy of <this>.  Any previous data in dst is discarded.
+     */
+    public void copyTo(@NonNull ArrayList<E> dst) {
+        dst.clear();
+        final int end = size();
+        dst.ensureCapacity(end);
+        for (int i = 0; i < end; i++) {
+            dst.add(get(i));
+        }
+    }
+
+    /**
+     * Return the underlying storage.  This breaks the wrapper but is necessary when
+     * passing the array to distant methods.
+     */
+    public ArrayList<E> untrackedStorage() {
+        return mStorage;
+    }
+
+    /**
+     * Append the specified element to the end of the list
+     */
+    public boolean add(E value) {
+        final boolean result = mStorage.add(value);
+        registerChild(value);
+        onChanged();
+        return result;
+    }
+
+    /**
+     * Insert the element into the list
+     */
+    public void add(int index, E value) {
+        mStorage.add(index, value);
+        registerChild(value);
+        onChanged();
+    }
+
+    /**
+     * Append the elements of the collection to the list.
+     */
+    public boolean addAll(Collection<? extends E> c) {
+        if (c.size() > 0) {
+            for (E e: c) {
+                mStorage.add(e);
+            }
+            onChanged();
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * Insert the elements of the collection into the list at the index.
+     */
+    public boolean addAll(int index, Collection<? extends E> c) {
+        if (c.size() > 0) {
+            for (E e: c) {
+                mStorage.add(index++, e);
+            }
+            onChanged();
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+
+    /**
+     * Remove all elements from the list.
+     */
+    public void clear() {
+        // The storage cannot be simply cleared.  Each element in the storage must be
+        // unregistered.  Deregistration is only needed if the array is actually
+        // watching.
+        if (mWatching) {
+            final int end = mStorage.size();
+            for (int i = 0; i < end; i++) {
+                unregisterChild(mStorage.get(i));
+            }
+        }
+        mStorage.clear();
+        onChanged();
+    }
+
+    /**
+     * Return true if the object is in the array.
+     */
+    public boolean contains(Object o) {
+        return mStorage.contains(o);
+    }
+
+    /**
+     * Ensure capacity.
+     */
+    public void ensureCapacity(int min) {
+        mStorage.ensureCapacity(min);
+    }
+
+    /**
+     * Retrieve the element at the specified index.
+     */
+    public E get(int index) {
+        return mStorage.get(index);
+    }
+
+    /**
+     * Return the index of the object.  -1 is returned if the object is not in the list.
+     */
+    public int indexOf(Object o) {
+        return mStorage.indexOf(o);
+    }
+
+    /**
+     * True if the list has no elements
+     */
+    public boolean isEmpty() {
+        return mStorage.isEmpty();
+    }
+
+    /**
+     * Return the index of the last occurrence of the object.
+     */
+    public int lastIndexOf(Object o) {
+        return mStorage.lastIndexOf(o);
+    }
+
+    /**
+     * Remove and return the element at the specified position.
+     */
+    public E remove(int index) {
+        final E result = mStorage.remove(index);
+        unregisterChildIf(result);
+        onChanged();
+        return result;
+    }
+
+    /**
+     * Remove the first occurrence of the object in the list.  Return true if the object
+     * was actually in the list and false otherwise.
+     */
+    public boolean remove(Object o) {
+        if (mStorage.remove(o)) {
+            unregisterChildIf(o);
+            onChanged();
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Replace the object at the index.
+     */
+    public E set(int index, E value) {
+        final E result = mStorage.set(index, value);
+        if (value != result) {
+            unregisterChildIf(result);
+            registerChild(value);
+            onChanged();
+        }
+        return result;
+    }
+
+    /**
+     * Return the number of elements in the list.
+     */
+    public int size() {
+        return mStorage.size();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean equals(@Nullable Object o) {
+        if (o instanceof WatchedArrayList) {
+            WatchedArrayList w = (WatchedArrayList) o;
+            return mStorage.equals(w.mStorage);
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int hashCode() {
+        return mStorage.hashCode();
+    }
+
+    /**
+     * Create a copy of the array.  If the element is a subclass of Snapper then the copy
+     * contains snapshots of the elements.  Otherwise the copy contains references to the
+     * elements.  The returned snapshot is immutable.
+     * @return A new array whose elements are the elements of <this>.
+     */
+    public WatchedArrayList<E> snapshot() {
+        WatchedArrayList<E> l = new WatchedArrayList<>(size());
+        snapshot(l, this);
+        return l;
+    }
+
+    /**
+     * Make <this> a snapshot of the argument.  Note that <this> is immutable when the
+     * method returns.  <this> must be empty when the function is called.
+     * @param r The source array, which is copied into <this>
+     */
+    public void snapshot(@NonNull WatchedArrayList<E> r) {
+        snapshot(this, r);
+    }
+
+    /**
+     * Make the destination a copy of the source.  If the element is a subclass of Snapper then the
+     * copy contains snapshots of the elements.  Otherwise the copy contains references to the
+     * elements.  The destination must be initially empty.  Upon return, the destination is
+     * immutable.
+     * @param dst The destination array.  It must be empty.
+     * @param src The source array.  It is not modified.
+     */
+    public static <E> void snapshot(@NonNull WatchedArrayList<E> dst,
+            @NonNull WatchedArrayList<E> src) {
+        if (dst.size() != 0) {
+            throw new IllegalArgumentException("snapshot destination is not empty");
+        }
+        final int end = src.size();
+        dst.mStorage.ensureCapacity(end);
+        for (int i = 0; i < end; i++) {
+            final E val = Snapshots.maybeSnapshot(src.get(i));
+            dst.add(i, val);
+        }
+        dst.seal();
+    }
+}
diff --git a/services/core/java/com/android/server/utils/WatchedArrayMap.java b/services/core/java/com/android/server/utils/WatchedArrayMap.java
index e8065f1..7c1cde8 100644
--- a/services/core/java/com/android/server/utils/WatchedArrayMap.java
+++ b/services/core/java/com/android/server/utils/WatchedArrayMap.java
@@ -160,10 +160,48 @@
     }
 
     /**
+     * Create a {@link WatchedArrayMap} from an {@link ArrayMap}
+     */
+    public WatchedArrayMap(@NonNull ArrayMap<K, V> c) {
+        mStorage = new ArrayMap<>(c);
+    }
+
+    /**
+     * Create a {@link WatchedArrayMap} from an {@link WatchedArrayMap}
+     */
+    public WatchedArrayMap(@NonNull WatchedArrayMap<K, V> c) {
+        mStorage = new ArrayMap<>(c.mStorage);
+    }
+
+    /**
+     * Make <this> a copy of src.  Any data in <this> is discarded.
+     */
+    public void copyFrom(@NonNull ArrayMap<K, V> src) {
+        clear();
+        final int end = src.size();
+        mStorage.ensureCapacity(end);
+        for (int i = 0; i < end; i++) {
+            put(src.keyAt(i), src.valueAt(i));
+        }
+    }
+
+    /**
+     * Make dst a copy of <this>.  Any previous data in dst is discarded.
+     */
+    public void copyTo(@NonNull ArrayMap<K, V> dst) {
+        dst.clear();
+        final int end = size();
+        dst.ensureCapacity(end);
+        for (int i = 0; i < end; i++) {
+            dst.put(keyAt(i), valueAt(i));
+        }
+    }
+
+    /**
      * Return the underlying storage.  This breaks the wrapper but is necessary when
      * passing the array to distant methods.
      */
-    public ArrayMap untrackedMap() {
+    public ArrayMap<K, V> untrackedStorage() {
         return mStorage;
     }
 
@@ -213,7 +251,7 @@
      * {@inheritDoc}
      */
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(@Nullable Object o) {
         if (o instanceof WatchedArrayMap) {
             WatchedArrayMap w = (WatchedArrayMap) o;
             return mStorage.equals(w.mStorage);
@@ -401,6 +439,15 @@
     }
 
     /**
+     * Make <this> a snapshot of the argument.  Note that <this> is immutable when the
+     * method returns.  <this> must be empty when the function is called.
+     * @param r The source array, which is copied into <this>
+     */
+    public void snapshot(@NonNull WatchedArrayMap<K, V> r) {
+        snapshot(this, r);
+    }
+
+    /**
      * Make the destination a copy of the source.  If the element is a subclass of Snapper then the
      * copy contains snapshots of the elements.  Otherwise the copy contains references to the
      * elements.  The destination must be initially empty.  Upon return, the destination is
@@ -414,6 +461,7 @@
             throw new IllegalArgumentException("snapshot destination is not empty");
         }
         final int end = src.size();
+        dst.mStorage.ensureCapacity(end);
         for (int i = 0; i < end; i++) {
             final V val = Snapshots.maybeSnapshot(src.valueAt(i));
             final K key = src.keyAt(i);
diff --git a/services/core/java/com/android/server/utils/WatchedArraySet.java b/services/core/java/com/android/server/utils/WatchedArraySet.java
new file mode 100644
index 0000000..5070dd1
--- /dev/null
+++ b/services/core/java/com/android/server/utils/WatchedArraySet.java
@@ -0,0 +1,434 @@
+/*
+ * 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.utils;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.ArraySet;
+
+/**
+ * WatchedArraySet is an {@link android.util.ArraySet} that can report changes to itself.  If its
+ * values are {@link Watchable} then the WatchedArraySet will also report changes to the values.
+ * A {@link Watchable} is notified only once, no matter how many times it is stored in the array.
+ * @param <E> The element type
+ */
+public class WatchedArraySet<E> extends WatchableImpl
+        implements Snappable {
+
+    // The storage
+    private final ArraySet<E> mStorage;
+
+    // If true, the array is watching its children
+    private volatile boolean mWatching = false;
+
+    // The local observer
+    private final Watcher mObserver = new Watcher() {
+            @Override
+            public void onChange(@Nullable Watchable what) {
+                WatchedArraySet.this.dispatchChange(what);
+            }
+        };
+
+    /**
+     * A convenience function called when the elements are added to or removed from the storage.
+     * The watchable is always {@link this}.
+     */
+    private void onChanged() {
+        dispatchChange(this);
+    }
+
+    /**
+     * A convenience function.  Register the object if it is {@link Watchable} and if the
+     * array is currently watching.  Note that the watching flag must be true if this
+     * function is to succeed.  Also note that if this is called with the same object
+     * twice, <this> is only registered once.
+     */
+    private void registerChild(Object o) {
+        if (mWatching && o instanceof Watchable) {
+            ((Watchable) o).registerObserver(mObserver);
+        }
+    }
+
+    /**
+     * A convenience function.  Unregister the object if it is {@link Watchable} and if the
+     * array is currently watching.  This unconditionally removes the object from the
+     * registered list.
+     */
+    private void unregisterChild(Object o) {
+        if (mWatching && o instanceof Watchable) {
+            ((Watchable) o).unregisterObserver(mObserver);
+        }
+    }
+
+    /**
+     * A convenience function.  Unregister the object if it is {@link Watchable}, if the
+     * array is currently watching, and if there are no other instances of this object in
+     * the storage.  Note that the watching flag must be true if this function is to
+     * succeed.  The object must already have been removed from the storage before this
+     * method is called.
+     */
+    private void unregisterChildIf(Object o) {
+        if (mWatching && o instanceof Watchable) {
+            if (!mStorage.contains(o)) {
+                ((Watchable) o).unregisterObserver(mObserver);
+            }
+        }
+    }
+
+    /**
+     * Register a {@link Watcher} with the array.  If this is the first Watcher than any
+     * array values that are {@link Watchable} are registered to the array itself.
+     */
+    @Override
+    public void registerObserver(@NonNull Watcher observer) {
+        super.registerObserver(observer);
+        if (registeredObserverCount() == 1) {
+            // The watching flag must be set true before any children are registered.
+            mWatching = true;
+            final int end = mStorage.size();
+            for (int i = 0; i < end; i++) {
+                registerChild(mStorage.valueAt(i));
+            }
+        }
+    }
+
+    /**
+     * Unregister a {@link Watcher} from the array.  If this is the last Watcher than any
+     * array values that are {@link Watchable} are unregistered to the array itself.
+     */
+    @Override
+    public void unregisterObserver(@NonNull Watcher observer) {
+        super.unregisterObserver(observer);
+        if (registeredObserverCount() == 0) {
+            final int end = mStorage.size();
+            for (int i = 0; i < end; i++) {
+                unregisterChild(mStorage.valueAt(i));
+            }
+            // The watching flag must be true while children are unregistered.
+            mWatching = false;
+        }
+    }
+
+    /**
+     * Create a new empty {@link WatchedArraySet}.  The default capacity of an array map
+     * is 0, and will grow once items are added to it.
+     */
+    public WatchedArraySet() {
+        this(0, false);
+    }
+
+    /**
+     * Create a new {@link WatchedArraySet} with a given initial capacity.
+     */
+    public WatchedArraySet(int capacity) {
+        this(capacity, false);
+    }
+
+    /** {@hide} */
+    public WatchedArraySet(int capacity, boolean identityHashCode) {
+        mStorage = new ArraySet<E>(capacity, identityHashCode);
+    }
+
+    /**
+     * Create a new {@link WatchedArraySet} with items from the given array
+     */
+    public WatchedArraySet(@Nullable E[] array) {
+        mStorage = new ArraySet(array);
+    }
+
+    /**
+     * Create a {@link WatchedArraySet} from an {@link ArraySet}
+     */
+    public WatchedArraySet(@NonNull ArraySet<E> c) {
+        mStorage = new ArraySet<>(c);
+    }
+
+    /**
+     * Create a {@link WatchedArraySet} from an {@link WatchedArraySet}
+     */
+    public WatchedArraySet(@NonNull WatchedArraySet<E> c) {
+        mStorage = new ArraySet<>(c.mStorage);
+    }
+
+    /**
+     * Make <this> a copy of src.  Any data in <this> is discarded.
+     */
+    public void copyFrom(@NonNull ArraySet<E> src) {
+        clear();
+        final int end = src.size();
+        mStorage.ensureCapacity(end);
+        for (int i = 0; i < end; i++) {
+            add(src.valueAt(i));
+        }
+    }
+
+    /**
+     * Make dst a copy of <this>.  Any previous data in dst is discarded.
+     */
+    public void copyTo(@NonNull ArraySet<E> dst) {
+        dst.clear();
+        final int end = size();
+        dst.ensureCapacity(end);
+        for (int i = 0; i < end; i++) {
+            dst.add(valueAt(i));
+        }
+    }
+
+    /**
+     * Return the underlying storage.  This breaks the wrapper but is necessary when
+     * passing the array to distant methods.
+     */
+    public ArraySet<E> untrackedStorage() {
+        return mStorage;
+    }
+
+    /**
+     * Make the array map empty.  All storage is released.
+     */
+    public void clear() {
+        // The storage cannot be simply cleared.  Each element in the storage must be
+        // unregistered.  Deregistration is only needed if the array is actually
+        // watching.
+        if (mWatching) {
+            final int end = mStorage.size();
+            for (int i = 0; i < end; i++) {
+                unregisterChild(mStorage.valueAt(i));
+            }
+        }
+        mStorage.clear();
+        onChanged();
+    }
+
+    /**
+     * Check whether a value exists in the set.
+     *
+     * @param key The value to search for.
+     * @return Returns true if the value exists, else false.
+     */
+    public boolean contains(Object key) {
+        return mStorage.contains(key);
+    }
+
+    /**
+     * Returns the index of a value in the set.
+     *
+     * @param key The value to search for.
+     * @return Returns the index of the value if it exists, else a negative integer.
+     */
+    public int indexOf(Object key) {
+        return mStorage.indexOf(key);
+    }
+
+    /**
+     * Return the value at the given index in the array.
+     *
+     * <p>For indices outside of the range <code>0...size()-1</code>, an
+     * {@link ArrayIndexOutOfBoundsException} is thrown.</p>
+     *
+     * @param index The desired index, must be between 0 and {@link #size()}-1.
+     * @return Returns the value stored at the given index.
+     */
+    public E valueAt(int index) {
+        return mStorage.valueAt(index);
+    }
+
+    /**
+     * Return true if the array map contains no items.
+     */
+    public boolean isEmpty() {
+        return mStorage.isEmpty();
+    }
+
+    /**
+     * Adds the specified object to this set. The set is not modified if it
+     * already contains the object.
+     *
+     * @param value the object to add.
+     * @return {@code true} if this set is modified, {@code false} otherwise.
+     */
+    public boolean add(E value) {
+        final boolean result = mStorage.add(value);
+        registerChild(value);
+        onChanged();
+        return result;
+    }
+
+    /**
+     * Special fast path for appending items to the end of the array without validation.
+     * The array must already be large enough to contain the item.
+     * @hide
+     */
+    public void append(E value) {
+        mStorage.append(value);
+        registerChild(value);
+        onChanged();
+    }
+
+    /**
+     * Perform a {@link #add(Object)} of all values in <var>array</var>
+     * @param array The array whose contents are to be retrieved.
+     */
+    public void addAll(ArraySet<? extends E> array) {
+        final int end = array.size();
+        for (int i = 0; i < end; i++) {
+            add(array.valueAt(i));
+        }
+    }
+
+    /**
+     * Perform a {@link #add(Object)} of all values in <var>array</var>
+     * @param array The array whose contents are to be retrieved.
+     */
+    public void addAll(WatchedArraySet<? extends E> array) {
+        final int end = array.size();
+        for (int i = 0; i < end; i++) {
+            add(array.valueAt(i));
+        }
+    }
+
+    /**
+     * Removes the specified object from this set.
+     *
+     * @param o the object to remove.
+     * @return {@code true} if this set was modified, {@code false} otherwise.
+     */
+    public boolean remove(Object o) {
+        if (mStorage.remove(o)) {
+            unregisterChildIf(o);
+            onChanged();
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Remove the key/value mapping at the given index.
+     *
+     * <p>For indices outside of the range <code>0...size()-1</code>, an
+     * {@link ArrayIndexOutOfBoundsException} is thrown.</p>
+     *
+     * @param index The desired index, must be between 0 and {@link #size()}-1.
+     * @return Returns the value that was stored at this index.
+     */
+    public E removeAt(int index) {
+        final E result = mStorage.removeAt(index);
+        unregisterChildIf(result);
+        onChanged();
+        return result;
+    }
+
+    /**
+     * Perform a {@link #remove(Object)} of all values in <var>array</var>
+     * @param array The array whose contents are to be removed.
+     */
+    public boolean removeAll(ArraySet<? extends E> array) {
+        final int end = array.size();
+        boolean any = false;
+        for (int i = 0; i < end; i++) {
+            any = remove(array.valueAt(i)) || any;
+        }
+        return any;
+    }
+
+    /**
+     * Return the number of items in this array map.
+     */
+    public int size() {
+        return mStorage.size();
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * <p>This implementation returns false if the object is not a set, or
+     * if the sets have different sizes.  Otherwise, for each value in this
+     * set, it checks to make sure the value also exists in the other set.
+     * If any value doesn't exist, the method returns false; otherwise, it
+     * returns true.
+     */
+    @Override
+    public boolean equals(@Nullable Object object) {
+        if (object instanceof WatchedArraySet) {
+            return mStorage.equals(((WatchedArraySet) object).mStorage);
+        } else {
+            return mStorage.equals(object);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int hashCode() {
+        return mStorage.hashCode();
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * <p>This implementation composes a string by iterating over its values. If
+     * this set contains itself as a value, the string "(this Set)"
+     * will appear in its place.
+     */
+    @Override
+    public String toString() {
+        return mStorage.toString();
+    }
+
+    /**
+     * Create a copy of the array.  If the element is a subclass of Snapper then the copy
+     * contains snapshots of the elements.  Otherwise the copy contains references to the
+     * elements.  The returned snapshot is immutable.
+     * @return A new array whose elements are the elements of <this>.
+     */
+    public WatchedArraySet<E> snapshot() {
+        WatchedArraySet<E> l = new WatchedArraySet<>();
+        snapshot(l, this);
+        return l;
+    }
+
+    /**
+     * Make <this> a snapshot of the argument.  Note that <this> is immutable when the
+     * method returns.  <this> must be empty when the function is called.
+     * @param r The source array, which is copied into <this>
+     */
+    public void snapshot(@NonNull WatchedArraySet<E> r) {
+        snapshot(this, r);
+    }
+
+    /**
+     * Make the destination a copy of the source.  If the element is a subclass of Snapper then the
+     * copy contains snapshots of the elements.  Otherwise the copy contains references to the
+     * elements.  The destination must be initially empty.  Upon return, the destination is
+     * immutable.
+     * @param dst The destination array.  It must be empty.
+     * @param src The source array.  It is not modified.
+     */
+    public static <E> void snapshot(@NonNull WatchedArraySet<E> dst,
+            @NonNull WatchedArraySet<E> src) {
+        if (dst.size() != 0) {
+            throw new IllegalArgumentException("snapshot destination is not empty");
+        }
+        final int end = src.size();
+        dst.mStorage.ensureCapacity(end);
+        for (int i = 0; i < end; i++) {
+            final E val = Snapshots.maybeSnapshot(src.valueAt(i));
+            dst.append(val);
+        }
+        dst.seal();
+    }
+}
diff --git a/services/core/java/com/android/server/utils/WatchedSparseArray.java b/services/core/java/com/android/server/utils/WatchedSparseArray.java
index 6797c6e..9b99b91 100644
--- a/services/core/java/com/android/server/utils/WatchedSparseArray.java
+++ b/services/core/java/com/android/server/utils/WatchedSparseArray.java
@@ -143,6 +143,13 @@
     }
 
     /**
+     * Create a {@link WatchedSparseArray} from a {@link SparseArray}
+     */
+    public WatchedSparseArray(@NonNull SparseArray<E> c) {
+        mStorage = c.clone();
+    }
+
+    /**
      * The copy constructor does not copy the watcher data.
      */
     public WatchedSparseArray(@NonNull WatchedSparseArray<E> r) {
@@ -150,6 +157,36 @@
     }
 
     /**
+     * Make <this> a copy of src.  Any data in <this> is discarded.
+     */
+    public void copyFrom(@NonNull SparseArray<E> src) {
+        clear();
+        final int end = src.size();
+        for (int i = 0; i < end; i++) {
+            put(src.keyAt(i), src.valueAt(i));
+        }
+    }
+
+    /**
+     * Make dst a copy of <this>.  Any previous data in dst is discarded.
+     */
+    public void copyTo(@NonNull SparseArray<E> dst) {
+        dst.clear();
+        final int end = size();
+        for (int i = 0; i < end; i++) {
+            dst.put(keyAt(i), valueAt(i));
+        }
+    }
+
+    /**
+     * Return the underlying storage.  This breaks the wrapper but is necessary when
+     * passing the array to distant methods.
+     */
+    public SparseArray<E> untrackedStorage() {
+        return mStorage;
+    }
+
+    /**
      * Returns true if the key exists in the array. This is equivalent to
      * {@link #indexOfKey(int)} >= 0.
      *
@@ -390,6 +427,21 @@
         onChanged();
     }
 
+    @Override
+    public int hashCode() {
+        return mStorage.hashCode();
+    }
+
+    @Override
+    public boolean equals(@Nullable Object o) {
+        if (o instanceof WatchedSparseArray) {
+            WatchedSparseArray w = (WatchedSparseArray) o;
+            return mStorage.equals(w.mStorage);
+        } else {
+            return false;
+        }
+    }
+
     /**
      * <p>This implementation composes a string by iterating over its mappings. If
      * this map contains itself as a value, the string "(this Map)"
@@ -407,12 +459,21 @@
      * @return A new array whose elements are the elements of <this>.
      */
     public WatchedSparseArray<E> snapshot() {
-        WatchedSparseArray<E> l = new WatchedSparseArray<>();
+        WatchedSparseArray<E> l = new WatchedSparseArray<>(size());
         snapshot(l, this);
         return l;
     }
 
     /**
+     * Make <this> a snapshot of the argument.  Note that <this> is immutable when the
+     * method returns.  <this> must be empty when the function is called.
+     * @param r The source array, which is copied into <this>
+     */
+    public void snapshot(@NonNull WatchedSparseArray<E> r) {
+        snapshot(this, r);
+    }
+
+    /**
      * Make the destination a copy of the source.  If the element is a subclass of Snapper then the
      * copy contains snapshots of the elements.  Otherwise the copy contains references to the
      * elements.  The destination must be initially empty.  Upon return, the destination is
diff --git a/services/core/java/com/android/server/utils/WatchedSparseBooleanArray.java b/services/core/java/com/android/server/utils/WatchedSparseBooleanArray.java
index b845eea..772a8d0 100644
--- a/services/core/java/com/android/server/utils/WatchedSparseBooleanArray.java
+++ b/services/core/java/com/android/server/utils/WatchedSparseBooleanArray.java
@@ -17,6 +17,7 @@
 package com.android.server.utils;
 
 import android.annotation.NonNull;
+import android.annotation.Nullable;
 import android.util.SparseBooleanArray;
 
 /**
@@ -53,6 +54,13 @@
     }
 
     /**
+     * Create a {@link WatchedSparseBooleanArray} from a {@link SparseBooleanArray}
+     */
+    public WatchedSparseBooleanArray(@NonNull SparseBooleanArray c) {
+        mStorage = c.clone();
+    }
+
+    /**
      * The copy constructor does not copy the watcher data.
      */
     public WatchedSparseBooleanArray(@NonNull WatchedSparseBooleanArray r) {
@@ -60,6 +68,36 @@
     }
 
     /**
+     * Make <this> a copy of src.  Any data in <this> is discarded.
+     */
+    public void copyFrom(@NonNull SparseBooleanArray src) {
+        clear();
+        final int end = src.size();
+        for (int i = 0; i < end; i++) {
+            put(src.keyAt(i), src.valueAt(i));
+        }
+    }
+
+    /**
+     * Make dst a copy of <this>.  Any previous data in dst is discarded.
+     */
+    public void copyTo(@NonNull SparseBooleanArray dst) {
+        dst.clear();
+        final int end = size();
+        for (int i = 0; i < end; i++) {
+            dst.put(keyAt(i), valueAt(i));
+        }
+    }
+
+    /**
+     * Return the underlying storage.  This breaks the wrapper but is necessary when
+     * passing the array to distant methods.
+     */
+    public SparseBooleanArray untrackedStorage() {
+        return mStorage;
+    }
+
+    /**
      * Gets the boolean mapped from the specified key, or <code>false</code>
      * if no such mapping has been made.
      */
@@ -99,10 +137,10 @@
      * was one.
      */
     public void put(int key, boolean value) {
-        if (mStorage.get(key) != value) {
-            mStorage.put(key, value);
-            onChanged();
-        }
+        // There is no fast way to know if the key exists with the input value, so this
+        // method always notifies change listeners.
+        mStorage.put(key, value);
+        onChanged();
     }
 
     /**
@@ -219,8 +257,13 @@
     }
 
     @Override
-    public boolean equals(Object that) {
-        return this == that || mStorage.equals(that);
+    public boolean equals(@Nullable Object o) {
+        if (o instanceof WatchedSparseBooleanArray) {
+            WatchedSparseBooleanArray w = (WatchedSparseBooleanArray) o;
+            return mStorage.equals(w.mStorage);
+        } else {
+            return false;
+        }
     }
 
     /**
@@ -249,13 +292,26 @@
      * @param r The source array, which is copied into <this>
      */
     public void snapshot(@NonNull WatchedSparseBooleanArray r) {
-        if (size() != 0) {
+        snapshot(this, r);
+    }
+
+    /**
+     * Make the destination a copy of the source.  If the element is a subclass of Snapper then the
+     * copy contains snapshots of the elements.  Otherwise the copy contains references to the
+     * elements.  The destination must be initially empty.  Upon return, the destination is
+     * immutable.
+     * @param dst The destination array.  It must be empty.
+     * @param src The source array.  It is not modified.
+     */
+    public static void snapshot(@NonNull WatchedSparseBooleanArray dst,
+            @NonNull WatchedSparseBooleanArray src) {
+        if (dst.size() != 0) {
             throw new IllegalArgumentException("snapshot destination is not empty");
         }
-        final int end = r.size();
+        final int end = src.size();
         for (int i = 0; i < end; i++) {
-            put(r.keyAt(i), r.valueAt(i));
+            dst.put(src.keyAt(i), src.valueAt(i));
         }
-        seal();
+        dst.seal();
     }
 }
diff --git a/services/core/java/com/android/server/utils/WatchedSparseIntArray.java b/services/core/java/com/android/server/utils/WatchedSparseIntArray.java
new file mode 100644
index 0000000..72705bf
--- /dev/null
+++ b/services/core/java/com/android/server/utils/WatchedSparseIntArray.java
@@ -0,0 +1,323 @@
+/*
+ * 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.utils;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.SparseIntArray;
+
+/**
+ * A watched variant of SparseIntArray.  Changes to the array are notified to
+ * registered {@link Watcher}s.
+ */
+public class WatchedSparseIntArray extends WatchableImpl
+        implements Snappable {
+
+    // The storage
+    private final SparseIntArray mStorage;
+
+    // A private convenience function
+    private void onChanged() {
+        dispatchChange(this);
+    }
+
+    /**
+     * Creates a new WatchedSparseIntArray containing no mappings.
+     */
+    public WatchedSparseIntArray() {
+        mStorage = new SparseIntArray();
+    }
+
+    /**
+     * Creates a new WatchedSparseIntArray containing no mappings that
+     * will not require any additional memory allocation to store the
+     * specified number of mappings.  If you supply an initial capacity of
+     * 0, the sparse array will be initialized with a light-weight
+     * representation not requiring any additional array allocations.
+     */
+    public WatchedSparseIntArray(int initialCapacity) {
+        mStorage = new SparseIntArray(initialCapacity);
+    }
+
+    /**
+     * Create a {@link WatchedSparseIntArray} from a {@link SparseIntArray}
+     */
+    public WatchedSparseIntArray(@NonNull SparseIntArray c) {
+        mStorage = c.clone();
+    }
+
+    /**
+     * The copy constructor does not copy the watcher data.
+     */
+    public WatchedSparseIntArray(@NonNull WatchedSparseIntArray r) {
+        mStorage = r.mStorage.clone();
+    }
+
+    /**
+     * Make <this> a copy of src.  Any data in <this> is discarded.
+     */
+    public void copyFrom(@NonNull SparseIntArray src) {
+        clear();
+        final int end = src.size();
+        for (int i = 0; i < end; i++) {
+            put(src.keyAt(i), src.valueAt(i));
+        }
+    }
+
+    /**
+     * Make dst a copy of <this>.  Any previous data in dst is discarded.
+     */
+    public void copyTo(@NonNull SparseIntArray dst) {
+        dst.clear();
+        final int end = size();
+        for (int i = 0; i < end; i++) {
+            dst.put(keyAt(i), valueAt(i));
+        }
+    }
+
+    /**
+     * Return the underlying storage.  This breaks the wrapper but is necessary when
+     * passing the array to distant methods.
+     */
+    public SparseIntArray untrackedStorage() {
+        return mStorage;
+    }
+
+    /**
+     * Gets the boolean mapped from the specified key, or <code>false</code>
+     * if no such mapping has been made.
+     */
+    public int get(int key) {
+        return mStorage.get(key);
+    }
+
+    /**
+     * Gets the boolean mapped from the specified key, or the specified value
+     * if no such mapping has been made.
+     */
+    public int get(int key, int valueIfKeyNotFound) {
+        return mStorage.get(key, valueIfKeyNotFound);
+    }
+
+    /**
+     * Removes the mapping from the specified key, if there was any.
+     */
+    public void delete(int key) {
+        // This code ensures that onChanged is called only if the key is actually
+        // present.
+        final int index = mStorage.indexOfKey(key);
+        if (index >= 0) {
+            mStorage.removeAt(index);
+            onChanged();
+        }
+    }
+
+    /**
+     * Removes the mapping at the specified index.
+     */
+    public void removeAt(int index) {
+        mStorage.removeAt(index);
+        onChanged();
+    }
+
+    /**
+     * Adds a mapping from the specified key to the specified value,
+     * replacing the previous mapping from the specified key if there
+     * was one.
+     */
+    public void put(int key, int value) {
+        // There is no fast way to know if the key exists with the input value, so this
+        // method always notifies change listeners.
+        mStorage.put(key, value);
+        onChanged();
+    }
+
+    /**
+     * Returns the number of key-value mappings that this SparseIntArray
+     * currently stores.
+     */
+    public int size() {
+        return mStorage.size();
+    }
+
+    /**
+     * Given an index in the range <code>0...size()-1</code>, returns
+     * the key from the <code>index</code>th key-value mapping that this
+     * SparseIntArray stores.
+     *
+     * <p>The keys corresponding to indices in ascending order are guaranteed to
+     * be in ascending order, e.g., <code>keyAt(0)</code> will return the
+     * smallest key and <code>keyAt(size()-1)</code> will return the largest
+     * key.</p>
+     *
+     * <p>For indices outside of the range <code>0...size()-1</code>, the behavior is undefined for
+     * apps targeting {@link android.os.Build.VERSION_CODES#P} and earlier, and an
+     * {@link ArrayIndexOutOfBoundsException} is thrown for apps targeting
+     * {@link android.os.Build.VERSION_CODES#Q} and later.</p>
+     */
+    public int keyAt(int index) {
+        return mStorage.keyAt(index);
+    }
+
+    /**
+     * Given an index in the range <code>0...size()-1</code>, returns
+     * the value from the <code>index</code>th key-value mapping that this
+     * SparseIntArray stores.
+     *
+     * <p>The values corresponding to indices in ascending order are guaranteed
+     * to be associated with keys in ascending order, e.g.,
+     * <code>valueAt(0)</code> will return the value associated with the
+     * smallest key and <code>valueAt(size()-1)</code> will return the value
+     * associated with the largest key.</p>
+     *
+     * <p>For indices outside of the range <code>0...size()-1</code>, the behavior is undefined for
+     * apps targeting {@link android.os.Build.VERSION_CODES#P} and earlier, and an
+     * {@link ArrayIndexOutOfBoundsException} is thrown for apps targeting
+     * {@link android.os.Build.VERSION_CODES#Q} and later.</p>
+     */
+    public int valueAt(int index) {
+        return mStorage.valueAt(index);
+    }
+
+    /**
+     * Directly set the value at a particular index.
+     *
+     * <p>For indices outside of the range <code>0...size()-1</code>, the behavior is undefined for
+     * apps targeting {@link android.os.Build.VERSION_CODES#P} and earlier, and an
+     * {@link ArrayIndexOutOfBoundsException} is thrown for apps targeting
+     * {@link android.os.Build.VERSION_CODES#Q} and later.</p>
+     */
+    public void setValueAt(int index, int value) {
+        if (mStorage.valueAt(index) != value) {
+            mStorage.setValueAt(index, value);
+            onChanged();
+        }
+    }
+
+    /**
+     * Returns the index for which {@link #keyAt} would return the
+     * specified key, or a negative number if the specified
+     * key is not mapped.
+     */
+    public int indexOfKey(int key) {
+        return mStorage.indexOfKey(key);
+    }
+
+    /**
+     * Returns an index for which {@link #valueAt} would return the
+     * specified key, or a negative number if no keys map to the
+     * specified value.
+     * Beware that this is a linear search, unlike lookups by key,
+     * and that multiple keys can map to the same value and this will
+     * find only one of them.
+     */
+    public int indexOfValue(int value) {
+        return mStorage.indexOfValue(value);
+    }
+
+    /**
+     * Removes all key-value mappings from this SparseIntArray.
+     */
+    public void clear() {
+        final int count = size();
+        mStorage.clear();
+        if (count > 0) {
+            onChanged();
+        }
+    }
+
+    /**
+     * Puts a key/value pair into the array, optimizing for the case where
+     * the key is greater than all existing keys in the array.
+     */
+    public void append(int key, int value) {
+        mStorage.append(key, value);
+        onChanged();
+    }
+
+    /**
+     * Provides a copy of keys.
+     **/
+    public int[] copyKeys() {
+        return mStorage.copyKeys();
+    }
+
+    @Override
+    public int hashCode() {
+        return mStorage.hashCode();
+    }
+
+    @Override
+    public boolean equals(@Nullable Object o) {
+        if (o instanceof WatchedSparseIntArray) {
+            WatchedSparseIntArray w = (WatchedSparseIntArray) o;
+            return mStorage.equals(w.mStorage);
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * <p>This implementation composes a string by iterating over its mappings.
+     */
+    @Override
+    public String toString() {
+        return mStorage.toString();
+    }
+
+    /**
+     * Create a snapshot.  The snapshot does not include any {@link Watchable}
+     * information.
+     */
+    public WatchedSparseIntArray snapshot() {
+        WatchedSparseIntArray l = new WatchedSparseIntArray(this);
+        l.seal();
+        return l;
+    }
+
+    /**
+     * Make <this> a snapshot of the argument.  Note that <this> is immutable when the
+     * method returns.  <this> must be empty when the function is called.
+     * @param r The source array, which is copied into <this>
+     */
+    public void snapshot(@NonNull WatchedSparseIntArray r) {
+        snapshot(this, r);
+    }
+
+    /**
+     * Make the destination a copy of the source.  If the element is a subclass of Snapper then the
+     * copy contains snapshots of the elements.  Otherwise the copy contains references to the
+     * elements.  The destination must be initially empty.  Upon return, the destination is
+     * immutable.
+     * @param dst The destination array.  It must be empty.
+     * @param src The source array.  It is not modified.
+     */
+    public static void snapshot(@NonNull WatchedSparseIntArray dst,
+            @NonNull WatchedSparseIntArray src) {
+        if (dst.size() != 0) {
+            throw new IllegalArgumentException("snapshot destination is not empty");
+        }
+        final int end = src.size();
+        for (int i = 0; i < end; i++) {
+            dst.put(src.keyAt(i), src.valueAt(i));
+        }
+        dst.seal();
+    }
+
+}
diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java
index a61e08a..c25f1b4 100644
--- a/services/core/java/com/android/server/wm/ActivityClientController.java
+++ b/services/core/java/com/android/server/wm/ActivityClientController.java
@@ -30,7 +30,6 @@
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_IMMERSIVE;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_ALL;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.DEBUG_SWITCH;
-import static com.android.server.wm.ActivityTaskManagerDebugConfig.POSTFIX_VISIBILITY;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM;
 import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE;
@@ -42,7 +41,6 @@
 import android.annotation.NonNull;
 import android.app.Activity;
 import android.app.ActivityManager;
-import android.app.ActivityOptions;
 import android.app.ActivityTaskManager;
 import android.app.IActivityClientController;
 import android.app.PictureInPictureParams;
@@ -80,7 +78,6 @@
  */
 class ActivityClientController extends IActivityClientController.Stub {
     private static final String TAG = TAG_WITH_CLASS_NAME ? "ActivityClientController" : TAG_ATM;
-    private static final String TAG_VISIBILITY = TAG + POSTFIX_VISIBILITY;
 
     private final ActivityTaskManagerService mService;
     private final WindowManagerGlobalLock mGlobalLock;
@@ -557,23 +554,6 @@
     }
 
     @Override
-    public Bundle getActivityOptions(IBinder token) {
-        final long origId = Binder.clearCallingIdentity();
-        try {
-            synchronized (mGlobalLock) {
-                final ActivityRecord r = ActivityRecord.isInRootTaskLocked(token);
-                if (r == null) {
-                    return null;
-                }
-                final ActivityOptions activityOptions = r.takeOptionsLocked(true /* fromClient */);
-                return activityOptions != null ? activityOptions.toBundle() : null;
-            }
-        } finally {
-            Binder.restoreCallingIdentity(origId);
-        }
-    }
-
-    @Override
     public void setRequestedOrientation(IBinder token, int requestedOrientation) {
         final long origId = Binder.clearCallingIdentity();
         try {
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index ef845c8..324e3ac 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -284,6 +284,7 @@
 import android.view.IAppTransitionAnimationSpecsFuture;
 import android.view.IApplicationToken;
 import android.view.InputApplicationHandle;
+import android.view.RemoteAnimationAdapter;
 import android.view.RemoteAnimationDefinition;
 import android.view.RemoteAnimationTarget;
 import android.view.SurfaceControl;
@@ -462,7 +463,10 @@
     HashSet<WeakReference<PendingIntentRecord>> pendingResults; // all pending intents for this act
     ArrayList<ReferrerIntent> newIntents; // any pending new intents for single-top mode
     Intent mLastNewIntent;  // the last new intent we delivered to client
-    ActivityOptions pendingOptions; // most recently given options
+    /** The most recently given options. */
+    private ActivityOptions mPendingOptions;
+    /** Non-null if {@link #mPendingOptions} specifies the remote animation. */
+    private RemoteAnimationAdapter mPendingRemoteAnimation;
     ActivityOptions returningOptions; // options that are coming back via convertToTranslucent
     AppTimeTracker appTimeTracker; // set if we are tracking the time in this app/task/activity
     ActivityServiceConnectionsHolder mServiceConnectionsHolder; // Service connections.
@@ -569,7 +573,7 @@
     private final WindowState.UpdateReportedVisibilityResults mReportedVisibilityResults =
             new WindowState.UpdateReportedVisibilityResults();
 
-    private boolean mUseTransferredAnimation;
+    boolean mUseTransferredAnimation;
 
     /**
      * @see #currentLaunchCanTurnScreenOn()
@@ -882,8 +886,13 @@
                 }
             }
         }
-        if (pendingOptions != null) {
-            pw.print(prefix); pw.print("pendingOptions="); pw.println(pendingOptions);
+        if (mPendingOptions != null) {
+            pw.print(prefix); pw.print("pendingOptions="); pw.println(mPendingOptions);
+        }
+        if (mPendingRemoteAnimation != null) {
+            pw.print(prefix);
+            pw.print("pendingRemoteAnimationCallingPid=");
+            pw.println(mPendingRemoteAnimation.getCallingPid());
         }
         if (appTimeTracker != null) {
             appTimeTracker.dumpWithHeader(pw, prefix, false);
@@ -1009,9 +1018,8 @@
             if (info.minAspectRatio != 0) {
                 pw.println(prefix + "minAspectRatio=" + info.minAspectRatio);
             }
-            if (info.supportsSizeChanges) {
-                pw.println(prefix + "supportsSizeChanges=true");
-            }
+            pw.println(prefix + "supportsSizeChanges="
+                    + ActivityInfo.sizeChangesSupportModeToString(info.supportsSizeChanges()));
             if (info.configChanges != 0) {
                 pw.println(prefix + "configChanges=0x" + Integer.toHexString(info.configChanges));
             }
@@ -1633,8 +1641,8 @@
         lockTaskLaunchMode = getLockTaskLaunchMode(aInfo, options);
 
         if (options != null) {
-            pendingOptions = options;
-            final PendingIntent usageReport = pendingOptions.getUsageTimeReport();
+            setOptions(options);
+            final PendingIntent usageReport = options.getUsageTimeReport();
             if (usageReport != null) {
                 appTimeTracker = new AppTimeTracker(usageReport);
             }
@@ -2206,7 +2214,7 @@
             if (task != null && !finishing) {
                 task = null;
             }
-            clearOptionsLocked();
+            abortAndClearOptionsAnimation();
         }
     }
 
@@ -2993,7 +3001,7 @@
         }
         finishing = true;
         if (stopped) {
-            clearOptionsLocked();
+            abortAndClearOptionsAnimation();
         }
         mAtmService.getTransitionController().requestTransitionIfNeeded(TRANSIT_CLOSE, this);
     }
@@ -3492,8 +3500,8 @@
 
                 ProtoLog.v(WM_DEBUG_ADD_REMOVE,
                         "Removing starting %s from %s", tStartingWindow, fromActivity);
-                fromActivity.removeChild(tStartingWindow);
-                addWindow(tStartingWindow);
+                mAtmService.getTransitionController().collect(tStartingWindow);
+                tStartingWindow.reparent(this, POSITION_TOP);
 
                 // Propagate other interesting state between the tokens. If the old token is displayed,
                 // we should immediately force the new one to be displayed. If it is animating, we need
@@ -3518,6 +3526,9 @@
                     // the token we transfer the animation over. Thus, set this flag to indicate
                     // we've transferred the animation.
                     mUseTransferredAnimation = true;
+                } else if (mAtmService.getTransitionController().getTransitionPlayer() != null) {
+                    // In the new transit system, just set this every time we transfer the window
+                    mUseTransferredAnimation = true;
                 }
                 // Post cleanup after the visibility and animation are transferred.
                 fromActivity.postWindowRemoveStartingWindowCleanup(tStartingWindow);
@@ -3861,33 +3872,47 @@
     void updateOptionsLocked(ActivityOptions options) {
         if (options != null) {
             if (DEBUG_TRANSITION) Slog.i(TAG, "Update options for " + this);
-            if (pendingOptions != null) {
-                pendingOptions.abort();
+            if (mPendingOptions != null) {
+                mPendingOptions.abort();
             }
-            pendingOptions = options;
+            setOptions(options);
         }
     }
 
-    void applyOptionsLocked() {
-        if (pendingOptions != null
-                && pendingOptions.getAnimationType() != ANIM_SCENE_TRANSITION) {
-            if (DEBUG_TRANSITION) Slog.i(TAG, "Applying options for " + this);
-            applyOptionsLocked(pendingOptions, intent);
-            if (task == null) {
-                clearOptionsLocked(false /* withAbort */);
-            } else {
-                // This will clear the options for all the ActivityRecords for this Task.
-                task.forAllActivities((r) -> {
-                    r.clearOptionsLocked(false /* withAbort */);
-                });
+    private void setOptions(@NonNull ActivityOptions options) {
+        mPendingOptions = options;
+        if (options.getAnimationType() == ANIM_REMOTE_ANIMATION) {
+            mPendingRemoteAnimation = options.getRemoteAnimationAdapter();
+        }
+    }
+
+    void applyOptionsAnimation() {
+        if (DEBUG_TRANSITION) Slog.i(TAG, "Applying options for " + this);
+        if (mPendingRemoteAnimation != null) {
+            mDisplayContent.mAppTransition.overridePendingAppTransitionRemote(
+                    mPendingRemoteAnimation);
+        } else {
+            if (mPendingOptions == null
+                    || mPendingOptions.getAnimationType() == ANIM_SCENE_TRANSITION) {
+                // Scene transition will run on the client side.
+                return;
             }
+            applyOptionsAnimation(mPendingOptions, intent);
+        }
+        if (task == null) {
+            clearOptionsAnimation();
+        } else {
+            // This will clear the options for all the ActivityRecords for this Task.
+            task.forAllActivities((r) -> {
+                r.clearOptionsAnimation();
+            });
         }
     }
 
     /**
      * Apply override app transition base on options & animation type.
      */
-    void applyOptionsLocked(ActivityOptions pendingOptions, Intent intent) {
+    private void applyOptionsAnimation(ActivityOptions pendingOptions, Intent intent) {
         final int animationType = pendingOptions.getAnimationType();
         final DisplayContent displayContent = getDisplayContent();
         switch (animationType) {
@@ -3969,10 +3994,6 @@
                 displayContent.mAppTransition
                         .overridePendingAppTransitionStartCrossProfileApps();
                 break;
-            case ANIM_REMOTE_ANIMATION:
-                displayContent.mAppTransition.overridePendingAppTransitionRemote(
-                        pendingOptions.getRemoteAnimationAdapter());
-                break;
             case ANIM_NONE:
             case ANIM_UNDEFINED:
                 break;
@@ -4033,35 +4054,32 @@
         }
     }
 
-    void clearOptionsLocked() {
-        clearOptionsLocked(true /* withAbort */);
-    }
-
-    void clearOptionsLocked(boolean withAbort) {
-        if (withAbort && pendingOptions != null) {
-            pendingOptions.abort();
+    void abortAndClearOptionsAnimation() {
+        if (mPendingOptions != null) {
+            mPendingOptions.abort();
         }
-        pendingOptions = null;
+        clearOptionsAnimation();
     }
 
-    ActivityOptions takeOptionsLocked(boolean fromClient) {
+    void clearOptionsAnimation() {
+        mPendingOptions = null;
+        mPendingRemoteAnimation = null;
+    }
+
+    ActivityOptions getOptions() {
+        return mPendingOptions;
+    }
+
+    ActivityOptions takeOptions() {
         if (DEBUG_TRANSITION) Slog.i(TAG, "Taking options for " + this + " callers="
                 + Debug.getCallers(6));
-        ActivityOptions opts = pendingOptions;
-
-        // If we are trying to take activity options from the client, do not null it out if it's a
-        // remote animation as the client doesn't need it ever. This is a workaround when client is
-        // faster to take the options than we are to resume the next activity.
-        // TODO (b/132432864): Fix the root cause of these transition preparing/applying options
-        // timing somehow
-        if (!fromClient || opts == null || opts.getRemoteAnimationAdapter() == null) {
-            pendingOptions = null;
-        }
+        final ActivityOptions opts = mPendingOptions;
+        mPendingOptions = null;
         return opts;
     }
 
     boolean allowMoveToFront() {
-        return pendingOptions == null || !pendingOptions.getAvoidMoveToFront();
+        return mPendingOptions == null || !mPendingOptions.getAvoidMoveToFront();
     }
 
     void removeUriPermissionsLocked() {
@@ -4883,7 +4901,7 @@
 
             try {
                 mAtmService.getLifecycleManager().scheduleTransaction(app.getThread(), appToken,
-                        StartActivityItem.obtain());
+                        StartActivityItem.obtain(takeOptions()));
             } catch (Exception e) {
                 Slog.w(TAG, "Exception thrown sending start: " + intent.getComponent(), e);
             }
@@ -5204,7 +5222,7 @@
             notifyAppStopped();
 
             if (finishing) {
-                clearOptionsLocked();
+                abortAndClearOptionsAnimation();
             } else {
                 if (deferRelaunchUntilPaused) {
                     destroyImmediately("stop-config");
@@ -5838,8 +5856,8 @@
             // We don't show starting window for overlay activities.
             return;
         }
-        if (pendingOptions != null
-                && pendingOptions.getAnimationType() == ActivityOptions.ANIM_SCENE_TRANSITION) {
+        if (mPendingOptions != null
+                && mPendingOptions.getAnimationType() == ActivityOptions.ANIM_SCENE_TRANSITION) {
             // Don't show starting window when using shared element transition.
             return;
         }
@@ -6539,7 +6557,7 @@
      *         aspect ratio.
      */
     boolean shouldUseSizeCompatMode() {
-        if (info.supportsSizeChanges) {
+        if (info.supportsSizeChanges() != ActivityInfo.SIZE_CHANGES_UNSUPPORTED) {
             return false;
         }
         if (inMultiWindowMode() || getWindowConfiguration().hasWindowDecorCaption()) {
diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java
index f9e85cd..2dd8c59 100644
--- a/services/core/java/com/android/server/wm/ActivityStartController.java
+++ b/services/core/java/com/android/server/wm/ActivityStartController.java
@@ -528,7 +528,7 @@
                     "pendingActivityLaunch");
             try {
                 starter.startResolvedActivity(pal.r, pal.sourceRecord, null, null, pal.startFlags,
-                        resume, pal.r.pendingOptions, null, pal.intentGrants);
+                        resume, pal.r.getOptions(), null, pal.intentGrants);
             } catch (Exception e) {
                 Slog.e(TAG, "Exception during pending activity launch pal=" + pal, e);
                 pal.sendErrorResult(e.getMessage());
diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java
index e30cd05..5289f86 100644
--- a/services/core/java/com/android/server/wm/ActivityStarter.java
+++ b/services/core/java/com/android/server/wm/ActivityStarter.java
@@ -1258,7 +1258,7 @@
         }
         // We pretend to the caller that it was really started to make it backward compatible, but
         // they will just get a cancel result.
-        ActivityOptions.abort(r.pendingOptions);
+        ActivityOptions.abort(r.getOptions());
         return true;
     }
 
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index ea04c64..9cbe277 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -34,7 +34,6 @@
 import static android.app.ActivityTaskManager.RESIZE_MODE_PRESERVE_WINDOW;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
 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_UNDEFINED;
 import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
 import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
@@ -745,7 +744,7 @@
             mActivityClientController.onSystemReady();
             mBlockActivityAfterHomeEnabled = DeviceConfig.getBoolean(
                     DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
-                    BLOCK_ACTIVITY_STARTS_AFTER_HOME_FLAG, false);
+                    BLOCK_ACTIVITY_STARTS_AFTER_HOME_FLAG, true);
         }
     }
 
@@ -1902,9 +1901,6 @@
 
     @Override
     public boolean setTaskWindowingMode(int taskId, int windowingMode, boolean toTop) {
-        if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY) {
-            return setTaskWindowingModeSplitScreenPrimary(taskId, toTop);
-        }
         enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_TASKS, "setTaskWindowingMode()");
         synchronized (mGlobalLock) {
             final long ident = Binder.clearCallingIdentity();
@@ -2235,28 +2231,6 @@
     }
 
     /**
-     * Moves the specified task to the primary-split-screen stack.
-     *
-     * @param taskId Id of task to move.
-     * @param toTop  If the task and stack should be moved to the top.
-     * @return Whether the task was successfully put into splitscreen.
-     */
-    @Override
-    public boolean setTaskWindowingModeSplitScreenPrimary(int taskId, boolean toTop) {
-        enforceCallerIsRecentsOrHasPermission(MANAGE_ACTIVITY_TASKS,
-                "setTaskWindowingModeSplitScreenPrimary()");
-        synchronized (mGlobalLock) {
-            final long ident = Binder.clearCallingIdentity();
-            try {
-                return setTaskWindowingModeSplitScreen(taskId, WINDOWING_MODE_SPLIT_SCREEN_PRIMARY,
-                        toTop);
-            } finally {
-                Binder.restoreCallingIdentity(ident);
-            }
-        }
-    }
-
-    /**
      * Moves the specified task into a split-screen tile.
      */
     private boolean setTaskWindowingModeSplitScreen(int taskId, int windowingMode, boolean toTop) {
@@ -2690,14 +2664,18 @@
                                     + ainfo.applicationInfo.uid + ", calling uid=" + callingUid);
                 }
 
-                final Task stack = r.getRootTask();
-                final Task task = stack.getDisplayArea().createRootTask(stack.getWindowingMode(),
-                        stack.getActivityType(), !ON_TOP, ainfo, intent,
-                        false /* createdByOrganizer */);
+                final Task rootTask = r.getRootTask();
+                final Task task = new Task.Builder(this)
+                        .setWindowingMode(rootTask.getWindowingMode())
+                        .setActivityType(rootTask.getActivityType())
+                        .setActivityInfo(ainfo)
+                        .setParent(rootTask.getDisplayArea())
+                        .setIntent(intent)
+                        .build();
 
                 if (!mRecentTasks.addToBottom(task)) {
                     // The app has too many tasks already and we can't add any more
-                    stack.removeChild(task, "addAppTask");
+                    rootTask.removeChild(task, "addAppTask");
                     return INVALID_TASK_ID;
                 }
                 task.getTaskDescription().copyFrom(description);
diff --git a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
index 16f415f..9d291b1 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
@@ -868,8 +868,8 @@
                         mergedConfiguration.getOverrideConfiguration(), r.compat,
                         r.launchedFromPackage, task.voiceInteractor, proc.getReportedProcState(),
                         r.getSavedState(), r.getPersistentSavedState(), results, newIntents,
-                        dc.isNextTransitionForward(), proc.createProfilerInfoIfNeeded(),
-                        r.assistToken, activityClientController,
+                        r.takeOptions(), dc.isNextTransitionForward(),
+                        proc.createProfilerInfoIfNeeded(), r.assistToken, activityClientController,
                         r.createFixedRotationAdjustmentsIfNeeded()));
 
                 // Set desired final state.
@@ -2566,9 +2566,10 @@
                 try {
                     mService.moveTaskToFrontLocked(null /* appThread */, null /* callingPackage */,
                             task.mTaskId, 0, options);
-                    // Apply options to prevent pendingOptions be taken by client to make sure
-                    // the override pending app transition will be applied immediately.
-                    targetActivity.applyOptionsLocked();
+                    // Apply options to prevent pendingOptions be taken when scheduling activity
+                    // lifecycle transaction to make sure the override pending app transition will
+                    // be applied immediately.
+                    targetActivity.applyOptionsAnimation();
                 } finally {
                     mActivityMetricsLogger.notifyActivityLaunched(launchingState,
                             START_TASK_TO_FRONT, targetActivity, activityOptions);
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index dde527d..d562bde 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -154,7 +154,7 @@
         ProtoLog.v(WM_DEBUG_APP_TRANSITIONS, "**** GOOD TO GO");
         // TODO(new-app-transition): Remove code using appTransition.getAppTransition()
         final AppTransition appTransition = mDisplayContent.mAppTransition;
-        mDisplayContent.mSkipAppTransitionAnimation = false;
+
         mDisplayContent.mNoAnimationNotifyOnTransitionFinished.clear();
 
         appTransition.removeAppTransitionTimeoutCallbacks();
@@ -188,7 +188,9 @@
         final @TransitionOldType int transit = getTransitCompatType(
                 mDisplayContent.mAppTransition,
                 mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
-                mWallpaperControllerLocked.getWallpaperTarget(), getOldWallpaper());
+                mWallpaperControllerLocked.getWallpaperTarget(), getOldWallpaper(),
+                mDisplayContent.mSkipAppTransitionAnimation);
+        mDisplayContent.mSkipAppTransitionAnimation = false;
 
         ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
                 "handleAppTransitionReady: displayId=%d appTransition={%s}"
@@ -274,7 +276,8 @@
      */
     static @TransitionOldType int getTransitCompatType(AppTransition appTransition,
             ArraySet<ActivityRecord> openingApps, ArraySet<ActivityRecord> closingApps,
-            @Nullable WindowState wallpaperTarget, @Nullable WindowState oldWallpaper) {
+            @Nullable WindowState wallpaperTarget, @Nullable WindowState oldWallpaper,
+            boolean skipAppTransitionAnimation) {
 
         // Determine if closing and opening app token sets are wallpaper targets, in which case
         // special animations are needed.
@@ -298,6 +301,10 @@
                 return TRANSIT_OLD_KEYGUARD_UNOCCLUDE;
         }
 
+        // This is not keyguard transition and one of the app has request to skip app transition.
+        if (skipAppTransitionAnimation) {
+            return WindowManager.TRANSIT_OLD_UNSET;
+        }
         final @TransitionFlags int flags = appTransition.getTransitFlags();
         final @TransitionType int firstTransit = appTransition.getFirstAppTransition();
 
diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartCallback.java b/services/core/java/com/android/server/wm/BackgroundActivityStartCallback.java
index af6c255..200f207 100644
--- a/services/core/java/com/android/server/wm/BackgroundActivityStartCallback.java
+++ b/services/core/java/com/android/server/wm/BackgroundActivityStartCallback.java
@@ -18,25 +18,29 @@
 
 import android.os.IBinder;
 
+import java.util.Collection;
+
 /**
- * Callback to be called when a background activity start is allowed exclusively because of the
- * token provided in {@link #getToken()}.
+ * Callback to decide activity starts and related operations based on originating tokens.
  */
 public interface BackgroundActivityStartCallback {
     /**
-     * The token for which this callback is responsible for deciding whether the app can start
-     * background activities or not.
+     * Returns true if the background activity start originating from {@code tokens} should be
+     * allowed or not.
      *
-     * Ideally this should just return a final variable, don't do anything costly here (don't hold
-     * any locks).
-     */
-    IBinder getToken();
-
-    /**
-     * Returns true if the background activity start due to originating token in {@link #getToken()}
-     * should be allowed or not.
+     * Note that if the start was allowed due to a mechanism other than tokens (eg. permission),
+     * this won't be called.
      *
      * This will be called holding the WM lock, don't do anything costly here.
      */
-    boolean isActivityStartAllowed(int uid, String packageName);
+    boolean isActivityStartAllowed(Collection<IBinder> tokens, int uid, String packageName);
+
+    /**
+     * Returns whether {@code uid} can send {@link android.content.Intent
+     * #ACTION_CLOSE_SYSTEM_DIALOGS}, presumably to start activities, based on the originating
+     * tokens {@code tokens} currently associated with potential activity starts.
+     *
+     * This will be called holding the AM and WM lock, don't do anything costly here.
+     */
+    boolean canCloseSystemDialogs(Collection<IBinder> tokens, int uid);
 }
diff --git a/services/core/java/com/android/server/wm/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java
index 36a1ef9..0fc4cdb 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainer.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java
@@ -547,19 +547,16 @@
         return activityType == ACTIVITY_TYPE_STANDARD || activityType == ACTIVITY_TYPE_UNDEFINED;
     }
 
-    public boolean hasCompatibleActivityType(ConfigurationContainer other) {
-        /*@WindowConfiguration.ActivityType*/ int thisType = getActivityType();
-        /*@WindowConfiguration.ActivityType*/ int otherType = other.getActivityType();
-
-        if (thisType == otherType) {
+    public static boolean isCompatibleActivityType(int currentType, int otherType) {
+        if (currentType == otherType) {
             return true;
         }
-        if (thisType == ACTIVITY_TYPE_ASSISTANT) {
+        if (currentType == ACTIVITY_TYPE_ASSISTANT) {
             // Assistant activities are only compatible with themselves...
             return false;
         }
         // Otherwise we are compatible if us or other is not currently defined.
-        return thisType == ACTIVITY_TYPE_UNDEFINED || otherType == ACTIVITY_TYPE_UNDEFINED;
+        return currentType == ACTIVITY_TYPE_UNDEFINED || otherType == ACTIVITY_TYPE_UNDEFINED;
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java
index 15483cb..1fd6d00 100644
--- a/services/core/java/com/android/server/wm/DisplayArea.java
+++ b/services/core/java/com/android/server/wm/DisplayArea.java
@@ -564,6 +564,22 @@
     }
 
     @Override
+    void onParentChanged(ConfigurationContainer newParent, ConfigurationContainer oldParent) {
+        super.onParentChanged(newParent, oldParent);
+        if (mOrganizer != null || newParent == null) {
+            return;
+        }
+        // Check if we have a registered organizer, just after mSurfaceControl is ready.
+        setOrganizer(mOrganizerController.getOrganizerByFeature(mFeatureId));
+    }
+
+    @Override
+    void removeImmediately() {
+        setOrganizer(null);
+        super.removeImmediately();
+    }
+
+    @Override
     DisplayArea getDisplayArea() {
         return this;
     }
diff --git a/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java b/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java
index 53f7009..38f78c9 100644
--- a/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java
+++ b/services/core/java/com/android/server/wm/DisplayAreaOrganizerController.java
@@ -21,6 +21,7 @@
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_WINDOW_ORGANIZER;
 import static com.android.server.wm.DisplayArea.Type.ANY;
 
+import android.annotation.Nullable;
 import android.content.pm.ParceledListSlice;
 import android.os.Binder;
 import android.os.IBinder;
@@ -50,6 +51,10 @@
     private final WindowManagerGlobalLock mGlobalLock;
     private final HashMap<Integer, IDisplayAreaOrganizer> mOrganizersByFeatureIds = new HashMap();
 
+    @Nullable IDisplayAreaOrganizer getOrganizerByFeature(int featureId) {
+        return mOrganizersByFeatureIds.get(featureId);
+    }
+
     private class DeathRecipient implements IBinder.DeathRecipient {
         int mFeature;
         IDisplayAreaOrganizer mOrganizer;
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 4c60a3d..c3a7542 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -332,6 +332,7 @@
     DisplayCutout mInitialDisplayCutout;
     private final RotationCache<DisplayCutout, WmDisplayCutout> mDisplayCutoutCache
             = new RotationCache<>(this::calculateDisplayCutoutForRotationUncached);
+    boolean mIgnoreDisplayCutout;
 
     /**
      * Overridden display size. Initialized with {@link #mInitialDisplayWidth}
@@ -2507,7 +2508,8 @@
         final int newWidth = rotated ? mDisplayInfo.logicalHeight : mDisplayInfo.logicalWidth;
         final int newHeight = rotated ? mDisplayInfo.logicalWidth : mDisplayInfo.logicalHeight;
         final int newDensity = mDisplayInfo.logicalDensityDpi;
-        final DisplayCutout newCutout = mDisplayInfo.displayCutout;
+        final DisplayCutout newCutout = mIgnoreDisplayCutout
+                ? DisplayCutout.NO_CUTOUT : mDisplayInfo.displayCutout;
         final String newUniqueId = mDisplayInfo.uniqueId;
 
         final boolean displayMetricsChanged = mInitialDisplayWidth != newWidth
diff --git a/services/core/java/com/android/server/wm/DisplayFrames.java b/services/core/java/com/android/server/wm/DisplayFrames.java
index 7f3cb49..38d0c21 100644
--- a/services/core/java/com/android/server/wm/DisplayFrames.java
+++ b/services/core/java/com/android/server/wm/DisplayFrames.java
@@ -16,11 +16,17 @@
 
 package com.android.server.wm;
 
+import static android.view.InsetsState.ITYPE_BOTTOM_DISPLAY_CUTOUT;
+import static android.view.InsetsState.ITYPE_LEFT_DISPLAY_CUTOUT;
+import static android.view.InsetsState.ITYPE_RIGHT_DISPLAY_CUTOUT;
+import static android.view.InsetsState.ITYPE_TOP_DISPLAY_CUTOUT;
+
 import android.annotation.NonNull;
 import android.graphics.Rect;
 import android.util.proto.ProtoOutputStream;
 import android.view.DisplayCutout;
 import android.view.DisplayInfo;
+import android.view.InsetsState;
 
 import com.android.server.wm.utils.WmDisplayCutout;
 
@@ -67,25 +73,41 @@
         mDisplayInfoCutout = displayCutout != null ? displayCutout : WmDisplayCutout.NO_CUTOUT;
     }
 
-    public void onBeginLayout() {
-        mUnrestricted.set(0, 0, mDisplayWidth, mDisplayHeight);
+    public void onBeginLayout(InsetsState state) {
         mDisplayCutout = mDisplayInfoCutout;
-        mDisplayCutoutSafe.set(Integer.MIN_VALUE, Integer.MIN_VALUE,
-                Integer.MAX_VALUE, Integer.MAX_VALUE);
-        if (!mDisplayCutout.getDisplayCutout().isEmpty()) {
-            final DisplayCutout c = mDisplayCutout.getDisplayCutout();
-            if (c.getSafeInsetLeft() > 0) {
-                mDisplayCutoutSafe.left = mUnrestricted.left + c.getSafeInsetLeft();
+        final Rect unrestricted = mUnrestricted;
+        final Rect safe = mDisplayCutoutSafe;
+        final DisplayCutout cutout = mDisplayCutout.getDisplayCutout();
+        unrestricted.set(0, 0, mDisplayWidth, mDisplayHeight);
+        safe.set(Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE);
+        state.setDisplayFrame(unrestricted);
+        state.setDisplayCutout(cutout);
+        if (!cutout.isEmpty()) {
+            if (cutout.getSafeInsetLeft() > 0) {
+                safe.left = unrestricted.left + cutout.getSafeInsetLeft();
             }
-            if (c.getSafeInsetTop() > 0) {
-                mDisplayCutoutSafe.top = mUnrestricted.top + c.getSafeInsetTop();
+            if (cutout.getSafeInsetTop() > 0) {
+                safe.top = unrestricted.top + cutout.getSafeInsetTop();
             }
-            if (c.getSafeInsetRight() > 0) {
-                mDisplayCutoutSafe.right = mUnrestricted.right - c.getSafeInsetRight();
+            if (cutout.getSafeInsetRight() > 0) {
+                safe.right = unrestricted.right - cutout.getSafeInsetRight();
             }
-            if (c.getSafeInsetBottom() > 0) {
-                mDisplayCutoutSafe.bottom = mUnrestricted.bottom - c.getSafeInsetBottom();
+            if (cutout.getSafeInsetBottom() > 0) {
+                safe.bottom = unrestricted.bottom - cutout.getSafeInsetBottom();
             }
+            state.getSource(ITYPE_LEFT_DISPLAY_CUTOUT).setFrame(
+                    unrestricted.left, unrestricted.top, safe.left, unrestricted.bottom);
+            state.getSource(ITYPE_TOP_DISPLAY_CUTOUT).setFrame(
+                    unrestricted.left, unrestricted.top, unrestricted.right, safe.top);
+            state.getSource(ITYPE_RIGHT_DISPLAY_CUTOUT).setFrame(
+                    safe.right, unrestricted.top, unrestricted.right, unrestricted.bottom);
+            state.getSource(ITYPE_BOTTOM_DISPLAY_CUTOUT).setFrame(
+                    unrestricted.left, safe.bottom, unrestricted.right, unrestricted.bottom);
+        } else {
+            state.removeSource(ITYPE_LEFT_DISPLAY_CUTOUT);
+            state.removeSource(ITYPE_TOP_DISPLAY_CUTOUT);
+            state.removeSource(ITYPE_RIGHT_DISPLAY_CUTOUT);
+            state.removeSource(ITYPE_BOTTOM_DISPLAY_CUTOUT);
         }
     }
 
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index cd02e00..fb005b3 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -25,20 +25,16 @@
 import static android.content.res.Configuration.UI_MODE_TYPE_CAR;
 import static android.content.res.Configuration.UI_MODE_TYPE_MASK;
 import static android.view.Display.TYPE_INTERNAL;
-import static android.view.InsetsState.ITYPE_BOTTOM_DISPLAY_CUTOUT;
 import static android.view.InsetsState.ITYPE_BOTTOM_GESTURES;
 import static android.view.InsetsState.ITYPE_BOTTOM_TAPPABLE_ELEMENT;
 import static android.view.InsetsState.ITYPE_CAPTION_BAR;
 import static android.view.InsetsState.ITYPE_CLIMATE_BAR;
 import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR;
 import static android.view.InsetsState.ITYPE_IME;
-import static android.view.InsetsState.ITYPE_LEFT_DISPLAY_CUTOUT;
 import static android.view.InsetsState.ITYPE_LEFT_GESTURES;
 import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
-import static android.view.InsetsState.ITYPE_RIGHT_DISPLAY_CUTOUT;
 import static android.view.InsetsState.ITYPE_RIGHT_GESTURES;
 import static android.view.InsetsState.ITYPE_STATUS_BAR;
-import static android.view.InsetsState.ITYPE_TOP_DISPLAY_CUTOUT;
 import static android.view.InsetsState.ITYPE_TOP_GESTURES;
 import static android.view.InsetsState.ITYPE_TOP_TAPPABLE_ELEMENT;
 import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
@@ -1408,15 +1404,13 @@
      * @param attrs The LayoutParams of the window.
      * @param windowToken The token of the window.
      * @param outFrame The frame of the window.
-     * @param outDisplayCutout The area that has been cut away from the display.
      * @param outInsetsState The insets state of this display from the client's perspective.
      * @param localClient Whether the client is from the our process.
      * @return Whether to always consume the system bars.
      *         See {@link #areSystemBarsForcedShownLw(WindowState)}.
      */
     boolean getLayoutHint(LayoutParams attrs, WindowToken windowToken, Rect outFrame,
-            DisplayCutout.ParcelableWrapper outDisplayCutout, InsetsState outInsetsState,
-            boolean localClient) {
+            InsetsState outInsetsState, boolean localClient) {
         final boolean isFixedRotationTransforming =
                 windowToken != null && windowToken.isFixedRotationTransforming();
         final ActivityRecord activity = windowToken != null ? windowToken.asActivityRecord() : null;
@@ -1432,19 +1426,6 @@
             outFrame.intersect(taskBounds);
         }
 
-        final int fl = attrs.flags;
-        final boolean layoutInScreenAndInsetDecor = (fl & FLAG_LAYOUT_IN_SCREEN) != 0
-                && (fl & FLAG_LAYOUT_INSET_DECOR) != 0;
-        final DisplayFrames displayFrames = isFixedRotationTransforming
-                ? windowToken.getFixedRotationTransformDisplayFrames()
-                : mDisplayContent.mDisplayFrames;
-        if (layoutInScreenAndInsetDecor) {
-            outDisplayCutout.set(
-                    displayFrames.mDisplayCutout.calculateRelativeTo(outFrame).getDisplayCutout());
-        } else {
-            outDisplayCutout.set(DisplayCutout.NO_CUTOUT);
-        }
-
         final boolean inSizeCompatMode = WindowState.inSizeCompatMode(attrs, windowToken);
         outInsetsState.set(state, inSizeCompatMode || localClient);
         if (inSizeCompatMode) {
@@ -1523,9 +1504,7 @@
      */
     void simulateLayoutDisplay(DisplayFrames displayFrames, InsetsState insetsState,
             SparseArray<Rect> barContentFrames) {
-        displayFrames.onBeginLayout();
-        updateInsetsStateForDisplayCutout(displayFrames, insetsState);
-        insetsState.setDisplayFrame(displayFrames.mUnrestricted);
+        displayFrames.onBeginLayout(insetsState);
         final WindowFrames simulatedWindowFrames = new WindowFrames();
         if (mNavigationBar != null) {
             simulateLayoutDecorWindow(mNavigationBar, displayFrames, insetsState,
@@ -1547,10 +1526,7 @@
      * @param uiMode The current uiMode in configuration.
      */
     public void beginLayoutLw(DisplayFrames displayFrames, int uiMode) {
-        displayFrames.onBeginLayout();
-        final InsetsState state = mDisplayContent.getInsetsStateController().getRawInsetsState();
-        updateInsetsStateForDisplayCutout(displayFrames, state);
-        state.setDisplayFrame(displayFrames.mUnrestricted);
+        displayFrames.onBeginLayout(mDisplayContent.getInsetsStateController().getRawInsetsState());
         mSystemGestures.screenWidth = displayFrames.mUnrestricted.width();
         mSystemGestures.screenHeight = displayFrames.mUnrestricted.height();
 
@@ -1595,23 +1571,6 @@
         }
     }
 
-    private static void updateInsetsStateForDisplayCutout(DisplayFrames displayFrames,
-            InsetsState state) {
-        if (displayFrames.mDisplayCutout.getDisplayCutout().isEmpty()) {
-            state.removeSource(ITYPE_LEFT_DISPLAY_CUTOUT);
-            state.removeSource(ITYPE_TOP_DISPLAY_CUTOUT);
-            state.removeSource(ITYPE_RIGHT_DISPLAY_CUTOUT);
-            state.removeSource(ITYPE_BOTTOM_DISPLAY_CUTOUT);
-            return;
-        }
-        final Rect u = displayFrames.mUnrestricted;
-        final Rect s = displayFrames.mDisplayCutoutSafe;
-        state.getSource(ITYPE_LEFT_DISPLAY_CUTOUT).setFrame(u.left, u.top, s.left, u.bottom);
-        state.getSource(ITYPE_TOP_DISPLAY_CUTOUT).setFrame(u.left, u.top, u.right, s.top);
-        state.getSource(ITYPE_RIGHT_DISPLAY_CUTOUT).setFrame(s.right, u.top, u.right, u.bottom);
-        state.getSource(ITYPE_BOTTOM_DISPLAY_CUTOUT).setFrame(u.left, s.bottom, u.right, u.bottom);
-    }
-
     private void layoutStatusBar(DisplayFrames displayFrames, Rect simulatedContentFrame) {
         // decide where the status bar goes ahead of time
         if (mStatusBar == null) {
@@ -1623,7 +1582,7 @@
         windowFrames.setFrames(sTmpStatusFrame /* parentFrame */,
                 sTmpStatusFrame /* displayFrame */);
         // Let the status bar determine its size.
-        mStatusBar.computeFrame(displayFrames);
+        mStatusBar.computeFrameAndUpdateSourceFrame();
 
         // For layout, the status bar is always at the top with our fixed height.
         int statusBarBottom = displayFrames.mUnrestricted.top
@@ -1690,7 +1649,7 @@
         final WindowFrames windowFrames = mNavigationBar.getLayoutingWindowFrames();
         windowFrames.setFrames(navigationFrame /* parentFrame */,
                 navigationFrame /* displayFrame */);
-        mNavigationBar.computeFrame(displayFrames);
+        mNavigationBar.computeFrameAndUpdateSourceFrame();
         final Rect contentFrame =  sTmpRect;
         contentFrame.set(windowFrames.mFrame);
         contentFrame.intersect(displayFrames.mDisplayCutoutSafe);
@@ -1872,7 +1831,7 @@
             windowFrames.setContentChanged(true);
         }
 
-        win.computeFrame(displayFrames);
+        win.computeFrameAndUpdateSourceFrame();
     }
 
     WindowState getTopFullscreenOpaqueWindow() {
diff --git a/services/core/java/com/android/server/wm/DisplayWindowSettings.java b/services/core/java/com/android/server/wm/DisplayWindowSettings.java
index 5d4dbc8..b82fdd2 100644
--- a/services/core/java/com/android/server/wm/DisplayWindowSettings.java
+++ b/services/core/java/com/android/server/wm/DisplayWindowSettings.java
@@ -261,6 +261,10 @@
                 ? settings.mIgnoreOrientationRequest : false;
         dc.setIgnoreOrientationRequest(ignoreOrientationRequest);
 
+        final boolean ignoreDisplayCutout = settings.mIgnoreDisplayCutout != null
+                ? settings.mIgnoreDisplayCutout : false;
+        dc.mIgnoreDisplayCutout = ignoreDisplayCutout;
+
         final int width = hasSizeOverride ? settings.mForcedWidth : dc.mInitialDisplayWidth;
         final int height = hasSizeOverride ? settings.mForcedHeight : dc.mInitialDisplayHeight;
         final int density = hasDensityOverride ? settings.mForcedDensity
@@ -352,6 +356,8 @@
             Integer mFixedToUserRotation;
             @Nullable
             Boolean mIgnoreOrientationRequest;
+            @Nullable
+            Boolean mIgnoreDisplayCutout;
 
             SettingsEntry() {}
 
@@ -422,6 +428,10 @@
                     mIgnoreOrientationRequest = other.mIgnoreOrientationRequest;
                     changed = true;
                 }
+                if (other.mIgnoreDisplayCutout != mIgnoreDisplayCutout) {
+                    mIgnoreDisplayCutout = other.mIgnoreDisplayCutout;
+                    changed = true;
+                }
                 return changed;
             }
 
@@ -500,6 +510,11 @@
                     mIgnoreOrientationRequest = delta.mIgnoreOrientationRequest;
                     changed = true;
                 }
+                if (delta.mIgnoreDisplayCutout != null
+                        && delta.mIgnoreDisplayCutout != mIgnoreDisplayCutout) {
+                    mIgnoreDisplayCutout = delta.mIgnoreDisplayCutout;
+                    changed = true;
+                }
                 return changed;
             }
 
@@ -515,7 +530,8 @@
                         && mShouldShowSystemDecors == null
                         && mImePolicy == null
                         && mFixedToUserRotation == null
-                        && mIgnoreOrientationRequest == null;
+                        && mIgnoreOrientationRequest == null
+                        && mIgnoreDisplayCutout == null;
             }
 
             @Override
@@ -536,8 +552,8 @@
                         && Objects.equals(mShouldShowSystemDecors, that.mShouldShowSystemDecors)
                         && Objects.equals(mImePolicy, that.mImePolicy)
                         && Objects.equals(mFixedToUserRotation, that.mFixedToUserRotation)
-                        && Objects.equals(mIgnoreOrientationRequest,
-                                that.mIgnoreOrientationRequest);
+                        && Objects.equals(mIgnoreOrientationRequest, that.mIgnoreOrientationRequest)
+                        && Objects.equals(mIgnoreDisplayCutout, that.mIgnoreDisplayCutout);
             }
 
             @Override
@@ -545,7 +561,7 @@
                 return Objects.hash(mWindowingMode, mUserRotationMode, mUserRotation, mForcedWidth,
                         mForcedHeight, mForcedDensity, mForcedScalingMode, mRemoveContentMode,
                         mShouldShowWithInsecureKeyguard, mShouldShowSystemDecors, mImePolicy,
-                        mFixedToUserRotation, mIgnoreOrientationRequest);
+                        mFixedToUserRotation, mIgnoreOrientationRequest, mIgnoreDisplayCutout);
             }
 
             @Override
@@ -564,6 +580,7 @@
                         + ", mShouldShowIme=" + mImePolicy
                         + ", mFixedToUserRotation=" + mFixedToUserRotation
                         + ", mIgnoreOrientationRequest=" + mIgnoreOrientationRequest
+                        + ", mIgnoreDisplayCutout=" + mIgnoreDisplayCutout
                         + '}';
             }
         }
diff --git a/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java b/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java
index 57c947f..5f3ab43 100644
--- a/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java
+++ b/services/core/java/com/android/server/wm/DisplayWindowSettingsProvider.java
@@ -403,6 +403,8 @@
                     null /* defaultValue */);
             settingsEntry.mIgnoreOrientationRequest = getBooleanAttribute(parser,
                     "ignoreOrientationRequest", null /* defaultValue */);
+            settingsEntry.mIgnoreDisplayCutout = getBooleanAttribute(parser,
+                    "ignoreDisplayCutout", null /* defaultValue */);
             fileData.mSettings.put(name, settingsEntry);
         }
         XmlUtils.skipCurrentTag(parser);
@@ -490,6 +492,10 @@
                     out.attributeBoolean(null, "ignoreOrientationRequest",
                             settingsEntry.mIgnoreOrientationRequest);
                 }
+                if (settingsEntry.mIgnoreDisplayCutout != null) {
+                    out.attributeBoolean(null, "ignoreDisplayCutout",
+                            settingsEntry.mIgnoreDisplayCutout);
+                }
                 out.endTag(null, "display");
             }
 
diff --git a/services/core/java/com/android/server/wm/RecentTasks.java b/services/core/java/com/android/server/wm/RecentTasks.java
index dc75bbe..6e8110e 100644
--- a/services/core/java/com/android/server/wm/RecentTasks.java
+++ b/services/core/java/com/android/server/wm/RecentTasks.java
@@ -1496,6 +1496,7 @@
         final Task removedTask = mTasks.remove(removeIndex);
         if (removedTask != task) {
             if (removedTask.hasChild()) {
+                Slog.i(TAG, "Add " + removedTask + " to hidden list because adding " + task);
                 // A non-empty task is replaced by a new task. Because the removed task is no longer
                 // managed by the recent tasks list, add it to the hidden list to prevent the task
                 // from becoming dangling.
diff --git a/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java b/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java
index 17cb890..fc347bc 100644
--- a/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java
+++ b/services/core/java/com/android/server/wm/ResetTargetTaskHelper.java
@@ -267,8 +267,9 @@
     private boolean takeOption(ActivityRecord p, boolean noOptions) {
         mCanMoveOptions = false;
         if (noOptions && mTopOptions == null) {
-            mTopOptions = p.takeOptionsLocked(false /* fromClient */);
+            mTopOptions = p.getOptions();
             if (mTopOptions != null) {
+                p.clearOptionsAnimation();
                 noOptions = false;
             }
         }
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 5195edf..54996a6 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -306,52 +306,54 @@
     private final FindTaskResult mTmpFindTaskResult = new FindTaskResult();
 
     static class FindTaskResult implements Function<Task, Boolean> {
-        ActivityRecord mRecord;
-        boolean mIdealMatch;
+        ActivityRecord mIdealRecord;
+        ActivityRecord mCandidateRecord;
 
-        private ActivityRecord mTarget;
-        private Intent intent;
-        private ActivityInfo info;
+        private int mActivityType;
+        private String mTaskAffinity;
+        private Intent mIntent;
+        private ActivityInfo mInfo;
         private ComponentName cls;
         private int userId;
         private boolean isDocument;
         private Uri documentData;
 
+        void init(int activityType, String taskAffinity, Intent intent, ActivityInfo info) {
+            mActivityType = activityType;
+            mTaskAffinity = taskAffinity;
+            mIntent = intent;
+            mInfo = info;
+            mIdealRecord = null;
+            mCandidateRecord = null;
+        }
+
         /**
          * Returns the top activity in any existing task matching the given Intent in the input
          * result. Returns null if no such task is found.
          */
-        void process(ActivityRecord target, Task parent) {
-            mTarget = target;
-
-            intent = target.intent;
-            info = target.info;
-            cls = intent.getComponent();
-            if (info.targetActivity != null) {
-                cls = new ComponentName(info.packageName, info.targetActivity);
+        void process(WindowContainer parent) {
+            cls = mIntent.getComponent();
+            if (mInfo.targetActivity != null) {
+                cls = new ComponentName(mInfo.packageName, mInfo.targetActivity);
             }
-            userId = UserHandle.getUserId(info.applicationInfo.uid);
-            isDocument = intent != null & intent.isDocument();
+            userId = UserHandle.getUserId(mInfo.applicationInfo.uid);
+            isDocument = mIntent != null & mIntent.isDocument();
             // If documentData is non-null then it must match the existing task data.
-            documentData = isDocument ? intent.getData() : null;
+            documentData = isDocument ? mIntent.getData() : null;
 
-            ProtoLog.d(WM_DEBUG_TASKS, "Looking for task of %s in %s", target,
+            ProtoLog.d(WM_DEBUG_TASKS, "Looking for task of %s in %s", mInfo,
                     parent);
             parent.forAllLeafTasks(this);
         }
 
-        void clear() {
-            mRecord = null;
-            mIdealMatch = false;
-        }
-
-        void setTo(FindTaskResult result) {
-            mRecord = result.mRecord;
-            mIdealMatch = result.mIdealMatch;
-        }
-
         @Override
         public Boolean apply(Task task) {
+            if (!ConfigurationContainer.isCompatibleActivityType(mActivityType,
+                    task.getActivityType())) {
+                ProtoLog.d(WM_DEBUG_TASKS, "Skipping task: (mismatch activity/task) %s", task);
+                return false;
+            }
+
             if (task.voiceSession != null) {
                 // We never match voice sessions; those always run independently.
                 ProtoLog.d(WM_DEBUG_TASKS, "Skipping %s: voice session", task);
@@ -370,7 +372,8 @@
                 ProtoLog.d(WM_DEBUG_TASKS, "Skipping %s: mismatch root %s", task, r);
                 return false;
             }
-            if (!r.hasCompatibleActivityType(mTarget)) {
+            if (!ConfigurationContainer.isCompatibleActivityType(r.getActivityType(),
+                    mActivityType)) {
                 ProtoLog.d(WM_DEBUG_TASKS, "Skipping %s: mismatch activity type", task);
                 return false;
             }
@@ -391,35 +394,33 @@
             }
 
             ProtoLog.d(WM_DEBUG_TASKS, "Comparing existing cls=%s /aff=%s to new cls=%s /aff=%s",
-                    r.getTask().rootAffinity, intent.getComponent().flattenToShortString(),
-                    info.taskAffinity, (task.realActivity != null
+                    r.getTask().rootAffinity, mIntent.getComponent().flattenToShortString(),
+                    mInfo.taskAffinity, (task.realActivity != null
                             ? task.realActivity.flattenToShortString() : ""));
             // TODO Refactor to remove duplications. Check if logic can be simplified.
             if (task.realActivity != null && task.realActivity.compareTo(cls) == 0
                     && Objects.equals(documentData, taskDocumentData)) {
                 ProtoLog.d(WM_DEBUG_TASKS, "Found matching class!");
                 //dump();
-                ProtoLog.d(WM_DEBUG_TASKS, "For Intent %s bringing to top: %s", intent, r.intent);
-                mRecord = r;
-                mIdealMatch = true;
+                ProtoLog.d(WM_DEBUG_TASKS, "For Intent %s bringing to top: %s", mIntent, r.intent);
+                mIdealRecord = r;
                 return true;
             } else if (affinityIntent != null && affinityIntent.getComponent() != null
                     && affinityIntent.getComponent().compareTo(cls) == 0 &&
                     Objects.equals(documentData, taskDocumentData)) {
                 ProtoLog.d(WM_DEBUG_TASKS, "Found matching class!");
-                ProtoLog.d(WM_DEBUG_TASKS, "For Intent %s bringing to top: %s", intent, r.intent);
-                mRecord = r;
-                mIdealMatch = true;
+                ProtoLog.d(WM_DEBUG_TASKS, "For Intent %s bringing to top: %s", mIntent, r.intent);
+                mIdealRecord = r;
                 return true;
             } else if (!isDocument && !taskIsDocument
-                    && mRecord == null && task.rootAffinity != null) {
-                if (task.rootAffinity.equals(mTarget.taskAffinity)) {
+                    && mIdealRecord == null && mCandidateRecord == null
+                    && task.rootAffinity != null) {
+                if (task.rootAffinity.equals(mTaskAffinity)) {
                     ProtoLog.d(WM_DEBUG_TASKS, "Found matching affinity candidate!");
                     // It is possible for multiple tasks to have the same root affinity especially
                     // if they are in separate stacks. We save off this candidate, but keep looking
                     // to see if there is a better candidate.
-                    mRecord = r;
-                    mIdealMatch = false;
+                    mCandidateRecord = r;
                 }
             } else {
                 ProtoLog.d(WM_DEBUG_TASKS, "Not a match: %s", task);
@@ -2152,9 +2153,13 @@
             } else {
                 // In the case of multiple activities, we will create a new task for it and then
                 // move the PIP activity into the task.
-                rootTask = taskDisplayArea.createRootTask(WINDOWING_MODE_UNDEFINED,
-                        r.getActivityType(), ON_TOP, r.info, r.intent,
-                        false /* createdByOrganizer */);
+                rootTask = new Task.Builder(mService)
+                        .setActivityType(r.getActivityType())
+                        .setOnTop(true)
+                        .setActivityInfo(r.info)
+                        .setParent(taskDisplayArea)
+                        .setIntent(r.intent)
+                        .build();
                 // It's possible the task entering PIP is in freeform, so save the last
                 // non-fullscreen bounds. Then when this new PIP task exits PIP, it can restore
                 // to its previous freeform bounds.
@@ -2234,38 +2239,48 @@
 
     @Nullable
     ActivityRecord findTask(ActivityRecord r, TaskDisplayArea preferredTaskDisplayArea) {
-        ProtoLog.d(WM_DEBUG_TASKS, "Looking for task of %s", r);
-        mTmpFindTaskResult.clear();
+        return findTask(r.getActivityType(), r.taskAffinity, r.intent, r.info,
+                preferredTaskDisplayArea);
+    }
+
+    @Nullable
+    ActivityRecord findTask(int activityType, String taskAffinity, Intent intent, ActivityInfo info,
+            TaskDisplayArea preferredTaskDisplayArea) {
+        ProtoLog.d(WM_DEBUG_TASKS, "Looking for task of type=%s, taskAffinity=%s, intent=%s"
+                        + ", info=%s, preferredTDA=%s", activityType, taskAffinity, intent, info,
+                preferredTaskDisplayArea);
+        mTmpFindTaskResult.init(activityType, taskAffinity, intent, info);
 
         // Looking up task on preferred display area first
+        ActivityRecord candidateActivity = null;
         if (preferredTaskDisplayArea != null) {
-            preferredTaskDisplayArea.findTaskLocked(r, true /* isPreferredDisplay */,
-                    mTmpFindTaskResult);
-            if (mTmpFindTaskResult.mIdealMatch) {
-                return mTmpFindTaskResult.mRecord;
+            mTmpFindTaskResult.process(preferredTaskDisplayArea);
+            if (mTmpFindTaskResult.mIdealRecord != null) {
+                return mTmpFindTaskResult.mIdealRecord;
+            } else if (mTmpFindTaskResult.mCandidateRecord != null) {
+                candidateActivity = mTmpFindTaskResult.mCandidateRecord;
             }
         }
 
-        final ActivityRecord task = getItemFromTaskDisplayAreas(taskDisplayArea -> {
+        final ActivityRecord idealMatchActivity = getItemFromTaskDisplayAreas(taskDisplayArea -> {
             if (taskDisplayArea == preferredTaskDisplayArea) {
                 return null;
             }
 
-            taskDisplayArea.findTaskLocked(r, false /* isPreferredDisplay */,
-                    mTmpFindTaskResult);
-            if (mTmpFindTaskResult.mIdealMatch) {
-                return mTmpFindTaskResult.mRecord;
+            mTmpFindTaskResult.process(taskDisplayArea);
+            if (mTmpFindTaskResult.mIdealRecord != null) {
+                return mTmpFindTaskResult.mIdealRecord;
             }
             return null;
         });
-        if (task != null) {
-            return task;
+        if (idealMatchActivity != null) {
+            return idealMatchActivity;
         }
 
-        if (WM_DEBUG_TASKS.isEnabled() && mTmpFindTaskResult.mRecord == null) {
+        if (WM_DEBUG_TASKS.isEnabled() && candidateActivity == null) {
             ProtoLog.d(WM_DEBUG_TASKS, "No task found");
         }
-        return mTmpFindTaskResult.mRecord;
+        return candidateActivity;
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 57d48c6..0cefa95 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -61,7 +61,6 @@
 import android.util.ArraySet;
 import android.util.MergedConfiguration;
 import android.util.Slog;
-import android.view.DisplayCutout;
 import android.view.IWindow;
 import android.view.IWindowId;
 import android.view.IWindowSession;
@@ -185,22 +184,21 @@
     @Override
     public int addToDisplay(IWindow window, WindowManager.LayoutParams attrs,
             int viewVisibility, int displayId, InsetsState requestedVisibility, Rect outFrame,
-            DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
-            InsetsState outInsetsState, InsetsSourceControl[] outActiveControls) {
+            InputChannel outInputChannel, InsetsState outInsetsState,
+            InsetsSourceControl[] outActiveControls) {
         return mService.addWindow(this, window, attrs, viewVisibility, displayId,
-                UserHandle.getUserId(mUid), requestedVisibility, outFrame, outDisplayCutout,
-                outInputChannel, outInsetsState, outActiveControls);
+                UserHandle.getUserId(mUid), requestedVisibility, outFrame, outInputChannel,
+                outInsetsState, outActiveControls);
     }
 
 
     @Override
     public int addToDisplayAsUser(IWindow window, WindowManager.LayoutParams attrs,
             int viewVisibility, int displayId, int userId, InsetsState requestedVisibility,
-            Rect outFrame, DisplayCutout.ParcelableWrapper outDisplayCutout,
-            InputChannel outInputChannel, InsetsState outInsetsState,
+            Rect outFrame, InputChannel outInputChannel, InsetsState outInsetsState,
             InsetsSourceControl[] outActiveControls) {
         return mService.addWindow(this, window, attrs, viewVisibility, displayId, userId,
-                requestedVisibility, outFrame, outDisplayCutout, outInputChannel, outInsetsState,
+                requestedVisibility, outFrame, outInputChannel, outInsetsState,
                 outActiveControls);
     }
 
@@ -209,8 +207,8 @@
             int viewVisibility, int displayId, InsetsState outInsetsState) {
         return mService.addWindow(this, window, attrs, viewVisibility, displayId,
                 UserHandle.getUserId(mUid), mDummyRequestedVisibility,
-                new Rect() /* outFrame */, new DisplayCutout.ParcelableWrapper() /* cutout */,
-                null /* outInputChannel */, outInsetsState, mDummyControls);
+                new Rect() /* outFrame */, null /* outInputChannel */, outInsetsState,
+                mDummyControls);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 2290c23..aa88657 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -807,55 +807,20 @@
     // Tracking cookie for the creation of this task.
     IBinder mLaunchCookie;
 
-    /**
-     * Don't use constructor directly. Use {@link TaskDisplayArea#createStackUnchecked()} instead.
-     */
-    Task(ActivityTaskManagerService atmService, int id, int activityType, ActivityInfo info,
-            Intent intent, boolean createdByOrganizer, boolean deferTaskAppear,
-            IBinder launchCookie) {
-        this(atmService, id, info, intent, null /*voiceSession*/, null /*voiceInteractor*/,
-                null /*taskDescription*/, null /*stack*/);
-        mCreatedByOrganizer = createdByOrganizer;
-        mLaunchCookie = launchCookie;
-        mDeferTaskAppear = deferTaskAppear;
-        setActivityType(activityType);
-    }
-
-    /**
-     * Don't use constructor directly. Use {@link Task#reuseOrCreateTask()} instead.
-     */
-    Task(ActivityTaskManagerService atmService, int _taskId, ActivityInfo info, Intent _intent,
-            IVoiceInteractionSession _voiceSession, IVoiceInteractor _voiceInteractor,
-            TaskDescription _taskDescription, Task stack) {
-        this(atmService, _taskId, _intent,  null /*_affinityIntent*/, null /*_affinity*/,
-                null /*_rootAffinity*/, null /*_realActivity*/, null /*_origActivity*/,
-                false /*_rootWasReset*/, false /*_autoRemoveRecents*/, false /*_askedCompatMode*/,
-                UserHandle.getUserId(info.applicationInfo.uid), 0 /*_effectiveUid*/,
-                null /*_lastDescription*/, System.currentTimeMillis(),
-                true /*neverRelinquishIdentity*/,
-                _taskDescription != null ? _taskDescription : new TaskDescription(),
-                _taskId, INVALID_TASK_ID, INVALID_TASK_ID,
-                info.applicationInfo.uid, info.packageName, null /* default featureId */,
-                info.resizeMode, info.supportsPictureInPicture(), false /*_realActivitySuspended*/,
-                false /*userSetupComplete*/, INVALID_MIN_SIZE, INVALID_MIN_SIZE, info,
-                _voiceSession, _voiceInteractor, stack);
-    }
-
-    /** Don't use constructor directly. This is only used by XML parser. */
-    Task(ActivityTaskManagerService atmService, int _taskId, Intent _intent, Intent _affinityIntent,
-            String _affinity, String _rootAffinity, ComponentName _realActivity,
-            ComponentName _origActivity, boolean _rootWasReset, boolean _autoRemoveRecents,
-            boolean _askedCompatMode, int _userId, int _effectiveUid, String _lastDescription,
-            long lastTimeMoved, boolean neverRelinquishIdentity,
+    private Task(ActivityTaskManagerService atmService, int _taskId, Intent _intent,
+            Intent _affinityIntent, String _affinity, String _rootAffinity,
+            ComponentName _realActivity, ComponentName _origActivity, boolean _rootWasReset,
+            boolean _autoRemoveRecents, boolean _askedCompatMode, int _userId, int _effectiveUid,
+            String _lastDescription, long lastTimeMoved, boolean neverRelinquishIdentity,
             TaskDescription _lastTaskDescription, int taskAffiliation, int prevTaskId,
             int nextTaskId, int callingUid, String callingPackage,
             @Nullable String callingFeatureId, int resizeMode, boolean supportsPictureInPicture,
             boolean _realActivitySuspended, boolean userSetupComplete, int minWidth, int minHeight,
             ActivityInfo info, IVoiceInteractionSession _voiceSession,
-            IVoiceInteractor _voiceInteractor, Task stack) {
+            IVoiceInteractor _voiceInteractor, boolean _createdByOrganizer,
+            IBinder _launchCookie, boolean _deferTaskAppear) {
         super(atmService.mWindowManager);
 
-        EventLogTags.writeWmTaskCreated(_taskId, stack != null ? getRootTaskId() : INVALID_TASK_ID);
         mAtmService = atmService;
         mTaskSupervisor = atmService.mTaskSupervisor;
         mRootWindowContainer = mAtmService.mRootWindowContainer;
@@ -903,6 +868,11 @@
         mAtmService.getTaskChangeNotificationController().notifyTaskCreated(_taskId, realActivity);
         mHandler = new ActivityTaskHandler(mTaskSupervisor.mLooper);
         mCurrentUser = mAtmService.mAmInternal.getCurrentUserId();
+
+        mCreatedByOrganizer = _createdByOrganizer;
+        mLaunchCookie = _launchCookie;
+        mDeferTaskAppear = _deferTaskAppear;
+        EventLogTags.writeWmTaskCreated(mTaskId, isRootTask() ? INVALID_TASK_ID : getRootTaskId());
     }
 
     Task reuseAsLeafTask(IVoiceInteractionSession _voiceSession, IVoiceInteractor _voiceInteractor,
@@ -1926,8 +1896,9 @@
         if (r == boundaryActivity) return true;
 
         if (!r.finishing) {
-            final ActivityOptions opts = r.takeOptionsLocked(false /* fromClient */);
+            final ActivityOptions opts = r.getOptions();
             if (opts != null) {
+                r.clearOptionsAnimation();
                 // TODO: Why is this updating the boundary activity vs. the current activity???
                 boundaryActivity.updateOptionsLocked(opts);
             }
@@ -4450,8 +4421,17 @@
             sb.append(stringName);
             sb.append(" U=");
             sb.append(mUserId);
-            sb.append(" StackId=");
-            sb.append(getRootTaskId());
+            final Task rootTask = getRootTask();
+            if (rootTask != this) {
+                sb.append(" rootTaskId=");
+                sb.append(rootTask.mTaskId);
+            }
+            sb.append(" visible=");
+            sb.append(shouldBeVisible(null /* starting */));
+            sb.append(" mode=");
+            sb.append(windowingModeToString(getWindowingMode()));
+            sb.append(" translucent=");
+            sb.append(isTranslucent(null /* starting */));
             sb.append(" sz=");
             sb.append(getChildCount());
             sb.append('}');
@@ -4461,10 +4441,7 @@
         sb.append(Integer.toHexString(System.identityHashCode(this)));
         sb.append(" #");
         sb.append(mTaskId);
-        sb.append(" visible=" + shouldBeVisible(null /* starting */));
         sb.append(" type=" + activityTypeToString(getActivityType()));
-        sb.append(" mode=" + windowingModeToString(getWindowingMode()));
-        sb.append(" translucent=" + isTranslucent(null /* starting */));
         if (affinity != null) {
             sb.append(" A=");
             sb.append(affinity);
@@ -4830,14 +4807,36 @@
             }
         }
 
-        final Task task = new Task(taskSupervisor.mService, taskId, intent,
-                affinityIntent, affinity, rootAffinity, realActivity, origActivity, rootHasReset,
-                autoRemoveRecents, askedCompatMode, userId, effectiveUid, lastDescription,
-                lastTimeOnTop, neverRelinquishIdentity, taskDescription, taskAffiliation,
-                prevTaskId, nextTaskId, callingUid, callingPackage,
-                callingFeatureId, resizeMode, supportsPictureInPicture, realActivitySuspended,
-                userSetupComplete, minWidth, minHeight, null /*ActivityInfo*/,
-                null /*_voiceSession*/, null /*_voiceInteractor*/, null /* stack */);
+        final Task task = new Task.Builder(taskSupervisor.mService)
+                .setTaskId(taskId)
+                .setIntent(intent)
+                .setAffinityIntent(affinityIntent)
+                .setAffinity(affinity)
+                .setRootAffinity(rootAffinity)
+                .setRealActivity(realActivity)
+                .setOrigActivity(origActivity)
+                .setRootWasReset(rootHasReset)
+                .setAutoRemoveRecents(autoRemoveRecents)
+                .setAskedCompatMode(askedCompatMode)
+                .setUserId(userId)
+                .setEffectiveUid(effectiveUid)
+                .setLastDescription(lastDescription)
+                .setLastTimeMoved(lastTimeOnTop)
+                .setNeverRelinquishIdentity(neverRelinquishIdentity)
+                .setLastTaskDescription(taskDescription)
+                .setTaskAffiliation(taskAffiliation)
+                .setPrevAffiliateTaskId(prevTaskId)
+                .setNextAffiliateTaskId(nextTaskId)
+                .setCallingUid(callingUid)
+                .setCallingPackage(callingPackage)
+                .setCallingFeatureId(callingFeatureId)
+                .setResizeMode(resizeMode)
+                .setSupportsPictureInPicture(supportsPictureInPicture)
+                .setRealActivitySuspended(realActivitySuspended)
+                .setUserSetupComplete(userSetupComplete)
+                .setMinWidth(minWidth)
+                .setMinHeight(minHeight)
+                .buildInner();
         task.mLastNonFullscreenBounds = lastNonFullscreenBounds;
         task.setBounds(lastNonFullscreenBounds);
         task.mWindowLayoutAffinity = windowLayoutAffinity;
@@ -5187,9 +5186,6 @@
 
     @Override
     public void setWindowingMode(int windowingMode) {
-        // Reset the cached result of toString()
-        stringName = null;
-
         // Calling Task#setWindowingMode() for leaf task since this is the a specialization of
         // {@link #setWindowingMode(int)} for ActivityStack.
         if (!isRootTask()) {
@@ -6247,9 +6243,9 @@
         }
 
         if (anim) {
-            next.applyOptionsLocked();
+            next.applyOptionsAnimation();
         } else {
-            next.clearOptionsLocked();
+            next.abortAndClearOptionsAnimation();
         }
 
         mTaskSupervisor.mNoAnimActivities.clear();
@@ -6356,7 +6352,7 @@
 
                 mAtmService.getAppWarningsLocked().onResumeActivity(next);
                 next.app.setPendingUiCleanAndForceProcessStateUpTo(mAtmService.mTopProcessState);
-                next.clearOptionsLocked();
+                next.abortAndClearOptionsAnimation();
                 transaction.setLifecycleStateRequest(
                         ResumeActivityItem.obtain(next.app.getReportedProcState(),
                                 dc.isNextTransitionForward()));
@@ -7312,11 +7308,15 @@
             final int taskId = activity != null
                     ? mTaskSupervisor.getNextTaskIdForUser(activity.mUserId)
                     : mTaskSupervisor.getNextTaskIdForUser();
-            task = new Task(mAtmService, taskId, info, intent, voiceSession,
-                    voiceInteractor, null /* taskDescription */, this);
-
-            // add the task to stack first, mTaskPositioner might need the stack association
-            addChild(task, toTop, (info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0);
+            task = new Task.Builder(mAtmService)
+                    .setTaskId(taskId)
+                    .setActivityInfo(info)
+                    .setIntent(intent)
+                    .setVoiceSession(voiceSession)
+                    .setVoiceInteractor(voiceInteractor)
+                    .setOnTop(toTop)
+                    .setParent(this)
+                    .build();
         }
 
         int displayId = getDisplayId();
@@ -7717,4 +7717,368 @@
 
         proto.end(token);
     }
+
+    static class Builder {
+        private final ActivityTaskManagerService mAtmService;
+        private WindowContainer mParent;
+        private int mTaskId;
+        private Intent mIntent;
+        private Intent mAffinityIntent;
+        private String mAffinity;
+        private String mRootAffinity;
+        private ComponentName mRealActivity;
+        private ComponentName mOrigActivity;
+        private boolean mRootWasReset;
+        private boolean mAutoRemoveRecents;
+        private boolean mAskedCompatMode;
+        private int mUserId;
+        private int mEffectiveUid;
+        private String mLastDescription;
+        private long mLastTimeMoved;
+        private boolean mNeverRelinquishIdentity;
+        private TaskDescription mLastTaskDescription;
+        private int mTaskAffiliation;
+        private int mPrevAffiliateTaskId = INVALID_TASK_ID;
+        private int mNextAffiliateTaskId = INVALID_TASK_ID;
+        private int mCallingUid;
+        private String mCallingPackage;
+        private String mCallingFeatureId;
+        private int mResizeMode;
+        private boolean mSupportsPictureInPicture;
+        private boolean mRealActivitySuspended;
+        private boolean mUserSetupComplete;
+        private int mMinWidth = INVALID_MIN_SIZE;
+        private int mMinHeight = INVALID_MIN_SIZE;
+        private ActivityInfo mActivityInfo;
+        private IVoiceInteractionSession mVoiceSession;
+        private IVoiceInteractor mVoiceInteractor;
+        private int mActivityType;
+        private int mWindowingMode = WINDOWING_MODE_UNDEFINED;
+        private boolean mCreatedByOrganizer;
+        private boolean mDeferTaskAppear;
+        private IBinder mLaunchCookie;
+        private boolean mOnTop;
+
+        Builder(ActivityTaskManagerService atm) {
+            mAtmService = atm;
+        }
+
+        Builder setParent(WindowContainer parent) {
+            mParent = parent;
+            return this;
+        }
+
+        Builder setTaskId(int taskId) {
+            mTaskId = taskId;
+            return this;
+        }
+
+        Builder setIntent(Intent intent) {
+            mIntent = intent;
+            return this;
+        }
+
+        Builder setRealActivity(ComponentName realActivity) {
+            mRealActivity = realActivity;
+            return this;
+        }
+
+        Builder setEffectiveUid(int effectiveUid) {
+            mEffectiveUid = effectiveUid;
+            return this;
+        }
+
+        Builder setMinWidth(int minWidth) {
+            mMinWidth = minWidth;
+            return this;
+        }
+
+        Builder setMinHeight(int minHeight) {
+            mMinHeight = minHeight;
+            return this;
+        }
+
+        Builder setActivityInfo(ActivityInfo info) {
+            mActivityInfo = info;
+            return this;
+        }
+
+        Builder setVoiceSession(IVoiceInteractionSession voiceSession) {
+            mVoiceSession = voiceSession;
+            return this;
+        }
+
+        Builder setActivityType(int activityType) {
+            mActivityType = activityType;
+            return this;
+        }
+
+        int getActivityType() {
+            return mActivityType;
+        }
+
+        Builder setWindowingMode(int windowingMode) {
+            mWindowingMode = windowingMode;
+            return this;
+        }
+
+        int getWindowingMode() {
+            return mWindowingMode;
+        }
+
+        Builder setCreatedByOrganizer(boolean createdByOrganizer) {
+            mCreatedByOrganizer = createdByOrganizer;
+            return this;
+        }
+
+        boolean getCreatedByOrganizer() {
+            return mCreatedByOrganizer;
+        }
+
+        Builder setDeferTaskAppear(boolean defer) {
+            mDeferTaskAppear = defer;
+            return this;
+        }
+
+        Builder setLaunchCookie(IBinder launchCookie) {
+            mLaunchCookie = launchCookie;
+            return this;
+        }
+
+        Builder setOnTop(boolean onTop) {
+            mOnTop = onTop;
+            return this;
+        }
+
+        private Builder setUserId(int userId) {
+            mUserId = userId;
+            return this;
+        }
+
+        private Builder setLastTimeMoved(long lastTimeMoved) {
+            mLastTimeMoved = lastTimeMoved;
+            return this;
+        }
+
+        private Builder setNeverRelinquishIdentity(boolean neverRelinquishIdentity) {
+            mNeverRelinquishIdentity = neverRelinquishIdentity;
+            return this;
+        }
+
+        private Builder setCallingUid(int callingUid) {
+            mCallingUid = callingUid;
+            return this;
+        }
+
+        private Builder setCallingPackage(String callingPackage) {
+            mCallingPackage = callingPackage;
+            return this;
+        }
+
+        private Builder setResizeMode(int resizeMode) {
+            mResizeMode = resizeMode;
+            return this;
+        }
+
+        private Builder setSupportsPictureInPicture(boolean supportsPictureInPicture) {
+            mSupportsPictureInPicture = supportsPictureInPicture;
+            return this;
+        }
+
+        private Builder setUserSetupComplete(boolean userSetupComplete) {
+            mUserSetupComplete = userSetupComplete;
+            return this;
+        }
+
+        private Builder setTaskAffiliation(int taskAffiliation) {
+            mTaskAffiliation = taskAffiliation;
+            return this;
+        }
+
+        private Builder setPrevAffiliateTaskId(int prevAffiliateTaskId) {
+            mPrevAffiliateTaskId = prevAffiliateTaskId;
+            return this;
+        }
+
+        private Builder setNextAffiliateTaskId(int nextAffiliateTaskId) {
+            mNextAffiliateTaskId = nextAffiliateTaskId;
+            return this;
+        }
+
+        private Builder setCallingFeatureId(String callingFeatureId) {
+            mCallingFeatureId = callingFeatureId;
+            return this;
+        }
+
+        private Builder setRealActivitySuspended(boolean realActivitySuspended) {
+            mRealActivitySuspended = realActivitySuspended;
+            return this;
+        }
+
+        private Builder setLastDescription(String lastDescription) {
+            mLastDescription = lastDescription;
+            return this;
+        }
+
+        private Builder setLastTaskDescription(TaskDescription lastTaskDescription) {
+            mLastTaskDescription = lastTaskDescription;
+            return this;
+        }
+
+        private Builder setOrigActivity(ComponentName origActivity) {
+            mOrigActivity = origActivity;
+            return this;
+        }
+
+        private Builder setRootWasReset(boolean rootWasReset) {
+            mRootWasReset = rootWasReset;
+            return this;
+        }
+
+        private Builder setAutoRemoveRecents(boolean autoRemoveRecents) {
+            mAutoRemoveRecents = autoRemoveRecents;
+            return this;
+        }
+
+        private Builder setAskedCompatMode(boolean askedCompatMode) {
+            mAskedCompatMode = askedCompatMode;
+            return this;
+        }
+
+        private Builder setAffinityIntent(Intent affinityIntent) {
+            mAffinityIntent = affinityIntent;
+            return this;
+        }
+
+        private Builder setAffinity(String affinity) {
+            mAffinity = affinity;
+            return this;
+        }
+
+        private Builder setRootAffinity(String rootAffinity) {
+            mRootAffinity = rootAffinity;
+            return this;
+        }
+
+        private Builder setVoiceInteractor(IVoiceInteractor voiceInteractor) {
+            mVoiceInteractor = voiceInteractor;
+            return this;
+        }
+
+        private void validateRootTask(TaskDisplayArea tda) {
+            if (mActivityType == ACTIVITY_TYPE_UNDEFINED && !mCreatedByOrganizer) {
+                // Can't have an undefined root task type yet...so re-map to standard. Anyone
+                // that wants anything else should be passing it in anyways...except for the task
+                // organizer.
+                mActivityType = ACTIVITY_TYPE_STANDARD;
+            }
+
+            if (mActivityType != ACTIVITY_TYPE_STANDARD
+                    && mActivityType != ACTIVITY_TYPE_UNDEFINED) {
+                // For now there can be only one root task of a particular non-standard activity
+                // type on a display. So, get that ignoring whatever windowing mode it is
+                // currently in.
+                Task rootTask = tda.getRootTask(WINDOWING_MODE_UNDEFINED, mActivityType);
+                if (rootTask != null) {
+                    throw new IllegalArgumentException("Root task=" + rootTask + " of activityType="
+                            + mActivityType + " already on display=" + tda
+                            + ". Can't have multiple.");
+                }
+            }
+
+            if (!TaskDisplayArea.isWindowingModeSupported(mWindowingMode,
+                    mAtmService.mSupportsMultiWindow,
+                    mAtmService.mSupportsSplitScreenMultiWindow,
+                    mAtmService.mSupportsFreeformWindowManagement,
+                    mAtmService.mSupportsPictureInPicture, mActivityType)) {
+                throw new IllegalArgumentException("Can't create root task for unsupported "
+                        + "windowingMode=" + mWindowingMode);
+            }
+
+            if (mWindowingMode == WINDOWING_MODE_PINNED
+                    && mActivityType != ACTIVITY_TYPE_STANDARD) {
+                throw new IllegalArgumentException(
+                        "Root task with pinned windowing mode cannot with "
+                                + "non-standard activity type.");
+            }
+
+            if (mWindowingMode == WINDOWING_MODE_PINNED && tda.getRootPinnedTask() != null) {
+                // Only 1 root task can be PINNED at a time, so dismiss the existing one
+                tda.getRootPinnedTask().dismissPip();
+            }
+
+            // Task created by organizer are added as root.
+            final Task launchRootTask = mCreatedByOrganizer
+                    ? null : tda.updateLaunchRootTask(mWindowingMode);
+            if (launchRootTask != null) {
+                // Since this task will be put into a root task, its windowingMode will be
+                // inherited.
+                mWindowingMode = WINDOWING_MODE_UNDEFINED;
+                mParent = launchRootTask;
+            }
+
+            mTaskId = tda.getNextRootTaskId();
+        }
+
+        Task build() {
+            if (mParent != null && mParent instanceof TaskDisplayArea) {
+                validateRootTask((TaskDisplayArea) mParent);
+            }
+
+            if (mActivityInfo == null) {
+                mActivityInfo = new ActivityInfo();
+                mActivityInfo.applicationInfo = new ApplicationInfo();
+            }
+
+            mUserId = UserHandle.getUserId(mActivityInfo.applicationInfo.uid);
+            mTaskAffiliation = mTaskId;
+            mLastTimeMoved = System.currentTimeMillis();
+            mNeverRelinquishIdentity = true;
+            mCallingUid = mActivityInfo.applicationInfo.uid;
+            mCallingPackage = mActivityInfo.packageName;
+            mResizeMode = mActivityInfo.resizeMode;
+            mSupportsPictureInPicture = mActivityInfo.supportsPictureInPicture();
+            if (mLastTaskDescription == null) {
+                mLastTaskDescription = new TaskDescription();
+            }
+
+            final Task task = buildInner();
+
+            // Set activity type before adding the root task to TaskDisplayArea, so home task can
+            // be cached, see TaskDisplayArea#addRootTaskReferenceIfNeeded().
+            if (mActivityType != ACTIVITY_TYPE_UNDEFINED) {
+                task.setActivityType(mActivityType);
+            }
+
+            if (mParent != null) {
+                if (mParent instanceof Task) {
+                    final Task parentTask = (Task) mParent;
+                    parentTask.addChild(task, mOnTop ? POSITION_TOP : POSITION_BOTTOM,
+                            (mActivityInfo.flags & FLAG_SHOW_FOR_ALL_USERS) != 0);
+                } else {
+                    mParent.addChild(task, mOnTop ? POSITION_TOP : POSITION_BOTTOM);
+                }
+            }
+
+            // Set windowing mode after attached to display area or it abort silently.
+            if (mWindowingMode != WINDOWING_MODE_UNDEFINED) {
+                task.setWindowingMode(mWindowingMode, true /* creating */);
+            }
+            return task;
+        }
+
+        /** Don't use {@link Builder#buildInner()} directly. This is only used by XML parser. */
+        @VisibleForTesting
+        Task buildInner() {
+            return new Task(mAtmService, mTaskId, mIntent, mAffinityIntent, mAffinity,
+                    mRootAffinity, mRealActivity, mOrigActivity, mRootWasReset, mAutoRemoveRecents,
+                    mAskedCompatMode, mUserId, mEffectiveUid, mLastDescription, mLastTimeMoved,
+                    mNeverRelinquishIdentity, mLastTaskDescription, mTaskAffiliation,
+                    mPrevAffiliateTaskId, mNextAffiliateTaskId, mCallingUid, mCallingPackage,
+                    mCallingFeatureId, mResizeMode, mSupportsPictureInPicture,
+                    mRealActivitySuspended, mUserSetupComplete, mMinWidth, mMinHeight,
+                    mActivityInfo, mVoiceSession, mVoiceInteractor, mCreatedByOrganizer,
+                    mLaunchCookie, mDeferTaskAppear);
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java
index 97383e3..3f4150b 100644
--- a/services/core/java/com/android/server/wm/TaskDisplayArea.java
+++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java
@@ -35,7 +35,6 @@
 
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION;
 import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_STATES;
-import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_TASKS;
 import static com.android.server.wm.ActivityTaskManagerService.TAG_ROOT_TASK;
 import static com.android.server.wm.DisplayContent.alwaysCreateRootTask;
 import static com.android.server.wm.Task.ActivityState.RESUMED;
@@ -47,9 +46,6 @@
 import android.app.ActivityOptions;
 import android.app.WindowConfiguration;
 import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
-import android.os.IBinder;
 import android.os.UserHandle;
 import android.util.IntArray;
 import android.util.Slog;
@@ -131,9 +127,6 @@
      */
     Task mPreferredTopFocusableRootTask;
 
-    private final RootWindowContainer.FindTaskResult
-            mTmpFindTaskResult = new RootWindowContainer.FindTaskResult();
-
     /**
      * If this is the same as {@link #getFocusedRootTask} then the activity on the top of the
      * focused root task has been resumed. If root tasks are changing position this will hold the
@@ -1045,8 +1038,13 @@
             }
             return stack;
         }
-        return createRootTask(windowingMode, activityType, onTop, null /*info*/, intent,
-                false /* createdByOrganizer */);
+        return new Task.Builder(mAtmService)
+                .setWindowingMode(windowingMode)
+                .setActivityType(activityType)
+                .setOnTop(onTop)
+                .setParent(this)
+                .setIntent(intent)
+                .build();
     }
 
     /**
@@ -1074,79 +1072,32 @@
         return mAtmService.mTaskSupervisor.getNextTaskIdForUser();
     }
 
-    Task createRootTask(int windowingMode, int activityType, boolean onTop) {
-        return createRootTask(windowingMode, activityType, onTop, null /* info */,
-                null /* intent */, false /* createdByOrganizer */);
-    }
-
-    Task createRootTask(int windowingMode, int activityType, boolean onTop, ActivityInfo info,
-            Intent intent, boolean createdByOrganizer) {
-        return createRootTask(windowingMode, activityType, onTop, null /* info */,
-                null /* intent */, false /* createdByOrganizer */, false /* deferTaskAppear */,
-                null /* launchCookie */);
-    }
-
     /**
-     * Creates a stack matching the input windowing mode and activity type on this display.
+     * A convinenit method of creating a root task by providing windowing mode and activity type
+     * on this display.
      *
-     * @param windowingMode      The windowing mode the stack should be created in. If
-     *                           {@link WindowConfiguration#WINDOWING_MODE_UNDEFINED} then the stack
-     *                           will
-     *                           inherit its parent's windowing mode.
-     * @param activityType       The activityType the stack should be created in. If
-     *                           {@link WindowConfiguration#ACTIVITY_TYPE_UNDEFINED} then the stack
-     *                           will
-     *                           be created in {@link WindowConfiguration#ACTIVITY_TYPE_STANDARD}.
-     * @param onTop              If true the stack will be created at the top of the display, else
-     *                           at the bottom.
-     * @param info               The started activity info.
-     * @param intent             The intent that started this task.
-     * @param createdByOrganizer @{code true} if this is created by task organizer, @{code false}
-     *                           otherwise.
-     * @param deferTaskAppear    @{code true} if the task appeared signal should be deferred.
-     * @param launchCookie       Launch cookie used for tracking/association of the task we are
-     *                           creating.
-     * @return The newly created stack.
+     * @param windowingMode      The windowing mode the root task should be created in. If
+     *                           {@link WindowConfiguration#WINDOWING_MODE_UNDEFINED} then the
+     *                           root task will inherit its parent's windowing mode.
+     * @param activityType       The activityType the root task should be created in. If
+     *                           {@link WindowConfiguration#ACTIVITY_TYPE_UNDEFINED} then the
+     *                           root task will be created in
+     *                           {@link WindowConfiguration#ACTIVITY_TYPE_STANDARD}.
+     * @param onTop              If true the root task will be created at the top of the display,
+     *                           else at the bottom.
+     * @return The newly created root task.
      */
-    Task createRootTask(int windowingMode, int activityType, boolean onTop, ActivityInfo info,
-            Intent intent, boolean createdByOrganizer, boolean deferTaskAppear,
-            IBinder launchCookie) {
-        if (activityType == ACTIVITY_TYPE_UNDEFINED && !createdByOrganizer) {
-            // Can't have an undefined stack type yet...so re-map to standard. Anyone that wants
-            // anything else should be passing it in anyways...except for the task organizer.
-            activityType = ACTIVITY_TYPE_STANDARD;
-        }
-
-        if (activityType != ACTIVITY_TYPE_STANDARD && activityType != ACTIVITY_TYPE_UNDEFINED) {
-            // For now there can be only one stack of a particular non-standard activity type on a
-            // display. So, get that ignoring whatever windowing mode it is currently in.
-            Task stack = getRootTask(WINDOWING_MODE_UNDEFINED, activityType);
-            if (stack != null) {
-                throw new IllegalArgumentException("Stack=" + stack + " of activityType="
-                        + activityType + " already on display=" + this + ". Can't have multiple.");
-            }
-        }
-
-        if (!isWindowingModeSupported(windowingMode, mAtmService.mSupportsMultiWindow,
-                mAtmService.mSupportsSplitScreenMultiWindow,
-                mAtmService.mSupportsFreeformWindowManagement,
-                mAtmService.mSupportsPictureInPicture, activityType)) {
-            throw new IllegalArgumentException("Can't create stack for unsupported windowingMode="
-                    + windowingMode);
-        }
-
-        if (windowingMode == WINDOWING_MODE_PINNED && getRootPinnedTask() != null) {
-            // Only 1 stack can be PINNED at a time, so dismiss the existing one
-            getRootPinnedTask().dismissPip();
-        }
-
-        final int stackId = getNextRootTaskId();
-        return createRootTaskUnchecked(windowingMode, activityType, stackId, onTop, info, intent,
-                createdByOrganizer, deferTaskAppear, launchCookie);
+    Task createRootTask(int windowingMode, int activityType, boolean onTop) {
+        return new Task.Builder(mAtmService)
+                .setWindowingMode(windowingMode)
+                .setActivityType(activityType)
+                .setParent(this)
+                .setOnTop(onTop)
+                .build();
     }
 
     /** @return the root task to create the next task in. */
-    private Task updateLaunchRootTask(int windowingMode) {
+    Task updateLaunchRootTask(int windowingMode) {
         if (!isSplitScreenWindowingMode(windowingMode)) {
             // Only split-screen windowing modes can do this currently...
             return null;
@@ -1181,40 +1132,6 @@
         return mLaunchRootTask;
     }
 
-    @VisibleForTesting
-    Task createRootTaskUnchecked(int windowingMode, int activityType, int stackId, boolean onTop,
-            ActivityInfo info, Intent intent, boolean createdByOrganizer, boolean deferTaskAppear,
-            IBinder launchCookie) {
-        if (windowingMode == WINDOWING_MODE_PINNED && activityType != ACTIVITY_TYPE_STANDARD) {
-            throw new IllegalArgumentException("Stack with windowing mode cannot with non standard "
-                    + "activity type.");
-        }
-        if (info == null) {
-            info = new ActivityInfo();
-            info.applicationInfo = new ApplicationInfo();
-        }
-
-        // Task created by organizer are added as root.
-        Task launchRootTask = createdByOrganizer ? null : updateLaunchRootTask(windowingMode);
-        if (launchRootTask != null) {
-            // Since this stack will be put into a root task, its windowingMode will be inherited.
-            windowingMode = WINDOWING_MODE_UNDEFINED;
-        }
-
-        final Task stack = new Task(mAtmService, stackId, activityType,
-                info, intent, createdByOrganizer, deferTaskAppear, launchCookie);
-        if (launchRootTask != null) {
-            launchRootTask.addChild(stack, onTop ? POSITION_TOP : POSITION_BOTTOM);
-            if (onTop) {
-                positionChildAt(POSITION_TOP, launchRootTask, false /* includingParents */);
-            }
-        } else {
-            addChild(stack, onTop ? POSITION_TOP : POSITION_BOTTOM);
-            stack.setWindowingMode(windowingMode, true /* creating */);
-        }
-        return stack;
-    }
-
     /**
      * Get the preferred focusable root task in priority. If the preferred root task does not exist,
      * find a focusable and visible root task from the top of root tasks in this display.
@@ -1391,43 +1308,6 @@
         return someActivityPaused[0] > 0;
     }
 
-    /**
-     * Find task for putting the Activity in.
-     */
-    void findTaskLocked(final ActivityRecord r, final boolean isPreferredDisplayArea,
-            RootWindowContainer.FindTaskResult result) {
-        mTmpFindTaskResult.clear();
-        for (int i = mChildren.size() - 1; i >= 0; --i) {
-            final Task rootTask = mChildren.get(i).asTask();
-            if (rootTask == null) {
-                continue;
-            }
-            if (!r.hasCompatibleActivityType(rootTask) && rootTask.isLeafTask()) {
-                ProtoLog.d(WM_DEBUG_TASKS, "Skipping rootTask: (mismatch activity/rootTask) "
-                        + "%s", rootTask);
-                continue;
-            }
-
-            mTmpFindTaskResult.process(r, rootTask);
-            // It is possible to have tasks in multiple root tasks with the same root affinity, so
-            // we should keep looking after finding an affinity match to see if there is a
-            // better match in another root task. Also, task affinity isn't a good enough reason
-            // to target a display which isn't the source of the intent, so skip any affinity
-            // matches not on the specified display.
-            if (mTmpFindTaskResult.mRecord != null) {
-                if (mTmpFindTaskResult.mIdealMatch) {
-                    result.setTo(mTmpFindTaskResult);
-                    return;
-                } else if (isPreferredDisplayArea) {
-                    // Note: since the traversing through the root tasks is top down, the floating
-                    // tasks should always have lower priority than any affinity-matching tasks
-                    // in the fullscreen root tasks
-                    result.setTo(mTmpFindTaskResult);
-                }
-            }
-        }
-    }
-
     void onSplitScreenModeDismissed() {
         // The focused task could be a non-resizeable fullscreen root task that is on top of the
         // other split-screen tasks, therefore had to dismiss split-screen, make sure the current
@@ -1492,7 +1372,7 @@
      * @param activityType        The activity type under consideration.
      * @return true if the windowing mode is supported.
      */
-    private boolean isWindowingModeSupported(int windowingMode, boolean supportsMultiWindow,
+    static boolean isWindowingModeSupported(int windowingMode, boolean supportsMultiWindow,
             boolean supportsSplitScreen, boolean supportsFreeform, boolean supportsPip,
             int activityType) {
 
diff --git a/services/core/java/com/android/server/wm/TaskOrganizerController.java b/services/core/java/com/android/server/wm/TaskOrganizerController.java
index 929d7bf..089071f 100644
--- a/services/core/java/com/android/server/wm/TaskOrganizerController.java
+++ b/services/core/java/com/android/server/wm/TaskOrganizerController.java
@@ -16,7 +16,6 @@
 
 package com.android.server.wm;
 
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
 
@@ -480,9 +479,14 @@
         // We want to defer the task appear signal until the task is fully created and attached to
         // to the hierarchy so that the complete starting configuration is in the task info we send
         // over to the organizer.
-        final Task task = display.getDefaultTaskDisplayArea().createRootTask(windowingMode,
-                ACTIVITY_TYPE_UNDEFINED, false /* onTop */, null /* info */, new Intent(),
-                true /* createdByOrganizer */, true /* deferTaskAppear */, launchCookie);
+        final Task task = new Task.Builder(mService)
+                .setWindowingMode(windowingMode)
+                .setIntent(new Intent())
+                .setCreatedByOrganizer(true)
+                .setDeferTaskAppear(true)
+                .setLaunchCookie(launchCookie)
+                .setParent(display.getDefaultTaskDisplayArea())
+                .build();
         task.setDeferTaskAppear(false /* deferTaskAppear */);
         return task;
     }
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
index 8c458a2..09df71c 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
@@ -227,7 +227,7 @@
         int displayId = activity.getDisplayContent().getDisplayId();
         try {
             final int res = session.addToDisplay(window, layoutParams,
-                    View.GONE, displayId, mTmpInsetsState, tmpFrames.frame, tmpFrames.displayCutout,
+                    View.GONE, displayId, mTmpInsetsState, tmpFrames.frame,
                     null /* outInputChannel */, mTmpInsetsState, mTempControls);
             if (res < 0) {
                 Slog.w(TAG, "Failed to add snapshot starting window res=" + res);
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 14504d8..be78d7a 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -30,6 +30,10 @@
 import static android.view.WindowManager.TRANSIT_OPEN;
 import static android.view.WindowManager.TRANSIT_TO_BACK;
 import static android.view.WindowManager.TRANSIT_TO_FRONT;
+import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
+import static android.window.TransitionInfo.FLAG_SHOW_WALLPAPER;
+import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT;
+import static android.window.TransitionInfo.FLAG_TRANSLUCENT;
 
 import android.annotation.IntDef;
 import android.annotation.NonNull;
@@ -160,8 +164,20 @@
         }
         if (mParticipants.contains(wc)) return;
         mSyncEngine.addToSyncSet(mSyncId, wc);
-        mChanges.put(wc, new ChangeInfo(wc));
+        ChangeInfo info = mChanges.get(wc);
+        if (info == null) {
+            info = new ChangeInfo(wc);
+            mChanges.put(wc, info);
+        }
         mParticipants.add(wc);
+        if (info.mShowWallpaper) {
+            // Collect the wallpaper so it is part of the sync set.
+            final WindowContainer wallpaper =
+                    wc.getDisplayContent().mWallpaperController.getTopVisibleWallpaper();
+            if (wallpaper != null) {
+                collect(wallpaper);
+            }
+        }
     }
 
     /**
@@ -386,6 +402,10 @@
         return -1;
     }
 
+    private static boolean isWallpaper(WindowContainer wc) {
+        return wc instanceof WallpaperWindowToken;
+    }
+
     /**
      * Under some conditions (eg. all visible targets within a parent container are transitioning
      * the same way) the transition can be "promoted" to the parent container. This means an
@@ -403,6 +423,10 @@
                     parent == null ? "no parent" : ("parent can't be target " + parent));
             return false;
         }
+        if (isWallpaper(target)) {
+            ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS, "      SKIP: is wallpaper");
+            return false;
+        }
         @TransitionInfo.TransitionMode int mode = TRANSIT_NONE;
         // Go through all siblings of this target to see if any of them would prevent
         // the target from promoting.
@@ -604,18 +628,32 @@
     static TransitionInfo calculateTransitionInfo(int type, int flags,
             ArraySet<WindowContainer> targets, ArrayMap<WindowContainer, ChangeInfo> changes) {
         final TransitionInfo out = new TransitionInfo(type, flags);
-        if (targets.isEmpty()) {
+
+        final ArraySet<WindowContainer> appTargets = new ArraySet<>();
+        final ArraySet<WindowContainer> wallpapers = new ArraySet<>();
+        for (int i = targets.size() - 1; i >= 0; --i) {
+            (isWallpaper(targets.valueAt(i)) ? wallpapers : appTargets).add(targets.valueAt(i));
+        }
+
+        // Find the top-most shared ancestor of app targets
+        WindowContainer ancestor = null;
+        for (int i = appTargets.size() - 1; i >= 0; --i) {
+            final WindowContainer wc = appTargets.valueAt(i);
+            ancestor = wc;
+            break;
+        }
+        if (ancestor == null) {
             out.setRootLeash(new SurfaceControl(), 0, 0);
             return out;
         }
+        ancestor = ancestor.getParent();
 
-        // Find the top-most shared ancestor
-        WindowContainer ancestor = targets.valueAt(0).getParent();
-        // Go up ancestor parent chain until all topTargets are descendants.
+        // Go up ancestor parent chain until all targets are descendants.
         ancestorLoop:
         while (ancestor != null) {
-            for (int i = 1; i < targets.size(); ++i) {
-                if (!targets.valueAt(i).isDescendantOf(ancestor)) {
+            for (int i = appTargets.size() - 1; i >= 0; --i) {
+                final WindowContainer wc = appTargets.valueAt(i);
+                if (!wc.isDescendantOf(ancestor)) {
                     ancestor = ancestor.getParent();
                     continue ancestorLoop;
                 }
@@ -623,7 +661,8 @@
             break;
         }
 
-        // Sort targets top-to-bottom in Z.
+        // Sort targets top-to-bottom in Z. Check ALL targets here in case the display area itself
+        // is animating: then we want to include wallpapers at the right position.
         ArrayList<WindowContainer> sortedTargets = new ArrayList<>();
         addMembersInOrder(ancestor, targets, sortedTargets);
 
@@ -640,6 +679,14 @@
         t.close();
         out.setRootLeash(rootLeash, ancestor.getBounds().left, ancestor.getBounds().top);
 
+        // add the wallpapers at the bottom
+        for (int i = wallpapers.size() - 1; i >= 0; --i) {
+            final WindowContainer wc = wallpapers.valueAt(i);
+            // If the displayarea itself is animating, then the wallpaper was already added.
+            if (wc.isDescendantOf(ancestor)) break;
+            sortedTargets.add(wc);
+        }
+
         // Convert all the resolved ChangeInfos into TransactionInfo.Change objects in order.
         final int count = sortedTargets.size();
         for (int i = 0; i < count; ++i) {
@@ -656,6 +703,7 @@
             change.setEndAbsBounds(target.getBounds());
             change.setEndRelOffset(target.getBounds().left - target.getParent().getBounds().left,
                     target.getBounds().top - target.getParent().getBounds().top);
+            change.setFlags(info.getChangeFlags(target));
             out.addChange(change);
         }
 
@@ -678,17 +726,20 @@
         boolean mVisible;
         int mWindowingMode;
         final Rect mAbsoluteBounds = new Rect();
+        boolean mShowWallpaper;
 
         ChangeInfo(@NonNull WindowContainer origState) {
             mVisible = origState.isVisibleRequested();
             mWindowingMode = origState.getWindowingMode();
             mAbsoluteBounds.set(origState.getBounds());
+            mShowWallpaper = origState.showWallpaper();
         }
 
         @VisibleForTesting
         ChangeInfo(boolean visible, boolean existChange) {
             mVisible = visible;
             mExistenceChanged = existChange;
+            mShowWallpaper = false;
         }
 
         boolean hasChanged(@NonNull WindowContainer newState) {
@@ -716,6 +767,29 @@
             }
         }
 
+        @TransitionInfo.ChangeFlags
+        int getChangeFlags(@NonNull WindowContainer wc) {
+            int flags = 0;
+            if (mShowWallpaper || wc.showWallpaper()) {
+                flags |= FLAG_SHOW_WALLPAPER;
+            }
+            if (!wc.fillsParent()) {
+                // TODO(b/172695805): hierarchical check. This is non-trivial because for containers
+                //                    it is effected by child visibility but needs to work even
+                //                    before visibility is committed. This means refactoring some
+                //                    checks to use requested visibility.
+                flags |= FLAG_TRANSLUCENT;
+            }
+            if (wc instanceof ActivityRecord
+                    && wc.asActivityRecord().mUseTransferredAnimation) {
+                flags |= FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT;
+            }
+            if (isWallpaper(wc)) {
+                flags |= FLAG_IS_WALLPAPER;
+            }
+            return flags;
+        }
+
         void addChild(@NonNull WindowContainer wc) {
             if (mChildren == null) {
                 mChildren = new ArraySet<>();
diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java
index 7809cbc..1a3138d 100644
--- a/services/core/java/com/android/server/wm/WallpaperController.java
+++ b/services/core/java/com/android/server/wm/WallpaperController.java
@@ -779,7 +779,7 @@
                 wallpaperBuffer.getHardwareBuffer(), wallpaperBuffer.getColorSpace());
     }
 
-    private WindowState getTopVisibleWallpaper() {
+    WindowState getTopVisibleWallpaper() {
         mTmpTopWallpaper = null;
 
         for (int curTokenNdx = mWallpaperTokens.size() - 1; curTokenNdx >= 0; curTokenNdx--) {
diff --git a/services/core/java/com/android/server/wm/WallpaperWindowToken.java b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
index c27d0cd..f572e8e 100644
--- a/services/core/java/com/android/server/wm/WallpaperWindowToken.java
+++ b/services/core/java/com/android/server/wm/WallpaperWindowToken.java
@@ -96,6 +96,7 @@
 
     void updateWallpaperVisibility(boolean visible) {
         if (isVisible() != visible) {
+            mWmService.mAtmService.getTransitionController().collect(this);
             // Need to do a layout to ensure the wallpaper now has the correct size.
             mDisplayContent.setLayoutNeeded();
         }
@@ -126,6 +127,7 @@
         if (isVisible() != visible) {
             if (DEBUG_WALLPAPER_LIGHT) Slog.d(TAG,
                     "Wallpaper token " + token + " visible=" + visible);
+            mWmService.mAtmService.getTransitionController().collect(this);
             // Need to do a layout to ensure the wallpaper now has the correct size.
             mDisplayContent.setLayoutNeeded();
         }
@@ -200,6 +202,17 @@
     }
 
     @Override
+    boolean fillsParent() {
+        return true;
+    }
+
+    @Override
+    boolean showWallpaper() {
+        return false;
+    }
+
+
+    @Override
     public String toString() {
         if (stringName == null) {
             StringBuilder sb = new StringBuilder();
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 576d224..19a750a 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -3077,6 +3077,23 @@
         return true;
     }
 
+    /** @return {@code true} if the wallpaper is visible behind this container. */
+    boolean showWallpaper() {
+        if (!isVisibleRequested()
+                // in multi-window mode, wallpaper is always visible at the back and not tied to
+                // the app (there is no wallpaper target).
+                || inMultiWindowMode()) {
+            return false;
+        }
+        for (int i = mChildren.size() - 1; i >= 0; --i) {
+            final WindowContainer child = mChildren.get(i);
+            if (child.showWallpaper()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     @Nullable
     static WindowContainer fromBinder(IBinder binder) {
         return RemoteToken.fromBinder(binder).getContainer();
diff --git a/services/core/java/com/android/server/wm/WindowFrames.java b/services/core/java/com/android/server/wm/WindowFrames.java
index 7991ec6..361694b 100644
--- a/services/core/java/com/android/server/wm/WindowFrames.java
+++ b/services/core/java/com/android/server/wm/WindowFrames.java
@@ -17,7 +17,6 @@
 package com.android.server.wm;
 
 import static com.android.server.wm.WindowFramesProto.CONTAINING_FRAME;
-import static com.android.server.wm.WindowFramesProto.CUTOUT;
 import static com.android.server.wm.WindowFramesProto.DISPLAY_FRAME;
 import static com.android.server.wm.WindowFramesProto.FRAME;
 import static com.android.server.wm.WindowFramesProto.PARENT_FRAME;
@@ -25,9 +24,6 @@
 import android.annotation.NonNull;
 import android.graphics.Rect;
 import android.util.proto.ProtoOutputStream;
-import android.view.DisplayCutout;
-
-import com.android.server.wm.utils.WmDisplayCutout;
 
 import java.io.PrintWriter;
 
@@ -96,30 +92,11 @@
      */
     private boolean mParentFrameWasClippedByDisplayCutout;
 
-    /**
-     * Part of the display that has been cut away. See {@link DisplayCutout}.
-     */
-    WmDisplayCutout mDisplayCutout = WmDisplayCutout.NO_CUTOUT;
-
-    /**
-     * The last cutout that has been reported to the client.
-     */
-    private WmDisplayCutout mLastDisplayCutout = WmDisplayCutout.NO_CUTOUT;
-
-    private boolean mDisplayCutoutChanged;
-
     boolean mLastForceReportingResized = false;
     boolean mForceReportingResized = false;
 
     private boolean mContentChanged;
 
-    public WindowFrames() {
-    }
-
-    public WindowFrames(Rect parentFrame, Rect displayFrame) {
-        setFrames(parentFrame, displayFrame);
-    }
-
     public void setFrames(Rect parentFrame, Rect displayFrame) {
         mParentFrame.set(parentFrame);
         mDisplayFrame.set(displayFrame);
@@ -134,10 +111,6 @@
         return mParentFrameWasClippedByDisplayCutout;
     }
 
-    public void setDisplayCutout(WmDisplayCutout displayCutout) {
-        mDisplayCutout = displayCutout;
-    }
-
     /**
      * @return true if the width or height has changed since last reported to the client.
      */
@@ -157,8 +130,7 @@
     boolean setReportResizeHints() {
         mLastForceReportingResized |= mForceReportingResized;
         mFrameSizeChanged |= didFrameSizeChange();
-        mDisplayCutoutChanged |= !mLastDisplayCutout.equals(mDisplayCutout);
-        return mLastForceReportingResized || mFrameSizeChanged || mDisplayCutoutChanged;
+        return mLastForceReportingResized || mFrameSizeChanged;
     }
 
     /**
@@ -168,7 +140,6 @@
     void clearReportResizeHints() {
         mLastForceReportingResized = false;
         mFrameSizeChanged = false;
-        mDisplayCutoutChanged = false;
     }
 
     /**
@@ -176,7 +147,6 @@
      */
     void onResizeHandled() {
         mForceReportingResized = false;
-        mLastDisplayCutout = mDisplayCutout;
     }
 
     /**
@@ -207,7 +177,6 @@
         mDisplayFrame.dumpDebug(proto, DISPLAY_FRAME);
         mContainingFrame.dumpDebug(proto, CONTAINING_FRAME);
         mFrame.dumpDebug(proto, FRAME);
-        mDisplayCutout.getDisplayCutout().dumpDebug(proto, CUTOUT);
 
         proto.end(token);
     }
@@ -219,12 +188,9 @@
                 + " display=" + mDisplayFrame.toShortString(sTmpSB));
         pw.println(prefix + "mFrame=" + mFrame.toShortString(sTmpSB)
                 + " last=" + mLastFrame.toShortString(sTmpSB));
-        pw.println(prefix + " cutout=" + mDisplayCutout.getDisplayCutout()
-                + " last=" + mLastDisplayCutout.getDisplayCutout());
     }
 
     String getInsetsChangedInfo() {
-        return "forceReportingResized=" + mLastForceReportingResized
-                + " displayCutoutChanged=" + mDisplayCutoutChanged;
+        return "forceReportingResized=" + mLastForceReportingResized;
     }
 }
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 7794f23..4eeae6c 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -27,7 +27,6 @@
 import static android.Manifest.permission.WRITE_SECURE_SETTINGS;
 import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
 import static android.app.ActivityManagerInternal.ALLOW_NON_FULL;
-import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
 import static android.app.AppOpsManager.OP_SYSTEM_ALERT_WINDOW;
 import static android.app.StatusBarManager.DISABLE_MASK;
 import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
@@ -220,7 +219,6 @@
 import android.util.proto.ProtoOutputStream;
 import android.view.Choreographer;
 import android.view.Display;
-import android.view.DisplayCutout;
 import android.view.DisplayInfo;
 import android.view.Gravity;
 import android.view.IAppTransitionAnimationSpecsFuture;
@@ -686,13 +684,10 @@
     // Whether the system should use BLAST for ViewRootImpl
     final boolean mUseBLAST;
     // Whether to enable BLASTSyncEngine Transaction passing.
-    final boolean mUseBLASTSync = false;
+    final boolean mUseBLASTSync = true;
 
     final BLASTSyncEngine mSyncEngine;
 
-    int mDockedStackCreateMode = SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
-    Rect mDockedStackCreateBounds;
-
     boolean mIsPc;
     /**
      * Flag that indicates that desktop mode is forced for public secondary screens.
@@ -1445,8 +1440,8 @@
 
     public int addWindow(Session session, IWindow client, LayoutParams attrs, int viewVisibility,
             int displayId, int requestUserId, InsetsState requestedVisibility, Rect outFrame,
-            DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
-            InsetsState outInsetsState, InsetsSourceControl[] outActiveControls) {
+            InputChannel outInputChannel, InsetsState outInsetsState,
+            InsetsSourceControl[] outActiveControls) {
         Arrays.fill(outActiveControls, null);
         int[] appOp = new int[1];
         final boolean isRoundedCornerOverlay = (attrs.privateFlags
@@ -1759,8 +1754,8 @@
                 prepareNoneTransitionForRelaunching(activity);
             }
 
-            if (displayPolicy.getLayoutHint(win.mAttrs, token, outFrame, outDisplayCutout,
-                    outInsetsState, win.isClientLocal())) {
+            if (displayPolicy.getLayoutHint(win.mAttrs, token, outFrame, outInsetsState,
+                    win.isClientLocal())) {
                 res |= WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_SYSTEM_BARS;
             }
 
@@ -8392,7 +8387,7 @@
 
     @Override
     public boolean getWindowInsets(WindowManager.LayoutParams attrs, int displayId,
-            DisplayCutout.ParcelableWrapper outDisplayCutout, InsetsState outInsetsState) {
+            InsetsState outInsetsState) {
         final boolean fromLocal = Binder.getCallingPid() == myPid();
         final long origId = Binder.clearCallingIdentity();
         try {
@@ -8404,7 +8399,7 @@
                 }
                 final WindowToken windowToken = dc.getWindowToken(attrs.token);
                 return dc.getDisplayPolicy().getLayoutHint(attrs, windowToken,
-                        mTmpRect /* outFrame */, outDisplayCutout, outInsetsState, fromLocal);
+                        mTmpRect /* outFrame */, outInsetsState, fromLocal);
             }
         } finally {
             Binder.restoreCallingIdentity(origId);
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 9d64af7..8aa154b 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -510,13 +510,9 @@
         }
     }
 
-    /**
-     * Returns true if background activity starts are allowed by any token added via {@link
-     * #addOrUpdateAllowBackgroundActivityStartsToken(Binder, IBinder)}.
-     */
-    public boolean areBackgroundActivityStartsAllowedByToken() {
+    public boolean areBackgroundActivityStartsAllowed() {
         synchronized (mAtm.mGlobalLock) {
-            return !mBackgroundActivityStartTokens.isEmpty();
+            return areBackgroundActivityStartsAllowed(mAtm.getBalAppSwitchesAllowed());
         }
     }
 
@@ -585,9 +581,8 @@
     }
 
     /**
-     * If there are no tokens, we don't allow *by token*. If there are tokens, we need to check if
-     * the callback handles all the tokens, if so we ask the callback if the activity should be
-     * started, otherwise we allow.
+     * If there are no tokens, we don't allow *by token*. If there are tokens, we ask the callback
+     * if the start is allowed for these tokens, otherwise if there is no callback we allow.
      */
     private boolean isBackgroundStartAllowedByToken() {
         if (mBackgroundActivityStartTokens.isEmpty()) {
@@ -597,16 +592,22 @@
             // We have tokens but no callback to decide => allow
             return true;
         }
-        IBinder callbackToken = mBackgroundActivityStartCallback.getToken();
-        for (IBinder token : mBackgroundActivityStartTokens.values()) {
-            if (token != callbackToken) {
-                // The callback doesn't handle all the tokens => allow
-                return true;
-            }
+        // The callback will decide
+        return mBackgroundActivityStartCallback.isActivityStartAllowed(
+                mBackgroundActivityStartTokens.values(), mInfo.uid, mInfo.packageName);
+    }
+
+    /**
+     * Returns whether this process is allowed to close system dialogs via a background activity
+     * start token that allows the close system dialogs operation (eg. notification).
+     */
+    public boolean canCloseSystemDialogsByToken() {
+        synchronized (mAtm.mGlobalLock) {
+            return !mBackgroundActivityStartTokens.isEmpty()
+                    && mBackgroundActivityStartCallback != null
+                    && mBackgroundActivityStartCallback.canCloseSystemDialogs(
+                            mBackgroundActivityStartTokens.values(), mInfo.uid);
         }
-        // The callback handles all the tokens => callback decides
-        return mBackgroundActivityStartCallback.isActivityStartAllowed(mInfo.uid,
-                mInfo.packageName);
     }
 
     private boolean isBoundByForegroundUid() {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index a8f4bae..551e06d 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -252,7 +252,6 @@
 import com.android.server.policy.WindowManagerPolicy;
 import com.android.server.wm.LocalAnimationAdapter.AnimationSpec;
 import com.android.server.wm.SurfaceAnimator.AnimationType;
-import com.android.server.wm.utils.WmDisplayCutout;
 
 import java.io.PrintWriter;
 import java.lang.ref.WeakReference;
@@ -1071,8 +1070,7 @@
         frame.inset(left, top, right, bottom);
     }
 
-    void computeFrame(DisplayFrames displayFrames) {
-        getLayoutingWindowFrames().setDisplayCutout(displayFrames.mDisplayCutout);
+    void computeFrameAndUpdateSourceFrame() {
         computeFrame();
         // Update the source frame to provide insets to other windows during layout. If the
         // simulated frames exist, then this is not computing a stable result so just skip.
@@ -1213,9 +1211,6 @@
             }
         }
 
-        windowFrames.setDisplayCutout(
-                windowFrames.mDisplayCutout.calculateRelativeTo(windowFrames.mFrame));
-
         // Offset the actual frame by the amount layout frame is off.
         windowFrames.offsetFrames(-layoutXDiff, -layoutYDiff);
 
@@ -1292,10 +1287,6 @@
         return mWindowFrames.mContainingFrame;
     }
 
-    WmDisplayCutout getWmDisplayCutout() {
-        return mWindowFrames.mDisplayCutout;
-    }
-
     void getCompatFrameSize(Rect outFrame) {
         outFrame.set(0, 0, mWindowFrames.mCompatFrame.width(), mWindowFrames.mCompatFrame.height());
     }
@@ -3577,7 +3568,6 @@
             final DisplayInfo displayInfo = getDisplayInfo();
             backdropFrame.set(0, 0, displayInfo.logicalWidth, displayInfo.logicalHeight);
         }
-        outFrames.displayCutout.set(mWindowFrames.mDisplayCutout.getDisplayCutout());
     }
 
     void reportResized() {
@@ -5736,6 +5726,17 @@
         return mAttrs.type == TYPE_APPLICATION_STARTING;
     }
 
+    @Override
+    boolean showWallpaper() {
+        if (!isVisibleRequested()
+                // in multi-window mode, wallpaper is always visible at the back and not tied to
+                // the app (there is no wallpaper target).
+                || inMultiWindowMode()) {
+            return false;
+        }
+        return (mAttrs.flags & FLAG_SHOW_WALLPAPER) != 0;
+    }
+
     /**
      * When using the two WindowOrganizer sync-primitives (BoundsChangeTransaction, BLASTSync)
      * it can be a little difficult to predict whether your change will actually trigger redrawing
diff --git a/services/core/java/com/android/server/wm/utils/WmDisplayCutout.java b/services/core/java/com/android/server/wm/utils/WmDisplayCutout.java
index 46fff03..ee3f4f4d 100644
--- a/services/core/java/com/android/server/wm/utils/WmDisplayCutout.java
+++ b/services/core/java/com/android/server/wm/utils/WmDisplayCutout.java
@@ -57,55 +57,6 @@
     }
 
     /**
-     * Insets the reference frame of the cutout in the given directions.
-     *
-     * @return a copy of this instance which has been inset
-     * @hide
-     */
-    public WmDisplayCutout inset(int insetLeft, int insetTop, int insetRight, int insetBottom) {
-        DisplayCutout newInner = mInner.inset(insetLeft, insetTop, insetRight, insetBottom);
-
-        if (mInner == newInner) {
-            return this;
-        }
-
-        Size frame = mFrameSize == null ? null : new Size(
-                mFrameSize.getWidth() - insetLeft - insetRight,
-                mFrameSize.getHeight() - insetTop - insetBottom);
-
-        return new WmDisplayCutout(newInner, frame);
-    }
-
-    /**
-     * Recalculates the cutout relative to the given reference frame.
-     *
-     * The safe insets must already have been computed, e.g. with {@link #computeSafeInsets}.
-     *
-     * @return a copy of this instance with the safe insets recalculated
-     * @hide
-     */
-    public WmDisplayCutout calculateRelativeTo(Rect frame) {
-        if (mFrameSize == null) {
-            return this;
-        }
-        final int insetRight = mFrameSize.getWidth() - frame.right;
-        final int insetBottom = mFrameSize.getHeight() - frame.bottom;
-        if (frame.left == 0 && frame.top == 0 && insetRight == 0 && insetBottom == 0) {
-            return this;
-        }
-        if (frame.left >= mInner.getSafeInsetLeft()
-                && frame.top >= mInner.getSafeInsetTop()
-                && insetRight >= mInner.getSafeInsetRight()
-                && insetBottom >= mInner.getSafeInsetBottom()) {
-            return NO_CUTOUT;
-        }
-        if (mInner.isEmpty()) {
-            return this;
-        }
-        return inset(frame.left, frame.top, insetRight, insetBottom);
-    }
-
-    /**
      * Calculates the safe insets relative to the given display size.
      *
      * @return a copy of this instance with the safe insets calculated
diff --git a/services/core/jni/com_android_server_am_BatteryStatsService.cpp b/services/core/jni/com_android_server_am_BatteryStatsService.cpp
index c44cea3..c3e7c7a 100644
--- a/services/core/jni/com_android_server_am_BatteryStatsService.cpp
+++ b/services/core/jni/com_android_server_am_BatteryStatsService.cpp
@@ -49,6 +49,8 @@
 #include <utils/misc.h>
 #include <utils/Log.h>
 
+#include <android-base/strings.h>
+
 using android::hardware::hidl_vec;
 using android::hardware::Return;
 using android::hardware::Void;
@@ -198,67 +200,13 @@
         return 0;
     }
 
-    char* mergedreasonpos = mergedreason;
-    int i = 0;
-    for (auto wakeupReason : wakeupReasons) {
-        auto reasonline = const_cast<char*>(wakeupReason.c_str());
-        char* pos = reasonline;
-        char* endPos;
-        int len;
-        // First field is the index or 'Abort'.
-        int irq = (int)strtol(pos, &endPos, 10);
-        if (pos != endPos) {
-            // Write the irq number to the merged reason string.
-            len = snprintf(mergedreasonpos, remainreasonlen, i == 0 ? "%d" : ":%d", irq);
-        } else {
-            // The first field is not an irq, it may be the word Abort.
-            const size_t abortPrefixLen = strlen("Abort:");
-            if (strncmp(pos, "Abort:", abortPrefixLen) != 0) {
-                // Ooops.
-                ALOGE("Bad reason line: %s", reasonline);
-                continue;
-            }
+    std::string mergedReasonStr = ::android::base::Join(wakeupReasons, ":");
+    strncpy(mergedreason, mergedReasonStr.c_str(), remainreasonlen);
+    mergedreason[remainreasonlen - 1] = '\0';
 
-            // Write 'Abort' to the merged reason string.
-            len = snprintf(mergedreasonpos, remainreasonlen, i == 0 ? "Abort" : ":Abort");
-            endPos = pos + abortPrefixLen;
-        }
-        pos = endPos;
+    ALOGV("Got %d reasons", (int)wakeupReasons.size());
 
-        if (len >= 0 && len < remainreasonlen) {
-            mergedreasonpos += len;
-            remainreasonlen -= len;
-        }
-
-        // Skip whitespace; rest of the buffer is the reason string.
-        while (*pos == ' ') {
-            pos++;
-        }
-
-        // Chop newline at end.
-        char* endpos = pos;
-        while (*endpos != 0) {
-            if (*endpos == '\n') {
-                *endpos = 0;
-                break;
-            }
-            endpos++;
-        }
-
-        len = snprintf(mergedreasonpos, remainreasonlen, ":%s", pos);
-        if (len >= 0 && len < remainreasonlen) {
-            mergedreasonpos += len;
-            remainreasonlen -= len;
-        }
-        i++;
-    }
-
-    ALOGV("Got %d reasons", i);
-    if (i > 0) {
-        *mergedreasonpos = 0;
-    }
-
-    return mergedreasonpos - mergedreason;
+    return strlen(mergedreason);
 }
 
 static bool checkPowerStatsHalResultLocked(const Return<void>& ret, const char* function)
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 1d55318..d0c2050 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -2055,7 +2055,6 @@
             ->enableSensor(deviceId, static_cast<InputDeviceSensorType>(sensorType),
                            std::chrono::microseconds(samplingPeriodUs),
                            std::chrono::microseconds(maxBatchReportLatencyUs));
-    return true;
 }
 
 static void nativeDisableSensor(JNIEnv* env, jclass /* clazz */, jlong ptr, jint deviceId,
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index 066dbce..35aad3e 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -31,6 +31,7 @@
 #include <android/hardware/gnss/2.1/IGnssMeasurement.h>
 #include <android/hardware/gnss/3.0/IGnssPsds.h>
 #include <android/hardware/gnss/BnGnss.h>
+#include <android/hardware/gnss/BnGnssCallback.h>
 #include <android/hardware/gnss/BnGnssMeasurementCallback.h>
 #include <android/hardware/gnss/BnGnssPowerIndicationCallback.h>
 #include <android/hardware/gnss/BnGnssPsdsCallback.h>
@@ -223,6 +224,7 @@
 using android::hardware::gnss::IGnssPowerIndicationCallback;
 using android::hardware::gnss::PsdsType;
 using IGnssAidl = android::hardware::gnss::IGnss;
+using IGnssCallbackAidl = android::hardware::gnss::IGnssCallback;
 using IGnssPsdsAidl = android::hardware::gnss::IGnssPsds;
 using IGnssPsdsCallbackAidl = android::hardware::gnss::IGnssPsdsCallback;
 using IGnssConfigurationAidl = android::hardware::gnss::IGnssConfiguration;
@@ -711,6 +713,19 @@
     return Void();
 }
 
+class GnssCallbackAidl : public android::hardware::gnss::BnGnssCallback {
+public:
+    Status gnssSetCapabilitiesCb(const int capabilities) override;
+};
+
+Status GnssCallbackAidl::gnssSetCapabilitiesCb(const int capabilities) {
+    ALOGD("GnssCallbackAidl::%s: %du\n", __func__, capabilities);
+    JNIEnv* env = getJniEnv();
+    env->CallVoidMethod(mCallbacksObj, method_setTopHalCapabilities, capabilities);
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+    return Status::ok();
+}
+
 /*
  * GnssPowerIndicationCallback class implements the callback methods for the IGnssPowerIndication
  * interface.
@@ -1442,7 +1457,7 @@
 };
 
 /* Initializes the GNSS service handle. */
-static void android_location_GnssLocationProvider_set_gps_service_handle() {
+static void android_location_gnss_hal_GnssNative_set_gps_service_handle() {
     gnssHalAidl = waitForVintfService<IGnssAidl>();
     if (gnssHalAidl != nullptr) {
         ALOGD("Successfully got GNSS AIDL handle.");
@@ -1489,9 +1504,9 @@
 }
 
 /* One time initialization at system boot */
-static void android_location_GnssNative_class_init_once(JNIEnv* env, jclass clazz) {
+static void android_location_gnss_hal_GnssNative_class_init_once(JNIEnv* env, jclass clazz) {
     // Initialize the top level gnss HAL handle.
-    android_location_GnssLocationProvider_set_gps_service_handle();
+    android_location_gnss_hal_GnssNative_set_gps_service_handle();
 
     // Cache methodIDs and class IDs.
     method_reportLocation = env->GetMethodID(clazz, "reportLocation",
@@ -1655,8 +1670,8 @@
 }
 
 /* Initialization needed at system boot and whenever GNSS service dies. */
-static void android_location_GnssNative_init_once(JNIEnv* env, jobject obj,
-                                                  jboolean reinitializeGnssServiceHandle) {
+static void android_location_gnss_hal_GnssNative_init_once(JNIEnv* env, jobject obj,
+                                                           jboolean reinitializeGnssServiceHandle) {
     /*
      * Save a pointer to JVM.
      */
@@ -1666,7 +1681,7 @@
     }
 
     if (reinitializeGnssServiceHandle) {
-        android_location_GnssLocationProvider_set_gps_service_handle();
+        android_location_gnss_hal_GnssNative_set_gps_service_handle();
     }
 
     if (gnssHal == nullptr) {
@@ -1934,7 +1949,7 @@
     }
 }
 
-static jboolean android_location_GnssNative_is_supported(JNIEnv* /* env */, jclass /* clazz */) {
+static jboolean android_location_gnss_hal_GnssNative_is_supported(JNIEnv* /* env */, jclass) {
     return (gnssHal != nullptr) ?  JNI_TRUE : JNI_FALSE;
 }
 
@@ -1952,7 +1967,7 @@
 }
 
 /* Initialization needed each time the GPS service is shutdown. */
-static jboolean android_location_GnssLocationProvider_init(JNIEnv* /* env */, jobject /* obj */) {
+static jboolean android_location_gnss_hal_GnssNative_init(JNIEnv* /* env */, jclass) {
     /*
      * This must be set before calling into the HAL library.
      */
@@ -1987,6 +2002,14 @@
         return JNI_FALSE;
     }
 
+    sp<IGnssCallbackAidl> gnssCbIfaceAidl = new GnssCallbackAidl();
+    if (gnssHalAidl != nullptr) {
+        auto status = gnssHalAidl->setCallback(gnssCbIfaceAidl);
+        if (!checkAidlStatus(status, "IGnssAidl setCallback() failed.")) {
+            return JNI_FALSE;
+        }
+    }
+
     // Set IGnssPsds or IGnssXtra callback.
     if (gnssPsdsAidlIface != nullptr) {
         sp<IGnssPsdsCallbackAidl> gnssPsdsCallbackAidl = new GnssPsdsCallbackAidl();
@@ -2087,7 +2110,7 @@
     return JNI_TRUE;
 }
 
-static void android_location_GnssLocationProvider_cleanup(JNIEnv* /* env */, jobject /* obj */) {
+static void android_location_gnss_hal_GnssNative_cleanup(JNIEnv* /* env */, jclass) {
     if (gnssHal == nullptr) {
         return;
     }
@@ -2096,9 +2119,9 @@
     checkHidlReturn(result, "IGnss cleanup() failed.");
 }
 
-static jboolean android_location_GnssLocationProvider_set_position_mode(JNIEnv* /* env */,
-        jobject /* obj */, jint mode, jint recurrence, jint min_interval, jint preferred_accuracy,
-        jint preferred_time, jboolean low_power_mode) {
+static jboolean android_location_gnss_hal_GnssNative_set_position_mode(
+        JNIEnv* /* env */, jclass, jint mode, jint recurrence, jint min_interval,
+        jint preferred_accuracy, jint preferred_time, jboolean low_power_mode) {
     Return<bool> result = false;
     if (gnssHal_V1_1 != nullptr) {
          result = gnssHal_V1_1->setPositionMode_1_1(static_cast<IGnss_V1_0::GnssPositionMode>(mode),
@@ -2118,7 +2141,7 @@
     return checkHidlReturn(result, "IGnss setPositionMode() failed.");
 }
 
-static jboolean android_location_GnssLocationProvider_start(JNIEnv* /* env */, jobject /* obj */) {
+static jboolean android_location_gnss_hal_GnssNative_start(JNIEnv* /* env */, jclass) {
     if (gnssHal == nullptr) {
         return JNI_FALSE;
     }
@@ -2127,7 +2150,7 @@
     return checkHidlReturn(result, "IGnss start() failed.");
 }
 
-static jboolean android_location_GnssLocationProvider_stop(JNIEnv* /* env */, jobject /* obj */) {
+static jboolean android_location_gnss_hal_GnssNative_stop(JNIEnv* /* env */, jclass) {
     if (gnssHal == nullptr) {
         return JNI_FALSE;
     }
@@ -2136,8 +2159,7 @@
     return checkHidlReturn(result, "IGnss stop() failed.");
 }
 
-static void android_location_GnssLocationProvider_delete_aiding_data(JNIEnv* /* env */,
-                                                                    jobject /* obj */,
+static void android_location_gnss_hal_GnssNative_delete_aiding_data(JNIEnv* /* env */, jclass,
                                                                     jint flags) {
     if (gnssHal == nullptr) {
         return;
@@ -2147,8 +2169,8 @@
     checkHidlReturn(result, "IGnss deleteAidingData() failed.");
 }
 
-static void android_location_GnssLocationProvider_agps_set_reference_location_cellid(
-        JNIEnv* /* env */, jobject /* obj */, jint type, jint mcc, jint mnc, jint lac, jint cid) {
+static void android_location_gnss_hal_GnssNative_agps_set_reference_location_cellid(
+        JNIEnv* /* env */, jclass, jint type, jint mcc, jint mnc, jint lac, jint cid) {
     IAGnssRil_V1_0::AGnssRefLocation location;
 
     if (agnssRilIface == nullptr) {
@@ -2175,8 +2197,8 @@
     checkHidlReturn(result, "IAGnssRil setRefLocation() failed.");
 }
 
-static void android_location_GnssLocationProvider_agps_set_id(JNIEnv* env, jobject /* obj */,
-                                                             jint type, jstring  setid_string) {
+static void android_location_gnss_hal_GnssNative_agps_set_id(JNIEnv* env, jclass, jint type,
+                                                             jstring setid_string) {
     if (agnssRilIface == nullptr) {
         ALOGE("%s: IAGnssRil interface not available.", __func__);
         return;
@@ -2187,8 +2209,8 @@
     checkHidlReturn(result, "IAGnssRil setSetId() failed.");
 }
 
-static jint android_location_GnssLocationProvider_read_nmea(JNIEnv* env, jobject /* obj */,
-                                            jbyteArray nmeaArray, jint buffer_size) {
+static jint android_location_gnss_hal_GnssNative_read_nmea(JNIEnv* env, jclass,
+                                                           jbyteArray nmeaArray, jint buffer_size) {
     // this should only be called from within a call to reportNmea
     jbyte* nmea = reinterpret_cast<jbyte *>(env->GetPrimitiveArrayCritical(nmeaArray, 0));
     int length = GnssCallback::sNmeaStringLength;
@@ -2199,8 +2221,9 @@
     return (jint) length;
 }
 
-static void android_location_GnssLocationProvider_inject_time(JNIEnv* /* env */, jobject /* obj */,
-        jlong time, jlong timeReference, jint uncertainty) {
+static void android_location_gnss_hal_GnssNative_inject_time(JNIEnv* /* env */, jclass, jlong time,
+                                                             jlong timeReference,
+                                                             jint uncertainty) {
     if (gnssHal == nullptr) {
         return;
     }
@@ -2209,22 +2232,12 @@
     checkHidlReturn(result, "IGnss injectTime() failed.");
 }
 
-static void android_location_GnssLocationProvider_inject_best_location(
-        JNIEnv*,
-        jobject,
-        jint gnssLocationFlags,
-        jdouble latitudeDegrees,
-        jdouble longitudeDegrees,
-        jdouble altitudeMeters,
-        jfloat speedMetersPerSec,
-        jfloat bearingDegrees,
-        jfloat horizontalAccuracyMeters,
-        jfloat verticalAccuracyMeters,
-        jfloat speedAccuracyMetersPerSecond,
-        jfloat bearingAccuracyDegrees,
-        jlong timestamp,
-        jint elapsedRealtimeFlags,
-        jlong elapsedRealtimeNanos,
+static void android_location_gnss_hal_GnssNative_inject_best_location(
+        JNIEnv* /* env */, jclass, jint gnssLocationFlags, jdouble latitudeDegrees,
+        jdouble longitudeDegrees, jdouble altitudeMeters, jfloat speedMetersPerSec,
+        jfloat bearingDegrees, jfloat horizontalAccuracyMeters, jfloat verticalAccuracyMeters,
+        jfloat speedAccuracyMetersPerSecond, jfloat bearingAccuracyDegrees, jlong timestamp,
+        jint elapsedRealtimeFlags, jlong elapsedRealtimeNanos,
         jdouble elapsedRealtimeUncertaintyNanos) {
     if (gnssHal_V2_0 != nullptr) {
         GnssLocation_V2_0 location = createGnssLocation_V2_0(
@@ -2267,8 +2280,10 @@
     ALOGE("IGnss injectBestLocation() is called but gnssHal_V1_1 is not available.");
 }
 
-static void android_location_GnssLocationProvider_inject_location(JNIEnv* /* env */,
-        jobject /* obj */, jdouble latitude, jdouble longitude, jfloat accuracy) {
+static void android_location_gnss_hal_GnssNative_inject_location(JNIEnv* /* env */, jclass,
+                                                                 jdouble latitude,
+                                                                 jdouble longitude,
+                                                                 jfloat accuracy) {
     if (gnssHal == nullptr) {
         return;
     }
@@ -2277,16 +2292,15 @@
     checkHidlReturn(result, "IGnss injectLocation() failed.");
 }
 
-static jboolean android_location_GnssLocationProvider_supports_psds(
-        JNIEnv* /* env */, jobject /* obj */) {
+static jboolean android_location_gnss_hal_GnssNative_supports_psds(JNIEnv* /* env */, jclass) {
     return (gnssPsdsAidlIface != nullptr || gnssPsdsIface != nullptr || gnssXtraIface != nullptr)
             ? JNI_TRUE
             : JNI_FALSE;
 }
 
-static void android_location_GnssLocationProvider_inject_psds_data(JNIEnv* env, jobject /* obj */,
-                                                                   jbyteArray data, jint length,
-                                                                   jint psdsType) {
+static void android_location_gnss_hal_GnssNative_inject_psds_data(JNIEnv* env, jclass,
+                                                                  jbyteArray data, jint length,
+                                                                  jint psdsType) {
     if (gnssPsdsAidlIface == nullptr && gnssPsdsIface == nullptr && gnssXtraIface == nullptr) {
         ALOGE("%s: IGnssPsds or IGnssXtra interface not available.", __func__);
         return;
@@ -2406,8 +2420,8 @@
     }
 }
 
-static void android_location_GnssLocationProvider_set_agps_server(JNIEnv* env, jobject /* obj */,
-        jint type, jstring hostname, jint port) {
+static void android_location_gnss_hal_GnssNative_set_agps_server(JNIEnv* env, jclass, jint type,
+                                                                 jstring hostname, jint port) {
     if (agnssIface_V2_0 != nullptr) {
         AGnssDispatcher::setServer<IAGnss_V2_0, IAGnssCallback_V2_0>(agnssIface_V2_0, env, type,
                 hostname, port);
@@ -2420,8 +2434,8 @@
     }
 }
 
-static void android_location_GnssLocationProvider_send_ni_response(JNIEnv* /* env */,
-        jobject /* obj */, jint notifId, jint response) {
+static void android_location_gnss_hal_GnssNative_send_ni_response(JNIEnv* /* env */, jclass,
+                                                                  jint notifId, jint response) {
     if (gnssNiIface == nullptr) {
         ALOGE("%s: IGnssNi interface not available.", __func__);
         return;
@@ -2504,8 +2518,7 @@
     return (jstring) env->NewStringUTF(internalState.str().c_str());
 }
 
-static jstring android_location_GnssLocationProvider_get_internal_state(JNIEnv* env,
-                                                                       jobject /* obj */) {
+static jstring android_location_gnss_hal_GnssNative_get_internal_state(JNIEnv* env, jclass) {
     jstring internalStateStr = nullptr;
     /*
      * TODO: Create a jobject to represent GnssDebug.
@@ -2537,8 +2550,7 @@
     return internalStateStr;
 }
 
-static void android_location_GnssLocationProvider_request_power_stats(JNIEnv* env,
-                                                                      jobject /* obj */) {
+static void android_location_gnss_hal_GnssNative_request_power_stats(JNIEnv* env) {
     if (gnssPowerIndicationIface == nullptr) {
         return;
     }
@@ -2546,8 +2558,8 @@
     checkAidlStatus(status, "IGnssPowerIndication requestGnssPowerStats() failed.");
 }
 
-static jboolean android_location_GnssLocationProvider_is_gnss_visibility_control_supported(
-        JNIEnv* /* env */, jclass /* clazz */) {
+static jboolean android_location_gnss_hal_GnssNative_is_gnss_visibility_control_supported(
+        JNIEnv* /* env */, jclass) {
     return (gnssVisibilityControlIface != nullptr) ?  JNI_TRUE : JNI_FALSE;
 }
 
@@ -2587,15 +2599,15 @@
     }
 }
 
-static jboolean android_location_GnssGeofenceProvider_is_geofence_supported(
-        JNIEnv* /* env */, jobject /* obj */) {
+static jboolean android_location_gnss_hal_GnssNative_is_geofence_supported(JNIEnv* /* env */,
+                                                                           jclass) {
     return (gnssGeofencingIface != nullptr) ? JNI_TRUE : JNI_FALSE;
 }
 
-static jboolean android_location_GnssGeofenceProvider_add_geofence(JNIEnv* /* env */,
-        jobject /* obj */, jint geofenceId, jdouble latitude, jdouble longitude, jdouble radius,
-        jint last_transition, jint monitor_transition, jint notification_responsiveness,
-        jint unknown_timer) {
+static jboolean android_location_gnss_hal_GnssNative_add_geofence(
+        JNIEnv* /* env */, jclass, jint geofenceId, jdouble latitude, jdouble longitude,
+        jdouble radius, jint last_transition, jint monitor_transition,
+        jint notification_responsiveness, jint unknown_timer) {
     if (gnssGeofencingIface == nullptr) {
         ALOGE("%s: IGnssGeofencing interface not available.", __func__);
         return JNI_FALSE;
@@ -2608,8 +2620,8 @@
     return checkHidlReturn(result, "IGnssGeofencing addGeofence() failed.");
 }
 
-static jboolean android_location_GnssGeofenceProvider_remove_geofence(JNIEnv* /* env */,
-        jobject /* obj */, jint geofenceId) {
+static jboolean android_location_gnss_hal_GnssNative_remove_geofence(JNIEnv* /* env */, jclass,
+                                                                     jint geofenceId) {
     if (gnssGeofencingIface == nullptr) {
         ALOGE("%s: IGnssGeofencing interface not available.", __func__);
         return JNI_FALSE;
@@ -2619,8 +2631,8 @@
     return checkHidlReturn(result, "IGnssGeofencing removeGeofence() failed.");
 }
 
-static jboolean android_location_GnssGeofenceProvider_pause_geofence(JNIEnv* /* env */,
-        jobject /* obj */, jint geofenceId) {
+static jboolean android_location_gnss_hal_GnssNative_pause_geofence(JNIEnv* /* env */, jclass,
+                                                                    jint geofenceId) {
     if (gnssGeofencingIface == nullptr) {
         ALOGE("%s: IGnssGeofencing interface not available.", __func__);
         return JNI_FALSE;
@@ -2630,8 +2642,9 @@
     return checkHidlReturn(result, "IGnssGeofencing pauseGeofence() failed.");
 }
 
-static jboolean android_location_GnssGeofenceProvider_resume_geofence(JNIEnv* /* env */,
-        jobject /* obj */, jint geofenceId, jint monitor_transition) {
+static jboolean android_location_gnss_hal_GnssNative_resume_geofence(JNIEnv* /* env */, jclass,
+                                                                     jint geofenceId,
+                                                                     jint monitor_transition) {
     if (gnssGeofencingIface == nullptr) {
         ALOGE("%s: IGnssGeofencing interface not available.", __func__);
         return JNI_FALSE;
@@ -2641,16 +2654,16 @@
     return checkHidlReturn(result, "IGnssGeofencing resumeGeofence() failed.");
 }
 
-static jboolean android_location_GnssAntennaInfoProvider_is_antenna_info_supported(JNIEnv* env,
-                                                                                   jclass clazz) {
+static jboolean android_location_gnss_hal_GnssNative_is_antenna_info_supported(JNIEnv* env,
+                                                                               jclass) {
     if (gnssAntennaInfoIface != nullptr) {
         return JNI_TRUE;
     }
     return JNI_FALSE;
 }
 
-static jboolean android_location_GnssAntennaInfoProvider_start_antenna_info_listening(
-        JNIEnv* /* env */, jobject /* obj */) {
+static jboolean android_location_gnss_hal_GnssNative_start_antenna_info_listening(JNIEnv* /* env */,
+                                                                                  jclass) {
     if (gnssAntennaInfoIface == nullptr) {
         ALOGE("%s: IGnssAntennaInfo interface not available.", __func__);
         return JNI_FALSE;
@@ -2676,8 +2689,8 @@
     return JNI_TRUE;
 }
 
-static jboolean android_location_GnssAntennaInfoProvider_stop_antenna_info_listening(
-        JNIEnv* /* env */, jobject /* obj */) {
+static jboolean android_location_gnss_hal_GnssNative_stop_antenna_info_listening(JNIEnv* /* env */,
+                                                                                 jclass) {
     if (gnssAntennaInfoIface == nullptr) {
         ALOGE("%s: IGnssAntennaInfo interface not available.", __func__);
         return JNI_FALSE;
@@ -2687,8 +2700,7 @@
     return checkHidlReturn(result, "IGnssAntennaInfo close() failed.");
 }
 
-static jboolean android_location_GnssMeasurementsProvider_is_measurement_supported(
-    JNIEnv* env, jclass clazz) {
+static jboolean android_location_gnss_hal_GnssNative_is_measurement_supported(JNIEnv* env, jclass) {
     if (gnssMeasurementIface != nullptr) {
         return JNI_TRUE;
     }
@@ -2696,10 +2708,8 @@
     return JNI_FALSE;
 }
 
-static jboolean android_location_GnssMeasurementsProvider_start_measurement_collection(
-        JNIEnv* /* env */,
-        jobject /* obj */,
-        jboolean enableFullTracking) {
+static jboolean android_location_gnss_hal_GnssNative_start_measurement_collection(
+        JNIEnv* /* env */, jclass, jboolean enableFullTracking) {
     if (gnssMeasurementIface == nullptr) {
         ALOGE("%s: IGnssMeasurement interface not available.", __func__);
         return JNI_FALSE;
@@ -2710,9 +2720,8 @@
                                              enableFullTracking);
 }
 
-static jboolean android_location_GnssMeasurementsProvider_stop_measurement_collection(
-        JNIEnv* env,
-        jobject obj) {
+static jboolean android_location_gnss_hal_GnssNative_stop_measurement_collection(JNIEnv* env,
+                                                                                 jclass) {
     if (gnssMeasurementIface == nullptr) {
         ALOGE("%s: IGnssMeasurement interface not available.", __func__);
         return JNI_FALSE;
@@ -2721,9 +2730,8 @@
     return gnssMeasurementIface->close();
 }
 
-static jboolean
-    android_location_GnssMeasurementCorrectionsProvider_is_measurement_corrections_supported(
-    JNIEnv* env, jclass clazz) {
+static jboolean android_location_gnss_hal_GnssNative_is_measurement_corrections_supported(
+        JNIEnv* env, jclass) {
     if (gnssCorrectionsIface_V1_0 != nullptr || gnssCorrectionsIface_V1_1 != nullptr) {
         return JNI_TRUE;
     }
@@ -2816,12 +2824,9 @@
         list[i] = singleSatCorrection;
     }
 }
-static jboolean
-    android_location_GnssMeasurementCorrectionsProvider_inject_gnss_measurement_corrections(
-        JNIEnv* env,
-        jobject obj /* clazz*/,
-        jobject correctionsObj) {
 
+static jboolean android_location_gnss_hal_GnssNative_inject_measurement_corrections(
+        JNIEnv* env, jclass, jobject correctionsObj) {
     if (gnssCorrectionsIface_V1_0 == nullptr && gnssCorrectionsIface_V1_1 == nullptr) {
         ALOGW("Trying to inject GNSS measurement corrections on a chipset that does not"
             " support them.");
@@ -2893,18 +2898,16 @@
     return checkHidlReturn(result, "IMeasurementCorrections 1.0 setCorrections() failed.");
 }
 
-static jboolean android_location_GnssNavigationMessageProvider_is_navigation_message_supported(
-        JNIEnv* env,
-        jclass clazz) {
+static jboolean android_location_gnss_hal_GnssNative_is_navigation_message_supported(JNIEnv* env,
+                                                                                     jclass) {
     if (gnssNavigationMessageIface != nullptr) {
         return JNI_TRUE;
     }
     return JNI_FALSE;
 }
 
-static jboolean android_location_GnssNavigationMessageProvider_start_navigation_message_collection(
-        JNIEnv* env,
-        jobject obj) {
+static jboolean android_location_gnss_hal_GnssNative_start_navigation_message_collection(
+        JNIEnv* env, jclass) {
     if (gnssNavigationMessageIface == nullptr) {
         ALOGE("%s: IGnssNavigationMessage interface not available.", __func__);
         return JNI_FALSE;
@@ -2926,9 +2929,8 @@
     return JNI_TRUE;
 }
 
-static jboolean android_location_GnssNavigationMessageProvider_stop_navigation_message_collection(
-        JNIEnv* env,
-        jobject obj) {
+static jboolean android_location_gnss_hal_GnssNative_stop_navigation_message_collection(JNIEnv* env,
+                                                                                        jclass) {
     if (gnssNavigationMessageIface == nullptr) {
         ALOGE("%s: IGnssNavigationMessage interface not available.", __func__);
         return JNI_FALSE;
@@ -3027,7 +3029,7 @@
     return gnssConfigurationIface->setEsExtensionSec(emergencyExtensionSeconds);
 }
 
-static jint android_location_GnssLocationProvider_get_batch_size(JNIEnv*, jclass) {
+static jint android_location_gnss_hal_GnssNative_get_batch_size(JNIEnv*) {
     if (gnssBatchingIface == nullptr) {
         return 0; // batching not supported, size = 0
     }
@@ -3039,7 +3041,7 @@
     return static_cast<jint>(result);
 }
 
-static jboolean android_location_GnssLocationProvider_init_batching(JNIEnv*, jclass) {
+static jboolean android_location_gnss_hal_GnssNative_init_batching(JNIEnv*, jclass) {
     if (gnssBatchingIface_V2_0 != nullptr) {
         sp<IGnssBatchingCallback_V2_0> gnssBatchingCbIface_V2_0 = new GnssBatchingCallback_V2_0();
         auto result = gnssBatchingIface_V2_0->init_2_0(gnssBatchingCbIface_V2_0);
@@ -3053,7 +3055,7 @@
     }
 }
 
-static void android_location_GnssLocationProvider_cleanup_batching(JNIEnv*, jclass) {
+static void android_location_gnss_hal_GnssNative_cleanup_batching(JNIEnv*, jclass) {
     if (gnssBatchingIface == nullptr) {
         return; // batching not supported
     }
@@ -3061,9 +3063,8 @@
     checkHidlReturn(result, "IGnssBatching cleanup() failed.");
 }
 
-static jboolean android_location_GnssLocationProvider_start_batch(JNIEnv*, jclass,
-                                                                  jlong periodNanos,
-                                                                  jboolean wakeOnFifoFull) {
+static jboolean android_location_gnss_hal_GnssNative_start_batch(JNIEnv*, jclass, jlong periodNanos,
+                                                                 jboolean wakeOnFifoFull) {
     if (gnssBatchingIface == nullptr) {
         return JNI_FALSE; // batching not supported
     }
@@ -3080,7 +3081,7 @@
     return checkHidlReturn(result, "IGnssBatching start() failed.");
 }
 
-static void android_location_GnssLocationProvider_flush_batch(JNIEnv*, jclass) {
+static void android_location_gnss_hal_GnssNative_flush_batch(JNIEnv*, jclass) {
     if (gnssBatchingIface == nullptr) {
         return; // batching not supported
     }
@@ -3088,7 +3089,7 @@
     checkHidlReturn(result, "IGnssBatching flush() failed.");
 }
 
-static jboolean android_location_GnssLocationProvider_stop_batch(JNIEnv*, jclass) {
+static jboolean android_location_gnss_hal_GnssNative_stop_batch(JNIEnv*, jclass) {
     if (gnssBatchingIface == nullptr) {
         return JNI_FALSE; // batching not supported
     }
@@ -3118,156 +3119,146 @@
 static const JNINativeMethod sCoreMethods[] = {
         /* name, signature, funcPtr */
         {"native_class_init_once", "()V",
-         reinterpret_cast<void*>(android_location_GnssNative_class_init_once)},
+         reinterpret_cast<void*>(android_location_gnss_hal_GnssNative_class_init_once)},
         {"native_is_supported", "()Z",
-         reinterpret_cast<void*>(android_location_GnssNative_is_supported)},
+         reinterpret_cast<void*>(android_location_gnss_hal_GnssNative_is_supported)},
         {"native_init_once", "(Z)V",
-         reinterpret_cast<void*>(android_location_GnssNative_init_once)},
+         reinterpret_cast<void*>(android_location_gnss_hal_GnssNative_init_once)},
 };
 
 static const JNINativeMethod sLocationProviderMethods[] = {
         /* name, signature, funcPtr */
-        {"native_init", "()Z", reinterpret_cast<void*>(android_location_GnssLocationProvider_init)},
+        {"native_init", "()Z", reinterpret_cast<void*>(android_location_gnss_hal_GnssNative_init)},
         {"native_cleanup", "()V",
-         reinterpret_cast<void*>(android_location_GnssLocationProvider_cleanup)},
+         reinterpret_cast<void*>(android_location_gnss_hal_GnssNative_cleanup)},
         {"native_set_position_mode", "(IIIIIZ)Z",
-         reinterpret_cast<void*>(android_location_GnssLocationProvider_set_position_mode)},
+         reinterpret_cast<void*>(android_location_gnss_hal_GnssNative_set_position_mode)},
         {"native_start", "()Z",
-         reinterpret_cast<void*>(android_location_GnssLocationProvider_start)},
-        {"native_stop", "()Z", reinterpret_cast<void*>(android_location_GnssLocationProvider_stop)},
+         reinterpret_cast<void*>(android_location_gnss_hal_GnssNative_start)},
+        {"native_stop", "()Z", reinterpret_cast<void*>(android_location_gnss_hal_GnssNative_stop)},
         {"native_delete_aiding_data", "(I)V",
-         reinterpret_cast<void*>(android_location_GnssLocationProvider_delete_aiding_data)},
+         reinterpret_cast<void*>(android_location_gnss_hal_GnssNative_delete_aiding_data)},
         {"native_read_nmea", "([BI)I",
-         reinterpret_cast<void*>(android_location_GnssLocationProvider_read_nmea)},
+         reinterpret_cast<void*>(android_location_gnss_hal_GnssNative_read_nmea)},
         {"native_inject_time", "(JJI)V",
-         reinterpret_cast<void*>(android_location_GnssLocationProvider_inject_time)},
+         reinterpret_cast<void*>(android_location_gnss_hal_GnssNative_inject_time)},
         {"native_inject_best_location", "(IDDDFFFFFFJIJD)V",
-         reinterpret_cast<void*>(android_location_GnssLocationProvider_inject_best_location)},
+         reinterpret_cast<void*>(android_location_gnss_hal_GnssNative_inject_best_location)},
         {"native_inject_location", "(DDF)V",
-         reinterpret_cast<void*>(android_location_GnssLocationProvider_inject_location)},
+         reinterpret_cast<void*>(android_location_gnss_hal_GnssNative_inject_location)},
         {"native_supports_psds", "()Z",
-         reinterpret_cast<void*>(android_location_GnssLocationProvider_supports_psds)},
+         reinterpret_cast<void*>(android_location_gnss_hal_GnssNative_supports_psds)},
         {"native_inject_psds_data", "([BII)V",
-         reinterpret_cast<void*>(android_location_GnssLocationProvider_inject_psds_data)},
+         reinterpret_cast<void*>(android_location_gnss_hal_GnssNative_inject_psds_data)},
         {"native_agps_set_id", "(ILjava/lang/String;)V",
-         reinterpret_cast<void*>(android_location_GnssLocationProvider_agps_set_id)},
+         reinterpret_cast<void*>(android_location_gnss_hal_GnssNative_agps_set_id)},
         {"native_agps_set_ref_location_cellid", "(IIIII)V",
          reinterpret_cast<void*>(
-                 android_location_GnssLocationProvider_agps_set_reference_location_cellid)},
+                 android_location_gnss_hal_GnssNative_agps_set_reference_location_cellid)},
         {"native_set_agps_server", "(ILjava/lang/String;I)V",
-         reinterpret_cast<void*>(android_location_GnssLocationProvider_set_agps_server)},
+         reinterpret_cast<void*>(android_location_gnss_hal_GnssNative_set_agps_server)},
         {"native_send_ni_response", "(II)V",
-         reinterpret_cast<void*>(android_location_GnssLocationProvider_send_ni_response)},
+         reinterpret_cast<void*>(android_location_gnss_hal_GnssNative_send_ni_response)},
         {"native_get_internal_state", "()Ljava/lang/String;",
-         reinterpret_cast<void*>(android_location_GnssLocationProvider_get_internal_state)},
+         reinterpret_cast<void*>(android_location_gnss_hal_GnssNative_get_internal_state)},
         {"native_is_gnss_visibility_control_supported", "()Z",
          reinterpret_cast<void*>(
-                 android_location_GnssLocationProvider_is_gnss_visibility_control_supported)},
+                 android_location_gnss_hal_GnssNative_is_gnss_visibility_control_supported)},
 };
 
-static const JNINativeMethod sMethodsBatching[] = {
+static const JNINativeMethod sBatchingMethods[] = {
         /* name, signature, funcPtr */
         {"native_get_batch_size", "()I",
-         reinterpret_cast<void*>(android_location_GnssLocationProvider_get_batch_size)},
+         reinterpret_cast<void*>(android_location_gnss_hal_GnssNative_get_batch_size)},
         {"native_start_batch", "(JZ)Z",
-         reinterpret_cast<void*>(android_location_GnssLocationProvider_start_batch)},
+         reinterpret_cast<void*>(android_location_gnss_hal_GnssNative_start_batch)},
         {"native_flush_batch", "()V",
-         reinterpret_cast<void*>(android_location_GnssLocationProvider_flush_batch)},
+         reinterpret_cast<void*>(android_location_gnss_hal_GnssNative_flush_batch)},
         {"native_stop_batch", "()Z",
-         reinterpret_cast<void*>(android_location_GnssLocationProvider_stop_batch)},
+         reinterpret_cast<void*>(android_location_gnss_hal_GnssNative_stop_batch)},
         {"native_init_batching", "()Z",
-         reinterpret_cast<void*>(android_location_GnssLocationProvider_init_batching)},
+         reinterpret_cast<void*>(android_location_gnss_hal_GnssNative_init_batching)},
         {"native_cleanup_batching", "()V",
-         reinterpret_cast<void*>(android_location_GnssLocationProvider_cleanup_batching)},
+         reinterpret_cast<void*>(android_location_gnss_hal_GnssNative_cleanup_batching)},
 };
 
 static const JNINativeMethod sAntennaInfoMethods[] = {
         /* name, signature, funcPtr */
         {"native_is_antenna_info_supported", "()Z",
-         reinterpret_cast<void*>(
-                 android_location_GnssAntennaInfoProvider_is_antenna_info_supported)},
+         reinterpret_cast<void*>(android_location_gnss_hal_GnssNative_is_antenna_info_supported)},
         {"native_start_antenna_info_listening", "()Z",
          reinterpret_cast<void*>(
-                 android_location_GnssAntennaInfoProvider_start_antenna_info_listening)},
+                 android_location_gnss_hal_GnssNative_start_antenna_info_listening)},
         {"native_stop_antenna_info_listening", "()Z",
-         reinterpret_cast<void*>(
-                 android_location_GnssAntennaInfoProvider_stop_antenna_info_listening)},
+         reinterpret_cast<void*>(android_location_gnss_hal_GnssNative_stop_antenna_info_listening)},
 };
 
 static const JNINativeMethod sGeofenceMethods[] = {
-     /* name, signature, funcPtr */
-    {"native_is_geofence_supported",
-            "()Z",
-            reinterpret_cast<void *>(android_location_GnssGeofenceProvider_is_geofence_supported)},
-    {"native_add_geofence",
-            "(IDDDIIII)Z",
-            reinterpret_cast<void *>(android_location_GnssGeofenceProvider_add_geofence)},
-    {"native_remove_geofence",
-            "(I)Z",
-            reinterpret_cast<void *>(android_location_GnssGeofenceProvider_remove_geofence)},
-    {"native_pause_geofence", "(I)Z", reinterpret_cast<void *>(
-            android_location_GnssGeofenceProvider_pause_geofence)},
-    {"native_resume_geofence",
-            "(II)Z",
-            reinterpret_cast<void *>(android_location_GnssGeofenceProvider_resume_geofence)},
+        /* name, signature, funcPtr */
+        {"native_is_geofence_supported", "()Z",
+         reinterpret_cast<void*>(android_location_gnss_hal_GnssNative_is_geofence_supported)},
+        {"native_add_geofence", "(IDDDIIII)Z",
+         reinterpret_cast<void*>(android_location_gnss_hal_GnssNative_add_geofence)},
+        {"native_remove_geofence", "(I)Z",
+         reinterpret_cast<void*>(android_location_gnss_hal_GnssNative_remove_geofence)},
+        {"native_pause_geofence", "(I)Z",
+         reinterpret_cast<void*>(android_location_gnss_hal_GnssNative_pause_geofence)},
+        {"native_resume_geofence", "(II)Z",
+         reinterpret_cast<void*>(android_location_gnss_hal_GnssNative_resume_geofence)},
 };
 
 static const JNINativeMethod sMeasurementMethods[] = {
-    /* name, signature, funcPtr */
-    {"native_is_measurement_supported", "()Z",
-            reinterpret_cast<void*>(
-            android_location_GnssMeasurementsProvider_is_measurement_supported)},
-    {"native_start_measurement_collection", "(Z)Z",
-            reinterpret_cast<void*>(
-            android_location_GnssMeasurementsProvider_start_measurement_collection)},
-    {"native_stop_measurement_collection", "()Z",
-            reinterpret_cast<void*>(
-            android_location_GnssMeasurementsProvider_stop_measurement_collection)},
+        /* name, signature, funcPtr */
+        {"native_is_measurement_supported", "()Z",
+         reinterpret_cast<void*>(android_location_gnss_hal_GnssNative_is_measurement_supported)},
+        {"native_start_measurement_collection", "(Z)Z",
+         reinterpret_cast<void*>(
+                 android_location_gnss_hal_GnssNative_start_measurement_collection)},
+        {"native_stop_measurement_collection", "()Z",
+         reinterpret_cast<void*>(android_location_gnss_hal_GnssNative_stop_measurement_collection)},
 };
 
 static const JNINativeMethod sMeasurementCorrectionsMethods[] = {
-    /* name, signature, funcPtr */
-    {"native_is_measurement_corrections_supported", "()Z",
-            reinterpret_cast<void*>(
-            android_location_GnssMeasurementCorrectionsProvider_is_measurement_corrections_supported)},
-    {"native_inject_gnss_measurement_corrections",
-            "(Landroid/location/GnssMeasurementCorrections;)Z",
-            reinterpret_cast<void*>(
-            android_location_GnssMeasurementCorrectionsProvider_inject_gnss_measurement_corrections)},
+        /* name, signature, funcPtr */
+        {"native_is_measurement_corrections_supported", "()Z",
+         reinterpret_cast<void*>(
+                 android_location_gnss_hal_GnssNative_is_measurement_corrections_supported)},
+        {"native_inject_measurement_corrections",
+         "(Landroid/location/GnssMeasurementCorrections;)Z",
+         reinterpret_cast<void*>(
+                 android_location_gnss_hal_GnssNative_inject_measurement_corrections)},
 };
 
 static const JNINativeMethod sNavigationMessageMethods[] = {
-     /* name, signature, funcPtr */
-    {"native_is_navigation_message_supported",
-            "()Z",
-            reinterpret_cast<void *>(
-                    android_location_GnssNavigationMessageProvider_is_navigation_message_supported)},
-    {"native_start_navigation_message_collection",
-            "()Z",
-            reinterpret_cast<void *>(
-                    android_location_GnssNavigationMessageProvider_start_navigation_message_collection)},
-    {"native_stop_navigation_message_collection",
-            "()Z",
-            reinterpret_cast<void *>(
-                    android_location_GnssNavigationMessageProvider_stop_navigation_message_collection)},
+        /* name, signature, funcPtr */
+        {"native_is_navigation_message_supported", "()Z",
+         reinterpret_cast<void*>(
+                 android_location_gnss_hal_GnssNative_is_navigation_message_supported)},
+        {"native_start_navigation_message_collection", "()Z",
+         reinterpret_cast<void*>(
+                 android_location_gnss_hal_GnssNative_start_navigation_message_collection)},
+        {"native_stop_navigation_message_collection", "()Z",
+         reinterpret_cast<void*>(
+                 android_location_gnss_hal_GnssNative_stop_navigation_message_collection)},
 };
 
 static const JNINativeMethod sNetworkConnectivityMethods[] = {
-     /* name, signature, funcPtr */
-    {"native_is_agps_ril_supported", "()Z",
-            reinterpret_cast<void *>(android_location_GnssNetworkConnectivityHandler_is_agps_ril_supported)},
-    {"native_update_network_state",
-            "(ZIZZLjava/lang/String;JS)V",
-            reinterpret_cast<void *>(android_location_GnssNetworkConnectivityHandler_update_network_state)},
-    {"native_agps_data_conn_open",
-            "(JLjava/lang/String;I)V",
-            reinterpret_cast<void *>(android_location_GnssNetworkConnectivityHandler_agps_data_conn_open)},
-    {"native_agps_data_conn_closed",
-            "()V",
-            reinterpret_cast<void *>(android_location_GnssNetworkConnectivityHandler_agps_data_conn_closed)},
-    {"native_agps_data_conn_failed",
-            "()V",
-            reinterpret_cast<void *>(android_location_GnssNetworkConnectivityHandler_agps_data_conn_failed)},
+        /* name, signature, funcPtr */
+        {"native_is_agps_ril_supported", "()Z",
+         reinterpret_cast<void*>(
+                 android_location_GnssNetworkConnectivityHandler_is_agps_ril_supported)},
+        {"native_update_network_state", "(ZIZZLjava/lang/String;JS)V",
+         reinterpret_cast<void*>(
+                 android_location_GnssNetworkConnectivityHandler_update_network_state)},
+        {"native_agps_data_conn_open", "(JLjava/lang/String;I)V",
+         reinterpret_cast<void*>(
+                 android_location_GnssNetworkConnectivityHandler_agps_data_conn_open)},
+        {"native_agps_data_conn_closed", "()V",
+         reinterpret_cast<void*>(
+                 android_location_GnssNetworkConnectivityHandler_agps_data_conn_closed)},
+        {"native_agps_data_conn_failed", "()V",
+         reinterpret_cast<void*>(
+                 android_location_GnssNetworkConnectivityHandler_agps_data_conn_failed)},
 };
 
 static const JNINativeMethod sConfigurationMethods[] = {
@@ -3297,45 +3288,73 @@
 };
 
 static const JNINativeMethod sVisibilityControlMethods[] = {
-     /* name, signature, funcPtr */
-    {"native_enable_nfw_location_access",
-            "([Ljava/lang/String;)Z",
-            reinterpret_cast<void *>(
-                    android_location_GnssVisibilityControl_enable_nfw_location_access)},
+        /* name, signature, funcPtr */
+        {"native_enable_nfw_location_access", "([Ljava/lang/String;)Z",
+         reinterpret_cast<void*>(
+                 android_location_GnssVisibilityControl_enable_nfw_location_access)},
 };
 
 static const JNINativeMethod sPowerIndicationMethods[] = {
         /* name, signature, funcPtr */
         {"native_request_power_stats", "()V",
-         reinterpret_cast<void*>(android_location_GnssLocationProvider_request_power_stats)},
+         reinterpret_cast<void*>(android_location_gnss_hal_GnssNative_request_power_stats)},
 };
 
 int register_android_server_location_GnssLocationProvider(JNIEnv* env) {
-    jniRegisterNativeMethods(env, "com/android/server/location/gnss/GnssAntennaInfoProvider",
-                             sAntennaInfoMethods, NELEM(sAntennaInfoMethods));
-    jniRegisterNativeMethods(env, "com/android/server/location/gnss/GnssLocationProvider",
-                             sMethodsBatching, NELEM(sMethodsBatching));
-    jniRegisterNativeMethods(env, "com/android/server/location/gnss/GnssGeofenceProvider",
-                             sGeofenceMethods, NELEM(sGeofenceMethods));
-    jniRegisterNativeMethods(env, "com/android/server/location/gnss/GnssMeasurementsProvider",
-                             sMeasurementMethods, NELEM(sMeasurementMethods));
-    jniRegisterNativeMethods(env,
-                             "com/android/server/location/gnss/GnssMeasurementCorrectionsProvider",
-                             sMeasurementCorrectionsMethods, NELEM(sMeasurementCorrectionsMethods));
-    jniRegisterNativeMethods(env, "com/android/server/location/gnss/GnssNavigationMessageProvider",
-                             sNavigationMessageMethods, NELEM(sNavigationMessageMethods));
-    jniRegisterNativeMethods(env, "com/android/server/location/gnss/GnssNetworkConnectivityHandler",
-                             sNetworkConnectivityMethods, NELEM(sNetworkConnectivityMethods));
-    jniRegisterNativeMethods(env, "com/android/server/location/gnss/GnssConfiguration",
-                             sConfigurationMethods, NELEM(sConfigurationMethods));
-    jniRegisterNativeMethods(env, "com/android/server/location/gnss/GnssVisibilityControl",
-                             sVisibilityControlMethods, NELEM(sVisibilityControlMethods));
-    jniRegisterNativeMethods(env, "com/android/server/location/gnss/GnssPowerIndicationProvider",
-                             sPowerIndicationMethods, NELEM(sPowerIndicationMethods));
-    jniRegisterNativeMethods(env, "com/android/server/location/gnss/GnssLocationProvider",
-                             sLocationProviderMethods, NELEM(sLocationProviderMethods));
-    return jniRegisterNativeMethods(env, "com/android/server/location/gnss/GnssNative",
-                                    sCoreMethods, NELEM(sCoreMethods));
+    int res;
+
+    res = jniRegisterNativeMethods(env, "com/android/server/location/gnss/hal/GnssNative",
+                                   sAntennaInfoMethods, NELEM(sAntennaInfoMethods));
+    LOG_FATAL_IF(res < 0, "Unable to register native methods.");
+
+    res = jniRegisterNativeMethods(env, "com/android/server/location/gnss/hal/GnssNative",
+                                   sBatchingMethods, NELEM(sBatchingMethods));
+    LOG_FATAL_IF(res < 0, "Unable to register native methods.");
+
+    res = jniRegisterNativeMethods(env, "com/android/server/location/gnss/hal/GnssNative",
+                                   sGeofenceMethods, NELEM(sGeofenceMethods));
+    LOG_FATAL_IF(res < 0, "Unable to register native methods.");
+
+    res = jniRegisterNativeMethods(env, "com/android/server/location/gnss/hal/GnssNative",
+                                   sMeasurementMethods, NELEM(sMeasurementMethods));
+    LOG_FATAL_IF(res < 0, "Unable to register native methods.");
+
+    res = jniRegisterNativeMethods(env, "com/android/server/location/gnss/hal/GnssNative",
+                                   sMeasurementCorrectionsMethods,
+                                   NELEM(sMeasurementCorrectionsMethods));
+    LOG_FATAL_IF(res < 0, "Unable to register native methods.");
+
+    res = jniRegisterNativeMethods(env, "com/android/server/location/gnss/hal/GnssNative",
+                                   sNavigationMessageMethods, NELEM(sNavigationMessageMethods));
+    LOG_FATAL_IF(res < 0, "Unable to register native methods.");
+
+    res = jniRegisterNativeMethods(env,
+                                   "com/android/server/location/gnss/"
+                                   "GnssNetworkConnectivityHandler",
+                                   sNetworkConnectivityMethods, NELEM(sNetworkConnectivityMethods));
+    LOG_FATAL_IF(res < 0, "Unable to register native methods.");
+
+    res = jniRegisterNativeMethods(env, "com/android/server/location/gnss/GnssConfiguration",
+                                   sConfigurationMethods, NELEM(sConfigurationMethods));
+    LOG_FATAL_IF(res < 0, "Unable to register native methods.");
+
+    res = jniRegisterNativeMethods(env, "com/android/server/location/gnss/GnssVisibilityControl",
+                                   sVisibilityControlMethods, NELEM(sVisibilityControlMethods));
+    LOG_FATAL_IF(res < 0, "Unable to register native methods.");
+
+    res = jniRegisterNativeMethods(env, "com/android/server/location/gnss/hal/GnssNative",
+                                   sPowerIndicationMethods, NELEM(sPowerIndicationMethods));
+    LOG_FATAL_IF(res < 0, "Unable to register native methods.");
+
+    res = jniRegisterNativeMethods(env, "com/android/server/location/gnss/hal/GnssNative",
+                                   sLocationProviderMethods, NELEM(sLocationProviderMethods));
+    LOG_FATAL_IF(res < 0, "Unable to register native methods.");
+
+    res = jniRegisterNativeMethods(env, "com/android/server/location/gnss/hal/GnssNative",
+                                   sCoreMethods, NELEM(sCoreMethods));
+    LOG_FATAL_IF(res < 0, "Unable to register native methods.");
+
+    return 0;
 }
 
 } /* namespace android */
diff --git a/services/core/xsd/display-device-config/display-device-config.xsd b/services/core/xsd/display-device-config/display-device-config.xsd
index d670991..1cb9e57 100644
--- a/services/core/xsd/display-device-config/display-device-config.xsd
+++ b/services/core/xsd/display-device-config/display-device-config.xsd
@@ -29,6 +29,11 @@
                     <xs:annotation name="nonnull"/>
                     <xs:annotation name="final"/>
                 </xs:element>
+                <xs:element type="nonNegativeDecimal" name="screenBrightnessDefault">
+                    <xs:annotation name="nonnull"/>
+                    <xs:annotation name="final"/>
+                </xs:element>
+                <xs:element type="highBrightnessMode" name="highBrightnessMode" minOccurs="0" maxOccurs="1"/>
                 <xs:element type="displayQuirks" name="quirks" minOccurs="0" maxOccurs="1" />
             </xs:sequence>
         </xs:complexType>
@@ -42,6 +47,38 @@
         </xs:sequence>
     </xs:complexType>
 
+    <xs:complexType name="highBrightnessMode">
+        <xs:all>
+            <xs:element name="transitionPoint" type="nonNegativeDecimal" minOccurs="1" maxOccurs="1">
+                <xs:annotation name="nonnull"/>
+                <xs:annotation name="final"/>
+            </xs:element>
+            <xs:element name="minimumLux" type="nonNegativeDecimal" minOccurs="1" maxOccurs="1">
+                <xs:annotation name="nonnull"/>
+                <xs:annotation name="final"/>
+            </xs:element>
+            <xs:element name="timing" type="hbmTiming" minOccurs="1" maxOccurs="1"/>
+        </xs:all>
+        <xs:attribute name="enabled" type="xs:boolean" use="optional"/>
+    </xs:complexType>
+
+    <xs:complexType name="hbmTiming">
+        <xs:all>
+            <xs:element name="timeWindowSecs" type="xs:nonNegativeInteger" minOccurs="1" maxOccurs="1">
+                <xs:annotation name="nonnull"/>
+                <xs:annotation name="final"/>
+            </xs:element>
+            <xs:element name="timeMaxSecs" type="xs:nonNegativeInteger" minOccurs="1" maxOccurs="1">
+                <xs:annotation name="nonnull"/>
+                <xs:annotation name="final"/>
+            </xs:element>
+            <xs:element name="timeMinSecs" type="xs:nonNegativeInteger" minOccurs="1" maxOccurs="1">
+                <xs:annotation name="nonnull"/>
+                <xs:annotation name="final"/>
+            </xs:element>
+        </xs:all>
+    </xs:complexType>
+
     <xs:complexType name="nitsMap">
         <xs:sequence>
             <xs:element name="point" type="point" maxOccurs="unbounded" minOccurs="2">
diff --git a/services/core/xsd/display-device-config/schema/current.txt b/services/core/xsd/display-device-config/schema/current.txt
index e68ca26..e073ab3 100644
--- a/services/core/xsd/display-device-config/schema/current.txt
+++ b/services/core/xsd/display-device-config/schema/current.txt
@@ -3,9 +3,13 @@
 
   public class DisplayConfiguration {
     ctor public DisplayConfiguration();
+    method public com.android.server.display.config.HighBrightnessMode getHighBrightnessMode();
     method public com.android.server.display.config.DisplayQuirks getQuirks();
+    method @NonNull public final java.math.BigDecimal getScreenBrightnessDefault();
     method @NonNull public final com.android.server.display.config.NitsMap getScreenBrightnessMap();
+    method public void setHighBrightnessMode(com.android.server.display.config.HighBrightnessMode);
     method public void setQuirks(com.android.server.display.config.DisplayQuirks);
+    method public final void setScreenBrightnessDefault(@NonNull java.math.BigDecimal);
     method public final void setScreenBrightnessMap(@NonNull com.android.server.display.config.NitsMap);
   }
 
@@ -14,6 +18,28 @@
     method public java.util.List<java.lang.String> getQuirk();
   }
 
+  public class HbmTiming {
+    ctor public HbmTiming();
+    method @NonNull public final java.math.BigInteger getTimeMaxSecs_all();
+    method @NonNull public final java.math.BigInteger getTimeMinSecs_all();
+    method @NonNull public final java.math.BigInteger getTimeWindowSecs_all();
+    method public final void setTimeMaxSecs_all(@NonNull java.math.BigInteger);
+    method public final void setTimeMinSecs_all(@NonNull java.math.BigInteger);
+    method public final void setTimeWindowSecs_all(@NonNull java.math.BigInteger);
+  }
+
+  public class HighBrightnessMode {
+    ctor public HighBrightnessMode();
+    method public boolean getEnabled();
+    method @NonNull public final java.math.BigDecimal getMinimumLux_all();
+    method public com.android.server.display.config.HbmTiming getTiming_all();
+    method @NonNull public final java.math.BigDecimal getTransitionPoint_all();
+    method public void setEnabled(boolean);
+    method public final void setMinimumLux_all(@NonNull java.math.BigDecimal);
+    method public void setTiming_all(com.android.server.display.config.HbmTiming);
+    method public final void setTransitionPoint_all(@NonNull java.math.BigDecimal);
+  }
+
   public class NitsMap {
     ctor public NitsMap();
     method @NonNull public final java.util.List<com.android.server.display.config.Point> getPoint();
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/CallerIdentity.java b/services/devicepolicy/java/com/android/server/devicepolicy/CallerIdentity.java
index b6b4d8a..4e422b2 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/CallerIdentity.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/CallerIdentity.java
@@ -25,7 +25,7 @@
  * All parameters are verified on object creation unless the component name is null and the
  * caller is a delegate.
  */
-class CallerIdentity {
+final class CallerIdentity {
 
     private final int mUid;
     @Nullable
@@ -51,7 +51,7 @@
         return UserHandle.getUserHandleForUid(mUid);
     }
 
-    @Nullable  public String getPackageName() {
+    @Nullable public String getPackageName() {
         return mPackageName;
     }
 
@@ -66,4 +66,16 @@
     public boolean hasPackage() {
         return mPackageName != null;
     }
+
+    @Override
+    public String toString() {
+        StringBuilder builder = new StringBuilder("CallerIdentity[uid=").append(mUid);
+        if (mPackageName != null) {
+            builder.append(", pkg=").append(mPackageName);
+        }
+        if (mComponentName != null) {
+            builder.append(", cmp=").append(mComponentName.flattenToShortString());
+        }
+        return builder.append("]").toString();
+    }
 }
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 5f6d76be..fdbd85a 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -983,8 +983,20 @@
 
     @Override
     public void setDevicePolicySafetyChecker(DevicePolicySafetyChecker safetyChecker) {
-        Slog.i(LOG_TAG, "Setting DevicePolicySafetyChecker as " + safetyChecker);
+        CallerIdentity callerIdentity = getCallerIdentity();
+        Preconditions.checkCallAuthorization(mIsAutomotive || isAdb(callerIdentity), "can only set "
+                + "DevicePolicySafetyChecker on automotive builds or from ADB (but caller is %s)",
+                callerIdentity);
+        setDevicePolicySafetyCheckerUnchecked(safetyChecker);
+    }
+
+    /**
+     * Used by {@code setDevicePolicySafetyChecker()} above and {@link OneTimeSafetyChecker}.
+     */
+    void setDevicePolicySafetyCheckerUnchecked(DevicePolicySafetyChecker safetyChecker) {
+        Slog.i(LOG_TAG, String.format("Setting DevicePolicySafetyChecker as %s", safetyChecker));
         mSafetyChecker = safetyChecker;
+        mInjector.setDevicePolicySafetyChecker(safetyChecker);
     }
 
     /**
@@ -1021,8 +1033,8 @@
     public void setNextOperationSafety(@DevicePolicyOperation int operation, boolean safe) {
         Preconditions.checkCallAuthorization(
                 hasCallingOrSelfPermission(permission.MANAGE_DEVICE_ADMINS));
-        Slog.i(LOG_TAG, "setNextOperationSafety(" + DevicePolicyManager.operationToString(operation)
-                + ", " + safe + ")");
+        Slog.i(LOG_TAG, String.format("setNextOperationSafety(%s, %b)",
+                DevicePolicyManager.operationToString(operation), safe));
         mSafetyChecker = new OneTimeSafetyChecker(this, operation, safe);
     }
 
@@ -1034,6 +1046,8 @@
 
         public final Context mContext;
 
+        private @Nullable DevicePolicySafetyChecker mSafetyChecker;
+
         Injector(Context context) {
             mContext = context;
         }
@@ -1278,7 +1292,8 @@
         void recoverySystemRebootWipeUserData(boolean shutdown, String reason, boolean force,
                 boolean wipeEuicc, boolean wipeExtRequested, boolean wipeResetProtectionData)
                         throws IOException {
-            FactoryResetter.newBuilder(mContext).setReason(reason).setShutdown(shutdown)
+            FactoryResetter.newBuilder(mContext).setSafetyChecker(mSafetyChecker)
+                    .setReason(reason).setShutdown(shutdown)
                     .setForce(force).setWipeEuicc(wipeEuicc)
                     .setWipeAdoptableStorage(wipeExtRequested)
                     .setWipeFactoryResetProtection(wipeResetProtectionData)
@@ -1433,6 +1448,10 @@
         public boolean isChangeEnabled(long changeId, String packageName, int userId) {
             return CompatChanges.isChangeEnabled(changeId, packageName, UserHandle.of(userId));
         }
+
+        void setDevicePolicySafetyChecker(DevicePolicySafetyChecker safetyChecker) {
+            mSafetyChecker = safetyChecker;
+        }
     }
 
     /**
@@ -7507,6 +7526,7 @@
         Preconditions.checkCallAuthorization((caller.hasAdminComponent() &&  isDeviceOwner(caller))
                 || (caller.hasPackage()
                 && isCallerDelegate(caller, DELEGATION_KEEP_UNINSTALLED_PACKAGES)));
+        checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_KEEP_UNINSTALLED_PACKAGES);
 
         synchronized (getLockObject()) {
             // Get the device owner
@@ -7620,10 +7640,9 @@
                         + " as profile owner on user " + currentForegroundUser);
                 // Sets profile owner on current foreground user since
                 // the human user will complete the DO setup workflow from there.
-                mInjector.binderWithCleanCallingIdentity(() ->
-                        manageUserUnchecked(/* deviceOwner= */ admin, /* profileOwner= */ admin,
-                                /* managedUser= */ currentForegroundUser,
-                                /* adminExtras= */ null));
+                manageUserUnchecked(/* deviceOwner= */ admin, /* profileOwner= */ admin,
+                            /* managedUser= */ currentForegroundUser,
+                            /* adminExtras= */ null);
             }
             return true;
         }
@@ -8976,6 +8995,7 @@
         Preconditions.checkCallAuthorization((caller.hasAdminComponent()
                 && (isProfileOwner(caller) || isDeviceOwner(caller)))
                 || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_APP_RESTRICTIONS)));
+        checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_APPLICATION_RESTRICTIONS);
 
         mInjector.binderWithCleanCallingIdentity(() -> {
             mUserManager.setApplicationRestrictions(packageName, settings,
@@ -9001,6 +9021,9 @@
         synchronized (getLockObject()) {
             ActiveAdmin ap = getActiveAdminForCallerLocked(admin,
                     DeviceAdminInfo.USES_POLICY_DISABLE_KEYGUARD_FEATURES, parent);
+            checkCanExecuteOrThrowUnsafe(
+                    DevicePolicyManager.OPERATION_SET_TRUST_AGENT_CONFIGURATION);
+
             ap.trustAgentInfos.put(agent.flattenToString(), new TrustAgentInfo(args));
             saveSettingsLocked(userHandle);
         }
@@ -9940,6 +9963,7 @@
         Preconditions.checkCallAuthorization((caller.hasAdminComponent()
                 && (isProfileOwner(caller) || isDeviceOwner(caller)))
                 || (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_PACKAGE_ACCESS)));
+        checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_PACKAGES_SUSPENDED);
 
         String[] result = null;
         synchronized (getLockObject()) {
@@ -10147,6 +10171,8 @@
                 mInjector.binderWithCleanCallingIdentity(() ->
                         enforcePackageIsSystemPackage(packageName, userId));
             }
+            checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_APPLICATION_HIDDEN);
+
             result = mInjector.binderWithCleanCallingIdentity(() -> mIPackageManager
                     .setApplicationHiddenSettingAsUser(packageName, hidden, userId));
         }
@@ -10745,6 +10771,7 @@
 
         synchronized (getLockObject()) {
             enforceCanCallLockTaskLocked(caller);
+            checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_LOCK_TASK_PACKAGES);
             final int userHandle = caller.getUserId();
             setLockTaskPackagesLocked(userHandle, new ArrayList<>(Arrays.asList(packages)));
         }
@@ -10797,6 +10824,7 @@
         final int userHandle = caller.getUserId();
         synchronized (getLockObject()) {
             enforceCanCallLockTaskLocked(caller);
+            checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_LOCK_TASK_FEATURES);
             setLockTaskFeaturesLocked(userHandle, flags);
         }
     }
@@ -10925,6 +10953,7 @@
         Preconditions.checkStringNotEmpty(setting, "String setting is null or empty");
         final CallerIdentity caller = getCallerIdentity(who);
         Preconditions.checkCallAuthorization(isProfileOwner(caller) || isDeviceOwner(caller));
+        checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_SYSTEM_SETTING);
 
         synchronized (getLockObject()) {
             if (!SYSTEM_SETTINGS_ALLOWLIST.contains(setting)) {
@@ -11226,6 +11255,7 @@
         if (isManagedProfile(userId)) {
             throw new SecurityException("Managed profile cannot disable keyguard");
         }
+        checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_KEYGUARD_DISABLED);
 
         long ident = mInjector.binderClearCallingIdentity();
         try {
@@ -11265,6 +11295,8 @@
             if (isManagedProfile(userId)) {
                 throw new SecurityException("Managed profile cannot disable status bar");
             }
+            checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_STATUS_BAR_DISABLED);
+
             DevicePolicyData policy = getUserData(userId);
             if (policy.mStatusBarDisabled != disabled) {
                 boolean isLockTaskMode = false;
@@ -11932,6 +11964,7 @@
         synchronized (getLockObject()) {
             Preconditions.checkCallAuthorization(isProfileOwnerOfOrganizationOwnedDevice(caller)
                     || isDeviceOwner(caller));
+            checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_SET_SYSTEM_UPDATE_POLICY);
 
             if (policy == null) {
                 mOwners.clearSystemUpdatePolicy();
@@ -15078,6 +15111,8 @@
         Preconditions.checkNotNull(packages, "packages is null");
         final CallerIdentity caller = getCallerIdentity(who);
         Preconditions.checkCallAuthorization(isDeviceOwner(caller));
+        checkCanExecuteOrThrowUnsafe(
+                DevicePolicyManager.OPERATION_SET_USER_CONTROL_DISABLED_PACKAGES);
 
         synchronized (getLockObject()) {
             setUserControlDisabledPackagesLocked(caller.getUserId(), packages);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/FactoryResetter.java b/services/devicepolicy/java/com/android/server/devicepolicy/FactoryResetter.java
index a0cf7a3..564950b 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/FactoryResetter.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/FactoryResetter.java
@@ -17,14 +17,18 @@
 package com.android.server.devicepolicy;
 
 import android.annotation.Nullable;
+import android.app.admin.DevicePolicySafetyChecker;
 import android.content.Context;
 import android.content.pm.PackageManager;
+import android.os.Bundle;
 import android.os.RecoverySystem;
+import android.os.RemoteException;
 import android.os.UserManager;
 import android.os.storage.StorageManager;
 import android.service.persistentdata.PersistentDataBlockManager;
-import android.util.Log;
+import android.util.Slog;
 
+import com.android.internal.os.IResultReceiver;
 import com.android.internal.util.Preconditions;
 
 import java.io.IOException;
@@ -38,6 +42,7 @@
     private static final String TAG = FactoryResetter.class.getSimpleName();
 
     private final Context mContext;
+    private final @Nullable DevicePolicySafetyChecker mSafetyChecker;
     private final @Nullable String mReason;
     private final boolean mShutdown;
     private final boolean mForce;
@@ -45,18 +50,40 @@
     private final boolean mWipeAdoptableStorage;
     private final boolean mWipeFactoryResetProtection;
 
-
     /**
      * Factory reset the device according to the builder's arguments.
      */
     public void factoryReset() throws IOException {
-        Log.i(TAG, String.format("factoryReset(): reason=%s, shutdown=%b, force=%b, wipeEuicc=%b"
-                + ", wipeAdoptableStorage=%b, wipeFRP=%b", mReason, mShutdown, mForce, mWipeEuicc,
-                mWipeAdoptableStorage, mWipeFactoryResetProtection));
-
         Preconditions.checkCallAuthorization(mContext.checkCallingOrSelfPermission(
                 android.Manifest.permission.MASTER_CLEAR) == PackageManager.PERMISSION_GRANTED);
 
+        if (mSafetyChecker == null) {
+            factoryResetInternalUnchecked();
+            return;
+        }
+
+        IResultReceiver receiver = new IResultReceiver.Stub() {
+            @Override
+            public void send(int resultCode, Bundle resultData) throws RemoteException {
+                Slog.i(TAG, String.format("Factory reset confirmed by %s, proceeding",
+                        mSafetyChecker));
+                try {
+                    factoryResetInternalUnchecked();
+                } catch (IOException e) {
+                    // Shouldn't happen
+                    Slog.wtf(TAG, "IOException calling underlying systems", e);
+                }
+            }
+        };
+        Slog.i(TAG, String.format("Delaying factory reset until %s confirms", mSafetyChecker));
+        mSafetyChecker.onFactoryReset(receiver);
+    }
+
+    private void factoryResetInternalUnchecked() throws IOException {
+        Slog.i(TAG, String.format("factoryReset(): reason=%s, shutdown=%b, force=%b, wipeEuicc=%b, "
+                + "wipeAdoptableStorage=%b, wipeFRP=%b", mReason, mShutdown, mForce, mWipeEuicc,
+                mWipeAdoptableStorage, mWipeFactoryResetProtection));
+
         UserManager um = mContext.getSystemService(UserManager.class);
         if (!mForce && um.hasUserRestriction(UserManager.DISALLOW_FACTORY_RESET)) {
             throw new SecurityException("Factory reset is not allowed for this user.");
@@ -66,15 +93,15 @@
             PersistentDataBlockManager manager = mContext
                     .getSystemService(PersistentDataBlockManager.class);
             if (manager != null) {
-                Log.w(TAG, "Wiping factory reset protection");
+                Slog.w(TAG, "Wiping factory reset protection");
                 manager.wipe();
             } else {
-                Log.w(TAG, "No need to wipe factory reset protection");
+                Slog.w(TAG, "No need to wipe factory reset protection");
             }
         }
 
         if (mWipeAdoptableStorage) {
-            Log.w(TAG, "Wiping adoptable storage");
+            Slog.w(TAG, "Wiping adoptable storage");
             StorageManager sm = mContext.getSystemService(StorageManager.class);
             sm.wipeAdoptableDisks();
         }
@@ -84,8 +111,9 @@
 
     private FactoryResetter(Builder builder) {
         mContext = builder.mContext;
-        mShutdown = builder.mShutdown;
+        mSafetyChecker = builder.mSafetyChecker;
         mReason = builder.mReason;
+        mShutdown = builder.mShutdown;
         mForce = builder.mForce;
         mWipeEuicc = builder.mWipeEuicc;
         mWipeAdoptableStorage = builder.mWipeAdoptableStorage;
@@ -105,6 +133,7 @@
     public static final class Builder {
 
         private final Context mContext;
+        private @Nullable DevicePolicySafetyChecker mSafetyChecker;
         private @Nullable String mReason;
         private boolean mShutdown;
         private boolean mForce;
@@ -117,6 +146,15 @@
         }
 
         /**
+         * Sets a {@link DevicePolicySafetyChecker} object that will be used to delay the
+         * factory reset when it's not safe to do so.
+         */
+        public Builder setSafetyChecker(@Nullable DevicePolicySafetyChecker safetyChecker) {
+            mSafetyChecker = safetyChecker;
+            return this;
+        }
+
+        /**
          * Sets the (non-null) reason for the factory reset that is visible in the logs
          */
         public Builder setReason(String reason) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/OneTimeSafetyChecker.java b/services/devicepolicy/java/com/android/server/devicepolicy/OneTimeSafetyChecker.java
index b0f8bfb..f7a8261 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/OneTimeSafetyChecker.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/OneTimeSafetyChecker.java
@@ -21,6 +21,8 @@
 import android.app.admin.DevicePolicySafetyChecker;
 import android.util.Slog;
 
+import com.android.internal.os.IResultReceiver;
+
 import java.util.Objects;
 
 //TODO(b/172376923): add unit tests
@@ -61,7 +63,12 @@
         }
         Slog.i(TAG, "isDevicePolicyOperationSafe(" + name + "): returning " + safe
                 + " and restoring DevicePolicySafetyChecker to " + mRealSafetyChecker);
-        mService.setDevicePolicySafetyChecker(mRealSafetyChecker);
+        mService.setDevicePolicySafetyCheckerUnchecked(mRealSafetyChecker);
         return safe;
     }
+
+    @Override
+    public void onFactoryReset(IResultReceiver callback) {
+        throw new UnsupportedOperationException();
+    }
 }
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 69e29d6..fff7f80 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -1030,6 +1030,9 @@
         mActivityManagerService.setSystemProcess();
         t.traceEnd();
 
+        // The package receiver depends on the activity service in order to get registered.
+        platformCompat.registerPackageReceiver(mSystemContext);
+
         // Complete the watchdog setup with an ActivityManager instance and listen for reboots
         // Do this only after the ActivityManagerService is properly started as a system process
         t.traceBegin("InitWatchdog");
diff --git a/services/musicrecognition/OWNERS b/services/musicrecognition/OWNERS
new file mode 100644
index 0000000..58f5d40
--- /dev/null
+++ b/services/musicrecognition/OWNERS
@@ -0,0 +1,6 @@
+# Bug component: 830636
+
+joannechung@google.com
+oni@google.com
+volnov@google.com
+
diff --git a/services/robotests/src/com/android/server/location/gnss/GnssGeofenceProviderTest.java b/services/robotests/src/com/android/server/location/gnss/GnssGeofenceProviderTest.java
deleted file mode 100644
index 48e6ce8..0000000
--- a/services/robotests/src/com/android/server/location/gnss/GnssGeofenceProviderTest.java
+++ /dev/null
@@ -1,155 +0,0 @@
-/*
- * 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.location.gnss;
-
-import static org.mockito.ArgumentMatchers.anyDouble;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.os.RemoteException;
-import android.platform.test.annotations.Presubmit;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.robolectric.RobolectricTestRunner;
-
-/**
- * Unit tests for {@link GnssGeofenceProvider}.
- */
-@RunWith(RobolectricTestRunner.class)
-@Presubmit
-public class GnssGeofenceProviderTest {
-    private static final int GEOFENCE_ID = 12345;
-    private static final double LATITUDE = 10.0;
-    private static final double LONGITUDE = 20.0;
-    private static final double RADIUS = 5.0;
-    private static final int LAST_TRANSITION = 0;
-    private static final int MONITOR_TRANSITIONS = 0;
-    private static final int NOTIFICATION_RESPONSIVENESS = 0;
-    private static final int UNKNOWN_TIMER = 0;
-    @Mock
-    private GnssGeofenceProvider.GnssGeofenceProviderNative mMockNative;
-    private GnssGeofenceProvider mTestProvider;
-
-    /**
-     * Mocks native methods and adds a geofence to the GnssGeofenceProvider.
-     */
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-        when(mMockNative.addGeofence(anyInt(), anyDouble(), anyDouble(), anyDouble(), anyInt(),
-                anyInt(), anyInt(), anyInt())).thenReturn(true);
-        when(mMockNative.pauseGeofence(anyInt())).thenReturn(true);
-        when(mMockNative.removeGeofence(anyInt())).thenReturn(true);
-        when(mMockNative.resumeGeofence(anyInt(), anyInt())).thenReturn(true);
-        mTestProvider = new GnssGeofenceProvider(mMockNative);
-        mTestProvider.addCircularHardwareGeofence(GEOFENCE_ID, LATITUDE,
-                LONGITUDE, RADIUS, LAST_TRANSITION, MONITOR_TRANSITIONS,
-                NOTIFICATION_RESPONSIVENESS,
-                UNKNOWN_TIMER);
-    }
-
-    /**
-     * Verify native add geofence method is called.
-     */
-    @Test
-    public void addGeofence_nativeAdded() {
-        verify(mMockNative).addGeofence(eq(GEOFENCE_ID), eq(LATITUDE), eq(LONGITUDE),
-                eq(RADIUS), eq(LAST_TRANSITION), eq(MONITOR_TRANSITIONS),
-                eq(NOTIFICATION_RESPONSIVENESS),
-                eq(UNKNOWN_TIMER));
-    }
-
-    /**
-     * Verify pauseHardwareGeofence calls native pauseGeofence method.
-     */
-    @Test
-    public void pauseGeofence_nativePaused() {
-        mTestProvider.pauseHardwareGeofence(GEOFENCE_ID);
-        verify(mMockNative).pauseGeofence(eq(GEOFENCE_ID));
-    }
-
-    /**
-     * Verify removeHardwareGeofence calls native removeGeofence method.
-     */
-    @Test
-    public void removeGeofence_nativeRemoved() {
-        mTestProvider.removeHardwareGeofence(GEOFENCE_ID);
-        verify(mMockNative).removeGeofence(eq(GEOFENCE_ID));
-    }
-
-    /**
-     * Verify resumeHardwareGeofence, called after pauseHardwareGeofence, will call native
-     * resumeGeofence method.
-     */
-    @Test
-    public void resumeGeofence_nativeResumed() {
-        mTestProvider.pauseHardwareGeofence(GEOFENCE_ID);
-        mTestProvider.resumeHardwareGeofence(GEOFENCE_ID, MONITOR_TRANSITIONS);
-        verify(mMockNative).resumeGeofence(eq(GEOFENCE_ID), eq(MONITOR_TRANSITIONS));
-    }
-
-    /**
-     * Verify resumeIfStarted method will re-add previously added geofences.
-     */
-    @Test
-    public void addGeofence_restart_added() throws RemoteException {
-        mTestProvider.resumeIfStarted();
-
-        verify(mMockNative, times(2)).addGeofence(eq(GEOFENCE_ID), eq(LATITUDE), eq(LONGITUDE),
-                eq(RADIUS), eq(LAST_TRANSITION), eq(MONITOR_TRANSITIONS),
-                eq(NOTIFICATION_RESPONSIVENESS),
-                eq(UNKNOWN_TIMER));
-    }
-
-    /**
-     * Verify resumeIfStarted method will not re-add previously added geofences if they have been
-     * removed.
-     */
-    @Test
-    public void removeGeofence_restart_notAdded() throws RemoteException {
-        mTestProvider.removeHardwareGeofence(GEOFENCE_ID);
-        mTestProvider.resumeIfStarted();
-
-        verify(mMockNative, times(1)).addGeofence(eq(GEOFENCE_ID), eq(LATITUDE), eq(LONGITUDE),
-                eq(RADIUS), eq(LAST_TRANSITION), eq(MONITOR_TRANSITIONS),
-                eq(NOTIFICATION_RESPONSIVENESS),
-                eq(UNKNOWN_TIMER));
-    }
-
-    /**
-     * Verify resumeIfStarted, called after pauseHardwareGeofence, will re-add previously added
-     * geofences, and re-pause geofencing.
-     */
-    @Test
-    public void pauseGeofence_restart_paused() throws RemoteException {
-        mTestProvider.pauseHardwareGeofence(GEOFENCE_ID);
-        mTestProvider.resumeIfStarted();
-
-        verify(mMockNative, times(2)).addGeofence(eq(GEOFENCE_ID), eq(LATITUDE), eq(LONGITUDE),
-                eq(RADIUS), eq(LAST_TRANSITION), eq(MONITOR_TRANSITIONS),
-                eq(NOTIFICATION_RESPONSIVENESS),
-                eq(UNKNOWN_TIMER));
-        verify(mMockNative, times(2)).pauseGeofence(eq(GEOFENCE_ID));
-    }
-}
diff --git a/services/robotests/src/com/android/server/location/gnss/GnssPositionModeTest.java b/services/robotests/src/com/android/server/location/gnss/GnssPositionModeTest.java
deleted file mode 100644
index e7d3e51..0000000
--- a/services/robotests/src/com/android/server/location/gnss/GnssPositionModeTest.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * 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.location.gnss;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.platform.test.annotations.Presubmit;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-
-import java.util.HashSet;
-
-/**
- * Unit tests for {@link GnssPositionMode}.
- */
-@RunWith(RobolectricTestRunner.class)
-@Presubmit
-public class GnssPositionModeTest {
-
-    private GnssPositionMode mPositionMode1 = createGnssPositionMode(0, 1000);
-    private GnssPositionMode mPositionMode2 = createGnssPositionMode(0, 1000);
-    private GnssPositionMode mPositionMode3 = createGnssPositionMode(1, 1000);
-
-    /**
-     * Verifies hashcode method.
-     */
-    @Test
-    public void testHashCode() {
-        assertThat(mPositionMode1.hashCode()).isEqualTo(mPositionMode2.hashCode());
-        assertThat(mPositionMode1.hashCode()).isNotEqualTo(mPositionMode3.hashCode());
-        assertThat(mPositionMode1.hashCode()).isNotEqualTo(mPositionMode3.hashCode());
-
-        HashSet<Integer> hashSet = new HashSet<>();
-        hashSet.add(mPositionMode1.hashCode());
-        hashSet.add(mPositionMode2.hashCode());
-        assertThat(hashSet.size()).isEqualTo(1);
-        hashSet.add(mPositionMode3.hashCode());
-        assertThat(hashSet.size()).isEqualTo(2);
-    }
-
-    /**
-     * Verify that GnssPositionMode objects that return true for equals() also have the same
-     * hashcode.
-     */
-    @Test
-    public void checkIfEqualsImpliesSameHashCode() {
-        assertTEqualsImpliesSameHashCode(mPositionMode1, mPositionMode2);
-        assertTEqualsImpliesSameHashCode(mPositionMode2, mPositionMode3);
-    }
-
-    private void assertTEqualsImpliesSameHashCode(GnssPositionMode mode1, GnssPositionMode mode2) {
-        if (mode1.equals(mode2)) {
-            assertThat(mode1.hashCode()).isEqualTo(mode2.hashCode());
-        }
-    }
-
-    private GnssPositionMode createGnssPositionMode(int mode, int minInterval) {
-        return new GnssPositionMode(mode, 0, minInterval, 0, 0, true);
-    }
-}
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ActiveServicesTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ActiveServicesTest.java
new file mode 100644
index 0000000..0633ab9
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/am/ActiveServicesTest.java
@@ -0,0 +1,95 @@
+/*
+ * 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.am;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
+import static com.android.server.am.ActiveServices.FGS_BG_START_USE_EXEMPTION_LIST_CHANGE_ID;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+
+import android.app.compat.CompatChanges;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
+
+
+
+@RunWith(AndroidJUnit4.class)
+public class ActiveServicesTest {
+
+    private MockitoSession mMockingSession;
+
+    @Before
+    public void setUp() {
+        mMockingSession = mockitoSession()
+                .initMocks(this)
+                .strictness(Strictness.LENIENT)
+                .mockStatic(CompatChanges.class)
+                .startMocking();
+    }
+
+    @After
+    public void tearDown() {
+        if (mMockingSession != null) {
+            mMockingSession.finishMocking();
+        }
+    }
+
+    private void checkPackageExempted(String pkg, int uid, boolean expected) {
+        assertEquals("Package=" + pkg + " uid=" + uid,
+                expected, ActiveServices.isPackageExemptedFromFgsRestriction(pkg, uid));
+    }
+
+    @Test
+    public void isPackageExemptedFromFgsRestriction() {
+        // Compat changes are enabled by default.
+        when(CompatChanges.isChangeEnabled(anyLong(), anyInt())).thenReturn(true);
+
+        checkPackageExempted("", 1, false);
+        checkPackageExempted("abc", 1, false);
+        checkPackageExempted("com.random", 1, false);
+
+        // This package is exempted but not its subpackages.
+        checkPackageExempted("com.google.pixel.exo.bootstrapping", 1, true);
+        checkPackageExempted("com.google.pixel.exo.bootstrapping.subpackage", 1, false);
+
+        // Subpackages are also exempted.
+        checkPackageExempted("com.android.webview", 1, true);
+        checkPackageExempted("com.android.webview.beta", 1, true);
+        checkPackageExempted("com.chrome", 1, true);
+        checkPackageExempted("com.chrome.canary", 1, true);
+
+        checkPackageExempted("com.android.webviewx", 1, false);
+
+        // Now toggle the compat ID for a specific UID.
+        when(CompatChanges.isChangeEnabled(FGS_BG_START_USE_EXEMPTION_LIST_CHANGE_ID, 10))
+                .thenReturn(false);
+        // Exempted package, but compat id is disabled for the UID.
+        checkPackageExempted("com.android.webview", 10, false);
+
+        // Exempted package, but compat id is still enabled for the UID.
+        checkPackageExempted("com.android.webview", 11, true);
+    }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/devicepolicy/FactoryResetterTest.java b/services/tests/mockingservicestests/src/com/android/server/devicepolicy/FactoryResetterTest.java
index 829e312..c4d14f9 100644
--- a/services/tests/mockingservicestests/src/com/android/server/devicepolicy/FactoryResetterTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/devicepolicy/FactoryResetterTest.java
@@ -26,6 +26,7 @@
 import static org.mockito.Mockito.when;
 import static org.testng.Assert.assertThrows;
 
+import android.app.admin.DevicePolicySafetyChecker;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.os.RecoverySystem;
@@ -35,6 +36,8 @@
 import android.service.persistentdata.PersistentDataBlockManager;
 import android.util.Log;
 
+import com.android.internal.os.IResultReceiver;
+
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -58,6 +61,7 @@
     private @Mock StorageManager mSm;
     private @Mock PersistentDataBlockManager mPdbm;
     private @Mock UserManager mUm;
+    private @Mock DevicePolicySafetyChecker mSafetyChecker;
 
     @Before
     public void startSession() {
@@ -69,7 +73,7 @@
 
         when(mContext.getSystemService(any(Class.class))).thenAnswer((inv) -> {
             Log.d(TAG, "Mocking " + inv);
-            Class serviceClass = (Class) inv.getArguments()[0];
+            Class<?> serviceClass = (Class<?>) inv.getArguments()[0];
             if (serviceClass.equals(PersistentDataBlockManager.class)) return mPdbm;
             if (serviceClass.equals(StorageManager.class)) return mSm;
             if (serviceClass.equals(UserManager.class)) return mUm;
@@ -194,6 +198,44 @@
         verifyRebootWipeUserDataAllArgsCalled();
     }
 
+    @Test
+    public void testFactoryReset_minimumArgs_safetyChecker_neverReplied() throws Exception {
+        allowFactoryReset();
+
+        FactoryResetter.newBuilder(mContext).setSafetyChecker(mSafetyChecker).build()
+                .factoryReset();
+
+        verifyWipeAdoptableStorageNotCalled();
+        verifyWipeFactoryResetProtectionNotCalled();
+        verifyRebootWipeUserDataNotCalled();
+    }
+
+    @Test
+    public void testFactoryReset_allArgs_safetyChecker_replied() throws Exception {
+        allowFactoryReset();
+
+        doAnswer((inv) -> {
+            Log.d(TAG, "Mocking " + inv);
+            IResultReceiver receiver = (IResultReceiver) inv.getArguments()[0];
+            receiver.send(0, null);
+            return null;
+        }).when(mSafetyChecker).onFactoryReset(any());
+
+        FactoryResetter.newBuilder(mContext)
+                .setSafetyChecker(mSafetyChecker)
+                .setReason(REASON)
+                .setForce(true)
+                .setShutdown(true)
+                .setWipeEuicc(true)
+                .setWipeAdoptableStorage(true)
+                .setWipeFactoryResetProtection(true)
+                .build().factoryReset();
+
+        verifyWipeAdoptableStorageCalled();
+        verifyWipeFactoryResetProtectionCalled();
+        verifyRebootWipeUserDataAllArgsCalled();
+    }
+
     private void revokeMasterClearPermission() {
         when(mContext.checkCallingOrSelfPermission(android.Manifest.permission.MASTER_CLEAR))
                 .thenReturn(PackageManager.PERMISSION_DENIED);
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
index 344a19a..05f1ed8 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
@@ -209,7 +209,8 @@
                 ArgumentCaptor.forClass(BroadcastReceiver.class);
         ArgumentCaptor<IUidObserver> uidObserverCaptor =
                 ArgumentCaptor.forClass(IUidObserver.class);
-        mQuotaController = new QuotaController(mJobSchedulerService);
+        mQuotaController = new QuotaController(mJobSchedulerService,
+                mock(BackgroundJobsController.class), mock(ConnectivityController.class));
 
         verify(mContext).registerReceiver(receiverCaptor.capture(), any());
         mChargingReceiver = receiverCaptor.getValue();
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/gnss/GnssGeofenceProxyTest.java b/services/tests/mockingservicestests/src/com/android/server/location/gnss/GnssGeofenceProxyTest.java
new file mode 100644
index 0000000..b480f24
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/location/gnss/GnssGeofenceProxyTest.java
@@ -0,0 +1,134 @@
+/*
+ * 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.location.gnss;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.platform.test.annotations.Presubmit;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.location.gnss.hal.FakeGnssHal;
+import com.android.server.location.gnss.hal.GnssNative;
+import com.android.server.location.injector.TestInjector;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Objects;
+
+@Presubmit
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class GnssGeofenceProxyTest {
+
+    private static final int GEOFENCE_ID = 12345;
+    private static final double LATITUDE = 10.0;
+    private static final double LONGITUDE = 20.0;
+    private static final double RADIUS = 5.0;
+    private static final int LAST_TRANSITION = 0;
+    private static final int MONITOR_TRANSITIONS = 0;
+    private static final int NOTIFICATION_RESPONSIVENESS = 0;
+    private static final int UNKNOWN_TIMER = 0;
+
+    private @Mock GnssConfiguration mMockConfiguration;
+    private @Mock GnssNative.GeofenceCallbacks mGeofenceCallbacks;
+
+    private FakeGnssHal mFakeHal;
+    private GnssGeofenceProxy mTestProvider;
+
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+
+        mFakeHal = new FakeGnssHal();
+        GnssNative.setGnssHalForTest(mFakeHal);
+
+        GnssNative gnssNative = Objects.requireNonNull(
+                GnssNative.create(new TestInjector(), mMockConfiguration));
+        gnssNative.setGeofenceCallbacks(mGeofenceCallbacks);
+        mTestProvider = new GnssGeofenceProxy(gnssNative);
+        gnssNative.register();
+
+        mTestProvider.addCircularHardwareGeofence(GEOFENCE_ID, LATITUDE, LONGITUDE, RADIUS,
+                LAST_TRANSITION, MONITOR_TRANSITIONS, NOTIFICATION_RESPONSIVENESS, UNKNOWN_TIMER);
+    }
+
+    @Test
+    public void testAddGeofence() {
+        assertThat(mFakeHal.getGeofences()).containsExactly(new FakeGnssHal.GnssHalGeofence(
+                GEOFENCE_ID, LATITUDE, LONGITUDE, RADIUS, LAST_TRANSITION, MONITOR_TRANSITIONS,
+                NOTIFICATION_RESPONSIVENESS, UNKNOWN_TIMER, false));
+    }
+
+    @Test
+    public void testRemoveGeofence() {
+        mTestProvider.removeHardwareGeofence(GEOFENCE_ID);
+
+        assertThat(mFakeHal.getGeofences()).isEmpty();
+    }
+
+    @Test
+    public void testPauseGeofence() {
+        mTestProvider.pauseHardwareGeofence(GEOFENCE_ID);
+
+        assertThat(mFakeHal.getGeofences()).containsExactly(new FakeGnssHal.GnssHalGeofence(
+                GEOFENCE_ID, LATITUDE, LONGITUDE, RADIUS, LAST_TRANSITION, MONITOR_TRANSITIONS,
+                NOTIFICATION_RESPONSIVENESS, UNKNOWN_TIMER, true));
+    }
+
+    @Test
+    public void testResumeGeofence() {
+        mTestProvider.pauseHardwareGeofence(GEOFENCE_ID);
+        mTestProvider.resumeHardwareGeofence(GEOFENCE_ID, MONITOR_TRANSITIONS);
+
+        assertThat(mFakeHal.getGeofences()).containsExactly(new FakeGnssHal.GnssHalGeofence(
+                GEOFENCE_ID, LATITUDE, LONGITUDE, RADIUS, LAST_TRANSITION, MONITOR_TRANSITIONS,
+                NOTIFICATION_RESPONSIVENESS, UNKNOWN_TIMER, false));
+    }
+
+    @Test
+    public void testAddGeofence_restart() {
+        mFakeHal.restartHal();
+
+        assertThat(mFakeHal.getGeofences()).containsExactly(new FakeGnssHal.GnssHalGeofence(
+                GEOFENCE_ID, LATITUDE, LONGITUDE, RADIUS, LAST_TRANSITION, MONITOR_TRANSITIONS,
+                NOTIFICATION_RESPONSIVENESS, UNKNOWN_TIMER, false));
+    }
+
+    @Test
+    public void testRemoveGeofence_restart() {
+        mTestProvider.removeHardwareGeofence(GEOFENCE_ID);
+        mFakeHal.restartHal();
+
+        assertThat(mFakeHal.getGeofences()).isEmpty();
+    }
+
+    @Test
+    public void testPauseGeofence_restart() {
+        mTestProvider.pauseHardwareGeofence(GEOFENCE_ID);
+        mFakeHal.restartHal();
+
+        assertThat(mFakeHal.getGeofences()).containsExactly(new FakeGnssHal.GnssHalGeofence(
+                GEOFENCE_ID, LATITUDE, LONGITUDE, RADIUS, LAST_TRANSITION, MONITOR_TRANSITIONS,
+                NOTIFICATION_RESPONSIVENESS, UNKNOWN_TIMER, true));
+    }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/gnss/GnssManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/location/gnss/GnssManagerServiceTest.java
deleted file mode 100644
index 2b21cc5..0000000
--- a/services/tests/mockingservicestests/src/com/android/server/location/gnss/GnssManagerServiceTest.java
+++ /dev/null
@@ -1,682 +0,0 @@
-/*
- * 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.location.gnss;
-
-import static android.app.AppOpsManager.OP_COARSE_LOCATION;
-import static android.app.AppOpsManager.OP_FINE_LOCATION;
-import static android.location.LocationManager.GPS_PROVIDER;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyInt;
-import static org.mockito.ArgumentMatchers.anyLong;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.ArgumentMatchers.nullable;
-import static org.mockito.Mockito.after;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.timeout;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-import static org.testng.Assert.assertThrows;
-
-import android.Manifest;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.location.GnssAntennaInfo;
-import android.location.GnssAntennaInfo.SphericalCorrections;
-import android.location.GnssClock;
-import android.location.GnssMeasurementCorrections;
-import android.location.GnssMeasurementRequest;
-import android.location.GnssMeasurementsEvent;
-import android.location.GnssNavigationMessage;
-import android.location.GnssSingleSatCorrection;
-import android.location.IGnssAntennaInfoListener;
-import android.location.IGnssMeasurementsListener;
-import android.location.IGnssNavigationMessageListener;
-import android.location.IGnssStatusListener;
-import android.location.INetInitiatedListener;
-import android.location.LocationManagerInternal;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.IInterface;
-import android.os.Message;
-import android.os.RemoteException;
-
-import com.android.server.LocalServices;
-import com.android.server.location.gnss.GnssAntennaInfoProvider.GnssAntennaInfoProviderNative;
-import com.android.server.location.gnss.GnssMeasurementsProvider.GnssMeasurementProviderNative;
-import com.android.server.location.gnss.GnssNavigationMessageProvider.GnssNavigationMessageProviderNative;
-import com.android.server.location.injector.TestInjector;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.AdditionalMatchers;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.MockitoAnnotations;
-import org.mockito.invocation.InvocationOnMock;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
-/**
- * Unit tests for {@link com.android.server.location.gnss.GnssManagerService}.
- */
-public class GnssManagerServiceTest {
-
-    private static final long TIMEOUT_MS = 5000;
-    private static final long FAILURE_TIMEOUT_MS = 200;
-
-    private static final String TEST_PACKAGE = "com.test";
-
-    private TestInjector mInjector;
-
-    @Mock private Handler mMockHandler;
-    @Mock private Context mMockContext;
-    @Mock private PackageManager mPackageManager;
-    @Mock private LocationManagerInternal mLocationManagerInternal;
-    @Mock private GnssNative.GnssNativeInitNative mGnssInitNative;
-    @Mock private GnssLocationProvider mMockGnssLocationProvider;
-    @Mock private GnssLocationProvider.GnssSystemInfoProvider mMockGnssSystemInfoProvider;
-    @Mock private GnssCapabilitiesProvider mMockGnssCapabilitiesProvider;
-    @Mock private GnssMeasurementCorrectionsProvider mMockGnssMeasurementCorrectionsProvider;
-    @Mock private INetInitiatedListener mNetInitiatedListener;
-
-    private GnssMeasurementsProvider mTestGnssMeasurementsProvider;
-    private GnssStatusProvider mTestGnssStatusProvider;
-    private GnssNavigationMessageProvider mTestGnssNavigationMessageProvider;
-    private GnssAntennaInfoProvider mTestGnssAntennaInfoProvider;
-
-    private GnssManagerService mGnssManagerService;
-
-    @Before
-    public void setUp() {
-        MockitoAnnotations.initMocks(this);
-
-        when(mGnssInitNative.isSupported()).thenReturn(true);
-        GnssNative.setInitNativeForTest(mGnssInitNative);
-        GnssNative.resetCallbacksForTest();
-
-        when(mMockContext.createAttributionContext(anyString())).thenReturn(mMockContext);
-        when(mMockContext.getPackageManager()).thenReturn(mPackageManager);
-        when(mPackageManager.getPackagesForUid(anyInt())).thenReturn(
-                new String[]{TEST_PACKAGE});
-
-        mInjector = new TestInjector();
-
-        enableLocationPermissions();
-
-        LocalServices.addService(LocationManagerInternal.class, mLocationManagerInternal);
-
-        // Mock Handler will execute posted runnables immediately
-        when(mMockHandler.sendMessageAtTime(any(Message.class), anyLong())).thenAnswer(
-                (InvocationOnMock invocation) -> {
-                    Message msg = (Message) (invocation.getArguments()[0]);
-                    msg.getCallback().run();
-                    return null;
-                });
-
-        // Setup providers
-        mTestGnssMeasurementsProvider = createGnssMeasurementsProvider();
-        mTestGnssStatusProvider = createGnssStatusListenerHelper();
-        mTestGnssNavigationMessageProvider = createGnssNavigationMessageProvider();
-        mTestGnssAntennaInfoProvider = createGnssAntennaInfoProvider();
-
-        // Setup GnssLocationProvider to return providers
-        when(mMockGnssLocationProvider.getGnssStatusProvider()).thenReturn(
-                mTestGnssStatusProvider);
-        when(mMockGnssLocationProvider.getGnssCapabilitiesProvider()).thenReturn(
-                mMockGnssCapabilitiesProvider);
-        when(mMockGnssLocationProvider.getGnssSystemInfoProvider()).thenReturn(
-                mMockGnssSystemInfoProvider);
-        when(mMockGnssLocationProvider.getGnssMeasurementCorrectionsProvider()).thenReturn(
-                mMockGnssMeasurementCorrectionsProvider);
-        when(mMockGnssLocationProvider.getGnssMeasurementsProvider()).thenReturn(
-                mTestGnssMeasurementsProvider);
-        when(mMockGnssLocationProvider.getGnssNavigationMessageProvider()).thenReturn(
-                mTestGnssNavigationMessageProvider);
-        when(mMockGnssLocationProvider.getNetInitiatedListener()).thenReturn(
-                mNetInitiatedListener);
-        when(mMockGnssLocationProvider.getGnssAntennaInfoProvider()).thenReturn(
-                mTestGnssAntennaInfoProvider);
-
-        // Create GnssManagerService
-        mGnssManagerService = new GnssManagerService(mMockContext, mInjector,
-                mMockGnssLocationProvider);
-        mGnssManagerService.onSystemReady();
-    }
-
-    @After
-    public void tearDown() {
-        LocalServices.removeServiceForTest(LocationManagerInternal.class);
-    }
-
-    private void overrideAsBinder(IInterface mockListener) {
-        IBinder mockBinder = mock(IBinder.class);
-        when(mockListener.asBinder()).thenReturn(mockBinder);
-    }
-
-    private IGnssStatusListener createMockGnssStatusListener() {
-        IGnssStatusListener mockListener = mock(IGnssStatusListener.class);
-        overrideAsBinder(mockListener);
-        return mockListener;
-    }
-
-    private IGnssMeasurementsListener createMockGnssMeasurementsListener() {
-        IGnssMeasurementsListener mockListener = mock(
-                IGnssMeasurementsListener.class);
-        overrideAsBinder(mockListener);
-        return mockListener;
-    }
-
-    private IGnssAntennaInfoListener createMockGnssAntennaInfoListener() {
-        IGnssAntennaInfoListener mockListener = mock(IGnssAntennaInfoListener.class);
-        overrideAsBinder(mockListener);
-        return mockListener;
-    }
-
-    private IGnssNavigationMessageListener createMockGnssNavigationMessageListener() {
-        IGnssNavigationMessageListener mockListener = mock(IGnssNavigationMessageListener.class);
-        overrideAsBinder(mockListener);
-        return mockListener;
-    }
-
-    private GnssMeasurementCorrections createDummyGnssMeasurementCorrections() {
-        GnssSingleSatCorrection gnssSingleSatCorrection =
-                new GnssSingleSatCorrection.Builder().build();
-        return
-                new GnssMeasurementCorrections.Builder().setSingleSatelliteCorrectionList(
-                        Collections.singletonList(gnssSingleSatCorrection)).build();
-    }
-
-    private static List<GnssAntennaInfo> createDummyGnssAntennaInfos() {
-        double carrierFrequencyMHz = 13758.0;
-
-        GnssAntennaInfo.PhaseCenterOffset phaseCenterOffset = new
-                GnssAntennaInfo.PhaseCenterOffset(
-                4.3d,
-                1.4d,
-                2.10d,
-                2.1d,
-                3.12d,
-                0.5d);
-
-        double[][] phaseCenterVariationCorrectionsMillimeters = new double[10][10];
-        double[][] phaseCenterVariationCorrectionsUncertaintyMillimeters = new double[10][10];
-        SphericalCorrections
-                phaseCenterVariationCorrections =
-                new SphericalCorrections(
-                        phaseCenterVariationCorrectionsMillimeters,
-                        phaseCenterVariationCorrectionsUncertaintyMillimeters);
-
-        double[][] signalGainCorrectionsDbi = new double[10][10];
-        double[][] signalGainCorrectionsUncertaintyDbi = new double[10][10];
-        SphericalCorrections signalGainCorrections = new
-                SphericalCorrections(
-                signalGainCorrectionsDbi,
-                signalGainCorrectionsUncertaintyDbi);
-
-        List<GnssAntennaInfo> gnssAntennaInfos = new ArrayList<>();
-        gnssAntennaInfos.add(new GnssAntennaInfo.Builder()
-                .setCarrierFrequencyMHz(carrierFrequencyMHz)
-                .setPhaseCenterOffset(phaseCenterOffset)
-                .setPhaseCenterVariationCorrections(phaseCenterVariationCorrections)
-                .setSignalGainCorrections(signalGainCorrections)
-                .build());
-        return gnssAntennaInfos;
-    }
-
-    private void enableLocationPermissions() {
-        Mockito.doThrow(new SecurityException()).when(
-                mMockContext).enforceCallingOrSelfPermission(
-                AdditionalMatchers.and(
-                        AdditionalMatchers.not(eq(Manifest.permission.LOCATION_HARDWARE)),
-                        AdditionalMatchers.not(eq(Manifest.permission.ACCESS_FINE_LOCATION))),
-                anyString());
-        when(mMockContext.checkPermission(
-                eq(android.Manifest.permission.LOCATION_HARDWARE), anyInt(), anyInt())).thenReturn(
-                PackageManager.PERMISSION_GRANTED);
-        when(mMockContext.checkPermission(
-                eq(Manifest.permission.ACCESS_FINE_LOCATION), anyInt(), anyInt())).thenReturn(
-                PackageManager.PERMISSION_GRANTED);
-        when(mMockContext.checkPermission(
-                eq(Manifest.permission.ACCESS_COARSE_LOCATION), anyInt(), anyInt())).thenReturn(
-                PackageManager.PERMISSION_GRANTED);
-
-        mInjector.getAppOpsHelper().setAppOpAllowed(OP_FINE_LOCATION, TEST_PACKAGE, true);
-        mInjector.getAppOpsHelper().setAppOpAllowed(OP_COARSE_LOCATION, TEST_PACKAGE, true);
-
-        when(mLocationManagerInternal.isProviderEnabledForUser(eq(GPS_PROVIDER), anyInt()))
-                .thenReturn(true);
-    }
-
-    private void disableLocationPermissions() {
-        Mockito.doThrow(new SecurityException()).when(
-                mMockContext).enforceCallingOrSelfPermission(anyString(), nullable(String.class));
-
-        when(mMockContext.checkPermission(
-                anyString(), anyInt(), anyInt())).thenReturn(
-                PackageManager.PERMISSION_DENIED);
-
-        mInjector.getAppOpsHelper().setAppOpAllowed(OP_FINE_LOCATION, TEST_PACKAGE, false);
-        mInjector.getAppOpsHelper().setAppOpAllowed(OP_COARSE_LOCATION, TEST_PACKAGE, false);
-
-        when(mLocationManagerInternal.isProviderEnabledForUser(eq(GPS_PROVIDER), anyInt()))
-                .thenReturn(false);
-    }
-
-    private GnssStatusProvider createGnssStatusListenerHelper() {
-        return new GnssStatusProvider(mInjector);
-    }
-
-    private GnssMeasurementsProvider createGnssMeasurementsProvider() {
-        GnssMeasurementProviderNative
-                mockGnssMeasurementProviderNative = mock(GnssMeasurementProviderNative.class);
-        when(mockGnssMeasurementProviderNative.isMeasurementSupported()).thenReturn(
-                true);
-        return new GnssMeasurementsProvider(mInjector,  mockGnssMeasurementProviderNative);
-    }
-
-    private GnssNavigationMessageProvider createGnssNavigationMessageProvider() {
-        GnssNavigationMessageProviderNative mockGnssNavigationMessageProviderNative = mock(
-                GnssNavigationMessageProviderNative.class);
-        when(mockGnssNavigationMessageProviderNative.isNavigationMessageSupported()).thenReturn(
-                true);
-        return new GnssNavigationMessageProvider(mInjector,
-                mockGnssNavigationMessageProviderNative);
-    }
-
-    private GnssAntennaInfoProvider createGnssAntennaInfoProvider() {
-        GnssAntennaInfoProviderNative mockGnssAntenaInfoProviderNative = mock(
-                GnssAntennaInfoProviderNative.class);
-        when(mockGnssAntenaInfoProviderNative.isAntennaInfoSupported()).thenReturn(
-                true);
-        return new GnssAntennaInfoProvider(mInjector, mockGnssAntenaInfoProviderNative);
-    }
-
-    @Test
-    public void getGnssYearOfHardwareTest() {
-        final int gnssYearOfHardware = 2012;
-        when(mMockGnssSystemInfoProvider.getGnssYearOfHardware()).thenReturn(gnssYearOfHardware);
-        enableLocationPermissions();
-
-        assertThat(mGnssManagerService.getGnssYearOfHardware()).isEqualTo(gnssYearOfHardware);
-    }
-
-    @Test
-    public void getGnssHardwareModelNameTest() {
-        final String gnssHardwareModelName = "hardwarename";
-        when(mMockGnssSystemInfoProvider.getGnssHardwareModelName()).thenReturn(
-                gnssHardwareModelName);
-        enableLocationPermissions();
-
-        assertThat(mGnssManagerService.getGnssHardwareModelName()).isEqualTo(
-                gnssHardwareModelName);
-    }
-
-    @Test
-    public void getGnssCapabilitiesWithPermissionsTest() {
-        final long mGnssCapabilities = 23132L;
-        when(mMockGnssCapabilitiesProvider.getGnssCapabilities()).thenReturn(mGnssCapabilities);
-        enableLocationPermissions();
-
-        assertThat(mGnssManagerService.getGnssCapabilities()).isEqualTo(mGnssCapabilities);
-    }
-
-    @Test
-    public void registerGnssStatusCallbackWithoutPermissionsTest() throws RemoteException {
-        final int timeToFirstFix = 20000;
-        IGnssStatusListener mockGnssStatusListener = createMockGnssStatusListener();
-
-        disableLocationPermissions();
-
-        assertThrows(SecurityException.class, () -> mGnssManagerService
-                .registerGnssStatusCallback(
-                        mockGnssStatusListener, TEST_PACKAGE, "abcd123"));
-
-        mTestGnssStatusProvider.onFirstFix(timeToFirstFix);
-
-        verify(mockGnssStatusListener, after(FAILURE_TIMEOUT_MS).times(0)).onFirstFix(
-                timeToFirstFix);
-    }
-
-    @Test
-    public void registerGnssStatusCallbackWithPermissionsTest() throws RemoteException {
-        final int timeToFirstFix = 20000;
-        IGnssStatusListener mockGnssStatusListener = createMockGnssStatusListener();
-
-        enableLocationPermissions();
-
-        mGnssManagerService.registerGnssStatusCallback(
-                mockGnssStatusListener, TEST_PACKAGE, "abcd123");
-
-        mTestGnssStatusProvider.onFirstFix(timeToFirstFix);
-
-        verify(mockGnssStatusListener, timeout(TIMEOUT_MS).times(1)).onFirstFix(timeToFirstFix);
-    }
-
-    @Test
-    public void unregisterGnssStatusCallbackWithPermissionsTest() throws RemoteException {
-        final int timeToFirstFix = 20000;
-        IGnssStatusListener mockGnssStatusListener = createMockGnssStatusListener();
-
-        enableLocationPermissions();
-
-        mGnssManagerService.registerGnssStatusCallback(
-                mockGnssStatusListener, TEST_PACKAGE, "abcd123");
-
-        mGnssManagerService.unregisterGnssStatusCallback(mockGnssStatusListener);
-
-        mTestGnssStatusProvider.onFirstFix(timeToFirstFix);
-
-        verify(mockGnssStatusListener, after(FAILURE_TIMEOUT_MS).times(0)).onFirstFix(
-                timeToFirstFix);
-    }
-
-    @Test
-    public void addGnssMeasurementsListenerWithoutPermissionsTest() throws RemoteException {
-        IGnssMeasurementsListener mockGnssMeasurementsListener =
-                createMockGnssMeasurementsListener();
-        GnssMeasurementsEvent gnssMeasurementsEvent = new GnssMeasurementsEvent(new GnssClock(),
-                null);
-
-        disableLocationPermissions();
-
-        assertThrows(SecurityException.class,
-                () -> mGnssManagerService.addGnssMeasurementsListener(
-                        new GnssMeasurementRequest.Builder().build(), mockGnssMeasurementsListener,
-                        TEST_PACKAGE, null));
-
-        mTestGnssMeasurementsProvider.onMeasurementsAvailable(gnssMeasurementsEvent);
-        verify(mockGnssMeasurementsListener,
-                after(FAILURE_TIMEOUT_MS).times(0)).onGnssMeasurementsReceived(
-                gnssMeasurementsEvent);
-    }
-
-    @Test
-    public void addGnssMeasurementsListenerWithPermissionsTest() throws RemoteException {
-        IGnssMeasurementsListener mockGnssMeasurementsListener =
-                createMockGnssMeasurementsListener();
-        GnssMeasurementsEvent gnssMeasurementsEvent = new GnssMeasurementsEvent(new GnssClock(),
-                null);
-
-        enableLocationPermissions();
-
-        mGnssManagerService.addGnssMeasurementsListener(
-                new GnssMeasurementRequest.Builder().build(),
-                mockGnssMeasurementsListener,
-                TEST_PACKAGE, null);
-
-        mTestGnssMeasurementsProvider.onMeasurementsAvailable(gnssMeasurementsEvent);
-        verify(mockGnssMeasurementsListener,
-                timeout(TIMEOUT_MS).times(1)).onGnssMeasurementsReceived(
-                gnssMeasurementsEvent);
-    }
-
-    @Test
-    public void injectGnssMeasurementCorrectionsWithoutPermissionsTest() {
-        GnssMeasurementCorrections gnssMeasurementCorrections =
-                createDummyGnssMeasurementCorrections();
-
-        disableLocationPermissions();
-
-        assertThrows(SecurityException.class,
-                () -> mGnssManagerService.injectGnssMeasurementCorrections(
-                        gnssMeasurementCorrections));
-        verify(mMockGnssMeasurementCorrectionsProvider, times(0))
-                .injectGnssMeasurementCorrections(
-                        gnssMeasurementCorrections);
-    }
-
-    @Test
-    public void injectGnssMeasurementCorrectionsWithPermissionsTest() {
-        GnssMeasurementCorrections gnssMeasurementCorrections =
-                createDummyGnssMeasurementCorrections();
-
-        enableLocationPermissions();
-
-        mGnssManagerService.injectGnssMeasurementCorrections(
-                gnssMeasurementCorrections);
-        verify(mMockGnssMeasurementCorrectionsProvider, times(1))
-                .injectGnssMeasurementCorrections(
-                        gnssMeasurementCorrections);
-    }
-
-    @Test
-    public void removeGnssMeasurementsListenerWithoutPermissionsTest() throws RemoteException {
-        IGnssMeasurementsListener mockGnssMeasurementsListener =
-                createMockGnssMeasurementsListener();
-        GnssMeasurementsEvent gnssMeasurementsEvent = new GnssMeasurementsEvent(new GnssClock(),
-                null);
-
-        enableLocationPermissions();
-
-        mGnssManagerService.addGnssMeasurementsListener(
-                new GnssMeasurementRequest.Builder().build(),
-                mockGnssMeasurementsListener,
-                TEST_PACKAGE, null);
-
-        disableLocationPermissions();
-
-        mGnssManagerService.removeGnssMeasurementsListener(
-                mockGnssMeasurementsListener);
-
-        mTestGnssMeasurementsProvider.onMeasurementsAvailable(gnssMeasurementsEvent);
-        verify(mockGnssMeasurementsListener,
-                after(FAILURE_TIMEOUT_MS).times(0)).onGnssMeasurementsReceived(
-                gnssMeasurementsEvent);
-    }
-
-    @Test
-    public void removeGnssMeasurementsListenerWithPermissionsTest() throws RemoteException {
-        IGnssMeasurementsListener mockGnssMeasurementsListener =
-                createMockGnssMeasurementsListener();
-        GnssMeasurementsEvent gnssMeasurementsEvent = new GnssMeasurementsEvent(new GnssClock(),
-                null);
-
-        enableLocationPermissions();
-
-        mGnssManagerService.addGnssMeasurementsListener(
-                new GnssMeasurementRequest.Builder().build(),
-                mockGnssMeasurementsListener,
-                TEST_PACKAGE, null);
-
-        disableLocationPermissions();
-
-        mGnssManagerService.removeGnssMeasurementsListener(
-                mockGnssMeasurementsListener);
-
-        mTestGnssMeasurementsProvider.onMeasurementsAvailable(gnssMeasurementsEvent);
-        verify(mockGnssMeasurementsListener,
-                after(FAILURE_TIMEOUT_MS).times(0)).onGnssMeasurementsReceived(
-                gnssMeasurementsEvent);
-    }
-
-    @Test
-    public void addGnssAntennaInfoListenerWithoutPermissionsTest() throws RemoteException {
-        IGnssAntennaInfoListener mockGnssAntennaInfoListener =
-                createMockGnssAntennaInfoListener();
-        List<GnssAntennaInfo> gnssAntennaInfos = createDummyGnssAntennaInfos();
-
-        disableLocationPermissions();
-
-        assertThrows(SecurityException.class,
-                () -> mGnssManagerService.addGnssAntennaInfoListener(
-                        mockGnssAntennaInfoListener,
-                        TEST_PACKAGE, null));
-
-        mTestGnssAntennaInfoProvider.onGnssAntennaInfoAvailable(gnssAntennaInfos);
-        verify(mockGnssAntennaInfoListener, after(FAILURE_TIMEOUT_MS).times(0))
-                .onGnssAntennaInfoReceived(gnssAntennaInfos);
-    }
-
-    @Test
-    public void addGnssAntennaInfoListenerWithPermissionsTest() throws RemoteException {
-        IGnssAntennaInfoListener mockGnssAntennaInfoListener =
-                createMockGnssAntennaInfoListener();
-        List<GnssAntennaInfo> gnssAntennaInfos = createDummyGnssAntennaInfos();
-
-        enableLocationPermissions();
-
-        mGnssManagerService.addGnssAntennaInfoListener(mockGnssAntennaInfoListener,
-                TEST_PACKAGE, null);
-
-        mTestGnssAntennaInfoProvider.onGnssAntennaInfoAvailable(gnssAntennaInfos);
-        verify(mockGnssAntennaInfoListener, timeout(TIMEOUT_MS).times(1))
-                .onGnssAntennaInfoReceived(gnssAntennaInfos);
-    }
-
-    @Test
-    public void removeGnssAntennaInfoListenerWithoutPermissionsTest() throws RemoteException {
-        IGnssAntennaInfoListener mockGnssAntennaInfoListener =
-                createMockGnssAntennaInfoListener();
-        List<GnssAntennaInfo> gnssAntennaInfos = createDummyGnssAntennaInfos();
-
-        enableLocationPermissions();
-
-        mGnssManagerService.addGnssAntennaInfoListener(
-                mockGnssAntennaInfoListener,
-                TEST_PACKAGE, null);
-
-        disableLocationPermissions();
-
-        mGnssManagerService.removeGnssAntennaInfoListener(
-                mockGnssAntennaInfoListener);
-
-        mTestGnssAntennaInfoProvider.onGnssAntennaInfoAvailable(gnssAntennaInfos);
-        verify(mockGnssAntennaInfoListener, after(FAILURE_TIMEOUT_MS).times(0))
-                .onGnssAntennaInfoReceived(gnssAntennaInfos);
-    }
-
-    @Test
-    public void removeGnssAntennaInfoListenerWithPermissionsTest() throws RemoteException {
-        IGnssAntennaInfoListener mockGnssAntennaInfoListener =
-                createMockGnssAntennaInfoListener();
-        List<GnssAntennaInfo> gnssAntennaInfos = createDummyGnssAntennaInfos();
-
-        enableLocationPermissions();
-
-        mGnssManagerService.addGnssAntennaInfoListener(
-                mockGnssAntennaInfoListener,
-                TEST_PACKAGE, null);
-
-        mGnssManagerService.removeGnssAntennaInfoListener(
-                mockGnssAntennaInfoListener);
-
-        mTestGnssAntennaInfoProvider.onGnssAntennaInfoAvailable(gnssAntennaInfos);
-        verify(mockGnssAntennaInfoListener,
-                after(FAILURE_TIMEOUT_MS).times(0)).onGnssAntennaInfoReceived(
-                gnssAntennaInfos);
-    }
-
-    @Test
-    public void addGnssNavigationMessageListenerWithoutPermissionsTest() throws RemoteException {
-        IGnssNavigationMessageListener mockGnssNavigationMessageListener =
-                createMockGnssNavigationMessageListener();
-        GnssNavigationMessage gnssNavigationMessage = new GnssNavigationMessage();
-
-        disableLocationPermissions();
-
-        assertThrows(SecurityException.class,
-                () -> mGnssManagerService.addGnssNavigationMessageListener(
-                        mockGnssNavigationMessageListener, TEST_PACKAGE, null));
-
-        mTestGnssNavigationMessageProvider.onNavigationMessageAvailable(gnssNavigationMessage);
-
-        verify(mockGnssNavigationMessageListener,
-                after(FAILURE_TIMEOUT_MS).times(0)).onGnssNavigationMessageReceived(
-                gnssNavigationMessage);
-    }
-
-    @Test
-    public void addGnssNavigationMessageListenerWithPermissionsTest() throws RemoteException {
-        IGnssNavigationMessageListener mockGnssNavigationMessageListener =
-                createMockGnssNavigationMessageListener();
-        GnssNavigationMessage gnssNavigationMessage = new GnssNavigationMessage();
-
-        enableLocationPermissions();
-
-        mGnssManagerService.addGnssNavigationMessageListener(
-                mockGnssNavigationMessageListener, TEST_PACKAGE, null);
-
-        mTestGnssNavigationMessageProvider.onNavigationMessageAvailable(gnssNavigationMessage);
-
-        verify(mockGnssNavigationMessageListener,
-                timeout(TIMEOUT_MS).times(1)).onGnssNavigationMessageReceived(
-                gnssNavigationMessage);
-    }
-
-    @Test
-    public void removeGnssNavigationMessageListenerWithoutPermissionsTest() throws RemoteException {
-        IGnssNavigationMessageListener mockGnssNavigationMessageListener =
-                createMockGnssNavigationMessageListener();
-        GnssNavigationMessage gnssNavigationMessage = new GnssNavigationMessage();
-
-        enableLocationPermissions();
-
-        mGnssManagerService.addGnssNavigationMessageListener(
-                mockGnssNavigationMessageListener, TEST_PACKAGE, null);
-
-        disableLocationPermissions();
-
-        mGnssManagerService.removeGnssNavigationMessageListener(
-                mockGnssNavigationMessageListener);
-
-        mTestGnssNavigationMessageProvider.onNavigationMessageAvailable(gnssNavigationMessage);
-
-        verify(mockGnssNavigationMessageListener,
-                after(FAILURE_TIMEOUT_MS).times(0)).onGnssNavigationMessageReceived(
-                gnssNavigationMessage);
-    }
-
-    @Test
-    public void removeGnssNavigationMessageListenerWithPermissionsTest() throws RemoteException {
-        IGnssNavigationMessageListener mockGnssNavigationMessageListener =
-                createMockGnssNavigationMessageListener();
-        GnssNavigationMessage gnssNavigationMessage = new GnssNavigationMessage();
-
-        enableLocationPermissions();
-
-        mGnssManagerService.addGnssNavigationMessageListener(
-                mockGnssNavigationMessageListener, TEST_PACKAGE, null);
-
-        mGnssManagerService.removeGnssNavigationMessageListener(
-                mockGnssNavigationMessageListener);
-
-        mTestGnssNavigationMessageProvider.onNavigationMessageAvailable(gnssNavigationMessage);
-
-        verify(mockGnssNavigationMessageListener,
-                after(FAILURE_TIMEOUT_MS).times(0)).onGnssNavigationMessageReceived(
-                gnssNavigationMessage);
-    }
-
-    @Test
-    public void sendNiResponseWithPermissionsTest() throws RemoteException {
-        int notifId = 0;
-        int userResponse = 0;
-        enableLocationPermissions();
-
-        mGnssManagerService.sendNiResponse(notifId, userResponse);
-
-        verify(mNetInitiatedListener, times(1)).sendNiResponse(notifId, userResponse);
-    }
-}
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/gnss/hal/FakeGnssHal.java b/services/tests/mockingservicestests/src/com/android/server/location/gnss/hal/FakeGnssHal.java
new file mode 100644
index 0000000..675274b
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/location/gnss/hal/FakeGnssHal.java
@@ -0,0 +1,674 @@
+/*
+ * 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.location.gnss.hal;
+
+import static com.android.server.location.gnss.hal.GnssNative.GNSS_LOCATION_HAS_ALTITUDE;
+import static com.android.server.location.gnss.hal.GnssNative.GNSS_LOCATION_HAS_BEARING;
+import static com.android.server.location.gnss.hal.GnssNative.GNSS_LOCATION_HAS_BEARING_ACCURACY;
+import static com.android.server.location.gnss.hal.GnssNative.GNSS_LOCATION_HAS_HORIZONTAL_ACCURACY;
+import static com.android.server.location.gnss.hal.GnssNative.GNSS_LOCATION_HAS_LAT_LONG;
+import static com.android.server.location.gnss.hal.GnssNative.GNSS_LOCATION_HAS_SPEED;
+import static com.android.server.location.gnss.hal.GnssNative.GNSS_LOCATION_HAS_SPEED_ACCURACY;
+import static com.android.server.location.gnss.hal.GnssNative.GNSS_LOCATION_HAS_VERTICAL_ACCURACY;
+import static com.android.server.location.gnss.hal.GnssNative.GNSS_REALTIME_HAS_TIMESTAMP_NS;
+import static com.android.server.location.gnss.hal.GnssNative.GNSS_REALTIME_HAS_TIME_UNCERTAINTY_NS;
+import static com.android.server.location.gnss.hal.GnssNative.GeofenceCallbacks.GEOFENCE_STATUS_ERROR_ID_EXISTS;
+import static com.android.server.location.gnss.hal.GnssNative.GeofenceCallbacks.GEOFENCE_STATUS_ERROR_ID_UNKNOWN;
+import static com.android.server.location.gnss.hal.GnssNative.GeofenceCallbacks.GEOFENCE_STATUS_OPERATION_SUCCESS;
+import static com.android.server.location.gnss.hal.GnssNative.GeofenceCallbacks.GEOFENCE_TRANSITION_ENTERED;
+import static com.android.server.location.gnss.hal.GnssNative.GeofenceCallbacks.GEOFENCE_TRANSITION_EXITED;
+
+import android.annotation.Nullable;
+import android.location.GnssAntennaInfo;
+import android.location.GnssMeasurementCorrections;
+import android.location.GnssMeasurementsEvent;
+import android.location.GnssNavigationMessage;
+import android.location.Location;
+
+import com.android.server.location.gnss.GnssPowerStats;
+import com.android.server.location.gnss.hal.GnssNative.GnssLocationFlags;
+import com.android.server.location.gnss.hal.GnssNative.GnssRealtimeFlags;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Fake GNSS HAL for testing.
+ */
+public final class FakeGnssHal extends GnssNative.GnssHal {
+
+    public static class GnssHalPositionMode {
+
+        public final int Mode;
+        public final int Recurrence;
+        public final int MinInterval;
+        public final int PreferredAccuracy;
+        public final int PreferredTime;
+        public final boolean LowPowerMode;
+
+        GnssHalPositionMode() {
+            Mode = 0;
+            Recurrence = 0;
+            MinInterval = 0;
+            PreferredAccuracy = 0;
+            PreferredTime = 0;
+            LowPowerMode = false;
+        }
+
+        public GnssHalPositionMode(int mode, int recurrence, int minInterval, int preferredAccuracy,
+                int preferredTime, boolean lowPowerMode) {
+            Mode = mode;
+            Recurrence = recurrence;
+            MinInterval = minInterval;
+            PreferredAccuracy = preferredAccuracy;
+            PreferredTime = preferredTime;
+            LowPowerMode = lowPowerMode;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (o == null || getClass() != o.getClass()) {
+                return false;
+            }
+
+            GnssHalPositionMode that = (GnssHalPositionMode) o;
+            return Mode == that.Mode
+                    && Recurrence == that.Recurrence
+                    && MinInterval == that.MinInterval
+                    && PreferredAccuracy == that.PreferredAccuracy
+                    && PreferredTime == that.PreferredTime
+                    && LowPowerMode == that.LowPowerMode;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(Recurrence, MinInterval);
+        }
+    }
+
+    public static class GnssHalBatchingMode {
+
+        public final long PeriodNanos;
+        public final boolean WakeOnFifoFull;
+
+        GnssHalBatchingMode() {
+            PeriodNanos = 0;
+            WakeOnFifoFull = false;
+        }
+
+        public GnssHalBatchingMode(long periodNanos, boolean wakeOnFifoFull) {
+            PeriodNanos = periodNanos;
+            WakeOnFifoFull = wakeOnFifoFull;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (o == null || getClass() != o.getClass()) {
+                return false;
+            }
+
+            GnssHalBatchingMode that = (GnssHalBatchingMode) o;
+            return PeriodNanos == that.PeriodNanos
+                    && WakeOnFifoFull == that.WakeOnFifoFull;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(PeriodNanos, WakeOnFifoFull);
+        }
+    }
+
+    public static class GnssHalInjectedTime {
+
+        public final long Time;
+        public final long TimeReference;
+        public final int Uncertainty;
+
+        public GnssHalInjectedTime(long time, long timeReference, int uncertainty) {
+            Time = time;
+            TimeReference = timeReference;
+            Uncertainty = uncertainty;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (o == null || getClass() != o.getClass()) {
+                return false;
+            }
+
+            GnssHalInjectedTime that = (GnssHalInjectedTime) o;
+            return Time == that.Time
+                    && TimeReference == that.TimeReference
+                    && Uncertainty == that.Uncertainty;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(Time);
+        }
+    }
+
+    public static class GnssHalGeofence {
+
+        public final int GeofenceId;
+        public final Location Center;
+        public final double Radius;
+        public int LastTransition;
+        public int MonitorTransitions;
+        public final int NotificationResponsiveness;
+        public final int UnknownTimer;
+        public boolean Paused;
+
+        public GnssHalGeofence(int geofenceId, double latitude, double longitude, double radius,
+                int lastTransition, int monitorTransitions, int notificationResponsiveness,
+                int unknownTimer, boolean paused) {
+            GeofenceId = geofenceId;
+            Center = new Location("");
+            Center.setLatitude(latitude);
+            Center.setLongitude(longitude);
+            Radius = radius;
+            LastTransition = lastTransition;
+            MonitorTransitions = monitorTransitions;
+            NotificationResponsiveness = notificationResponsiveness;
+            UnknownTimer = unknownTimer;
+            Paused = paused;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
+                return true;
+            }
+            if (o == null || getClass() != o.getClass()) {
+                return false;
+            }
+
+            GnssHalGeofence that = (GnssHalGeofence) o;
+            return GeofenceId == that.GeofenceId
+                    && Double.compare(that.Radius, Radius) == 0
+                    && LastTransition == that.LastTransition
+                    && MonitorTransitions == that.MonitorTransitions
+                    && NotificationResponsiveness == that.NotificationResponsiveness
+                    && UnknownTimer == that.UnknownTimer
+                    && Paused == that.Paused
+                    && Center.equals(that.Center);
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(GeofenceId);
+        }
+    }
+
+    private static class HalState {
+        private boolean mStarted = false;
+        private boolean mBatchingStarted = false;
+        private boolean mNavigationMessagesStarted = false;
+        private boolean mAntennaInfoListeningStarted = false;
+        private boolean mMeasurementCollectionStarted = false;
+        private boolean mMeasurementCollectionFullTracking = false;
+        private GnssHalPositionMode mPositionMode = new GnssHalPositionMode();
+        private GnssHalBatchingMode mBatchingMode = new GnssHalBatchingMode();
+        private final ArrayList<Location> mBatchedLocations = new ArrayList<>();
+        private Location mInjectedLocation = null;
+        private Location mInjectedBestLocation = null;
+        private GnssHalInjectedTime mInjectedTime = null;
+        private GnssMeasurementCorrections mInjectedMeasurementCorrections = null;
+        private final HashMap<Integer, GnssHalGeofence> mGeofences = new HashMap<>();
+        private GnssPowerStats mPowerStats = new GnssPowerStats(0, 0, 0, 0, 0, 0, 0, 0,
+                new double[0]);
+    }
+
+    private @Nullable GnssNative mGnssNative;
+    private HalState mState = new HalState();
+
+    private boolean mIsNavigationMessageCollectionSupported = true;
+    private boolean mIsAntennaInfoListeningSupported = true;
+    private boolean mIsMeasurementSupported = true;
+    private boolean mIsMeasurementCorrectionsSupported = true;
+    private int mBatchSize = 0;
+    private boolean mIsGeofencingSupported = true;
+    private boolean mIsVisibilityControlSupported = true;
+
+    public FakeGnssHal() {}
+
+    public void restartHal() {
+        mState = new HalState();
+        Objects.requireNonNull(mGnssNative).restartHal();
+    }
+
+    public void setIsNavigationMessageCollectionSupported(boolean supported) {
+        mIsNavigationMessageCollectionSupported = supported;
+    }
+
+    public void setIsAntennaInfoListeningSupported(boolean supported) {
+        mIsAntennaInfoListeningSupported = supported;
+    }
+
+    public void setIsMeasurementSupported(boolean supported) {
+        mIsMeasurementSupported = supported;
+    }
+
+    public void setIsMeasurementCorrectionsSupported(boolean supported) {
+        mIsMeasurementCorrectionsSupported = supported;
+    }
+
+    public void setBatchSize(int batchSize) {
+        mBatchSize = batchSize;
+    }
+
+    public void setIsGeofencingSupported(boolean supported) {
+        mIsGeofencingSupported = supported;
+    }
+
+    public void setPowerStats(GnssPowerStats powerStats) {
+        mState.mPowerStats = powerStats;
+    }
+
+    public void setIsVisibilityControlSupported(boolean supported) {
+        mIsVisibilityControlSupported = supported;
+    }
+
+    public GnssHalPositionMode getPositionMode() {
+        return mState.mPositionMode;
+    }
+
+    public void reportLocation(Location location) {
+        if (mState.mStarted) {
+            Objects.requireNonNull(mGnssNative).reportLocation(true, location);
+        }
+        if (mState.mBatchingStarted) {
+            mState.mBatchedLocations.add(location);
+            if (mState.mBatchedLocations.size() >= mBatchSize) {
+                if (mState.mBatchingMode.WakeOnFifoFull) {
+                    flushBatch();
+                } else {
+                    mState.mBatchedLocations.remove(0);
+                }
+            }
+        }
+        for (GnssHalGeofence geofence : mState.mGeofences.values()) {
+            if (!geofence.Paused) {
+                if (geofence.Center.distanceTo(location) > geofence.Radius) {
+                    if (geofence.LastTransition != GEOFENCE_TRANSITION_EXITED) {
+                        geofence.LastTransition = GEOFENCE_TRANSITION_EXITED;
+                        if ((geofence.MonitorTransitions & GEOFENCE_TRANSITION_EXITED) != 0) {
+                            Objects.requireNonNull(mGnssNative).reportGeofenceTransition(
+                                    geofence.GeofenceId, location, GEOFENCE_TRANSITION_EXITED,
+                                    location.getTime());
+                        }
+                    }
+                } else {
+                    if (geofence.LastTransition != GEOFENCE_TRANSITION_ENTERED) {
+                        geofence.LastTransition = GEOFENCE_TRANSITION_ENTERED;
+                        if ((geofence.MonitorTransitions & GEOFENCE_TRANSITION_ENTERED) != 0) {
+                            Objects.requireNonNull(mGnssNative).reportGeofenceTransition(
+                                    geofence.GeofenceId, location, GEOFENCE_TRANSITION_ENTERED,
+                                    location.getTime());
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    public void reportNavigationMessage(GnssNavigationMessage message) {
+        if (mState.mNavigationMessagesStarted) {
+            Objects.requireNonNull(mGnssNative).reportNavigationMessage(message);
+        }
+    }
+
+    public void reportAntennaInfo(List<GnssAntennaInfo> antennaInfos) {
+        if (mState.mAntennaInfoListeningStarted) {
+            Objects.requireNonNull(mGnssNative).reportAntennaInfo(antennaInfos);
+        }
+    }
+
+    public boolean isMeasurementCollectionFullTracking() {
+        return mState.mMeasurementCollectionFullTracking;
+    }
+
+    public void reportMeasurement(GnssMeasurementsEvent event) {
+        if (mState.mMeasurementCollectionStarted) {
+            Objects.requireNonNull(mGnssNative).reportMeasurementData(event);
+        }
+    }
+
+    public GnssHalInjectedTime getLastInjectedTime() {
+        return mState.mInjectedTime;
+    }
+
+    public GnssMeasurementCorrections getLastInjectedCorrections() {
+        return mState.mInjectedMeasurementCorrections;
+    }
+
+    public Collection<GnssHalGeofence> getGeofences() {
+        return mState.mGeofences.values();
+    }
+
+    @Override
+    protected void classInitOnce() {}
+
+    @Override
+    protected boolean isSupported() {
+        return true;
+    }
+
+    @Override
+    protected void initOnce(GnssNative gnssNative, boolean reinitializeGnssServiceHandle) {
+        mGnssNative = Objects.requireNonNull(gnssNative);
+    }
+
+    @Override
+    protected boolean init() {
+        return true;
+    }
+
+    @Override
+    protected void cleanup() {}
+
+    @Override
+    protected boolean start() {
+        mState.mStarted = true;
+        return true;
+    }
+
+    @Override
+    protected boolean stop() {
+        mState.mStarted = false;
+        return true;
+    }
+
+    @Override
+    protected boolean setPositionMode(int mode, int recurrence, int minInterval,
+            int preferredAccuracy, int preferredTime, boolean lowPowerMode) {
+        mState.mPositionMode = new GnssHalPositionMode(mode, recurrence, minInterval,
+                preferredAccuracy, preferredTime, lowPowerMode);
+        return true;
+    }
+
+    @Override
+    protected String getInternalState() {
+        return "DebugState";
+    }
+
+    @Override
+    protected void deleteAidingData(int flags) {}
+
+    @Override
+    protected int readNmea(byte[] buffer, int bufferSize) {
+        return 0;
+    }
+
+    @Override
+    protected void injectLocation(double latitude, double longitude, float accuracy) {
+        mState.mInjectedLocation = new Location("injected");
+        mState.mInjectedLocation.setLatitude(latitude);
+        mState.mInjectedLocation.setLongitude(longitude);
+        mState.mInjectedLocation.setAccuracy(accuracy);
+    }
+
+    @Override
+    protected void injectBestLocation(@GnssLocationFlags int gnssLocationFlags, double latitude,
+            double longitude, double altitude, float speed, float bearing, float horizontalAccuracy,
+            float verticalAccuracy, float speedAccuracy, float bearingAccuracy, long timestamp,
+            @GnssRealtimeFlags int elapsedRealtimeFlags, long elapsedRealtimeNanos,
+            double elapsedRealtimeUncertaintyNanos) {
+        mState.mInjectedBestLocation = new Location("injectedBest");
+        if ((gnssLocationFlags & GNSS_LOCATION_HAS_LAT_LONG) != 0) {
+            mState.mInjectedBestLocation.setLatitude(latitude);
+            mState.mInjectedBestLocation.setLongitude(longitude);
+        }
+        if ((gnssLocationFlags & GNSS_LOCATION_HAS_ALTITUDE) != 0) {
+            mState.mInjectedBestLocation.setAltitude(altitude);
+        }
+        if ((gnssLocationFlags & GNSS_LOCATION_HAS_SPEED) != 0) {
+            mState.mInjectedBestLocation.setSpeed(speed);
+        }
+        if ((gnssLocationFlags & GNSS_LOCATION_HAS_BEARING) != 0) {
+            mState.mInjectedBestLocation.setBearing(bearing);
+        }
+        if ((gnssLocationFlags & GNSS_LOCATION_HAS_HORIZONTAL_ACCURACY) != 0) {
+            mState.mInjectedBestLocation.setAccuracy(horizontalAccuracy);
+        }
+        if ((gnssLocationFlags & GNSS_LOCATION_HAS_VERTICAL_ACCURACY) != 0) {
+            mState.mInjectedBestLocation.setVerticalAccuracyMeters(verticalAccuracy);
+        }
+        if ((gnssLocationFlags & GNSS_LOCATION_HAS_SPEED_ACCURACY) != 0) {
+            mState.mInjectedBestLocation.setSpeedAccuracyMetersPerSecond(speedAccuracy);
+        }
+        if ((gnssLocationFlags & GNSS_LOCATION_HAS_BEARING_ACCURACY) != 0) {
+            mState.mInjectedBestLocation.setBearingAccuracyDegrees(bearingAccuracy);
+        }
+        mState.mInjectedBestLocation.setTime(timestamp);
+        if ((elapsedRealtimeFlags & GNSS_REALTIME_HAS_TIMESTAMP_NS) != 0) {
+            mState.mInjectedBestLocation.setElapsedRealtimeNanos(elapsedRealtimeNanos);
+        }
+        if ((elapsedRealtimeFlags & GNSS_REALTIME_HAS_TIME_UNCERTAINTY_NS) != 0) {
+            mState.mInjectedBestLocation.setElapsedRealtimeUncertaintyNanos(
+                    elapsedRealtimeUncertaintyNanos);
+        }
+    }
+
+    @Override
+    protected void injectTime(long time, long timeReference, int uncertainty) {
+        mState.mInjectedTime = new GnssHalInjectedTime(time, timeReference, uncertainty);
+    }
+
+    @Override
+    protected boolean isNavigationMessageCollectionSupported() {
+        return mIsNavigationMessageCollectionSupported;
+    }
+
+    @Override
+    protected boolean startNavigationMessageCollection() {
+        mState.mNavigationMessagesStarted = true;
+        return true;
+    }
+
+    @Override
+    protected boolean stopNavigationMessageCollection() {
+        mState.mNavigationMessagesStarted = false;
+        return true;
+    }
+
+    @Override
+    protected boolean isAntennaInfoListeningSupported() {
+        return mIsAntennaInfoListeningSupported;
+    }
+
+    @Override
+    protected boolean startAntennaInfoListening() {
+        mState.mAntennaInfoListeningStarted = true;
+        return true;
+    }
+
+    @Override
+    protected boolean stopAntennaInfoListening() {
+        mState.mAntennaInfoListeningStarted = false;
+        return true;
+    }
+
+    @Override
+    protected boolean isMeasurementSupported() {
+        return mIsMeasurementSupported;
+    }
+
+    @Override
+    protected boolean startMeasurementCollection(boolean enableFullTracking) {
+        mState.mMeasurementCollectionStarted = true;
+        mState.mMeasurementCollectionFullTracking = enableFullTracking;
+        return true;
+    }
+
+    @Override
+    protected boolean stopMeasurementCollection() {
+        mState.mMeasurementCollectionStarted = false;
+        mState.mMeasurementCollectionFullTracking = false;
+        return true;
+    }
+
+    @Override
+    protected boolean isMeasurementCorrectionsSupported() {
+        return mIsMeasurementCorrectionsSupported;
+    }
+
+    @Override
+    protected boolean injectMeasurementCorrections(GnssMeasurementCorrections corrections) {
+        mState.mInjectedMeasurementCorrections = corrections;
+        return true;
+    }
+
+    @Override
+    protected int getBatchSize() {
+        return mBatchSize;
+    }
+
+    @Override
+    protected boolean initBatching() {
+        return true;
+    }
+
+    @Override
+    protected void cleanupBatching() {}
+
+    @Override
+    protected boolean startBatch(long periodNanos, boolean wakeOnFifoFull) {
+        mState.mBatchingStarted = true;
+        mState.mBatchingMode = new GnssHalBatchingMode(periodNanos, wakeOnFifoFull);
+        return true;
+    }
+
+    @Override
+    protected void flushBatch() {
+        Location[] locations = mState.mBatchedLocations.toArray(new Location[0]);
+        mState.mBatchedLocations.clear();
+        Objects.requireNonNull(mGnssNative).reportLocationBatch(locations);
+    }
+
+    @Override
+    protected void stopBatch() {
+        mState.mBatchingStarted = false;
+        mState.mBatchingMode = new GnssHalBatchingMode();
+        mState.mBatchedLocations.clear();
+    }
+
+    @Override
+    protected boolean isGeofencingSupported() {
+        return mIsGeofencingSupported;
+    }
+
+    @Override
+    protected boolean addGeofence(int geofenceId, double latitude, double longitude, double radius,
+            int lastTransition, int monitorTransitions, int notificationResponsiveness,
+            int unknownTimer) {
+        if (mState.mGeofences.containsKey(geofenceId)) {
+            Objects.requireNonNull(mGnssNative).reportGeofenceAddStatus(geofenceId,
+                    GEOFENCE_STATUS_ERROR_ID_EXISTS);
+        } else {
+            mState.mGeofences.put(geofenceId,
+                    new GnssHalGeofence(geofenceId, latitude, longitude, radius, lastTransition,
+                            monitorTransitions, notificationResponsiveness, unknownTimer, false));
+            Objects.requireNonNull(mGnssNative).reportGeofenceAddStatus(geofenceId,
+                    GEOFENCE_STATUS_OPERATION_SUCCESS);
+        }
+        return true;
+    }
+
+    @Override
+    protected boolean resumeGeofence(int geofenceId, int monitorTransitions) {
+        GnssHalGeofence geofence = mState.mGeofences.get(geofenceId);
+        if (geofence != null) {
+            geofence.Paused = false;
+            geofence.MonitorTransitions = monitorTransitions;
+            Objects.requireNonNull(mGnssNative).reportGeofenceAddStatus(geofenceId,
+                    GEOFENCE_STATUS_OPERATION_SUCCESS);
+        } else {
+            Objects.requireNonNull(mGnssNative).reportGeofenceAddStatus(geofenceId,
+                    GEOFENCE_STATUS_ERROR_ID_UNKNOWN);
+        }
+        return true;
+    }
+
+    @Override
+    protected boolean pauseGeofence(int geofenceId) {
+        GnssHalGeofence geofence = mState.mGeofences.get(geofenceId);
+        if (geofence != null) {
+            geofence.Paused = true;
+            Objects.requireNonNull(mGnssNative).reportGeofenceAddStatus(geofenceId,
+                    GEOFENCE_STATUS_OPERATION_SUCCESS);
+        } else {
+            Objects.requireNonNull(mGnssNative).reportGeofenceAddStatus(geofenceId,
+                    GEOFENCE_STATUS_ERROR_ID_UNKNOWN);
+        }
+        return true;
+    }
+
+    @Override
+    protected boolean removeGeofence(int geofenceId) {
+        if (mState.mGeofences.remove(geofenceId) != null) {
+            Objects.requireNonNull(mGnssNative).reportGeofenceRemoveStatus(geofenceId,
+                    GEOFENCE_STATUS_OPERATION_SUCCESS);
+        } else {
+            Objects.requireNonNull(mGnssNative).reportGeofenceRemoveStatus(geofenceId,
+                    GEOFENCE_STATUS_ERROR_ID_UNKNOWN);
+        }
+        return true;
+    }
+
+    @Override
+    protected boolean isGnssVisibilityControlSupported() {
+        return mIsVisibilityControlSupported;
+    }
+
+    @Override
+    protected void sendNiResponse(int notificationId, int userResponse) {}
+
+    @Override
+    protected void requestPowerStats() {
+        Objects.requireNonNull(mGnssNative).reportGnssPowerStats(mState.mPowerStats);
+    }
+
+    @Override
+    protected void setAgpsServer(int type, String hostname, int port) {}
+
+    @Override
+    protected void setAgpsSetId(int type, String setId) {}
+
+    @Override
+    protected void setAgpsReferenceLocationCellId(int type, int mcc, int mnc, int lac, int cid) {}
+
+    @Override
+    protected boolean isPsdsSupported() {
+        return true;
+    }
+
+    @Override
+    protected void injectPsdsData(byte[] data, int length, int psdsType) {}
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeEmergencyHelper.java b/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeEmergencyHelper.java
new file mode 100644
index 0000000..2cf57da
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/location/injector/FakeEmergencyHelper.java
@@ -0,0 +1,36 @@
+/*
+ * 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.location.injector;
+
+/**
+ * Version of EmergencyHelper for testing.
+ */
+public class FakeEmergencyHelper extends EmergencyHelper {
+
+    private boolean mInEmergency;
+
+    public FakeEmergencyHelper() {}
+
+    public void setInEmergency(boolean inEmergency) {
+        mInEmergency = inEmergency;
+    }
+
+    @Override
+    public boolean isInEmergency(long extensionTimeMs) {
+        return mInEmergency;
+    }
+}
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/injector/TestInjector.java b/services/tests/mockingservicestests/src/com/android/server/location/injector/TestInjector.java
index f3c31c2..8e5b16e 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/injector/TestInjector.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/injector/TestInjector.java
@@ -28,6 +28,7 @@
     private final FakeLocationPowerSaveModeHelper mLocationPowerSaveModeHelper;
     private final FakeScreenInteractiveHelper mScreenInteractiveHelper;
     private final LocationAttributionHelper mLocationAttributionHelper;
+    private final FakeEmergencyHelper mEmergencyHelper;
     private final LocationUsageLogger mLocationUsageLogger;
 
     public TestInjector() {
@@ -41,6 +42,7 @@
         mLocationPowerSaveModeHelper = new FakeLocationPowerSaveModeHelper(mLocationEventLog);
         mScreenInteractiveHelper = new FakeScreenInteractiveHelper();
         mLocationAttributionHelper = new LocationAttributionHelper(mAppOpsHelper);
+        mEmergencyHelper = new FakeEmergencyHelper();
         mLocationUsageLogger = new LocationUsageLogger();
     }
 
@@ -90,6 +92,11 @@
     }
 
     @Override
+    public EmergencyHelper getEmergencyHelper() {
+        return mEmergencyHelper;
+    }
+
+    @Override
     public LocationUsageLogger getLocationUsageLogger() {
         return mLocationUsageLogger;
     }
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
index 63b36fc..df7a445 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/provider/LocationProviderManagerTest.java
@@ -66,6 +66,7 @@
 import android.location.LocationManagerInternal.ProviderEnabledListener;
 import android.location.LocationRequest;
 import android.location.LocationResult;
+import android.location.ProviderProperties;
 import android.location.util.identity.CallerIdentity;
 import android.os.Bundle;
 import android.os.ICancellationSignal;
@@ -80,7 +81,6 @@
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
-import com.android.internal.location.ProviderProperties;
 import com.android.internal.location.ProviderRequest;
 import com.android.server.FgThread;
 import com.android.server.LocalServices;
@@ -1016,8 +1016,7 @@
         private final ArrayList<Runnable> mFlushCallbacks = new ArrayList<>();
 
         TestProvider(ProviderProperties properties, CallerIdentity identity) {
-            super(DIRECT_EXECUTOR, identity);
-            setProperties(properties);
+            super(DIRECT_EXECUTOR, identity, properties);
         }
 
         public void setProviderAllowed(boolean allowed) {
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/provider/MockableLocationProviderTest.java b/services/tests/mockingservicestests/src/com/android/server/location/provider/MockableLocationProviderTest.java
index bcf65d3..daa8a22 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/provider/MockableLocationProviderTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/provider/MockableLocationProviderTest.java
@@ -15,8 +15,6 @@
  */
 package com.android.server.location.provider;
 
-import static androidx.test.ext.truth.location.LocationSubject.assertThat;
-
 import static com.android.internal.location.ProviderRequest.EMPTY_REQUEST;
 
 import static com.google.common.truth.Truth.assertThat;
@@ -29,13 +27,13 @@
 import android.location.Criteria;
 import android.location.Location;
 import android.location.LocationResult;
+import android.location.ProviderProperties;
 import android.location.util.identity.CallerIdentity;
 import android.platform.test.annotations.Presubmit;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
-import com.android.internal.location.ProviderProperties;
 import com.android.internal.location.ProviderRequest;
 import com.android.server.location.test.FakeProvider;
 import com.android.server.location.test.ProviderListenerCapture;
diff --git a/services/tests/mockingservicestests/src/com/android/server/location/test/FakeProvider.java b/services/tests/mockingservicestests/src/com/android/server/location/test/FakeProvider.java
index 9266d6f..1eb0386 100644
--- a/services/tests/mockingservicestests/src/com/android/server/location/test/FakeProvider.java
+++ b/services/tests/mockingservicestests/src/com/android/server/location/test/FakeProvider.java
@@ -40,7 +40,7 @@
     private final FakeProviderInterface mFakeInterface;
 
     public FakeProvider(FakeProviderInterface fakeInterface) {
-        super(Runnable::run);
+        super(Runnable::run, null, null);
         mFakeInterface = fakeInterface;
     }
 
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
index 015eead..c522541 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
@@ -36,7 +36,6 @@
 import android.os.UserHandle
 import android.os.UserManager
 import android.os.incremental.IncrementalManager
-import android.permission.IPermissionManager
 import android.util.ArrayMap
 import android.util.DisplayMetrics
 import android.util.EventLog
@@ -184,7 +183,6 @@
         val dexManager: DexManager = mock()
         val installer: Installer = mock()
         val displayMetrics: DisplayMetrics = mock()
-        val permissionManager: IPermissionManager = mock()
     }
 
     companion object {
@@ -246,7 +244,6 @@
         whenever(mocks.injector.permissionManagerServiceInternal) {
             mocks.permissionManagerInternal
         }
-        whenever(mocks.injector.permissionManagerService).thenReturn(mocks.permissionManager)
         whenever(mocks.injector.incrementalManager).thenReturn(mocks.incrementalManager)
         whenever(mocks.injector.compatibility).thenReturn(mocks.platformCompat)
         whenever(mocks.injector.settings).thenReturn(mocks.settings)
diff --git a/services/tests/servicestests/src/com/android/internal/location/timezone/LocationTimeZoneEventTest.java b/services/tests/servicestests/src/com/android/internal/location/timezone/LocationTimeZoneEventTest.java
deleted file mode 100644
index 84b886e..0000000
--- a/services/tests/servicestests/src/com/android/internal/location/timezone/LocationTimeZoneEventTest.java
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- * 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.internal.location.timezone;
-
-import static com.android.internal.location.timezone.ParcelableTestSupport.assertRoundTripParcelable;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
-
-import static java.util.Collections.singletonList;
-
-import org.junit.Test;
-
-import java.util.List;
-
-public class LocationTimeZoneEventTest {
-
-    private static final long ARBITRARY_ELAPSED_REALTIME_MILLIS = 9999;
-
-    private static final List<String> ARBITRARY_TIME_ZONE_IDS = singletonList("Europe/London");
-
-    @Test(expected = RuntimeException.class)
-    public void testSetInvalidEventType() {
-        new LocationTimeZoneEvent.Builder().setEventType(Integer.MAX_VALUE);
-    }
-
-    @Test(expected = RuntimeException.class)
-    public void testBuildUnsetEventType() {
-        new LocationTimeZoneEvent.Builder()
-                .setTimeZoneIds(ARBITRARY_TIME_ZONE_IDS)
-                .setElapsedRealtimeMillis(ARBITRARY_ELAPSED_REALTIME_MILLIS)
-                .build();
-    }
-
-    @Test(expected = RuntimeException.class)
-    public void testInvalidTimeZoneIds() {
-        new LocationTimeZoneEvent.Builder()
-                .setEventType(LocationTimeZoneEvent.EVENT_TYPE_UNCERTAIN)
-                .setTimeZoneIds(ARBITRARY_TIME_ZONE_IDS)
-                .setElapsedRealtimeMillis(ARBITRARY_ELAPSED_REALTIME_MILLIS)
-                .build();
-    }
-
-    @Test
-    public void testEquals() {
-        LocationTimeZoneEvent.Builder builder1 = new LocationTimeZoneEvent.Builder()
-                .setEventType(LocationTimeZoneEvent.EVENT_TYPE_UNCERTAIN)
-                .setElapsedRealtimeMillis(ARBITRARY_ELAPSED_REALTIME_MILLIS);
-        {
-            LocationTimeZoneEvent one = builder1.build();
-            assertEquals(one, one);
-        }
-
-        LocationTimeZoneEvent.Builder builder2 = new LocationTimeZoneEvent.Builder()
-                .setEventType(LocationTimeZoneEvent.EVENT_TYPE_UNCERTAIN)
-                .setElapsedRealtimeMillis(ARBITRARY_ELAPSED_REALTIME_MILLIS);
-        {
-            LocationTimeZoneEvent one = builder1.build();
-            LocationTimeZoneEvent two = builder2.build();
-            assertEquals(one, two);
-            assertEquals(two, one);
-        }
-
-        builder1.setElapsedRealtimeMillis(ARBITRARY_ELAPSED_REALTIME_MILLIS + 1);
-        {
-            LocationTimeZoneEvent one = builder1.build();
-            LocationTimeZoneEvent two = builder2.build();
-            assertNotEquals(one, two);
-            assertNotEquals(two, one);
-        }
-
-        builder2.setElapsedRealtimeMillis(ARBITRARY_ELAPSED_REALTIME_MILLIS + 1);
-        {
-            LocationTimeZoneEvent one = builder1.build();
-            LocationTimeZoneEvent two = builder2.build();
-            assertEquals(one, two);
-            assertEquals(two, one);
-        }
-
-        builder2.setEventType(LocationTimeZoneEvent.EVENT_TYPE_SUCCESS);
-        {
-            LocationTimeZoneEvent one = builder1.build();
-            LocationTimeZoneEvent two = builder2.build();
-            assertNotEquals(one, two);
-            assertNotEquals(two, one);
-        }
-
-        builder1.setEventType(LocationTimeZoneEvent.EVENT_TYPE_SUCCESS);
-        {
-            LocationTimeZoneEvent one = builder1.build();
-            LocationTimeZoneEvent two = builder2.build();
-            assertEquals(one, two);
-            assertEquals(two, one);
-        }
-
-        builder2.setTimeZoneIds(ARBITRARY_TIME_ZONE_IDS);
-        {
-            LocationTimeZoneEvent one = builder1.build();
-            LocationTimeZoneEvent two = builder2.build();
-            assertNotEquals(one, two);
-            assertNotEquals(two, one);
-        }
-
-        builder1.setTimeZoneIds(ARBITRARY_TIME_ZONE_IDS);
-        {
-            LocationTimeZoneEvent one = builder1.build();
-            LocationTimeZoneEvent two = builder2.build();
-            assertEquals(one, two);
-            assertEquals(two, one);
-        }
-    }
-
-    @Test
-    public void testParcelable() {
-        LocationTimeZoneEvent.Builder builder = new LocationTimeZoneEvent.Builder()
-                .setEventType(LocationTimeZoneEvent.EVENT_TYPE_PERMANENT_FAILURE)
-                .setElapsedRealtimeMillis(ARBITRARY_ELAPSED_REALTIME_MILLIS);
-        assertRoundTripParcelable(builder.build());
-
-        builder.setEventType(LocationTimeZoneEvent.EVENT_TYPE_SUCCESS)
-                .setTimeZoneIds(ARBITRARY_TIME_ZONE_IDS);
-        assertRoundTripParcelable(builder.build());
-    }
-}
diff --git a/services/tests/servicestests/src/com/android/internal/location/timezone/LocationTimeZoneProviderRequestTest.java b/services/tests/servicestests/src/com/android/internal/location/timezone/LocationTimeZoneProviderRequestTest.java
deleted file mode 100644
index 95daa36..0000000
--- a/services/tests/servicestests/src/com/android/internal/location/timezone/LocationTimeZoneProviderRequestTest.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * 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.internal.location.timezone;
-
-import static com.android.internal.location.timezone.ParcelableTestSupport.assertRoundTripParcelable;
-
-import org.junit.Test;
-
-import java.time.Duration;
-
-public class LocationTimeZoneProviderRequestTest {
-
-    @Test
-    public void testParcelable() {
-        LocationTimeZoneProviderRequest.Builder builder =
-                new LocationTimeZoneProviderRequest.Builder()
-                        .setReportLocationTimeZone(false);
-        assertRoundTripParcelable(builder.build());
-
-        builder.setReportLocationTimeZone(true)
-                .setInitializationTimeoutMillis(Duration.ofMinutes(5).toMillis());
-
-        assertRoundTripParcelable(builder.build());
-    }
-}
diff --git a/services/tests/servicestests/src/com/android/internal/location/timezone/ParcelableTestSupport.java b/services/tests/servicestests/src/com/android/internal/location/timezone/ParcelableTestSupport.java
deleted file mode 100644
index ece5d00..0000000
--- a/services/tests/servicestests/src/com/android/internal/location/timezone/ParcelableTestSupport.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.location.timezone;
-
-import static org.junit.Assert.assertEquals;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.lang.reflect.Field;
-
-/** Utility methods related to {@link Parcelable} objects used in several tests. */
-final class ParcelableTestSupport {
-
-    private ParcelableTestSupport() {}
-
-    /** Returns the result of parceling and unparceling the argument. */
-    @SuppressWarnings("unchecked")
-    public static <T extends Parcelable> T roundTripParcelable(T parcelable) {
-        Parcel parcel = Parcel.obtain();
-        parcel.writeTypedObject(parcelable, 0);
-        parcel.setDataPosition(0);
-
-        Parcelable.Creator<T> creator;
-        try {
-            Field creatorField = parcelable.getClass().getField("CREATOR");
-            creator = (Parcelable.Creator<T>) creatorField.get(null);
-        } catch (NoSuchFieldException | IllegalAccessException e) {
-            throw new AssertionError(e);
-        }
-        T toReturn = parcel.readTypedObject(creator);
-        parcel.recycle();
-        return toReturn;
-    }
-
-    public static <T extends Parcelable> void assertRoundTripParcelable(T instance) {
-        assertEquals(instance, roundTripParcelable(instance));
-    }
-}
diff --git a/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java b/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java
index 870fe4a..4f4aa3f 100644
--- a/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java
+++ b/services/tests/servicestests/src/com/android/server/compat/CompatConfigBuilder.java
@@ -117,6 +117,7 @@
 
     CompatConfig build() {
         CompatConfig config = new CompatConfig(mBuildClassifier, mContext);
+        config.forceNonDebuggableFinalForTest(false);
         for (CompatChange change : mChanges) {
             config.addChange(change);
         }
diff --git a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
index 8c63bfc..ac8dc34 100644
--- a/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/compat/CompatConfigTest.java
@@ -28,6 +28,7 @@
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
 
 import androidx.test.runner.AndroidJUnit4;
 
@@ -81,6 +82,8 @@
     @Test
     public void testUnknownChangeEnabled() throws Exception {
         CompatConfig compatConfig = new CompatConfig(mBuildClassifier, mContext);
+        compatConfig.forceNonDebuggableFinalForTest(false);
+
         assertThat(compatConfig.isChangeEnabled(1234L, ApplicationInfoBuilder.create().build()))
             .isTrue();
     }
@@ -180,6 +183,8 @@
     @Test
     public void testPackageOverrideUnknownPackage() throws Exception {
         CompatConfig compatConfig = new CompatConfig(mBuildClassifier, mContext);
+        compatConfig.forceNonDebuggableFinalForTest(false);
+
 
         compatConfig.addOverride(1234L, "com.some.package", false);
 
@@ -230,6 +235,83 @@
     }
 
     @Test
+    public void testApplyDeferredOverridesAfterInstallingApp() throws Exception {
+        ApplicationInfo applicationInfo = ApplicationInfoBuilder.create()
+                .withPackageName("com.notinstalled.foo")
+                .debuggable().build();
+        when(mPackageManager.getApplicationInfo(eq("com.notinstalled.foo"), anyInt()))
+                .thenThrow(new NameNotFoundException());
+        CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
+                .addDisabledChangeWithId(1234L).build();
+        when(mBuildClassifier.isDebuggableBuild()).thenReturn(false);
+        when(mBuildClassifier.isFinalBuild()).thenReturn(true);
+
+        // Add override before the app is available.
+        compatConfig.addOverride(1234L, "com.notinstalled.foo", true);
+        assertThat(compatConfig.isChangeEnabled(1234L, applicationInfo)).isFalse();
+
+        // Pretend the app is now installed.
+        when(mPackageManager.getApplicationInfo(eq("com.notinstalled.foo"), anyInt()))
+                .thenReturn(applicationInfo);
+
+        compatConfig.recheckOverrides("com.notinstalled.foo");
+        assertThat(compatConfig.isChangeEnabled(1234L, applicationInfo)).isTrue();
+    }
+
+    @Test
+    public void testApplyDeferredOverrideClearsOverrideAfterUninstall() throws Exception {
+        ApplicationInfo applicationInfo = ApplicationInfoBuilder.create()
+                .withPackageName("com.installedapp.foo")
+                .debuggable().build();
+        when(mPackageManager.getApplicationInfo(eq("com.installedapp.foo"), anyInt()))
+                .thenReturn(applicationInfo);
+
+        CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
+                .addDisabledChangeWithId(1234L).build();
+        when(mBuildClassifier.isDebuggableBuild()).thenReturn(false);
+        when(mBuildClassifier.isFinalBuild()).thenReturn(true);
+
+        // Add override when app is installed.
+        compatConfig.addOverride(1234L, "com.installedapp.foo", true);
+        assertThat(compatConfig.isChangeEnabled(1234L, applicationInfo)).isTrue();
+
+        // Pretend the app is now uninstalled.
+        when(mPackageManager.getApplicationInfo(eq("com.installedapp.foo"), anyInt()))
+                .thenThrow(new NameNotFoundException());
+
+        compatConfig.recheckOverrides("com.installedapp.foo");
+        assertThat(compatConfig.isChangeEnabled(1234L, applicationInfo)).isFalse();
+    }
+
+    @Test
+    public void testApplyDeferredOverrideClearsOverrideAfterChange() throws Exception {
+        ApplicationInfo debuggableApp = ApplicationInfoBuilder.create()
+                .withPackageName("com.installedapp.foo")
+                .debuggable().build();
+        ApplicationInfo releaseApp = ApplicationInfoBuilder.create()
+                .withPackageName("com.installedapp.foo")
+                .build();
+        when(mPackageManager.getApplicationInfo(eq("com.installedapp.foo"), anyInt()))
+                .thenReturn(debuggableApp);
+
+        CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
+                .addDisabledChangeWithId(1234L).build();
+        when(mBuildClassifier.isDebuggableBuild()).thenReturn(false);
+        when(mBuildClassifier.isFinalBuild()).thenReturn(true);
+
+        // Add override for debuggable app.
+        compatConfig.addOverride(1234L, "com.installedapp.foo", true);
+        assertThat(compatConfig.isChangeEnabled(1234L, debuggableApp)).isTrue();
+
+        // Pretend the app now is no longer debuggable, but has the same package.
+        when(mPackageManager.getApplicationInfo(eq("com.installedapp.foo"), anyInt()))
+                .thenReturn(releaseApp);
+
+        compatConfig.recheckOverrides("com.installedapp.foo");
+        assertThat(compatConfig.isChangeEnabled(1234L, releaseApp)).isFalse();
+    }
+
+    @Test
     public void testLoggingOnlyChangePreventAddOverride() throws Exception {
         CompatConfig compatConfig = CompatConfigBuilder.create(mBuildClassifier, mContext)
                 .addLoggingOnlyChangeWithId(1234L)
@@ -259,7 +341,7 @@
         // Reject all override attempts.
         // Force the validator to prevent overriding the change by using a user build.
         when(mBuildClassifier.isDebuggableBuild()).thenReturn(false);
-        when(mBuildClassifier.isFinalBuild()).thenReturn(true);
+        when(mBuildClassifier.isFinalBuild()).thenReturn(false);
         // Try to turn off change, but validator prevents it.
         assertThrows(SecurityException.class,
                 () -> compatConfig.removeOverride(1234L, "com.some.package"));
@@ -360,6 +442,8 @@
     @Test
     public void testLookupChangeIdNotPresent() throws Exception {
         CompatConfig compatConfig = new CompatConfig(mBuildClassifier, mContext);
+        compatConfig.forceNonDebuggableFinalForTest(false);
+
         assertThat(compatConfig.lookupChangeId("MY_CHANGE")).isEqualTo(-1L);
     }
 
@@ -374,6 +458,8 @@
         File dir = createTempDir();
         writeToFile(dir, "platform_compat_config.xml", configXml);
         CompatConfig compatConfig = new CompatConfig(mBuildClassifier, mContext);
+        compatConfig.forceNonDebuggableFinalForTest(false);
+
         compatConfig.initConfigFromLib(dir);
 
         assertThat(compatConfig.isChangeEnabled(1234L,
@@ -400,6 +486,8 @@
         writeToFile(dir, "libcore_platform_compat_config.xml", configXml1);
         writeToFile(dir, "frameworks_platform_compat_config.xml", configXml2);
         CompatConfig compatConfig = new CompatConfig(mBuildClassifier, mContext);
+        compatConfig.forceNonDebuggableFinalForTest(false);
+
         compatConfig.initConfigFromLib(dir);
 
         assertThat(compatConfig.isChangeEnabled(1234L,
diff --git a/services/tests/servicestests/src/com/android/server/compat/OverrideValidatorImplTest.java b/services/tests/servicestests/src/com/android/server/compat/OverrideValidatorImplTest.java
index c53b29a..0fd6445 100644
--- a/services/tests/servicestests/src/com/android/server/compat/OverrideValidatorImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/compat/OverrideValidatorImplTest.java
@@ -17,6 +17,7 @@
 package com.android.server.compat;
 
 import static com.android.internal.compat.OverrideAllowedState.ALLOWED;
+import static com.android.internal.compat.OverrideAllowedState.DEFERRED_VERIFICATION;
 import static com.android.internal.compat.OverrideAllowedState.DISABLED_NON_TARGET_SDK;
 import static com.android.internal.compat.OverrideAllowedState.DISABLED_NOT_DEBUGGABLE;
 import static com.android.internal.compat.OverrideAllowedState.DISABLED_TARGET_SDK_TOO_HIGH;
@@ -31,6 +32,7 @@
 
 import android.content.Context;
 import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
 
 import androidx.test.runner.AndroidJUnit4;
 
@@ -409,4 +411,216 @@
         assertThat(stateDLoggingOnlyChange)
                 .isEqualTo(new OverrideAllowedState(LOGGING_ONLY_CHANGE, -1, -1));
     }
+    @Test
+    public void getOverrideAllowedState_finalBuildAnyChangeNotInstalledApp_deferOverride()
+            throws Exception {
+        CompatConfig config = CompatConfigBuilder.create(finalBuild(), mContext)
+                        .addEnableAfterSdkChangeWithId(TARGET_SDK_BEFORE, 1)
+                        .addEnableAfterSdkChangeWithId(TARGET_SDK, 2)
+                        .addEnableAfterSdkChangeWithId(TARGET_SDK_AFTER, 3)
+                        .addEnabledChangeWithId(4)
+                        .addDisabledChangeWithId(5)
+                        .addLoggingOnlyChangeWithId(6).build();
+        IOverrideValidator overrideValidator = config.getOverrideValidator();
+        when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt()))
+                .thenThrow(new NameNotFoundException());
+
+        OverrideAllowedState stateTargetSdkLessChange =
+                overrideValidator.getOverrideAllowedState(1, PACKAGE_NAME);
+        OverrideAllowedState stateTargetSdkEqualChange =
+                overrideValidator.getOverrideAllowedState(2, PACKAGE_NAME);
+        OverrideAllowedState stateTargetSdkAfterChange =
+                overrideValidator.getOverrideAllowedState(3, PACKAGE_NAME);
+        OverrideAllowedState stateEnabledChange =
+                overrideValidator.getOverrideAllowedState(4, PACKAGE_NAME);
+        OverrideAllowedState stateDisabledChange =
+                overrideValidator.getOverrideAllowedState(5, PACKAGE_NAME);
+        OverrideAllowedState stateDLoggingOnlyChange =
+                overrideValidator.getOverrideAllowedState(6, PACKAGE_NAME);
+
+        assertThat(stateTargetSdkLessChange)
+                .isEqualTo(new OverrideAllowedState(DEFERRED_VERIFICATION, -1, -1));
+        assertThat(stateTargetSdkEqualChange)
+                .isEqualTo(new OverrideAllowedState(DEFERRED_VERIFICATION, -1, -1));
+        assertThat(stateTargetSdkAfterChange)
+                .isEqualTo(new OverrideAllowedState(DEFERRED_VERIFICATION, -1, -1));
+        assertThat(stateEnabledChange)
+                .isEqualTo(new OverrideAllowedState(DEFERRED_VERIFICATION, -1, -1));
+        assertThat(stateDisabledChange)
+                .isEqualTo(new OverrideAllowedState(DEFERRED_VERIFICATION, -1, -1));
+        assertThat(stateDLoggingOnlyChange)
+                .isEqualTo(new OverrideAllowedState(LOGGING_ONLY_CHANGE, -1, -1));
+    }
+
+    @Test
+    public void getOverrideAllowedState_forceFinalBuildTargetSdkChangeDebugAppOptin_allowOverride()
+            throws Exception {
+        CompatConfig config = CompatConfigBuilder.create(debuggableBuild(), mContext)
+                        .addEnableAfterSdkChangeWithId(TARGET_SDK_AFTER, 1)
+                        .addEnableAfterSdkChangeWithId(TARGET_SDK, 2).build();
+        config.forceNonDebuggableFinalForTest(true);
+        IOverrideValidator overrideValidator = config.getOverrideValidator();
+        when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt()))
+                .thenReturn(ApplicationInfoBuilder.create()
+                        .debuggable()
+                        .withTargetSdk(TARGET_SDK)
+                        .withPackageName(PACKAGE_NAME).build());
+
+        OverrideAllowedState stateTargetSdkGreaterChange =
+                overrideValidator.getOverrideAllowedState(1, PACKAGE_NAME);
+        OverrideAllowedState stateTargetSdkEqualChange =
+                overrideValidator.getOverrideAllowedState(2, PACKAGE_NAME);
+
+        assertThat(stateTargetSdkGreaterChange)
+                .isEqualTo(new OverrideAllowedState(ALLOWED, TARGET_SDK, TARGET_SDK_AFTER));
+
+        assertThat(stateTargetSdkEqualChange)
+                .isEqualTo(new OverrideAllowedState(ALLOWED, TARGET_SDK, TARGET_SDK));
+    }
+
+    @Test
+    public void getOverrideAllowedState_forceFinalBldTargetSdkChangeDebugAppOptout_rejectOverride()
+            throws Exception {
+        CompatConfig config = CompatConfigBuilder.create(debuggableBuild(), mContext)
+                        .addEnableAfterSdkChangeWithId(TARGET_SDK_BEFORE, 1).build();
+        config.forceNonDebuggableFinalForTest(true);
+        IOverrideValidator overrideValidator = config.getOverrideValidator();
+        when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt()))
+                .thenReturn(ApplicationInfoBuilder.create()
+                        .withPackageName(PACKAGE_NAME)
+                        .withTargetSdk(TARGET_SDK)
+                        .debuggable()
+                        .build());
+
+        OverrideAllowedState stateTargetSdkLessChange =
+                overrideValidator.getOverrideAllowedState(1, PACKAGE_NAME);
+
+        assertThat(stateTargetSdkLessChange).isEqualTo(
+                new OverrideAllowedState(DISABLED_TARGET_SDK_TOO_HIGH, TARGET_SDK,
+                                         TARGET_SDK_BEFORE));
+    }
+
+    @Test
+    public void getOverrideAllowedState_forceFinalBuildEnabledChangeDebugApp_rejectOverride()
+            throws Exception {
+        CompatConfig config = CompatConfigBuilder.create(debuggableBuild(), mContext)
+                        .addEnabledChangeWithId(1).build();
+        config.forceNonDebuggableFinalForTest(true);
+        IOverrideValidator overrideValidator = config.getOverrideValidator();
+        when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt()))
+                .thenReturn(ApplicationInfoBuilder.create()
+                        .withPackageName(PACKAGE_NAME)
+                        .debuggable().build());
+
+        OverrideAllowedState allowedState =
+                overrideValidator.getOverrideAllowedState(1, PACKAGE_NAME);
+
+        assertThat(allowedState)
+                .isEqualTo(new OverrideAllowedState(DISABLED_NON_TARGET_SDK, -1, -1));
+    }
+
+    @Test
+    public void getOverrideAllowedState_forceFinalBuildDisabledChangeDebugApp_allowOverride()
+            throws Exception {
+        CompatConfig config = CompatConfigBuilder.create(debuggableBuild(), mContext)
+                .addDisabledChangeWithId(1).build();
+        config.forceNonDebuggableFinalForTest(true);
+        IOverrideValidator overrideValidator = config.getOverrideValidator();
+        when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt()))
+                .thenReturn(ApplicationInfoBuilder.create()
+                        .withPackageName(PACKAGE_NAME)
+                        .withTargetSdk(TARGET_SDK)
+                        .debuggable().build());
+
+        OverrideAllowedState allowedState =
+                overrideValidator.getOverrideAllowedState(1, PACKAGE_NAME);
+
+        assertThat(allowedState)
+                .isEqualTo(new OverrideAllowedState(ALLOWED, TARGET_SDK, -1));
+    }
+
+    @Test
+    public void getOverrideAllowedState_forceFinalBuildAnyChangeReleaseApp_rejectOverride()
+            throws Exception {
+        CompatConfig config = CompatConfigBuilder.create(debuggableBuild(), mContext)
+                        .addEnableAfterSdkChangeWithId(TARGET_SDK_BEFORE, 1)
+                        .addEnableAfterSdkChangeWithId(TARGET_SDK, 2)
+                        .addEnableAfterSdkChangeWithId(TARGET_SDK_AFTER, 3)
+                        .addEnabledChangeWithId(4)
+                        .addDisabledChangeWithId(5)
+                        .addLoggingOnlyChangeWithId(6).build();
+        config.forceNonDebuggableFinalForTest(true);
+        IOverrideValidator overrideValidator = config.getOverrideValidator();
+        when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt()))
+                .thenReturn(ApplicationInfoBuilder.create()
+                        .withPackageName(PACKAGE_NAME)
+                        .withTargetSdk(TARGET_SDK).build());
+
+        OverrideAllowedState stateTargetSdkLessChange =
+                overrideValidator.getOverrideAllowedState(1, PACKAGE_NAME);
+        OverrideAllowedState stateTargetSdkEqualChange =
+                overrideValidator.getOverrideAllowedState(2, PACKAGE_NAME);
+        OverrideAllowedState stateTargetSdkAfterChange =
+                overrideValidator.getOverrideAllowedState(3, PACKAGE_NAME);
+        OverrideAllowedState stateEnabledChange =
+                overrideValidator.getOverrideAllowedState(4, PACKAGE_NAME);
+        OverrideAllowedState stateDisabledChange =
+                overrideValidator.getOverrideAllowedState(5, PACKAGE_NAME);
+        OverrideAllowedState stateDLoggingOnlyChange =
+                overrideValidator.getOverrideAllowedState(6, PACKAGE_NAME);
+
+        assertThat(stateTargetSdkLessChange)
+                .isEqualTo(new OverrideAllowedState(DISABLED_NOT_DEBUGGABLE, -1, -1));
+        assertThat(stateTargetSdkEqualChange)
+                .isEqualTo(new OverrideAllowedState(DISABLED_NOT_DEBUGGABLE, -1, -1));
+        assertThat(stateTargetSdkAfterChange)
+                .isEqualTo(new OverrideAllowedState(DISABLED_NOT_DEBUGGABLE, -1, -1));
+        assertThat(stateEnabledChange)
+                .isEqualTo(new OverrideAllowedState(DISABLED_NOT_DEBUGGABLE, -1, -1));
+        assertThat(stateDisabledChange)
+                .isEqualTo(new OverrideAllowedState(DISABLED_NOT_DEBUGGABLE, -1, -1));
+        assertThat(stateDLoggingOnlyChange)
+                .isEqualTo(new OverrideAllowedState(LOGGING_ONLY_CHANGE, -1, -1));
+    }
+    @Test
+    public void getOverrideAllowedState_forceFinalBuildAnyChangeNotInstalledApp_deferOverride()
+            throws Exception {
+        CompatConfig config = CompatConfigBuilder.create(debuggableBuild(), mContext)
+                        .addEnableAfterSdkChangeWithId(TARGET_SDK_BEFORE, 1)
+                        .addEnableAfterSdkChangeWithId(TARGET_SDK, 2)
+                        .addEnableAfterSdkChangeWithId(TARGET_SDK_AFTER, 3)
+                        .addEnabledChangeWithId(4)
+                        .addDisabledChangeWithId(5)
+                        .addLoggingOnlyChangeWithId(6).build();
+        config.forceNonDebuggableFinalForTest(true);
+        IOverrideValidator overrideValidator = config.getOverrideValidator();
+        when(mPackageManager.getApplicationInfo(eq(PACKAGE_NAME), anyInt()))
+                .thenThrow(new NameNotFoundException());
+
+        OverrideAllowedState stateTargetSdkLessChange =
+                overrideValidator.getOverrideAllowedState(1, PACKAGE_NAME);
+        OverrideAllowedState stateTargetSdkEqualChange =
+                overrideValidator.getOverrideAllowedState(2, PACKAGE_NAME);
+        OverrideAllowedState stateTargetSdkAfterChange =
+                overrideValidator.getOverrideAllowedState(3, PACKAGE_NAME);
+        OverrideAllowedState stateEnabledChange =
+                overrideValidator.getOverrideAllowedState(4, PACKAGE_NAME);
+        OverrideAllowedState stateDisabledChange =
+                overrideValidator.getOverrideAllowedState(5, PACKAGE_NAME);
+        OverrideAllowedState stateDLoggingOnlyChange =
+                overrideValidator.getOverrideAllowedState(6, PACKAGE_NAME);
+
+        assertThat(stateTargetSdkLessChange)
+                .isEqualTo(new OverrideAllowedState(DEFERRED_VERIFICATION, -1, -1));
+        assertThat(stateTargetSdkEqualChange)
+                .isEqualTo(new OverrideAllowedState(DEFERRED_VERIFICATION, -1, -1));
+        assertThat(stateTargetSdkAfterChange)
+                .isEqualTo(new OverrideAllowedState(DEFERRED_VERIFICATION, -1, -1));
+        assertThat(stateEnabledChange)
+                .isEqualTo(new OverrideAllowedState(DEFERRED_VERIFICATION, -1, -1));
+        assertThat(stateDisabledChange)
+                .isEqualTo(new OverrideAllowedState(DEFERRED_VERIFICATION, -1, -1));
+        assertThat(stateDLoggingOnlyChange)
+                .isEqualTo(new OverrideAllowedState(LOGGING_ONLY_CHANGE, -1, -1));
+    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java b/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
index 1d3b643..3f65a46 100644
--- a/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
+++ b/services/tests/servicestests/src/com/android/server/compat/PlatformCompatTest.java
@@ -73,6 +73,7 @@
         mCompatConfig = new CompatConfig(mBuildClassifier, mContext);
         mPlatformCompat = new PlatformCompat(mContext, mCompatConfig);
         // Assume userdebug/eng non-final build
+        mCompatConfig.forceNonDebuggableFinalForTest(false);
         when(mBuildClassifier.isDebuggableBuild()).thenReturn(true);
         when(mBuildClassifier.isFinalBuild()).thenReturn(false);
         LocalServices.removeServiceForTest(PackageManagerInternal.class);
diff --git a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
index ec747ac..23a4c2f 100644
--- a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
@@ -53,21 +53,29 @@
     private static final int DARKENING_LIGHT_DEBOUNCE_CONFIG = 0;
     private static final float DOZE_SCALE_FACTOR = 0.0f;
     private static final boolean RESET_AMBIENT_LUX_AFTER_WARMUP_CONFIG = false;
+    private static final int DISPLAY_ID = 0;
+    private static final int LAYER_STACK = 0;
 
     private Context mContext;
+    private LogicalDisplay mLogicalDisplay;
+
     @Mock SensorManager mSensorManager;
     @Mock BrightnessMappingStrategy mBrightnessMappingStrategy;
     @Mock HysteresisLevels mAmbientBrightnessThresholds;
     @Mock HysteresisLevels mScreenBrightnessThresholds;
-    @Mock Handler mNoopHandler;
+    @Mock Handler mNoOpHandler;
     @Mock DisplayDeviceConfig mDisplayDeviceConfig;
+    @Mock DisplayDevice mDisplayDevice;
 
     private static final int LIGHT_SENSOR_WARMUP_TIME = 0;
     @Before
     public void setUp() {
+        // Share classloader to allow package private access.
+        System.setProperty("dexmaker.share_classloader", "true");
         MockitoAnnotations.initMocks(this);
 
         mContext = InstrumentationRegistry.getContext();
+        mLogicalDisplay = new LogicalDisplay(DISPLAY_ID, LAYER_STACK, mDisplayDevice);
     }
 
     private AutomaticBrightnessController setupController(Sensor lightSensor) {
@@ -75,7 +83,7 @@
                 new AutomaticBrightnessController.Injector() {
                     @Override
                     public Handler getBackgroundThreadHandler() {
-                        return mNoopHandler;
+                        return mNoOpHandler;
                     }
                 },
                 () -> { }, mContext.getMainLooper(), mSensorManager, lightSensor,
@@ -83,7 +91,7 @@
                 BRIGHTNESS_MAX_FLOAT, DOZE_SCALE_FACTOR, LIGHT_SENSOR_RATE,
                 INITIAL_LIGHT_SENSOR_RATE, BRIGHTENING_LIGHT_DEBOUNCE_CONFIG,
                 DARKENING_LIGHT_DEBOUNCE_CONFIG, RESET_AMBIENT_LUX_AFTER_WARMUP_CONFIG,
-                mAmbientBrightnessThresholds, mScreenBrightnessThresholds, mContext
+                mAmbientBrightnessThresholds, mScreenBrightnessThresholds, mLogicalDisplay, mContext
         );
         controller.setLoggingEnabled(true);
 
diff --git a/services/tests/servicestests/src/com/android/server/location/timezone/ControllerImplTest.java b/services/tests/servicestests/src/com/android/server/location/timezone/ControllerImplTest.java
index dc81237..23365f7 100644
--- a/services/tests/servicestests/src/com/android/server/location/timezone/ControllerImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/location/timezone/ControllerImplTest.java
@@ -961,6 +961,11 @@
         }
 
         @Override
+        void destroy() {
+            // No-op test impl.
+        }
+
+        @Override
         ConfigurationInternal getCurrentUserConfigurationInternal() {
             return mConfigurationInternal;
         }
@@ -1024,6 +1029,7 @@
         /** Used to track historic provider states for tests. */
         private final TestState<ProviderState> mTestProviderState = new TestState<>();
         private boolean mInitialized;
+        private boolean mDestroyed;
 
         /**
          * Creates the instance.
@@ -1038,6 +1044,11 @@
         }
 
         @Override
+        void onDestroy() {
+            mDestroyed = true;
+        }
+
+        @Override
         void onSetCurrentState(ProviderState newState) {
             mTestProviderState.set(newState);
         }
@@ -1053,11 +1064,6 @@
         }
 
         @Override
-        void logWarn(String msg) {
-            System.out.println(msg);
-        }
-
-        @Override
         public void dump(IndentingPrintWriter pw, String[] args) {
             // Nothing needed for tests.
         }
diff --git a/services/tests/servicestests/src/com/android/server/location/timezone/HandlerThreadingDomainTest.java b/services/tests/servicestests/src/com/android/server/location/timezone/HandlerThreadingDomainTest.java
index 4d6775d..02de24d 100644
--- a/services/tests/servicestests/src/com/android/server/location/timezone/HandlerThreadingDomainTest.java
+++ b/services/tests/servicestests/src/com/android/server/location/timezone/HandlerThreadingDomainTest.java
@@ -30,10 +30,12 @@
 import org.junit.Before;
 import org.junit.Test;
 
+import java.time.Duration;
 import java.util.Objects;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
 
 /** Tests for {@link HandlerThreadingDomain}. */
 @Presubmit
@@ -85,6 +87,28 @@
     }
 
     @Test
+    public void assertNotCurrentThread() throws Exception {
+        ThreadingDomain domain = new HandlerThreadingDomain(mTestHandler);
+
+        // Expect no exception (current thread != handler thread)
+        domain.assertNotCurrentThread();
+
+        AtomicBoolean exceptionThrown = new AtomicBoolean(false);
+        LatchedRunnable testCode = new LatchedRunnable(() -> {
+            // Expect an exception (current thread == handler thread)
+            try {
+                domain.assertNotCurrentThread();
+                fail("Expected exception");
+            } catch (RuntimeException expected) {
+                exceptionThrown.set(true);
+            }
+        });
+        mTestHandler.post(testCode);
+        testCode.assertCompletesWithin(60, TimeUnit.SECONDS);
+        assertTrue(exceptionThrown.get());
+    }
+
+    @Test
     public void post() throws Exception {
         ThreadingDomain domain = new HandlerThreadingDomain(mTestHandler);
         AtomicBoolean ranOnExpectedThread = new AtomicBoolean(false);
@@ -93,6 +117,7 @@
         });
         domain.post(testLogic);
         testLogic.assertCompletesWithin(60, TimeUnit.SECONDS);
+        assertTrue(testLogic.isComplete());
         assertTrue(ranOnExpectedThread.get());
     }
 
@@ -101,17 +126,65 @@
         ThreadingDomain domain = new HandlerThreadingDomain(mTestHandler);
 
         long beforeExecutionNanos = System.nanoTime();
+        Duration executionDelay = Duration.ofSeconds(5);
+
+        AtomicReference<Long> executionNanosHolder = new AtomicReference<>();
         AtomicBoolean ranOnExpectedThread = new AtomicBoolean(false);
         LatchedRunnable testLogic = new LatchedRunnable(() -> {
             ranOnExpectedThread.set(Thread.currentThread() == mTestHandler.getLooper().getThread());
+            executionNanosHolder.set(System.nanoTime());
         });
-        domain.postDelayed(testLogic, 5000);
 
-        testLogic.assertCompletesWithin(60, TimeUnit.SECONDS);
+        domain.postDelayed(testLogic, executionDelay.toMillis());
+        long afterPostNanos = System.nanoTime();
+
+        testLogic.assertCompletesWithin(
+                executionDelay.multipliedBy(10).toMillis(), TimeUnit.MILLISECONDS);
+        long afterWaitNanos = System.nanoTime();
+
+        assertTrue(testLogic.isComplete());
         assertTrue(ranOnExpectedThread.get());
 
+        // The execution should not take place until at least delayDuration after postDelayed().
+        Duration actualExecutionDelay =
+                Duration.ofNanos(executionNanosHolder.get() - beforeExecutionNanos);
+        assertTrue(actualExecutionDelay.compareTo(executionDelay) >= 0);
+
+        // The time taken in postDelayed() should be negligible. Certainly less than the
+        // executionDelay.
+        Duration postDuration = Duration.ofNanos(afterPostNanos - beforeExecutionNanos);
+        assertTrue(postDuration.compareTo(executionDelay) < 0);
+
+        // The result should not be ready until at least executionDelay has elapsed.
+        Duration delayBeforeExecuted = Duration.ofNanos(afterWaitNanos - beforeExecutionNanos);
+        assertTrue(delayBeforeExecuted.compareTo(executionDelay) >= 0);
+    }
+
+    @Test
+    public void postAndWait() throws Exception {
+        ThreadingDomain domain = new HandlerThreadingDomain(mTestHandler);
+
+        Duration workDuration = Duration.ofSeconds(5);
+        AtomicBoolean ranOnExpectedThread = new AtomicBoolean(false);
+        LatchedRunnable testLogic = new LatchedRunnable(() -> {
+            ranOnExpectedThread.set(Thread.currentThread() == mTestHandler.getLooper().getThread());
+
+            // The work takes workDuration to complete.
+            try {
+                Thread.sleep(workDuration.toMillis());
+            } catch (InterruptedException e) {
+                throw new AssertionError(e);
+            }
+        });
+
+        long beforeExecutionNanos = System.nanoTime();
+        domain.postAndWait(testLogic, workDuration.multipliedBy(10).toMillis());
         long afterExecutionNanos = System.nanoTime();
-        assertTrue(afterExecutionNanos - beforeExecutionNanos >= TimeUnit.SECONDS.toNanos(5));
+        Duration waitDuration = Duration.ofNanos(afterExecutionNanos - beforeExecutionNanos);
+
+        assertTrue(waitDuration.compareTo(workDuration) >= 0);
+        assertTrue(testLogic.isComplete());
+        assertTrue(ranOnExpectedThread.get());
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/location/timezone/LocationTimeZoneProviderTest.java b/services/tests/servicestests/src/com/android/server/location/timezone/LocationTimeZoneProviderTest.java
new file mode 100644
index 0000000..49c67ea
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/location/timezone/LocationTimeZoneProviderTest.java
@@ -0,0 +1,261 @@
+/*
+ * 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.location.timezone;
+
+import static android.service.timezone.TimeZoneProviderService.TEST_COMMAND_RESULT_ERROR_KEY;
+import static android.service.timezone.TimeZoneProviderService.TEST_COMMAND_RESULT_SUCCESS_KEY;
+
+import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_CERTAIN;
+import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_INITIALIZING;
+import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_UNCERTAIN;
+import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STOPPED;
+import static com.android.server.location.timezone.TestSupport.USER1_CONFIG_GEO_DETECTION_ENABLED;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Bundle;
+import android.os.RemoteCallback;
+import android.platform.test.annotations.Presubmit;
+import android.service.timezone.TimeZoneProviderSuggestion;
+import android.util.IndentingPrintWriter;
+
+import com.android.server.location.timezone.LocationTimeZoneProvider.ProviderListener;
+import com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState;
+import com.android.server.timezonedetector.ConfigurationInternal;
+import com.android.server.timezonedetector.TestState;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.time.Duration;
+import java.util.Arrays;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Tests for {@link LocationTimeZoneProvider}.
+ */
+@Presubmit
+public class LocationTimeZoneProviderTest {
+
+    private static final long ARBITRARY_ELAPSED_REALTIME_MILLIS = 123456789L;
+
+    private TestThreadingDomain mTestThreadingDomain;
+
+    private TestProviderListener mProviderListener;
+
+    @Before
+    public void setUp() {
+        mTestThreadingDomain = new TestThreadingDomain();
+        mProviderListener = new TestProviderListener();
+    }
+
+    @Test
+    public void lifecycle() {
+        String providerName = "arbitrary";
+        TestLocationTimeZoneProvider provider =
+                new TestLocationTimeZoneProvider(mTestThreadingDomain, providerName);
+
+        // initialize()
+        provider.initialize(mProviderListener);
+        provider.assertOnInitializeCalled();
+
+        ProviderState currentState = provider.getCurrentState();
+        assertEquals(PROVIDER_STATE_STOPPED, currentState.stateEnum);
+        assertNull(currentState.currentUserConfiguration);
+        assertSame(provider, currentState.provider);
+        mTestThreadingDomain.assertQueueEmpty();
+
+        // startUpdates()
+        ConfigurationInternal config = USER1_CONFIG_GEO_DETECTION_ENABLED;
+        Duration arbitraryInitializationTimeout = Duration.ofMinutes(5);
+        Duration arbitraryInitializationTimeoutFuzz = Duration.ofMinutes(2);
+        provider.startUpdates(config, arbitraryInitializationTimeout,
+                arbitraryInitializationTimeoutFuzz);
+
+        provider.assertOnStartCalled(arbitraryInitializationTimeout);
+
+        currentState = provider.getCurrentState();
+        assertSame(provider, currentState.provider);
+        assertEquals(PROVIDER_STATE_STARTED_INITIALIZING, currentState.stateEnum);
+        assertEquals(config, currentState.currentUserConfiguration);
+        assertNull(currentState.event);
+        // The initialization timeout should be queued.
+        Duration expectedInitializationTimeout =
+                arbitraryInitializationTimeout.plus(arbitraryInitializationTimeoutFuzz);
+        mTestThreadingDomain.assertSingleDelayedQueueItem(expectedInitializationTimeout);
+        // We don't intend to trigger the timeout, so clear it.
+        mTestThreadingDomain.removeAllQueuedRunnables();
+
+        // Entering started does not trigger an onProviderStateChanged() as it is requested by the
+        // controller.
+        mProviderListener.assertProviderChangeNotReported();
+
+        // Simulate a suggestion event being received.
+        TimeZoneProviderSuggestion suggestion = new TimeZoneProviderSuggestion.Builder()
+                .setElapsedRealtimeMillis(ARBITRARY_ELAPSED_REALTIME_MILLIS)
+                .setTimeZoneIds(Arrays.asList("Europe/London"))
+                .build();
+        TimeZoneProviderEvent event = TimeZoneProviderEvent.createSuggestionEvent(suggestion);
+        provider.simulateProviderEventReceived(event);
+
+        currentState = provider.getCurrentState();
+        assertSame(provider, currentState.provider);
+        assertEquals(PROVIDER_STATE_STARTED_CERTAIN, currentState.stateEnum);
+        assertEquals(event, currentState.event);
+        assertEquals(config, currentState.currentUserConfiguration);
+        mTestThreadingDomain.assertQueueEmpty();
+        mProviderListener.assertProviderChangeReported(PROVIDER_STATE_STARTED_CERTAIN);
+
+        // Simulate an uncertain event being received.
+        event = TimeZoneProviderEvent.createUncertainEvent();
+        provider.simulateProviderEventReceived(event);
+
+        currentState = provider.getCurrentState();
+        assertSame(provider, currentState.provider);
+        assertEquals(PROVIDER_STATE_STARTED_UNCERTAIN, currentState.stateEnum);
+        assertEquals(event, currentState.event);
+        assertEquals(config, currentState.currentUserConfiguration);
+        mTestThreadingDomain.assertQueueEmpty();
+        mProviderListener.assertProviderChangeReported(PROVIDER_STATE_STARTED_UNCERTAIN);
+
+        // stopUpdates()
+        provider.stopUpdates();
+        provider.assertOnStopUpdatesCalled();
+
+        currentState = provider.getCurrentState();
+        assertSame(provider, currentState.provider);
+        assertEquals(PROVIDER_STATE_STOPPED, currentState.stateEnum);
+        assertNull(currentState.event);
+        assertNull(currentState.currentUserConfiguration);
+        mTestThreadingDomain.assertQueueEmpty();
+        // Entering stopped does not trigger an onProviderStateChanged() as it is requested by the
+        // controller.
+        mProviderListener.assertProviderChangeNotReported();
+
+        // destroy()
+        provider.destroy();
+        provider.assertOnDestroyCalled();
+    }
+
+    @Test
+    public void defaultHandleTestCommandImpl() {
+        String providerName = "primary";
+        TestLocationTimeZoneProvider provider =
+                new TestLocationTimeZoneProvider(mTestThreadingDomain, providerName);
+
+        TestCommand testCommand = TestCommand.createForTests("test", new Bundle());
+        AtomicReference<Bundle> resultReference = new AtomicReference<>();
+        RemoteCallback callback = new RemoteCallback(resultReference::set);
+        provider.handleTestCommand(testCommand, callback);
+
+        Bundle result = resultReference.get();
+        assertNotNull(result);
+        assertFalse(result.getBoolean(TEST_COMMAND_RESULT_SUCCESS_KEY));
+        assertNotNull(result.getString(TEST_COMMAND_RESULT_ERROR_KEY));
+    }
+
+    /** A test stand-in for the real {@link LocationTimeZoneProviderController}'s listener. */
+    private static class TestProviderListener implements ProviderListener {
+
+        private final TestState<ProviderState> mReportedProviderStateChanges = new TestState<>();
+
+        @Override
+        public void onProviderStateChange(ProviderState providerState) {
+            mReportedProviderStateChanges.set(providerState);
+        }
+
+        void assertProviderChangeReported(int expectedStateEnum) {
+            mReportedProviderStateChanges.assertChangeCount(1);
+
+            ProviderState latest = mReportedProviderStateChanges.getLatest();
+            assertEquals(expectedStateEnum, latest.stateEnum);
+            mReportedProviderStateChanges.commitLatest();
+        }
+
+        public void assertProviderChangeNotReported() {
+            mReportedProviderStateChanges.assertHasNotBeenSet();
+        }
+    }
+
+    private static class TestLocationTimeZoneProvider extends LocationTimeZoneProvider {
+
+        private boolean mOnInitializeCalled;
+        private boolean mOnDestroyCalled;
+        private boolean mOnStartUpdatesCalled;
+        private Duration mInitializationTimeout;
+        private boolean mOnStopUpdatesCalled;
+
+        /** Creates the instance. */
+        TestLocationTimeZoneProvider(@NonNull ThreadingDomain threadingDomain,
+                @NonNull String providerName) {
+            super(threadingDomain, providerName);
+        }
+
+        @Override
+        void onInitialize() {
+            mOnInitializeCalled = true;
+        }
+
+        @Override
+        void onDestroy() {
+            mOnDestroyCalled = true;
+        }
+
+        @Override
+        void onStartUpdates(@NonNull Duration initializationTimeout) {
+            mOnStartUpdatesCalled = true;
+            mInitializationTimeout = initializationTimeout;
+        }
+
+        @Override
+        void onStopUpdates() {
+            mOnStopUpdatesCalled = true;
+        }
+
+        @Override
+        public void dump(@NonNull IndentingPrintWriter ipw, @Nullable String[] args) {
+            // No-op for tests
+        }
+
+        void assertOnInitializeCalled() {
+            assertTrue(mOnInitializeCalled);
+        }
+
+        void assertOnStartCalled(Duration expectedInitializationTimeout) {
+            assertTrue(mOnStartUpdatesCalled);
+            assertEquals(expectedInitializationTimeout, mInitializationTimeout);
+        }
+
+        void simulateProviderEventReceived(TimeZoneProviderEvent event) {
+            handleTimeZoneProviderEvent(event);
+        }
+
+        void assertOnStopUpdatesCalled() {
+            assertTrue(mOnStopUpdatesCalled);
+        }
+
+        void assertOnDestroyCalled() {
+            assertTrue(mOnDestroyCalled);
+        }
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/location/timezone/NullLocationTimeZoneProviderTest.java b/services/tests/servicestests/src/com/android/server/location/timezone/NullLocationTimeZoneProviderTest.java
deleted file mode 100644
index e4a3ebd..0000000
--- a/services/tests/servicestests/src/com/android/server/location/timezone/NullLocationTimeZoneProviderTest.java
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * 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.location.timezone;
-
-import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_PERM_FAILED;
-import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STARTED_INITIALIZING;
-import static com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState.PROVIDER_STATE_STOPPED;
-import static com.android.server.location.timezone.TestSupport.USER1_CONFIG_GEO_DETECTION_ENABLED;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertSame;
-
-import android.platform.test.annotations.Presubmit;
-import android.util.IndentingPrintWriter;
-
-import com.android.server.location.timezone.LocationTimeZoneProvider.ProviderState;
-import com.android.server.timezonedetector.ConfigurationInternal;
-import com.android.server.timezonedetector.TestState;
-
-import org.junit.Before;
-import org.junit.Test;
-
-import java.time.Duration;
-
-/**
- * Tests for {@link NullLocationTimeZoneProvider} and, indirectly, the class it extends
- * {@link LocationTimeZoneProvider}.
- */
-@Presubmit
-public class NullLocationTimeZoneProviderTest {
-
-    private TestThreadingDomain mTestThreadingDomain;
-
-    private TestController mTestController;
-
-    @Before
-    public void setUp() {
-        mTestThreadingDomain = new TestThreadingDomain();
-        mTestController = new TestController(mTestThreadingDomain);
-    }
-
-    @Test
-    public void initialization() {
-        String providerName = "primary";
-        NullLocationTimeZoneProvider provider =
-                new NullLocationTimeZoneProvider(mTestThreadingDomain, providerName);
-        provider.initialize(providerState -> mTestController.onProviderStateChange(providerState));
-
-        ProviderState currentState = provider.getCurrentState();
-        assertEquals(PROVIDER_STATE_STOPPED, currentState.stateEnum);
-        assertNull(currentState.currentUserConfiguration);
-        assertSame(provider, currentState.provider);
-        mTestThreadingDomain.assertQueueEmpty();
-    }
-
-    @Test
-    public void startSchedulesPermFailure() {
-        String providerName = "primary";
-        NullLocationTimeZoneProvider provider =
-                new NullLocationTimeZoneProvider(mTestThreadingDomain, providerName);
-        provider.initialize(providerState -> mTestController.onProviderStateChange(providerState));
-
-        ConfigurationInternal config = USER1_CONFIG_GEO_DETECTION_ENABLED;
-        Duration arbitraryInitializationTimeout = Duration.ofMinutes(5);
-        Duration arbitraryInitializationTimeoutFuzz = Duration.ofMinutes(2);
-        provider.startUpdates(config, arbitraryInitializationTimeout,
-                arbitraryInitializationTimeoutFuzz);
-
-        // The NullProvider should enter the enabled state, but have schedule an immediate runnable
-        // to switch to perm failure.
-        ProviderState currentState = provider.getCurrentState();
-        assertSame(provider, currentState.provider);
-        assertEquals(PROVIDER_STATE_STARTED_INITIALIZING, currentState.stateEnum);
-        assertEquals(config, currentState.currentUserConfiguration);
-        mTestThreadingDomain.assertNextQueueItemIsImmediate();
-        // Entering enabled() does not trigger an onProviderStateChanged() as it is requested by the
-        // controller.
-        mTestController.assertProviderChangeNotTriggered();
-
-        // Check the queued runnable causes the provider to go into perm failed state.
-        mTestThreadingDomain.executeNext();
-
-        // Entering perm failed triggers an onProviderStateChanged() as it is asynchronously
-        // triggered.
-        mTestController.assertProviderChangeTriggered(PROVIDER_STATE_PERM_FAILED);
-    }
-
-    /** A test stand-in for the {@link LocationTimeZoneProviderController}. */
-    private static class TestController extends LocationTimeZoneProviderController {
-
-        private TestState<ProviderState> mProviderState = new TestState<>();
-
-        TestController(ThreadingDomain threadingDomain) {
-            super(threadingDomain);
-        }
-
-        @Override
-        void initialize(Environment environment, Callback callback) {
-            // Not needed for provider testing.
-        }
-
-        @Override
-        void onConfigChanged() {
-            // Not needed for provider testing.
-        }
-
-        @Override
-        boolean isUncertaintyTimeoutSet() {
-            // Not needed for provider testing.
-            return false;
-        }
-
-        @Override
-        long getUncertaintyTimeoutDelayMillis() {
-            // Not needed for provider testing.
-            return 0;
-        }
-
-        void onProviderStateChange(ProviderState providerState) {
-            this.mProviderState.set(providerState);
-        }
-
-        @Override
-        public void dump(IndentingPrintWriter pw, String[] args) {
-            // Not needed for provider testing.
-        }
-
-        void assertProviderChangeTriggered(int expectedStateEnum) {
-            assertEquals(expectedStateEnum, mProviderState.getLatest().stateEnum);
-            mProviderState.commitLatest();
-        }
-
-        public void assertProviderChangeNotTriggered() {
-            mProviderState.assertHasNotBeenSet();
-        }
-    }
-}
diff --git a/services/tests/servicestests/src/com/android/server/location/timezone/OWNERS b/services/tests/servicestests/src/com/android/server/location/timezone/OWNERS
new file mode 100644
index 0000000..28aff18
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/location/timezone/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 847766
+nfuller@google.com
+include /core/java/android/app/timedetector/OWNERS
diff --git a/services/tests/servicestests/src/com/android/server/location/timezone/TestThreadingDomain.java b/services/tests/servicestests/src/com/android/server/location/timezone/TestThreadingDomain.java
index 7359abd..b1a5ff9 100644
--- a/services/tests/servicestests/src/com/android/server/location/timezone/TestThreadingDomain.java
+++ b/services/tests/servicestests/src/com/android/server/location/timezone/TestThreadingDomain.java
@@ -26,6 +26,7 @@
 import java.util.ArrayList;
 import java.util.Comparator;
 import java.util.Objects;
+import java.util.concurrent.Callable;
 
 /**
  * A ThreadingDomain that simulates idealized post() semantics. Execution takes place in zero time,
@@ -79,6 +80,11 @@
     }
 
     @Override
+    <V> V postAndWait(Callable<V> callable, long durationMillis) {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    @Override
     void postDelayed(Runnable r, long delayMillis) {
         postDelayed(r, null, delayMillis);
     }
@@ -94,6 +100,10 @@
         mQueue.removeIf(runnable -> runnable.token != null && runnable.token == token);
     }
 
+    void removeAllQueuedRunnables() {
+        mQueue.clear();
+    }
+
     void assertSingleDelayedQueueItem(Duration expectedDelay) {
         assertQueueLength(1);
         assertNextQueueItemIsDelayed(expectedDelay);
diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplRebootTests.java b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplRebootTests.java
index 391611b..5468fba 100644
--- a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplRebootTests.java
+++ b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplRebootTests.java
@@ -78,7 +78,7 @@
     }
 
     @Test
-    public void testImmutableEnabledChange() {
+    public void testImmutableEnabledChange() throws Exception {
         final OverlayManagerServiceImpl impl = getImpl();
         installNewPackage(target(TARGET), USER);
         installNewPackage(overlay(OVERLAY, TARGET), USER);
@@ -106,7 +106,7 @@
     }
 
     @Test
-    public void testMutableEnabledChangeHasNoEffect() {
+    public void testMutableEnabledChangeHasNoEffect() throws Exception {
         final OverlayManagerServiceImpl impl = getImpl();
         installNewPackage(target(TARGET), USER);
         installNewPackage(overlay(OVERLAY, TARGET), USER);
@@ -134,7 +134,7 @@
     }
 
     @Test
-    public void testMutableEnabledToImmutableEnabled() {
+    public void testMutableEnabledToImmutableEnabled() throws Exception {
         final OverlayManagerServiceImpl impl = getImpl();
         installNewPackage(target(TARGET), USER);
         installNewPackage(overlay(OVERLAY, TARGET), USER);
@@ -178,7 +178,7 @@
     }
 
     @Test
-    public void testMutablePriorityChange() {
+    public void testMutablePriorityChange() throws Exception {
         final OverlayManagerServiceImpl impl = getImpl();
         installNewPackage(target(TARGET), USER);
         installNewPackage(overlay(OVERLAY, TARGET), USER);
@@ -218,7 +218,7 @@
     }
 
     @Test
-    public void testImmutablePriorityChange() {
+    public void testImmutablePriorityChange() throws Exception {
         final OverlayManagerServiceImpl impl = getImpl();
         installNewPackage(target(TARGET), USER);
         installNewPackage(overlay(OVERLAY, TARGET), USER);
diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java
index 4f882ce..33dbcc0 100644
--- a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java
+++ b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTests.java
@@ -22,11 +22,14 @@
 import static android.os.OverlayablePolicy.CONFIG_SIGNATURE;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
+import static org.testng.Assert.assertThrows;
 
 import android.content.om.OverlayInfo;
+import android.util.Pair;
 
 import androidx.test.runner.AndroidJUnit4;
 
@@ -35,6 +38,7 @@
 
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 
 @RunWith(AndroidJUnit4.class)
 public class OverlayManagerServiceImplTests extends OverlayManagerServiceImplTestsBase {
@@ -55,7 +59,7 @@
     private static final String CERT_CONFIG_NOK = "config_certificate_nok";
 
     @Test
-    public void testGetOverlayInfo() {
+    public void testGetOverlayInfo() throws Exception {
         installNewPackage(overlay(OVERLAY, TARGET), USER);
 
         final OverlayManagerServiceImpl impl = getImpl();
@@ -67,7 +71,7 @@
     }
 
     @Test
-    public void testGetOverlayInfosForTarget() {
+    public void testGetOverlayInfosForTarget() throws Exception {
         installNewPackage(overlay(OVERLAY, TARGET), USER);
         installNewPackage(overlay(OVERLAY2, TARGET), USER);
         installNewPackage(overlay(OVERLAY3, TARGET), USER2);
@@ -92,7 +96,7 @@
     }
 
     @Test
-    public void testGetOverlayInfosForUser() {
+    public void testGetOverlayInfosForUser() throws Exception {
         installNewPackage(target(TARGET), USER);
         installNewPackage(overlay(OVERLAY, TARGET), USER);
         installNewPackage(overlay(OVERLAY2, TARGET), USER);
@@ -119,7 +123,7 @@
     }
 
     @Test
-    public void testPriority() {
+    public void testPriority() throws Exception {
         installNewPackage(overlay(OVERLAY, TARGET), USER);
         installNewPackage(overlay(OVERLAY2, TARGET), USER);
         installNewPackage(overlay(OVERLAY3, TARGET), USER);
@@ -131,18 +135,21 @@
 
         assertOverlayInfoForTarget(TARGET, USER, o1, o2, o3);
 
-        assertTrue(impl.setLowestPriority(OVERLAY3, USER));
+        assertEquals(impl.setLowestPriority(OVERLAY3, USER),
+                Optional.of(new PackageAndUser(TARGET, USER)));
         assertOverlayInfoForTarget(TARGET, USER, o3, o1, o2);
 
-        assertTrue(impl.setHighestPriority(OVERLAY3, USER));
+        assertEquals(impl.setHighestPriority(OVERLAY3, USER),
+                Optional.of(new PackageAndUser(TARGET, USER)));
         assertOverlayInfoForTarget(TARGET, USER, o1, o2, o3);
 
-        assertTrue(impl.setPriority(OVERLAY, OVERLAY2, USER));
+        assertEquals(impl.setPriority(OVERLAY, OVERLAY2, USER),
+                Optional.of(new PackageAndUser(TARGET, USER)));
         assertOverlayInfoForTarget(TARGET, USER, o2, o1, o3);
     }
 
     @Test
-    public void testOverlayInfoStateTransitions() {
+    public void testOverlayInfoStateTransitions() throws Exception {
         final OverlayManagerServiceImpl impl = getImpl();
         assertNull(impl.getOverlayInfo(OVERLAY, USER));
 
@@ -153,7 +160,8 @@
         installNewPackage(target, USER);
         assertState(STATE_DISABLED, OVERLAY, USER);
 
-        impl.setEnabled(OVERLAY, true, USER);
+        assertEquals(impl.setEnabled(OVERLAY, true, USER),
+                Optional.of(new PackageAndUser(TARGET, USER)));
         assertState(STATE_ENABLED, OVERLAY, USER);
 
         // target upgrades do not change the state of the overlay
@@ -168,50 +176,40 @@
     }
 
     @Test
-    public void testOnOverlayPackageUpgraded() {
-        final FakeListener listener = getListener();
+    public void testOnOverlayPackageUpgraded() throws Exception {
         final FakeDeviceState.PackageBuilder target = target(TARGET);
         final FakeDeviceState.PackageBuilder overlay = overlay(OVERLAY, TARGET);
         installNewPackage(target, USER);
         installNewPackage(overlay, USER);
-        listener.count = 0;
         upgradePackage(overlay, USER);
-        assertEquals(2, listener.count);
 
         // upgrade to a version where the overlay has changed its target
-        // expect once for the old target package, once for the new target package
-        listener.count = 0;
         final FakeDeviceState.PackageBuilder overlay2 = overlay(OVERLAY, "some.other.target");
-        upgradePackage(overlay2, USER);
-        assertEquals(3, listener.count);
-
-        listener.count = 0;
-        upgradePackage(overlay2, USER);
-        assertEquals(2, listener.count);
+        final Pair<Optional<PackageAndUser>, Optional<PackageAndUser>> pair =
+                upgradePackage(overlay2, USER);
+        assertEquals(pair.first, Optional.of(new PackageAndUser(TARGET, USER)));
+        assertEquals(pair.second, Optional.of(new PackageAndUser("some.other.target", USER)));
     }
 
     @Test
-    public void testListener() {
+    public void testSetEnabledAtVariousConditions() throws Exception {
         final OverlayManagerServiceImpl impl = getImpl();
-        final FakeListener listener = getListener();
-        installNewPackage(overlay(OVERLAY, TARGET), USER);
-        assertEquals(1, listener.count);
-        listener.count = 0;
+        assertThrows(OverlayManagerServiceImpl.OperationFailedException.class,
+                () -> impl.setEnabled(OVERLAY, true, USER));
 
+        // request succeeded, and there was a change that needs to be
+        // propagated to the rest of the system
         installNewPackage(target(TARGET), USER);
-        assertEquals(1, listener.count);
-        listener.count = 0;
+        installNewPackage(overlay(OVERLAY, TARGET), USER);
+        assertEquals(impl.setEnabled(OVERLAY, true, USER),
+                Optional.of(new PackageAndUser(TARGET, USER)));
 
-        impl.setEnabled(OVERLAY, true, USER);
-        assertEquals(1, listener.count);
-        listener.count = 0;
-
-        impl.setEnabled(OVERLAY, true, USER);
-        assertEquals(0, listener.count);
+        // request succeeded, but nothing changed
+        assertFalse(impl.setEnabled(OVERLAY, true, USER).isPresent());
     }
 
     @Test
-    public void testConfigSignaturePolicyOk() {
+    public void testConfigSignaturePolicyOk() throws Exception {
         setConfigSignaturePackageName(CONFIG_SIGNATURE_REFERENCE_PKG);
         reinitializeImpl();
 
@@ -229,7 +227,7 @@
     }
 
     @Test
-    public void testConfigSignaturePolicyCertNok() {
+    public void testConfigSignaturePolicyCertNok() throws Exception {
         setConfigSignaturePackageName(CONFIG_SIGNATURE_REFERENCE_PKG);
         reinitializeImpl();
 
@@ -247,7 +245,7 @@
     }
 
     @Test
-    public void testConfigSignaturePolicyNoConfig() {
+    public void testConfigSignaturePolicyNoConfig() throws Exception {
         addPackage(target(CONFIG_SIGNATURE_REFERENCE_PKG).setCertificate(CERT_CONFIG_OK), USER);
         installNewPackage(target(TARGET), USER);
         installNewPackage(overlay(OVERLAY, TARGET).setCertificate(CERT_CONFIG_NOK), USER);
@@ -262,7 +260,7 @@
     }
 
     @Test
-    public void testConfigSignaturePolicyNoRefPkg() {
+    public void testConfigSignaturePolicyNoRefPkg() throws Exception {
         installNewPackage(target(TARGET), USER);
         installNewPackage(overlay(OVERLAY, TARGET).setCertificate(CERT_CONFIG_NOK), USER);
 
@@ -276,7 +274,7 @@
     }
 
     @Test
-    public void testConfigSignaturePolicyRefPkgNotSystem() {
+    public void testConfigSignaturePolicyRefPkgNotSystem() throws Exception {
         setConfigSignaturePackageName(CONFIG_SIGNATURE_REFERENCE_PKG);
         reinitializeImpl();
 
diff --git a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTestsBase.java b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTestsBase.java
index 006dda0..2c477c8 100644
--- a/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/om/OverlayManagerServiceImplTestsBase.java
@@ -16,6 +16,8 @@
 
 package com.android.server.om;
 
+import static com.android.server.om.OverlayManagerServiceImpl.OperationFailedException;
+
 import static org.junit.Assert.assertEquals;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.mock;
@@ -30,6 +32,7 @@
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.ArraySet;
+import android.util.Pair;
 
 import androidx.annotation.Nullable;
 
@@ -43,13 +46,13 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 import java.util.stream.Collectors;
 
 /** Base class for creating {@link OverlayManagerServiceImplTests} tests. */
 class OverlayManagerServiceImplTestsBase {
     private OverlayManagerServiceImpl mImpl;
     private FakeDeviceState mState;
-    private FakeListener mListener;
     private FakePackageManagerHelper mPackageManager;
     private FakeIdmapDaemon mIdmapDaemon;
     private OverlayConfig mOverlayConfig;
@@ -58,7 +61,6 @@
     @Before
     public void setUp() {
         mState = new FakeDeviceState();
-        mListener = new FakeListener();
         mPackageManager = new FakePackageManagerHelper(mState);
         mIdmapDaemon = new FakeIdmapDaemon(mState);
         mOverlayConfig = mock(OverlayConfig.class);
@@ -73,18 +75,13 @@
                 new IdmapManager(mIdmapDaemon, mPackageManager),
                 new OverlayManagerSettings(),
                 mOverlayConfig,
-                new String[0],
-                mListener);
+                new String[0]);
     }
 
     OverlayManagerServiceImpl getImpl() {
         return mImpl;
     }
 
-    FakeListener getListener() {
-        return mListener;
-    }
-
     FakeIdmapDaemon getIdmapd() {
         return mIdmapDaemon;
     }
@@ -155,7 +152,8 @@
      *
      * @throws IllegalStateException if the package is currently installed
      */
-    void installNewPackage(FakeDeviceState.PackageBuilder pkg, int userId) {
+    void installNewPackage(FakeDeviceState.PackageBuilder pkg, int userId)
+            throws OperationFailedException {
         if (mState.select(pkg.packageName, userId) != null) {
             throw new IllegalStateException("package " + pkg.packageName + " already installed");
         }
@@ -176,23 +174,30 @@
      * {@link android.content.Intent#ACTION_PACKAGE_ADDED} broadcast with the
      * {@link android.content.Intent#EXTRA_REPLACING} extra.
      *
+     * @return the two Optional<PackageAndUser> objects from starting and finishing the upgrade
+     *
      * @throws IllegalStateException if the package is not currently installed
      */
-    void upgradePackage(FakeDeviceState.PackageBuilder pkg, int userId) {
+    Pair<Optional<PackageAndUser>, Optional<PackageAndUser>> upgradePackage(
+            FakeDeviceState.PackageBuilder pkg, int userId) throws OperationFailedException {
         final FakeDeviceState.Package replacedPackage = mState.select(pkg.packageName, userId);
         if (replacedPackage == null) {
             throw new IllegalStateException("package " + pkg.packageName + " not installed");
         }
+        Optional<PackageAndUser> opt1 = Optional.empty();
         if (replacedPackage.targetPackageName != null) {
-            mImpl.onOverlayPackageReplacing(pkg.packageName, userId);
+            opt1 = mImpl.onOverlayPackageReplacing(pkg.packageName, userId);
         }
 
         mState.add(pkg, userId);
+        Optional<PackageAndUser> opt2;
         if (pkg.targetPackage == null) {
-            mImpl.onTargetPackageReplaced(pkg.packageName, userId);
+            opt2 = mImpl.onTargetPackageReplaced(pkg.packageName, userId);
         } else {
-            mImpl.onOverlayPackageReplaced(pkg.packageName, userId);
+            opt2 = mImpl.onOverlayPackageReplaced(pkg.packageName, userId);
         }
+
+        return Pair.create(opt1, opt2);
     }
 
     /**
@@ -203,7 +208,7 @@
      *
      * @throws IllegalStateException if the package is not currently installed
      */
-    void uninstallPackage(String packageName, int userId) {
+    void uninstallPackage(String packageName, int userId) throws OperationFailedException {
         final FakeDeviceState.Package pkg = mState.select(packageName, userId);
         if (pkg == null) {
             throw new IllegalStateException("package " + packageName+ " not installed");
@@ -485,12 +490,4 @@
             }
         }
     }
-
-    static class FakeListener implements OverlayManagerServiceImpl.OverlayChangeListener {
-        public int count;
-
-        public void onOverlaysChanged(@NonNull String targetPackage, int userId) {
-            count++;
-        }
-    }
 }
diff --git a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
index 205548c..9a52643 100644
--- a/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/AppsFilterTest.java
@@ -209,7 +209,10 @@
         final AppsFilter appsFilter =
                 new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
                         mMockExecutor);
+        final WatchableTester watcher = new WatchableTester(appsFilter, "onChange");
+        watcher.register();
         appsFilter.onSystemReady();
+        watcher.verifyChangeReported("systemReady");
         verify(mFeatureConfigMock).onSystemReady();
     }
 
@@ -218,45 +221,60 @@
         final AppsFilter appsFilter =
                 new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
                         mMockExecutor);
+        final WatchableTester watcher = new WatchableTester(appsFilter, "onChange");
+        watcher.register();
         simulateAddBasicAndroid(appsFilter);
+        watcher.verifyChangeReported("addBasicAndroid");
         appsFilter.onSystemReady();
+        watcher.verifyChangeReported("systemReady");
 
         PackageSetting target = simulateAddPackage(appsFilter,
                 pkg("com.some.package", new IntentFilter("TEST_ACTION")), DUMMY_TARGET_APPID);
+        watcher.verifyChangeReported("add package");
         PackageSetting calling = simulateAddPackage(appsFilter,
                 pkg("com.some.other.package", new Intent("TEST_ACTION")), DUMMY_CALLING_APPID);
+        watcher.verifyChangeReported("add package");
 
         assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target,
                 SYSTEM_USER));
+        watcher.verifyNoChangeReported("shouldFilterAplication");
     }
-
     @Test
     public void testQueriesProtectedAction_FilterDoesNotMatch() throws Exception {
         final AppsFilter appsFilter =
                 new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
                         mMockExecutor);
+        final WatchableTester watcher = new WatchableTester(appsFilter, "onChange");
+        watcher.register();
         final Signature frameworkSignature = Mockito.mock(Signature.class);
         final PackageParser.SigningDetails frameworkSigningDetails =
                 new PackageParser.SigningDetails(new Signature[]{frameworkSignature}, 1);
         final ParsingPackage android = pkg("android");
+        watcher.verifyNoChangeReported("prepare");
         android.addProtectedBroadcast("TEST_ACTION");
         simulateAddPackage(appsFilter, android, 1000,
                 b -> b.setSigningDetails(frameworkSigningDetails));
+        watcher.verifyChangeReported("addPackage");
         appsFilter.onSystemReady();
+        watcher.verifyChangeReported("systemReady");
 
         final int activityUid = DUMMY_TARGET_APPID;
         PackageSetting targetActivity = simulateAddPackage(appsFilter,
                 pkg("com.target.activity", new IntentFilter("TEST_ACTION")), activityUid);
+        watcher.verifyChangeReported("addPackage");
         final int receiverUid = DUMMY_TARGET_APPID + 1;
         PackageSetting targetReceiver = simulateAddPackage(appsFilter,
                 pkgWithReceiver("com.target.receiver", new IntentFilter("TEST_ACTION")),
                 receiverUid);
+        watcher.verifyChangeReported("addPackage");
         final int callingUid = DUMMY_CALLING_APPID;
         PackageSetting calling = simulateAddPackage(appsFilter,
                 pkg("com.calling.action", new Intent("TEST_ACTION")), callingUid);
+        watcher.verifyChangeReported("addPackage");
         final int wildcardUid = DUMMY_CALLING_APPID + 1;
         PackageSetting callingWildCard = simulateAddPackage(appsFilter,
                 pkg("com.calling.wildcard", new Intent("*")), wildcardUid);
+        watcher.verifyChangeReported("addPackage");
 
         assertFalse(appsFilter.shouldFilterApplication(callingUid, calling, targetActivity,
                 SYSTEM_USER));
@@ -267,6 +285,7 @@
                 wildcardUid, callingWildCard, targetActivity, SYSTEM_USER));
         assertTrue(appsFilter.shouldFilterApplication(
                 wildcardUid, callingWildCard, targetReceiver, SYSTEM_USER));
+        watcher.verifyNoChangeReported("shouldFilterApplication");
     }
 
     @Test
@@ -274,17 +293,24 @@
         final AppsFilter appsFilter =
                 new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
                         mMockExecutor);
+        final WatchableTester watcher = new WatchableTester(appsFilter, "onChange");
+        watcher.register();
         simulateAddBasicAndroid(appsFilter);
+        watcher.verifyChangeReported("addPackage");
         appsFilter.onSystemReady();
+        watcher.verifyChangeReported("systemReady");
 
         PackageSetting target = simulateAddPackage(appsFilter,
                 pkgWithProvider("com.some.package", "com.some.authority"), DUMMY_TARGET_APPID);
+        watcher.verifyChangeReported("addPackage");
         PackageSetting calling = simulateAddPackage(appsFilter,
                 pkgQueriesProvider("com.some.other.package", "com.some.authority"),
                 DUMMY_CALLING_APPID);
+        watcher.verifyChangeReported("addPackage");
 
         assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target,
                 SYSTEM_USER));
+        watcher.verifyNoChangeReported("shouldFilterApplication");
     }
 
     @Test
@@ -292,17 +318,24 @@
         final AppsFilter appsFilter =
                 new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
                         mMockExecutor);
+        final WatchableTester watcher = new WatchableTester(appsFilter, "onChange");
+        watcher.register();
         simulateAddBasicAndroid(appsFilter);
+        watcher.verifyChangeReported("addPackage");
         appsFilter.onSystemReady();
+        watcher.verifyChangeReported("systemReady");
 
         PackageSetting target = simulateAddPackage(appsFilter,
                 pkgWithProvider("com.some.package", "com.some.authority"), DUMMY_TARGET_APPID);
+        watcher.verifyChangeReported("addPackage");
         PackageSetting calling = simulateAddPackage(appsFilter,
                 pkgQueriesProvider("com.some.other.package", "com.some.other.authority"),
                 DUMMY_CALLING_APPID);
+        watcher.verifyChangeReported("addPackage");
 
         assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target,
                 SYSTEM_USER));
+        watcher.verifyNoChangeReported("shouldFilterApplication");
     }
 
     @Test
@@ -779,16 +812,23 @@
         final AppsFilter appsFilter =
                 new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
                         mMockExecutor);
+        final WatchableTester watcher = new WatchableTester(appsFilter, "onChange");
+        watcher.register();
         simulateAddBasicAndroid(appsFilter);
+        watcher.verifyChangeReported("addBasicAndroid");
         appsFilter.onSystemReady();
+        watcher.verifyChangeReported("systemReady");
 
         PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package"),
                 DUMMY_TARGET_APPID);
+        watcher.verifyChangeReported("add package");
         PackageSetting calling = simulateAddPackage(appsFilter, pkg("com.some.other.package"),
                 DUMMY_CALLING_APPID, withInstallSource(null, target.name, null, null, false));
+        watcher.verifyChangeReported("add package");
 
         assertTrue(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target,
                 SYSTEM_USER));
+        watcher.verifyNoChangeReported("shouldFilterAplication");
     }
 
     @Test
@@ -796,16 +836,23 @@
         final AppsFilter appsFilter =
                 new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
                         mMockExecutor);
+        final WatchableTester watcher = new WatchableTester(appsFilter, "onChange");
+        watcher.register();
         simulateAddBasicAndroid(appsFilter);
+        watcher.verifyChangeReported("addBasicAndroid");
         appsFilter.onSystemReady();
+        watcher.verifyChangeReported("systemReady");
 
         PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package"),
                 DUMMY_TARGET_APPID);
+        watcher.verifyChangeReported("add package");
         PackageSetting calling = simulateAddPackage(appsFilter, pkg("com.some.other.package"),
                 DUMMY_CALLING_APPID, withInstallSource(null, null, target.name, null, false));
+        watcher.verifyChangeReported("add package");
 
         assertFalse(appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, calling, target,
                 SYSTEM_USER));
+        watcher.verifyNoChangeReported("shouldFilterAplication");
     }
 
     @Test
@@ -813,15 +860,20 @@
         final AppsFilter appsFilter =
                 new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
                         mMockExecutor);
+        final WatchableTester watcher = new WatchableTester(appsFilter, "onChange");
+        watcher.register();
         simulateAddBasicAndroid(appsFilter);
+        watcher.verifyChangeReported("addBasicAndroid");
         appsFilter.onSystemReady();
-
+        watcher.verifyChangeReported("systemReady");
 
         PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package"),
                 DUMMY_TARGET_APPID);
+        watcher.verifyChangeReported("add package");
         PackageSetting instrumentation = simulateAddPackage(appsFilter,
                 pkgWithInstrumentation("com.some.other.package", "com.some.package"),
                 DUMMY_CALLING_APPID);
+        watcher.verifyChangeReported("add package");
 
         assertFalse(
                 appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, instrumentation, target,
@@ -829,6 +881,7 @@
         assertFalse(
                 appsFilter.shouldFilterApplication(DUMMY_TARGET_APPID, target, instrumentation,
                         SYSTEM_USER));
+        watcher.verifyNoChangeReported("shouldFilterAplication");
     }
 
     @Test
@@ -836,8 +889,12 @@
         final AppsFilter appsFilter =
                 new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
                         mMockExecutor);
+        final WatchableTester watcher = new WatchableTester(appsFilter, "onChange");
+        watcher.register();
         simulateAddBasicAndroid(appsFilter);
+        watcher.verifyChangeReported("addBasicAndroid");
         appsFilter.onSystemReady();
+        watcher.verifyChangeReported("systemReady");
 
         final int systemAppId = Process.FIRST_APPLICATION_UID - 1;
         final int seesNothingAppId = Process.FIRST_APPLICATION_UID;
@@ -845,25 +902,34 @@
         final int queriesProviderAppId = Process.FIRST_APPLICATION_UID + 2;
 
         PackageSetting system = simulateAddPackage(appsFilter, pkg("some.system.pkg"), systemAppId);
+        watcher.verifyChangeReported("add package");
         PackageSetting seesNothing = simulateAddPackage(appsFilter, pkg("com.some.package"),
                 seesNothingAppId);
+        watcher.verifyChangeReported("add package");
         PackageSetting hasProvider = simulateAddPackage(appsFilter,
                 pkgWithProvider("com.some.other.package", "com.some.authority"), hasProviderAppId);
+        watcher.verifyChangeReported("add package");
         PackageSetting queriesProvider = simulateAddPackage(appsFilter,
                 pkgQueriesProvider("com.yet.some.other.package", "com.some.authority"),
                 queriesProviderAppId);
+        watcher.verifyChangeReported("add package");
 
         final SparseArray<int[]> systemFilter =
                 appsFilter.getVisibilityAllowList(system, USER_ARRAY, mExisting);
+        watcher.verifyNoChangeReported("getVisibility");
         assertThat(toList(systemFilter.get(SYSTEM_USER)),
                 contains(seesNothingAppId, hasProviderAppId, queriesProviderAppId));
+        watcher.verifyNoChangeReported("getVisibility");
 
         final SparseArray<int[]> seesNothingFilter =
                 appsFilter.getVisibilityAllowList(seesNothing, USER_ARRAY, mExisting);
+        watcher.verifyNoChangeReported("getVisibility");
         assertThat(toList(seesNothingFilter.get(SYSTEM_USER)),
                 contains(seesNothingAppId));
+        watcher.verifyNoChangeReported("getVisibility");
         assertThat(toList(seesNothingFilter.get(SECONDARY_USER)),
                 contains(seesNothingAppId));
+        watcher.verifyNoChangeReported("getVisibility");
 
         final SparseArray<int[]> hasProviderFilter =
                 appsFilter.getVisibilityAllowList(hasProvider, USER_ARRAY, mExisting);
@@ -872,17 +938,22 @@
 
         SparseArray<int[]> queriesProviderFilter =
                 appsFilter.getVisibilityAllowList(queriesProvider, USER_ARRAY, mExisting);
+        watcher.verifyNoChangeReported("getVisibility");
         assertThat(toList(queriesProviderFilter.get(SYSTEM_USER)),
                 contains(queriesProviderAppId));
+        watcher.verifyNoChangeReported("getVisibility");
 
         // provider read
         appsFilter.grantImplicitAccess(hasProviderAppId, queriesProviderAppId);
+        watcher.verifyChangeReported("grantImplicitAccess");
 
         // ensure implicit access is included in the filter
         queriesProviderFilter =
                 appsFilter.getVisibilityAllowList(queriesProvider, USER_ARRAY, mExisting);
+        watcher.verifyNoChangeReported("getVisibility");
         assertThat(toList(queriesProviderFilter.get(SYSTEM_USER)),
                 contains(hasProviderAppId, queriesProviderAppId));
+        watcher.verifyNoChangeReported("getVisibility");
     }
 
     @Test
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
index 282047a..333ec929 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -1215,7 +1215,7 @@
     private void verifyKeySetMetaData(Settings settings)
             throws ReflectiveOperationException, IllegalAccessException {
         ArrayMap<String, PackageSetting> packages =
-                settings.mPackages.untrackedMap();
+                settings.mPackages.untrackedStorage();
         KeySetManagerService ksms = settings.mKeySetManagerService;
 
         /* verify keyset and public key ref counts */
diff --git a/services/tests/servicestests/src/com/android/server/powerstats/OWNERS b/services/tests/servicestests/src/com/android/server/powerstats/OWNERS
index d68066b..12f13ea 100644
--- a/services/tests/servicestests/src/com/android/server/powerstats/OWNERS
+++ b/services/tests/servicestests/src/com/android/server/powerstats/OWNERS
@@ -1 +1 @@
-include /services/core/java/com/android/server/power/OWNERS
+include /services/core/java/com/android/server/powerstats/OWNERS
diff --git a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java
index 8f7ea87..62be98c 100644
--- a/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/tv/tunerresourcemanager/TunerResourceManagerServiceTest.java
@@ -86,7 +86,7 @@
                     return (actual == null) && (expected == null);
                 }
 
-                return actual.getId() == expected.getId()
+                return actual.getHandle() == expected.getHandle()
                         && actual.getType() == expected.getFrontendType()
                         && actual.getExclusiveGroupId() == expected.getExclusiveGroupId();
             },  "is correctly configured from ");
@@ -119,12 +119,12 @@
         Map<Integer, FrontendResource> resources =
                 mTunerResourceManagerService.getFrontendResources();
         for (int id = 0; id < infos.length; id++) {
-            assertThat(resources.get(infos[id].getId())
-                    .getExclusiveGroupMemberFeIds().size()).isEqualTo(0);
+            assertThat(resources.get(infos[id].getHandle())
+                    .getExclusiveGroupMemberFeHandles().size()).isEqualTo(0);
         }
         for (int id = 0; id < infos.length; id++) {
-            assertThat(resources.get(infos[id].getId())
-                    .getExclusiveGroupMemberFeIds().size()).isEqualTo(0);
+            assertThat(resources.get(infos[id].getHandle())
+                    .getExclusiveGroupMemberFeHandles().size()).isEqualTo(0);
         }
         assertThat(resources.values()).comparingElementsUsing(FR_TFI_COMPARE)
                 .containsExactlyElementsIn(Arrays.asList(infos));
@@ -149,10 +149,10 @@
         assertThat(resources.values()).comparingElementsUsing(FR_TFI_COMPARE)
                 .containsExactlyElementsIn(Arrays.asList(infos));
 
-        assertThat(resources.get(0).getExclusiveGroupMemberFeIds()).isEmpty();
-        assertThat(resources.get(1).getExclusiveGroupMemberFeIds()).containsExactly(2, 3);
-        assertThat(resources.get(2).getExclusiveGroupMemberFeIds()).containsExactly(1, 3);
-        assertThat(resources.get(3).getExclusiveGroupMemberFeIds()).containsExactly(1, 2);
+        assertThat(resources.get(0).getExclusiveGroupMemberFeHandles()).isEmpty();
+        assertThat(resources.get(1).getExclusiveGroupMemberFeHandles()).containsExactly(2, 3);
+        assertThat(resources.get(2).getExclusiveGroupMemberFeHandles()).containsExactly(1, 3);
+        assertThat(resources.get(3).getExclusiveGroupMemberFeHandles()).containsExactly(1, 2);
     }
 
     @Test
@@ -195,8 +195,8 @@
         Map<Integer, FrontendResource> resources =
                 mTunerResourceManagerService.getFrontendResources();
         for (int id = 0; id < infos1.length; id++) {
-            assertThat(resources.get(infos1[id].getId())
-                    .getExclusiveGroupMemberFeIds().size()).isEqualTo(0);
+            assertThat(resources.get(infos1[id].getHandle())
+                    .getExclusiveGroupMemberFeHandles().size()).isEqualTo(0);
         }
         assertThat(resources.values()).comparingElementsUsing(FR_TFI_COMPARE)
                 .containsExactlyElementsIn(Arrays.asList(infos1));
@@ -222,8 +222,8 @@
         Map<Integer, FrontendResource> resources =
                 mTunerResourceManagerService.getFrontendResources();
         for (int id = 0; id < infos1.length; id++) {
-            assertThat(resources.get(infos1[id].getId())
-                    .getExclusiveGroupMemberFeIds().size()).isEqualTo(0);
+            assertThat(resources.get(infos1[id].getHandle())
+                    .getExclusiveGroupMemberFeHandles().size()).isEqualTo(0);
         }
         assertThat(resources.values()).comparingElementsUsing(FR_TFI_COMPARE)
                 .containsExactlyElementsIn(Arrays.asList(infos1));
@@ -236,8 +236,7 @@
         int[] frontendHandle = new int[1];
         assertThat(mTunerResourceManagerService
                 .requestFrontendInternal(request, frontendHandle)).isFalse();
-        assertThat(mTunerResourceManagerService.getResourceIdFromHandle(frontendHandle[0]))
-                .isEqualTo(TunerResourceManager.INVALID_RESOURCE_HANDLE);
+        assertThat(frontendHandle[0]).isEqualTo(TunerResourceManager.INVALID_RESOURCE_HANDLE);
     }
 
     @Test
@@ -260,8 +259,7 @@
         int[] frontendHandle = new int[1];
         assertThat(mTunerResourceManagerService
                 .requestFrontendInternal(request, frontendHandle)).isFalse();
-        assertThat(mTunerResourceManagerService.getResourceIdFromHandle(frontendHandle[0]))
-                .isEqualTo(TunerResourceManager.INVALID_RESOURCE_HANDLE);
+        assertThat(frontendHandle[0]).isEqualTo(TunerResourceManager.INVALID_RESOURCE_HANDLE);
     }
 
     @Test
@@ -275,12 +273,18 @@
 
         // Init frontend resources.
         TunerFrontendInfo[] infos = new TunerFrontendInfo[3];
-        infos[0] =
-                new TunerFrontendInfo(0 /*id*/, FrontendSettings.TYPE_DVBT, 0 /*exclusiveGroupId*/);
-        infos[1] =
-                new TunerFrontendInfo(1 /*id*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/);
-        infos[2] =
-                new TunerFrontendInfo(2 /*id*/, FrontendSettings.TYPE_DVBS, 1 /*exclusiveGroupId*/);
+        infos[0] = new TunerFrontendInfo(
+                0 /*handle*/,
+                FrontendSettings.TYPE_DVBT,
+                0 /*exclusiveGroupId*/);
+        infos[1] = new TunerFrontendInfo(
+                1 /*handle*/,
+                FrontendSettings.TYPE_DVBT,
+                1 /*exclusiveGroupId*/);
+        infos[2] = new TunerFrontendInfo(
+                2 /*handle*/,
+                FrontendSettings.TYPE_DVBS,
+                1 /*exclusiveGroupId*/);
         mTunerResourceManagerService.setFrontendInfoListInternal(infos);
 
         TunerFrontendRequest request =
@@ -288,8 +292,7 @@
         int[] frontendHandle = new int[1];
         assertThat(mTunerResourceManagerService
                 .requestFrontendInternal(request, frontendHandle)).isTrue();
-        assertThat(mTunerResourceManagerService.getResourceIdFromHandle(frontendHandle[0]))
-                .isEqualTo(0);
+        assertThat(frontendHandle[0]).isEqualTo(0);
     }
 
     @Test
@@ -309,12 +312,18 @@
 
         // Init frontend resources.
         TunerFrontendInfo[] infos = new TunerFrontendInfo[3];
-        infos[0] =
-                new TunerFrontendInfo(0 /*id*/, FrontendSettings.TYPE_DVBT, 0 /*exclusiveGroupId*/);
-        infos[1] =
-                new TunerFrontendInfo(1 /*id*/, FrontendSettings.TYPE_DVBT, 1 /*exclusiveGroupId*/);
-        infos[2] =
-                new TunerFrontendInfo(2 /*id*/, FrontendSettings.TYPE_DVBS, 1 /*exclusiveGroupId*/);
+        infos[0] = new TunerFrontendInfo(
+                0 /*handle*/,
+                FrontendSettings.TYPE_DVBT,
+                0 /*exclusiveGroupId*/);
+        infos[1] = new TunerFrontendInfo(
+                1 /*handle*/,
+                FrontendSettings.TYPE_DVBT,
+                1 /*exclusiveGroupId*/);
+        infos[2] = new TunerFrontendInfo(
+                2 /*handle*/,
+                FrontendSettings.TYPE_DVBS,
+                1 /*exclusiveGroupId*/);
         mTunerResourceManagerService.setFrontendInfoListInternal(infos);
 
         int[] frontendHandle = new int[1];
@@ -322,19 +331,17 @@
                 new TunerFrontendRequest(clientId1[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
         assertThat(mTunerResourceManagerService
                 .requestFrontendInternal(request, frontendHandle)).isTrue();
-        assertThat(mTunerResourceManagerService.getResourceIdFromHandle(frontendHandle[0]))
-                .isEqualTo(infos[0].getId());
+        assertThat(frontendHandle[0]).isEqualTo(infos[0].getHandle());
 
         request =
                 new TunerFrontendRequest(clientId0[0] /*clientId*/, FrontendSettings.TYPE_DVBT);
         assertThat(mTunerResourceManagerService
                 .requestFrontendInternal(request, frontendHandle)).isTrue();
-        assertThat(mTunerResourceManagerService.getResourceIdFromHandle(frontendHandle[0]))
-                .isEqualTo(infos[1].getId());
-        assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].getId())
-                .isInUse()).isTrue();
-        assertThat(mTunerResourceManagerService.getFrontendResource(infos[2].getId())
-                .isInUse()).isTrue();
+        assertThat(frontendHandle[0]).isEqualTo(infos[1].getHandle());
+        assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].getHandle()).isInUse())
+                .isTrue();
+        assertThat(mTunerResourceManagerService.getFrontendResource(infos[2].getHandle()).isInUse())
+                .isTrue();
     }
 
     @Test
@@ -424,25 +431,23 @@
         int[] frontendHandle = new int[1];
         assertThat(mTunerResourceManagerService
                 .requestFrontendInternal(request, frontendHandle)).isTrue();
-        assertThat(mTunerResourceManagerService.getResourceIdFromHandle(frontendHandle[0]))
-                .isEqualTo(infos[0].getId());
+        assertThat(frontendHandle[0]).isEqualTo(infos[0].getHandle());
         assertThat(mTunerResourceManagerService.getClientProfile(clientId0[0])
-                .getInUseFrontendIds()).isEqualTo(
-                        new HashSet<Integer>(Arrays.asList(infos[0].getId(), infos[1].getId())));
+                .getInUseFrontendHandles()).isEqualTo(new HashSet<Integer>(Arrays.asList(
+                        infos[0].getHandle(), infos[1].getHandle())));
 
         request =
                 new TunerFrontendRequest(clientId1[0] /*clientId*/, FrontendSettings.TYPE_DVBS);
         assertThat(mTunerResourceManagerService
                 .requestFrontendInternal(request, frontendHandle)).isTrue();
-        assertThat(mTunerResourceManagerService.getResourceIdFromHandle(frontendHandle[0]))
-                .isEqualTo(infos[1].getId());
-        assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].getId())
+        assertThat(frontendHandle[0]).isEqualTo(infos[1].getHandle());
+        assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].getHandle())
                 .isInUse()).isTrue();
-        assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].getId())
+        assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].getHandle())
                 .isInUse()).isTrue();
-        assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].getId())
+        assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].getHandle())
                 .getOwnerClientId()).isEqualTo(clientId1[0]);
-        assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].getId())
+        assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].getHandle())
                 .getOwnerClientId()).isEqualTo(clientId1[0]);
         assertThat(listener.isReclaimed()).isTrue();
     }
@@ -471,20 +476,19 @@
         int[] frontendHandle = new int[1];
         assertThat(mTunerResourceManagerService
                 .requestFrontendInternal(request, frontendHandle)).isTrue();
-        int frontendId = mTunerResourceManagerService.getResourceIdFromHandle(frontendHandle[0]);
-        assertThat(frontendId).isEqualTo(infos[0].getId());
+        assertThat(frontendHandle[0]).isEqualTo(infos[0].getHandle());
         assertThat(mTunerResourceManagerService
-                .getFrontendResource(infos[1].getId()).isInUse()).isTrue();
+                .getFrontendResource(infos[1].getHandle()).isInUse()).isTrue();
 
         // Release frontend
         mTunerResourceManagerService.releaseFrontendInternal(mTunerResourceManagerService
-                .getFrontendResource(frontendId), clientId[0]);
+                .getFrontendResource(frontendHandle[0]), clientId[0]);
         assertThat(mTunerResourceManagerService
-                .getFrontendResource(frontendId).isInUse()).isFalse();
+                .getFrontendResource(frontendHandle[0]).isInUse()).isFalse();
         assertThat(mTunerResourceManagerService
-                .getFrontendResource(infos[1].getId()).isInUse()).isFalse();
+                .getFrontendResource(infos[1].getHandle()).isInUse()).isFalse();
         assertThat(mTunerResourceManagerService
-                .getClientProfile(clientId[0]).getInUseFrontendIds().size()).isEqualTo(0);
+                .getClientProfile(clientId[0]).getInUseFrontendHandles().size()).isEqualTo(0);
     }
 
     @Test
@@ -604,30 +608,28 @@
                 .setPriority(clientPriorities[1]);
 
         // Init lnb resources.
-        int[] lnbIds = {1};
-        mTunerResourceManagerService.setLnbInfoListInternal(lnbIds);
+        int[] lnbHandles = {1};
+        mTunerResourceManagerService.setLnbInfoListInternal(lnbHandles);
 
         TunerLnbRequest request = new TunerLnbRequest(clientId0[0]);
         int[] lnbHandle = new int[1];
         assertThat(mTunerResourceManagerService
                 .requestLnbInternal(request, lnbHandle)).isTrue();
-        assertThat(mTunerResourceManagerService.getResourceIdFromHandle(lnbHandle[0]))
-                .isEqualTo(lnbIds[0]);
-        assertThat(mTunerResourceManagerService.getClientProfile(clientId0[0])
-                .getInUseLnbIds()).isEqualTo(new HashSet<Integer>(Arrays.asList(lnbIds[0])));
+        assertThat(lnbHandle[0]).isEqualTo(lnbHandles[0]);
+        assertThat(mTunerResourceManagerService.getClientProfile(clientId0[0]).getInUseLnbHandles())
+                .isEqualTo(new HashSet<Integer>(Arrays.asList(lnbHandles[0])));
 
         request = new TunerLnbRequest(clientId1[0]);
         assertThat(mTunerResourceManagerService
                 .requestLnbInternal(request, lnbHandle)).isTrue();
-        assertThat(mTunerResourceManagerService.getResourceIdFromHandle(lnbHandle[0]))
-                .isEqualTo(lnbIds[0]);
-        assertThat(mTunerResourceManagerService.getLnbResource(lnbIds[0])
+        assertThat(lnbHandle[0]).isEqualTo(lnbHandles[0]);
+        assertThat(mTunerResourceManagerService.getLnbResource(lnbHandles[0])
                 .isInUse()).isTrue();
-        assertThat(mTunerResourceManagerService.getLnbResource(lnbIds[0])
+        assertThat(mTunerResourceManagerService.getLnbResource(lnbHandles[0])
                 .getOwnerClientId()).isEqualTo(clientId1[0]);
         assertThat(listener.isReclaimed()).isTrue();
         assertThat(mTunerResourceManagerService.getClientProfile(clientId0[0])
-                .getInUseLnbIds().size()).isEqualTo(0);
+                .getInUseLnbHandles().size()).isEqualTo(0);
     }
 
     @Test
@@ -642,23 +644,22 @@
         assertThat(clientId[0]).isNotEqualTo(TunerResourceManagerService.INVALID_CLIENT_ID);
 
         // Init lnb resources.
-        int[] lnbIds = {0};
-        mTunerResourceManagerService.setLnbInfoListInternal(lnbIds);
+        int[] lnbHandles = {0};
+        mTunerResourceManagerService.setLnbInfoListInternal(lnbHandles);
 
         TunerLnbRequest request = new TunerLnbRequest(clientId[0]);
         int[] lnbHandle = new int[1];
         assertThat(mTunerResourceManagerService
                 .requestLnbInternal(request, lnbHandle)).isTrue();
-        int lnbId = mTunerResourceManagerService.getResourceIdFromHandle(lnbHandle[0]);
-        assertThat(lnbId).isEqualTo(lnbIds[0]);
+        assertThat(lnbHandle[0]).isEqualTo(lnbHandles[0]);
 
         // Release lnb
         mTunerResourceManagerService.releaseLnbInternal(mTunerResourceManagerService
-                .getLnbResource(lnbId));
+                .getLnbResource(lnbHandle[0]));
         assertThat(mTunerResourceManagerService
-                .getLnbResource(lnbId).isInUse()).isFalse();
+                .getLnbResource(lnbHandle[0]).isInUse()).isFalse();
         assertThat(mTunerResourceManagerService
-                .getClientProfile(clientId[0]).getInUseLnbIds().size()).isEqualTo(0);
+                .getClientProfile(clientId[0]).getInUseLnbHandles().size()).isEqualTo(0);
     }
 
     @Test
@@ -684,18 +685,17 @@
         int[] frontendHandle = new int[1];
         assertThat(mTunerResourceManagerService
                 .requestFrontendInternal(request, frontendHandle)).isTrue();
-        assertThat(mTunerResourceManagerService.getResourceIdFromHandle(frontendHandle[0]))
-                .isEqualTo(infos[0].getId());
-        assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].getId())
+        assertThat(frontendHandle[0]).isEqualTo(infos[0].getHandle());
+        assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].getHandle())
                 .isInUse()).isTrue();
-        assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].getId())
+        assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].getHandle())
                 .isInUse()).isTrue();
 
         // Unregister client when using frontend
         mTunerResourceManagerService.unregisterClientProfileInternal(clientId[0]);
-        assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].getId())
+        assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].getHandle())
                 .isInUse()).isFalse();
-        assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].getId())
+        assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].getHandle())
                 .isInUse()).isFalse();
         assertThat(mTunerResourceManagerService.checkClientExists(clientId[0])).isFalse();
 
@@ -838,8 +838,8 @@
                 1 /*exclusiveGroupId*/);
 
         /**** Init Lnb Resources ****/
-        int[] lnbIds = {1};
-        mTunerResourceManagerService.setLnbInfoListInternal(lnbIds);
+        int[] lnbHandles = {1};
+        mTunerResourceManagerService.setLnbInfoListInternal(lnbHandles);
 
         // Update frontend list in TRM
         mTunerResourceManagerService.setFrontendInfoListInternal(infos);
@@ -856,15 +856,13 @@
         assertThat(mTunerResourceManagerService
                 .requestFrontendInternal(request, frontendHandle))
                 .isTrue();
-        assertThat(mTunerResourceManagerService
-                .getResourceIdFromHandle(frontendHandle[0]))
-                .isEqualTo(infos[0].getId());
+        assertThat(frontendHandle[0]).isEqualTo(infos[0].getHandle());
         assertThat(mTunerResourceManagerService
                 .getClientProfile(ownerClientId0[0])
-                .getInUseFrontendIds())
+                .getInUseFrontendHandles())
                 .isEqualTo(new HashSet<Integer>(Arrays.asList(
-                        infos[0].getId(),
-                        infos[1].getId())));
+                        infos[0].getHandle(),
+                        infos[1].getHandle())));
 
         /**** Share Frontend ****/
 
@@ -876,14 +874,14 @@
                 shareClientId1[0]/*selfClientId*/,
                 ownerClientId0[0]/*targetClientId*/);
         // Verify fe in use status
-        assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].getId())
+        assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].getHandle())
                 .isInUse()).isTrue();
-        assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].getId())
+        assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].getHandle())
                 .isInUse()).isTrue();
         // Verify fe owner status
-        assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].getId())
+        assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].getHandle())
                 .getOwnerClientId()).isEqualTo(ownerClientId0[0]);
-        assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].getId())
+        assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].getHandle())
                 .getOwnerClientId()).isEqualTo(ownerClientId0[0]);
         // Verify share fe client status in the primary owner client
         assertThat(mTunerResourceManagerService.getClientProfile(ownerClientId0[0])
@@ -894,22 +892,22 @@
         // Verify in use frontend list in all the primary owner and share owner clients
         assertThat(mTunerResourceManagerService
                 .getClientProfile(ownerClientId0[0])
-                .getInUseFrontendIds())
+                .getInUseFrontendHandles())
                 .isEqualTo(new HashSet<Integer>(Arrays.asList(
-                        infos[0].getId(),
-                        infos[1].getId())));
+                        infos[0].getHandle(),
+                        infos[1].getHandle())));
         assertThat(mTunerResourceManagerService
                 .getClientProfile(shareClientId0[0])
-                .getInUseFrontendIds())
+                .getInUseFrontendHandles())
                 .isEqualTo(new HashSet<Integer>(Arrays.asList(
-                        infos[0].getId(),
-                        infos[1].getId())));
+                        infos[0].getHandle(),
+                        infos[1].getHandle())));
         assertThat(mTunerResourceManagerService
                 .getClientProfile(shareClientId1[0])
-                .getInUseFrontendIds())
+                .getInUseFrontendHandles())
                 .isEqualTo(new HashSet<Integer>(Arrays.asList(
-                        infos[0].getId(),
-                        infos[1].getId())));
+                        infos[0].getHandle(),
+                        infos[1].getHandle())));
 
         /**** Remove Frontend Share Owner ****/
 
@@ -923,16 +921,16 @@
                         shareClientId0[0])));
         assertThat(mTunerResourceManagerService
                 .getClientProfile(ownerClientId0[0])
-                .getInUseFrontendIds())
+                .getInUseFrontendHandles())
                 .isEqualTo(new HashSet<Integer>(Arrays.asList(
-                        infos[0].getId(),
-                        infos[1].getId())));
+                        infos[0].getHandle(),
+                        infos[1].getHandle())));
         assertThat(mTunerResourceManagerService
                 .getClientProfile(shareClientId0[0])
-                .getInUseFrontendIds())
+                .getInUseFrontendHandles())
                 .isEqualTo(new HashSet<Integer>(Arrays.asList(
-                        infos[0].getId(),
-                        infos[1].getId())));
+                        infos[0].getHandle(),
+                        infos[1].getHandle())));
 
         /**** Request Shared Frontend with Higher Priority Client ****/
 
@@ -947,27 +945,25 @@
                 .isTrue();
 
         // Validate granted resource and internal mapping
-        assertThat(mTunerResourceManagerService
-                .getResourceIdFromHandle(frontendHandle[0]))
-                .isEqualTo(infos[0].getId());
-        assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].getId())
+        assertThat(frontendHandle[0]).isEqualTo(infos[0].getHandle());
+        assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].getHandle())
                 .getOwnerClientId()).isEqualTo(ownerClientId1[0]);
-        assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].getId())
+        assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].getHandle())
                 .getOwnerClientId()).isEqualTo(ownerClientId1[0]);
         assertThat(mTunerResourceManagerService
                 .getClientProfile(ownerClientId1[0])
-                .getInUseFrontendIds())
+                .getInUseFrontendHandles())
                 .isEqualTo(new HashSet<Integer>(Arrays.asList(
-                        infos[0].getId(),
-                        infos[1].getId())));
+                        infos[0].getHandle(),
+                        infos[1].getHandle())));
         assertThat(mTunerResourceManagerService
                 .getClientProfile(ownerClientId0[0])
-                .getInUseFrontendIds()
+                .getInUseFrontendHandles()
                 .isEmpty())
                 .isTrue();
         assertThat(mTunerResourceManagerService
                 .getClientProfile(shareClientId0[0])
-                .getInUseFrontendIds()
+                .getInUseFrontendHandles()
                 .isEmpty())
                 .isTrue();
         assertThat(mTunerResourceManagerService
@@ -987,22 +983,22 @@
 
         // Release the frontend resource from the primary owner
         mTunerResourceManagerService.releaseFrontendInternal(mTunerResourceManagerService
-                .getFrontendResource(infos[0].getId()), ownerClientId1[0]);
+                .getFrontendResource(infos[0].getHandle()), ownerClientId1[0]);
 
         // Validate the internal mapping
-        assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].getId())
+        assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].getHandle())
                 .isInUse()).isFalse();
-        assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].getId())
+        assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].getHandle())
                 .isInUse()).isFalse();
         // Verify client status
         assertThat(mTunerResourceManagerService
                 .getClientProfile(ownerClientId1[0])
-                .getInUseFrontendIds()
+                .getInUseFrontendHandles()
                 .isEmpty())
                 .isTrue();
         assertThat(mTunerResourceManagerService
                 .getClientProfile(shareClientId0[0])
-                .getInUseFrontendIds()
+                .getInUseFrontendHandles()
                 .isEmpty())
                 .isTrue();
         assertThat(mTunerResourceManagerService
@@ -1034,20 +1030,20 @@
         mTunerResourceManagerService.unregisterClientProfileInternal(ownerClientId1[0]);
 
         // Validate the internal mapping
-        assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].getId())
+        assertThat(mTunerResourceManagerService.getFrontendResource(infos[0].getHandle())
                 .isInUse()).isFalse();
-        assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].getId())
+        assertThat(mTunerResourceManagerService.getFrontendResource(infos[1].getHandle())
                 .isInUse()).isFalse();
         // Verify client status
         assertThat(mTunerResourceManagerService
                 .getClientProfile(shareClientId0[0])
-                .getInUseFrontendIds()
+                .getInUseFrontendHandles()
                 .isEmpty())
                 .isTrue();
         assertThat(mTunerResourceManagerService
                 .getClientProfile(shareClientId0[0])
-                .getInUseLnbIds())
+                .getInUseLnbHandles())
                 .isEqualTo(new HashSet<Integer>(Arrays.asList(
-                        lnbIds[0])));
+                        lnbHandles[0])));
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/utils/WatcherTest.java b/services/tests/servicestests/src/com/android/server/utils/WatcherTest.java
index 9bea9d4..7c65dc0 100644
--- a/services/tests/servicestests/src/com/android/server/utils/WatcherTest.java
+++ b/services/tests/servicestests/src/com/android/server/utils/WatcherTest.java
@@ -20,12 +20,20 @@
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.SparseArray;
+import android.util.SparseBooleanArray;
+import android.util.SparseIntArray;
+
 import androidx.test.filters.SmallTest;
 
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 
+import java.util.ArrayList;
+
 /**
  * Test class for {@link Watcher}, {@link Watchable}, {@link WatchableImpl},
  * {@link WatchedArrayMap}, {@link WatchedSparseArray}, and
@@ -40,7 +48,7 @@
     // A counter to generate unique IDs for Leaf elements.
     private int mLeafId = 0;
 
-    // Useful indices used int the tests.
+    // Useful indices used in the tests.
     private static final int INDEX_A = 1;
     private static final int INDEX_B = 2;
     private static final int INDEX_C = 3;
@@ -171,6 +179,7 @@
 
     @Test
     public void testWatchedArrayMap() {
+        final String name = "WatchedArrayMap";
         WatchableTester tester;
 
         // Create a few leaves
@@ -183,7 +192,7 @@
         WatchedArrayMap<Integer, Leaf> array = new WatchedArrayMap<>();
         array.put(INDEX_A, leafA);
         array.put(INDEX_B, leafB);
-        tester = new WatchableTester(array, "WatchedArrayMap");
+        tester = new WatchableTester(array, name);
         tester.verify(0, "Initial array - no registration");
         leafA.tick();
         tester.verify(0, "Updates with no registration");
@@ -231,20 +240,20 @@
             final WatchedArrayMap<Integer, Leaf> arraySnap = array.snapshot();
             tester.verify(14, "Generate snapshot (no changes)");
             // Verify that the snapshot is a proper copy of the source.
-            assertEquals("WatchedArrayMap snap same size",
+            assertEquals(name + " snap same size",
                          array.size(), arraySnap.size());
             for (int i = 0; i < array.size(); i++) {
                 for (int j = 0; j < arraySnap.size(); j++) {
-                    assertTrue("WatchedArrayMap elements differ",
+                    assertTrue(name + " elements differ",
                                array.valueAt(i) != arraySnap.valueAt(j));
                 }
-                assertTrue("WatchedArrayMap element copy",
+                assertTrue(name + " element copy",
                            array.valueAt(i).equals(arraySnap.valueAt(i)));
             }
             leafD.tick();
             tester.verify(15, "Tick after snapshot");
             // Verify that the snapshot is sealed
-            verifySealed("WatchedArrayMap", ()->arraySnap.put(INDEX_A, leafA));
+            verifySealed(name, ()->arraySnap.put(INDEX_A, leafA));
         }
         // Recreate the snapshot since the test corrupted it.
         {
@@ -253,10 +262,235 @@
             final Leaf arraySnapElement = arraySnap.valueAt(0);
             verifySealed("ArraySnapshotElement", ()->arraySnapElement.tick());
         }
+        // Verify copy-in/out
+        {
+            final String msg = name + " copy-in/out failed";
+            ArrayMap<Integer, Leaf> base = new ArrayMap<>();
+            array.copyTo(base);
+            WatchedArrayMap<Integer, Leaf> copy = new WatchedArrayMap<>();
+            copy.copyFrom(base);
+            if (!array.equals(copy)) {
+                fail(msg);
+            }
+        }
+    }
+
+    @Test
+    public void testWatchedArraySet() {
+        final String name = "WatchedArraySet";
+        WatchableTester tester;
+
+        // Create a few leaves
+        Leaf leafA = new Leaf();
+        Leaf leafB = new Leaf();
+        Leaf leafC = new Leaf();
+        Leaf leafD = new Leaf();
+
+        // Test WatchedArraySet
+        WatchedArraySet<Leaf> array = new WatchedArraySet<>();
+        array.add(leafA);
+        array.add(leafB);
+        tester = new WatchableTester(array, name);
+        tester.verify(0, "Initial array - no registration");
+        leafA.tick();
+        tester.verify(0, "Updates with no registration");
+        tester.register();
+        tester.verify(0, "Updates with no registration");
+        leafA.tick();
+        tester.verify(1, "Updates with registration");
+        leafB.tick();
+        tester.verify(2, "Updates with registration");
+        array.remove(leafB);
+        tester.verify(3, "Removed b");
+        leafB.tick();
+        tester.verify(3, "Updates with b not watched");
+        array.add(leafB);
+        array.add(leafB);
+        tester.verify(5, "Added b once");
+        leafB.tick();
+        tester.verify(6, "Changed b - single notification");
+        array.remove(leafB);
+        tester.verify(7, "Removed b");
+        leafB.tick();
+        tester.verify(7, "Changed b - not watched");
+        array.remove(leafB);
+        tester.verify(7, "Removed non-existent b");
+        array.clear();
+        tester.verify(8, "Cleared array");
+        leafA.tick();
+        tester.verify(8, "Change to a not in array");
+
+        // Special methods
+        array.add(leafA);
+        array.add(leafB);
+        array.add(leafC);
+        tester.verify(11, "Added a, b, c");
+        leafC.tick();
+        tester.verify(12, "Ticked c");
+        array.removeAt(array.indexOf(leafC));
+        tester.verify(13, "Removed c");
+        leafC.tick();
+        tester.verify(13, "Ticked c, not registered");
+        array.append(leafC);
+        tester.verify(14, "Append c");
+        leafC.tick();
+        leafD.tick();
+        tester.verify(15, "Ticked d and c");
+        assertEquals("Verify three elements", 3, array.size());
+
+        // Snapshot
+        {
+            final WatchedArraySet<Leaf> arraySnap = array.snapshot();
+            tester.verify(15, "Generate snapshot (no changes)");
+            // Verify that the snapshot is a proper copy of the source.
+            assertEquals(name + " snap same size",
+                         array.size(), arraySnap.size());
+            for (int i = 0; i < array.size(); i++) {
+                for (int j = 0; j < arraySnap.size(); j++) {
+                    assertTrue(name + " elements differ",
+                               array.valueAt(i) != arraySnap.valueAt(j));
+                }
+            }
+            leafC.tick();
+            tester.verify(16, "Tick after snapshot");
+            // Verify that the array snapshot is sealed
+            verifySealed(name, ()->arraySnap.add(leafB));
+        }
+        // Recreate the snapshot since the test corrupted it.
+        {
+            final WatchedArraySet<Leaf> arraySnap = array.snapshot();
+            // Verify that elements are also snapshots
+            final Leaf arraySnapElement = arraySnap.valueAt(0);
+            verifySealed(name + " snap element", ()->arraySnapElement.tick());
+        }
+        // Verify copy-in/out
+        {
+            final String msg = name + " copy-in/out";
+            ArraySet<Leaf> base = new ArraySet<>();
+            array.copyTo(base);
+            WatchedArraySet<Leaf> copy = new WatchedArraySet<>();
+            copy.copyFrom(base);
+            if (!array.equals(copy)) {
+                fail(msg);
+            }
+        }
+    }
+
+    @Test
+    public void testWatchedArrayList() {
+        final String name = "WatchedArrayList";
+        WatchableTester tester;
+
+        // Create a few leaves
+        Leaf leafA = new Leaf();
+        Leaf leafB = new Leaf();
+        Leaf leafC = new Leaf();
+        Leaf leafD = new Leaf();
+
+        // Redefine the indices used in the tests to be zero-based
+        final int indexA = 0;
+        final int indexB = 1;
+        final int indexC = 2;
+        final int indexD = 3;
+
+        // Test WatchedArrayList
+        WatchedArrayList<Leaf> array = new WatchedArrayList<>();
+        // A spacer that takes up index 0 (and is not Watchable).
+        array.add(indexA, leafA);
+        array.add(indexB, leafB);
+        tester = new WatchableTester(array, name);
+        tester.verify(0, "Initial array - no registration");
+        leafA.tick();
+        tester.verify(0, "Updates with no registration");
+        tester.register();
+        tester.verify(0, "Updates with no registration");
+        leafA.tick();
+        tester.verify(1, "Updates with registration");
+        leafB.tick();
+        tester.verify(2, "Updates with registration");
+        array.remove(indexB);
+        tester.verify(3, "Removed b");
+        leafB.tick();
+        tester.verify(3, "Updates with b not watched");
+        array.add(indexB, leafB);
+        array.add(indexC, leafB);
+        tester.verify(5, "Added b twice");
+        leafB.tick();
+        tester.verify(6, "Changed b - single notification");
+        array.remove(indexC);
+        tester.verify(7, "Removed first b");
+        leafB.tick();
+        tester.verify(8, "Changed b - single notification");
+        array.remove(indexB);
+        tester.verify(9, "Removed second b");
+        leafB.tick();
+        tester.verify(9, "Updated leafB - no change");
+        array.clear();
+        tester.verify(10, "Cleared array");
+        leafB.tick();
+        tester.verify(10, "Change to b not in array");
+
+        // Special methods
+        array.add(indexA, leafA);
+        array.add(indexB, leafB);
+        array.add(indexC, leafC);
+        tester.verify(13, "Added c");
+        leafC.tick();
+        tester.verify(14, "Ticked c");
+        array.set(array.indexOf(leafC), leafD);
+        tester.verify(15, "Replaced c with d");
+        leafC.tick();
+        leafD.tick();
+        tester.verify(16, "Ticked d and c (c not registered)");
+        array.add(leafC);
+        tester.verify(17, "Append c");
+        leafC.tick();
+        leafD.tick();
+        tester.verify(19, "Ticked d and c");
+
+        // Snapshot
+        {
+            final WatchedArrayList<Leaf> arraySnap = array.snapshot();
+            tester.verify(19, "Generate snapshot (no changes)");
+            // Verify that the snapshot is a proper copy of the source.
+            assertEquals(name + " snap same size",
+                         array.size(), arraySnap.size());
+            for (int i = 0; i < array.size(); i++) {
+                for (int j = 0; j < arraySnap.size(); j++) {
+                    assertTrue(name + " elements differ",
+                               array.get(i) != arraySnap.get(j));
+                }
+                assertTrue(name + " element copy",
+                           array.get(i).equals(arraySnap.get(i)));
+            }
+            leafD.tick();
+            tester.verify(20, "Tick after snapshot");
+            // Verify that the array snapshot is sealed
+            verifySealed(name, ()->arraySnap.add(indexA, leafB));
+        }
+        // Recreate the snapshot since the test corrupted it.
+        {
+            final WatchedArrayList<Leaf> arraySnap = array.snapshot();
+            // Verify that elements are also snapshots
+            final Leaf arraySnapElement = arraySnap.get(0);
+            verifySealed("ArraySnapshotElement", ()->arraySnapElement.tick());
+        }
+        // Verify copy-in/out
+        {
+            final String msg = name + " copy-in/out";
+            ArrayList<Leaf> base = new ArrayList<>();
+            array.copyTo(base);
+            WatchedArrayList<Leaf> copy = new WatchedArrayList<>();
+            copy.copyFrom(base);
+            if (!array.equals(copy)) {
+                fail(msg);
+            }
+        }
     }
 
     @Test
     public void testWatchedSparseArray() {
+        final String name = "WatchedSparseArray";
         WatchableTester tester;
 
         // Create a few leaves
@@ -269,7 +503,7 @@
         WatchedSparseArray<Leaf> array = new WatchedSparseArray<>();
         array.put(INDEX_A, leafA);
         array.put(INDEX_B, leafB);
-        tester = new WatchableTester(array, "WatchedSparseArray");
+        tester = new WatchableTester(array, name);
         tester.verify(0, "Initial array - no registration");
         leafA.tick();
         tester.verify(0, "Updates with no registration");
@@ -338,20 +572,20 @@
             final WatchedSparseArray<Leaf> arraySnap = array.snapshot();
             tester.verify(22, "Generate snapshot (no changes)");
             // Verify that the snapshot is a proper copy of the source.
-            assertEquals("WatchedSparseArray snap same size",
+            assertEquals(name + " snap same size",
                          array.size(), arraySnap.size());
             for (int i = 0; i < array.size(); i++) {
                 for (int j = 0; j < arraySnap.size(); j++) {
-                    assertTrue("WatchedSparseArray elements differ",
+                    assertTrue(name + " elements differ",
                                array.valueAt(i) != arraySnap.valueAt(j));
                 }
-                assertTrue("WatchedArrayMap element copy",
+                assertTrue(name + " element copy",
                            array.valueAt(i).equals(arraySnap.valueAt(i)));
             }
             leafD.tick();
             tester.verify(23, "Tick after snapshot");
             // Verify that the array snapshot is sealed
-            verifySealed("WatchedSparseArray", ()->arraySnap.put(INDEX_A, leafB));
+            verifySealed(name, ()->arraySnap.put(INDEX_A, leafB));
         }
         // Recreate the snapshot since the test corrupted it.
         {
@@ -360,15 +594,30 @@
             final Leaf arraySnapElement = arraySnap.valueAt(0);
             verifySealed("ArraySnapshotElement", ()->arraySnapElement.tick());
         }
+        // Verify copy-in/out
+        {
+            final String msg = name + " copy-in/out";
+            SparseArray<Leaf> base = new SparseArray<>();
+            array.copyTo(base);
+            WatchedSparseArray<Leaf> copy = new WatchedSparseArray<>();
+            copy.copyFrom(base);
+            final int end = array.size();
+            assertTrue(msg + " size mismatch " + end + " " + copy.size(), end == copy.size());
+            for (int i = 0; i < end; i++) {
+                final int key = array.keyAt(i);
+                assertTrue(msg, array.get(i) == copy.get(i));
+            }
+        }
     }
 
     @Test
     public void testWatchedSparseBooleanArray() {
+        final String name = "WatchedSparseBooleanArray";
         WatchableTester tester;
 
         // Test WatchedSparseBooleanArray
         WatchedSparseBooleanArray array = new WatchedSparseBooleanArray();
-        tester = new WatchableTester(array, "WatchedSparseBooleanArray");
+        tester = new WatchableTester(array, name);
         tester.verify(0, "Initial array - no registration");
         array.put(INDEX_A, true);
         tester.verify(0, "Updates with no registration");
@@ -376,14 +625,10 @@
         tester.verify(0, "Updates with no registration");
         array.put(INDEX_B, true);
         tester.verify(1, "Updates with registration");
-        array.put(INDEX_B, true);
-        tester.verify(1, "Null update");
         array.put(INDEX_B, false);
         array.put(INDEX_C, true);
         tester.verify(3, "Updates with registration");
         // Special methods
-        array.put(INDEX_C, true);
-        tester.verify(3, "Added true, no change");
         array.setValueAt(array.indexOfKey(INDEX_C), false);
         tester.verify(4, "Replaced true with false");
         array.append(INDEX_D, true);
@@ -403,7 +648,77 @@
             array.put(INDEX_D, false);
             tester.verify(6, "Tick after snapshot");
             // Verify that the array is sealed
-            verifySealed("WatchedSparseBooleanArray", ()->arraySnap.put(INDEX_D, false));
+            verifySealed(name, ()->arraySnap.put(INDEX_D, false));
+        }
+        // Verify copy-in/out
+        {
+            final String msg = name + " copy-in/out";
+            SparseBooleanArray base = new SparseBooleanArray();
+            array.copyTo(base);
+            WatchedSparseBooleanArray copy = new WatchedSparseBooleanArray();
+            copy.copyFrom(base);
+            final int end = array.size();
+            assertTrue(msg + " size mismatch/2 " + end + " " + copy.size(), end == copy.size());
+            for (int i = 0; i < end; i++) {
+                final int key = array.keyAt(i);
+                assertTrue(msg + " element", array.get(i) == copy.get(i));
+            }
+        }
+    }
+
+    @Test
+    public void testWatchedSparseIntArray() {
+        final String name = "WatchedSparseIntArray";
+        WatchableTester tester;
+
+        // Test WatchedSparseIntArray
+        WatchedSparseIntArray array = new WatchedSparseIntArray();
+        tester = new WatchableTester(array, name);
+        tester.verify(0, "Initial array - no registration");
+        array.put(INDEX_A, 1);
+        tester.verify(0, "Updates with no registration");
+        tester.register();
+        tester.verify(0, "Updates with no registration");
+        array.put(INDEX_B, 2);
+        tester.verify(1, "Updates with registration");
+        array.put(INDEX_B, 4);
+        array.put(INDEX_C, 5);
+        tester.verify(3, "Updates with registration");
+        // Special methods
+        array.setValueAt(array.indexOfKey(INDEX_C), 7);
+        tester.verify(4, "Replaced 6 with 7");
+        array.append(INDEX_D, 8);
+        tester.verify(5, "Append 8");
+
+        // Snapshot
+        {
+            WatchedSparseIntArray arraySnap = array.snapshot();
+            tester.verify(5, "Generate snapshot");
+            // Verify that the snapshot is a proper copy of the source.
+            assertEquals("WatchedSparseIntArray snap same size",
+                         array.size(), arraySnap.size());
+            for (int i = 0; i < array.size(); i++) {
+                assertEquals(name + " element copy",
+                             array.valueAt(i), arraySnap.valueAt(i));
+            }
+            array.put(INDEX_D, 9);
+            tester.verify(6, "Tick after snapshot");
+            // Verify that the array is sealed
+            verifySealed(name, ()->arraySnap.put(INDEX_D, 10));
+        }
+        // Verify copy-in/out
+        {
+            final String msg = name + " copy-in/out";
+            SparseIntArray base = new SparseIntArray();
+            array.copyTo(base);
+            WatchedSparseIntArray copy = new WatchedSparseIntArray();
+            copy.copyFrom(base);
+            final int end = array.size();
+            assertTrue(msg + " size mismatch " + end + " " + copy.size(), end == copy.size());
+            for (int i = 0; i < end; i++) {
+                final int key = array.keyAt(i);
+                assertTrue(msg, array.get(i) == copy.get(i));
+            }
         }
     }
 }
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java
new file mode 100644
index 0000000..afcf08ef
--- /dev/null
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationListenersTest.java
@@ -0,0 +1,242 @@
+/*
+ * 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.notification;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.app.INotificationManager;
+import android.content.ComponentName;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.service.notification.NotificationListenerFilter;
+import android.util.ArraySet;
+import android.util.Pair;
+import android.util.TypedXmlPullParser;
+import android.util.TypedXmlSerializer;
+import android.util.Xml;
+
+import com.android.server.UiServiceTestCase;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+public class NotificationListenersTest extends UiServiceTestCase {
+
+    @Mock
+    private PackageManager mPm;
+    @Mock
+    private IPackageManager miPm;
+
+    @Mock
+    NotificationManagerService mNm;
+    @Mock
+    private INotificationManager mINm;
+
+    NotificationManagerService.NotificationListeners mListeners;
+
+    private ComponentName mCn1 = new ComponentName("pkg", "pkg.cmp");
+    private ComponentName mCn2 = new ComponentName("pkg2", "pkg2.cmp2");
+
+
+    @Before
+    public void setUp() throws Exception {
+        MockitoAnnotations.initMocks(this);
+        getContext().setMockPackageManager(mPm);
+
+        mListeners = spy(mNm.new NotificationListeners(
+                mContext, new Object(), mock(ManagedServices.UserProfiles.class), miPm));
+        when(mNm.getBinderService()).thenReturn(mINm);
+    }
+
+    @Test
+    public void testReadExtraTag() throws Exception {
+        String xml = "<req_listeners>"
+                + "<listener component=\"" + mCn1.flattenToString() + "\" user=\"0\">"
+                + "<allowed types=\"7\" />"
+                + "<disallowed pkgs=\"\" />"
+                + "</listener>"
+                + "<listener component=\"" + mCn2.flattenToString() + "\" user=\"10\">"
+                + "<allowed types=\"4\" />"
+                + "<disallowed pkgs=\"something\" />"
+                + "</listener>"
+                + "</req_listeners>";
+
+        TypedXmlPullParser parser = Xml.newFastPullParser();
+        parser.setInput(new BufferedInputStream(
+                new ByteArrayInputStream(xml.getBytes())), null);
+        parser.nextTag();
+        mListeners.readExtraTag("req_listeners", parser);
+
+        validateListenersFromXml();
+    }
+
+    @Test
+    public void testWriteExtraTag() throws Exception {
+        NotificationListenerFilter nlf = new NotificationListenerFilter(7, new ArraySet<>());
+        NotificationListenerFilter nlf2 =
+                new NotificationListenerFilter(4, new ArraySet<>(new String[] {"something"}));
+        mListeners.setNotificationListenerFilter(Pair.create(mCn1, 0), nlf);
+        mListeners.setNotificationListenerFilter(Pair.create(mCn2, 10), nlf2);
+
+        TypedXmlSerializer serializer = Xml.newFastSerializer();
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        serializer.setOutput(new BufferedOutputStream(baos), "utf-8");
+        serializer.startDocument(null, true);
+        mListeners.writeExtraXmlTags(serializer);
+        serializer.endDocument();
+        serializer.flush();
+
+        TypedXmlPullParser parser = Xml.newFastPullParser();
+        parser.setInput(new BufferedInputStream(
+                new ByteArrayInputStream(baos.toByteArray())), null);
+        parser.nextTag();
+        mListeners.readExtraTag("req_listeners", parser);
+
+        validateListenersFromXml();
+    }
+
+    private void validateListenersFromXml() {
+        assertThat(mListeners.getNotificationListenerFilter(Pair.create(mCn1, 0)).getTypes())
+                .isEqualTo(7);
+        assertThat(mListeners.getNotificationListenerFilter(Pair.create(mCn1, 0))
+                .getDisallowedPackages())
+                .isEmpty();
+
+        assertThat(mListeners.getNotificationListenerFilter(Pair.create(mCn2, 10)).getTypes())
+                .isEqualTo(4);
+        assertThat(mListeners.getNotificationListenerFilter(Pair.create(mCn2, 10))
+                .getDisallowedPackages())
+                .contains("something");
+    }
+
+    @Test
+    public void testOnUserRemoved() {
+        NotificationListenerFilter nlf = new NotificationListenerFilter(7, new ArraySet<>());
+        NotificationListenerFilter nlf2 =
+                new NotificationListenerFilter(4, new ArraySet<>(new String[] {"something"}));
+        mListeners.setNotificationListenerFilter(Pair.create(mCn1, 0), nlf);
+        mListeners.setNotificationListenerFilter(Pair.create(mCn2, 10), nlf2);
+
+        mListeners.onUserRemoved(0);
+
+        assertThat(mListeners.getNotificationListenerFilter(Pair.create(mCn1, 0))).isNull();
+        assertThat(mListeners.getNotificationListenerFilter(Pair.create(mCn2, 10)).getTypes())
+                .isEqualTo(4);
+    }
+
+    @Test
+    public void testOnUserUnlocked() {
+        // one exists already, say from xml
+        NotificationListenerFilter nlf =
+                new NotificationListenerFilter(4, new ArraySet<>(new String[] {"something"}));
+        mListeners.setNotificationListenerFilter(Pair.create(mCn2, 0), nlf);
+
+        // new service exists or backfilling on upgrade to S
+        ServiceInfo si = new ServiceInfo();
+        si.permission = mListeners.getConfig().bindPermission;
+        si.packageName = "new";
+        si.name = "comp";
+        ResolveInfo ri = new ResolveInfo();
+        ri.serviceInfo = si;
+
+        // incorrect service
+        ServiceInfo si2 = new ServiceInfo();
+        ResolveInfo ri2 = new ResolveInfo();
+        ri2.serviceInfo = si2;
+        si2.packageName = "new2";
+        si2.name = "comp2";
+
+        List<ResolveInfo> ris = new ArrayList<>();
+        ris.add(ri);
+        ris.add(ri2);
+
+        when(mPm.queryIntentServicesAsUser(any(), anyInt(), anyInt())).thenReturn(ris);
+
+        mListeners.onUserUnlocked(0);
+
+        assertThat(mListeners.getNotificationListenerFilter(Pair.create(mCn2, 0)).getTypes())
+                .isEqualTo(4);
+        assertThat(mListeners.getNotificationListenerFilter(Pair.create(mCn2, 0))
+                .getDisallowedPackages())
+                .contains("something");
+
+        assertThat(mListeners.getNotificationListenerFilter(
+                Pair.create(si.getComponentName(), 0)).getTypes())
+                .isEqualTo(15);
+        assertThat(mListeners.getNotificationListenerFilter(Pair.create(si.getComponentName(), 0))
+                .getDisallowedPackages())
+                .isEmpty();
+
+        assertThat(mListeners.getNotificationListenerFilter(Pair.create(si2.getComponentName(), 0)))
+                .isNull();
+
+    }
+
+    @Test
+    public void testOnPackageChanged() {
+        NotificationListenerFilter nlf = new NotificationListenerFilter(7, new ArraySet<>());
+        NotificationListenerFilter nlf2 =
+                new NotificationListenerFilter(4, new ArraySet<>(new String[] {"something"}));
+        mListeners.setNotificationListenerFilter(Pair.create(mCn1, 0), nlf);
+        mListeners.setNotificationListenerFilter(Pair.create(mCn2, 10), nlf2);
+
+        String[] pkgs = new String[] {mCn1.getPackageName()};
+        int[] uids = new int[] {1};
+        mListeners.onPackagesChanged(false, pkgs, uids);
+
+        // not removing; no change
+        assertThat(mListeners.getNotificationListenerFilter(Pair.create(mCn1, 0)).getTypes())
+                .isEqualTo(7);
+        assertThat(mListeners.getNotificationListenerFilter(Pair.create(mCn2, 10)).getTypes())
+                .isEqualTo(4);
+    }
+
+    @Test
+    public void testOnPackageChanged_removing() {
+        NotificationListenerFilter nlf = new NotificationListenerFilter(7, new ArraySet<>());
+        NotificationListenerFilter nlf2 =
+                new NotificationListenerFilter(4, new ArraySet<>(new String[] {"something"}));
+        mListeners.setNotificationListenerFilter(Pair.create(mCn1, 0), nlf);
+        mListeners.setNotificationListenerFilter(Pair.create(mCn2, 0), nlf2);
+
+        String[] pkgs = new String[] {mCn1.getPackageName()};
+        int[] uids = new int[] {1};
+        mListeners.onPackagesChanged(true, pkgs, uids);
+
+        // only mCn1 removed
+        assertThat(mListeners.getNotificationListenerFilter(Pair.create(mCn1, 0))).isNull();
+        assertThat(mListeners.getNotificationListenerFilter(Pair.create(mCn2, 0)).getTypes())
+                .isEqualTo(4);
+    }
+
+}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index a18bce7..bd622e1 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -55,6 +55,7 @@
 import static android.os.UserHandle.USER_SYSTEM;
 import static android.service.notification.Adjustment.KEY_IMPORTANCE;
 import static android.service.notification.Adjustment.KEY_USER_SENTIMENT;
+import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ALERTING;
 import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE;
 import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL;
 
@@ -143,6 +144,7 @@
 import android.provider.Settings;
 import android.service.notification.Adjustment;
 import android.service.notification.ConversationChannelWrapper;
+import android.service.notification.NotificationListenerFilter;
 import android.service.notification.NotificationListenerService;
 import android.service.notification.NotificationStats;
 import android.service.notification.StatusBarNotification;
@@ -269,6 +271,8 @@
 
     @Mock
     private NotificationListeners mListeners;
+    @Mock
+    private NotificationListenerFilter mNlf;
     @Mock private NotificationAssistants mAssistants;
     @Mock private ConditionProviders mConditionProviders;
     private ManagedServices.ManagedServiceInfo mListener;
@@ -459,6 +463,10 @@
         mPolicyFile.finishWrite(fos);
 
         // Setup managed services
+        when(mNlf.isTypeAllowed(anyInt())).thenReturn(true);
+        when(mNlf.isPackageAllowed(anyString())).thenReturn(true);
+        when(mNlf.isPackageAllowed(null)).thenReturn(true);
+        when(mListeners.getNotificationListenerFilter(any())).thenReturn(mNlf);
         mListener = mListeners.new ManagedServiceInfo(
                 null, new ComponentName(PKG, "test_class"),
                 UserHandle.getUserId(mUid), true, null, 0);
@@ -3596,7 +3604,7 @@
 
         mService.mNotificationDelegate.onNotificationDirectReplied(r.getKey());
         assertTrue(mService.getNotificationRecord(r.getKey()).getStats().hasDirectReplied());
-        verify(mAssistants).notifyAssistantNotificationDirectReplyLocked(eq(r.getSbn()));
+        verify(mAssistants).notifyAssistantNotificationDirectReplyLocked(eq(r));
 
         assertEquals(1, mNotificationRecordLogger.numCalls());
         assertEquals(NotificationRecordLogger.NotificationEvent.NOTIFICATION_DIRECT_REPLIED,
@@ -3610,14 +3618,14 @@
 
         mService.mNotificationDelegate.onNotificationExpansionChanged(r.getKey(), true, true,
                 NOTIFICATION_LOCATION_UNKNOWN);
-        verify(mAssistants).notifyAssistantExpansionChangedLocked(eq(r.getSbn()), eq(true),
-                eq((true)));
+        verify(mAssistants).notifyAssistantExpansionChangedLocked(eq(r.getSbn()),
+                eq(FLAG_FILTER_TYPE_ALERTING), eq(true), eq((true)));
         assertTrue(mService.getNotificationRecord(r.getKey()).getStats().hasExpanded());
 
         mService.mNotificationDelegate.onNotificationExpansionChanged(r.getKey(), true, false,
                 NOTIFICATION_LOCATION_UNKNOWN);
-        verify(mAssistants).notifyAssistantExpansionChangedLocked(eq(r.getSbn()), eq(true),
-                eq((false)));
+        verify(mAssistants).notifyAssistantExpansionChangedLocked(eq(r.getSbn()),
+                eq(FLAG_FILTER_TYPE_ALERTING), eq(true), eq((false)));
         assertTrue(mService.getNotificationRecord(r.getKey()).getStats().hasExpanded());
 
         assertEquals(2, mNotificationRecordLogger.numCalls());
@@ -3635,14 +3643,14 @@
         mService.mNotificationDelegate.onNotificationExpansionChanged(r.getKey(), false, true,
                 NOTIFICATION_LOCATION_UNKNOWN);
         assertFalse(mService.getNotificationRecord(r.getKey()).getStats().hasExpanded());
-        verify(mAssistants).notifyAssistantExpansionChangedLocked(eq(r.getSbn()), eq(false),
-                eq((true)));
+        verify(mAssistants).notifyAssistantExpansionChangedLocked(eq(r.getSbn()),
+                eq(FLAG_FILTER_TYPE_ALERTING), eq(false), eq((true)));
 
         mService.mNotificationDelegate.onNotificationExpansionChanged(r.getKey(), false, false,
                 NOTIFICATION_LOCATION_UNKNOWN);
         assertFalse(mService.getNotificationRecord(r.getKey()).getStats().hasExpanded());
         verify(mAssistants).notifyAssistantExpansionChangedLocked(
-                eq(r.getSbn()), eq(false), eq((false)));
+                eq(r.getSbn()), eq(FLAG_FILTER_TYPE_ALERTING), eq(false), eq((false)));
     }
 
     @Test
@@ -3662,11 +3670,11 @@
         final NotificationVisibility nv = NotificationVisibility.obtain(r.getKey(), 1, 2, true);
         mService.mNotificationDelegate.onNotificationVisibilityChanged(
                 new NotificationVisibility[] {nv}, new NotificationVisibility[]{});
-        verify(mAssistants).notifyAssistantVisibilityChangedLocked(eq(r.getSbn()), eq(true));
+        verify(mAssistants).notifyAssistantVisibilityChangedLocked(eq(r), eq(true));
         assertTrue(mService.getNotificationRecord(r.getKey()).getStats().hasSeen());
         mService.mNotificationDelegate.onNotificationVisibilityChanged(
                 new NotificationVisibility[] {}, new NotificationVisibility[]{nv});
-        verify(mAssistants).notifyAssistantVisibilityChangedLocked(eq(r.getSbn()), eq(false));
+        verify(mAssistants).notifyAssistantVisibilityChangedLocked(eq(r), eq(false));
         assertTrue(mService.getNotificationRecord(r.getKey()).getStats().hasSeen());
     }
 
@@ -5324,7 +5332,7 @@
                 r.getKey(), replyIndex, reply, NOTIFICATION_LOCATION_UNKNOWN,
                 modifiedBeforeSending);
         verify(mAssistants).notifyAssistantSuggestedReplySent(
-                eq(r.getSbn()), eq(reply), eq(generatedByAssistant));
+                eq(r.getSbn()), eq(FLAG_FILTER_TYPE_ALERTING), eq(reply), eq(generatedByAssistant));
         assertEquals(1, mNotificationRecordLogger.numCalls());
         assertEquals(NotificationRecordLogger.NotificationEvent.NOTIFICATION_SMART_REPLIED,
                 mNotificationRecordLogger.event(0));
@@ -5346,7 +5354,7 @@
                 10, 10, r.getKey(), actionIndex, action, notificationVisibility,
                 generatedByAssistant);
         verify(mAssistants).notifyAssistantActionClicked(
-                eq(r.getSbn()), eq(action), eq(generatedByAssistant));
+                eq(r), eq(action), eq(generatedByAssistant));
 
         assertEquals(1, mNotificationRecordLogger.numCalls());
         assertEquals(
@@ -5370,7 +5378,7 @@
                 10, 10, r.getKey(), actionIndex, action, notificationVisibility,
                 generatedByAssistant);
         verify(mAssistants).notifyAssistantActionClicked(
-                eq(r.getSbn()), eq(action), eq(generatedByAssistant));
+                eq(r), eq(action), eq(generatedByAssistant));
 
         assertEquals(1, mNotificationRecordLogger.numCalls());
         assertEquals(
@@ -7249,7 +7257,7 @@
         when(info.enabledAndUserMatches(info.userid)).thenReturn(false);
         when(mAssistants.checkServiceTokenLocked(any())).thenReturn(assistant);
 
-        assertFalse(mService.isVisibleToListener(sbn, info));
+        assertFalse(mService.isVisibleToListener(sbn, 0, info));
     }
 
     @Test
@@ -7262,7 +7270,7 @@
         when(info.enabledAndUserMatches(info.userid)).thenReturn(true);
         when(mAssistants.checkServiceTokenLocked(any())).thenReturn(null);
 
-        assertTrue(mService.isVisibleToListener(sbn, info));
+        assertTrue(mService.isVisibleToListener(sbn, 0, info));
     }
 
     @Test
@@ -7277,7 +7285,7 @@
         when(info.enabledAndUserMatches(info.userid)).thenReturn(true);
         when(mAssistants.checkServiceTokenLocked(any())).thenReturn(assistant);
 
-        assertFalse(mService.isVisibleToListener(sbn, info));
+        assertFalse(mService.isVisibleToListener(sbn, 0, info));
     }
 
     @Test
@@ -7292,7 +7300,42 @@
         when(info.enabledAndUserMatches(info.userid)).thenReturn(true);
         when(mAssistants.checkServiceTokenLocked(any())).thenReturn(assistant);
 
-        assertTrue(mService.isVisibleToListener(sbn, info));
+        assertTrue(mService.isVisibleToListener(sbn, 0, info));
+    }
+
+    @Test
+    public void testIsVisibleToListener_mismatchedType() {
+        when(mNlf.isTypeAllowed(anyInt())).thenReturn(false);
+
+        StatusBarNotification sbn = mock(StatusBarNotification.class);
+        when(sbn.getUserId()).thenReturn(10);
+        ManagedServices.ManagedServiceInfo info = mock(ManagedServices.ManagedServiceInfo.class);
+        ManagedServices.ManagedServiceInfo assistant = mock(ManagedServices.ManagedServiceInfo.class);
+        info.userid = 10;
+        when(info.isSameUser(anyInt())).thenReturn(true);
+        when(assistant.isSameUser(anyInt())).thenReturn(true);
+        when(info.enabledAndUserMatches(info.userid)).thenReturn(true);
+        when(mAssistants.checkServiceTokenLocked(any())).thenReturn(assistant);
+
+        assertFalse(mService.isVisibleToListener(sbn, 0, info));
+    }
+
+    @Test
+    public void testIsVisibleToListener_disallowedPackage() {
+        when(mNlf.isPackageAllowed(null)).thenReturn(false);
+
+        StatusBarNotification sbn = mock(StatusBarNotification.class);
+        when(sbn.getUserId()).thenReturn(10);
+        ManagedServices.ManagedServiceInfo info = mock(ManagedServices.ManagedServiceInfo.class);
+        ManagedServices.ManagedServiceInfo assistant =
+                mock(ManagedServices.ManagedServiceInfo.class);
+        info.userid = 10;
+        when(info.isSameUser(anyInt())).thenReturn(true);
+        when(assistant.isSameUser(anyInt())).thenReturn(true);
+        when(info.enabledAndUserMatches(info.userid)).thenReturn(true);
+        when(mAssistants.checkServiceTokenLocked(any())).thenReturn(assistant);
+
+        assertFalse(mService.isVisibleToListener(sbn, 0, info));
     }
 
     @Test
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
index 976f408..da613e6 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
@@ -21,6 +21,9 @@
 import static android.app.NotificationManager.IMPORTANCE_LOW;
 import static android.service.notification.Adjustment.KEY_IMPORTANCE;
 import static android.service.notification.Adjustment.KEY_NOT_CONVERSATION;
+import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_ALERTING;
+import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_CONVERSATIONS;
+import static android.service.notification.NotificationListenerService.FLAG_FILTER_TYPE_SILENT;
 import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE;
 import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEUTRAL;
 import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_POSITIVE;
@@ -910,11 +913,13 @@
         record.setAssistantImportance(IMPORTANCE_LOW);
         record.calculateImportance();
         assertEquals(IMPORTANCE_LOW, record.getImportance());
+        assertEquals(FLAG_FILTER_TYPE_SILENT, record.getNotificationType());
 
         record.updateNotificationChannel(
                 new NotificationChannel(channelId, "", IMPORTANCE_DEFAULT));
 
         assertEquals(IMPORTANCE_LOW, record.getImportance());
+        assertEquals(FLAG_FILTER_TYPE_SILENT, record.getNotificationType());
     }
 
     @Test
@@ -1125,6 +1130,7 @@
         record.setShortcutInfo(mock(ShortcutInfo.class));
 
         assertTrue(record.isConversation());
+        assertEquals(FLAG_FILTER_TYPE_CONVERSATIONS, record.getNotificationType());
     }
 
     @Test
@@ -1134,6 +1140,7 @@
         record.setShortcutInfo(null);
 
         assertTrue(record.isConversation());
+        assertEquals(FLAG_FILTER_TYPE_CONVERSATIONS, record.getNotificationType());
     }
 
     @Test
@@ -1144,6 +1151,7 @@
         record.setHasSentValidMsg(true);
 
         assertFalse(record.isConversation());
+        assertEquals(FLAG_FILTER_TYPE_ALERTING, record.getNotificationType());
     }
 
     @Test
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
index cfdd246..57d5323 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java
@@ -58,6 +58,7 @@
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
@@ -101,7 +102,6 @@
 
 import com.android.internal.R;
 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
-import com.android.internal.util.FastXmlSerializer;
 import com.android.server.UiServiceTestCase;
 import com.android.server.notification.ManagedServices.UserProfiles;
 
@@ -110,9 +110,7 @@
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
-import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
 
 import java.io.BufferedInputStream;
 import java.io.BufferedOutputStream;
@@ -134,9 +132,13 @@
     private static final String EVENTS_DEFAULT_RULE_ID = "EVENTS_DEFAULT_RULE";
     private static final String SCHEDULE_DEFAULT_RULE_ID = "EVERY_NIGHT_DEFAULT_RULE";
     private static final int ZEN_MODE_FOR_TESTING = 99;
+    private static final String CUSTOM_PKG_NAME = "not.android";
+    private static final int CUSTOM_PKG_UID = 1;
+    private static final String CUSTOM_RULE_ID = "custom_rule";
 
     ConditionProviders mConditionProviders;
     @Mock NotificationManager mNotificationManager;
+    @Mock PackageManager mPackageManager;
     private Resources mResources;
     private TestableLooper mTestableLooper;
     private ZenModeHelper mZenModeHelperSpy;
@@ -146,7 +148,7 @@
     private WrappedSysUiStatsEvent.WrappedBuilderFactory mStatsEventBuilderFactory;
 
     @Before
-    public void setUp() {
+    public void setUp() throws PackageManager.NameNotFoundException {
         MockitoAnnotations.initMocks(this);
 
         mTestableLooper = TestableLooper.get(this);
@@ -169,6 +171,10 @@
         mConditionProviders.addSystemProvider(new CountdownConditionProvider());
         mZenModeHelperSpy = spy(new ZenModeHelper(mContext, mTestableLooper.getLooper(),
                 mConditionProviders, mStatsEventBuilderFactory));
+
+        when(mPackageManager.getPackageUidAsUser(eq(CUSTOM_PKG_NAME), anyInt()))
+                .thenReturn(CUSTOM_PKG_UID);
+        mZenModeHelperSpy.mPm = mPackageManager;
     }
 
     private XmlResourceParser getDefaultConfigParser() throws IOException, XmlPullParserException {
@@ -238,19 +244,24 @@
 
     private ArrayMap<String, ZenModeConfig.ZenRule> getCustomAutomaticRules(int zenMode) {
         ArrayMap<String, ZenModeConfig.ZenRule> automaticRules = new ArrayMap<>();
+        ZenModeConfig.ZenRule rule = createCustomAutomaticRule(zenMode, CUSTOM_RULE_ID);
+        automaticRules.put(rule.id, rule);
+        return automaticRules;
+    }
+
+    private ZenModeConfig.ZenRule createCustomAutomaticRule(int zenMode, String id) {
         ZenModeConfig.ZenRule customRule = new ZenModeConfig.ZenRule();
         final ScheduleInfo customRuleInfo = new ScheduleInfo();
         customRule.enabled = true;
         customRule.creationTime = 0;
-        customRule.id = "customRule";
-        customRule.name = "Custom Rule";
+        customRule.id = id;
+        customRule.name = "Custom Rule with id=" + id;
         customRule.zenMode = zenMode;
         customRule.conditionId = ZenModeConfig.toScheduleConditionId(customRuleInfo);
         customRule.configurationActivity =
-                new ComponentName("not.android", "ScheduleConditionProvider");
+                new ComponentName(CUSTOM_PKG_NAME, "ScheduleConditionProvider");
         customRule.pkg = customRule.configurationActivity.getPackageName();
-        automaticRules.put("customRule", customRule);
-        return automaticRules;
+        return customRule;
     }
 
     @Test
@@ -893,7 +904,7 @@
             if (builder.getAtomId() == DND_MODE_RULE) {
                 if (ZEN_MODE_FOR_TESTING == builder.getInt(ZEN_MODE_FIELD_NUMBER)) {
                     foundCustomEvent = true;
-                    assertEquals(0, builder.getInt(UID_FIELD_NUMBER));
+                    assertEquals(CUSTOM_PKG_UID, builder.getInt(UID_FIELD_NUMBER));
                     assertTrue(builder.getBoolean(ENABLED_FIELD_NUMBER));
                 }
             } else {
@@ -904,6 +915,46 @@
     }
 
     @Test
+    public void ruleUidsCached() throws Exception {
+        setupZenConfig();
+        // one enabled automatic rule
+        mZenModeHelperSpy.mConfig.automaticRules = getCustomAutomaticRules();
+        List<StatsEvent> events = new LinkedList<>();
+        // first time retrieving uid:
+        mZenModeHelperSpy.pullRules(events);
+        verify(mPackageManager, atLeastOnce()).getPackageUidAsUser(anyString(), anyInt());
+
+        // second time retrieving uid:
+        reset(mPackageManager);
+        mZenModeHelperSpy.pullRules(events);
+        verify(mPackageManager, never()).getPackageUidAsUser(anyString(), anyInt());
+
+        // new rule from same package + user added
+        reset(mPackageManager);
+        ZenModeConfig.ZenRule rule = createCustomAutomaticRule(ZEN_MODE_IMPORTANT_INTERRUPTIONS,
+                CUSTOM_RULE_ID + "2");
+        mZenModeHelperSpy.mConfig.automaticRules.put(rule.id, rule);
+        mZenModeHelperSpy.pullRules(events);
+        verify(mPackageManager, never()).getPackageUidAsUser(anyString(), anyInt());
+    }
+
+    @Test
+    public void ruleUidAutomaticZenRuleRemovedUpdatesCache() throws Exception {
+        when(mContext.checkCallingPermission(anyString()))
+                .thenReturn(PackageManager.PERMISSION_GRANTED);
+
+        setupZenConfig();
+        // one enabled automatic rule
+        mZenModeHelperSpy.mConfig.automaticRules = getCustomAutomaticRules();
+        List<StatsEvent> events = new LinkedList<>();
+
+        mZenModeHelperSpy.pullRules(events);
+        mZenModeHelperSpy.removeAutomaticZenRule(CUSTOM_RULE_ID, "test");
+        assertTrue(-1
+                == mZenModeHelperSpy.mRulesUidCache.getOrDefault(CUSTOM_PKG_NAME + "|" + 0, -1));
+    }
+
+    @Test
     public void testProtoRedactsIds() throws Exception {
         setupZenConfig();
         // one enabled automatic rule
diff --git a/services/tests/wmtests/Android.bp b/services/tests/wmtests/Android.bp
index 1ecf850..cf977b4 100644
--- a/services/tests/wmtests/Android.bp
+++ b/services/tests/wmtests/Android.bp
@@ -47,6 +47,7 @@
         "testables",
         "ub-uiautomator",
         "hamcrest-library",
+        "platform-compat-test-rules",
     ],
 
     libs: [
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityOptionsTest.java b/services/tests/wmtests/src/com/android/server/wm/ActivityOptionsTest.java
index 2acb647..8983b4e 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityOptionsTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityOptionsTest.java
@@ -16,7 +16,6 @@
 
 package com.android.server.wm;
 
-import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT;
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_ROTATE;
@@ -53,7 +52,6 @@
         opts.setRotationAnimationHint(ROTATION_ANIMATION_ROTATE);
         opts.setTaskAlwaysOnTop(true);
         opts.setTaskOverlay(true, true);
-        opts.setSplitScreenCreateMode(SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT);
         Bundle optsBundle = opts.toBundle();
 
         // Try and merge the constructed options with a new set of options
@@ -71,7 +69,5 @@
         assertTrue(restoredOpts.getTaskAlwaysOnTop());
         assertTrue(restoredOpts.getTaskOverlay());
         assertTrue(restoredOpts.canTaskOverlayResume());
-        assertEquals(SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT,
-                restoredOpts.getSplitScreenCreateMode());
     }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index b50e50b..83cadf3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -254,26 +254,26 @@
 
         // Set and apply options for ActivityRecord. Pending options should be cleared
         activity.updateOptionsLocked(activityOptions);
-        activity.applyOptionsLocked();
-        assertNull(activity.pendingOptions);
+        activity.applyOptionsAnimation();
+        assertNull(activity.getOptions());
 
         // Set options for two ActivityRecords in same Task. Apply one ActivityRecord options.
         // Pending options should be cleared for both ActivityRecords
         ActivityRecord activity2 = new ActivityBuilder(mAtm).setTask(activity.getTask()).build();
         activity2.updateOptionsLocked(activityOptions);
         activity.updateOptionsLocked(activityOptions);
-        activity.applyOptionsLocked();
-        assertNull(activity.pendingOptions);
-        assertNull(activity2.pendingOptions);
+        activity.applyOptionsAnimation();
+        assertNull(activity.getOptions());
+        assertNull(activity2.getOptions());
 
         // Set options for two ActivityRecords in separate Tasks. Apply one ActivityRecord options.
         // Pending options should be cleared for only ActivityRecord that was applied
         activity2 = new ActivityBuilder(mAtm).setCreateTask(true).build();
         activity2.updateOptionsLocked(activityOptions);
         activity.updateOptionsLocked(activityOptions);
-        activity.applyOptionsLocked();
-        assertNull(activity.pendingOptions);
-        assertNotNull(activity2.pendingOptions);
+        activity.applyOptionsAnimation();
+        assertNull(activity.getOptions());
+        assertNotNull(activity2.getOptions());
     }
 
     @Test
@@ -653,21 +653,21 @@
                     public void onAnimationStart(RemoteAnimationTarget[] apps,
                             RemoteAnimationTarget[] wallpapers,
                             IRemoteAnimationFinishedCallback finishedCallback) {
-
                     }
 
                     @Override
                     public void onAnimationCancelled() {
-
                     }
                 }, 0, 0));
         activity.updateOptionsLocked(opts);
-        assertNotNull(activity.takeOptionsLocked(true /* fromClient */));
-        assertNotNull(activity.pendingOptions);
+        assertNotNull(activity.takeOptions());
+        assertNull(activity.getOptions());
 
-        activity.updateOptionsLocked(ActivityOptions.makeBasic());
-        assertNotNull(activity.takeOptionsLocked(false /* fromClient */));
-        assertNull(activity.pendingOptions);
+        final AppTransition appTransition = activity.mDisplayContent.mAppTransition;
+        spyOn(appTransition);
+        activity.applyOptionsAnimation();
+
+        verify(appTransition).overridePendingAppTransitionRemote(any());
     }
 
     @Test
@@ -1660,8 +1660,8 @@
                     any() /* window */,  any() /* attrs */,
                     anyInt() /* viewVisibility */, anyInt() /* displayId */,
                     any() /* requestedVisibility */, any() /* outFrame */,
-                    any() /* outDisplayCutout */, any() /* outInputChannel */,
-                    any() /* outInsetsState */, any() /* outActiveControls */);
+                    any() /* outInputChannel */, any() /* outInsetsState */,
+                    any() /* outActiveControls */);
             mAtm.mWindowManager.mStartingSurfaceController
                     .createTaskSnapshotSurface(activity, snapshot);
         } catch (RemoteException ignored) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
index 8b8617d..21aa6bf 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
@@ -314,11 +314,12 @@
 
         final RootWindowContainer.FindTaskResult result =
                 new RootWindowContainer.FindTaskResult();
-        result.process(r, task);
+        result.init(r.getActivityType(), r.taskAffinity, r.intent, r.info);
+        result.process(task);
 
         assertEquals(r, task.getTopNonFinishingActivity(false /* includeOverlays */));
         assertEquals(taskOverlay, task.getTopNonFinishingActivity(true /* includeOverlays */));
-        assertNotNull(result.mRecord);
+        assertNotNull(result.mIdealRecord);
     }
 
     @Test
@@ -340,15 +341,17 @@
         final ActivityRecord r1 = new ActivityBuilder(mAtm).setComponent(
                 target).setTargetActivity(targetActivity).build();
         RootWindowContainer.FindTaskResult result = new RootWindowContainer.FindTaskResult();
-        result.process(r1, parentTask);
-        assertThat(result.mRecord).isNotNull();
+        result.init(r1.getActivityType(), r1.taskAffinity, r1.intent, r1.info);
+        result.process(parentTask);
+        assertThat(result.mIdealRecord).isNotNull();
 
         // Using alias activity to find task.
         final ActivityRecord r2 = new ActivityBuilder(mAtm).setComponent(
                 alias).setTargetActivity(targetActivity).build();
         result = new RootWindowContainer.FindTaskResult();
-        result.process(r2, parentTask);
-        assertThat(result.mRecord).isNotNull();
+        result.init(r2.getActivityType(), r2.taskAffinity, r2.intent, r2.info);
+        result.process(parentTask);
+        assertThat(result.mIdealRecord).isNotNull();
     }
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
index 6c824d6..ce5fc40 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
@@ -83,7 +83,7 @@
         assertEquals(WindowManager.TRANSIT_OLD_TRANSLUCENT_ACTIVITY_OPEN,
                 AppTransitionController.getTransitCompatType(mDisplayContent.mAppTransition,
                     mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
-                    null, null));
+                    null, null, false));
     }
 
     @Test
@@ -99,7 +99,7 @@
         assertEquals(WindowManager.TRANSIT_OLD_TRANSLUCENT_ACTIVITY_CLOSE,
                 AppTransitionController.getTransitCompatType(mDisplayContent.mAppTransition,
                         mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
-                        null, null));
+                        null, null, false));
     }
 
     @Test
@@ -117,7 +117,7 @@
         assertEquals(TRANSIT_OLD_TASK_CHANGE_WINDOWING_MODE,
                 AppTransitionController.getTransitCompatType(mDisplayContent.mAppTransition,
                         mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
-                        null, null));
+                        null, null, false));
     }
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
index 8cc515e..f1e3609 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
@@ -23,6 +23,7 @@
 import static android.view.WindowManager.TRANSIT_KEYGUARD_GOING_AWAY;
 import static android.view.WindowManager.TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE;
 import static android.view.WindowManager.TRANSIT_OLD_KEYGUARD_GOING_AWAY;
+import static android.view.WindowManager.TRANSIT_OLD_UNSET;
 import static android.view.WindowManager.TRANSIT_OPEN;
 
 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
@@ -81,7 +82,8 @@
         assertEquals(TRANSIT_OLD_KEYGUARD_GOING_AWAY,
                 AppTransitionController.getTransitCompatType(mDc.mAppTransition,
                         mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
-                        null /* wallpaperTarget */, null /* oldWallpaper */));
+                        null /* wallpaperTarget */, null /* oldWallpaper */,
+                        false /*skipAppTransitionAnimation*/));
     }
 
     @Test
@@ -95,7 +97,8 @@
         assertEquals(TRANSIT_OLD_KEYGUARD_GOING_AWAY,
                 AppTransitionController.getTransitCompatType(mDc.mAppTransition,
                         mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
-                        null /* wallpaperTarget */, null /* oldWallpaper */));
+                        null /* wallpaperTarget */, null /* oldWallpaper */,
+                        false /*skipAppTransitionAnimation*/));
     }
 
     @Test
@@ -109,7 +112,8 @@
         assertEquals(TRANSIT_OLD_CRASHING_ACTIVITY_CLOSE,
                 AppTransitionController.getTransitCompatType(mDc.mAppTransition,
                         mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
-                        null /* wallpaperTarget */, null /* oldWallpaper */));
+                        null /* wallpaperTarget */, null /* oldWallpaper */,
+                        false /*skipAppTransitionAnimation*/));
     }
 
     @Test
@@ -123,7 +127,23 @@
         assertEquals(TRANSIT_OLD_KEYGUARD_GOING_AWAY,
                 AppTransitionController.getTransitCompatType(mDc.mAppTransition,
                         mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
-                        null /* wallpaperTarget */, null /* oldWallpaper */));
+                        null /* wallpaperTarget */, null /* oldWallpaper */,
+                        false /*skipAppTransitionAnimation*/));
+    }
+
+    @Test
+    public void testSkipTransitionAnimation() {
+        final DisplayContent dc = createNewDisplay(Display.STATE_ON);
+        final ActivityRecord activity = createActivityRecord(dc);
+
+        mDc.prepareAppTransition(TRANSIT_OPEN);
+        mDc.prepareAppTransition(TRANSIT_CLOSE);
+        mDc.mClosingApps.add(activity);
+        assertEquals(TRANSIT_OLD_UNSET,
+                AppTransitionController.getTransitCompatType(mDc.mAppTransition,
+                        mDisplayContent.mOpeningApps, mDisplayContent.mClosingApps,
+                        null /* wallpaperTarget */, null /* oldWallpaper */,
+                        true /*skipAppTransitionAnimation*/));
     }
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
index 6f5a874..99ec954 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaTest.java
@@ -43,11 +43,15 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
 import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
 
 import android.content.pm.ActivityInfo;
 import android.graphics.Rect;
@@ -56,6 +60,7 @@
 import android.view.SurfaceControl;
 import android.view.View;
 import android.view.WindowManager;
+import android.window.IDisplayAreaOrganizer;
 
 import com.google.android.collect.Lists;
 
@@ -498,6 +503,42 @@
         assertThat(mDisplayContent.getOrientationRequestingTaskDisplayArea()).isEqualTo(tda);
     }
 
+    @Test
+    public void onParentChanged_onDisplayAreaAppeared() {
+        final DisplayArea<WindowContainer> displayArea = new DisplayArea<>(
+                mWm, BELOW_TASKS, "NewArea", FEATURE_DEFAULT_TASK_CONTAINER);
+
+        final IDisplayAreaOrganizer mockDisplayAreaOrganizer = mock(IDisplayAreaOrganizer.class);
+        spyOn(mWm.mAtmService.mWindowOrganizerController.mDisplayAreaOrganizerController);
+        when(mWm.mAtmService.mWindowOrganizerController.mDisplayAreaOrganizerController
+                .getOrganizerByFeature(FEATURE_DEFAULT_TASK_CONTAINER))
+                .thenReturn(mockDisplayAreaOrganizer);
+
+        mDisplayContent.addChild(displayArea, 0);
+        assertEquals(mockDisplayAreaOrganizer, displayArea.mOrganizer);
+        verify(mWm.mAtmService.mWindowOrganizerController.mDisplayAreaOrganizerController)
+                .onDisplayAreaAppeared(
+                        eq(mockDisplayAreaOrganizer),
+                        argThat(it -> it == displayArea && it.getSurfaceControl() != null));
+    }
+
+    @Test
+    public void onParentChanged_onDisplayAreaVanished() {
+        final DisplayArea<WindowContainer> displayArea = new DisplayArea<>(
+                mWm, BELOW_TASKS, "NewArea", FEATURE_DEFAULT_TASK_CONTAINER);
+
+        final IDisplayAreaOrganizer mockDisplayAreaOrganizer = mock(IDisplayAreaOrganizer.class);
+        displayArea.mOrganizer = mockDisplayAreaOrganizer;
+        spyOn(mWm.mAtmService.mWindowOrganizerController.mDisplayAreaOrganizerController);
+
+        mDisplayContent.addChild(displayArea, 0);
+        displayArea.removeImmediately();
+
+        assertNull(displayArea.mOrganizer);
+        verify(mWm.mAtmService.mWindowOrganizerController.mDisplayAreaOrganizerController)
+                .onDisplayAreaVanished(mockDisplayAreaOrganizer, displayArea);
+    }
+
     private static class TestDisplayArea<T extends WindowContainer> extends DisplayArea<T> {
         private TestDisplayArea(WindowManagerService wms, Rect bounds) {
             super(wms, ANY, "half display area");
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
index 378a0a8..0f03f68 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyLayoutTests.java
@@ -689,7 +689,7 @@
         final InsetsState outState = new InsetsState();
 
         mDisplayPolicy.getLayoutHint(mWindow.mAttrs, null /* windowToken */, outFrame,
-                outDisplayCutout, outState, true /* localClient */);
+                outState, true /* localClient */);
 
         assertThat(outFrame, is(outState.getDisplayFrame()));
         assertThat(outDisplayCutout, is(new DisplayCutout.ParcelableWrapper()));
@@ -716,8 +716,8 @@
                 new DisplayCutout.ParcelableWrapper();
         final InsetsState outState = new InsetsState();
 
-        mDisplayPolicy.getLayoutHint(mWindow.mAttrs, mWindow.mToken, outFrame, outDisplayCutout,
-                outState, true /* localClient */);
+        mDisplayPolicy.getLayoutHint(mWindow.mAttrs, mWindow.mToken, outFrame, outState,
+                true /* localClient */);
 
         assertThat(outFrame, is(taskBounds));
         assertThat(outDisplayCutout, is(new DisplayCutout.ParcelableWrapper()));
@@ -756,8 +756,8 @@
                 new DisplayCutout.ParcelableWrapper();
         final InsetsState outState = new InsetsState();
 
-        mDisplayPolicy.getLayoutHint(mWindow.mAttrs, mWindow.mToken, outFrame, outDisplayCutout,
-                outState, true /* localClient */);
+        mDisplayPolicy.getLayoutHint(mWindow.mAttrs, mWindow.mToken, outFrame, outState,
+                true /* localClient */);
 
         assertThat(outFrame, is(taskBounds));
         assertThat(outDisplayCutout, is(new DisplayCutout.ParcelableWrapper()));
diff --git a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java
index 089fd20..61140d6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LaunchParamsControllerTests.java
@@ -495,7 +495,7 @@
 
         // We didn't set up the overall environment for this test, so we need to mute the side
         // effect of layout passes that loosen the stable frame.
-        doNothing().when(display.mDisplayContent.mDisplayFrames).onBeginLayout();
+        doNothing().when(display.mDisplayContent.mDisplayFrames).onBeginLayout(any());
         return display;
     }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
index 4892ef3..4b42d56 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -324,7 +324,9 @@
                 .setActivityType(ACTIVITY_TYPE_UNDEFINED)
                 .setFlags(FLAG_ACTIVITY_NEW_TASK)
                 .build();
-        assertThat(task1.getActivityType()).isEqualTo(ACTIVITY_TYPE_UNDEFINED);
+        // Set the activity type again or the activity type of a root task would be remapped
+        // to ACTIVITY_TYPE_STANDARD, {@link TaskDisplayArea#createRootTask()}
+        task1.getWindowConfiguration().setActivityType(ACTIVITY_TYPE_UNDEFINED);
         mRecentTasks.add(task1);
         mCallbacksRecorder.clear();
 
@@ -347,7 +349,9 @@
                 .setFlags(FLAG_ACTIVITY_NEW_TASK)
                 .setUserId(TEST_USER_0_ID)
                 .build();
-        assertEquals(ACTIVITY_TYPE_UNDEFINED, task1.getActivityType());
+        // Set the activity type again or the activity type of a root task would be remapped
+        // to ACTIVITY_TYPE_STANDARD, {@link TaskDisplayArea#createRootTask()}
+        task1.getWindowConfiguration().setActivityType(ACTIVITY_TYPE_UNDEFINED);
         mRecentTasks.add(task1);
         mCallbacksRecorder.clear();
 
@@ -1006,8 +1010,6 @@
         assertNotRestoreTask(
                 () -> mAtm.setTaskWindowingMode(taskId, WINDOWING_MODE_FULLSCREEN,
                         false/* toTop */));
-        assertNotRestoreTask(
-                () -> mAtm.setTaskWindowingModeSplitScreenPrimary(taskId, false /* toTop */));
     }
 
     @Test
@@ -1144,8 +1146,6 @@
         assertSecurityException(expectCallable,
                 () -> mAtm.moveTaskToRootTask(0, INVALID_STACK_ID, true));
         assertSecurityException(expectCallable,
-                () -> mAtm.setTaskWindowingModeSplitScreenPrimary(0, true));
-        assertSecurityException(expectCallable,
                 () -> mAtm.moveTopActivityToPinnedRootTask(INVALID_STACK_ID, new Rect()));
         assertSecurityException(expectCallable, () -> mAtm.getAllRootTaskInfos());
         assertSecurityException(expectCallable,
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index c7175a0c..e190248 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -49,6 +49,8 @@
 import android.app.ActivityManagerInternal;
 import android.app.TaskStackListener;
 import android.app.WindowConfiguration;
+import android.compat.testing.PlatformCompatChangeRule;
+import android.content.ComponentName;
 import android.content.pm.ActivityInfo;
 import android.content.res.Configuration;
 import android.graphics.Rect;
@@ -58,7 +60,11 @@
 
 import androidx.test.filters.MediumTest;
 
+import libcore.junit.util.compat.CoreCompatChangeRule.EnableCompatChanges;
+
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.TestRule;
 import org.junit.runner.RunWith;
 
 import java.util.ArrayList;
@@ -73,6 +79,9 @@
 @Presubmit
 @RunWith(WindowTestRunner.class)
 public class SizeCompatTests extends WindowTestsBase {
+    @Rule
+    public TestRule compatChangeRule = new PlatformCompatChangeRule();
+
     private Task mTask;
     private ActivityRecord mActivity;
 
@@ -535,6 +544,26 @@
     }
 
     @Test
+    @EnableCompatChanges({ActivityInfo.FORCE_RESIZE_APP})
+    public void testNoSizeCompatWhenPerAppOverrideSet() {
+        setUpDisplaySizeWithApp(1000, 2500);
+
+        // Make the task root resizable.
+        mActivity.info.resizeMode = ActivityInfo.RESIZE_MODE_RESIZEABLE;
+
+        // Create a size compat activity on the same task.
+        final ActivityRecord activity = new ActivityBuilder(mAtm)
+                .setTask(mTask)
+                .setResizeMode(ActivityInfo.RESIZE_MODE_UNRESIZEABLE)
+                .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
+                .setComponent(ComponentName.createRelative(mContext,
+                        SizeCompatTests.class.getName()))
+                .setUid(android.os.Process.myUid())
+                .build();
+        assertFalse(activity.shouldUseSizeCompatMode());
+    }
+
+    @Test
     public void testLaunchWithFixedRotationTransform() {
         final int dw = 1000;
         final int dh = 2500;
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
index 1c93e0f..10d2da0 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
@@ -43,8 +43,6 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.never;
@@ -175,15 +173,9 @@
     }
 
     @Test
-    public void testReuseTaskAsStack() {
+    public void testReuseTaskAsRootTask() {
         final Task candidateTask = createTaskStackOnDisplay(WINDOWING_MODE_FULLSCREEN,
                 ACTIVITY_TYPE_STANDARD, mDisplayContent);
-        final Task newStack = createTaskStackOnDisplay(WINDOWING_MODE_FULLSCREEN,
-                ACTIVITY_TYPE_STANDARD, mDisplayContent);
-        final TaskDisplayArea taskDisplayArea = candidateTask.getDisplayArea();
-        doReturn(newStack).when(taskDisplayArea).createRootTask(anyInt(), anyInt(), anyBoolean(),
-                any(), any(), anyBoolean());
-
         final int type = ACTIVITY_TYPE_STANDARD;
         assertGetOrCreateRootTask(WINDOWING_MODE_FULLSCREEN, type, candidateTask,
                 true /* reuseCandidate */);
@@ -359,8 +351,8 @@
     private void assertGetOrCreateRootTask(int windowingMode, int activityType, Task candidateTask,
             boolean reuseCandidate) {
         final TaskDisplayArea taskDisplayArea = candidateTask.getDisplayArea();
-        final Task stack = taskDisplayArea.getOrCreateRootTask(windowingMode, activityType,
+        final Task rootTask = taskDisplayArea.getOrCreateRootTask(windowingMode, activityType,
                 false /* onTop */, null /* intent */, candidateTask /* candidateTask */);
-        assertEquals(reuseCandidate, stack == candidateTask);
+        assertEquals(reuseCandidate, rootTask == candidateTask);
     }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
index 184995d..f20513d 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskRecordTests.java
@@ -571,9 +571,11 @@
         info.packageName = DEFAULT_COMPONENT_PACKAGE_NAME;
         info.targetActivity = targetClassName;
 
-        final Task task = new Task(mAtm, 1 /* taskId */, info, intent,
-                null /* voiceSession */, null /* voiceInteractor */, null /* taskDescriptor */,
-                null /*stack*/);
+        final Task task = new Task.Builder(mAtm)
+                .setTaskId(1)
+                .setActivityInfo(info)
+                .setIntent(intent)
+                .build();
         assertEquals("The alias activity component should be saved in task intent.", aliasClassName,
                 task.intent.getComponent().getClassName());
 
@@ -1122,10 +1124,11 @@
     }
 
     private Task createTask(int taskId) {
-        return new Task(mAtm, taskId, new Intent(), null, null, null,
-                ActivityBuilder.getDefaultComponent(), null, false, false, false, 0, 10050, null,
-                0, false, null, 0, 0, 0, 0, null, null, 0, false, false, false, 0,
-                0, null /*ActivityInfo*/, null /*_voiceSession*/, null /*_voiceInteractor*/,
-                null /*stack*/);
+        return new Task.Builder(mAtm)
+                .setTaskId(taskId)
+                .setIntent(new Intent())
+                .setRealActivity(ActivityBuilder.getDefaultComponent())
+                .setEffectiveUid(10050)
+                .buildInner();
     }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
index eb3a5e0..d49956a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java
@@ -37,7 +37,6 @@
 import static org.mockito.Matchers.eq;
 
 import android.app.ActivityManager.TaskDescription;
-import android.window.TaskSnapshot;
 import android.content.ComponentName;
 import android.graphics.Canvas;
 import android.graphics.Color;
@@ -52,6 +51,7 @@
 import android.view.Surface;
 import android.view.SurfaceControl;
 import android.view.WindowManager;
+import android.window.TaskSnapshot;
 
 import androidx.test.filters.SmallTest;
 
@@ -145,7 +145,7 @@
 
         assertThat(surface).isNotNull();
         verify(session).addToDisplay(any(), argThat(this::isTrustedOverlay), anyInt(), anyInt(),
-                any(), any(), any(), any(), any(), any());
+                any(), any(), any(), any(), any());
     }
 
     @Test
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
index 00c2aa5..74248a9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -19,14 +19,21 @@
 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
 import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN;
 import static android.view.WindowManager.TRANSIT_OPEN;
 import static android.view.WindowManager.TRANSIT_TO_BACK;
+import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
+import static android.window.TransitionInfo.FLAG_SHOW_WALLPAPER;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
 
+import android.os.IBinder;
 import android.platform.test.annotations.Presubmit;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -280,4 +287,55 @@
                     info.getChanges().get(i).getContainer());
         }
     }
+
+    @Test
+    public void testCreateInfo_wallpaper() {
+        final Transition transition = createTestTransition(TRANSIT_OLD_TASK_OPEN);
+        // pick some number with a high enough chance of being out-of-order when added to set.
+        final int taskCount = 4;
+        final int showWallpaperTask = 2;
+
+        final Task[] tasks = new Task[taskCount];
+        for (int i = 0; i < taskCount; ++i) {
+            // Each add goes on top, so at the end of this, task[9] should be on top
+            tasks[i] = createTaskStackOnDisplay(WINDOWING_MODE_FULLSCREEN,
+                    ACTIVITY_TYPE_STANDARD, mDisplayContent);
+            final ActivityRecord act = createActivityRecord(tasks[i]);
+            // alternate so that the transition doesn't get promoted to the display area
+            act.mVisibleRequested = (i % 2) == 0; // starts invisible
+            if (i == showWallpaperTask) {
+                doReturn(true).when(act).showWallpaper();
+            }
+        }
+
+        final WallpaperWindowToken wallpaperWindowToken = spy(new WallpaperWindowToken(mWm,
+                mock(IBinder.class), true, mDisplayContent, true /* ownerCanManageAppTokens */));
+        final WindowState wallpaperWindow = createWindow(null, TYPE_WALLPAPER, wallpaperWindowToken,
+                "wallpaperWindow");
+        wallpaperWindow.mWallpaperVisible = false;
+        transition.collect(wallpaperWindowToken);
+        wallpaperWindow.mWallpaperVisible = true;
+        wallpaperWindow.mHasSurface = true;
+
+        // doesn't matter which order collected since participants is a set
+        for (int i = 0; i < taskCount; ++i) {
+            transition.collectExistenceChange(tasks[i]);
+            final ActivityRecord act = tasks[i].getTopMostActivity();
+            transition.collect(act);
+            tasks[i].getTopMostActivity().mVisibleRequested = (i % 2) != 0;
+        }
+
+        ArraySet<WindowContainer> targets = Transition.calculateTargets(
+                transition.mParticipants, transition.mChanges);
+        TransitionInfo info = Transition.calculateTransitionInfo(
+                0, 0, targets, transition.mChanges);
+        // verify that wallpaper is at bottom
+        assertEquals(taskCount + 1, info.getChanges().size());
+        // The wallpaper is not organized, so it won't have a token; however, it will be marked
+        // as IS_WALLPAPER
+        assertEquals(FLAG_IS_WALLPAPER,
+                info.getChanges().get(info.getChanges().size() - 1).getFlags());
+        assertEquals(FLAG_SHOW_WALLPAPER, info.getChange(
+                tasks[showWallpaperTask].mRemoteToken.toWindowContainerToken()).getFlags());
+    }
 }
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
index 4a6906b..1607f01 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowFrameTests.java
@@ -19,8 +19,6 @@
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
-import static android.view.DisplayCutout.BOUNDS_POSITION_TOP;
-import static android.view.DisplayCutout.fromBoundingRect;
 import static android.view.InsetsState.ITYPE_IME;
 import static android.view.InsetsState.ITYPE_STATUS_BAR;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
@@ -42,8 +40,6 @@
 import androidx.test.filters.FlakyTest;
 import androidx.test.filters.SmallTest;
 
-import com.android.server.wm.utils.WmDisplayCutout;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -258,30 +254,6 @@
     }
 
     @Test
-    @FlakyTest(bugId = 130388666)
-    public void testDisplayCutout() {
-        // Regular fullscreen task and window
-        WindowState w = createWindow();
-        w.mAttrs.gravity = Gravity.LEFT | Gravity.TOP;
-
-        final Rect pf = new Rect(0, 0, 1000, 2000);
-        // Create a display cutout of size 50x50, aligned top-center
-        final WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
-                fromBoundingRect(500, 0, 550, 50, BOUNDS_POSITION_TOP),
-                pf.width(), pf.height());
-
-        final WindowFrames windowFrames = w.getWindowFrames();
-        windowFrames.setFrames(pf, pf);
-        windowFrames.setDisplayCutout(cutout);
-        w.computeFrame();
-
-        assertEquals(w.getWmDisplayCutout().getDisplayCutout().getSafeInsetTop(), 50);
-        assertEquals(w.getWmDisplayCutout().getDisplayCutout().getSafeInsetBottom(), 0);
-        assertEquals(w.getWmDisplayCutout().getDisplayCutout().getSafeInsetLeft(), 0);
-        assertEquals(w.getWmDisplayCutout().getDisplayCutout().getSafeInsetRight(), 0);
-    }
-
-    @Test
     public void testFreeformContentInsets() {
         removeGlobalMinSizeRestriction();
         // fullscreen task doesn't use bounds for computeFrame
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index 5ceb4ca0..7db2fc9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -67,14 +67,11 @@
 import static org.mockito.Mockito.clearInvocations;
 import static org.mockito.Mockito.when;
 
-import android.graphics.Insets;
 import android.graphics.Matrix;
 import android.graphics.Rect;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.platform.test.annotations.Presubmit;
-import android.util.Size;
-import android.view.DisplayCutout;
 import android.view.InputWindowHandle;
 import android.view.InsetsState;
 import android.view.SurfaceControl;
@@ -82,8 +79,6 @@
 
 import androidx.test.filters.SmallTest;
 
-import com.android.server.wm.utils.WmDisplayCutout;
-
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -527,24 +522,6 @@
     }
 
     @Test
-    public void testDisplayCutoutIsCalculatedRelativeToFrame() {
-        final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
-        WindowFrames wf = app.getWindowFrames();
-        wf.mParentFrame.set(7, 10, 185, 380);
-        wf.mDisplayFrame.set(wf.mParentFrame);
-        final DisplayCutout cutout = new DisplayCutout(
-                Insets.of(0, 15, 0, 22) /* safeInset */,
-                null /* boundLeft */,
-                new Rect(95, 0, 105, 15),
-                null /* boundRight */,
-                new Rect(95, 378, 105, 400));
-        wf.setDisplayCutout(new WmDisplayCutout(cutout, new Size(200, 400)));
-
-        app.computeFrame();
-        assertThat(app.getWmDisplayCutout().getDisplayCutout(), is(cutout.inset(7, 10, 5, 20)));
-    }
-
-    @Test
     public void testVisibilityChangeSwitchUser() {
         final WindowState window = createWindow(null, TYPE_APPLICATION, "app");
         window.mHasSurface = true;
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 ba5ca2e..31e2dce 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -1055,23 +1055,25 @@
                 mIntent.setFlags(mFlags);
             }
 
-            Task task;
-            final int taskId = mTaskId >= 0 ? mTaskId : mTaskDisplayArea.getNextRootTaskId();
+            final Task.Builder builder = new Task.Builder(mSupervisor.mService)
+                    .setTaskId(mTaskId >= 0 ? mTaskId : mTaskDisplayArea.getNextRootTaskId())
+                    .setWindowingMode(mWindowingMode)
+                    .setActivityInfo(mActivityInfo)
+                    .setIntent(mIntent)
+                    .setOnTop(mOnTop)
+                    .setVoiceSession(mVoiceSession);
+            final Task task;
             if (mParentTask == null) {
-                task = mTaskDisplayArea.createRootTaskUnchecked(
-                        mWindowingMode, mActivityType, taskId, mOnTop, mActivityInfo, mIntent,
-                        false /* createdByOrganizer */, false /* deferTaskAppear */,
-                        null /* launchCookie */);
+                task = builder.setActivityType(mActivityType)
+                        .setParent(mTaskDisplayArea)
+                        .build();
             } else {
-                task = new Task(mSupervisor.mService, taskId, mActivityInfo,
-                        mIntent /*intent*/, mVoiceSession, null /*_voiceInteractor*/,
-                        null /*taskDescription*/, mParentTask);
+                task = builder.setParent(mParentTask).build();
                 mParentTask.moveToFront("build-task");
-                mParentTask.addChild(task, true, true);
             }
             spyOn(task);
             task.mUserId = mUserId;
-            Task rootTask = task.getRootTask();
+            final Task rootTask = task.getRootTask();
             if (task != rootTask && !Mockito.mockingDetails(rootTask).isSpy()) {
                 spyOn(rootTask);
             }
diff --git a/services/tests/wmtests/src/com/android/server/wm/utils/WmDisplayCutoutTest.java b/services/tests/wmtests/src/com/android/server/wm/utils/WmDisplayCutoutTest.java
index a283476..39976a5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/utils/WmDisplayCutoutTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/utils/WmDisplayCutoutTest.java
@@ -23,10 +23,8 @@
 import static android.view.DisplayCutout.NO_CUTOUT;
 import static android.view.DisplayCutout.fromBoundingRect;
 
-import static org.hamcrest.Matchers.equalTo;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertThat;
 
 import android.graphics.Insets;
 import android.graphics.Rect;
@@ -54,52 +52,6 @@
             null /* boundRight */, null /* boundBottom */);
 
     @Test
-    public void calculateRelativeTo_top() {
-        WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
-                fromBoundingRect(0, 0, 100, 20, BOUNDS_POSITION_TOP), 200, 400)
-                .calculateRelativeTo(new Rect(5, 5, 95, 195));
-
-        assertEquals(new Rect(0, 15, 0, 0), cutout.getDisplayCutout().getSafeInsets());
-    }
-
-    @Test
-    public void calculateRelativeTo_left() {
-        WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
-                fromBoundingRect(0, 0, 20, 100, BOUNDS_POSITION_LEFT), 400, 200)
-                .calculateRelativeTo(new Rect(5, 5, 195, 95));
-
-        assertEquals(new Rect(15, 0, 0, 0), cutout.getDisplayCutout().getSafeInsets());
-    }
-
-    @Test
-    public void calculateRelativeTo_bottom() {
-        WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
-                fromBoundingRect(0, 180, 100, 200, BOUNDS_POSITION_BOTTOM), 100, 200)
-                .calculateRelativeTo(new Rect(5, 5, 95, 195));
-
-        assertEquals(new Rect(0, 0, 0, 15), cutout.getDisplayCutout().getSafeInsets());
-    }
-
-    @Test
-    public void calculateRelativeTo_right() {
-        WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
-                fromBoundingRect(180, 0, 200, 100, BOUNDS_POSITION_RIGHT), 200, 100)
-                .calculateRelativeTo(new Rect(5, 5, 195, 95));
-
-        assertEquals(new Rect(0, 0, 15, 0), cutout.getDisplayCutout().getSafeInsets());
-    }
-
-    @Test
-    public void calculateRelativeTo_bounds() {
-        WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
-                fromBoundingRect(0, 0, 100, 20, BOUNDS_POSITION_TOP), 200, 400)
-                .calculateRelativeTo(new Rect(5, 10, 95, 180));
-
-        assertThat(cutout.getDisplayCutout().getBoundingRectTop(),
-                equalTo(new Rect(-5, -10, 95, 10)));
-    }
-
-    @Test
     public void computeSafeInsets_cutoutTop() {
         WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
                 fromBoundingRect(80, 0, 120, 20, BOUNDS_POSITION_TOP), 200, 400);
diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index e145fcf..e06dcdb 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -638,7 +638,7 @@
                                 persistMessage, messageId);
                     } catch (RemoteException e) {
                         Log.e(TAG, "sendTextMessageInternal: Couldn't send SMS, exception - "
-                                + e.getMessage() + " id: " + messageId);
+                                + e.getMessage() + " " + formatCrossStackMessageId(messageId));
                         notifySmsError(sentIntent, RESULT_REMOTE_EXCEPTION);
                     }
                 }
@@ -658,7 +658,7 @@
                         persistMessage, messageId);
             } catch (RemoteException e) {
                 Log.e(TAG, "sendTextMessageInternal (no persist): Couldn't send SMS, exception - "
-                        + e.getMessage() + " id: " + messageId);
+                        + e.getMessage() + " " + formatCrossStackMessageId(messageId));
                 notifySmsError(sentIntent, RESULT_REMOTE_EXCEPTION);
             }
         }
@@ -1072,8 +1072,7 @@
                                     deliveryIntents, persistMessage, messageId);
                         } catch (RemoteException e) {
                             Log.e(TAG, "sendMultipartTextMessageInternal: Couldn't send SMS - "
-                                    + e.getMessage() + " id: "
-                                    + messageId);
+                                    + e.getMessage() + " " + formatCrossStackMessageId(messageId));
                             notifySmsError(sentIntents, RESULT_REMOTE_EXCEPTION);
                         }
                     }
@@ -1094,7 +1093,7 @@
                     }
                 } catch (RemoteException e) {
                     Log.e(TAG, "sendMultipartTextMessageInternal: Couldn't send SMS - "
-                            + e.getMessage() + " id: " + messageId);
+                            + e.getMessage() + " " + formatCrossStackMessageId(messageId));
                     notifySmsError(sentIntents, RESULT_REMOTE_EXCEPTION);
                 }
             }
@@ -3150,4 +3149,8 @@
             ex.rethrowFromSystemServer();
         }
     }
+
+    private static String formatCrossStackMessageId(long id) {
+        return "{x-message-id:" + id + "}";
+    }
 }
diff --git a/telephony/java/android/telephony/data/IQualifiedNetworksService.aidl b/telephony/java/android/telephony/data/IQualifiedNetworksService.aidl
index 2904082..ba2b62d 100644
--- a/telephony/java/android/telephony/data/IQualifiedNetworksService.aidl
+++ b/telephony/java/android/telephony/data/IQualifiedNetworksService.aidl
@@ -17,7 +17,7 @@
 package android.telephony.data;
 
 import android.telephony.data.IQualifiedNetworksServiceCallback;
-import android.telephony.data.ApnThrottleStatus;
+import android.telephony.data.ThrottleStatus;
 
 /**
  * {@hide}
@@ -26,5 +26,5 @@
 {
     oneway void createNetworkAvailabilityProvider(int slotId, IQualifiedNetworksServiceCallback callback);
     oneway void removeNetworkAvailabilityProvider(int slotId);
-    oneway void reportApnThrottleStatusChanged(int slotId, in List<ApnThrottleStatus> statuses);
+    oneway void reportThrottleStatusChanged(int slotId, in List<ThrottleStatus> statuses);
 }
diff --git a/telephony/java/android/telephony/data/QualifiedNetworksService.java b/telephony/java/android/telephony/data/QualifiedNetworksService.java
index 4af63b4..4e85d89 100644
--- a/telephony/java/android/telephony/data/QualifiedNetworksService.java
+++ b/telephony/java/android/telephony/data/QualifiedNetworksService.java
@@ -168,8 +168,8 @@
          *
          * @param statuses the statuses that have changed
          */
-        public void reportApnThrottleStatusChanged(@NonNull List<ApnThrottleStatus> statuses) {
-            Log.d(TAG, "reportApnThrottleStatusChanged: statuses size=" + statuses.size());
+        public void reportThrottleStatusChanged(@NonNull List<ThrottleStatus> statuses) {
+            Log.d(TAG, "reportThrottleStatusChanged: statuses size=" + statuses.size());
         }
 
         /**
@@ -212,8 +212,8 @@
                     break;
                 case QNS_APN_THROTTLE_STATUS_CHANGED:
                     if (provider != null) {
-                        List<ApnThrottleStatus> statuses = (List<ApnThrottleStatus>) message.obj;
-                        provider.reportApnThrottleStatusChanged(statuses);
+                        List<ThrottleStatus> statuses = (List<ThrottleStatus>) message.obj;
+                        provider.reportThrottleStatusChanged(statuses);
                     }
                     break;
 
@@ -307,8 +307,8 @@
         }
 
         @Override
-        public void reportApnThrottleStatusChanged(int slotIndex,
-                List<ApnThrottleStatus> statuses) {
+        public void reportThrottleStatusChanged(int slotIndex,
+                List<ThrottleStatus> statuses) {
             mHandler.obtainMessage(QNS_APN_THROTTLE_STATUS_CHANGED, slotIndex, 0, statuses)
                     .sendToTarget();
         }
diff --git a/telephony/java/android/telephony/data/ApnThrottleStatus.aidl b/telephony/java/android/telephony/data/ThrottleStatus.aidl
similarity index 95%
rename from telephony/java/android/telephony/data/ApnThrottleStatus.aidl
rename to telephony/java/android/telephony/data/ThrottleStatus.aidl
index 46bc4ab..f75f3570 100644
--- a/telephony/java/android/telephony/data/ApnThrottleStatus.aidl
+++ b/telephony/java/android/telephony/data/ThrottleStatus.aidl
@@ -17,4 +17,4 @@
 /** @hide */
 package android.telephony.data;
 
-parcelable ApnThrottleStatus;
+parcelable ThrottleStatus;
diff --git a/telephony/java/android/telephony/data/ApnThrottleStatus.java b/telephony/java/android/telephony/data/ThrottleStatus.java
similarity index 86%
rename from telephony/java/android/telephony/data/ApnThrottleStatus.java
rename to telephony/java/android/telephony/data/ThrottleStatus.java
index eec1408..0335c68 100644
--- a/telephony/java/android/telephony/data/ApnThrottleStatus.java
+++ b/telephony/java/android/telephony/data/ThrottleStatus.java
@@ -35,7 +35,7 @@
  * @hide
  */
 @SystemApi
-public final class ApnThrottleStatus implements Parcelable {
+public final class ThrottleStatus implements Parcelable {
     /**
      * The APN type is not throttled.
      */
@@ -43,14 +43,14 @@
 
     /**
      * The APN type is throttled until {@link android.os.SystemClock#elapsedRealtime()}
-     * has reached {@link ApnThrottleStatus#getThrottleExpiryTimeMillis}
+     * has reached {@link ThrottleStatus#getThrottleExpiryTimeMillis}
      */
     public static final int THROTTLE_TYPE_ELAPSED_TIME = 2;
 
     /** {@hide} */
     @IntDef(flag = true, prefix = {"THROTTLE_TYPE_"}, value = {
-            ApnThrottleStatus.THROTTLE_TYPE_NONE,
-            ApnThrottleStatus.THROTTLE_TYPE_ELAPSED_TIME,
+            ThrottleStatus.THROTTLE_TYPE_NONE,
+            ThrottleStatus.THROTTLE_TYPE_ELAPSED_TIME,
     })
     public @interface ThrottleType {
     }
@@ -72,9 +72,9 @@
 
     /** {@hide} */
     @IntDef(flag = true, prefix = {"RETRY_TYPE_"}, value = {
-            ApnThrottleStatus.RETRY_TYPE_NONE,
-            ApnThrottleStatus.RETRY_TYPE_NEW_CONNECTION,
-            ApnThrottleStatus.RETRY_TYPE_HANDOVER,
+            ThrottleStatus.RETRY_TYPE_NONE,
+            ThrottleStatus.RETRY_TYPE_NEW_CONNECTION,
+            ThrottleStatus.RETRY_TYPE_HANDOVER,
     })
     public @interface RetryType {
     }
@@ -141,7 +141,7 @@
      * {@link SystemClock#elapsedRealtime}.
      *
      * This value only applies when the throttle type is set to
-     * {@link ApnThrottleStatus#THROTTLE_TYPE_ELAPSED_TIME}.
+     * {@link ThrottleStatus#THROTTLE_TYPE_ELAPSED_TIME}.
      *
      * A value of {@link Long#MAX_VALUE} implies that the APN type is throttled indefinitely.
      *
@@ -152,7 +152,7 @@
         return mThrottleExpiryTimeMillis;
     }
 
-    private ApnThrottleStatus(int slotIndex,
+    private ThrottleStatus(int slotIndex,
             @AccessNetworkConstants.TransportType int transportType,
             @Annotation.ApnType int apnTypes,
             @ThrottleType int throttleType,
@@ -166,7 +166,7 @@
         mRetryType = retryType;
     }
 
-    private ApnThrottleStatus(@NonNull Parcel source) {
+    private ThrottleStatus(@NonNull Parcel source) {
         mSlotIndex = source.readInt();
         mTransportType = source.readInt();
         mApnType = source.readInt();
@@ -185,16 +185,16 @@
         dest.writeInt(mThrottleType);
     }
 
-    public static final @NonNull Parcelable.Creator<ApnThrottleStatus> CREATOR =
-            new Parcelable.Creator<ApnThrottleStatus>() {
+    public static final @NonNull Parcelable.Creator<ThrottleStatus> CREATOR =
+            new Parcelable.Creator<ThrottleStatus>() {
                 @Override
-                public ApnThrottleStatus createFromParcel(@NonNull Parcel source) {
-                    return new ApnThrottleStatus(source);
+                public ThrottleStatus createFromParcel(@NonNull Parcel source) {
+                    return new ThrottleStatus(source);
                 }
 
                 @Override
-                public ApnThrottleStatus[] newArray(int size) {
-                    return new ApnThrottleStatus[size];
+                public ThrottleStatus[] newArray(int size) {
+                    return new ThrottleStatus[size];
                 }
             };
 
@@ -213,8 +213,8 @@
     public boolean equals(Object obj) {
         if (obj == null) {
             return false;
-        } else if (obj instanceof ApnThrottleStatus) {
-            ApnThrottleStatus other = (ApnThrottleStatus) obj;
+        } else if (obj instanceof ThrottleStatus) {
+            ThrottleStatus other = (ThrottleStatus) obj;
             return this.mSlotIndex == other.mSlotIndex
                     && this.mApnType == other.mApnType
                     && this.mRetryType == other.mRetryType
@@ -228,7 +228,7 @@
 
     @Override
     public String toString() {
-        return "ApnThrottleStatus{"
+        return "ThrottleStatus{"
                 + "mSlotIndex=" + mSlotIndex
                 + ", mTransportType=" + mTransportType
                 + ", mApnType=" + ApnSetting.getApnTypeString(mApnType)
@@ -239,18 +239,18 @@
     }
 
     /**
-     * Provides a convenient way to set the fields of an {@link ApnThrottleStatus} when creating a
+     * Provides a convenient way to set the fields of an {@link ThrottleStatus} when creating a
      * new instance.
      *
-     * <p>The example below shows how you might create a new {@code ApnThrottleStatus}:
+     * <p>The example below shows how you might create a new {@code ThrottleStatus}:
      *
      * <pre><code>
      *
-     * DataCallResponseApnThrottleStatus = new ApnThrottleStatus.Builder()
+     * ThrottleStatus = new ThrottleStatus.Builder()
      *     .setSlotIndex(1)
      *     .setApnType({@link ApnSetting#TYPE_EMERGENCY})
      *     .setNoThrottle()
-     *     .setRetryType({@link ApnThrottleStatus#RETRY_TYPE_NEW_CONNECTION})
+     *     .setRetryType({@link ThrottleStatus#RETRY_TYPE_NEW_CONNECTION})
      *     .build();
      * </code></pre>
      */
@@ -316,7 +316,7 @@
          * {@link SystemClock#elapsedRealtime}.
          *
          * When setting this value, the throttle type is set to
-         * {@link ApnThrottleStatus#THROTTLE_TYPE_ELAPSED_TIME}.
+         * {@link ThrottleStatus#THROTTLE_TYPE_ELAPSED_TIME}.
          *
          * A value of {@link Long#MAX_VALUE} implies that the APN type is throttled indefinitely.
          *
@@ -342,7 +342,7 @@
          * Sets the status of the APN type as not being throttled.
          *
          * When setting this value, the throttle type is set to
-         * {@link ApnThrottleStatus#THROTTLE_TYPE_NONE} and the expiry time is set to
+         * {@link ThrottleStatus#THROTTLE_TYPE_NONE} and the expiry time is set to
          * {@link Builder#NO_THROTTLE_EXPIRY_TIME}.
          *
          * @return The same instance of the builder.
@@ -369,13 +369,13 @@
         }
 
         /**
-         * Build the {@link ApnThrottleStatus}
+         * Build the {@link ThrottleStatus}
          *
-         * @return the {@link ApnThrottleStatus} object
+         * @return the {@link ThrottleStatus} object
          */
         @NonNull
-        public ApnThrottleStatus build() {
-            return new ApnThrottleStatus(
+        public ThrottleStatus build() {
+            return new ThrottleStatus(
                     mSlotIndex,
                     mTransportType,
                     mApnType,
diff --git a/telephony/java/android/telephony/ims/ImsCallSession.java b/telephony/java/android/telephony/ims/ImsCallSession.java
index a3efb79..0aff997 100755
--- a/telephony/java/android/telephony/ims/ImsCallSession.java
+++ b/telephony/java/android/telephony/ims/ImsCallSession.java
@@ -101,10 +101,29 @@
      */
     public static class Listener {
         /**
-         * Called when a request is sent out to initiate a new session
-         * and 1xx response is received from the network.
+         * Called when the session is initiating.
          *
-         * @param session the session object that carries out the IMS session
+         * see: {@link ImsCallSessionListener#callSessionInitiating(ImsCallProfile)}
+         */
+        public void callSessionInitiating(ImsCallSession session,
+                ImsCallProfile profile) {
+            // no-op
+        }
+
+        /**
+         * Called when the session failed before initiating was called.
+         *
+         * see: {@link ImsCallSessionListener#callSessionInitiatingFailed(ImsReasonInfo)}
+         */
+        public void callSessionInitiatingFailed(ImsCallSession session,
+                ImsReasonInfo reasonInfo) {
+            // no-op
+        }
+
+        /**
+         * Called when the session is progressing.
+         *
+         * see: {@link ImsCallSessionListener#callSessionProgressing(ImsStreamMediaProfile)}
          */
         public void callSessionProgressing(ImsCallSession session,
                 ImsStreamMediaProfile profile) {
@@ -1179,6 +1198,13 @@
          * Notifies the result of the basic session operation (setup / terminate).
          */
         @Override
+        public void callSessionInitiating(ImsCallProfile profile) {
+            if (mListener != null) {
+                mListener.callSessionInitiating(ImsCallSession.this, profile);
+            }
+        }
+
+        @Override
         public void callSessionProgressing(ImsStreamMediaProfile profile) {
             if (mListener != null) {
                 mListener.callSessionProgressing(ImsCallSession.this, profile);
@@ -1193,6 +1219,13 @@
         }
 
         @Override
+        public void callSessionInitiatingFailed(ImsReasonInfo reasonInfo) {
+            if (mListener != null) {
+                mListener.callSessionStartFailed(ImsCallSession.this, reasonInfo);
+            }
+        }
+
+        @Override
         public void callSessionInitiatedFailed(ImsReasonInfo reasonInfo) {
             if (mListener != null) {
                 mListener.callSessionStartFailed(ImsCallSession.this, reasonInfo);
diff --git a/telephony/java/android/telephony/ims/ImsCallSessionListener.java b/telephony/java/android/telephony/ims/ImsCallSessionListener.java
index 86bb5d9..db99acf 100644
--- a/telephony/java/android/telephony/ims/ImsCallSessionListener.java
+++ b/telephony/java/android/telephony/ims/ImsCallSessionListener.java
@@ -53,8 +53,45 @@
     }
 
     /**
-     * A request has been sent out to initiate a new IMS call session and a 1xx response has been
-     * received from the network.
+     * Called when the network first begins to establish the call session and is now connecting
+     * to the remote party. This must be called once after {@link ImsCallSessionImplBase#start} and
+     * before any other method on this listener.  After this is called,
+     * {@link #callSessionProgressing(ImsStreamMediaProfile)} must be called to communicate any
+     * further updates.
+     * <p/>
+     * Once this is called, {@link #callSessionTerminated} must be called
+     * to end the call session.  In the event that the session failed before the remote party
+     * was contacted, {@link #callSessionInitiatingFailed} must be called.
+     *
+     * @param profile the associated {@link ImsCallProfile}.
+     */
+    public void callSessionInitiating(@NonNull ImsCallProfile profile) {
+        try {
+            mListener.callSessionInitiating(profile);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * The IMS call session establishment has failed while initiating.
+     *
+     * @param reasonInfo {@link ImsReasonInfo} detailing the reason of the IMS call session
+     * establishment failure.
+     */
+    public void callSessionInitiatingFailed(@NonNull ImsReasonInfo reasonInfo) {
+        try {
+            mListener.callSessionInitiatingFailed(reasonInfo);
+        } catch (RemoteException e) {
+            e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Called after the network has contacted the remote party and the call state should move to
+     * ALERTING.
+     *
+     * @param profile the associated {@link ImsCallProfile}.
      */
     public void callSessionProgressing(ImsStreamMediaProfile profile) {
         try {
@@ -65,7 +102,8 @@
     }
 
     /**
-     * The IMS call session has been initiated.
+     * Called once the outgoing IMS call session has been begun between the local and remote party.
+     * The call state should move to ACTIVE.
      *
      * @param profile the associated {@link ImsCallProfile}.
      */
@@ -82,7 +120,12 @@
      *
      * @param reasonInfo {@link ImsReasonInfo} detailing the reason of the IMS call session
      * establishment failure.
+     * @deprecated {@link #callSessionInitiated(ImsCallProfile)} is called immediately after
+     * the session is first started which meant that there was no time in which a call to this
+     * method was technically valid.  This method is replaced starting Android S in favor of
+     * {@link #callSessionInitiatingFailed(ImsReasonInfo)}.
      */
+    @Deprecated
     public void callSessionInitiatedFailed(ImsReasonInfo reasonInfo) {
         try {
             mListener.callSessionInitiatedFailed(reasonInfo);
diff --git a/telephony/java/android/telephony/ims/aidl/IImsCallSessionListener.aidl b/telephony/java/android/telephony/ims/aidl/IImsCallSessionListener.aidl
index ed895b7..ed03752 100644
--- a/telephony/java/android/telephony/ims/aidl/IImsCallSessionListener.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsCallSessionListener.aidl
@@ -37,6 +37,8 @@
     /**
      * Notifies the result of the basic session operation (setup / terminate).
      */
+    void callSessionInitiating(in ImsCallProfile profile);
+    void callSessionInitiatingFailed(in ImsReasonInfo reasonInfo);
     void callSessionProgressing(in ImsStreamMediaProfile profile);
     void callSessionInitiated(in ImsCallProfile profile);
     void callSessionInitiatedFailed(in ImsReasonInfo reasonInfo);
diff --git a/test-mock/src/android/test/mock/MockContext.java b/test-mock/src/android/test/mock/MockContext.java
index f7cebd1..98e7b53 100644
--- a/test-mock/src/android/test/mock/MockContext.java
+++ b/test-mock/src/android/test/mock/MockContext.java
@@ -891,6 +891,12 @@
         throw new UnsupportedOperationException();
     }
 
+    /** @hide */
+    @Override
+    public Context createTokenContext(@NonNull IBinder token, @NonNull Display display) {
+        throw new UnsupportedOperationException();
+    }
+
     @Override
     public boolean isDeviceProtectedStorage() {
         throw new UnsupportedOperationException();
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
index d81c24d..ba7770d 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppColdTest.kt
@@ -107,7 +107,7 @@
                                 configuration.endRotation)
                             navBarLayerIsAlwaysVisible(enabled = false)
                             statusBarLayerIsAlwaysVisible(enabled = false)
-                            visibleLayersShownMoreThanOneConsecutiveEntry(bugId = 174541970)
+                            visibleLayersShownMoreThanOneConsecutiveEntry()
 
                             appLayerReplacesWallpaperLayer(testApp.`package`)
                         }
diff --git a/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt b/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
index 70f6386..8e18751 100644
--- a/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
+++ b/tests/net/integration/src/com/android/server/net/integrationtests/ConnectivityServiceIntegrationTest.kt
@@ -25,7 +25,6 @@
 import android.net.ConnectivityManager
 import android.net.IDnsResolver
 import android.net.INetd
-import android.net.INetworkPolicyManager
 import android.net.INetworkStatsService
 import android.net.LinkProperties
 import android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL
@@ -88,8 +87,6 @@
     @Mock
     private lateinit var statsService: INetworkStatsService
     @Mock
-    private lateinit var policyManager: INetworkPolicyManager
-    @Mock
     private lateinit var log: IpConnectivityLog
     @Mock
     private lateinit var netd: INetd
@@ -171,7 +168,7 @@
     }
 
     private inner class TestConnectivityService(deps: Dependencies) : ConnectivityService(
-            context, netManager, statsService, policyManager, dnsResolver, log, netd, deps)
+            context, netManager, statsService, dnsResolver, log, netd, deps)
 
     private fun makeDependencies(): ConnectivityService.Dependencies {
         val deps = spy(ConnectivityService.Dependencies())
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 35eb6ba..3433880 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -166,7 +166,6 @@
 import android.net.INetworkMonitor;
 import android.net.INetworkMonitorCallbacks;
 import android.net.INetworkPolicyListener;
-import android.net.INetworkPolicyManager;
 import android.net.INetworkStatsService;
 import android.net.InetAddresses;
 import android.net.InterfaceConfigurationParcel;
@@ -183,6 +182,7 @@
 import android.net.NetworkFactory;
 import android.net.NetworkInfo;
 import android.net.NetworkInfo.DetailedState;
+import android.net.NetworkPolicyManager;
 import android.net.NetworkRequest;
 import android.net.NetworkSpecifier;
 import android.net.NetworkStack;
@@ -299,6 +299,7 @@
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.function.Predicate;
 import java.util.function.Supplier;
+import java.util.stream.Collectors;
 
 import kotlin.reflect.KClass;
 
@@ -365,7 +366,6 @@
     @Mock INetworkManagementService mNetworkManagementService;
     @Mock INetworkStatsService mStatsService;
     @Mock IBatteryStats mBatteryStatsService;
-    @Mock INetworkPolicyManager mNpm;
     @Mock IDnsResolver mMockDnsResolver;
     @Mock INetd mMockNetd;
     @Mock NetworkStackClient mNetworkStack;
@@ -380,6 +380,7 @@
     @Mock TelephonyManager mTelephonyManager;
     @Mock MockableSystemProperties mSystemProperties;
     @Mock EthernetManager mEthernetManager;
+    @Mock NetworkPolicyManager mNetworkPolicyManager;
 
     private ArgumentCaptor<ResolverParamsParcel> mResolverParamsParcelCaptor =
             ArgumentCaptor.forClass(ResolverParamsParcel.class);
@@ -412,6 +413,7 @@
 
         @Spy private Resources mResources;
         private final LinkedBlockingQueue<Intent> mStartedActivities = new LinkedBlockingQueue<>();
+
         // Map of permission name -> PermissionManager.Permission_{GRANTED|DENIED} constant
         private final HashMap<String, Integer> mMockedPermissions = new HashMap<>();
 
@@ -477,6 +479,7 @@
             if (Context.APP_OPS_SERVICE.equals(name)) return mAppOpsManager;
             if (Context.TELEPHONY_SERVICE.equals(name)) return mTelephonyManager;
             if (Context.ETHERNET_SERVICE.equals(name)) return mEthernetManager;
+            if (Context.NETWORK_POLICY_SERVICE.equals(name)) return mNetworkPolicyManager;
             return super.getSystemService(name);
         }
 
@@ -1326,7 +1329,6 @@
         mService = new ConnectivityService(mServiceContext,
                 mNetworkManagementService,
                 mStatsService,
-                mNpm,
                 mMockDnsResolver,
                 mock(IpConnectivityLog.class),
                 mMockNetd,
@@ -1336,7 +1338,7 @@
 
         final ArgumentCaptor<INetworkPolicyListener> policyListenerCaptor =
                 ArgumentCaptor.forClass(INetworkPolicyListener.class);
-        verify(mNpm).registerListener(policyListenerCaptor.capture());
+        verify(mNetworkPolicyManager).registerListener(policyListenerCaptor.capture());
         mPolicyListener = policyListenerCaptor.getValue();
 
         // Create local CM before sending system ready so that we can answer
@@ -6509,6 +6511,26 @@
         checkNetworkInfo(mCm.getNetworkInfo(type), type, state);
     }
 
+    // Checks that each of the |agents| receive a blocked status change callback with the specified
+    // |blocked| value, in any order. This is needed because when an event affects multiple
+    // networks, ConnectivityService does not guarantee the order in which callbacks are fired.
+    private void assertBlockedCallbackInAnyOrder(TestNetworkCallback callback, boolean blocked,
+            TestNetworkAgentWrapper... agents) {
+        final List<Network> expectedNetworks = Arrays.asList(agents).stream()
+                .map((agent) -> agent.getNetwork())
+                .collect(Collectors.toList());
+
+        // Expect exactly one blocked callback for each agent.
+        for (int i = 0; i < agents.length; i++) {
+            CallbackEntry e = callback.expectCallbackThat(TIMEOUT_MS, (c) ->
+                    c instanceof CallbackEntry.BlockedStatus
+                            && ((CallbackEntry.BlockedStatus) c).getBlocked() == blocked);
+            Network network = e.getNetwork();
+            assertTrue("Received unexpected blocked callback for network " + network,
+                    expectedNetworks.remove(network));
+        }
+    }
+
     @Test
     public void testNetworkBlockedStatusAlwaysOnVpn() throws Exception {
         mServiceContext.setPermission(
@@ -6555,9 +6577,10 @@
         assertNetworkInfo(TYPE_WIFI, DetailedState.BLOCKED);
 
         // Disable lockdown, expect to see the network unblocked.
-        // There are no callbacks because they are not implemented yet.
         mService.setAlwaysOnVpnPackage(userId, null, false /* lockdown */, allowList);
         expectNetworkRejectNonSecureVpn(inOrder, false, firstHalf, secondHalf);
+        callback.expectBlockedStatusCallback(false, mWiFiNetworkAgent);
+        defaultCallback.expectBlockedStatusCallback(false, mWiFiNetworkAgent);
         vpnUidCallback.assertNoCallback();
         assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID));
         assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
@@ -6605,6 +6628,8 @@
         allowList.clear();
         mService.setAlwaysOnVpnPackage(userId, ALWAYS_ON_PACKAGE, true /* lockdown */, allowList);
         expectNetworkRejectNonSecureVpn(inOrder, true, firstHalf, secondHalf);
+        defaultCallback.expectBlockedStatusCallback(true, mWiFiNetworkAgent);
+        assertBlockedCallbackInAnyOrder(callback, true, mWiFiNetworkAgent, mCellNetworkAgent);
         vpnUidCallback.assertNoCallback();
         assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID));
         assertNull(mCm.getActiveNetwork());
@@ -6614,6 +6639,8 @@
 
         // Disable lockdown. Everything is unblocked.
         mService.setAlwaysOnVpnPackage(userId, null, false /* lockdown */, allowList);
+        defaultCallback.expectBlockedStatusCallback(false, mWiFiNetworkAgent);
+        assertBlockedCallbackInAnyOrder(callback, false, mWiFiNetworkAgent, mCellNetworkAgent);
         vpnUidCallback.assertNoCallback();
         assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID));
         assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetwork());
@@ -6647,6 +6674,8 @@
 
         // Enable lockdown and connect a VPN. The VPN is not blocked.
         mService.setAlwaysOnVpnPackage(userId, ALWAYS_ON_PACKAGE, true /* lockdown */, allowList);
+        defaultCallback.expectBlockedStatusCallback(true, mWiFiNetworkAgent);
+        assertBlockedCallbackInAnyOrder(callback, true, mWiFiNetworkAgent, mCellNetworkAgent);
         vpnUidCallback.assertNoCallback();
         assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID));
         assertNull(mCm.getActiveNetwork());
@@ -6658,7 +6687,7 @@
         defaultCallback.expectAvailableThenValidatedCallbacks(mMockVpn);
         vpnUidCallback.assertNoCallback();  // vpnUidCallback has NOT_VPN capability.
         assertEquals(mMockVpn.getNetwork(), mCm.getActiveNetwork());
-        assertEquals(null, mCm.getActiveNetworkForUid(VPN_UID));  // BUG?
+        assertEquals(mWiFiNetworkAgent.getNetwork(), mCm.getActiveNetworkForUid(VPN_UID));
         assertActiveNetworkInfo(TYPE_WIFI, DetailedState.CONNECTED);
         assertNetworkInfo(TYPE_MOBILE, DetailedState.DISCONNECTED);
         assertNetworkInfo(TYPE_VPN, DetailedState.CONNECTED);
diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java
index cc47317..3648c4d 100644
--- a/tests/net/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/net/java/com/android/server/connectivity/VpnTest.java
@@ -27,7 +27,6 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
-import static org.mockito.AdditionalMatchers.aryEq;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyBoolean;
 import static org.mockito.ArgumentMatchers.anyInt;
@@ -89,6 +88,7 @@
 import android.security.KeyStore;
 import android.util.ArrayMap;
 import android.util.ArraySet;
+import android.util.Range;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
@@ -350,7 +350,7 @@
 
         // Set always-on with lockdown.
         assertTrue(vpn.setAlwaysOnPackage(PKGS[1], true, null, mKeyStore));
-        verify(mNetd).networkRejectNonSecureVpn(eq(true), aryEq(new UidRangeParcel[] {
+        verify(mConnectivityManager).setRequireVpnForUids(true, toRanges(new UidRangeParcel[] {
                 new UidRangeParcel(user.start, user.start + PKG_UIDS[1] - 1),
                 new UidRangeParcel(user.start + PKG_UIDS[1] + 1, user.stop)
         }));
@@ -361,12 +361,11 @@
 
         // Switch to another app.
         assertTrue(vpn.setAlwaysOnPackage(PKGS[3], true, null, mKeyStore));
-        verify(mNetd).networkRejectNonSecureVpn(eq(false), aryEq(new UidRangeParcel[] {
+        verify(mConnectivityManager).setRequireVpnForUids(false, toRanges(new UidRangeParcel[] {
                 new UidRangeParcel(user.start, user.start + PKG_UIDS[1] - 1),
                 new UidRangeParcel(user.start + PKG_UIDS[1] + 1, user.stop)
         }));
-
-        verify(mNetd).networkRejectNonSecureVpn(eq(true), aryEq(new UidRangeParcel[] {
+        verify(mConnectivityManager).setRequireVpnForUids(true, toRanges(new UidRangeParcel[] {
                 new UidRangeParcel(user.start, user.start + PKG_UIDS[3] - 1),
                 new UidRangeParcel(user.start + PKG_UIDS[3] + 1, user.stop)
         }));
@@ -383,7 +382,7 @@
         // Set always-on with lockdown and allow app PKGS[2] from lockdown.
         assertTrue(vpn.setAlwaysOnPackage(
                 PKGS[1], true, Collections.singletonList(PKGS[2]), mKeyStore));
-        verify(mNetd).networkRejectNonSecureVpn(eq(true), aryEq(new UidRangeParcel[] {
+        verify(mConnectivityManager).setRequireVpnForUids(true, toRanges(new UidRangeParcel[] {
                 new UidRangeParcel(user.start, user.start + PKG_UIDS[1] - 1),
                 new UidRangeParcel(user.start + PKG_UIDS[2] + 1, user.stop)
         }));
@@ -392,10 +391,10 @@
         // Change allowed app list to PKGS[3].
         assertTrue(vpn.setAlwaysOnPackage(
                 PKGS[1], true, Collections.singletonList(PKGS[3]), mKeyStore));
-        verify(mNetd).networkRejectNonSecureVpn(eq(false), aryEq(new UidRangeParcel[] {
+        verify(mConnectivityManager).setRequireVpnForUids(false, toRanges(new UidRangeParcel[] {
                 new UidRangeParcel(user.start + PKG_UIDS[2] + 1, user.stop)
         }));
-        verify(mNetd).networkRejectNonSecureVpn(eq(true), aryEq(new UidRangeParcel[] {
+        verify(mConnectivityManager).setRequireVpnForUids(true, toRanges(new UidRangeParcel[] {
                 new UidRangeParcel(user.start + PKG_UIDS[1] + 1, user.start + PKG_UIDS[3] - 1),
                 new UidRangeParcel(user.start + PKG_UIDS[3] + 1, user.stop)
         }));
@@ -405,11 +404,11 @@
         // Change the VPN app.
         assertTrue(vpn.setAlwaysOnPackage(
                 PKGS[0], true, Collections.singletonList(PKGS[3]), mKeyStore));
-        verify(mNetd).networkRejectNonSecureVpn(eq(false), aryEq(new UidRangeParcel[] {
+        verify(mConnectivityManager).setRequireVpnForUids(false, toRanges(new UidRangeParcel[] {
                 new UidRangeParcel(user.start, user.start + PKG_UIDS[1] - 1),
                 new UidRangeParcel(user.start + PKG_UIDS[1] + 1, user.start + PKG_UIDS[3] - 1)
         }));
-        verify(mNetd).networkRejectNonSecureVpn(eq(true), aryEq(new UidRangeParcel[] {
+        verify(mConnectivityManager).setRequireVpnForUids(true, toRanges(new UidRangeParcel[] {
                 new UidRangeParcel(user.start, user.start + PKG_UIDS[0] - 1),
                 new UidRangeParcel(user.start + PKG_UIDS[0] + 1, user.start + PKG_UIDS[3] - 1)
         }));
@@ -418,11 +417,11 @@
 
         // Remove the list of allowed packages.
         assertTrue(vpn.setAlwaysOnPackage(PKGS[0], true, null, mKeyStore));
-        verify(mNetd).networkRejectNonSecureVpn(eq(false), aryEq(new UidRangeParcel[] {
+        verify(mConnectivityManager).setRequireVpnForUids(false, toRanges(new UidRangeParcel[] {
                 new UidRangeParcel(user.start + PKG_UIDS[0] + 1, user.start + PKG_UIDS[3] - 1),
                 new UidRangeParcel(user.start + PKG_UIDS[3] + 1, user.stop)
         }));
-        verify(mNetd).networkRejectNonSecureVpn(eq(true), aryEq(new UidRangeParcel[] {
+        verify(mConnectivityManager).setRequireVpnForUids(true, toRanges(new UidRangeParcel[] {
                 new UidRangeParcel(user.start + PKG_UIDS[0] + 1, user.stop),
         }));
         assertBlocked(vpn, user.start + PKG_UIDS[1], user.start + PKG_UIDS[2],
@@ -432,10 +431,10 @@
         // Add the list of allowed packages.
         assertTrue(vpn.setAlwaysOnPackage(
                 PKGS[0], true, Collections.singletonList(PKGS[1]), mKeyStore));
-        verify(mNetd).networkRejectNonSecureVpn(eq(false), aryEq(new UidRangeParcel[] {
+        verify(mConnectivityManager).setRequireVpnForUids(false, toRanges(new UidRangeParcel[] {
                 new UidRangeParcel(user.start + PKG_UIDS[0] + 1, user.stop)
         }));
-        verify(mNetd).networkRejectNonSecureVpn(eq(true), aryEq(new UidRangeParcel[] {
+        verify(mConnectivityManager).setRequireVpnForUids(true, toRanges(new UidRangeParcel[] {
                 new UidRangeParcel(user.start + PKG_UIDS[0] + 1, user.start + PKG_UIDS[1] - 1),
                 new UidRangeParcel(user.start + PKG_UIDS[1] + 1, user.stop)
         }));
@@ -450,11 +449,11 @@
         // allowed package should change from PGKS[1] to PKGS[2].
         assertTrue(vpn.setAlwaysOnPackage(
                 PKGS[0], true, Arrays.asList("com.foo.app", PKGS[2], "com.bar.app"), mKeyStore));
-        verify(mNetd).networkRejectNonSecureVpn(eq(false), aryEq(new UidRangeParcel[]{
+        verify(mConnectivityManager).setRequireVpnForUids(false, toRanges(new UidRangeParcel[] {
                 new UidRangeParcel(user.start + PKG_UIDS[0] + 1, user.start + PKG_UIDS[1] - 1),
                 new UidRangeParcel(user.start + PKG_UIDS[1] + 1, user.stop)
         }));
-        verify(mNetd).networkRejectNonSecureVpn(eq(true), aryEq(new UidRangeParcel[]{
+        verify(mConnectivityManager).setRequireVpnForUids(true, toRanges(new UidRangeParcel[] {
                 new UidRangeParcel(user.start + PKG_UIDS[0] + 1, user.start + PKG_UIDS[2] - 1),
                 new UidRangeParcel(user.start + PKG_UIDS[2] + 1, user.stop)
         }));
@@ -475,7 +474,7 @@
 
         // Set lockdown.
         assertTrue(vpn.setAlwaysOnPackage(PKGS[3], true, null, mKeyStore));
-        verify(mNetd).networkRejectNonSecureVpn(eq(true), aryEq(new UidRangeParcel[] {
+        verify(mConnectivityManager).setRequireVpnForUids(true, toRanges(new UidRangeParcel[] {
                 new UidRangeParcel(user.start, user.start + PKG_UIDS[3] - 1),
                 new UidRangeParcel(user.start + PKG_UIDS[3] + 1, user.stop)
         }));
@@ -485,7 +484,7 @@
         // Add the restricted user.
         setMockedUsers(primaryUser, tempProfile);
         vpn.onUserAdded(tempProfile.id);
-        verify(mNetd).networkRejectNonSecureVpn(eq(true), aryEq(new UidRangeParcel[] {
+        verify(mConnectivityManager).setRequireVpnForUids(true, toRanges(new UidRangeParcel[] {
                 new UidRangeParcel(profile.start, profile.start + PKG_UIDS[3] - 1),
                 new UidRangeParcel(profile.start + PKG_UIDS[3] + 1, profile.stop)
         }));
@@ -493,7 +492,7 @@
         // Remove the restricted user.
         tempProfile.partial = true;
         vpn.onUserRemoved(tempProfile.id);
-        verify(mNetd).networkRejectNonSecureVpn(eq(false), aryEq(new UidRangeParcel[] {
+        verify(mConnectivityManager).setRequireVpnForUids(false, toRanges(new UidRangeParcel[] {
                 new UidRangeParcel(profile.start, profile.start + PKG_UIDS[3] - 1),
                 new UidRangeParcel(profile.start + PKG_UIDS[3] + 1, profile.stop)
         }));
@@ -506,22 +505,29 @@
                 new UidRangeParcel(PRI_USER_RANGE.start, PRI_USER_RANGE.stop)};
         // Given legacy lockdown is already enabled,
         vpn.setLockdown(true);
-
-        verify(mNetd).networkRejectNonSecureVpn(eq(true), aryEq(primaryUserRangeParcel));
+        verify(mConnectivityManager, times(1)).setRequireVpnForUids(true,
+                toRanges(primaryUserRangeParcel));
 
         // Enabling legacy lockdown twice should do nothing.
         vpn.setLockdown(true);
-        verify(mNetd, times(1))
-                .networkRejectNonSecureVpn(anyBoolean(), any(UidRangeParcel[].class));
+        verify(mConnectivityManager, times(1)).setRequireVpnForUids(anyBoolean(), any());
 
         // And disabling should remove the rules exactly once.
         vpn.setLockdown(false);
-        verify(mNetd).networkRejectNonSecureVpn(eq(false), aryEq(primaryUserRangeParcel));
+        verify(mConnectivityManager, times(1)).setRequireVpnForUids(false,
+                toRanges(primaryUserRangeParcel));
 
         // Removing the lockdown again should have no effect.
         vpn.setLockdown(false);
-        verify(mNetd, times(2)).networkRejectNonSecureVpn(
-                anyBoolean(), any(UidRangeParcel[].class));
+        verify(mConnectivityManager, times(2)).setRequireVpnForUids(anyBoolean(), any());
+    }
+
+    private ArrayList<Range<Integer>> toRanges(UidRangeParcel[] ranges) {
+        ArrayList<Range<Integer>> rangesArray = new ArrayList<>(ranges.length);
+        for (int i = 0; i < ranges.length; i++) {
+            rangesArray.add(new Range<>(ranges[i].start, ranges[i].stop));
+        }
+        return rangesArray;
     }
 
     @Test
@@ -535,21 +541,21 @@
             new UidRangeParcel(entireUser[0].start + PKG_UIDS[0] + 1, entireUser[0].stop)
         };
 
-        final InOrder order = inOrder(mNetd);
+        final InOrder order = inOrder(mConnectivityManager);
 
         // Given lockdown is enabled with no package (legacy VPN),
         vpn.setLockdown(true);
-        order.verify(mNetd).networkRejectNonSecureVpn(eq(true), aryEq(entireUser));
+        order.verify(mConnectivityManager).setRequireVpnForUids(true, toRanges(entireUser));
 
         // When a new VPN package is set the rules should change to cover that package.
         vpn.prepare(null, PKGS[0], VpnManager.TYPE_VPN_SERVICE);
-        order.verify(mNetd).networkRejectNonSecureVpn(eq(false), aryEq(entireUser));
-        order.verify(mNetd).networkRejectNonSecureVpn(eq(true), aryEq(exceptPkg0));
+        order.verify(mConnectivityManager).setRequireVpnForUids(false, toRanges(entireUser));
+        order.verify(mConnectivityManager).setRequireVpnForUids(true, toRanges(exceptPkg0));
 
         // When that VPN package is unset, everything should be undone again in reverse.
         vpn.prepare(null, VpnConfig.LEGACY_VPN, VpnManager.TYPE_VPN_SERVICE);
-        order.verify(mNetd).networkRejectNonSecureVpn(eq(false), aryEq(exceptPkg0));
-        order.verify(mNetd).networkRejectNonSecureVpn(eq(true), aryEq(entireUser));
+        order.verify(mConnectivityManager).setRequireVpnForUids(false, toRanges(exceptPkg0));
+        order.verify(mConnectivityManager).setRequireVpnForUids(true, toRanges(entireUser));
     }
 
     @Test
diff --git a/tests/net/java/com/android/server/net/NetworkStatsCollectionTest.java b/tests/net/java/com/android/server/net/NetworkStatsCollectionTest.java
index 89146f9..435c3c0 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsCollectionTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsCollectionTest.java
@@ -64,7 +64,6 @@
 
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
-import java.io.DataOutputStream;
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.InputStream;
@@ -124,7 +123,7 @@
 
         // now export into a unified format
         final ByteArrayOutputStream bos = new ByteArrayOutputStream();
-        collection.write(new DataOutputStream(bos));
+        collection.write(bos);
 
         // clear structure completely
         collection.reset();
@@ -152,7 +151,7 @@
 
         // now export into a unified format
         final ByteArrayOutputStream bos = new ByteArrayOutputStream();
-        collection.write(new DataOutputStream(bos));
+        collection.write(bos);
 
         // clear structure completely
         collection.reset();
@@ -180,7 +179,7 @@
 
         // now export into a unified format
         final ByteArrayOutputStream bos = new ByteArrayOutputStream();
-        collection.write(new DataOutputStream(bos));
+        collection.write(bos);
 
         // clear structure completely
         collection.reset();
diff --git a/tools/powerstats/OWNERS b/tools/powerstats/OWNERS
index d68066b..12f13ea 100644
--- a/tools/powerstats/OWNERS
+++ b/tools/powerstats/OWNERS
@@ -1 +1 @@
-include /services/core/java/com/android/server/power/OWNERS
+include /services/core/java/com/android/server/powerstats/OWNERS
diff --git a/wifi/MOVED.txt b/wifi/MOVED.txt
new file mode 100644
index 0000000..6ffb23c
--- /dev/null
+++ b/wifi/MOVED.txt
@@ -0,0 +1,8 @@
+Source code and tests for Wifi module APIs have moved to
+packages/modules/Wifi/framework.
+
+- frameworks/base/wifi/java -> packages/modules/Wifi/framework/java
+- frameworks/base/wifi/tests -> packages/modules/Wifi/framework/tests
+
+What remains in frameworks/base/wifi are Wifi APIs that
+are not part of the Wifi module.