Merge "Add accessibility floating menu settings key"
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/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/SearchResults.java b/apex/appsearch/framework/java/android/app/appsearch/SearchResults.java
index 05735cc..82c2d26 100644
--- a/apex/appsearch/framework/java/android/app/appsearch/SearchResults.java
+++ b/apex/appsearch/framework/java/android/app/appsearch/SearchResults.java
@@ -45,33 +45,35 @@
 
     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,
+    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);
     }
 
@@ -90,11 +92,14 @@
             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 +109,24 @@
         }
     }
 
+    @Override
+    public void close() {
+        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));
+            }
+        };
+    }
+
     private void invokeCallback(AppSearchResult result,
             @NonNull Consumer<AppSearchResult<List<SearchResult>>> callback) {
         if (result.isSuccess()) {
@@ -120,23 +143,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/synced_jetpack_changeid.txt b/apex/appsearch/synced_jetpack_changeid.txt
index f9a0bed..5b818c7 100644
--- a/apex/appsearch/synced_jetpack_changeid.txt
+++ b/apex/appsearch/synced_jetpack_changeid.txt
@@ -1 +1 @@
-I0577839bfddf95a555399df441d317b00c7c7c48
+Idd770a064edfeb6dc648571fc6706c087b8e605a
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/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..2464dda 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -11641,6 +11641,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 +11898,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 +12347,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 +16254,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();
@@ -18966,8 +18976,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 {
@@ -19303,9 +19328,11 @@
     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);
@@ -19366,18 +19393,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 +19457,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 +19961,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 +21913,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 +24277,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 +24294,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 +24304,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 +45279,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 +47966,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 +50238,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);
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 94a6576..f22b0f4 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 {
@@ -116,14 +114,14 @@
     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);
-    method public void registerRemoteVolumeControllerCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.session.MediaSessionManager.RemoteVolumeControllerCallback);
-    method public void unregisterRemoteVolumeControllerCallback(@NonNull android.media.session.MediaSessionManager.RemoteVolumeControllerCallback);
+    method public void registerRemoteSessionCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.session.MediaSessionManager.RemoteSessionCallback);
+    method public void unregisterRemoteSessionCallback(@NonNull android.media.session.MediaSessionManager.RemoteSessionCallback);
     field public static final int RESULT_MEDIA_KEY_HANDLED = 1; // 0x1
     field public static final int RESULT_MEDIA_KEY_NOT_HANDLED = 0; // 0x0
   }
 
-  public static interface MediaSessionManager.RemoteVolumeControllerCallback {
-    method public void onSessionChanged(@Nullable android.media.session.MediaSession.Token);
+  public static interface MediaSessionManager.RemoteSessionCallback {
+    method public void onDefaultRemoteSessionChanged(@Nullable android.media.session.MediaSession.Token);
     method public void onVolumeChanged(@NonNull android.media.session.MediaSession.Token, int);
   }
 
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 576963d..8d748e6 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";
@@ -1346,7 +1347,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 +1373,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 +2222,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 +3938,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();
@@ -9776,7 +9774,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";
   }
@@ -12127,7 +12124,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..4fb283a 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 {
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/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/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/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/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/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/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/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/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/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/os/image/OWNERS b/core/java/android/os/image/OWNERS
index 389b55b..08a51ff 100644
--- a/core/java/android/os/image/OWNERS
+++ b/core/java/android/os/image/OWNERS
@@ -1 +1,3 @@
+include /packages/DynamicSystemInstallationService/OWNERS
+
 andrewhsieh@google.com
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/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..6a8dca1
--- /dev/null
+++ b/core/java/android/permission/PermissionUsageHelper.java
@@ -0,0 +1,441 @@
+/*
+ * 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) {
+        if (!shouldShowIndicators()) {
+            return null;
+        }
+
+        List<String> ops = 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<PermGroupUsage> usages = new ArrayList<>();
+        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/BaseColumns.java b/core/java/android/provider/BaseColumns.java
index b216e2b..667bb29 100644
--- a/core/java/android/provider/BaseColumns.java
+++ b/core/java/android/provider/BaseColumns.java
@@ -19,12 +19,16 @@
 public interface BaseColumns {
     /**
      * The unique ID for a row.
+     *
+     * <p>Type: INTEGER (long)</p>
      */
     // @Column(Cursor.FIELD_TYPE_INTEGER)
     public static final String _ID = "_id";
 
     /**
      * The count of rows in a directory.
+     *
+     * <p>Type: INTEGER</p>
      */
     // @Column(Cursor.FIELD_TYPE_INTEGER)
     public static final String _COUNT = "_count";
diff --git a/core/java/android/provider/CalendarContract.java b/core/java/android/provider/CalendarContract.java
index 1ee2f19..92a1883 100644
--- a/core/java/android/provider/CalendarContract.java
+++ b/core/java/android/provider/CalendarContract.java
@@ -40,6 +40,7 @@
 import android.net.Uri;
 import android.os.Build;
 import android.os.RemoteException;
+import android.os.StrictMode;
 import android.text.format.DateUtils;
 import android.text.format.TimeMigrationUtils;
 import android.util.Log;
@@ -2619,8 +2620,14 @@
             intent.setData(ContentUris.withAppendedId(CalendarContract.CONTENT_URI, alarmTime));
             intent.putExtra(ALARM_TIME, alarmTime);
             intent.setFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+
+            // Disable strict mode VM policy violations temporarily for intents that contain a
+            // content URI but don't have FLAG_GRANT_READ_URI_PERMISSION.
+            StrictMode.VmPolicy oldVmPolicy = StrictMode.allowVmViolations();
             PendingIntent pi = PendingIntent.getBroadcast(context, 0, intent,
                     PendingIntent.FLAG_IMMUTABLE);
+            StrictMode.setVmPolicy(oldVmPolicy);
+
             manager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, alarmTime, pi);
         }
 
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/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..c945c2d
--- /dev/null
+++ b/core/java/android/service/notification/NotificationListenerFilter.java
@@ -0,0 +1,102 @@
+/**
+ * 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_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;
+        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..ccde0bc 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -242,6 +242,23 @@
     public @interface NotificationCancelReason{};
 
     /**
+     * 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;
+
+    /**
      * 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/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/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/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/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/widget/TextView.java b/core/java/android/widget/TextView.java
index 977a0e8..4edfc5f 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -11862,16 +11862,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/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/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/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/FastDataOutput.java b/core/java/com/android/internal/util/FastDataOutput.java
index 83d26e1..cf5b296 100644
--- a/core/java/com/android/internal/util/FastDataOutput.java
+++ b/core/java/com/android/internal/util/FastDataOutput.java
@@ -115,8 +115,7 @@
 
         // Magnitude of this returned value indicates the number of bytes
         // required to encode the string; sign indicates success/failure
-        int len = CharsetUtils.toModifiedUtf8Bytes(s, mBufferPtr, mBufferPos + 2,
-                mBufferCap - mBufferPos - 2);
+        int len = CharsetUtils.toModifiedUtf8Bytes(s, mBufferPtr, mBufferPos + 2, mBufferCap);
         if (Math.abs(len) > MAX_UNSIGNED_SHORT) {
             throw new IOException("Modified UTF-8 length too large: " + len);
         }
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/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/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/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/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/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/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 2e12795..e44ab34 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,9 @@
         <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" />
     </privapp-permissions>
 
     <privapp-permissions package="com.android.statementservice">
@@ -464,6 +469,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..145ad13 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",
@@ -475,6 +469,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 +1531,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/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/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/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/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/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/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index 4b283e4..519ce36 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -64,9 +64,9 @@
 import com.android.wm.shell.R;
 import com.android.wm.shell.ShellTaskOrganizer;
 import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
 import com.android.wm.shell.pip.phone.PipMotionHelper;
 import com.android.wm.shell.pip.phone.PipUpdateThread;
-import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -251,7 +251,7 @@
      * If set to {@code true}, the entering animation will be skipped and we will wait for
      * {@link #onFixedRotationFinished(int)} callback to actually enter PiP.
      */
-    private boolean mShouldDeferEnteringPip;
+    private boolean mWaitForFixedRotation;
 
     /**
      * If set to {@code true}, no entering PiP transition would be kicked off and most likely
@@ -259,7 +259,7 @@
      * auto PiP-able Activity to home.
      * See also {@link #startSwipePipToHome(ComponentName, ActivityInfo, PictureInPictureParams)}.
      */
-    private boolean mShouldIgnoreEnteringPipTransition;
+    private boolean mInSwipePipToHomeTransition;
 
     public PipTaskOrganizer(Context context, @NonNull PipBoundsState pipBoundsState,
             @NonNull PipBoundsAlgorithm boundsHandler,
@@ -304,7 +304,7 @@
     }
 
     public boolean isDeferringEnterPipAnimation() {
-        return mState.isInPip() && mShouldDeferEnteringPip;
+        return mState.isInPip() && mWaitForFixedRotation;
     }
 
     /**
@@ -336,9 +336,12 @@
      */
     public Rect startSwipePipToHome(ComponentName componentName, ActivityInfo activityInfo,
             PictureInPictureParams pictureInPictureParams) {
-        mShouldIgnoreEnteringPipTransition = true;
+        mInSwipePipToHomeTransition = true;
         sendOnPipTransitionStarted(componentName, TRANSITION_DIRECTION_TO_PIP);
         setBoundsStateForEntry(componentName, pictureInPictureParams, activityInfo);
+        // disable the conflicting transaction from fixed rotation, see also
+        // onFixedRotationStarted and onFixedRotationFinished
+        mWaitForFixedRotation = false;
         return mPipBoundsAlgorithm.getEntryDestinationBounds();
     }
 
@@ -348,7 +351,7 @@
      */
     public void stopSwipePipToHome(ComponentName componentName, Rect destinationBounds) {
         // do nothing if there is no startSwipePipToHome being called before
-        if (mShouldIgnoreEnteringPipTransition) {
+        if (mInSwipePipToHomeTransition) {
             mPipBoundsState.setBounds(destinationBounds);
         }
     }
@@ -502,7 +505,7 @@
 
         mPipMenuController.attach(leash);
 
-        if (mShouldIgnoreEnteringPipTransition) {
+        if (mInSwipePipToHomeTransition) {
             final Rect destinationBounds = mPipBoundsState.getBounds();
             // animation is finished in the Launcher and here we directly apply the final touch.
             applyEnterPipSyncTransaction(destinationBounds, () -> {
@@ -510,11 +513,11 @@
                 finishResizeForMenu(destinationBounds);
                 sendOnPipTransitionFinished(TRANSITION_DIRECTION_TO_PIP);
             });
-            mShouldIgnoreEnteringPipTransition = false;
+            mInSwipePipToHomeTransition = false;
             return;
         }
 
-        if (mShouldDeferEnteringPip) {
+        if (mWaitForFixedRotation) {
             if (DEBUG) Log.d(TAG, "Defer entering PiP animation, fixed rotation is ongoing");
             // if deferred, hide the surface till fixed rotation is completed
             final SurfaceControl.Transaction tx =
@@ -666,8 +669,8 @@
             Log.wtf(TAG, "Unrecognized token: " + token);
             return;
         }
-        mShouldDeferEnteringPip = false;
-        mShouldIgnoreEnteringPipTransition = false;
+        mWaitForFixedRotation = false;
+        mInSwipePipToHomeTransition = false;
         mPictureInPictureParams = null;
         mState = State.UNDEFINED;
         mPipUiEventLoggerLogger.setTaskInfo(null);
@@ -694,17 +697,17 @@
 
     @Override
     public void onFixedRotationStarted(int displayId, int newRotation) {
-        mShouldDeferEnteringPip = true;
+        mWaitForFixedRotation = true;
     }
 
     @Override
     public void onFixedRotationFinished(int displayId) {
-        if (mShouldDeferEnteringPip && mState.isInPip()) {
+        if (mWaitForFixedRotation && mState.isInPip()) {
             final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds();
             // schedule a regular animation to ensure all the callbacks are still being sent
             enterPipWithAlphaAnimation(destinationBounds, 0 /* durationMs */);
         }
-        mShouldDeferEnteringPip = false;
+        mWaitForFixedRotation = false;
     }
 
     /**
@@ -723,6 +726,12 @@
     public void onMovementBoundsChanged(Rect destinationBoundsOut, boolean fromRotation,
             boolean fromImeAdjustment, boolean fromShelfAdjustment,
             WindowContainerTransaction wct) {
+        // note that this can be called when swiping pip to home is happening. For instance,
+        // swiping an app in landscape to portrait home. skip this entirely if that's the case.
+        if (mInSwipePipToHomeTransition && fromRotation) {
+            if (DEBUG) Log.d(TAG, "skip onMovementBoundsChanged due to swipe-pip-to-home");
+            return;
+        }
         final PipAnimationController.PipTransitionAnimator animator =
                 mPipAnimationController.getCurrentAnimator();
         if (animator == null || !animator.isRunning()
@@ -806,7 +815,7 @@
      */
     public void scheduleAnimateResizePip(Rect toBounds, int duration,
             Consumer<Rect> updateBoundsCallback) {
-        if (mShouldDeferEnteringPip) {
+        if (mWaitForFixedRotation) {
             Log.d(TAG, "skip scheduleAnimateResizePip, entering pip deferred");
             return;
         }
@@ -820,7 +829,7 @@
      */
     public void scheduleAnimateResizePip(Rect fromBounds, Rect toBounds, int duration,
             Consumer<Rect> updateBoundsCallback) {
-        if (mShouldDeferEnteringPip) {
+        if (mWaitForFixedRotation) {
             Log.d(TAG, "skip scheduleAnimateResizePip, entering pip deferred");
             return;
         }
@@ -922,7 +931,7 @@
         if (mState.shouldBlockResizeRequest()) {
             return;
         }
-        if (mShouldDeferEnteringPip) {
+        if (mWaitForFixedRotation) {
             Log.d(TAG, "skip scheduleOffsetPip, entering pip deferred");
             return;
         }
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/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/hwui/DisplayListOps.in b/libs/hwui/DisplayListOps.in
index c6c4ba8..1b1be43 100644
--- a/libs/hwui/DisplayListOps.in
+++ b/libs/hwui/DisplayListOps.in
@@ -40,7 +40,6 @@
 X(DrawDrawable)
 X(DrawPicture)
 X(DrawImage)
-X(DrawImageNine)
 X(DrawImageRect)
 X(DrawImageLattice)
 X(DrawTextBlob)
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index a495ec4..6bf2e99 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -306,42 +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); }
-};
-struct DrawImageNine final : Op {
-    static const auto kType = Type::DrawImageNine;
-    DrawImageNine(sk_sp<const SkImage>&& image, const SkIRect& center, const SkRect& dst,
-                  const SkPaint* paint)
-            : image(std::move(image)), center(center), dst(dst) {
-        if (paint) {
-            this->paint = *paint;
-        }
-    }
-    sk_sp<const SkImage> image;
-    SkIRect center;
-    SkRect dst;
-    SkPaint paint;
     void draw(SkCanvas* c, const SkMatrix&) const {
-        c->drawImageNine(image.get(), center, dst, &paint);
+        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;
@@ -349,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;
@@ -375,6 +365,7 @@
     int xs, ys, fs;
     SkIRect src;
     SkRect dst;
+    SkFilterMode filter;
     SkPaint paint;
     BitmapPalette palette;
     void draw(SkCanvas* c, const SkMatrix&) const {
@@ -383,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);
     }
 };
 
@@ -448,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;
         }
@@ -460,7 +453,8 @@
     }
     sk_sp<const SkImage> atlas;
     int count;
-    SkBlendMode xfermode;
+    SkBlendMode mode;
+    SkSamplingOptions sampling;
     SkRect cull = kUnset;
     SkPaint paint;
     bool has_colors;
@@ -469,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 {
@@ -630,20 +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);
-}
-void DisplayListData::drawImageNine(sk_sp<const SkImage> image, const SkIRect& center,
-                                    const SkRect& dst, const SkPaint* paint) {
-    this->push<DrawImageNine>(0, std::move(image), center, dst, paint);
+                                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;
@@ -651,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);
 }
@@ -671,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) {
@@ -908,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;
@@ -933,28 +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::onDrawImageNine(const SkImage* img, const SkIRect& center, const SkRect& dst,
-                                      const SkPaint* paint) {
-    fDL->drawImageNine(sk_ref_sp(img), center, dst, paint);
+
+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::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::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],
@@ -970,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 4851148..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,26 +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 onDrawImageNine(const SkImage*, const SkIRect&, 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.h b/libs/hwui/SkiaCanvas.h
index 591ae5c..584321e 100644
--- a/libs/hwui/SkiaCanvas.h
+++ b/libs/hwui/SkiaCanvas.h
@@ -190,7 +190,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/hwui/ImageDecoder.cpp b/libs/hwui/hwui/ImageDecoder.cpp
index 43cc4f2..6889134 100644
--- a/libs/hwui/hwui/ImageDecoder.cpp
+++ b/libs/hwui/hwui/ImageDecoder.cpp
@@ -40,13 +40,14 @@
 ImageDecoder::ImageDecoder(std::unique_ptr<SkAndroidCodec> codec, sk_sp<SkPngChunkReader> peeker)
     : mCodec(std::move(codec))
     , mPeeker(std::move(peeker))
-    , mTargetSize(mCodec->getInfo().dimensions())
-    , mDecodeSize(mTargetSize)
+    , mDecodeSize(mCodec->codec()->dimensions())
     , mOutColorType(mCodec->computeOutputColorType(kN32_SkColorType))
     , mUnpremultipliedRequired(false)
     , mOutColorSpace(getDefaultColorSpace())
     , mSampleSize(1)
 {
+    mTargetSize = swapWidthHeight() ? SkISize { mDecodeSize.height(), mDecodeSize.width() }
+                                    : mDecodeSize;
 }
 
 SkAlphaType ImageDecoder::getOutAlphaType() const {
@@ -77,7 +78,8 @@
         }
     }
 
-    SkISize targetSize = { width, height }, decodeSize = targetSize;
+    SkISize targetSize = { width, height };
+    SkISize decodeSize = swapWidthHeight() ? SkISize { height, width } : targetSize;
     int sampleSize = mCodec->computeSampleSize(&decodeSize);
 
     if (decodeSize != targetSize && mUnpremultipliedRequired && !opaque()) {
@@ -157,6 +159,22 @@
     return SkImageInfo::Make(size, mOutColorType, getOutAlphaType(), getOutputColorSpace());
 }
 
+bool ImageDecoder::swapWidthHeight() const {
+    return SkEncodedOriginSwapsWidthHeight(mCodec->codec()->getOrigin());
+}
+
+int ImageDecoder::width() const {
+    return swapWidthHeight()
+            ? mCodec->codec()->dimensions().height()
+            : mCodec->codec()->dimensions().width();
+}
+
+int ImageDecoder::height() const {
+    return swapWidthHeight()
+            ? mCodec->codec()->dimensions().width()
+            : mCodec->codec()->dimensions().height();
+}
+
 bool ImageDecoder::opaque() const {
     return mCodec->getInfo().alphaType() == kOpaque_SkAlphaType;
 }
@@ -174,7 +192,9 @@
     // FIXME: Use scanline decoding on only a couple lines to save memory. b/70709380.
     SkBitmap tmp;
     const bool scale = mDecodeSize != mTargetSize;
-    if (scale || mCropRect) {
+    const auto origin = mCodec->codec()->getOrigin();
+    const bool handleOrigin = origin != kDefault_SkEncodedOrigin;
+    if (scale || handleOrigin || mCropRect) {
         if (!tmp.setInfo(decodeInfo)) {
             return SkCodec::kInternalError;
         }
@@ -189,7 +209,7 @@
     options.fSampleSize = mSampleSize;
     auto result = mCodec->getAndroidPixels(decodeInfo, decodePixels, decodeRowBytes, &options);
 
-    if (scale || mCropRect) {
+    if (scale || handleOrigin || mCropRect) {
         SkBitmap scaledBm;
         if (!scaledBm.installPixels(getOutputInfo(), pixels, rowBytes)) {
             return SkCodec::kInternalError;
@@ -200,15 +220,27 @@
         paint.setFilterQuality(kLow_SkFilterQuality);  // bilinear filtering
 
         SkCanvas canvas(scaledBm, SkCanvas::ColorBehavior::kLegacy);
+        SkMatrix outputMatrix;
         if (mCropRect) {
-            canvas.translate(-mCropRect->fLeft, -mCropRect->fTop);
-        }
-        if (scale) {
-            float scaleX = (float) mTargetSize.width()  / mDecodeSize.width();
-            float scaleY = (float) mTargetSize.height() / mDecodeSize.height();
-            canvas.scale(scaleX, scaleY);
+            outputMatrix.setTranslate(-mCropRect->fLeft, -mCropRect->fTop);
         }
 
+        int targetWidth  = mTargetSize.width();
+        int targetHeight = mTargetSize.height();
+        if (handleOrigin) {
+            outputMatrix.preConcat(SkEncodedOriginToMatrix(origin, targetWidth, targetHeight));
+            if (SkEncodedOriginSwapsWidthHeight(origin)) {
+                std::swap(targetWidth, targetHeight);
+            }
+        }
+
+        if (scale) {
+            float scaleX = (float) targetWidth  / mDecodeSize.width();
+            float scaleY = (float) targetHeight / mDecodeSize.height();
+            outputMatrix.preScale(scaleX, scaleY);
+        }
+
+        canvas.setMatrix(outputMatrix);
         canvas.drawBitmap(tmp, 0.0f, 0.0f, &paint);
     }
 
diff --git a/libs/hwui/hwui/ImageDecoder.h b/libs/hwui/hwui/ImageDecoder.h
index a1b5157..a08e924 100644
--- a/libs/hwui/hwui/ImageDecoder.h
+++ b/libs/hwui/hwui/ImageDecoder.h
@@ -49,7 +49,11 @@
     // The size is the final size after scaling and cropping.
     SkImageInfo getOutputInfo() const;
 
+    int width() const;
+    int height() const;
+
     bool opaque() const;
+
     bool gray() const;
 
     SkCodec::Result decode(void* pixels, size_t rowBytes);
@@ -68,6 +72,7 @@
 
     SkAlphaType getOutAlphaType() const;
     sk_sp<SkColorSpace> getOutputColorSpace() const;
+    bool swapWidthHeight() const;
 };
 
 } // namespace android
diff --git a/libs/hwui/jni/ImageDecoder.cpp b/libs/hwui/jni/ImageDecoder.cpp
index da91d46..96e912f 100644
--- a/libs/hwui/jni/ImageDecoder.cpp
+++ b/libs/hwui/jni/ImageDecoder.cpp
@@ -135,19 +135,15 @@
         return throw_exception(env, kSourceException, "", jexception, source);
     }
 
-    auto androidCodec = SkAndroidCodec::MakeFromCodec(std::move(codec),
-            SkAndroidCodec::ExifOrientationBehavior::kRespect);
+    auto androidCodec = SkAndroidCodec::MakeFromCodec(std::move(codec));
     if (!androidCodec.get()) {
         return throw_exception(env, kSourceMalformedData, "", nullptr, source);
     }
 
-    const auto& info = androidCodec->getInfo();
-    const int width = info.width();
-    const int height = info.height();
     const bool isNinePatch = peeker->mPatch != nullptr;
     ImageDecoder* decoder = new ImageDecoder(std::move(androidCodec), std::move(peeker));
     return env->NewObject(gImageDecoder_class, gImageDecoder_constructorMethodID,
-                          reinterpret_cast<jlong>(decoder), width, height,
+                          reinterpret_cast<jlong>(decoder), decoder->width(), decoder->height(),
                           animated, isNinePatch);
 }
 
diff --git a/libs/hwui/pipeline/skia/DumpOpsCanvas.h b/libs/hwui/pipeline/skia/DumpOpsCanvas.h
index 0eb526a..26ff8bf 100644
--- a/libs/hwui/pipeline/skia/DumpOpsCanvas.h
+++ b/libs/hwui/pipeline/skia/DumpOpsCanvas.h
@@ -86,22 +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 onDrawImageNine(const SkImage*, const SkIRect& center, const SkRect& dst,
-                         const SkPaint*) override {
-        mOutput << mIdent << "drawImageNine" << 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..a436278 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -185,18 +185,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 +207,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 +239,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 +257,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 +292,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/tests/common/CallCountingCanvas.h b/libs/hwui/tests/common/CallCountingCanvas.h
index 594afd0..d3c41191 100644
--- a/libs/hwui/tests/common/CallCountingCanvas.h
+++ b/libs/hwui/tests/common/CallCountingCanvas.h
@@ -108,33 +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 drawImageNineCount = 0;
-    void onDrawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst,
-                         const SkPaint* paint) override {
-        drawImageNineCount++;
-    }
-
     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++;
     }
 
@@ -171,4 +165,4 @@
 
 } /* namespace test */
 } /* namespace uirenderer */
-} /* namespace android */
\ No newline at end of file
+} /* namespace android */
diff --git a/libs/hwui/tests/unit/FatalTestCanvas.h b/libs/hwui/tests/unit/FatalTestCanvas.h
index 76ae085..2a74afc 100644
--- a/libs/hwui/tests/unit/FatalTestCanvas.h
+++ b/libs/hwui/tests/unit/FatalTestCanvas.h
@@ -60,25 +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 onDrawImageNine(const SkImage*, const SkIRect& center, const SkRect& dst, const SkPaint*) {
-        ADD_FAILURE() << "onDrawImageNine 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/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/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..621fe1b 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -21,6 +21,7 @@
 import android.location.Criteria;
 import android.location.GeocoderParams;
 import android.location.Geofence;
+import android.location.GnssCapabilities;
 import android.location.GnssMeasurementCorrections;
 import android.location.GnssMeasurementRequest;
 import android.location.IGeocodeListener;
@@ -28,17 +29,17 @@
 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,13 +72,16 @@
         double upperRightLatitude, double upperRightLongitude, int maxResults,
         in GeocoderParams params, in IGeocodeListener listener);
 
-    long getGnssCapabilities();
+    GnssCapabilities getGnssCapabilities();
     int getGnssYearOfHardware();
     String getGnssHardwareModelName();
 
     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);
@@ -93,6 +97,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..00381a6 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -61,7 +61,6 @@
 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;
@@ -381,6 +380,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 +405,15 @@
         }
     }
 
+    private GnssNmeaTransportMultiplexer getGnssNmeaTransportMultiplexer() {
+        synchronized (mLock) {
+            if (mGnssNmeaTransportMultiplexer == null) {
+                mGnssNmeaTransportMultiplexer = new GnssNmeaTransportMultiplexer();
+            }
+            return mGnssNmeaTransportMultiplexer;
+        }
+    }
+
     private GnssMeasurementsTransportMultiplexer getGnssMeasurementsTransportMultiplexer() {
         synchronized (mLock) {
             if (mGnssMeasurementsTransportMultiplexer == null) {
@@ -1616,6 +1626,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 +1732,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 +1757,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 +1882,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 +2103,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 +2122,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() {
@@ -2170,8 +2220,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 +2240,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 +2261,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 +2311,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 +2330,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 +2351,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 +2366,7 @@
     public boolean addNmeaListener(
             @NonNull @CallbackExecutor Executor executor,
             @NonNull OnNmeaMessageListener listener) {
-        getGnssStatusTransportMultiplexer().addListener(listener, executor);
+        getGnssNmeaTransportMultiplexer().addListener(listener, executor);
         return true;
     }
 
@@ -2311,7 +2376,7 @@
      * @param listener a {@link OnNmeaMessageListener} object to remove
      */
     public void removeNmeaListener(@NonNull OnNmeaMessageListener listener) {
-        getGnssStatusTransportMultiplexer().removeListener(listener);
+        getGnssNmeaTransportMultiplexer().removeListener(listener);
     }
 
     /**
@@ -2339,10 +2404,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 +2424,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 +2448,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 +2472,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 +2525,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,12 +2554,14 @@
     }
 
     /**
-     * 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()}.
+     *
+     * @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
@@ -2503,7 +2578,7 @@
     /**
      * Unregisters a GNSS Antenna Info listener.
      *
-     * @param listener a {@link GnssAntennaInfo.Listener} object to remove.
+     * @param listener a {@link GnssAntennaInfo.Listener} object to remove
      */
     public void unregisterAntennaInfoListener(@NonNull GnssAntennaInfo.Listener listener) {
         getGnssAntennaInfoTransportMultiplexer().removeListener(listener);
@@ -2534,10 +2609,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 +2629,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 +2653,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 +2689,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 +2895,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 +2942,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 +2994,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));
             }
         }
     }
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..588edc3 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,25 @@
     }
 
     /**
+     * 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;
+        }
+        // TODO(175802592): change this to AudioManager.getDeviceForPortId() when available
+        AudioDeviceInfo[] devices =
+                AudioManager.getDevicesStatic(AudioManager.GET_DEVICES_OUTPUTS);
+        for (int i = 0; i < devices.length; i++) {
+            if (devices[i].getId() == mDeviceId) {
+                return devices[i];
+            }
+        }
+        return null;
+    }
+
+    /**
      * @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 +391,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 +484,7 @@
 
     @Override
     public int hashCode() {
-        return Objects.hash(mPlayerIId, mPlayerType, mClientUid, mClientPid);
+        return Objects.hash(mPlayerIId, mDeviceId, mPlayerType, mClientUid, mClientPid);
     }
 
     @Override
@@ -447,6 +495,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 +510,7 @@
 
     private AudioPlaybackConfiguration(Parcel in) {
         mPlayerIId = in.readInt();
+        mDeviceId = in.readInt();
         mPlayerType = in.readInt();
         mClientUid = in.readInt();
         mClientPid = in.readInt();
@@ -478,6 +528,7 @@
         AudioPlaybackConfiguration that = (AudioPlaybackConfiguration) o;
 
         return ((mPlayerIId == that.mPlayerIId)
+                && (mDeviceId == that.mDeviceId)
                 && (mPlayerType == that.mPlayerType)
                 && (mClientUid == that.mClientUid)
                 && (mClientPid == that.mClientPid));
@@ -486,6 +537,7 @@
     @Override
     public String toString() {
         return "AudioPlaybackConfiguration piid:" + mPlayerIId
+                + " deviceId:" + mDeviceId
                 + " type:" + toLogFriendlyPlayerType(mPlayerType)
                 + " u/pid:" + mClientUid + "/" + mClientPid
                 + " state:" + toLogFriendlyPlayerState(mPlayerState)
@@ -571,6 +623,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/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..9f0df20 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();
     }
 
     /**
@@ -3496,23 +3503,45 @@
         return null;
     }
 
-    /*
-     * Call BEFORE adding a routing callback handler.
-     */
-    @GuardedBy("mRoutingChangeListeners")
-    private void testEnableNativeRoutingCallbacksLocked() {
-        if (mRoutingChangeListeners.size() == 0) {
-            native_enableDeviceCallback();
+    private void tryToDisableNativeRoutingCallback() {
+        synchronized (mRoutingChangeListeners) {
+            if (mEnableSelfRoutingMonitor) {
+                mEnableSelfRoutingMonitor = false;
+                testDisableNativeRoutingCallbacksLocked();
+            }
         }
     }
 
+    /**
+     * 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();
+                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 +3557,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 +3659,7 @@
      */
     private void broadcastRoutingChange() {
         AudioManager.resetAudioPortGeneration();
+        baseUpdateDeviceId(getRoutedDevice());
         synchronized (mRoutingChangeListeners) {
             for (NativeRoutingEventHandlerDelegate delegate : mRoutingChangeListeners.values()) {
                 delegate.notifyClient();
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/IRemoteVolumeControllerCallback.aidl b/media/java/android/media/IRemoteSessionCallback.aidl
similarity index 95%
rename from media/java/android/media/IRemoteVolumeControllerCallback.aidl
rename to media/java/android/media/IRemoteSessionCallback.aidl
index 34c6361..e16c87e 100644
--- a/media/java/android/media/IRemoteVolumeControllerCallback.aidl
+++ b/media/java/android/media/IRemoteSessionCallback.aidl
@@ -25,7 +25,7 @@
  * TODO add in better support for multiple remote sessions.
  * @hide
  */
-oneway interface IRemoteVolumeControllerCallback {
+oneway interface IRemoteSessionCallback {
     void onVolumeChanged(in MediaSession.Token sessionToken, int flags);
     // sets the default session to use with the slider, replaces remoteSliderVisibility
     // on IVolumeController
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..8e5af13 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;
@@ -1497,13 +1494,72 @@
         return null;
     }
 
-    /*
-     * Call BEFORE adding a routing callback handler or AFTER removing a routing callback handler.
+
+    /**
+     * 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();
+            }
+        }
+    }
+
+    /**
+     * Call BEFORE adding a routing callback handler and when enabling self routing listener
+     * @return returns true for success, false otherwise.
      */
     @GuardedBy("mRoutingChangeListeners")
-    private void enableNativeRoutingCallbacksLocked(boolean enabled) {
-        if (mRoutingChangeListeners.size() == 0) {
-            native_enableDeviceCallback(enabled);
+    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 AFTER removing a routing callback handler and when disabling self routing listener
+     */
+    @GuardedBy("mRoutingChangeListeners")
+    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 +1572,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 +1588,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 +1607,8 @@
         synchronized (mRoutingChangeListeners) {
             if (mRoutingChangeListeners.containsKey(listener)) {
                 mRoutingChangeListeners.remove(listener);
-                enableNativeRoutingCallbacksLocked(false);
             }
+            testDisableNativeRoutingCallbacksLocked();
         }
     }
 
@@ -3301,6 +3360,7 @@
 
     @Override
     protected void finalize() {
+        tryToDisableNativeRoutingCallback();
         baseRelease();
         native_finalize();
     }
@@ -3415,6 +3475,8 @@
 
             case MEDIA_STOPPED:
                 {
+                    tryToDisableNativeRoutingCallback();
+                    baseStop();
                     TimeProvider timeProvider = mTimeProvider;
                     if (timeProvider != null) {
                         timeProvider.onStopped();
@@ -3423,8 +3485,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 +3660,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 +3866,7 @@
     private final OnCompletionListener mOnCompletionInternalListener = new OnCompletionListener() {
         @Override
         public void onCompletion(MediaPlayer mp) {
+            tryToDisableNativeRoutingCallback();
             baseStop();
         }
     };
diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java
index d0304f2..36f7bed 100644
--- a/media/java/android/media/MediaRouter.java
+++ b/media/java/android/media/MediaRouter.java
@@ -23,9 +23,6 @@
 import android.annotation.Nullable;
 import android.annotation.SystemService;
 import android.app.ActivityThread;
-import android.bluetooth.BluetoothA2dp;
-import android.bluetooth.BluetoothDevice;
-import android.bluetooth.BluetoothHearingAid;
 import android.compat.annotation.UnsupportedAppUsage;
 import android.content.BroadcastReceiver;
 import android.content.Context;
@@ -102,7 +99,6 @@
 
         RouteInfo mDefaultAudioVideo;
         RouteInfo mBluetoothA2dpRoute;
-        volatile boolean mHasActiveBluetoothDevices;
 
         RouteInfo mSelectedRoute;
 
@@ -176,20 +172,14 @@
                     new IntentFilter(DisplayManager.ACTION_WIFI_DISPLAY_STATUS_CHANGED));
             appContext.registerReceiver(new VolumeChangeReceiver(),
                     new IntentFilter(AudioManager.VOLUME_CHANGED_ACTION));
-            IntentFilter intentFilter = new IntentFilter();
-            intentFilter.addAction(BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED);
-            intentFilter.addAction(BluetoothHearingAid.ACTION_ACTIVE_DEVICE_CHANGED);
-            appContext.registerReceiver(new BluetoothStateChangedReceiver(), intentFilter);
 
             mDisplayService.registerDisplayListener(this, mHandler);
 
             AudioRoutesInfo newAudioRoutes = null;
             try {
                 newAudioRoutes = mAudioService.startWatchingRoutes(mAudioRoutesObserver);
-                mHasActiveBluetoothDevices = mAudioService.isBluetoothA2dpOn();
             } catch (RemoteException e) {
             }
-
             if (newAudioRoutes != null) {
                 // This will select the active BT route if there is one and the current
                 // selected route is the default system route, or if there is no selected
@@ -263,8 +253,7 @@
             }
 
             if (audioRoutesChanged) {
-                Log.v(TAG, "Audio routes updated: " + newRoutes + ", hasActiveBTDevices="
-                        + mHasActiveBluetoothDevices);
+                Log.v(TAG, "Audio routes updated: " + newRoutes + ", a2dp=" + isBluetoothA2dpOn());
                 if (mSelectedRoute == null || mSelectedRoute == mDefaultAudioVideo
                         || mSelectedRoute == mBluetoothA2dpRoute) {
                     if (forceUseDefaultRoute || mBluetoothA2dpRoute == null) {
@@ -288,6 +277,15 @@
             return mStreamVolume.get(streamType);
         }
 
+        boolean isBluetoothA2dpOn() {
+            try {
+                return mBluetoothA2dpRoute != null && mAudioService.isBluetoothA2dpOn();
+            } catch (RemoteException e) {
+                Log.e(TAG, "Error querying Bluetooth A2DP state", e);
+                return false;
+            }
+        }
+
         void updateDiscoveryRequest() {
             // What are we looking for today?
             int routeTypes = 0;
@@ -396,7 +394,7 @@
         }
 
         void updateSelectedRouteForId(String routeId) {
-            RouteInfo selectedRoute = sStatic.mHasActiveBluetoothDevices
+            RouteInfo selectedRoute = isBluetoothA2dpOn()
                     ? mBluetoothA2dpRoute : mDefaultAudioVideo;
             final int count = mRoutes.size();
             for (int i = 0; i < count; i++) {
@@ -1045,7 +1043,7 @@
         Log.v(TAG, "Selecting route: " + route);
         assert(route != null);
         final RouteInfo oldRoute = sStatic.mSelectedRoute;
-        final RouteInfo currentSystemRoute = sStatic.mHasActiveBluetoothDevices
+        final RouteInfo currentSystemRoute = sStatic.isBluetoothA2dpOn()
                 ? sStatic.mBluetoothA2dpRoute : sStatic.mDefaultAudioVideo;
         boolean wasDefaultOrBluetoothRoute = (oldRoute == sStatic.mDefaultAudioVideo
                 || oldRoute == sStatic.mBluetoothA2dpRoute);
@@ -1108,8 +1106,7 @@
 
     static void selectDefaultRouteStatic() {
         // TODO: Be smarter about the route types here; this selects for all valid.
-        if (sStatic.mSelectedRoute != sStatic.mBluetoothA2dpRoute
-                && sStatic.mHasActiveBluetoothDevices) {
+        if (sStatic.mSelectedRoute != sStatic.mBluetoothA2dpRoute && sStatic.isBluetoothA2dpOn()) {
             selectRouteStatic(ROUTE_TYPE_ANY, sStatic.mBluetoothA2dpRoute, false);
         } else {
             selectRouteStatic(ROUTE_TYPE_ANY, sStatic.mDefaultAudioVideo, false);
@@ -1446,8 +1443,13 @@
         if (selectedRoute == sStatic.mBluetoothA2dpRoute ||
                 selectedRoute == sStatic.mDefaultAudioVideo) {
             dispatchRouteVolumeChanged(selectedRoute);
-        } else if (sStatic.mHasActiveBluetoothDevices) {
-            dispatchRouteVolumeChanged(sStatic.mBluetoothA2dpRoute);
+        } else if (sStatic.mBluetoothA2dpRoute != null) {
+            try {
+                dispatchRouteVolumeChanged(sStatic.mAudioService.isBluetoothA2dpOn() ?
+                        sStatic.mBluetoothA2dpRoute : sStatic.mDefaultAudioVideo);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Error checking Bluetooth A2DP state to report volume change", e);
+            }
         } else {
             dispatchRouteVolumeChanged(sStatic.mDefaultAudioVideo);
         }
@@ -3170,17 +3172,4 @@
             }
         }
     }
-
-    static class BluetoothStateChangedReceiver extends BroadcastReceiver {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            switch (intent.getAction()) {
-                case BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED:
-                case BluetoothHearingAid.ACTION_ACTIVE_DEVICE_CHANGED:
-                    sStatic.mHasActiveBluetoothDevices =
-                            intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE) != null;
-                    break;
-            }
-        }
-    }
 }
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/session/ISessionManager.aidl b/media/java/android/media/session/ISessionManager.aidl
index f157d7f..66d5794 100644
--- a/media/java/android/media/session/ISessionManager.aidl
+++ b/media/java/android/media/session/ISessionManager.aidl
@@ -17,7 +17,7 @@
 
 import android.content.ComponentName;
 import android.content.pm.ParceledListSlice;
-import android.media.IRemoteVolumeControllerCallback;
+import android.media.IRemoteSessionCallback;
 import android.media.Session2Token;
 import android.media.session.IActiveSessionsListener;
 import android.media.session.IOnMediaKeyEventDispatchedListener;
@@ -57,8 +57,8 @@
     void addSession2TokensListener(in ISession2TokensListener listener, int userId);
     void removeSession2TokensListener(in ISession2TokensListener listener);
 
-    void registerRemoteVolumeControllerCallback(in IRemoteVolumeControllerCallback rvc);
-    void unregisterRemoteVolumeControllerCallback(in IRemoteVolumeControllerCallback rvc);
+    void registerRemoteSessionCallback(in IRemoteSessionCallback rvc);
+    void unregisterRemoteSessionCallback(in IRemoteSessionCallback rvc);
 
     // For PhoneWindowManager to precheck media keys
     boolean isGlobalPriorityActive();
diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java
index 00ee914..f22bcd8 100644
--- a/media/java/android/media/session/MediaSessionManager.java
+++ b/media/java/android/media/session/MediaSessionManager.java
@@ -27,10 +27,11 @@
 import android.content.Context;
 import android.content.pm.ParceledListSlice;
 import android.media.AudioManager;
-import android.media.IRemoteVolumeControllerCallback;
+import android.media.IRemoteSessionCallback;
 import android.media.MediaFrameworkPlatformInitializer;
 import android.media.MediaSession2;
 import android.media.Session2Token;
+import android.media.VolumeProvider;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.RemoteException;
@@ -87,8 +88,8 @@
     private final OnMediaKeyEventSessionChangedListenerStub
             mOnMediaKeyEventSessionChangedListenerStub =
             new OnMediaKeyEventSessionChangedListenerStub();
-    private final RemoteVolumeControllerCallbackStub mRemoteVolumeControllerCallbackStub =
-            new RemoteVolumeControllerCallbackStub();
+    private final RemoteSessionCallbackStub mRemoteSessionCallbackStub =
+            new RemoteSessionCallbackStub();
 
     private final Object mLock = new Object();
     @GuardedBy("mLock")
@@ -108,8 +109,8 @@
     @GuardedBy("mLock")
     private MediaSession.Token mCurMediaKeyEventSession;
     @GuardedBy("mLock")
-    private final Map<RemoteVolumeControllerCallback, Executor>
-            mRemoteVolumeControllerCallbacks = new ArrayMap<>();
+    private final Map<RemoteSessionCallback, Executor>
+            mRemoteSessionCallbacks = new ArrayMap<>();
 
     private Context mContext;
     private OnVolumeKeyLongPressListenerImpl mOnVolumeKeyLongPressListener;
@@ -482,27 +483,29 @@
      * Set the remote volume controller callback to receive volume updates on.
      * Only for use by System UI and Settings application.
      *
+     * @param executor The executor on which the callback should be invoked
      * @param callback The volume controller callback to receive updates on.
+     *
      * @hide
      */
     @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
-    public void registerRemoteVolumeControllerCallback(
+    public void registerRemoteSessionCallback(
             @NonNull @CallbackExecutor Executor executor,
-            @NonNull RemoteVolumeControllerCallback callback) {
+            @NonNull RemoteSessionCallback callback) {
         Objects.requireNonNull(executor, "executor shouldn't be null");
         Objects.requireNonNull(callback, "callback shouldn't be null");
         boolean shouldRegisterCallback = false;
         synchronized (mLock) {
-            int prevCallbackCount = mRemoteVolumeControllerCallbacks.size();
-            mRemoteVolumeControllerCallbacks.put(callback, executor);
-            if (prevCallbackCount == 0 && mRemoteVolumeControllerCallbacks.size() == 1) {
+            int prevCallbackCount = mRemoteSessionCallbacks.size();
+            mRemoteSessionCallbacks.put(callback, executor);
+            if (prevCallbackCount == 0 && mRemoteSessionCallbacks.size() == 1) {
                 shouldRegisterCallback = true;
             }
         }
         if (shouldRegisterCallback) {
             try {
-                mService.registerRemoteVolumeControllerCallback(
-                        mRemoteVolumeControllerCallbackStub);
+                mService.registerRemoteSessionCallback(
+                        mRemoteSessionCallbackStub);
             } catch (RemoteException e) {
                 Log.e(TAG, "Failed to register remote volume controller callback", e);
             }
@@ -511,27 +514,27 @@
 
     /**
      * Unregisters the remote volume controller callback which was previously registered with
-     * {@link #registerRemoteVolumeControllerCallback(Executor, RemoteVolumeControllerCallback)}.
+     * {@link #registerRemoteSessionCallback(Executor, RemoteSessionCallback)}.
      * Only for use by System UI and Settings application.
      *
      * @param callback The volume controller callback to receive updates on.
      * @hide
      */
     @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
-    public void unregisterRemoteVolumeControllerCallback(
-            @NonNull RemoteVolumeControllerCallback callback) {
+    public void unregisterRemoteSessionCallback(
+            @NonNull RemoteSessionCallback callback) {
         Objects.requireNonNull(callback, "callback shouldn't be null");
         boolean shouldUnregisterCallback = false;
         synchronized (mLock) {
-            if (mRemoteVolumeControllerCallbacks.remove(callback) != null
-                    && mRemoteVolumeControllerCallbacks.size() == 0) {
+            if (mRemoteSessionCallbacks.remove(callback) != null
+                    && mRemoteSessionCallbacks.size() == 0) {
                 shouldUnregisterCallback = true;
             }
         }
         try {
             if (shouldUnregisterCallback) {
-                mService.unregisterRemoteVolumeControllerCallback(
-                        mRemoteVolumeControllerCallbackStub);
+                mService.unregisterRemoteSessionCallback(
+                        mRemoteSessionCallbackStub);
             }
         } catch (RemoteException e) {
             Log.e(TAG, "Failed to unregister remote volume controller callback", e);
@@ -1107,26 +1110,34 @@
     }
 
     /**
-     * Callback to receive changes in the remote volume controller.
+     * Callback to receive changes in the existing remote sessions. A remote session is a
+     * {@link MediaSession} that is connected to a remote player via
+     * {@link MediaSession#setPlaybackToRemote(VolumeProvider)}
      *
      * @hide
      */
     @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
-    public interface RemoteVolumeControllerCallback {
+    public interface RemoteSessionCallback {
         /**
-         * Called when the volume is changed.
+         * Called when the volume is changed for the given session. Flags that are defined in
+         * {@link AudioManager} will also be sent and will contain information about how to
+         * handle the volume change. For example, {@link AudioManager#FLAG_SHOW_UI} indicates that a
+         * toast showing the volume should be shown.
          *
          * @param sessionToken the remote media session token
-         * @param flags any of the flags from {@link AudioManager}
+         * @param flags extra information about how to handle the volume change
          */
         void onVolumeChanged(@NonNull MediaSession.Token sessionToken, int flags);
 
         /**
-         * Called when the session for the default remote controller is changed.
+         * Called when the default remote session is changed where the default remote session
+         * denotes an active remote session that has the highest priority for receiving key events.
+         * Null will be sent if there are currently no active remote sessions.
          *
-         * @param sessionToken the remote media session token
+         * @param sessionToken the token of the default remote session, a session with the highest
+         *                     priority for receiving key events.
          */
-        void onSessionChanged(@Nullable MediaSession.Token sessionToken);
+        void onDefaultRemoteSessionChanged(@Nullable MediaSession.Token sessionToken);
     }
 
     /**
@@ -1362,27 +1373,27 @@
         }
     }
 
-    private final class RemoteVolumeControllerCallbackStub
-            extends IRemoteVolumeControllerCallback.Stub {
+    private final class RemoteSessionCallbackStub
+            extends IRemoteSessionCallback.Stub {
         @Override
         public void onVolumeChanged(MediaSession.Token sessionToken, int flags) {
-            Map<RemoteVolumeControllerCallback, Executor> callbacks = new ArrayMap<>();
+            Map<RemoteSessionCallback, Executor> callbacks = new ArrayMap<>();
             synchronized (mLock) {
-                callbacks.putAll(mRemoteVolumeControllerCallbacks);
+                callbacks.putAll(mRemoteSessionCallbacks);
             }
-            for (Map.Entry<RemoteVolumeControllerCallback, Executor> e : callbacks.entrySet()) {
+            for (Map.Entry<RemoteSessionCallback, Executor> e : callbacks.entrySet()) {
                 e.getValue().execute(() -> e.getKey().onVolumeChanged(sessionToken, flags));
             }
         }
 
         @Override
         public void onSessionChanged(MediaSession.Token sessionToken) {
-            Map<RemoteVolumeControllerCallback, Executor> callbacks = new ArrayMap<>();
+            Map<RemoteSessionCallback, Executor> callbacks = new ArrayMap<>();
             synchronized (mLock) {
-                callbacks.putAll(mRemoteVolumeControllerCallbacks);
+                callbacks.putAll(mRemoteSessionCallbacks);
             }
-            for (Map.Entry<RemoteVolumeControllerCallback, Executor> e : callbacks.entrySet()) {
-                e.getValue().execute(() -> e.getKey().onSessionChanged(sessionToken));
+            for (Map.Entry<RemoteSessionCallback, Executor> e : callbacks.entrySet()) {
+                e.getValue().execute(() -> e.getKey().onDefaultRemoteSessionChanged(sessionToken));
             }
         }
     }
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/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/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/graphics/jni/imagedecoder.cpp b/native/graphics/jni/imagedecoder.cpp
index ac4c16a..4aeebe4 100644
--- a/native/graphics/jni/imagedecoder.cpp
+++ b/native/graphics/jni/imagedecoder.cpp
@@ -65,17 +65,18 @@
     SkCodec::Result result;
     auto codec = SkCodec::MakeFromStream(std::move(stream), &result, nullptr,
                                          SkCodec::SelectionPolicy::kPreferAnimation);
-    auto androidCodec = SkAndroidCodec::MakeFromCodec(std::move(codec),
-            SkAndroidCodec::ExifOrientationBehavior::kRespect);
+    // These may be swapped due to the SkEncodedOrigin, but we're just checking
+    // them to make sure they fit in int32_t.
+    auto dimensions = codec->dimensions();
+    auto androidCodec = SkAndroidCodec::MakeFromCodec(std::move(codec));
     if (!androidCodec) {
         return ResultToErrorCode(result);
     }
 
     // AImageDecoderHeaderInfo_getWidth/Height return an int32_t. Ensure that
     // the conversion is safe.
-    const auto& info = androidCodec->getInfo();
-    if (info.width() > std::numeric_limits<int32_t>::max()
-        || info.height() > std::numeric_limits<int32_t>::max()) {
+    if (dimensions.width() > std::numeric_limits<int32_t>::max() ||
+        dimensions.height() > std::numeric_limits<int32_t>::max()) {
         return ANDROID_IMAGE_DECODER_INVALID_INPUT;
     }
 
@@ -200,14 +201,14 @@
     if (!info) {
         return 0;
     }
-    return toDecoder(info)->mCodec->getInfo().width();
+    return toDecoder(info)->width();
 }
 
 int32_t AImageDecoderHeaderInfo_getHeight(const AImageDecoderHeaderInfo* info) {
     if (!info) {
         return 0;
     }
-    return toDecoder(info)->mCodec->getInfo().height();
+    return toDecoder(info)->height();
 }
 
 const char* AImageDecoderHeaderInfo_getMimeType(const AImageDecoderHeaderInfo* info) {
diff --git a/packages/DynamicSystemInstallationService/OWNERS b/packages/DynamicSystemInstallationService/OWNERS
index 60910c4..c1b7ec4 100644
--- a/packages/DynamicSystemInstallationService/OWNERS
+++ b/packages/DynamicSystemInstallationService/OWNERS
@@ -1,3 +1,3 @@
-yochiang@google.com
 howardsoc@google.com
 pchsueh@google.com
+yochiang@google.com
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/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/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/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-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index 4cc6775..db2ffe5 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -212,9 +212,9 @@
     <string name="adb_wireless_settings" msgid="2295017847215680229">"ניפוי באגים אלחוטי"</string>
     <string name="adb_wireless_list_empty_off" msgid="1713707973837255490">"כדי להציג את המכשירים הזמינים ולהשתמש בהם, יש להפעיל ניפוי באגים אלחוטי"</string>
     <string name="adb_pair_method_qrcode_title" msgid="6982904096137468634">"‏התאמת מכשיר באמצעות קוד QR"</string>
-    <string name="adb_pair_method_qrcode_summary" msgid="7130694277228970888">"‏התאמת מכשירים חדשים באמצעות סורק של קודי QR"</string>
+    <string name="adb_pair_method_qrcode_summary" msgid="7130694277228970888">"‏התאמת מכשירים חדשים באמצעות סורק קודי QR"</string>
     <string name="adb_pair_method_code_title" msgid="1122590300445142904">"התאמת מכשיר באמצעות קוד התאמה"</string>
-    <string name="adb_pair_method_code_summary" msgid="6370414511333685185">"התאמת מכשירים חדשים באמצעות קוד בן שש ספרות"</string>
+    <string name="adb_pair_method_code_summary" msgid="6370414511333685185">"התאמת מכשירים חדשים באמצעות קוד בן 6 ספרות"</string>
     <string name="adb_paired_devices_title" msgid="5268997341526217362">"מכשירים מותאמים"</string>
     <string name="adb_wireless_device_connected_summary" msgid="3039660790249148713">"מחובר עכשיו"</string>
     <string name="adb_wireless_device_details_title" msgid="7129369670526565786">"פרטי מכשיר"</string>
@@ -226,12 +226,12 @@
     <string name="adb_pairing_device_dialog_pairing_code_label" msgid="3639239786669722731">"‏קוד התאמה של Wi-Fi"</string>
     <string name="adb_pairing_device_dialog_failed_title" msgid="3426758947882091735">"ההתאמה נכשלה"</string>
     <string name="adb_pairing_device_dialog_failed_msg" msgid="6611097519661997148">"יש לוודא שהמכשיר מחובר לאותה רשת."</string>
-    <string name="adb_wireless_qrcode_summary" msgid="8051414549011801917">"‏יש לסרוק קוד QR כדי להתאים מכשיר באמצעות Wi‑Fi"</string>
+    <string name="adb_wireless_qrcode_summary" msgid="8051414549011801917">"‏כדי להתאים מכשיר דרך Wi‑Fi, יש לסרוק קוד QR"</string>
     <string name="adb_wireless_verifying_qrcode_text" msgid="6123192424916029207">"המכשיר בתהליך התאמה…"</string>
     <string name="adb_qrcode_pairing_device_failed_msg" msgid="6936292092592914132">"‏התאמת המכשיר נכשלה. קוד ה-QR היה שגוי או שהמכשיר לא מחובר לאותה רשת."</string>
     <string name="adb_wireless_ip_addr_preference_title" msgid="8335132107715311730">"‏יציאה וכתובת IP"</string>
     <string name="adb_wireless_qrcode_pairing_title" msgid="1906409667944674707">"‏סריקת קוד QR"</string>
-    <string name="adb_wireless_qrcode_pairing_description" msgid="6014121407143607851">"‏יש לסרוק קוד QR כדי להתאים מכשיר באמצעות Wi‑Fi"</string>
+    <string name="adb_wireless_qrcode_pairing_description" msgid="6014121407143607851">"‏כדי להתאים מכשיר דרך Wi‑Fi, יש לסרוק קוד QR"</string>
     <string name="adb_wireless_no_network_msg" msgid="2365795244718494658">"‏יש להתחבר לרשת Wi-Fi"</string>
     <string name="keywords_adb_wireless" msgid="6507505581882171240">"‏adb, ניפוי באגים, פיתוח"</string>
     <string name="bugreport_in_power" msgid="8664089072534638709">"קיצור של דוח באגים"</string>
diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml
index 122b481..b7de429 100644
--- a/packages/SettingsLib/res/values-ml/strings.xml
+++ b/packages/SettingsLib/res/values-ml/strings.xml
@@ -145,7 +145,7 @@
     <string name="data_usage_ota" msgid="7984667793701597001">"സിസ്‌റ്റം അപ്‌ഡേറ്റുകൾ"</string>
     <string name="tether_settings_title_usb" msgid="3728686573430917722">"USB ടെതറിംഗ്"</string>
     <string name="tether_settings_title_wifi" msgid="4803402057533895526">"പോർട്ടബിൾ ഹോട്ട്സ്‌പോട്ട്"</string>
-    <string name="tether_settings_title_bluetooth" msgid="916519902721399656">"ബ്ലൂടൂത്ത് ടെതറിംഗ്"</string>
+    <string name="tether_settings_title_bluetooth" msgid="916519902721399656">"Bluetooth ടെതറിംഗ്"</string>
     <string name="tether_settings_title_usb_bluetooth" msgid="1727111807207577322">"ടെതറിംഗ്"</string>
     <string name="tether_settings_title_all" msgid="8910259483383010470">"ടെതറിംഗും പോർട്ടബിൾ ഹോട്ട്സ്‌പോട്ടും"</string>
     <string name="managed_user_title" msgid="449081789742645723">"എല്ലാ ഔദ്യോഗിക ആപ്‌സും"</string>
diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml
index 5774d9b..821e897b 100644
--- a/packages/SettingsLib/res/values-sq/strings.xml
+++ b/packages/SettingsLib/res/values-sq/strings.xml
@@ -203,9 +203,9 @@
     <string name="vpn_settings_not_available" msgid="2894137119965668920">"Cilësimet e VPN-së nuk ofrohen për këtë përdorues"</string>
     <string name="tethering_settings_not_available" msgid="266821736434699780">"Cilësimet e ndarjes nuk ofrohen për këtë përdorues"</string>
     <string name="apn_settings_not_available" msgid="1147111671403342300">"Cilësimet e \"Emrit të pikës së qasjes\" nuk mund të përdoren për këtë përdorues"</string>
-    <string name="enable_adb" msgid="8072776357237289039">"Korrigjimi i USB-së"</string>
+    <string name="enable_adb" msgid="8072776357237289039">"Korrigjimi përmes USB-së"</string>
     <string name="enable_adb_summary" msgid="3711526030096574316">"Korrigjo gabimet e modalitetit kur UBS-ja është e lidhur"</string>
-    <string name="clear_adb_keys" msgid="3010148733140369917">"Anulo autorizimet e korrigjimeve të gabimeve të USB-së"</string>
+    <string name="clear_adb_keys" msgid="3010148733140369917">"Anulo autorizimet e korrigjimeve përmes USB-së"</string>
     <string name="enable_adb_wireless" msgid="6973226350963971018">"Korrigjimi përmes Wi-Fi"</string>
     <string name="enable_adb_wireless_summary" msgid="7344391423657093011">"Modaliteti i korrigjimit kur Wi‑Fi është i lidhur"</string>
     <string name="adb_wireless_error" msgid="721958772149779856">"Gabim"</string>
@@ -225,7 +225,7 @@
     <string name="adb_pairing_device_dialog_title" msgid="7141739231018530210">"Çifto me pajisjen"</string>
     <string name="adb_pairing_device_dialog_pairing_code_label" msgid="3639239786669722731">"Kodi i çiftimit të Wi‑Fi"</string>
     <string name="adb_pairing_device_dialog_failed_title" msgid="3426758947882091735">"Çiftimi ishte i pasuksesshëm"</string>
-    <string name="adb_pairing_device_dialog_failed_msg" msgid="6611097519661997148">"Sigurohu që pajisja të jetë e lidhur me të njëjtin rrjet"</string>
+    <string name="adb_pairing_device_dialog_failed_msg" msgid="6611097519661997148">"Sigurohu që pajisja të jetë e lidhur me të njëjtin rrjet."</string>
     <string name="adb_wireless_qrcode_summary" msgid="8051414549011801917">"Çifto pajisjen përmes Wi‑Fi duke skanuar një kod QR"</string>
     <string name="adb_wireless_verifying_qrcode_text" msgid="6123192424916029207">"Po çifton pajisjen…"</string>
     <string name="adb_qrcode_pairing_device_failed_msg" msgid="6936292092592914132">"Çiftimi i pajisjes dështoi. Ose kodi QR nuk ishte i saktë, ose pajisja nuk është e lidhur me të njëjtin rrjet."</string>
@@ -299,11 +299,11 @@
     <string name="debug_view_attributes" msgid="3539609843984208216">"Aktivizo shikimin e inspektimit të atributeve"</string>
     <string name="mobile_data_always_on_summary" msgid="1112156365594371019">"Mbaji të dhënat celulare gjithmonë aktive edhe kur Wi‑Fi është aktiv (për ndërrim të shpejtë të rrjetit)."</string>
     <string name="tethering_hardware_offload_summary" msgid="7801345335142803029">"Përdor përshpejtimin e harduerit për ndarjen e lidhjes (internet) nëse është i disponueshëm"</string>
-    <string name="adb_warning_title" msgid="7708653449506485728">"Të lejohet korrigjimi i USB-së?"</string>
-    <string name="adb_warning_message" msgid="8145270656419669221">"Korrigjuesi i USB-së është vetëm për qëllime zhvillimore. Përdore për të kopjuar të dhëna mes kompjuterit dhe pajisjes tënde, për të instaluar aplikacione në pajisjen tënde pa asnjë njoftim si dhe për të lexuar të dhënat e ditarit."</string>
+    <string name="adb_warning_title" msgid="7708653449506485728">"Të lejohet korrigjimi përmes USB-së?"</string>
+    <string name="adb_warning_message" msgid="8145270656419669221">"Korrigjuesi përmes USB-së është vetëm për qëllime zhvillimore. Përdore për të kopjuar të dhëna mes kompjuterit dhe pajisjes tënde, për të instaluar aplikacione në pajisjen tënde pa asnjë njoftim si dhe për të lexuar të dhënat e evidencave."</string>
     <string name="adbwifi_warning_title" msgid="727104571653031865">"Të lejohet korrigjimi përmes Wi-Fi?"</string>
     <string name="adbwifi_warning_message" msgid="8005936574322702388">"Korrigjimin përmes Wi-Fi është vetëm për qëllime zhvillimore. Përdore për të kopjuar të dhëna mes kompjuterit dhe pajisjes sate, për të instaluar aplikacione në pajisjen tënde pa asnjë njoftim si dhe për të lexuar të dhënat e regjistrit."</string>
-    <string name="adb_keys_warning_message" msgid="2968555274488101220">"Të bllokohet qasja për korrigjim të USB-së nga të gjithë kompjuterët që ke autorizuar më parë?"</string>
+    <string name="adb_keys_warning_message" msgid="2968555274488101220">"Të bllokohet qasja për korrigjim përmes USB-së nga të gjithë kompjuterët që ke autorizuar më parë?"</string>
     <string name="dev_settings_warning_title" msgid="8251234890169074553">"Të lejohen cilësimet e zhvillimit?"</string>
     <string name="dev_settings_warning_message" msgid="37741686486073668">"Këto cilësime janë të projektuara vetëm për përdorim në programim. Ato mund të shkaktojnë që pajisja dhe aplikacionet në të, të mos punojnë ose të veprojnë në mënyrë të gabuar."</string>
     <string name="verify_apps_over_usb_title" msgid="6031809675604442636">"Verifiko apl. përmes USB-së"</string>
diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml
index 4363f0b..5a8b6dc 100644
--- a/packages/SettingsLib/res/values-ur/strings.xml
+++ b/packages/SettingsLib/res/values-ur/strings.xml
@@ -31,7 +31,7 @@
     <string name="wifi_disabled_password_failure" msgid="6892387079613226738">"توثیق کا مسئلہ"</string>
     <string name="wifi_cant_connect" msgid="5718417542623056783">"منسلک نہیں ہو سکتا ہے"</string>
     <string name="wifi_cant_connect_to_ap" msgid="3099667989279700135">"\'<xliff:g id="AP_NAME">%1$s</xliff:g>\' سے منسلک نہیں ہو سکتا ہے"</string>
-    <string name="wifi_check_password_try_again" msgid="8817789642851605628">"پاسورڈ چیک کر کے دوبارہ کوشش کریں"</string>
+    <string name="wifi_check_password_try_again" msgid="8817789642851605628">"پاس ورڈ چیک کر کے دوبارہ کوشش کریں"</string>
     <string name="wifi_not_in_range" msgid="1541760821805777772">"رینج میں نہیں ہے"</string>
     <string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"خودکار طور پر منسلک نہیں ہو گا"</string>
     <string name="wifi_no_internet" msgid="1774198889176926299">"انٹرنیٹ تک کوئی رسائی نہیں"</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/volume/MediaSessions.java b/packages/SettingsLib/src/com/android/settingslib/volume/MediaSessions.java
index 12ef639..fbf8a2f 100644
--- a/packages/SettingsLib/src/com/android/settingslib/volume/MediaSessions.java
+++ b/packages/SettingsLib/src/com/android/settingslib/volume/MediaSessions.java
@@ -33,7 +33,7 @@
 import android.media.session.MediaSession.Token;
 import android.media.session.MediaSessionManager;
 import android.media.session.MediaSessionManager.OnActiveSessionsChangedListener;
-import android.media.session.MediaSessionManager.RemoteVolumeControllerCallback;
+import android.media.session.MediaSessionManager.RemoteSessionCallback;
 import android.media.session.PlaybackState;
 import android.os.Bundle;
 import android.os.Handler;
@@ -100,8 +100,8 @@
         mMgr.addOnActiveSessionsChangedListener(mSessionsListener, null, mHandler);
         mInit = true;
         postUpdateSessions();
-        mMgr.registerRemoteVolumeControllerCallback(mHandlerExecutor,
-                mRemoteVolumeControllerCallback);
+        mMgr.registerRemoteSessionCallback(mHandlerExecutor,
+                mRemoteSessionCallback);
     }
 
     protected void postUpdateSessions() {
@@ -116,7 +116,7 @@
         if (D.BUG) Log.d(TAG, "destroy");
         mInit = false;
         mMgr.removeOnActiveSessionsChangedListener(mSessionsListener);
-        mMgr.unregisterRemoteVolumeControllerCallback(mRemoteVolumeControllerCallback);
+        mMgr.unregisterRemoteSessionCallback(mRemoteSessionCallback);
     }
 
     /**
@@ -142,11 +142,11 @@
         mCallbacks.onRemoteVolumeChanged(token, flags);
     }
 
-    private void onUpdateRemoteControllerH(Token sessionToken) {
+    private void onUpdateRemoteSessionListH(Token sessionToken) {
         final MediaController controller =
                 sessionToken != null ? new MediaController(mContext, sessionToken) : null;
         final String pkg = controller != null ? controller.getPackageName() : null;
-        if (D.BUG) Log.d(TAG, "updateRemoteControllerH " + pkg);
+        if (D.BUG) Log.d(TAG, "onUpdateRemoteSessionListH " + pkg);
         // this may be our only indication that a remote session is changed, refresh
         postUpdateSessions();
     }
@@ -336,8 +336,8 @@
                 }
             };
 
-    private final RemoteVolumeControllerCallback mRemoteVolumeControllerCallback =
-            new RemoteVolumeControllerCallback() {
+    private final RemoteSessionCallback mRemoteSessionCallback =
+            new RemoteSessionCallback() {
                 @Override
                 public void onVolumeChanged(@NonNull MediaSession.Token sessionToken,
                         int flags) {
@@ -346,15 +346,17 @@
                 }
 
                 @Override
-                public void onSessionChanged(@Nullable MediaSession.Token sessionToken) {
-                    mHandler.obtainMessage(H.UPDATE_REMOTE_CONTROLLER, sessionToken).sendToTarget();
+                public void onDefaultRemoteSessionChanged(
+                        @Nullable MediaSession.Token sessionToken) {
+                    mHandler.obtainMessage(H.UPDATE_REMOTE_SESSION_LIST,
+                            sessionToken).sendToTarget();
                 }
     };
 
     private final class H extends Handler {
         private static final int UPDATE_SESSIONS = 1;
         private static final int REMOTE_VOLUME_CHANGED = 2;
-        private static final int UPDATE_REMOTE_CONTROLLER = 3;
+        private static final int UPDATE_REMOTE_SESSION_LIST = 3;
 
         private H(Looper looper) {
             super(looper);
@@ -369,8 +371,8 @@
                 case REMOTE_VOLUME_CHANGED:
                     onRemoteVolumeChangedH((Token) msg.obj, msg.arg1);
                     break;
-                case UPDATE_REMOTE_CONTROLLER:
-                    onUpdateRemoteControllerH((Token) msg.obj);
+                case UPDATE_REMOTE_SESSION_LIST:
+                    onUpdateRemoteSessionListH((Token) msg.obj);
                     break;
             }
         }
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/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/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 7ea9686..f83f670 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -298,6 +298,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 +374,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/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/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-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/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/pip/PipSurfaceTransactionHelper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java
index 22ffd28..7f04f28 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/pip/PipSurfaceTransactionHelper.java
@@ -19,6 +19,8 @@
 import android.graphics.Matrix;
 import android.graphics.Rect;
 import android.graphics.RectF;
+import android.view.Choreographer;
+import android.view.Surface;
 import android.view.SurfaceControl;
 
 /**
@@ -50,8 +52,32 @@
                 .setPosition(leash, left, top);
     }
 
-    public void reset(SurfaceControl.Transaction tx, SurfaceControl leash, Rect destinationBounds) {
+    public void scaleAndRotate(SurfaceControl.Transaction tx, SurfaceControl leash,
+            Rect sourceBounds, Rect destinationBounds, Rect insets,
+            float degree, float positionX, float positionY) {
+        mTmpSourceRectF.set(sourceBounds);
+        mTmpDestinationRect.set(sourceBounds);
+        mTmpDestinationRect.inset(insets);
+        // Scale by the shortest edge and offset such that the top/left of the scaled inset
+        // source rect aligns with the top/left of the destination bounds
+        final float scale = sourceBounds.width() <= sourceBounds.height()
+                ? (float) destinationBounds.width() / sourceBounds.width()
+                : (float) destinationBounds.height() / sourceBounds.height();
+        mTmpTransform.setRotate(degree, 0, 0);
+        mTmpTransform.postScale(scale, scale);
+        tx.setMatrix(leash, mTmpTransform, mTmpFloat9)
+                .setWindowCrop(leash, mTmpDestinationRect)
+                .setPosition(leash, positionX, positionY);
+    }
+
+    public void reset(SurfaceControl.Transaction tx, SurfaceControl leash, Rect destinationBounds,
+            @Surface.Rotation int rotation) {
         resetScale(tx, leash, destinationBounds);
+        if (rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270) {
+            final int degree = (rotation == Surface.ROTATION_90) ? -90 : 90;
+            mTmpTransform.setRotate(degree, 0, 0);
+            tx.setMatrix(leash, mTmpTransform, mTmpFloat9);
+        }
         resetCornerRadius(tx, leash);
         crop(tx, leash, destinationBounds);
     }
@@ -71,4 +97,11 @@
         tx.setWindowCrop(leash, destinationBounds.width(), destinationBounds.height())
                 .setPosition(leash, destinationBounds.left, destinationBounds.top);
     }
+
+    /** @return {@link SurfaceControl.Transaction} instance with vsync-id */
+    public static SurfaceControl.Transaction newSurfaceControlTransaction() {
+        final SurfaceControl.Transaction tx = new SurfaceControl.Transaction();
+        tx.setFrameTimelineVsync(Choreographer.getSfInstance().getVsyncId());
+        return tx;
+    }
 }
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/KeyguardHostViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java
index 3fafa5c..ea60f0d 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardHostViewController.java
@@ -375,6 +375,17 @@
     }
 
     /**
+     * @return true if the current bouncer is password
+     */
+    public boolean dispatchBackKeyEventPreIme() {
+        if (mKeyguardSecurityContainerController.getCurrentSecurityMode()
+                == SecurityMode.Password) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
      * Allows the media keys to work when the keyguard is showing.
      * The media keys should be of no interest to the actual keyguard view(s),
      * so intercepting them here should not be of any harm.
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardInputView.java
index d42a53c..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);
@@ -43,6 +49,10 @@
 
     abstract CharSequence getTitle();
 
+    void animateForIme(float interpolatedFraction) {
+        return;
+    }
+
     boolean disallowInterceptTouch(MotionEvent event) {
         return false;
     }
@@ -52,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/KeyguardPasswordView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
index aaa5efe..92b65b2 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java
@@ -136,24 +136,14 @@
 
     @Override
     public void startAppearAnimation() {
+        // Reset state, and let IME animation reveal the view as it slides in
         setAlpha(0f);
         setTranslationY(0f);
-        animate()
-                .alpha(1)
-                .withLayer()
-                .setDuration(300)
-                .setInterpolator(mLinearOutSlowInInterpolator);
     }
 
     @Override
-    public boolean startDisappearAnimation(Runnable finishRunnable) {
-        animate()
-                .alpha(0f)
-                .translationY(mDisappearYTranslation)
-                .setInterpolator(mFastOutLinearInInterpolator)
-                .setDuration(100)
-                .withEndAction(finishRunnable);
-        return true;
+    public void animateForIme(float interpolatedFraction) {
+        setAlpha(interpolatedFraction);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java
index 5e33917..0f1c3c8 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordViewController.java
@@ -194,14 +194,18 @@
     @Override
     public void onResume(int reason) {
         super.onResume(reason);
-        // Wait a bit to focus the field so the focusable flag on the window is already set then.
-        mMainExecutor.execute(() -> {
-            if (mView.isShown() && mPasswordEntry.isEnabled()) {
-                mPasswordEntry.requestFocus();
-                if (reason != KeyguardSecurityView.SCREEN_ON || mShowImeAtScreenOn) {
-                    mInputMethodManager.showSoftInput(
-                            mPasswordEntry, InputMethodManager.SHOW_IMPLICIT);
-                }
+
+        mPasswordEntry.requestFocus();
+        if (reason != KeyguardSecurityView.SCREEN_ON || mShowImeAtScreenOn) {
+            showInput();
+        }
+    }
+
+    private void showInput() {
+        mPasswordEntry.post(() -> {
+            if (mPasswordEntry.isFocused() && mView.isShown()) {
+                mInputMethodManager.showSoftInput(
+                        mPasswordEntry, InputMethodManager.SHOW_IMPLICIT);
             }
         });
     }
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/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index b62ea6b..42f3cc7 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -119,21 +119,24 @@
                 @Override
                 public WindowInsets onProgress(WindowInsets windowInsets,
                         List<WindowInsetsAnimation> list) {
-                    int translationY = 0;
                     if (mDisappearAnimRunning) {
                         mSecurityViewFlipper.setTranslationY(
                                 mInitialBounds.bottom - mFinalBounds.bottom);
                     } else {
+                        int translationY = 0;
+                        float interpolatedFraction = 1f;
                         for (WindowInsetsAnimation animation : list) {
                             if ((animation.getTypeMask() & WindowInsets.Type.ime()) == 0) {
                                 continue;
                             }
+                            interpolatedFraction = animation.getInterpolatedFraction();
+
                             final int paddingBottom = (int) MathUtils.lerp(
                                     mInitialBounds.bottom - mFinalBounds.bottom, 0,
-                                    animation.getInterpolatedFraction());
+                                    interpolatedFraction);
                             translationY += paddingBottom;
                         }
-                        mSecurityViewFlipper.setTranslationY(translationY);
+                        mSecurityViewFlipper.animateForIme(translationY, interpolatedFraction);
                     }
                     return windowInsets;
                 }
@@ -141,7 +144,7 @@
                 @Override
                 public void onEnd(WindowInsetsAnimation animation) {
                     if (!mDisappearAnimRunning) {
-                        mSecurityViewFlipper.setTranslationY(0);
+                        mSecurityViewFlipper.animateForIme(0, /* interpolatedFraction */ 1f);
                     }
                 }
             };
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipper.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipper.java
index b8439af..7773fe9 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipper.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityViewFlipper.java
@@ -83,6 +83,16 @@
         return "";
     }
 
+    /**
+      * Translate the entire view, and optionally inform the wrapped view of the progress
+      * so it can animate with the parent.
+      */
+    public void animateForIme(int translationY, float interpolatedFraction) {
+        super.setTranslationY(translationY);
+        KeyguardInputView v = getSecurityView();
+        if (v != null) v.animateForIme(interpolatedFraction);
+    }
+
     @Override
     protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
         return p instanceof LayoutParams;
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/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..2719d3e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ImageExporter.java
@@ -0,0 +1,360 @@
+/*
+ * 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);
+
+        exif.setAttribute(ExifInterface.TAG_DATETIME_DIGITIZED, dateTime);
+        exif.setAttribute(ExifInterface.TAG_SUBSEC_TIME_DIGITIZED, subSec);
+        exif.setAttribute(ExifInterface.TAG_OFFSET_TIME_DIGITIZED, 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/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/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index db0713c..713daaa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -451,10 +451,6 @@
         }
     }
 
-    public boolean onBackPressed() {
-        return mKeyguardViewController != null && mKeyguardViewController.handleBackKey();
-    }
-
     /**
      * @return True if and only if the security method should be shown before showing the
      * notifications on Keyguard, like SIM PIN/PUK.
@@ -494,6 +490,14 @@
         return mKeyguardViewController.interceptMediaKey(event);
     }
 
+    /**
+     * @return true if the pre IME back event should be handled
+     */
+    public boolean dispatchBackKeyEventPreIme() {
+        ensureView();
+        return mKeyguardViewController.dispatchBackKeyEventPreIme();
+    }
+
     public void notifyKeyguardAuthenticated(boolean strongAuth) {
         ensureView();
         mKeyguardViewController.finish(strongAuth, KeyguardUpdateMonitor.getCurrentUser());
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/NotificationShadeWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowView.java
index a4fc3a3..619aadb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowView.java
@@ -162,6 +162,11 @@
         return mInteractionEventHandler.dispatchKeyEvent(event);
     }
 
+    @Override
+    public boolean dispatchKeyEventPreIme(KeyEvent event) {
+        return mInteractionEventHandler.dispatchKeyEventPreIme(event);
+    }
+
     protected void setInteractionEventHandler(InteractionEventHandler listener) {
         mInteractionEventHandler = listener;
     }
@@ -361,6 +366,8 @@
         boolean interceptMediaKey(KeyEvent event);
 
         boolean dispatchKeyEvent(KeyEvent event);
+
+        boolean dispatchKeyEventPreIme(KeyEvent event);
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
index 2ac9f30..5595ae7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowViewController.java
@@ -348,6 +348,11 @@
             }
 
             @Override
+            public boolean dispatchKeyEventPreIme(KeyEvent event) {
+                return mService.dispatchKeyEventPreIme(event);
+            }
+
+            @Override
             public boolean dispatchKeyEvent(KeyEvent event) {
                 boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
                 switch (event.getKeyCode()) {
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 a18d87c..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);
@@ -3520,6 +3513,23 @@
                 && mStatusBarKeyguardViewManager.interceptMediaKey(event);
     }
 
+    /**
+     * While IME is active and a BACK event is detected, check with
+     * {@link StatusBarKeyguardViewManager#dispatchBackKeyEventPreIme(KeyEvent)} to see if the event
+     * should be handled before routing to IME, in order to prevent the user having to hit back
+     * twice to exit bouncer.
+     */
+    public boolean dispatchKeyEventPreIme(KeyEvent event) {
+        switch (event.getKeyCode()) {
+            case KeyEvent.KEYCODE_BACK:
+                if (mState == StatusBarState.KEYGUARD
+                        && mStatusBarKeyguardViewManager.dispatchBackKeyEventPreIme()) {
+                    return onBackPressed();
+                }
+        }
+        return false;
+    }
+
     protected boolean shouldUnlockOnMenuPressed() {
         return mDeviceInteractive && mState != StatusBarState.SHADE
             && mStatusBarKeyguardViewManager.shouldDismissOnMenuPressed();
@@ -4027,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/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 055b78a..b4c687d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -955,6 +955,13 @@
         return mBouncer.interceptMediaKey(event);
     }
 
+    /**
+     * @return true if the pre IME back event should be handled
+     */
+    public boolean dispatchBackKeyEventPreIme() {
+        return mBouncer.dispatchBackKeyEventPreIme();
+    }
+
     public void readyForKeyguardDone() {
         mViewMediatorCallback.readyForKeyguardDone();
     }
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/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/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/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/screenshot/ImageExporterTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ImageExporterTest.java
new file mode 100644
index 0000000..f566880
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ImageExporterTest.java
@@ -0,0 +1,180 @@
+/*
+ * 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));
+        assertEquals("Exif " + ExifInterface.TAG_OFFSET_TIME_ORIGINAL, "+00:00",
+                exifInterface.getAttribute(ExifInterface.TAG_OFFSET_TIME_DIGITIZED));
+    }
+
+    @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));
+
+                assertEquals("Exif " + ExifInterface.TAG_DATETIME_DIGITIZED, "2020:12:15 13:15:00",
+                        exifInterface.getAttribute(ExifInterface.TAG_DATETIME_DIGITIZED));
+                assertEquals("Exif " + ExifInterface.TAG_SUBSEC_TIME_DIGITIZED, "000",
+                        exifInterface.getAttribute(ExifInterface.TAG_SUBSEC_TIME_DIGITIZED));
+                assertEquals("Exif " + ExifInterface.TAG_OFFSET_TIME_DIGITIZED, "-05:00",
+                        exifInterface.getAttribute(ExifInterface.TAG_OFFSET_TIME_DIGITIZED));
+            }
+        } 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/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/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/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 4e75a9e..0403247 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -95,7 +95,6 @@
 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;
@@ -337,7 +336,7 @@
     @VisibleForTesting
     protected INetd mNetd;
     private INetworkStatsService mStatsService;
-    private INetworkPolicyManager mPolicyManager;
+    private NetworkPolicyManager mPolicyManager;
     private NetworkPolicyManagerInternal mPolicyManagerInternal;
 
     /**
@@ -946,15 +945,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 +991,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 +1007,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);
@@ -3597,8 +3591,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;
         }
@@ -8234,6 +8228,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 +8261,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 +8390,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/OWNERS b/services/core/java/com/android/server/OWNERS
index a10764b..8f1ca83 100644
--- a/services/core/java/com/android/server/OWNERS
+++ b/services/core/java/com/android/server/OWNERS
@@ -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
@@ -22,6 +25,7 @@
 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
 per-file MmsServiceBroker.java = file:/telephony/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/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 5fde046..6a52266 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -1700,12 +1700,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 +1761,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);
     }
 
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index b0f296f..6892ef7 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -370,9 +370,9 @@
     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.
@@ -806,7 +806,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..8cb9b0a 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -14052,18 +14052,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/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/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/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index 574d8c6..0a30e07 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -16,10 +16,13 @@
 
 package com.android.server.display;
 
+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.NitsMap;
@@ -33,6 +36,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 +53,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";
@@ -57,11 +62,21 @@
     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 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 DisplayDeviceConfig() {
+    private final Context mContext;
+
+    private DisplayDeviceConfig(Context context) {
+        mContext = context;
     }
 
     /**
@@ -72,37 +87,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 +139,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 +161,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,
@@ -153,12 +187,15 @@
         String str = "DisplayDeviceConfig{"
                 + "mBrightness=" + Arrays.toString(mBrightness)
                 + ", mNits=" + Arrays.toString(mNits)
+                + ", mBrightnessMinimum=" + mBrightnessMinimum
+                + ", mBrightnessMaximum=" + mBrightnessMaximum
+                + ", mBrightnessDefault=" + mBrightnessDefault
                 + ", mQuirks=" + mQuirks
                 + "}";
         return str;
     }
 
-    private static DisplayDeviceConfig getConfigFromSuffix(File baseDirectory,
+    private static DisplayDeviceConfig getConfigFromSuffix(Context context, File baseDirectory,
             String suffixFormat, long idNumber) {
 
         final String suffix = String.format(suffixFormat, idNumber);
@@ -167,13 +204,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 +236,87 @@
 
         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();
+                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();
 
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..6fa244e 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];
@@ -1108,7 +1102,7 @@
         }
         addDisplayPowerControllerLocked(displayId);
         mDisplayStates.append(displayId, Display.STATE_OFF);
-        mDisplayBrightnesses.append(displayId, mDisplayDefaultBrightness);
+        mDisplayBrightnesses.append(displayId, display.getDisplayInfoLocked().brightnessDefault);
 
         DisplayManagerGlobal.invalidateLocalDisplayInfoCaches();
 
@@ -1876,7 +1870,7 @@
         }
         final DisplayPowerController displayPowerController = new DisplayPowerController(
                 mContext, mDisplayPowerCallbacks, mPowerHandler, mSensorManager,
-                mDisplayBlanker, displayId);
+                mDisplayBlanker, mLogicalDisplayMapper.getLocked(displayId));
         mDisplayPowerControllers.append(displayId, displayPowerController);
     }
 
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index f488260..e31704f 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(
@@ -1833,6 +1837,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/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/hdmi/HdmiCecConfig.java b/services/core/java/com/android/server/hdmi/HdmiCecConfig.java
index 371fd3d..16695d1 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecConfig.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecConfig.java
@@ -22,12 +22,19 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.StringDef;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.SharedPreferences;
+import android.database.ContentObserver;
 import android.hardware.hdmi.HdmiControlManager;
+import android.net.Uri;
 import android.os.Environment;
+import android.os.Handler;
+import android.os.Looper;
 import android.os.SystemProperties;
+import android.os.UserHandle;
 import android.provider.Settings.Global;
+import android.util.ArrayMap;
 import android.util.Slog;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -88,6 +95,23 @@
     @Nullable private final CecSettings mSystemConfig;
     @Nullable private final CecSettings mVendorOverride;
 
+    private final ArrayMap<Setting, Set<SettingChangeListener>>
+            mSettingChangeListeners = new ArrayMap<>();
+
+    private SettingsObserver mSettingsObserver;
+
+    /**
+     * Listener used to get notifications when value of a setting changes.
+     */
+    public interface SettingChangeListener {
+        /**
+         * Called when value of a setting changes.
+         *
+         * @param setting name of a CEC setting that changed
+         */
+        void onChange(@NonNull @CecSettingName String setting);
+    }
+
     /**
      * Setting storage input/output helper class.
      */
@@ -159,6 +183,18 @@
         }
     }
 
+    private class SettingsObserver extends ContentObserver {
+        SettingsObserver(Handler handler) {
+            super(handler);
+        }
+
+        @Override
+        public void onChange(boolean selfChange, Uri uri) {
+            String setting = uri.getLastPathSegment();
+            HdmiCecConfig.this.notifyGlobalSettingChanged(setting);
+        }
+    }
+
     @VisibleForTesting
     HdmiCecConfig(@NonNull Context context,
                   @NonNull StorageAdapter storageAdapter,
@@ -311,6 +347,7 @@
         } else if (storage == STORAGE_SHARED_PREFS) {
             Slog.d(TAG, "Setting '" + storageKey + "' shared pref.");
             mStorageAdapter.storeSharedPref(storageKey, value);
+            notifySettingChanged(setting);
         }
     }
 
@@ -318,6 +355,103 @@
         return Integer.decode(value.getIntValue());
     }
 
+    private void notifyGlobalSettingChanged(String setting) {
+        switch (setting) {
+            case Global.HDMI_CONTROL_ENABLED:
+                notifySettingChanged(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED);
+                break;
+            case Global.HDMI_CEC_VERSION:
+                notifySettingChanged(HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_VERSION);
+                break;
+            case Global.HDMI_CONTROL_SEND_STANDBY_ON_SLEEP:
+                notifySettingChanged(HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE);
+                break;
+        }
+    }
+
+    private void notifySettingChanged(@NonNull @CecSettingName String name) {
+        Setting setting = getSetting(name);
+        if (setting == null) {
+            throw new IllegalArgumentException("Setting '" + name + "' does not exist.");
+        }
+        notifySettingChanged(setting);
+    }
+
+    private void notifySettingChanged(@NonNull Setting setting) {
+        Set<SettingChangeListener> listeners = mSettingChangeListeners.get(setting);
+        if (listeners == null) {
+            return;  // No listeners registered, do nothing.
+        }
+        for (SettingChangeListener listener: listeners) {
+            listener.onChange(setting.getName());
+        }
+    }
+
+    /**
+     * This method registers Global Setting change observer.
+     * Needs to be called once after initialization of HdmiCecConfig.
+     */
+    public void registerGlobalSettingsObserver(Looper looper) {
+        Handler handler = new Handler(looper);
+        mSettingsObserver = new SettingsObserver(handler);
+        ContentResolver resolver = mContext.getContentResolver();
+        String[] settings = new String[] {
+                Global.HDMI_CONTROL_ENABLED,
+                Global.HDMI_CEC_VERSION,
+                Global.HDMI_CONTROL_SEND_STANDBY_ON_SLEEP,
+        };
+        for (String setting: settings) {
+            resolver.registerContentObserver(Global.getUriFor(setting), false,
+                                             mSettingsObserver, UserHandle.USER_ALL);
+        }
+    }
+
+    /**
+     * This method unregisters Global Setting change observer.
+     */
+    public void unregisterGlobalSettingsObserver() {
+        ContentResolver resolver = mContext.getContentResolver();
+        resolver.unregisterContentObserver(mSettingsObserver);
+    }
+
+    /**
+     * Register change listener for a given setting name.
+     */
+    public void registerChangeListener(@NonNull @CecSettingName String name,
+                                       SettingChangeListener listener) {
+        Setting setting = getSetting(name);
+        if (setting == null) {
+            throw new IllegalArgumentException("Setting '" + name + "' does not exist.");
+        }
+        @Storage int storage = getStorage(setting);
+        if (storage != STORAGE_GLOBAL_SETTINGS && storage != STORAGE_SHARED_PREFS) {
+            throw new IllegalArgumentException("Change listeners for setting '" + name
+                    + "' not supported.");
+        }
+        if (!mSettingChangeListeners.containsKey(setting)) {
+            mSettingChangeListeners.put(setting, new HashSet<>());
+        }
+        mSettingChangeListeners.get(setting).add(listener);
+    }
+
+    /**
+     * Remove change listener for a given setting name.
+     */
+    public void removeChangeListener(@NonNull @CecSettingName String name,
+                                     SettingChangeListener listener) {
+        Setting setting = getSetting(name);
+        if (setting == null) {
+            throw new IllegalArgumentException("Setting '" + name + "' does not exist.");
+        }
+        if (mSettingChangeListeners.containsKey(setting)) {
+            Set<SettingChangeListener> listeners = mSettingChangeListeners.get(setting);
+            listeners.remove(listener);
+            if (listeners.isEmpty()) {
+                mSettingChangeListeners.remove(setting);
+            }
+        }
+    }
+
     /**
      * Returns a list of all settings based on the XML metadata.
      */
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index c15fdca..b78954dcb 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -498,6 +498,7 @@
         if (mMessageValidator == null) {
             mMessageValidator = new HdmiCecMessageValidator(this);
         }
+        mHdmiCecConfig.registerGlobalSettingsObserver(mIoLooper);
     }
 
     private void bootCompleted() {
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..372bcee 100644
--- a/services/core/java/com/android/server/location/LocationManagerService.java
+++ b/services/core/java/com/android/server/location/LocationManagerService.java
@@ -52,8 +52,8 @@
 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,8 +907,8 @@
     }
 
     @Override
-    public long getGnssCapabilities() {
-        return mGnssManagerService == null ? GnssCapabilities.INVALID_CAPABILITIES
+    public GnssCapabilities getGnssCapabilities() {
+        return mGnssManagerService == null ? new GnssCapabilities.Builder().build()
                 : mGnssManagerService.getGnssCapabilities();
     }
 
@@ -944,11 +968,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 +1308,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 +1322,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 +1348,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 +1394,6 @@
         }
 
         @Override
-        public LocationUsageLogger getLocationUsageLogger() {
-            return mLocationUsageLogger;
-        }
-
-        @Override
         public LocationPowerSaveModeHelper getLocationPowerSaveModeHelper() {
             return mLocationPowerSaveModeHelper;
         }
@@ -1373,8 +1409,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
index 9961d27..e9f79ef 100644
--- a/services/core/java/com/android/server/location/gnss/GnssAntennaInfoProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssAntennaInfoProvider.java
@@ -20,12 +20,12 @@
 import static com.android.server.location.gnss.GnssManagerService.TAG;
 
 import android.location.GnssAntennaInfo;
+import android.location.GnssCapabilities;
 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.gnss.hal.GnssNative;
 import com.android.server.location.injector.Injector;
 
 import java.util.Collection;
@@ -35,23 +35,22 @@
  * Provides GNSS antenna information to clients.
  */
 public class GnssAntennaInfoProvider extends
-        GnssListenerMultiplexer<Void, IGnssAntennaInfoListener, Void> {
+        GnssListenerMultiplexer<Void, IGnssAntennaInfoListener, Void> implements
+        GnssNative.BaseCallbacks, GnssNative.AntennaInfoCallbacks {
 
-    private final GnssAntennaInfoProviderNative mNative;
+    private final GnssNative mGnssNative;
 
-    public GnssAntennaInfoProvider(Injector injector) {
-        this(injector, new GnssAntennaInfoProviderNative());
-    }
-
-    @VisibleForTesting
-    public GnssAntennaInfoProvider(Injector injector, GnssAntennaInfoProviderNative aNative) {
+    public GnssAntennaInfoProvider(Injector injector, GnssNative gnssNative) {
         super(injector);
-        mNative = aNative;
+        mGnssNative = gnssNative;
+
+        mGnssNative.addBaseCallbacks(this);
+        mGnssNative.addAntennaInfoCallbacks(this);
     }
 
     @Override
     protected boolean isServiceSupported() {
-        return mNative.isAntennaInfoSupported();
+        return mGnssNative.isAntennaInfoListeningSupported();
     }
 
     @Override
@@ -62,9 +61,7 @@
     @Override
     protected boolean registerWithService(Void ignored,
             Collection<GnssListenerRegistration> registrations) {
-        Preconditions.checkState(mNative.isAntennaInfoSupported());
-
-        if (mNative.startAntennaInfoListening()) {
+        if (mGnssNative.startAntennaInfoListening()) {
             if (D) {
                 Log.d(TAG, "starting gnss antenna info");
             }
@@ -77,7 +74,7 @@
 
     @Override
     protected void unregisterWithService() {
-        if (mNative.stopAntennaInfoListening()) {
+        if (mGnssNative.stopAntennaInfoListening()) {
             if (D) {
                 Log.d(TAG, "stopping gnss antenna info");
             }
@@ -86,39 +83,19 @@
         }
     }
 
-    /**
-     * Called by GnssLocationProvider.
-     */
-    public void onGnssAntennaInfoAvailable(List<GnssAntennaInfo> gnssAntennaInfos) {
-        deliverToListeners((listener) -> {
-            listener.onGnssAntennaInfoReceived(gnssAntennaInfos);
+    @Override
+    public void onHalRestarted() {
+        resetService();
+    }
+
+    @Override
+    public void onCapabilitiesChanged(GnssCapabilities oldCapabilities,
+            GnssCapabilities newCapabilities) {}
+
+    @Override
+    public void onReportAntennaInfo(List<GnssAntennaInfo> antennaInfos) {
+        deliverToListeners(listener -> {
+            listener.onGnssAntennaInfoReceived(antennaInfos);
         });
     }
-
-    /**
-     * 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..32a7952
--- /dev/null
+++ b/services/core/java/com/android/server/location/gnss/GnssGeofenceProxy.java
@@ -0,0 +1,148 @@
+/*
+ * 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.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);
+                }
+            }
+        }
+    }
+
+    @Override
+    public void onCapabilitiesChanged(GnssCapabilities oldCapabilities,
+            GnssCapabilities newCapabilities) {}
+}
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..afe7567 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,16 @@
 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 +89,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 +113,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 +131,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 +224,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 +243,6 @@
     private boolean mBatchingEnabled;
 
     private boolean mShutdown;
-    private boolean mNavigating;
     private boolean mStarted;
     private boolean mBatchingStarted;
     private long mStartedChangedElapsedRealtime;
@@ -335,9 +262,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 +282,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 +301,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 +370,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 +406,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 +416,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 +460,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 +515,7 @@
      */
     @Override
     public void injectTime(long time, long timeReference, int uncertainty) {
-        native_inject_time(time, timeReference, uncertainty);
+        mGnssNative.injectTime(time, timeReference, uncertainty);
     }
 
     /**
@@ -647,7 +527,7 @@
         if (mSupportsPsds) {
             synchronized (mLock) {
                 for (int psdsType : mPendingDownloadPsdsTypes) {
-                    downloadPsdsData(psdsType);
+                    sendMessage(DOWNLOAD_PSDS_DATA, psdsType, null);
                 }
                 mPendingDownloadPsdsTypes.clear();
             }
@@ -724,38 +604,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 +638,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 +673,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 +684,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 +700,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 +721,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 +759,8 @@
             mGnssVisibilityControl.onGpsEnabledChanged(/* isEnabled= */ false);
         }
         // do this before releasing wakelock
-        native_cleanup_batching();
-        native_cleanup();
+        mGnssNative.cleanupBatching();
+        mGnssNative.cleanup();
     }
 
     private void updateEnabled() {
@@ -951,7 +799,7 @@
      * minimum size guaranteed to be available for batching operations.
      */
     public int getBatchSize() {
-        return native_get_batch_size();
+        return mGnssNative.getBatchSize();
     }
 
     @Override
@@ -965,7 +813,7 @@
         if (!added) {
             listener.run();
         } else {
-            native_flush_batch();
+            mGnssNative.flushBatch();
         }
     }
 
@@ -1009,9 +857,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 +893,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 +973,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 +987,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 +1016,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 +1027,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 +1043,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 +1059,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 +1075,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 +1091,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 +1101,7 @@
     private void stopBatching() {
         if (DEBUG) Log.d(TAG, "stopBatching");
         if (mBatchingStarted) {
-            native_stop_batch();
+            mGnssNative.stopBatch();
             mBatchingStarted = false;
         }
     }
@@ -1279,28 +1121,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 +1164,6 @@
             if (mStarted) {
                 mGnssMetrics.logTimeToFirstFixMilliSecs(mTimeToFirstFix);
             }
-
-            // notify status listeners
-            mGnssStatusListenerHelper.onFirstFix(mTimeToFirstFix);
         }
 
         if (mStarted) {
@@ -1353,51 +1171,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 +1213,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 +1232,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 +1260,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 +1309,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 +1335,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 +1345,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 +1442,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..ff92444 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,78 @@
 import android.Manifest;
 import android.annotation.Nullable;
 import android.content.Context;
-import android.location.GnssAntennaInfo;
+import android.hardware.location.GeofenceHardware;
+import android.hardware.location.GeofenceHardwareImpl;
+import android.location.FusedBatchOptions;
+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.util.identity.CallerIdentity;
+import android.os.BatteryStats;
 import android.os.RemoteException;
+import android.os.ServiceManager;
 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.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 GnssMetrics mGnssMetrics;
 
-    @VisibleForTesting
-    GnssManagerService(Context context, Injector injector,
-            GnssLocationProvider gnssLocationProvider) {
-        Preconditions.checkState(isGnssSupported());
-
-        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);
+        mGnssAntennaInfoProvider = new GnssAntennaInfoProvider(injector, mGnssNative);
+        mGnssNavigationMessageProvider = new GnssNavigationMessageProvider(injector, mGnssNative);
+        mGnssGeofenceProxy = new GnssGeofenceProxy(mGnssNative);
+
+        mGnssNative.setGeofenceCallbacks(new GnssGeofenceHalModule());
 
         // 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 +100,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 +116,15 @@
      */
     @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}.
      */
-    public long getGnssCapabilities() {
-        return mGnssCapabilitiesProvider.getGnssCapabilities();
+    public GnssCapabilities getGnssCapabilities() {
+        return mGnssNative.getCapabilities();
     }
 
     /**
@@ -174,6 +153,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 +189,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");
+        }
     }
 
     /**
@@ -248,9 +247,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,12 +258,12 @@
      */
     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("Capabilities: " + mGnssNative.getCapabilities());
+
         ipw.println("Antenna Info Provider:");
         ipw.increaseIndent();
         mGnssAntennaInfoProvider.dump(fd, ipw, args);
@@ -284,169 +283,92 @@
         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 GnssGeofenceHalModule implements GnssNative.GeofenceCallbacks {
 
-    @Override
-    public void reportLocation(boolean hasLatLong, Location location) {
-        mGnssLocationProvider.reportLocation(hasLatLong, location);
-    }
+        private GeofenceHardwareImpl mGeofenceHardwareImpl;
 
-    @Override
-    public void reportStatus(int status) {
-        mGnssLocationProvider.reportStatus(status);
-    }
+        private synchronized GeofenceHardwareImpl getGeofenceHardware() {
+            if (mGeofenceHardwareImpl == null) {
+                mGeofenceHardwareImpl = GeofenceHardwareImpl.getInstance(mContext);
+            }
+            return mGeofenceHardwareImpl;
+        }
 
-    @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);
-    }
+        @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 reportAGpsStatus(int agpsType, int agpsStatus, byte[] suplIpAddr) {
-        mGnssLocationProvider.reportAGpsStatus(agpsType, agpsStatus, suplIpAddr);
-    }
+        @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 reportNmea(long timestamp) {
-        mGnssLocationProvider.reportNmea(timestamp);
-    }
+        @Override
+        public void onReportGeofenceAddStatus(int geofenceId, @GeofenceStatus int status) {
+            FgThread.getHandler().post(() -> getGeofenceHardware().reportGeofenceAddStatus(
+                    geofenceId, translateGeofenceStatus(status)));
+        }
 
-    @Override
-    public void reportMeasurementData(GnssMeasurementsEvent event) {
-        mGnssLocationProvider.reportMeasurementData(event);
-    }
+        @Override
+        public void onReportGeofenceRemoveStatus(int geofenceId, @GeofenceStatus int status) {
+            FgThread.getHandler().post(() -> getGeofenceHardware().reportGeofenceRemoveStatus(
+                    geofenceId, translateGeofenceStatus(status)));
+        }
 
-    @Override
-    public void reportAntennaInfo(List<GnssAntennaInfo> antennaInfos) {
-        mGnssLocationProvider.reportAntennaInfo(antennaInfos);
-    }
+        @Override
+        public void onReportGeofencePauseStatus(int geofenceId, @GeofenceStatus int status) {
+            FgThread.getHandler().post(() -> getGeofenceHardware().reportGeofencePauseStatus(
+                    geofenceId, translateGeofenceStatus(status)));
+        }
 
-    @Override
-    public void reportNavigationMessage(GnssNavigationMessage event) {
-        mGnssLocationProvider.reportNavigationMessage(event);
-    }
+        @Override
+        public void onReportGeofenceResumeStatus(int geofenceId, @GeofenceStatus int status) {
+            FgThread.getHandler().post(() -> getGeofenceHardware().reportGeofenceResumeStatus(
+                    geofenceId, translateGeofenceStatus(status)));
+        }
 
-    @Override
-    public void reportGnssPowerStats(GnssPowerStats powerStats) {
-        mGnssLocationProvider.reportGnssPowerStats(powerStats);
-    }
-
-    @Override
-    public void setTopHalCapabilities(int topHalCapabilities) {
-        mGnssLocationProvider.setTopHalCapabilities(topHalCapabilities);
-    }
-
-    @Override
-    public void setSubHalMeasurementCorrectionsCapabilities(int subHalCapabilities) {
-        mGnssLocationProvider.setSubHalMeasurementCorrectionsCapabilities(subHalCapabilities);
-    }
-
-    @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();
+        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;
+            }
+        }
     }
 }
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..b7cc9f5 100644
--- a/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssMeasurementsProvider.java
@@ -21,6 +21,7 @@
 
 import android.annotation.Nullable;
 import android.app.AppOpsManager;
+import android.location.GnssCapabilities;
 import android.location.GnssMeasurementRequest;
 import android.location.GnssMeasurementsEvent;
 import android.location.IGnssMeasurementsListener;
@@ -29,8 +30,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 +38,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 +46,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 {
 
@@ -79,24 +80,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 +111,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 +126,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 +152,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 +195,17 @@
                 null, registration.isForeground());
     }
 
-    /**
-     * Called by GnssLocationProvider.
-     */
-    public void onMeasurementsAvailable(GnssMeasurementsEvent event) {
+    @Override
+    public void onHalRestarted() {
+        resetService();
+    }
+
+    @Override
+    public void onCapabilitiesChanged(GnssCapabilities oldCapabilities,
+            GnssCapabilities newCapabilities) {}
+
+    @Override
+    public void onReportMeasurements(GnssMeasurementsEvent event) {
         deliverToListeners(registration -> {
             if (mAppOpsHelper.noteOpNoThrow(AppOpsManager.OP_FINE_LOCATION,
                     registration.getIdentity())) {
@@ -211,25 +215,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..9b1cde0 100644
--- a/services/core/java/com/android/server/location/gnss/GnssNavigationMessageProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssNavigationMessageProvider.java
@@ -20,13 +20,13 @@
 import static com.android.server.location.gnss.GnssManagerService.TAG;
 
 import android.app.AppOpsManager;
+import android.location.GnssCapabilities;
 import android.location.GnssNavigationMessage;
 import android.location.IGnssNavigationMessageListener;
 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 +40,24 @@
  * @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 final GnssNative mGnssNative;
 
-    public GnssNavigationMessageProvider(Injector injector) {
-        this(injector, new GnssNavigationMessageProviderNative());
-    }
-
-    @VisibleForTesting
-    public GnssNavigationMessageProvider(Injector injector,
-            GnssNavigationMessageProviderNative aNative) {
+    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
@@ -65,9 +68,7 @@
     @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 +81,26 @@
 
     @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 onCapabilitiesChanged(GnssCapabilities oldCapabilities,
+            GnssCapabilities newCapabilities) {}
+
+    @Override
+    public void onReportNavigationMessage(GnssNavigationMessage event) {
         deliverToListeners(registration -> {
             if (mAppOpsHelper.noteOpNoThrow(AppOpsManager.OP_FINE_LOCATION,
                     registration.getIdentity())) {
@@ -104,30 +110,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..bad1b79
--- /dev/null
+++ b/services/core/java/com/android/server/location/gnss/GnssNmeaProvider.java
@@ -0,0 +1,115 @@
+/*
+ * 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.GnssCapabilities;
+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 onCapabilitiesChanged(GnssCapabilities oldCapabilities,
+            GnssCapabilities newCapabilities) {}
+
+    @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..e0673db 100644
--- a/services/core/java/com/android/server/location/gnss/GnssStatusProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssStatusProvider.java
@@ -20,6 +20,7 @@
 import static com.android.server.location.gnss.GnssManagerService.TAG;
 
 import android.app.AppOpsManager;
+import android.location.GnssCapabilities;
 import android.location.GnssStatus;
 import android.location.IGnssStatusListener;
 import android.location.util.identity.CallerIdentity;
@@ -27,6 +28,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 +38,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 +105,50 @@
                 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 onCapabilitiesChanged(GnssCapabilities oldCapabilities,
+            GnssCapabilities newCapabilities) {}
+
+    @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 +158,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..89d8249
--- /dev/null
+++ b/services/core/java/com/android/server/location/gnss/hal/GnssNative.java
@@ -0,0 +1,1490 @@
+/*
+ * 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 {
+        void onHalRestarted();
+        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 @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);
+    }
+
+    private void initializeGnss(boolean restart) {
+        Preconditions.checkState(mRegistered);
+        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) {
+        GnssCapabilities oldCapabilities = mCapabilities;
+        mCapabilities = oldCapabilities.withTopHalFlags(capabilities);
+        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
+
+    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..5f744fe 100644
--- a/services/core/java/com/android/server/location/timezone/BinderLocationTimeZoneProvider.java
+++ b/services/core/java/com/android/server/location/timezone/BinderLocationTimeZoneProvider.java
@@ -25,8 +25,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;
@@ -161,9 +161,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 +201,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/ControllerImpl.java b/services/core/java/com/android/server/location/timezone/ControllerImpl.java
index 7496168..396e5b0 100644
--- a/services/core/java/com/android/server/location/timezone/ControllerImpl.java
+++ b/services/core/java/com/android/server/location/timezone/ControllerImpl.java
@@ -31,6 +31,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 +118,7 @@
         mThreadingDomain.assertCurrentThread();
 
         synchronized (mSharedLock) {
-            debugLog("onEnvironmentConfigChanged()");
+            debugLog("onConfigChanged()");
 
             ConfigurationInternal oldConfig = mCurrentUserConfiguration;
             ConfigurationInternal newConfig = mEnvironment.getCurrentUserConfigurationInternal();
@@ -553,6 +554,7 @@
         }
     }
 
+    @NonNull
     private static GeolocationTimeZoneSuggestion createUncertainSuggestion(@NonNull String reason) {
         GeolocationTimeZoneSuggestion suggestion = new GeolocationTimeZoneSuggestion(null);
         suggestion.addDebugInfo(reason);
@@ -560,30 +562,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/LocationTimeZoneManagerService.java b/services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerService.java
index 98ebec2..880dddf 100644
--- a/services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerService.java
+++ b/services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerService.java
@@ -21,6 +21,9 @@
 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;
@@ -39,7 +42,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},
@@ -115,8 +122,11 @@
     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.";
+    static final String PROVIDER_MODE_OVERRIDE_SYSTEM_PROPERTY_PREFIX = "persist.sys.geotz.";
+    static final String PROVIDER_MODE_SIMULATED = "simulated";
+    static final String PROVIDER_MODE_DISABLED = "disabled";
+
+    private static final long BLOCKING_OP_WAIT_DURATION_MILLIS = Duration.ofSeconds(20).toMillis();
 
     private static final String ATTRIBUTION_TAG = "LocationTimeZoneService";
 
@@ -129,14 +139,17 @@
     @NonNull private final Context mContext;
 
     /**
-     * The {@link ThreadingDomain} used to supply the {@link android.os.Handler} and shared lock
-     * object used by the controller and related components.
+     * The {@link ThreadingDomain} used to supply the shared lock object used by the controller and
+     * related components.
      *
      * <p>Most operations are executed on the associated handler thread <em>but not all</em>, hence
      * the requirement for additional synchronization using a shared lock.
      */
     @NonNull private final ThreadingDomain mThreadingDomain;
 
+    /** A handler associated with the {@link #mThreadingDomain}. */
+    @NonNull private final Handler mHandler;
+
     /** The shared lock from {@link #mThreadingDomain}. */
     @NonNull private final Object mSharedLock;
 
@@ -146,7 +159,8 @@
 
     LocationTimeZoneManagerService(Context context) {
         mContext = context.createAttributionContext(ATTRIBUTION_TAG);
-        mThreadingDomain = new HandlerThreadingDomain(FgThread.getHandler());
+        mHandler = FgThread.getHandler();
+        mThreadingDomain = new HandlerThreadingDomain(mHandler);
         mSharedLock = mThreadingDomain.getLockObject();
     }
 
@@ -182,8 +196,7 @@
     }
 
     private LocationTimeZoneProvider createPrimaryProvider() {
-        Resources resources = mContext.getResources();
-        if (!resources.getBoolean(R.bool.config_enablePrimaryLocationTimeZoneProvider)) {
+        if (isDisabled(PRIMARY_PROVIDER_NAME)) {
             return new NullLocationTimeZoneProvider(mThreadingDomain, PRIMARY_PROVIDER_NAME);
         }
 
@@ -193,6 +206,7 @@
         } else {
             proxy = new RealLocationTimeZoneProviderProxy(
                     mContext,
+                    mHandler,
                     mThreadingDomain,
                     PRIMARY_LOCATION_TIME_ZONE_SERVICE_ACTION,
                     R.bool.config_enablePrimaryLocationTimeZoneOverlay,
@@ -203,8 +217,7 @@
     }
 
     private LocationTimeZoneProvider createSecondaryProvider() {
-        Resources resources = mContext.getResources();
-        if (!resources.getBoolean(R.bool.config_enableSecondaryLocationTimeZoneProvider)) {
+        if (isDisabled(SECONDARY_PROVIDER_NAME)) {
             return new NullLocationTimeZoneProvider(mThreadingDomain, SECONDARY_PROVIDER_NAME);
         }
 
@@ -214,6 +227,7 @@
         } else {
             proxy = new RealLocationTimeZoneProviderProxy(
                     mContext,
+                    mHandler,
                     mThreadingDomain,
                     SECONDARY_LOCATION_TIME_ZONE_SERVICE_ACTION,
                     R.bool.config_enableSecondaryLocationTimeZoneOverlay,
@@ -223,9 +237,41 @@
         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, PROVIDER_MODE_SIMULATED);
+    }
+
+    /** Used for bug triage, tests and experiments to remove a provider. */
+    private boolean isDisabled(String providerName) {
+        return !isProviderEnabledInConfig(providerName)
+                || isProviderModeSetInSystemProperties(providerName, 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 systemPropertyProviderMode = SystemProperties.get(
+                PROVIDER_MODE_OVERRIDE_SYSTEM_PROPERTY_PREFIX + providerName, null);
+        return Objects.equals(systemPropertyProviderMode, mode);
     }
 
     @Override
@@ -237,19 +283,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
@@ -278,8 +350,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..9b8863f 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,25 @@
  */
 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.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 static final String CMD_SEND_PROVIDER_TEST_COMMAND = "send_provider_test_command";
+
     private final LocationTimeZoneManagerService mService;
 
     LocationTimeZoneManagerShellCommand(LocationTimeZoneManagerService service) {
@@ -35,8 +47,8 @@
         }
 
         switch (cmd) {
-            case "simulate_binder": {
-                return runSimulateBinderEvent();
+            case CMD_SEND_PROVIDER_TEST_COMMAND: {
+                return runSendProviderTestCommand();
             }
             default: {
                 return handleDefaultCommands(cmd);
@@ -44,37 +56,82 @@
         }
     }
 
-    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 <provider name> <test command>\n", CMD_SEND_PROVIDER_TEST_COMMAND);
+        pw.println("    Passes a test command to the named provider.");
         pw.println();
-        SimulatedBinderProviderEvent.printCommandLineOpts(pw);
+        pw.printf("%s details:\n", CMD_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<provider name>\" system"
+                        + " property and restarting the service or rebooting the device.\n",
+                LocationTimeZoneManagerService.PROVIDER_MODE_OVERRIDE_SYSTEM_PROPERTY_PREFIX);
+        pw.println("Values are:");
+        pw.printf("  %s - simulation mode (see below for commands)\n",
+                LocationTimeZoneManagerService.PROVIDER_MODE_SIMULATED);
+        pw.printf("  %s - disabled mode\n", LocationTimeZoneManagerService.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 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..d25e4a8 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,11 @@
 
 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_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 +34,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;
@@ -345,12 +351,21 @@
             }
             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);
+            }
         }
     }
 
@@ -483,6 +498,21 @@
      */
     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) {
+        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();
@@ -498,7 +528,7 @@
                 case PROVIDER_STATE_PERM_FAILED: {
                     // After entering perm failed, there is nothing to do. The remote peer is
                     // supposed to stop sending events after it has reported perm failure.
-                    logWarn("handleTimeZoneProviderEvent: Event=" + timeZoneProviderEvent
+                    warnLog("handleTimeZoneProviderEvent: Event=" + timeZoneProviderEvent
                             + " received for provider=" + this + " when in failed state");
                     return;
                 }
@@ -509,7 +539,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 +552,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 +574,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 +614,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/LocationTimeZoneProviderProxy.java b/services/core/java/com/android/server/location/timezone/LocationTimeZoneProviderProxy.java
index 91b52f1..0937b3e 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;
@@ -79,8 +80,8 @@
                 throw new IllegalStateException("listener already set");
             }
             this.mListener = listener;
+            onInitialize();
         }
-        onInitialize();
     }
 
     /**
@@ -94,6 +95,13 @@
     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
index e11a7ce..4b321e6 100644
--- a/services/core/java/com/android/server/location/timezone/NullLocationTimeZoneProvider.java
+++ b/services/core/java/com/android/server/location/timezone/NullLocationTimeZoneProvider.java
@@ -21,7 +21,6 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.util.IndentingPrintWriter;
-import android.util.Slog;
 
 import java.time.Duration;
 
@@ -74,11 +73,6 @@
     }
 
     @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}");
diff --git a/services/core/java/com/android/server/location/timezone/OWNERS b/services/core/java/com/android/server/location/timezone/OWNERS
new file mode 100644
index 0000000..28aff18
--- /dev/null
+++ b/services/core/java/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/core/java/com/android/server/location/timezone/RealLocationTimeZoneProviderProxy.java b/services/core/java/com/android/server/location/timezone/RealLocationTimeZoneProviderProxy.java
index 1bb5cec..ca81095 100644
--- a/services/core/java/com/android/server/location/timezone/RealLocationTimeZoneProviderProxy.java
+++ b/services/core/java/com/android/server/location/timezone/RealLocationTimeZoneProviderProxy.java
@@ -16,12 +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;
@@ -31,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
@@ -43,21 +56,51 @@
 
     @NonNull private final ServiceWatcher mServiceWatcher;
 
-    @GuardedBy("mProxyLock")
+    @GuardedBy("mSharedLock")
     @Nullable private ManagerProxy mManagerProxy;
 
-    @GuardedBy("mProxyLock")
+    @GuardedBy("mSharedLock")
     @NonNull private TimeZoneProviderRequest mRequest;
 
     RealLocationTimeZoneProviderProxy(
-            @NonNull Context context, @NonNull ThreadingDomain threadingDomain,
-            @NonNull String action, int enableOverlayResId,
-            int nonOverlayPackageResId) {
+            @NonNull Context context, @NonNull Handler handler,
+            @NonNull ThreadingDomain threadingDomain, @NonNull String action,
+            int enableOverlayResId, int nonOverlayPackageResId) {
         super(context, threadingDomain);
         mManagerProxy = null;
         mRequest = TimeZoneProviderRequest.createStopUpdatesRequest();
-        mServiceWatcher = new ServiceWatcher(context, action, this::onBind, this::onUnbind,
-                enableOverlayResId, nonOverlayPackageResId);
+
+        // 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, intentServiceCheckPredicate);
     }
 
     @Override
@@ -76,42 +119,26 @@
     }
 
     private void onBind(IBinder binder, ComponentName componentName) {
-        processServiceWatcherCallbackOnThreadingDomainThread(() -> onBindOnHandlerThread(binder));
-    }
-
-    private void onUnbind() {
-        processServiceWatcherCallbackOnThreadingDomainThread(this::onUnbindOnHandlerThread);
-    }
-
-    private void processServiceWatcherCallbackOnThreadingDomainThread(@NonNull Runnable runnable) {
-        // For simplicity, this code just post()s the runnable to the mThreadingDomain Thread in all
-        // cases. This adds a delay if ServiceWatcher and ThreadingDomain happen to be using the
-        // same thread, but nothing here should be performance critical.
-        mThreadingDomain.post(runnable);
-    }
-
-    private void onBindOnHandlerThread(@NonNull IBinder binder) {
         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();
         }
     }
 
-    private void onUnbindOnHandlerThread() {
+    private void onUnbind() {
         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();
         }
@@ -125,23 +152,43 @@
         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("mProxyLock")
+    @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) {
@@ -180,6 +227,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..d06438e 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,23 @@
 
 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.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;
 
 /**
@@ -36,6 +41,17 @@
  */
 class SimulatedLocationTimeZoneProviderProxy extends LocationTimeZoneProviderProxy {
 
+    private static final String TEST_COMMAND_NAME_ON_BIND = "on_bind";
+    private static final String TEST_COMMAND_NAME_ON_UNBIND = "on_unbind";
+    private static final String TEST_COMMAND_NAME_PERM_FAIL = "perm_fail";
+    private static final String TEST_COMMAND_NAME_UNCERTAIN = "uncertain";
+    private static final String TEST_COMMAND_NAME_SUCCESS = "success";
+
+    /**
+     * Used for {@link #TEST_COMMAND_NAME_SUCCESS}.
+     */
+    private static final String KEY_TIME_ZONE = "tz";
+
     @GuardedBy("mSharedLock")
     @NonNull private TimeZoneProviderRequest mRequest;
 
@@ -53,38 +69,56 @@
         // No-op - nothing to do for the simulated provider.
     }
 
-    void simulate(@NonNull SimulatedBinderProviderEvent event) {
+    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 TEST_COMMAND_NAME_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 TEST_COMMAND_NAME_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 TEST_COMMAND_NAME_PERM_FAIL:
+                case TEST_COMMAND_NAME_UNCERTAIN:
+                case TEST_COMMAND_NAME_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);
+            }
         }
     }
 
@@ -127,4 +161,47 @@
             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", TEST_COMMAND_NAME_ON_BIND);
+        pw.printf("%s\n", TEST_COMMAND_NAME_ON_UNBIND);
+        pw.printf("%s\n", TEST_COMMAND_NAME_PERM_FAIL);
+        pw.printf("%s\n", TEST_COMMAND_NAME_UNCERTAIN);
+        pw.printf("%s %s=string_array:<time zone id>[&<time zone id>]+\n",
+                TEST_COMMAND_NAME_SUCCESS, KEY_TIME_ZONE);
+    }
+
+    @NonNull
+    private static TimeZoneProviderEvent createTimeZoneProviderEventFromTestCommand(
+            @NonNull TestCommand testCommand) {
+        String name = testCommand.getName();
+        switch (name) {
+            case TEST_COMMAND_NAME_PERM_FAIL: {
+                return TimeZoneProviderEvent.createPermanentFailureEvent("Simulated failure");
+            }
+            case TEST_COMMAND_NAME_UNCERTAIN: {
+                return TimeZoneProviderEvent.createUncertainEvent();
+            }
+            case TEST_COMMAND_NAME_SUCCESS: {
+                Bundle args = testCommand.getArgs();
+                String[] timeZoneIds = args.getStringArray(KEY_TIME_ZONE);
+                if (timeZoneIds == null) {
+                    throw new IllegalArgumentException("No " + KEY_TIME_ZONE + " 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..70113b1
--- /dev/null
+++ b/services/core/java/com/android/server/location/timezone/TestCommand.java
@@ -0,0 +1,186 @@
+/*
+ * 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 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);
+    }
+
+    /**
+     * 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..d04d4a62 100644
--- a/services/core/java/com/android/server/location/timezone/ThreadingDomain.java
+++ b/services/core/java/com/android/server/location/timezone/ThreadingDomain.java
@@ -62,6 +62,14 @@
     }
 
     /**
+     * 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.checkArgument(Thread.currentThread() != getThread());
+    }
+
+    /**
      * Execute the supplied runnable on the threading domain's thread.
      */
     abstract void post(@NonNull Runnable runnable);
diff --git a/services/core/java/com/android/server/media/BluetoothRouteProvider.java b/services/core/java/com/android/server/media/BluetoothRouteProvider.java
index 8a13969..7afa81a 100644
--- a/services/core/java/com/android/server/media/BluetoothRouteProvider.java
+++ b/services/core/java/com/android/server/media/BluetoothRouteProvider.java
@@ -439,7 +439,6 @@
             }
         }
     }
-
     private class BluetoothBroadcastReceiver extends BroadcastReceiver {
         @Override
         public void onReceive(Context context, Intent intent) {
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 17ceb15..1b27ef4 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -43,7 +43,7 @@
 import android.content.pm.ParceledListSlice;
 import android.media.AudioManager;
 import android.media.AudioPlaybackConfiguration;
-import android.media.IRemoteVolumeControllerCallback;
+import android.media.IRemoteSessionCallback;
 import android.media.Session2Token;
 import android.media.session.IActiveSessionsListener;
 import android.media.session.IOnMediaKeyEventDispatchedListener;
@@ -142,7 +142,7 @@
 
     // Used to notify System UI and Settings when remote volume was changed.
     @GuardedBy("mLock")
-    final RemoteCallbackList<IRemoteVolumeControllerCallback> mRemoteVolumeControllers =
+    final RemoteCallbackList<IRemoteSessionCallback> mRemoteVolumeControllers =
             new RemoteCallbackList<>();
 
     private SessionPolicyProvider mCustomSessionPolicyProvider;
@@ -304,7 +304,7 @@
             MediaSession.Token token = session.getSessionToken();
             for (int i = size - 1; i >= 0; i--) {
                 try {
-                    IRemoteVolumeControllerCallback cb =
+                    IRemoteSessionCallback cb =
                             mRemoteVolumeControllers.getBroadcastItem(i);
                     cb.onVolumeChanged(token, flags);
                 } catch (Exception e) {
@@ -713,7 +713,7 @@
 
             for (int i = size - 1; i >= 0; i--) {
                 try {
-                    IRemoteVolumeControllerCallback cb =
+                    IRemoteSessionCallback cb =
                             mRemoteVolumeControllers.getBroadcastItem(i);
                     cb.onSessionChanged(token);
                 } catch (Exception e) {
@@ -1839,7 +1839,7 @@
         }
 
         @Override
-        public void registerRemoteVolumeControllerCallback(IRemoteVolumeControllerCallback rvc) {
+        public void registerRemoteSessionCallback(IRemoteSessionCallback rvc) {
             final int pid = Binder.getCallingPid();
             final int uid = Binder.getCallingUid();
             final long token = Binder.clearCallingIdentity();
@@ -1854,7 +1854,7 @@
         }
 
         @Override
-        public void unregisterRemoteVolumeControllerCallback(IRemoteVolumeControllerCallback rvc) {
+        public void unregisterRemoteSessionCallback(IRemoteSessionCallback rvc) {
             final int pid = Binder.getCallingPid();
             final int uid = Binder.getCallingUid();
             final long token = Binder.clearCallingIdentity();
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/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..c106558 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -66,6 +66,9 @@
 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_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 +105,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 +167,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 +215,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 +308,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 +1029,7 @@
                     nv.recycle();
                 }
                 reportUserInteraction(r);
-                mAssistants.notifyAssistantActionClicked(r.getSbn(), action, generatedByAssistant);
+                mAssistants.notifyAssistantActionClicked(r, action, generatedByAssistant);
             }
         }
 
@@ -1110,7 +1118,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 +1137,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 +1169,7 @@
                         reportUserInteraction(r);
                     }
                     mAssistants.notifyAssistantExpansionChangedLocked(
-                            r.getSbn(), userAction, expanded);
+                            r.getSbn(), r.getNotificationType(), userAction, expanded);
                 }
             }
         }
@@ -1180,7 +1188,7 @@
                             NotificationRecordLogger.NotificationEvent.NOTIFICATION_DIRECT_REPLIED,
                             r);
                     reportUserInteraction(r);
-                    mAssistants.notifyAssistantNotificationDirectReplyLocked(r.getSbn());
+                    mAssistants.notifyAssistantNotificationDirectReplyLocked(r);
                 }
             }
         }
@@ -1227,7 +1235,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 +2250,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 +3263,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 +4293,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 +4323,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 +6364,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 +8837,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 +8911,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 +9161,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 +9277,7 @@
             final StatusBarNotification sbn = r.getSbn();
             notifyAssistantLocked(
                     sbn,
+                    r.getNotificationType(),
                     true /* sameUserOnly */,
                     (assistant, sbnHolder) -> {
                         try {
@@ -9257,14 +9294,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 +9316,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 +9335,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 +9352,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 +9375,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 +9402,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 +9429,7 @@
         @GuardedBy("mNotificationLock")
         private void notifyAssistantLocked(
                 final StatusBarNotification sbn,
+                int notificationType,
                 boolean sameUserOnly,
                 BiConsumer<INotificationListener, StatusBarNotificationHolder> callback) {
             TrimCache trimCache = new TrimCache(sbn);
@@ -9397,7 +9443,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 +9497,22 @@
 
     public class NotificationListeners extends ManagedServices {
         static final String TAG_ENABLED_NOTIFICATION_LISTENERS = "enabled_listeners";
+        static final String TAG_REQUESTED_LISTENERS = "requested_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 +9609,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 +9673,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;
+
+                    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 +9797,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 +9852,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 +9894,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 +9934,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 +9946,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 +10161,7 @@
         }
     }
 
+
     class RoleObserver implements OnRoleHoldersChangedListener {
         // Role name : user id : list of approved packages
         private ArrayMap<String, ArrayMap<Integer, ArraySet<String>>> mNonBlockableDefaultApps;
@@ -10233,12 +10416,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 +10442,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/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/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 8071672..966be99 100644
--- a/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
+++ b/services/core/java/com/android/server/stats/pull/StatsPullAtomService.java
@@ -255,6 +255,9 @@
      */
     private static final int MIN_CPU_TIME_PER_UID_FREQ = 10;
 
+    /** Number of entries in CpuCyclesPerUidCluster atom stored in an array for each cluster. */
+    private static final int CPU_CYCLES_PER_UID_CLUSTER_VALUES = 3;
+
     private final Object mThermalLock = new Object();
     @GuardedBy("mThermalLock")
     private IThermalService mThermalService;
@@ -447,6 +450,12 @@
                         synchronized (mCpuTimePerUidLock) {
                             return pullCpuTimePerUidLocked(atomTag, data);
                         }
+                    case FrameworkStatsLog.CPU_CYCLES_PER_UID_CLUSTER:
+                        // Use the same lock as CPU_TIME_PER_UID_FREQ because data is pulled from
+                        // the same source.
+                        synchronized (mCpuTimePerUidFreqLock) {
+                            return pullCpuCyclesPerUidClusterLocked(atomTag, data);
+                        }
                     case FrameworkStatsLog.CPU_TIME_PER_UID_FREQ:
                         synchronized (mCpuTimePerUidFreqLock) {
                             return pullCpuTimePerUidFreqLocked(atomTag, data);
@@ -785,6 +794,7 @@
         registerKernelWakelock();
         registerCpuTimePerFreq();
         registerCpuTimePerUid();
+        registerCpuCyclesPerUidCluster();
         registerCpuTimePerUidFreq();
         registerCpuActiveTime();
         registerCpuClusterTime();
@@ -1502,6 +1512,97 @@
         return StatsManager.PULL_SUCCESS;
     }
 
+    private void registerCpuCyclesPerUidCluster() {
+        int tagId = FrameworkStatsLog.CPU_CYCLES_PER_UID_CLUSTER;
+        PullAtomMetadata metadata = new PullAtomMetadata.Builder()
+                .setAdditiveFields(new int[] {3, 4, 5})
+                .build();
+        mStatsManager.setPullAtomCallback(
+                tagId,
+                metadata,
+                DIRECT_EXECUTOR,
+                mStatsCallbackImpl
+        );
+    }
+
+    int pullCpuCyclesPerUidClusterLocked(int atomTag, List<StatsEvent> pulledData) {
+        PowerProfile powerProfile = new PowerProfile(mContext);
+        // Frequency index to frequency mapping.
+        long[] freqs = mCpuUidFreqTimeReader.readFreqs(powerProfile);
+        // Frequency index to cluster mapping.
+        int[] freqClusters = new int[freqs.length];
+        // Frequency index to power mapping.
+        double[] freqPowers = new double[freqs.length];
+        // Number of clusters.
+        int clusters;
+
+        // Initialize frequency mappings.
+        {
+            int cluster = 0;
+            int freqClusterIndex = 0;
+            long lastFreq = -1;
+            for (int freqIndex = 0; freqIndex < freqs.length; ++freqIndex, ++freqClusterIndex) {
+                long currFreq = freqs[freqIndex];
+                if (currFreq <= lastFreq) {
+                    cluster++;
+                    freqClusterIndex = 0;
+                }
+                freqClusters[freqIndex] = cluster;
+                freqPowers[freqIndex] =
+                        powerProfile.getAveragePowerForCpuCore(cluster, freqClusterIndex);
+                lastFreq = currFreq;
+            }
+
+            clusters = cluster + 1;
+        }
+
+        // Aggregate 0: mcycles, 1: runtime ms, 2: power profile estimate for the same uids for
+        // each cluster.
+        SparseArray<double[]> aggregated = new SparseArray<>();
+        mCpuUidFreqTimeReader.readAbsolute((uid, cpuFreqTimeMs) -> {
+            if (UserHandle.isIsolated(uid)) {
+                // Skip individual isolated uids because they are recycled and quickly removed from
+                // the underlying data source.
+                return;
+            } else if (UserHandle.isSharedAppGid(uid)) {
+                // All shared app gids are accounted together.
+                uid = LAST_SHARED_APPLICATION_GID;
+            } else {
+                // Everything else is accounted under their base uid.
+                uid = UserHandle.getAppId(uid);
+            }
+
+            double[] values = aggregated.get(uid);
+            if (values == null) {
+                values = new double[clusters * CPU_CYCLES_PER_UID_CLUSTER_VALUES];
+                aggregated.put(uid, values);
+            }
+
+            for (int freqIndex = 0; freqIndex < cpuFreqTimeMs.length; ++freqIndex) {
+                int cluster = freqClusters[freqIndex];
+                long timeMs = cpuFreqTimeMs[freqIndex];
+                values[cluster * CPU_CYCLES_PER_UID_CLUSTER_VALUES] += freqs[freqIndex] * timeMs;
+                values[cluster * CPU_CYCLES_PER_UID_CLUSTER_VALUES + 1] += timeMs;
+                values[cluster * CPU_CYCLES_PER_UID_CLUSTER_VALUES + 2] +=
+                        freqPowers[freqIndex] * timeMs;
+            }
+        });
+
+        int size = aggregated.size();
+        for (int i = 0; i < size; ++i) {
+            int uid = aggregated.keyAt(i);
+            double[] values = aggregated.valueAt(i);
+            for (int cluster = 0; cluster < clusters; ++cluster) {
+                pulledData.add(FrameworkStatsLog.buildStatsEvent(
+                        atomTag, uid, cluster,
+                        (long) (values[cluster * CPU_CYCLES_PER_UID_CLUSTER_VALUES] / 1e6),
+                        (long) values[cluster * CPU_CYCLES_PER_UID_CLUSTER_VALUES + 1],
+                        (long) (values[cluster * CPU_CYCLES_PER_UID_CLUSTER_VALUES + 2] / 1e3)));
+            }
+        }
+        return StatsManager.PULL_SUCCESS;
+    }
+
     private void registerCpuTimePerUidFreq() {
         // the throttling is 3sec, handled in
         // frameworks/base/core/java/com/android/internal/os/KernelCpuProcReader
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/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..c9d8eef 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.
@@ -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);
     }
@@ -3861,33 +3869,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 +3991,6 @@
                 displayContent.mAppTransition
                         .overridePendingAppTransitionStartCrossProfileApps();
                 break;
-            case ANIM_REMOTE_ANIMATION:
-                displayContent.mAppTransition.overridePendingAppTransitionRemote(
-                        pendingOptions.getRemoteAnimationAdapter());
-                break;
             case ANIM_NONE:
             case ANIM_UNDEFINED:
                 break;
@@ -4033,35 +4051,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 +4898,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 +5219,7 @@
             notifyAppStopped();
 
             if (finishing) {
-                clearOptionsLocked();
+                abortAndClearOptionsAnimation();
             } else {
                 if (deferRelaunchUntilPaused) {
                     destroyImmediately("stop-config");
@@ -5838,8 +5853,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 +6554,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..61da564 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;
@@ -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/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/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/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 269a910..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;
     }
 
     /**
@@ -2422,12 +2437,27 @@
     }
 
     private RootTaskInfo getRootTaskInfo(Task task) {
-        final TaskDisplayArea taskDisplayArea = task.getDisplayArea();
         RootTaskInfo info = new RootTaskInfo();
         task.fillTaskInfo(info);
 
-        // A task might be not attached to a display.
-        info.position = taskDisplayArea != null ? taskDisplayArea.getTaskIndexOf(task) : 0;
+        final DisplayContent displayContent = task.getDisplayContent();
+        if (displayContent == null) {
+            // A task might be not attached to a display.
+            info.position = -1;
+        } else {
+            // Find the task z-order among all root tasks on the display from bottom to top.
+            final int[] taskIndex = new int[1];
+            final boolean[] hasFound = new boolean[1];
+            displayContent.forAllRootTasks(rootTask -> {
+                if (task == rootTask) {
+                    hasFound[0] = true;
+                    return true;
+                }
+                taskIndex[0]++;
+                return false;
+            }, false /* traverseTopToBottom */);
+            info.position = hasFound[0] ? taskIndex[0] : -1;
+        }
         info.visible = task.shouldBeVisible(null);
         task.getBounds(info.bounds);
 
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 011500b..a0e6b42 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);
             }
@@ -4830,14 +4801,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;
@@ -6247,9 +6240,9 @@
         }
 
         if (anim) {
-            next.applyOptionsLocked();
+            next.applyOptionsAnimation();
         } else {
-            next.clearOptionsLocked();
+            next.abortAndClearOptionsAnimation();
         }
 
         mTaskSupervisor.mNoAnimActivities.clear();
@@ -6356,7 +6349,7 @@
 
                 mAtmService.getAppWarningsLocked().onResumeActivity(next);
                 next.app.setPendingUiCleanAndForceProcessStateUpTo(mAtmService.mTopProcessState);
-                next.clearOptionsLocked();
+                next.abortAndClearOptionsAnimation();
                 transaction.setLifecycleStateRequest(
                         ResumeActivityItem.obtain(next.app.getReportedProcState(),
                                 dc.isNextTransitionForward()));
@@ -6734,15 +6727,20 @@
         if (taskDisplayArea == null) {
             return false;
         }
-        final int index = taskDisplayArea.getTaskIndexOf(this);
-        if (index == 0) {
-            return false;
-        }
-        final int[] indexCount = new int[1];
+        final boolean[] hasFound = new boolean[1];
         final Task rootTaskBehind = taskDisplayArea.getRootTask(
-                // From bottom to top, find the one behind this Task.
-                task -> ++indexCount[0] == index, false /* traverseTopToBottom */);
-        return rootTaskBehind.isActivityTypeStandard();
+                // From top to bottom, find the one behind this Task.
+                task -> {
+                    if (hasFound[0]) {
+                        return true;
+                    }
+                    if (task == this) {
+                        // The next one is our target.
+                        hasFound[0] = true;
+                    }
+                    return false;
+                });
+        return rootTaskBehind != null && rootTaskBehind.isActivityTypeStandard();
     }
 
     boolean shouldUpRecreateTaskLocked(ActivityRecord srec, String destAffinity) {
@@ -7307,11 +7305,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();
@@ -7712,4 +7714,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 866abbd..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
@@ -212,34 +205,6 @@
         return getRootTask(t -> true);
     }
 
-    // TODO(b/175832855): Figure-out a way to remove since it might be a source of confusion.
-    /**
-     * Gets the order of the given {@link Task} as its z-order in the hierarchy below this TDA.
-     * The Task can be a direct child of a child TaskDisplayArea. {@code -1} if not found.
-     */
-    int getTaskIndexOf(Task task) {
-        int index = 0;
-        final int childCount = getChildCount();
-        for (int i = 0; i < childCount; i++) {
-            final WindowContainer wc = getChildAt(i);
-            if (wc.asTask() != null) {
-                if (wc.asTask() == task) {
-                    return index;
-                }
-                index++;
-            } else {
-                final TaskDisplayArea tda = wc.asTaskDisplayArea();
-                final int subIndex = tda.getTaskIndexOf(task);
-                if (subIndex > -1) {
-                    return index + subIndex;
-                } else {
-                    index += tda.getRootTaskCount();
-                }
-            }
-        }
-        return -1;
-    }
-
     @Nullable
     Task getRootHomeTask() {
         return mRootHomeTask;
@@ -1073,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();
     }
 
     /**
@@ -1102,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;
@@ -1209,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.
@@ -1419,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
@@ -1520,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/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 6e0fec1..576d224 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -1677,13 +1677,13 @@
         int count = mChildren.size();
         if (traverseTopToBottom) {
             for (int i = count - 1; i >= 0; --i) {
-                if (mChildren.get(i).forAllRootTasks(callback)) {
+                if (mChildren.get(i).forAllRootTasks(callback, traverseTopToBottom)) {
                     return true;
                 }
             }
         } else {
             for (int i = 0; i < count; i++) {
-                if (mChildren.get(i).forAllRootTasks(callback)) {
+                if (mChildren.get(i).forAllRootTasks(callback, traverseTopToBottom)) {
                     return true;
                 }
                 // Root tasks may be removed from this display. Ensure each task will be processed
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..90949cb 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -585,9 +585,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 +596,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..d1ea9a2 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() {
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..e3a8bb4 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -1442,7 +1442,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 +1489,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 +1655,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 +1666,7 @@
     }
 
     if (reinitializeGnssServiceHandle) {
-        android_location_GnssLocationProvider_set_gps_service_handle();
+        android_location_gnss_hal_GnssNative_set_gps_service_handle();
     }
 
     if (gnssHal == nullptr) {
@@ -1934,7 +1934,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 +1952,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.
      */
@@ -2087,7 +2087,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 +2096,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 +2118,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 +2127,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 +2136,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 +2146,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 +2174,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 +2186,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 +2198,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 +2209,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 +2257,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 +2269,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 +2397,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 +2411,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 +2495,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 +2527,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 +2535,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 +2576,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 +2597,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 +2608,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 +2619,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 +2631,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 +2666,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 +2677,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 +2685,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 +2697,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 +2707,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 +2801,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 +2875,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 +2906,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 +3006,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 +3018,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 +3032,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 +3040,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 +3058,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 +3066,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 +3096,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 +3265,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..88964b7 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,10 @@
                     <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="displayQuirks" name="quirks" minOccurs="0" maxOccurs="1" />
             </xs:sequence>
         </xs:complexType>
diff --git a/services/core/xsd/display-device-config/schema/current.txt b/services/core/xsd/display-device-config/schema/current.txt
index e68ca26..6906fda 100644
--- a/services/core/xsd/display-device-config/schema/current.txt
+++ b/services/core/xsd/display-device-config/schema/current.txt
@@ -4,8 +4,10 @@
   public class DisplayConfiguration {
     ctor public DisplayConfiguration();
     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 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);
   }
 
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 5f6d76be..7b4c1be 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -7620,10 +7620,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;
         }
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/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/OWNERS b/services/tests/servicestests/src/com/android/internal/location/timezone/OWNERS
new file mode 100644
index 0000000..28aff18
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/internal/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/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/hdmi/HdmiCecConfigTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java
index 777713e..798cf85 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java
@@ -18,13 +18,17 @@
 import static com.google.common.truth.Truth.assertThat;
 
 import static junit.framework.Assert.assertTrue;
+import static junit.framework.Assert.fail;
 
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 import static org.testng.Assert.assertThrows;
 
+import android.annotation.NonNull;
 import android.content.Context;
 import android.hardware.hdmi.HdmiControlManager;
+import android.os.test.TestLooper;
 import android.platform.test.annotations.Presubmit;
 import android.provider.Settings.Global;
 
@@ -32,21 +36,30 @@
 import androidx.test.filters.SmallTest;
 
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
 @SmallTest
 @Presubmit
 @RunWith(JUnit4.class)
 public final class HdmiCecConfigTest {
     private static final String TAG = "HdmiCecConfigTest";
 
+    private static final int TIMEOUT_CONTENT_CHANGE_SEC = 3;
+
+    private final TestLooper mTestLooper = new TestLooper();
+
     private Context mContext;
 
     @Mock private HdmiCecConfig.StorageAdapter mStorageAdapter;
+    @Mock private HdmiCecConfig.SettingChangeListener mSettingChangeListener;
 
     @Before
     public void setUp() throws Exception {
@@ -1019,4 +1032,119 @@
                 HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING,
                 Integer.toString(HdmiControlManager.SYSTEM_AUDIO_MODE_MUTING_DISABLED));
     }
+
+    @Test
+    public void registerChangeListener_SharedPref_BasicSanity() {
+        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
+                mContext, mStorageAdapter,
+                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+                        + "<cec-settings>"
+                        + "  <setting name=\"system_audio_mode_muting\""
+                        + "           value-type=\"int\""
+                        + "           user-configurable=\"true\">"
+                        + "    <allowed-values>"
+                        + "      <value int-value=\"0\" />"
+                        + "      <value int-value=\"1\" />"
+                        + "    </allowed-values>"
+                        + "    <default-value int-value=\"1\" />"
+                        + "  </setting>"
+                        + "</cec-settings>", null);
+        hdmiCecConfig.registerChangeListener(
+                HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING,
+                mSettingChangeListener);
+        hdmiCecConfig.setIntValue(
+                HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING,
+                HdmiControlManager.SYSTEM_AUDIO_MODE_MUTING_DISABLED);
+        verify(mSettingChangeListener).onChange(
+                HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING);
+    }
+
+    @Test
+    public void removeChangeListener_SharedPref_BasicSanity() {
+        HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
+                mContext, mStorageAdapter,
+                "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+                        + "<cec-settings>"
+                        + "  <setting name=\"system_audio_mode_muting\""
+                        + "           value-type=\"int\""
+                        + "           user-configurable=\"true\">"
+                        + "    <allowed-values>"
+                        + "      <value int-value=\"0\" />"
+                        + "      <value int-value=\"1\" />"
+                        + "    </allowed-values>"
+                        + "    <default-value int-value=\"1\" />"
+                        + "  </setting>"
+                        + "</cec-settings>", null);
+        hdmiCecConfig.registerChangeListener(
+                HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING,
+                mSettingChangeListener);
+        hdmiCecConfig.removeChangeListener(
+                HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING,
+                mSettingChangeListener);
+        hdmiCecConfig.setIntValue(
+                HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING,
+                HdmiControlManager.SYSTEM_AUDIO_MODE_MUTING_DISABLED);
+        verify(mSettingChangeListener, never()).onChange(
+                HdmiControlManager.CEC_SETTING_NAME_SYSTEM_AUDIO_MODE_MUTING);
+    }
+
+    /**
+     * Externally modified Global Settings still need to be supported. This test verifies that
+     * setting change notification is being forwarded to listeners registered via HdmiCecConfig.
+     */
+    @Test
+    @Ignore("b/175381065")
+    public void globalSettingObserver_BasicSanity() throws Exception {
+        CountDownLatch notifyLatch = new CountDownLatch(1);
+        // Get current value of the setting in the system.
+        String originalValue = Global.getString(mContext.getContentResolver(),
+                Global.HDMI_CONTROL_ENABLED);
+        try {
+            HdmiCecConfig hdmiCecConfig = HdmiCecConfig.createFromStrings(
+                    mContext, mStorageAdapter,
+                    "<?xml version='1.0' encoding='utf-8' standalone='yes' ?>"
+                            + "<cec-settings>"
+                            + "  <setting name=\"hdmi_cec_enabled\""
+                            + "           value-type=\"int\""
+                            + "           user-configurable=\"true\">"
+                            + "    <allowed-values>"
+                            + "      <value int-value=\"0\" />"
+                            + "      <value int-value=\"1\" />"
+                            + "    </allowed-values>"
+                            + "    <default-value int-value=\"1\" />"
+                            + "  </setting>"
+                            + "</cec-settings>", null);
+            hdmiCecConfig.registerGlobalSettingsObserver(mTestLooper.getLooper());
+            HdmiCecConfig.SettingChangeListener latchUpdateListener =
+                    new HdmiCecConfig.SettingChangeListener() {
+                        @Override
+                        public void onChange(
+                                @NonNull @HdmiControlManager.CecSettingName String setting) {
+                            notifyLatch.countDown();
+                            assertThat(setting).isEqualTo(
+                                    HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED);
+                        }
+                    };
+            hdmiCecConfig.registerChangeListener(
+                    HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED,
+                    latchUpdateListener);
+
+            // Flip the value of the setting.
+            String valueToSet = ((originalValue == null || originalValue.equals("1")) ? "0" : "1");
+            Global.putString(mContext.getContentResolver(), Global.HDMI_CONTROL_ENABLED,
+                    valueToSet);
+            assertThat(Global.getString(mContext.getContentResolver(),
+                    Global.HDMI_CONTROL_ENABLED)).isEqualTo(valueToSet);
+            mTestLooper.dispatchAll();
+
+            if (!notifyLatch.await(TIMEOUT_CONTENT_CHANGE_SEC, TimeUnit.SECONDS)) {
+                fail("Timed out waiting for the notify callback");
+            }
+            hdmiCecConfig.unregisterGlobalSettingsObserver();
+        } finally {
+            // Restore the previous value of the setting in the system.
+            Global.putString(mContext.getContentResolver(), Global.HDMI_CONTROL_ENABLED,
+                    originalValue);
+        }
+    }
 }
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..4de4d95 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
@@ -1053,11 +1053,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..c36812c 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
@@ -85,6 +85,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);
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/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/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..a093e0d
--- /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 = "<requested_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>"
+                + "</requested_listeners>";
+
+        TypedXmlPullParser parser = Xml.newFastPullParser();
+        parser.setInput(new BufferedInputStream(
+                new ByteArrayInputStream(xml.getBytes())), null);
+        parser.nextTag();
+        mListeners.readExtraTag("requested_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("requested_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(7);
+        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/ActivityDisplayTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
index ce0cd70..1700707 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityDisplayTests.java
@@ -248,7 +248,7 @@
         int topPosition = taskDisplayArea.getRootTaskCount() - 1;
         // Ensure the new alwaysOnTop stack is put below the pinned stack, but on top of the
         // existing alwaysOnTop stack.
-        assertEquals(topPosition - 1, taskDisplayArea.getTaskIndexOf(anotherAlwaysOnTopStack));
+        assertEquals(topPosition - 1, getTaskIndexOf(taskDisplayArea, anotherAlwaysOnTopStack));
 
         final Task nonAlwaysOnTopStack = taskDisplayArea.createRootTask(
                 WINDOWING_MODE_FREEFORM, ACTIVITY_TYPE_STANDARD, true /* onTop */);
@@ -256,7 +256,7 @@
         topPosition = taskDisplayArea.getRootTaskCount() - 1;
         // Ensure the non-alwaysOnTop stack is put below the three alwaysOnTop stacks, but above the
         // existing other non-alwaysOnTop stacks.
-        assertEquals(topPosition - 3, taskDisplayArea.getTaskIndexOf(nonAlwaysOnTopStack));
+        assertEquals(topPosition - 3, getTaskIndexOf(taskDisplayArea, nonAlwaysOnTopStack));
 
         anotherAlwaysOnTopStack.setAlwaysOnTop(false);
         taskDisplayArea.positionChildAt(POSITION_TOP, anotherAlwaysOnTopStack,
@@ -264,16 +264,16 @@
         assertFalse(anotherAlwaysOnTopStack.isAlwaysOnTop());
         // Ensure, when always on top is turned off for a stack, the stack is put just below all
         // other always on top stacks.
-        assertEquals(topPosition - 2, taskDisplayArea.getTaskIndexOf(anotherAlwaysOnTopStack));
+        assertEquals(topPosition - 2, getTaskIndexOf(taskDisplayArea, anotherAlwaysOnTopStack));
         anotherAlwaysOnTopStack.setAlwaysOnTop(true);
 
         // Ensure always on top state changes properly when windowing mode changes.
         anotherAlwaysOnTopStack.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
         assertFalse(anotherAlwaysOnTopStack.isAlwaysOnTop());
-        assertEquals(topPosition - 2, taskDisplayArea.getTaskIndexOf(anotherAlwaysOnTopStack));
+        assertEquals(topPosition - 2, getTaskIndexOf(taskDisplayArea, anotherAlwaysOnTopStack));
         anotherAlwaysOnTopStack.setWindowingMode(WINDOWING_MODE_FREEFORM);
         assertTrue(anotherAlwaysOnTopStack.isAlwaysOnTop());
-        assertEquals(topPosition - 1, taskDisplayArea.getTaskIndexOf(anotherAlwaysOnTopStack));
+        assertEquals(topPosition - 1, getTaskIndexOf(taskDisplayArea, anotherAlwaysOnTopStack));
 
         final Task dreamStack = taskDisplayArea.createRootTask(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_DREAM, true /* onTop */);
@@ -282,7 +282,7 @@
         topPosition = taskDisplayArea.getRootTaskCount() - 1;
         // Ensure dream shows above all activities, including PiP
         assertEquals(dreamStack, taskDisplayArea.getTopRootTask());
-        assertEquals(topPosition - 1, taskDisplayArea.getTaskIndexOf(pinnedStack));
+        assertEquals(topPosition - 1, getTaskIndexOf(taskDisplayArea, pinnedStack));
 
         final Task assistStack = taskDisplayArea.createRootTask(
                 WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_ASSISTANT, true /* onTop */);
@@ -295,7 +295,7 @@
         final boolean isAssistantOnTop = mContext.getResources()
                 .getBoolean(com.android.internal.R.bool.config_assistantOnTopOfDream);
         assertEquals(isAssistantOnTop ? topPosition : topPosition - 4,
-                taskDisplayArea.getTaskIndexOf(assistStack));
+                getTaskIndexOf(taskDisplayArea, assistStack));
     }
 
     @Test
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 da70a51..21aa6bf 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java
@@ -167,7 +167,7 @@
                 null /* task */);
 
         // Assert that stack is at the bottom.
-        assertEquals(0, mDefaultTaskDisplayArea.getTaskIndexOf(primarySplitScreen));
+        assertEquals(0, getTaskIndexOf(mDefaultTaskDisplayArea, primarySplitScreen));
 
         // Ensure no longer in splitscreen.
         assertEquals(WINDOWING_MODE_FULLSCREEN, primarySplitScreen.getWindowingMode());
@@ -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
@@ -748,10 +751,10 @@
         doReturn(false).when(fullscreenStack).isTranslucent(any());
 
         // Ensure that we don't move the home stack if it is already behind the top fullscreen stack
-        int homeStackIndex = mDefaultTaskDisplayArea.getTaskIndexOf(homeStack);
+        int homeStackIndex = getTaskIndexOf(mDefaultTaskDisplayArea, homeStack);
         assertEquals(fullscreenStack, getRootTaskAbove(homeStack));
         mDefaultTaskDisplayArea.moveRootTaskBehindBottomMostVisibleRootTask(homeStack);
-        assertEquals(homeStackIndex, mDefaultTaskDisplayArea.getTaskIndexOf(homeStack));
+        assertEquals(homeStackIndex, getTaskIndexOf(mDefaultTaskDisplayArea, homeStack));
     }
 
     @Test
@@ -766,10 +769,10 @@
         doReturn(true).when(fullscreenStack).isTranslucent(any());
 
         // Ensure that we don't move the home stack if it is already behind the top fullscreen stack
-        int homeStackIndex = mDefaultTaskDisplayArea.getTaskIndexOf(homeStack);
+        int homeStackIndex = getTaskIndexOf(mDefaultTaskDisplayArea, homeStack);
         assertEquals(fullscreenStack, getRootTaskAbove(homeStack));
         mDefaultTaskDisplayArea.moveRootTaskBehindBottomMostVisibleRootTask(homeStack);
-        assertEquals(homeStackIndex, mDefaultTaskDisplayArea.getTaskIndexOf(homeStack));
+        assertEquals(homeStackIndex, getTaskIndexOf(mDefaultTaskDisplayArea, homeStack));
     }
 
     @Test
@@ -784,10 +787,10 @@
         doReturn(false).when(fullscreenStack).isTranslucent(any());
 
         // Ensure we don't move the home stack if it is already on top
-        int homeStackIndex = mDefaultTaskDisplayArea.getTaskIndexOf(homeStack);
+        int homeStackIndex = getTaskIndexOf(mDefaultTaskDisplayArea, homeStack);
         assertNull(getRootTaskAbove(homeStack));
         mDefaultTaskDisplayArea.moveRootTaskBehindBottomMostVisibleRootTask(homeStack);
-        assertEquals(homeStackIndex, mDefaultTaskDisplayArea.getTaskIndexOf(homeStack));
+        assertEquals(homeStackIndex, getTaskIndexOf(mDefaultTaskDisplayArea, homeStack));
     }
 
     @Test
@@ -853,9 +856,9 @@
         doReturn(false).when(fullscreenStack2).isTranslucent(any());
 
         // Ensure we don't move the home stack behind itself
-        int homeStackIndex = mDefaultTaskDisplayArea.getTaskIndexOf(homeStack);
+        int homeStackIndex = getTaskIndexOf(mDefaultTaskDisplayArea, homeStack);
         mDefaultTaskDisplayArea.moveRootTaskBehindRootTask(homeStack, homeStack);
-        assertEquals(homeStackIndex, mDefaultTaskDisplayArea.getTaskIndexOf(homeStack));
+        assertEquals(homeStackIndex, getTaskIndexOf(mDefaultTaskDisplayArea, homeStack));
     }
 
     @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/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 56c8bd1..31e2dce 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -363,6 +363,33 @@
         }
     }
 
+    /**
+     * Gets the order of the given {@link Task} as its z-order in the hierarchy below this TDA.
+     * The Task can be a direct child of a child TaskDisplayArea. {@code -1} if not found.
+     */
+    static int getTaskIndexOf(TaskDisplayArea taskDisplayArea, Task task) {
+        int index = 0;
+        final int childCount = taskDisplayArea.getChildCount();
+        for (int i = 0; i < childCount; i++) {
+            final WindowContainer wc = taskDisplayArea.getChildAt(i);
+            if (wc.asTask() != null) {
+                if (wc.asTask() == task) {
+                    return index;
+                }
+                index++;
+            } else {
+                final TaskDisplayArea tda = wc.asTaskDisplayArea();
+                final int subIndex = getTaskIndexOf(tda, task);
+                if (subIndex > -1) {
+                    return index + subIndex;
+                } else {
+                    index += tda.getRootTaskCount();
+                }
+            }
+        }
+        return -1;
+    }
+
     /** Creates a {@link TaskDisplayArea} right above the default one. */
     static TaskDisplayArea createTaskDisplayArea(DisplayContent displayContent,
             WindowManagerService service, String name, int displayAreaFeature) {
@@ -1028,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/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/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..0e8bfe7 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;
@@ -365,7 +365,6 @@
     @Mock INetworkManagementService mNetworkManagementService;
     @Mock INetworkStatsService mStatsService;
     @Mock IBatteryStats mBatteryStatsService;
-    @Mock INetworkPolicyManager mNpm;
     @Mock IDnsResolver mMockDnsResolver;
     @Mock INetd mMockNetd;
     @Mock NetworkStackClient mNetworkStack;
@@ -380,6 +379,7 @@
     @Mock TelephonyManager mTelephonyManager;
     @Mock MockableSystemProperties mSystemProperties;
     @Mock EthernetManager mEthernetManager;
+    @Mock NetworkPolicyManager mNetworkPolicyManager;
 
     private ArgumentCaptor<ResolverParamsParcel> mResolverParamsParcelCaptor =
             ArgumentCaptor.forClass(ResolverParamsParcel.class);
@@ -477,6 +477,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 +1327,6 @@
         mService = new ConnectivityService(mServiceContext,
                 mNetworkManagementService,
                 mStatsService,
-                mNpm,
                 mMockDnsResolver,
                 mock(IpConnectivityLog.class),
                 mMockNetd,
@@ -1336,7 +1336,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
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.