Merge "Revert "Enforce that a PendingIntent has an explicit mutability ...""
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..f6d0d9d 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);
@@ -21865,6 +21912,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
@@ -45224,7 +45273,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])";
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index d6b7b08..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 {
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 576963d..f89f96f 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 {
@@ -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/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/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/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/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/TimeZoneProviderService.java b/core/java/android/service/timezone/TimeZoneProviderService.java
index 9533a8f..f2bf176 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
@@ -120,14 +130,6 @@
     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();
 
     /** Set by {@link #mHandler} thread. */
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/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/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 b66dd29e..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/View.java b/core/java/android/view/View.java
index 1d1c87d..24b71ab 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -29071,9 +29071,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..7d309eb 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;
@@ -7595,7 +7571,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 +7731,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 +9997,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 +10008,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 +10027,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/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/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/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/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..830771c 100644
--- a/core/tests/coretests/src/android/view/ViewRootImplTest.java
+++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java
@@ -22,7 +22,8 @@
 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;
@@ -153,7 +154,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 +164,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 +183,10 @@
         // 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());
     }
 }
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..91e6455 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" />
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/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/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/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/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index a2028ca..6bf2e99 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -306,25 +306,29 @@
 
 struct DrawImage final : Op {
     static const auto kType = Type::DrawImage;
-    DrawImage(sk_sp<const SkImage>&& image, SkScalar x, SkScalar y, const SkPaint* paint,
-              BitmapPalette palette)
-            : image(std::move(image)), x(x), y(y), palette(palette) {
+    DrawImage(sk_sp<const SkImage>&& image, SkScalar x, SkScalar y,
+              const SkSamplingOptions& sampling, const SkPaint* paint, BitmapPalette palette)
+            : image(std::move(image)), x(x), y(y), sampling(sampling), palette(palette) {
         if (paint) {
             this->paint = *paint;
         }
     }
     sk_sp<const SkImage> image;
     SkScalar x, y;
+    SkSamplingOptions sampling;
     SkPaint paint;
     BitmapPalette palette;
-    void draw(SkCanvas* c, const SkMatrix&) const { c->drawImage(image.get(), x, y, &paint); }
+    void draw(SkCanvas* c, const SkMatrix&) const {
+        c->drawImage(image.get(), x, y, sampling, &paint);
+    }
 };
 struct DrawImageRect final : Op {
     static const auto kType = Type::DrawImageRect;
     DrawImageRect(sk_sp<const SkImage>&& image, const SkRect* src, const SkRect& dst,
-                  const SkPaint* paint, SkCanvas::SrcRectConstraint constraint,
-                  BitmapPalette palette)
-            : image(std::move(image)), dst(dst), constraint(constraint), palette(palette) {
+                  const SkSamplingOptions& sampling, const SkPaint* paint,
+                  SkCanvas::SrcRectConstraint constraint, BitmapPalette palette)
+            : image(std::move(image)), dst(dst), sampling(sampling), constraint(constraint)
+            , palette(palette) {
         this->src = src ? *src : SkRect::MakeIWH(this->image->width(), this->image->height());
         if (paint) {
             this->paint = *paint;
@@ -332,23 +336,26 @@
     }
     sk_sp<const SkImage> image;
     SkRect src, dst;
+    SkSamplingOptions sampling;
     SkPaint paint;
     SkCanvas::SrcRectConstraint constraint;
     BitmapPalette palette;
     void draw(SkCanvas* c, const SkMatrix&) const {
-        c->drawImageRect(image.get(), src, dst, &paint, constraint);
+        c->drawImageRect(image.get(), src, dst, sampling, &paint, constraint);
     }
 };
 struct DrawImageLattice final : Op {
     static const auto kType = Type::DrawImageLattice;
     DrawImageLattice(sk_sp<const SkImage>&& image, int xs, int ys, int fs, const SkIRect& src,
-                     const SkRect& dst, const SkPaint* paint, BitmapPalette palette)
+                     const SkRect& dst, SkFilterMode filter, const SkPaint* paint,
+                     BitmapPalette palette)
             : image(std::move(image))
             , xs(xs)
             , ys(ys)
             , fs(fs)
             , src(src)
             , dst(dst)
+            , filter(filter)
             , palette(palette) {
         if (paint) {
             this->paint = *paint;
@@ -358,6 +365,7 @@
     int xs, ys, fs;
     SkIRect src;
     SkRect dst;
+    SkFilterMode filter;
     SkPaint paint;
     BitmapPalette palette;
     void draw(SkCanvas* c, const SkMatrix&) const {
@@ -366,7 +374,8 @@
         auto flags =
                 (0 == fs) ? nullptr : pod<SkCanvas::Lattice::RectType>(
                                               this, (xs + ys) * sizeof(int) + fs * sizeof(SkColor));
-        c->drawImageLattice(image.get(), {xdivs, ydivs, flags, xs, ys, &src, colors}, dst, &paint);
+        c->drawImageLattice(image.get(), {xdivs, ydivs, flags, xs, ys, &src, colors}, dst,
+                            filter, &paint);
     }
 };
 
@@ -431,9 +440,10 @@
 };
 struct DrawAtlas final : Op {
     static const auto kType = Type::DrawAtlas;
-    DrawAtlas(const SkImage* atlas, int count, SkBlendMode xfermode, const SkRect* cull,
-              const SkPaint* paint, bool has_colors)
-            : atlas(sk_ref_sp(atlas)), count(count), xfermode(xfermode), has_colors(has_colors) {
+    DrawAtlas(const SkImage* atlas, int count, SkBlendMode mode, const SkSamplingOptions& sampling,
+              const SkRect* cull, const SkPaint* paint, bool has_colors)
+            : atlas(sk_ref_sp(atlas)), count(count), mode(mode), sampling(sampling)
+            , has_colors(has_colors) {
         if (cull) {
             this->cull = *cull;
         }
@@ -443,7 +453,8 @@
     }
     sk_sp<const SkImage> atlas;
     int count;
-    SkBlendMode xfermode;
+    SkBlendMode mode;
+    SkSamplingOptions sampling;
     SkRect cull = kUnset;
     SkPaint paint;
     bool has_colors;
@@ -452,7 +463,8 @@
         auto texs = pod<SkRect>(this, count * sizeof(SkRSXform));
         auto colors = has_colors ? pod<SkColor>(this, count * (sizeof(SkRSXform) + sizeof(SkRect)))
                                  : nullptr;
-        c->drawAtlas(atlas.get(), xforms, texs, colors, count, xfermode, maybe_unset(cull), &paint);
+        c->drawAtlas(atlas.get(), xforms, texs, colors, count, mode, sampling, maybe_unset(cull),
+                     &paint);
     }
 };
 struct DrawShadowRec final : Op {
@@ -613,16 +625,18 @@
     this->push<DrawPicture>(0, picture, matrix, paint);
 }
 void DisplayListData::drawImage(sk_sp<const SkImage> image, SkScalar x, SkScalar y,
-                                const SkPaint* paint, BitmapPalette palette) {
-    this->push<DrawImage>(0, std::move(image), x, y, paint, palette);
+                                const SkSamplingOptions& sampling, const SkPaint* paint,
+                                BitmapPalette palette) {
+    this->push<DrawImage>(0, std::move(image), x, y, sampling, paint, palette);
 }
 void DisplayListData::drawImageRect(sk_sp<const SkImage> image, const SkRect* src,
-                                    const SkRect& dst, const SkPaint* paint,
-                                    SkCanvas::SrcRectConstraint constraint, BitmapPalette palette) {
-    this->push<DrawImageRect>(0, std::move(image), src, dst, paint, constraint, palette);
+                                    const SkRect& dst, const SkSamplingOptions& sampling,
+                                    const SkPaint* paint, SkCanvas::SrcRectConstraint constraint,
+                                    BitmapPalette palette) {
+    this->push<DrawImageRect>(0, std::move(image), src, dst, sampling, paint, constraint, palette);
 }
 void DisplayListData::drawImageLattice(sk_sp<const SkImage> image, const SkCanvas::Lattice& lattice,
-                                       const SkRect& dst, const SkPaint* paint,
+                                       const SkRect& dst, SkFilterMode filter, const SkPaint* paint,
                                        BitmapPalette palette) {
     int xs = lattice.fXCount, ys = lattice.fYCount;
     int fs = lattice.fRectTypes ? (xs + 1) * (ys + 1) : 0;
@@ -630,7 +644,7 @@
                    fs * sizeof(SkColor);
     SkASSERT(lattice.fBounds);
     void* pod = this->push<DrawImageLattice>(bytes, std::move(image), xs, ys, fs, *lattice.fBounds,
-                                             dst, paint, palette);
+                                             dst, filter, paint, palette);
     copy_v(pod, lattice.fXDivs, xs, lattice.fYDivs, ys, lattice.fColors, fs, lattice.fRectTypes,
            fs);
 }
@@ -650,18 +664,19 @@
     void* pod = this->push<DrawPoints>(count * sizeof(SkPoint), mode, count, paint);
     copy_v(pod, points, count);
 }
-void DisplayListData::drawVertices(const SkVertices* vertices, SkBlendMode mode, const SkPaint& paint) {
-    this->push<DrawVertices>(0, vertices, mode, paint);
+void DisplayListData::drawVertices(const SkVertices* vert, SkBlendMode mode, const SkPaint& paint) {
+    this->push<DrawVertices>(0, vert, mode, paint);
 }
 void DisplayListData::drawAtlas(const SkImage* atlas, const SkRSXform xforms[], const SkRect texs[],
                                 const SkColor colors[], int count, SkBlendMode xfermode,
-                                const SkRect* cull, const SkPaint* paint) {
+                                const SkSamplingOptions& sampling, const SkRect* cull,
+                                const SkPaint* paint) {
     size_t bytes = count * (sizeof(SkRSXform) + sizeof(SkRect));
     if (colors) {
         bytes += count * sizeof(SkColor);
     }
-    void* pod =
-            this->push<DrawAtlas>(bytes, atlas, count, xfermode, cull, paint, colors != nullptr);
+    void* pod = this->push<DrawAtlas>(bytes, atlas, count, xfermode, sampling, cull, paint,
+                                      colors != nullptr);
     copy_v(pod, xforms, count, texs, count, colors, colors ? count : 0);
 }
 void DisplayListData::drawShadowRec(const SkPath& path, const SkDrawShadowRec& rec) {
@@ -887,18 +902,20 @@
 }
 
 void RecordingCanvas::drawImage(const sk_sp<SkImage>& image, SkScalar x, SkScalar y,
-                                const SkPaint* paint, BitmapPalette palette) {
-    fDL->drawImage(image, x, y, paint, palette);
+                                const SkSamplingOptions& sampling, const SkPaint* paint,
+                                BitmapPalette palette) {
+    fDL->drawImage(image, x, y, sampling, paint, palette);
 }
 
 void RecordingCanvas::drawImageRect(const sk_sp<SkImage>& image, const SkRect& src,
-                                    const SkRect& dst, const SkPaint* paint,
-                                    SrcRectConstraint constraint, BitmapPalette palette) {
-    fDL->drawImageRect(image, &src, dst, paint, constraint, palette);
+                                    const SkRect& dst, const SkSamplingOptions& sampling,
+                                    const SkPaint* paint, SrcRectConstraint constraint,
+                                    BitmapPalette palette) {
+    fDL->drawImageRect(image, &src, dst, sampling, paint, constraint, palette);
 }
 
 void RecordingCanvas::drawImageLattice(const sk_sp<SkImage>& image, const Lattice& lattice,
-                                       const SkRect& dst, const SkPaint* paint,
+                                       const SkRect& dst, SkFilterMode filter, const SkPaint* paint,
                                        BitmapPalette palette) {
     if (!image || dst.isEmpty()) {
         return;
@@ -912,24 +929,29 @@
     }
 
     if (SkLatticeIter::Valid(image->width(), image->height(), latticePlusBounds)) {
-        fDL->drawImageLattice(image, latticePlusBounds, dst, paint, palette);
+        fDL->drawImageLattice(image, latticePlusBounds, dst, filter, paint, palette);
     } else {
-        fDL->drawImageRect(image, nullptr, dst, paint, SrcRectConstraint::kFast_SrcRectConstraint,
-                           palette);
+        SkSamplingOptions sampling(filter, SkMipmapMode::kNone);
+        fDL->drawImageRect(image, nullptr, dst, sampling, paint, kFast_SrcRectConstraint, palette);
     }
 }
 
-void RecordingCanvas::onDrawImage(const SkImage* img, SkScalar x, SkScalar y,
-                                  const SkPaint* paint) {
-    fDL->drawImage(sk_ref_sp(img), x, y, paint, BitmapPalette::Unknown);
+void RecordingCanvas::onDrawImage2(const SkImage* img, SkScalar x, SkScalar y,
+                                   const SkSamplingOptions& sampling, const SkPaint* paint) {
+    fDL->drawImage(sk_ref_sp(img), x, y, sampling, paint, BitmapPalette::Unknown);
 }
-void RecordingCanvas::onDrawImageRect(const SkImage* img, const SkRect* src, const SkRect& dst,
-                                      const SkPaint* paint, SrcRectConstraint constraint) {
-    fDL->drawImageRect(sk_ref_sp(img), src, dst, paint, constraint, BitmapPalette::Unknown);
+
+void RecordingCanvas::onDrawImageRect2(const SkImage* img, const SkRect& src, const SkRect& dst,
+                                       const SkSamplingOptions& sampling, const SkPaint* paint,
+                                       SrcRectConstraint constraint) {
+    fDL->drawImageRect(sk_ref_sp(img), &src, dst, sampling, paint, constraint,
+                       BitmapPalette::Unknown);
 }
-void RecordingCanvas::onDrawImageLattice(const SkImage* img, const SkCanvas::Lattice& lattice,
-                                         const SkRect& dst, const SkPaint* paint) {
-    fDL->drawImageLattice(sk_ref_sp(img), lattice, dst, paint, BitmapPalette::Unknown);
+
+void RecordingCanvas::onDrawImageLattice2(const SkImage* img, const SkCanvas::Lattice& lattice,
+                                          const SkRect& dst, SkFilterMode filter,
+                                          const SkPaint* paint) {
+    fDL->drawImageLattice(sk_ref_sp(img), lattice, dst, filter, paint, BitmapPalette::Unknown);
 }
 
 void RecordingCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4],
@@ -945,10 +967,11 @@
                                            SkBlendMode mode, const SkPaint& paint) {
     fDL->drawVertices(vertices, mode, paint);
 }
-void RecordingCanvas::onDrawAtlas(const SkImage* atlas, const SkRSXform xforms[],
-                                  const SkRect texs[], const SkColor colors[], int count,
-                                  SkBlendMode bmode, const SkRect* cull, const SkPaint* paint) {
-    fDL->drawAtlas(atlas, xforms, texs, colors, count, bmode, cull, paint);
+void RecordingCanvas::onDrawAtlas2(const SkImage* atlas, const SkRSXform xforms[],
+                                   const SkRect texs[], const SkColor colors[], int count,
+                                   SkBlendMode bmode, const SkSamplingOptions& sampling,
+                                   const SkRect* cull, const SkPaint* paint) {
+    fDL->drawAtlas(atlas, xforms, texs, colors, count, bmode, sampling, cull, paint);
 }
 void RecordingCanvas::onDrawShadowRec(const SkPath& path, const SkDrawShadowRec& rec) {
     fDL->drawShadowRec(path, rec);
diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h
index 9e2b0a9..89e3df7 100644
--- a/libs/hwui/RecordingCanvas.h
+++ b/libs/hwui/RecordingCanvas.h
@@ -108,19 +108,20 @@
 
     void drawTextBlob(const SkTextBlob*, SkScalar, SkScalar, const SkPaint&);
 
-    void drawImage(sk_sp<const SkImage>, SkScalar, SkScalar, const SkPaint*, BitmapPalette palette);
+    void drawImage(sk_sp<const SkImage>, SkScalar, SkScalar, const SkSamplingOptions&,
+                   const SkPaint*, BitmapPalette palette);
     void drawImageNine(sk_sp<const SkImage>, const SkIRect&, const SkRect&, const SkPaint*);
-    void drawImageRect(sk_sp<const SkImage>, const SkRect*, const SkRect&, const SkPaint*,
-                       SkCanvas::SrcRectConstraint, BitmapPalette palette);
+    void drawImageRect(sk_sp<const SkImage>, const SkRect*, const SkRect&, const SkSamplingOptions&,
+                       const SkPaint*, SkCanvas::SrcRectConstraint, BitmapPalette palette);
     void drawImageLattice(sk_sp<const SkImage>, const SkCanvas::Lattice&, const SkRect&,
-                          const SkPaint*, BitmapPalette);
+                          SkFilterMode, const SkPaint*, BitmapPalette);
 
     void drawPatch(const SkPoint[12], const SkColor[4], const SkPoint[4], SkBlendMode,
                    const SkPaint&);
     void drawPoints(SkCanvas::PointMode, size_t, const SkPoint[], const SkPaint&);
     void drawVertices(const SkVertices*, SkBlendMode, const SkPaint&);
     void drawAtlas(const SkImage*, const SkRSXform[], const SkRect[], const SkColor[], int,
-                   SkBlendMode, const SkRect*, const SkPaint*);
+                   SkBlendMode, const SkSamplingOptions&, const SkRect*, const SkPaint*);
     void drawShadowRec(const SkPath&, const SkDrawShadowRec&);
     void drawVectorDrawable(VectorDrawableRoot* tree);
     void drawWebView(skiapipeline::FunctorDrawable*);
@@ -178,25 +179,27 @@
 
     void onDrawTextBlob(const SkTextBlob*, SkScalar, SkScalar, const SkPaint&) override;
 
-    void drawImage(const sk_sp<SkImage>& image, SkScalar left, SkScalar top, const SkPaint* paint,
-                   BitmapPalette pallete);
+    void drawImage(const sk_sp<SkImage>&, SkScalar left, SkScalar top, const SkSamplingOptions&,
+                   const SkPaint* paint, BitmapPalette pallete);
 
     void drawImageRect(const sk_sp<SkImage>& image, const SkRect& src, const SkRect& dst,
-                       const SkPaint* paint, SrcRectConstraint constraint, BitmapPalette palette);
+                       const SkSamplingOptions&, const SkPaint*, SrcRectConstraint, BitmapPalette);
     void drawImageLattice(const sk_sp<SkImage>& image, const Lattice& lattice, const SkRect& dst,
-                          const SkPaint* paint, BitmapPalette palette);
+                          SkFilterMode, const SkPaint* paint, BitmapPalette palette);
 
-    void onDrawImage(const SkImage*, SkScalar, SkScalar, const SkPaint*) override;
-    void onDrawImageLattice(const SkImage*, const Lattice&, const SkRect&, const SkPaint*) override;
-    void onDrawImageRect(const SkImage*, const SkRect*, const SkRect&, const SkPaint*,
-                         SrcRectConstraint) override;
+    void onDrawImage2(const SkImage*, SkScalar, SkScalar, const SkSamplingOptions&,
+                      const SkPaint*) override;
+    void onDrawImageLattice2(const SkImage*, const Lattice&, const SkRect&, SkFilterMode,
+                             const SkPaint*) override;
+    void onDrawImageRect2(const SkImage*, const SkRect&, const SkRect&, const SkSamplingOptions&,
+                          const SkPaint*, SrcRectConstraint) override;
 
     void onDrawPatch(const SkPoint[12], const SkColor[4], const SkPoint[4], SkBlendMode,
                      const SkPaint&) override;
     void onDrawPoints(PointMode, size_t count, const SkPoint pts[], const SkPaint&) override;
     void onDrawVerticesObject(const SkVertices*, SkBlendMode, const SkPaint&) override;
-    void onDrawAtlas(const SkImage*, const SkRSXform[], const SkRect[], const SkColor[], int,
-                     SkBlendMode, const SkRect*, const SkPaint*) override;
+    void onDrawAtlas2(const SkImage*, const SkRSXform[], const SkRect[], const SkColor[], int,
+                     SkBlendMode, const SkSamplingOptions&, const SkRect*, const SkPaint*) override;
     void onDrawShadowRec(const SkPath&, const SkDrawShadowRec&) override;
 
     void drawVectorDrawable(VectorDrawableRoot* tree);
diff --git a/libs/hwui/SkiaCanvas.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/pipeline/skia/DumpOpsCanvas.h b/libs/hwui/pipeline/skia/DumpOpsCanvas.h
index d5b46d5..26ff8bf 100644
--- a/libs/hwui/pipeline/skia/DumpOpsCanvas.h
+++ b/libs/hwui/pipeline/skia/DumpOpsCanvas.h
@@ -86,17 +86,18 @@
         mOutput << mIdent << "drawTextBlob" << std::endl;
     }
 
-    void onDrawImage(const SkImage*, SkScalar dx, SkScalar dy, const SkPaint*) override {
+    void onDrawImage2(const SkImage*, SkScalar dx, SkScalar dy, const SkSamplingOptions&,
+                      const SkPaint*) override {
         mOutput << mIdent << "drawImage" << std::endl;
     }
 
-    void onDrawImageRect(const SkImage*, const SkRect*, const SkRect&, const SkPaint*,
-                         SrcRectConstraint) override {
+    void onDrawImageRect2(const SkImage*, const SkRect&, const SkRect&, const SkSamplingOptions&,
+                          const SkPaint*, SrcRectConstraint) override {
         mOutput << mIdent << "drawImageRect" << std::endl;
     }
 
-    void onDrawImageLattice(const SkImage*, const Lattice& lattice, const SkRect& dst,
-                            const SkPaint*) override {
+    void onDrawImageLattice2(const SkImage*, const Lattice& lattice, const SkRect& dst,
+                             SkFilterMode, const SkPaint*) override {
         mOutput << mIdent << "drawImageLattice" << std::endl;
     }
 
diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
index e292cbd..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 40b5747..d3c41191 100644
--- a/libs/hwui/tests/common/CallCountingCanvas.h
+++ b/libs/hwui/tests/common/CallCountingCanvas.h
@@ -108,27 +108,27 @@
     }
 
     int drawImageCount = 0;
-    void onDrawImage(const SkImage* image, SkScalar dx, SkScalar dy,
+    void onDrawImage2(const SkImage* image, SkScalar dx, SkScalar dy, const SkSamplingOptions&,
                      const SkPaint* paint) override {
         drawImageCount++;
     }
 
     int drawImageRectCount = 0;
-    void onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
-                         const SkPaint* paint, SkCanvas::SrcRectConstraint constraint) override {
+    void onDrawImageRect2(const SkImage*, const SkRect&, const SkRect&, const SkSamplingOptions&,
+                          const SkPaint*, SkCanvas::SrcRectConstraint) override {
         drawImageRectCount++;
     }
 
     int drawImageLatticeCount = 0;
-    void onDrawImageLattice(const SkImage* image, const SkCanvas::Lattice& lattice,
-                            const SkRect& dst, const SkPaint* paint) override {
+    void onDrawImageLattice2(const SkImage* image, const SkCanvas::Lattice& lattice,
+                             const SkRect& dst, SkFilterMode, const SkPaint* paint) override {
         drawImageLatticeCount++;
     }
 
     int drawAtlasCount = 0;
-    void onDrawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect rect[],
-                     const SkColor colors[], int count, SkBlendMode mode, const SkRect* cull,
-                     const SkPaint* paint) override {
+    void onDrawAtlas2(const SkImage* atlas, const SkRSXform xform[], const SkRect rect[],
+                      const SkColor colors[], int count, SkBlendMode mode, const SkSamplingOptions&,
+                      const SkRect* cull, const SkPaint* paint) override {
         drawAtlasCount++;
     }
 
diff --git a/libs/hwui/tests/unit/FatalTestCanvas.h b/libs/hwui/tests/unit/FatalTestCanvas.h
index 8467be9..2a74afc 100644
--- a/libs/hwui/tests/unit/FatalTestCanvas.h
+++ b/libs/hwui/tests/unit/FatalTestCanvas.h
@@ -60,22 +60,23 @@
     void onDrawVerticesObject(const SkVertices*, SkBlendMode, const SkPaint&) {
         ADD_FAILURE() << "onDrawVertices not expected in this test";
     }
-    void onDrawAtlas(const SkImage*, const SkRSXform[], const SkRect[], const SkColor[], int count,
-                     SkBlendMode, const SkRect* cull, const SkPaint*) {
+    void onDrawAtlas2(const SkImage*, const SkRSXform[], const SkRect[], const SkColor[], int count,
+                      SkBlendMode, const SkSamplingOptions&, const SkRect* cull, const SkPaint*) {
         ADD_FAILURE() << "onDrawAtlas not expected in this test";
     }
     void onDrawPath(const SkPath&, const SkPaint&) {
         ADD_FAILURE() << "onDrawPath not expected in this test";
     }
-    void onDrawImage(const SkImage*, SkScalar dx, SkScalar dy, const SkPaint*) {
+    void onDrawImage2(const SkImage*, SkScalar dx, SkScalar dy, const SkSamplingOptions&,
+                      const SkPaint*) {
         ADD_FAILURE() << "onDrawImage not expected in this test";
     }
-    void onDrawImageRect(const SkImage*, const SkRect*, const SkRect&, const SkPaint*,
-                         SrcRectConstraint) {
+    void onDrawImageRect2(const SkImage*, const SkRect&, const SkRect&, const SkSamplingOptions&,
+                          const SkPaint*, SrcRectConstraint) {
         ADD_FAILURE() << "onDrawImageRect not expected in this test";
     }
-    void onDrawImageLattice(const SkImage*, const Lattice& lattice, const SkRect& dst,
-                            const SkPaint*) {
+    void onDrawImageLattice2(const SkImage*, const Lattice& lattice, const SkRect& dst,
+                             SkFilterMode, const SkPaint*) {
         ADD_FAILURE() << "onDrawImageLattice not expected in this test";
     }
     void onClipRRect(const SkRRect& rrect, SkClipOp, ClipEdgeStyle) {
diff --git a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
index 26bc659..423400e 100644
--- a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
+++ b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
@@ -938,7 +938,8 @@
         void onDrawRect(const SkRect& rect, const SkPaint& paint) override {
             EXPECT_EQ(0, mDrawCounter++);
         }
-        void onDrawImage(const SkImage*, SkScalar dx, SkScalar dy, const SkPaint*) override {
+        void onDrawImage2(const SkImage*, SkScalar dx, SkScalar dy, const SkSamplingOptions&,
+                          const SkPaint*) override {
             EXPECT_EQ(1, mDrawCounter++);
         }
     };
@@ -1047,7 +1048,7 @@
     EXPECT_EQ(2, canvas.mDrawCounter);
 }
 
-// Verify that layers are composed with kLow_SkFilterQuality filter quality.
+// Verify that layers are composed with linear filtering.
 RENDERTHREAD_SKIA_PIPELINE_TEST(RenderNodeDrawable, layerComposeQuality) {
     static const int CANVAS_WIDTH = 1;
     static const int CANVAS_HEIGHT = 1;
@@ -1056,10 +1057,12 @@
     class FrameTestCanvas : public TestCanvasBase {
     public:
         FrameTestCanvas() : TestCanvasBase(CANVAS_WIDTH, CANVAS_HEIGHT) {}
-        void onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst,
-                             const SkPaint* paint, SrcRectConstraint constraint) override {
+        void onDrawImageRect2(const SkImage* image, const SkRect& src, const SkRect& dst,
+                              const SkSamplingOptions& sampling, const SkPaint* paint,
+                              SrcRectConstraint constraint) override {
             mDrawCounter++;
-            EXPECT_EQ(kLow_SkFilterQuality, paint->getFilterQuality());
+            EXPECT_FALSE(sampling.useCubic);
+            EXPECT_EQ(SkFilterMode::kLinear, sampling.filter);
         }
     };
 
@@ -1169,8 +1172,9 @@
     class VectorDrawableTestCanvas : public TestCanvasBase {
     public:
         VectorDrawableTestCanvas() : TestCanvasBase(CANVAS_WIDTH, CANVAS_HEIGHT) {}
-        void onDrawImageRect(const SkImage*, const SkRect* src, const SkRect& dst,
-                              const SkPaint* paint, SrcRectConstraint constraint) override {
+        void onDrawImageRect2(const SkImage*, const SkRect& src, const SkRect& dst,
+                              const SkSamplingOptions&, const SkPaint* paint,
+                              SrcRectConstraint constraint) override {
             const int index = mDrawCounter++;
             switch (index) {
                 case 0:
diff --git a/libs/hwui/tests/unit/SkiaPipelineTests.cpp b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
index e7a889d..6dd57b1 100644
--- a/libs/hwui/tests/unit/SkiaPipelineTests.cpp
+++ b/libs/hwui/tests/unit/SkiaPipelineTests.cpp
@@ -302,7 +302,8 @@
     class ClippedTestCanvas : public SkCanvas {
     public:
         ClippedTestCanvas() : SkCanvas(CANVAS_WIDTH, CANVAS_HEIGHT) {}
-        void onDrawImage(const SkImage*, SkScalar dx, SkScalar dy, const SkPaint*) override {
+        void onDrawImage2(const SkImage*, SkScalar dx, SkScalar dy, const SkSamplingOptions&,
+                          const SkPaint*) override {
             EXPECT_EQ(0, mDrawCounter++);
             EXPECT_EQ(SkRect::MakeLTRB(10, 20, 30, 40), TestUtils::getClipBounds(this));
             EXPECT_TRUE(getTotalMatrix().isIdentity());
@@ -336,7 +337,8 @@
     class ClippedTestCanvas : public SkCanvas {
     public:
         ClippedTestCanvas() : SkCanvas(CANVAS_WIDTH, CANVAS_HEIGHT) {}
-        void onDrawImage(const SkImage*, SkScalar dx, SkScalar dy, const SkPaint*) override {
+        void onDrawImage2(const SkImage*, SkScalar dx, SkScalar dy, const SkSamplingOptions&,
+                          const SkPaint*) override {
             EXPECT_EQ(0, mDrawCounter++);
             // Expect clip to be rotated.
             EXPECT_EQ(SkRect::MakeLTRB(CANVAS_HEIGHT - dirty.fTop - dirty.height(), dirty.fLeft,
diff --git a/location/java/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 6026862..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
@@ -2807,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;
@@ -2868,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);
         }
@@ -2925,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/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/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/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/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/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/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..a184cd7 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 -->
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java
index 1a71f11..d672c2c 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ActivityOptionsCompat.java
@@ -16,8 +16,6 @@
 
 package com.android.systemui.shared.system;
 
-import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT;
-import static android.app.ActivityTaskManager.SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT;
 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
 import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
@@ -48,9 +46,6 @@
         options.setLaunchWindowingMode(isPrimary
                 ? WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
                 : WINDOWING_MODE_SPLIT_SCREEN_SECONDARY);
-        options.setSplitScreenCreateMode(dockTopLeft
-                ? SPLIT_SCREEN_CREATE_MODE_TOP_OR_LEFT
-                : SPLIT_SCREEN_CREATE_MODE_BOTTOM_OR_RIGHT);
         return options;
     }
 
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/LatencyTrackerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/LatencyTrackerCompat.java
index bf4fb0b..a8c19ec 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/LatencyTrackerCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/LatencyTrackerCompat.java
@@ -24,10 +24,6 @@
  * @see LatencyTracker
  */
 public class LatencyTrackerCompat {
-    public static boolean isEnabled(Context context) {
-        return LatencyTracker.isEnabled(context);
-    }
-
     /**
      * @see LatencyTracker
      * @deprecated Please use {@link LatencyTrackerCompat#logToggleRecents(Context, int)} instead.
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewTreeObserverWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewTreeObserverWrapper.java
index 4a870f1..cfb23f9 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewTreeObserverWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewTreeObserverWrapper.java
@@ -16,6 +16,7 @@
 
 package com.android.systemui.shared.system;
 
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.graphics.Rect;
 import android.graphics.Region;
@@ -26,18 +27,21 @@
 
 public class ViewTreeObserverWrapper {
 
+    private static final HashMap<OnComputeInsetsListener, ViewTreeObserver>
+            sListenerObserverMap = new HashMap<>();
     private static final HashMap<OnComputeInsetsListener, OnComputeInternalInsetsListener>
-            sOnComputeInsetsListenerMap = new HashMap<>();
+            sListenerInternalListenerMap = new HashMap<>();
 
     /**
-     * Register a callback to be invoked when the invoked when it is time to
-     * compute the window's insets.
+     * Register a callback to be invoked when the invoked when it is time to compute the window's
+     * insets.
      *
+     * @param observer The observer to be added
      * @param listener The callback to add
      * @throws IllegalStateException If {@link ViewTreeObserver#isAlive()} returns false
      */
     public static void addOnComputeInsetsListener(
-            ViewTreeObserver observer, OnComputeInsetsListener listener) {
+            @NonNull ViewTreeObserver observer, @NonNull OnComputeInsetsListener listener) {
         final OnComputeInternalInsetsListener internalListener = internalInOutInfo -> {
             final InsetsInfo inOutInfo = new InsetsInfo();
             inOutInfo.contentInsets.set(internalInOutInfo.contentInsets);
@@ -49,23 +53,26 @@
             internalInOutInfo.touchableRegion.set(inOutInfo.touchableRegion);
             internalInOutInfo.setTouchableInsets(inOutInfo.mTouchableInsets);
         };
-        sOnComputeInsetsListenerMap.put(listener, internalListener);
+        sListenerObserverMap.put(listener, observer);
+        sListenerInternalListenerMap.put(listener, internalListener);
         observer.addOnComputeInternalInsetsListener(internalListener);
     }
 
     /**
-     * Remove a previously installed insets computation callback
+     * Remove a previously installed insets computation callback.
      *
      * @param victim The callback to remove
      * @throws IllegalStateException If {@link ViewTreeObserver#isAlive()} returns false
      * @see #addOnComputeInsetsListener(ViewTreeObserver, OnComputeInsetsListener)
      */
-    public void removeOnComputeInsetsListener(
-            ViewTreeObserver observer, OnComputeInsetsListener victim) {
-        final OnComputeInternalInsetsListener listener = sOnComputeInsetsListenerMap.get(victim);
-        if (listener != null) {
+    public static void removeOnComputeInsetsListener(@NonNull OnComputeInsetsListener victim) {
+        final ViewTreeObserver observer = sListenerObserverMap.get(victim);
+        final OnComputeInternalInsetsListener listener = sListenerInternalListenerMap.get(victim);
+        if (observer != null && listener != null) {
             observer.removeOnComputeInternalInsetsListener(listener);
         }
+        sListenerObserverMap.remove(victim);
+        sListenerInternalListenerMap.remove(victim);
     }
 
     /**
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
index d3066b4..b38270c 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/WindowManagerWrapper.java
@@ -83,6 +83,11 @@
     public static final int WINDOWING_MODE_FREEFORM = WindowConfiguration.WINDOWING_MODE_FREEFORM;
 
     public static final int ITYPE_EXTRA_NAVIGATION_BAR = InsetsState.ITYPE_EXTRA_NAVIGATION_BAR;
+    public static final int ITYPE_LEFT_TAPPABLE_ELEMENT = InsetsState.ITYPE_LEFT_TAPPABLE_ELEMENT;
+    public static final int ITYPE_TOP_TAPPABLE_ELEMENT = InsetsState.ITYPE_TOP_TAPPABLE_ELEMENT;
+    public static final int ITYPE_RIGHT_TAPPABLE_ELEMENT = InsetsState.ITYPE_RIGHT_TAPPABLE_ELEMENT;
+    public static final int ITYPE_BOTTOM_TAPPABLE_ELEMENT =
+            InsetsState.ITYPE_BOTTOM_TAPPABLE_ELEMENT;
 
     private static final WindowManagerWrapper sInstance = new WindowManagerWrapper();
 
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardInputView.java
index d58b95c..d1494df 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardInputView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardInputView.java
@@ -23,10 +23,16 @@
 
 import androidx.annotation.Nullable;
 
+import com.android.systemui.Gefingerpoken;
+
+import java.util.ArrayList;
+import java.util.List;
+
 /**
  * A Base class for all Keyguard password/pattern/pin related inputs.
  */
 public abstract class KeyguardInputView extends LinearLayout {
+    private final List<Gefingerpoken> mMotionEventListener = new ArrayList<>();
 
     public KeyguardInputView(Context context) {
         super(context);
@@ -56,4 +62,25 @@
     boolean startDisappearAnimation(Runnable finishRunnable) {
         return false;
     }
+
+    void addMotionEventListener(Gefingerpoken listener) {
+        mMotionEventListener.add(listener);
+    }
+
+    void removeMotionEventListener(Gefingerpoken listener) {
+        mMotionEventListener.remove(listener);
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent event) {
+        return mMotionEventListener.stream().anyMatch(listener -> listener.onTouchEvent(event))
+                || super.onTouchEvent(event);
+    }
+
+    @Override
+    public boolean onInterceptTouchEvent(MotionEvent event) {
+        return mMotionEventListener.stream().anyMatch(
+                listener -> listener.onInterceptTouchEvent(event))
+                || super.onInterceptTouchEvent(event);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
index 6aa5e0d..1c691e7 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java
@@ -26,6 +26,7 @@
 import com.android.internal.widget.LockPatternUtils;
 import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
 import com.android.systemui.R;
+import com.android.systemui.classifier.FalsingCollector;
 import com.android.systemui.dagger.qualifiers.Main;
 import com.android.systemui.util.ViewController;
 import com.android.systemui.util.concurrency.DelayableExecutor;
@@ -155,6 +156,7 @@
         private final Resources mResources;
         private LiftToActivateListener mLiftToActivateListener;
         private TelephonyManager mTelephonyManager;
+        private final FalsingCollector mFalsingCollector;
 
         @Inject
         public Factory(KeyguardUpdateMonitor keyguardUpdateMonitor,
@@ -163,7 +165,7 @@
                 KeyguardMessageAreaController.Factory messageAreaControllerFactory,
                 InputMethodManager inputMethodManager, @Main DelayableExecutor mainExecutor,
                 @Main Resources resources, LiftToActivateListener liftToActivateListener,
-                TelephonyManager telephonyManager) {
+                TelephonyManager telephonyManager, FalsingCollector falsingCollector) {
             mKeyguardUpdateMonitor = keyguardUpdateMonitor;
             mLockPatternUtils = lockPatternUtils;
             mLatencyTracker = latencyTracker;
@@ -173,6 +175,7 @@
             mResources = resources;
             mLiftToActivateListener = liftToActivateListener;
             mTelephonyManager = telephonyManager;
+            mFalsingCollector = falsingCollector;
         }
 
         /** Create a new {@link KeyguardInputViewController}. */
@@ -191,17 +194,17 @@
                 return new KeyguardPinViewController((KeyguardPINView) keyguardInputView,
                         mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
                         keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker,
-                        mLiftToActivateListener);
+                        mLiftToActivateListener, mFalsingCollector);
             } else if (keyguardInputView instanceof KeyguardSimPinView) {
                 return new KeyguardSimPinViewController((KeyguardSimPinView) keyguardInputView,
                         mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
                         keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker,
-                        mLiftToActivateListener, mTelephonyManager);
+                        mLiftToActivateListener, mTelephonyManager, mFalsingCollector);
             } else if (keyguardInputView instanceof KeyguardSimPukView) {
                 return new KeyguardSimPukViewController((KeyguardSimPukView) keyguardInputView,
                         mKeyguardUpdateMonitor, securityMode, mLockPatternUtils,
                         keyguardSecurityCallback, mMessageAreaControllerFactory, mLatencyTracker,
-                        mLiftToActivateListener, mTelephonyManager);
+                        mLiftToActivateListener, mTelephonyManager, mFalsingCollector);
             }
 
             throw new RuntimeException("Unable to find controller for " + keyguardInputView);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java
index 4d0ebff..f247948 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java
@@ -25,12 +25,15 @@
 import com.android.internal.util.LatencyTracker;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
+import com.android.systemui.Gefingerpoken;
 import com.android.systemui.R;
+import com.android.systemui.classifier.FalsingCollector;
 
 public abstract class KeyguardPinBasedInputViewController<T extends KeyguardPinBasedInputView>
         extends KeyguardAbsKeyInputViewController<T> {
 
     private final LiftToActivateListener mLiftToActivateListener;
+    private final FalsingCollector mFalsingCollector;
     protected PasswordTextView mPasswordEntry;
 
     private final OnKeyListener mOnKeyListener = (v, keyCode, event) -> {
@@ -40,13 +43,26 @@
         return false;
     };
 
-    private final OnTouchListener mOnTouchListener = (v, event) -> {
+    private final OnTouchListener mActionButtonTouchListener = (v, event) -> {
         if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
             mView.doHapticKeyClick();
         }
         return false;
     };
 
+    private final Gefingerpoken mGlobalTouchListener = new Gefingerpoken() {
+        @Override
+        public boolean onInterceptTouchEvent(MotionEvent ev) {
+            mFalsingCollector.avoidGesture();
+            return false;
+        }
+
+        @Override
+        public boolean onTouchEvent(MotionEvent ev) {
+            return false;
+        }
+    };
+
     protected KeyguardPinBasedInputViewController(T view,
             KeyguardUpdateMonitor keyguardUpdateMonitor,
             SecurityMode securityMode,
@@ -54,10 +70,12 @@
             KeyguardSecurityCallback keyguardSecurityCallback,
             KeyguardMessageAreaController.Factory messageAreaControllerFactory,
             LatencyTracker latencyTracker,
-            LiftToActivateListener liftToActivateListener) {
+            LiftToActivateListener liftToActivateListener,
+            FalsingCollector falsingCollector) {
         super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
                 messageAreaControllerFactory, latencyTracker);
         mLiftToActivateListener = liftToActivateListener;
+        mFalsingCollector = falsingCollector;
         mPasswordEntry = mView.findViewById(mView.getPasswordTextViewId());
     }
 
@@ -65,11 +83,13 @@
     protected void onViewAttached() {
         super.onViewAttached();
 
+        mView.addMotionEventListener(mGlobalTouchListener);
+
         mPasswordEntry.setOnKeyListener(mOnKeyListener);
         mPasswordEntry.setUserActivityListener(this::onUserInput);
 
         View deleteButton = mView.findViewById(R.id.delete_button);
-        deleteButton.setOnTouchListener(mOnTouchListener);
+        deleteButton.setOnTouchListener(mActionButtonTouchListener);
         deleteButton.setOnClickListener(v -> {
             // check for time-based lockouts
             if (mPasswordEntry.isEnabled()) {
@@ -87,7 +107,7 @@
 
         View okButton = mView.findViewById(R.id.key_enter);
         if (okButton != null) {
-            okButton.setOnTouchListener(mOnTouchListener);
+            okButton.setOnTouchListener(mActionButtonTouchListener);
             okButton.setOnClickListener(new View.OnClickListener() {
                 @Override
                 public void onClick(View v) {
@@ -101,6 +121,12 @@
     }
 
     @Override
+    protected void onViewDetached() {
+        super.onViewDetached();
+        mView.removeMotionEventListener(mGlobalTouchListener);
+    }
+
+    @Override
     public void onResume(int reason) {
         super.onResume(reason);
         mPasswordEntry.requestFocus();
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
index fb0d6be..c0aa2af 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
@@ -22,6 +22,7 @@
 import com.android.internal.widget.LockPatternUtils;
 import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
 import com.android.systemui.R;
+import com.android.systemui.classifier.FalsingCollector;
 
 public class KeyguardPinViewController
         extends KeyguardPinBasedInputViewController<KeyguardPINView> {
@@ -32,10 +33,11 @@
             SecurityMode securityMode, LockPatternUtils lockPatternUtils,
             KeyguardSecurityCallback keyguardSecurityCallback,
             KeyguardMessageAreaController.Factory messageAreaControllerFactory,
-            LatencyTracker latencyTracker,
-            LiftToActivateListener liftToActivateListener) {
+            LatencyTracker latencyTracker, LiftToActivateListener liftToActivateListener,
+            FalsingCollector falsingCollector) {
         super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
-                messageAreaControllerFactory, latencyTracker, liftToActivateListener);
+                messageAreaControllerFactory, latencyTracker, liftToActivateListener,
+                falsingCollector);
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
     }
 
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
index 5b4a7ff..b218141 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java
@@ -38,6 +38,7 @@
 import com.android.internal.widget.LockPatternUtils;
 import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
 import com.android.systemui.R;
+import com.android.systemui.classifier.FalsingCollector;
 
 public class KeyguardSimPinViewController
         extends KeyguardPinBasedInputViewController<KeyguardSimPinView> {
@@ -76,11 +77,11 @@
             SecurityMode securityMode, LockPatternUtils lockPatternUtils,
             KeyguardSecurityCallback keyguardSecurityCallback,
             KeyguardMessageAreaController.Factory messageAreaControllerFactory,
-            LatencyTracker latencyTracker,
-            LiftToActivateListener liftToActivateListener,
-            TelephonyManager telephonyManager) {
+            LatencyTracker latencyTracker, LiftToActivateListener liftToActivateListener,
+            TelephonyManager telephonyManager, FalsingCollector falsingCollector) {
         super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
-                messageAreaControllerFactory, latencyTracker, liftToActivateListener);
+                messageAreaControllerFactory, latencyTracker, liftToActivateListener,
+                falsingCollector);
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
         mTelephonyManager = telephonyManager;
         mSimImageView = mView.findViewById(R.id.keyguard_sim);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
index eafb33f..890a17c 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java
@@ -39,6 +39,7 @@
 import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
 import com.android.systemui.Dependency;
 import com.android.systemui.R;
+import com.android.systemui.classifier.FalsingCollector;
 
 public class KeyguardSimPukViewController
         extends KeyguardPinBasedInputViewController<KeyguardSimPukView> {
@@ -83,11 +84,11 @@
             SecurityMode securityMode, LockPatternUtils lockPatternUtils,
             KeyguardSecurityCallback keyguardSecurityCallback,
             KeyguardMessageAreaController.Factory messageAreaControllerFactory,
-            LatencyTracker latencyTracker,
-            LiftToActivateListener liftToActivateListener,
-            TelephonyManager telephonyManager) {
+            LatencyTracker latencyTracker, LiftToActivateListener liftToActivateListener,
+            TelephonyManager telephonyManager, FalsingCollector falsingCollector) {
         super(view, keyguardUpdateMonitor, securityMode, lockPatternUtils, keyguardSecurityCallback,
-                messageAreaControllerFactory, latencyTracker, liftToActivateListener);
+                messageAreaControllerFactory, latencyTracker, liftToActivateListener,
+                falsingCollector);
         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
         mTelephonyManager = telephonyManager;
         mSimImageView = mView.findViewById(R.id.keyguard_sim);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
index 3cbab8e..fb97a30 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java
@@ -26,6 +26,7 @@
 import android.content.res.Resources;
 import android.graphics.Color;
 import android.graphics.drawable.Drawable;
+import android.graphics.drawable.InsetDrawable;
 import android.graphics.text.LineBreaker;
 import android.net.Uri;
 import android.os.Trace;
@@ -239,6 +240,12 @@
                 final int iconSize = mHasHeader ? mIconSizeWithHeader : mIconSize;
                 iconDrawable = icon.getIcon().loadDrawable(mContext);
                 if (iconDrawable != null) {
+                    if ((iconDrawable instanceof InsetDrawable)
+                            && mLockScreenMode == KeyguardUpdateMonitor.LOCK_SCREEN_MODE_LAYOUT_1) {
+                        // System icons (DnD) use insets which are fine for centered slice content
+                        // but will cause a slight indent for left/right-aligned slice views
+                        iconDrawable = ((InsetDrawable) iconDrawable).getDrawable();
+                    }
                     final int width = (int) (iconDrawable.getIntrinsicWidth()
                             / (float) iconDrawable.getIntrinsicHeight() * iconSize);
                     iconDrawable.setBounds(0, 0, Math.max(width, 1), iconSize);
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationGestureDetector.java b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationGestureDetector.java
new file mode 100644
index 0000000..4c892e29
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationGestureDetector.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.accessibility;
+
+import android.annotation.DisplayContext;
+import android.annotation.NonNull;
+import android.content.Context;
+import android.graphics.PointF;
+import android.os.Handler;
+import android.view.Display;
+import android.view.MotionEvent;
+import android.view.ViewConfiguration;
+
+/**
+ * Detects single tap and drag gestures using the supplied {@link MotionEvent}s. The {@link
+ * OnGestureListener} callback will notify users when a particular motion event has occurred. This
+ * class should only be used with {@link MotionEvent}s reported via touch (don't use for trackball
+ * events).
+ */
+class MagnificationGestureDetector {
+
+    interface OnGestureListener {
+        /**
+         * Called when a tap is completed within {@link ViewConfiguration#getLongPressTimeout()} and
+         * the offset between {@link MotionEvent}s and the down event doesn't exceed {@link
+         * ViewConfiguration#getScaledTouchSlop()}.
+         *
+         * @return {@code true} if this gesture is handled.
+         */
+        boolean onSingleTap();
+
+        /**
+         * Called when the user is performing dragging gesture. It is started after the offset
+         * between the down location and the move event location exceed
+         * {@link ViewConfiguration#getScaledTouchSlop()}.
+         *
+         * @param offsetX The X offset in screen coordinate.
+         * @param offsetY The Y offset in screen coordinate.
+         * @return {@code true} if this gesture is handled.
+         */
+        boolean onDrag(float offsetX, float offsetY);
+
+        /**
+         * Notified when a tap occurs with the down {@link MotionEvent} that triggered it. This will
+         * be triggered immediately for every down event. All other events should be preceded by
+         * this.
+         *
+         * @param x The X coordinate of the down event.
+         * @param y The Y coordinate of the down event.
+         * @return {@code true} if the down event is handled, otherwise the events won't be sent to
+         * the view.
+         */
+        boolean onStart(float x, float y);
+
+        /**
+         * Called when the detection is finished. In other words, it is called when up/cancel {@link
+         * MotionEvent} is received. It will be triggered after single-tap
+         *
+         * @param x The X coordinate on the screen of the up event or the cancel event.
+         * @param y The Y coordinate on the screen of the up event or the cancel event.
+         * @return {@code true} if the event is handled.
+         */
+        boolean onFinish(float x, float y);
+    }
+
+    private final PointF mPointerDown = new PointF();
+    private final PointF mPointerLocation = new PointF(Float.NaN, Float.NaN);
+    private final Handler mHandler;
+    private final Runnable mCancelTapGestureRunnable;
+    private final OnGestureListener mOnGestureListener;
+    private int mTouchSlopSquare;
+    // Assume the gesture default is a single-tap. Set it to false if the gesture couldn't be a
+    // single-tap anymore.
+    private boolean mDetectSingleTap = true;
+    private boolean mDraggingDetected = false;
+
+    /**
+     * @param context  {@link Context} that is from {@link Context#createDisplayContext(Display)}.
+     * @param handler  The handler to post the runnable.
+     * @param listener The listener invoked for all the callbacks.
+     */
+    MagnificationGestureDetector(@DisplayContext Context context, @NonNull Handler handler,
+            @NonNull OnGestureListener listener) {
+        final int touchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
+        mTouchSlopSquare = touchSlop * touchSlop;
+        mHandler = handler;
+        mOnGestureListener = listener;
+        mCancelTapGestureRunnable = () -> mDetectSingleTap = false;
+    }
+
+    /**
+     * Analyzes the given motion event and if applicable to trigger the appropriate callbacks on the
+     * {@link OnGestureListener} supplied.
+     *
+     * @param event The current motion event.
+     * @return {@code True} if the {@link OnGestureListener} consumes the event, else false.
+     */
+    boolean onTouch(MotionEvent event) {
+        final float rawX = event.getRawX();
+        final float rawY = event.getRawY();
+        boolean handled = false;
+        switch (event.getAction()) {
+            case MotionEvent.ACTION_DOWN:
+                mPointerDown.set(rawX, rawY);
+                mHandler.postAtTime(mCancelTapGestureRunnable,
+                        event.getDownTime() + ViewConfiguration.getLongPressTimeout());
+                handled |= mOnGestureListener.onStart(rawX, rawY);
+                break;
+            case MotionEvent.ACTION_POINTER_DOWN:
+                stopSingleTapDetection();
+                break;
+            case MotionEvent.ACTION_MOVE:
+                stopSingleTapDetectionIfNeeded(rawX, rawY);
+                handled |= notifyDraggingGestureIfNeeded(rawX, rawY);
+                break;
+            case MotionEvent.ACTION_UP:
+                stopSingleTapDetectionIfNeeded(rawX, rawY);
+                if (mDetectSingleTap) {
+                    handled |= mOnGestureListener.onSingleTap();
+                }
+                // Fall through
+            case MotionEvent.ACTION_CANCEL:
+                handled |= mOnGestureListener.onFinish(rawX, rawY);
+                reset();
+                break;
+        }
+        return handled;
+    }
+
+    private void stopSingleTapDetectionIfNeeded(float x, float y) {
+        if (mDraggingDetected) {
+            return;
+        }
+        if (!isLocationValid(mPointerDown)) {
+            return;
+        }
+
+        final int deltaX = (int) (mPointerDown.x - x);
+        final int deltaY = (int) (mPointerDown.y - y);
+        final int distanceSquare = (deltaX * deltaX) + (deltaY * deltaY);
+        if (distanceSquare > mTouchSlopSquare) {
+            mDraggingDetected = true;
+            stopSingleTapDetection();
+        }
+    }
+
+    private void stopSingleTapDetection() {
+        mHandler.removeCallbacks(mCancelTapGestureRunnable);
+        mDetectSingleTap = false;
+    }
+
+    private boolean notifyDraggingGestureIfNeeded(float x, float y) {
+        if (!mDraggingDetected) {
+            return false;
+        }
+        if (!isLocationValid(mPointerLocation)) {
+            mPointerLocation.set(mPointerDown);
+        }
+        final float offsetX = x - mPointerLocation.x;
+        final float offsetY = y - mPointerLocation.y;
+        mPointerLocation.set(x, y);
+        return mOnGestureListener.onDrag(offsetX, offsetY);
+    }
+
+    private void reset() {
+        resetPointF(mPointerDown);
+        resetPointF(mPointerLocation);
+        mHandler.removeCallbacks(mCancelTapGestureRunnable);
+        mDetectSingleTap = true;
+        mDraggingDetected = false;
+    }
+
+    private static void resetPointF(PointF pointF) {
+        pointF.x = Float.NaN;
+        pointF.y = Float.NaN;
+    }
+
+    private static boolean isLocationValid(PointF location) {
+        return !Float.isNaN(location.x) && !Float.isNaN(location.y);
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java
index edc3216..c1cf8d3 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/MagnificationModeSwitch.java
@@ -22,16 +22,13 @@
 import android.content.Context;
 import android.content.pm.ActivityInfo;
 import android.graphics.PixelFormat;
-import android.graphics.PointF;
 import android.graphics.Rect;
 import android.os.Bundle;
 import android.os.UserHandle;
 import android.provider.Settings;
-import android.util.MathUtils;
 import android.view.Gravity;
 import android.view.MotionEvent;
 import android.view.View;
-import android.view.ViewConfiguration;
 import android.view.WindowManager;
 import android.view.WindowManager.LayoutParams;
 import android.view.accessibility.AccessibilityManager;
@@ -46,11 +43,11 @@
 
 /**
  * Shows/hides a {@link android.widget.ImageView} on the screen and changes the values of
- * {@link Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE} when the UI is toggled.
+ * {@link Settings.Secure#ACCESSIBILITY_MAGNIFICATION_MODE} when the UI is toggled.
  * The button icon is movable by dragging. And the button UI would automatically be dismissed after
  * displaying for a period of time.
  */
-class MagnificationModeSwitch {
+class MagnificationModeSwitch implements MagnificationGestureDetector.OnGestureListener {
 
     @VisibleForTesting
     static final long FADING_ANIMATION_DURATION_MS = 300;
@@ -66,13 +63,11 @@
     private final AccessibilityManager mAccessibilityManager;
     private final WindowManager mWindowManager;
     private final ImageView mImageView;
-    private final PointF mLastDown = new PointF();
-    private final PointF mLastDrag = new PointF();
-    private final int mTapTimeout = ViewConfiguration.getTapTimeout();
-    private final int mTouchSlop;
     private int mMagnificationMode = Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN;
     private final LayoutParams mParams;
     private boolean mIsVisible = false;
+    private final MagnificationGestureDetector mGestureDetector;
+    private boolean mSingleTapDetected = false;
 
     MagnificationModeSwitch(Context context) {
         this(context, createView(context));
@@ -86,7 +81,6 @@
                 Context.WINDOW_SERVICE);
         mParams = createLayoutParams(context);
         mImageView = imageView;
-        mTouchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop();
         applyResourcesValues();
         mImageView.setOnTouchListener(this::onTouch);
         mImageView.setAccessibilityDelegate(new View.AccessibilityDelegate() {
@@ -127,6 +121,8 @@
                     .start();
             mIsFadeOutAnimating = true;
         };
+        mGestureDetector = new MagnificationGestureDetector(context,
+                context.getMainThreadHandler(), this);
     }
 
     private CharSequence formatStateDescription() {
@@ -144,39 +140,38 @@
     }
 
     private boolean onTouch(View v, MotionEvent event) {
-        if (!mIsVisible || mImageView == null) {
+        if (!mIsVisible) {
             return false;
         }
-        switch (event.getAction()) {
-            case MotionEvent.ACTION_DOWN:
-                stopFadeOutAnimation();
-                mLastDown.set(event.getRawX(), event.getRawY());
-                mLastDrag.set(event.getRawX(), event.getRawY());
-                return true;
-            case MotionEvent.ACTION_MOVE:
-                // Move the button position.
-                moveButton(event.getRawX() - mLastDrag.x,
-                        event.getRawY() - mLastDrag.y);
-                mLastDrag.set(event.getRawX(), event.getRawY());
-                return true;
-            case MotionEvent.ACTION_UP:
-                // Single tap to toggle magnification mode and the button position will be reset
-                // after the action is performed.
-                final float distance = MathUtils.dist(mLastDown.x, mLastDown.y,
-                        event.getRawX(), event.getRawY());
-                if ((event.getEventTime() - event.getDownTime()) <= mTapTimeout
-                        && distance <= mTouchSlop) {
-                    handleSingleTap();
-                } else {
-                    showButton(mMagnificationMode);
-                }
-                return true;
-            case MotionEvent.ACTION_CANCEL:
-                showButton(mMagnificationMode);
-                return true;
-            default:
-                return false;
+        return mGestureDetector.onTouch(event);
+    }
+
+    @Override
+    public boolean onSingleTap() {
+        mSingleTapDetected = true;
+        handleSingleTap();
+        return true;
+    }
+
+    @Override
+    public boolean onDrag(float offsetX, float offsetY) {
+        moveButton(offsetX, offsetY);
+        return true;
+    }
+
+    @Override
+    public boolean onStart(float x, float y) {
+        stopFadeOutAnimation();
+        return true;
+    }
+
+    @Override
+    public boolean onFinish(float xOffset, float yOffset) {
+        if (!mSingleTapDetected) {
+            showButton(mMagnificationMode);
         }
+        mSingleTapDetected = false;
+        return true;
     }
 
     private void moveButton(float offsetX, float offsetY) {
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
index 87dc6a5..3f7d2d8 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/WindowMagnificationController.java
@@ -20,6 +20,8 @@
 import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_2BUTTON;
 import static android.view.WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL;
 
+import android.animation.ObjectAnimator;
+import android.animation.PropertyValuesHolder;
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.content.Context;
@@ -28,7 +30,6 @@
 import android.graphics.Matrix;
 import android.graphics.PixelFormat;
 import android.graphics.Point;
-import android.graphics.PointF;
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.graphics.Region;
@@ -66,7 +67,7 @@
  * Class to handle adding and removing a window magnification.
  */
 class WindowMagnificationController implements View.OnTouchListener, SurfaceHolder.Callback,
-        MirrorWindowControl.MirrorWindowDelegate {
+        MirrorWindowControl.MirrorWindowDelegate, MagnificationGestureDetector.OnGestureListener {
 
     private static final String TAG = "WindowMagnificationController";
     // Delay to avoid updating state description too frequently.
@@ -74,6 +75,7 @@
     // It should be consistent with the value defined in WindowMagnificationGestureHandler.
     private static final Range<Float> A11Y_ACTION_SCALE_RANGE = new Range<>(2.0f, 8.0f);
     private static final float A11Y_CHANGE_SCALE_DIFFERENCE = 1.0f;
+    private static final float ANIMATION_BOUNCE_EFFECT_SCALE = 1.05f;
     private final Context mContext;
     private final Resources mResources;
     private final Handler mHandler;
@@ -102,7 +104,6 @@
     private View mRightDrag;
     private View mBottomDrag;
 
-    private final PointF mLastDrag = new PointF();
     @NonNull
     private final WindowMagnifierCallback mWindowMagnifierCallback;
 
@@ -123,9 +124,12 @@
     private int mNavGestureHeight;
 
     private final SfVsyncFrameCallbackProvider mSfVsyncFrameProvider;
+    private final MagnificationGestureDetector mGestureDetector;
+    private final int mBounceEffectDuration;
     private Choreographer.FrameCallback mMirrorViewGeometryVsyncCallback;
     private Locale mLocale;
     private NumberFormat mPercentFormat;
+    private float mBounceEffectAnimationScale;
 
     @Nullable
     private MirrorWindowControl mMirrorWindowControl;
@@ -147,14 +151,19 @@
 
         mResources = mContext.getResources();
         mScale = mResources.getInteger(R.integer.magnification_default_scale);
+        mBounceEffectDuration = mResources.getInteger(
+                com.android.internal.R.integer.config_shortAnimTime);
         updateDimensions();
+        setInitialStartBounds();
+        computeBounceAnimationScale();
 
         mMirrorWindowControl = mirrorWindowControl;
         if (mMirrorWindowControl != null) {
             mMirrorWindowControl.setWindowDelegate(this);
         }
         mTransaction = transaction;
-        setInitialStartBounds();
+        mGestureDetector =
+                new MagnificationGestureDetector(mContext, handler, this);
 
         // Initialize listeners.
         mMirrorViewRunnable = () -> {
@@ -203,6 +212,13 @@
         updateNavigationBarDimensions();
     }
 
+    private void computeBounceAnimationScale() {
+        final float windowWidth = mMagnificationFrame.width() + 2 * mMirrorSurfaceMargin;
+        final float visibleWindowWidth = windowWidth - 2 * mOuterBorderSize;
+        final float animationScaleMax = windowWidth / visibleWindowWidth;
+        mBounceEffectAnimationScale = Math.min(animationScaleMax, ANIMATION_BOUNCE_EFFECT_SCALE);
+    }
+
     private void updateNavigationBarDimensions() {
         if (!supportsSwipeUpGesture()) {
             mNavGestureHeight = 0;
@@ -248,6 +264,7 @@
     void onConfigurationChanged(int configDiff) {
         if ((configDiff & ActivityInfo.CONFIG_DENSITY) != 0) {
             updateDimensions();
+            computeBounceAnimationScale();
             if (isWindowVisible()) {
                 deleteWindowMagnification();
                 enableWindowMagnification(Float.NaN, Float.NaN, Float.NaN);
@@ -483,20 +500,7 @@
     public boolean onTouch(View v, MotionEvent event) {
         if (v == mDragView || v == mLeftDrag || v == mTopDrag || v == mRightDrag
                 || v == mBottomDrag) {
-            return handleDragTouchEvent(event);
-        }
-        return false;
-    }
-
-    private boolean handleDragTouchEvent(MotionEvent event) {
-        switch (event.getAction()) {
-            case MotionEvent.ACTION_DOWN:
-                mLastDrag.set(event.getRawX(), event.getRawY());
-                return true;
-            case MotionEvent.ACTION_MOVE:
-                moveWindowMagnifier(event.getRawX() - mLastDrag.x, event.getRawY() - mLastDrag.y);
-                mLastDrag.set(event.getRawX(), event.getRawY());
-                return true;
+            return mGestureDetector.onTouch(event);
         }
         return false;
     }
@@ -685,6 +689,36 @@
         return mPercentFormat.format(scale);
     }
 
+    @Override
+    public boolean onSingleTap() {
+        animateBounceEffect();
+        return true;
+    }
+
+    @Override
+    public boolean onDrag(float offsetX, float offsetY) {
+        moveWindowMagnifier(offsetX, offsetY);
+        return true;
+    }
+
+    @Override
+    public boolean onStart(float x, float y) {
+        return true;
+    }
+
+    @Override
+    public boolean onFinish(float x, float y) {
+        return false;
+    }
+
+    private void animateBounceEffect() {
+        final ObjectAnimator scaleAnimator = ObjectAnimator.ofPropertyValuesHolder(mMirrorView,
+                PropertyValuesHolder.ofFloat(View.SCALE_X, 1, mBounceEffectAnimationScale, 1),
+                PropertyValuesHolder.ofFloat(View.SCALE_Y, 1, mBounceEffectAnimationScale, 1));
+        scaleAnimator.setDuration(mBounceEffectDuration);
+        scaleAnimator.start();
+    }
+
     private class MirrorWindowA11yDelegate extends View.AccessibilityDelegate {
 
         @Override
diff --git a/packages/SystemUI/src/com/android/systemui/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/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/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index a991d3615..f9c7799 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);
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..da9aa97 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;
@@ -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/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/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/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/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 4e75a9e..1378776 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -3597,8 +3597,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 +8234,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 +8267,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 +8396,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/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 046d927..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
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/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/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/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..e9f79efb 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..0881cd2 100644
--- a/services/core/java/com/android/server/location/timezone/BinderLocationTimeZoneProvider.java
+++ b/services/core/java/com/android/server/location/timezone/BinderLocationTimeZoneProvider.java
@@ -162,11 +162,6 @@
     }
 
     @Override
-    void logWarn(String msg) {
-        Slog.w(TAG, msg);
-    }
-
-    @Override
     public void dump(@NonNull IndentingPrintWriter ipw, @Nullable String[] args) {
         synchronized (mSharedLock) {
             ipw.println("{BinderLocationTimeZoneProvider}");
diff --git a/services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerService.java b/services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerService.java
index ab64f97..a23b9d7 100644
--- a/services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerService.java
+++ b/services/core/java/com/android/server/location/timezone/LocationTimeZoneManagerService.java
@@ -285,8 +285,12 @@
     }
 
     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);
         }
     }
 }
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..e55d1cc 100644
--- a/services/core/java/com/android/server/location/timezone/LocationTimeZoneProvider.java
+++ b/services/core/java/com/android/server/location/timezone/LocationTimeZoneProvider.java
@@ -17,6 +17,7 @@
 package com.android.server.location.timezone;
 
 import static com.android.server.location.timezone.LocationTimeZoneManagerService.debugLog;
+import static com.android.server.location.timezone.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;
@@ -345,12 +346,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);
+            }
         }
     }
 
@@ -498,7 +508,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 +519,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 +532,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 +554,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 +594,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..16f9e97 100644
--- a/services/core/java/com/android/server/location/timezone/LocationTimeZoneProviderProxy.java
+++ b/services/core/java/com/android/server/location/timezone/LocationTimeZoneProviderProxy.java
@@ -79,8 +79,8 @@
                 throw new IllegalStateException("listener already set");
             }
             this.mListener = listener;
+            onInitialize();
         }
-        onInitialize();
     }
 
     /**
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/RealLocationTimeZoneProviderProxy.java b/services/core/java/com/android/server/location/timezone/RealLocationTimeZoneProviderProxy.java
index 6cc148a..231136bc 100644
--- a/services/core/java/com/android/server/location/timezone/RealLocationTimeZoneProviderProxy.java
+++ b/services/core/java/com/android/server/location/timezone/RealLocationTimeZoneProviderProxy.java
@@ -16,10 +16,18 @@
 
 package com.android.server.location.timezone;
 
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+
+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.Handler;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -32,6 +40,7 @@
 import com.android.server.ServiceWatcher;
 
 import java.util.Objects;
+import java.util.function.Predicate;
 
 /**
  * System server-side proxy for ITimeZoneProvider implementations, i.e. this provides the
@@ -57,8 +66,38 @@
         super(context, threadingDomain);
         mManagerProxy = null;
         mRequest = TimeZoneProviderRequest.createStopUpdatesRequest();
+
+        // A predicate that is used to confirm that an intent service can be used as a
+        // location-based TimeZoneProvider. The service must:
+        // 1) Declare android:permission="android.permission.BIND_TIME_ZONE_PROVIDER_SERVICE" - this
+        //    ensures that the provider will only communicate with the system server.
+        // 2) Be in an application that has been granted the
+        //    android.permission.INSTALL_LOCATION_TIME_ZONE_PROVIDER_SERVICE permission. This
+        //    ensures only trusted time zone providers will be discovered.
+        final String requiredClientPermission = Manifest.permission.BIND_TIME_ZONE_PROVIDER_SERVICE;
+        final String requiredPermission =
+                Manifest.permission.INSTALL_LOCATION_TIME_ZONE_PROVIDER_SERVICE;
+        Predicate<ResolveInfo> intentServiceCheckPredicate = resolveInfo -> {
+            ServiceInfo serviceInfo = resolveInfo.serviceInfo;
+
+            boolean hasClientPermissionRequirement =
+                    requiredClientPermission.equals(serviceInfo.permission);
+
+            String packageName = serviceInfo.packageName;
+            PackageManager packageManager = context.getPackageManager();
+            int checkResult = packageManager.checkPermission(requiredPermission, packageName);
+            boolean hasRequiredPermission = checkResult == PERMISSION_GRANTED;
+
+            boolean result = hasClientPermissionRequirement && hasRequiredPermission;
+            if (!result) {
+                warnLog("resolveInfo=" + resolveInfo + " does not meet requirements:"
+                        + " hasClientPermissionRequirement=" + hasClientPermissionRequirement
+                        + ", hasRequiredPermission=" + hasRequiredPermission);
+            }
+            return result;
+        };
         mServiceWatcher = new ServiceWatcher(context, handler, action, this::onBind, this::onUnbind,
-                enableOverlayResId, nonOverlayPackageResId);
+                enableOverlayResId, nonOverlayPackageResId, intentServiceCheckPredicate);
     }
 
     @Override
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/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..cd5ea46 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) {
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/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/Session.java b/services/core/java/com/android/server/wm/Session.java
index 57d48c6..0cefa95 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -61,7 +61,6 @@
 import android.util.ArraySet;
 import android.util.MergedConfiguration;
 import android.util.Slog;
-import android.view.DisplayCutout;
 import android.view.IWindow;
 import android.view.IWindowId;
 import android.view.IWindowSession;
@@ -185,22 +184,21 @@
     @Override
     public int addToDisplay(IWindow window, WindowManager.LayoutParams attrs,
             int viewVisibility, int displayId, InsetsState requestedVisibility, Rect outFrame,
-            DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
-            InsetsState outInsetsState, InsetsSourceControl[] outActiveControls) {
+            InputChannel outInputChannel, InsetsState outInsetsState,
+            InsetsSourceControl[] outActiveControls) {
         return mService.addWindow(this, window, attrs, viewVisibility, displayId,
-                UserHandle.getUserId(mUid), requestedVisibility, outFrame, outDisplayCutout,
-                outInputChannel, outInsetsState, outActiveControls);
+                UserHandle.getUserId(mUid), requestedVisibility, outFrame, outInputChannel,
+                outInsetsState, outActiveControls);
     }
 
 
     @Override
     public int addToDisplayAsUser(IWindow window, WindowManager.LayoutParams attrs,
             int viewVisibility, int displayId, int userId, InsetsState requestedVisibility,
-            Rect outFrame, DisplayCutout.ParcelableWrapper outDisplayCutout,
-            InputChannel outInputChannel, InsetsState outInsetsState,
+            Rect outFrame, InputChannel outInputChannel, InsetsState outInsetsState,
             InsetsSourceControl[] outActiveControls) {
         return mService.addWindow(this, window, attrs, viewVisibility, displayId, userId,
-                requestedVisibility, outFrame, outDisplayCutout, outInputChannel, outInsetsState,
+                requestedVisibility, outFrame, outInputChannel, outInsetsState,
                 outActiveControls);
     }
 
@@ -209,8 +207,8 @@
             int viewVisibility, int displayId, InsetsState outInsetsState) {
         return mService.addWindow(this, window, attrs, viewVisibility, displayId,
                 UserHandle.getUserId(mUid), mDummyRequestedVisibility,
-                new Rect() /* outFrame */, new DisplayCutout.ParcelableWrapper() /* cutout */,
-                null /* outInputChannel */, outInsetsState, mDummyControls);
+                new Rect() /* outFrame */, null /* outInputChannel */, outInsetsState,
+                mDummyControls);
     }
 
     @Override
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 2290c23..81cbd61 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -1926,8 +1926,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);
             }
@@ -6247,9 +6248,9 @@
         }
 
         if (anim) {
-            next.applyOptionsLocked();
+            next.applyOptionsAnimation();
         } else {
-            next.clearOptionsLocked();
+            next.abortAndClearOptionsAnimation();
         }
 
         mTaskSupervisor.mNoAnimActivities.clear();
@@ -6356,7 +6357,7 @@
 
                 mAtmService.getAppWarningsLocked().onResumeActivity(next);
                 next.app.setPendingUiCleanAndForceProcessStateUpTo(mAtmService.mTopProcessState);
-                next.clearOptionsLocked();
+                next.abortAndClearOptionsAnimation();
                 transaction.setLifecycleStateRequest(
                         ResumeActivityItem.obtain(next.app.getReportedProcState(),
                                 dc.isNextTransitionForward()));
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/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..292d7ea 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;
@@ -690,9 +688,6 @@
 
     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_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/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/servicestests/src/com/android/internal/location/timezone/LocationTimeZoneEventTest.java b/services/tests/servicestests/src/com/android/internal/location/timezone/LocationTimeZoneEventTest.java
deleted file mode 100644
index 84b886e..0000000
--- a/services/tests/servicestests/src/com/android/internal/location/timezone/LocationTimeZoneEventTest.java
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.location.timezone;
-
-import static com.android.internal.location.timezone.ParcelableTestSupport.assertRoundTripParcelable;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
-
-import static java.util.Collections.singletonList;
-
-import org.junit.Test;
-
-import java.util.List;
-
-public class LocationTimeZoneEventTest {
-
-    private static final long ARBITRARY_ELAPSED_REALTIME_MILLIS = 9999;
-
-    private static final List<String> ARBITRARY_TIME_ZONE_IDS = singletonList("Europe/London");
-
-    @Test(expected = RuntimeException.class)
-    public void testSetInvalidEventType() {
-        new LocationTimeZoneEvent.Builder().setEventType(Integer.MAX_VALUE);
-    }
-
-    @Test(expected = RuntimeException.class)
-    public void testBuildUnsetEventType() {
-        new LocationTimeZoneEvent.Builder()
-                .setTimeZoneIds(ARBITRARY_TIME_ZONE_IDS)
-                .setElapsedRealtimeMillis(ARBITRARY_ELAPSED_REALTIME_MILLIS)
-                .build();
-    }
-
-    @Test(expected = RuntimeException.class)
-    public void testInvalidTimeZoneIds() {
-        new LocationTimeZoneEvent.Builder()
-                .setEventType(LocationTimeZoneEvent.EVENT_TYPE_UNCERTAIN)
-                .setTimeZoneIds(ARBITRARY_TIME_ZONE_IDS)
-                .setElapsedRealtimeMillis(ARBITRARY_ELAPSED_REALTIME_MILLIS)
-                .build();
-    }
-
-    @Test
-    public void testEquals() {
-        LocationTimeZoneEvent.Builder builder1 = new LocationTimeZoneEvent.Builder()
-                .setEventType(LocationTimeZoneEvent.EVENT_TYPE_UNCERTAIN)
-                .setElapsedRealtimeMillis(ARBITRARY_ELAPSED_REALTIME_MILLIS);
-        {
-            LocationTimeZoneEvent one = builder1.build();
-            assertEquals(one, one);
-        }
-
-        LocationTimeZoneEvent.Builder builder2 = new LocationTimeZoneEvent.Builder()
-                .setEventType(LocationTimeZoneEvent.EVENT_TYPE_UNCERTAIN)
-                .setElapsedRealtimeMillis(ARBITRARY_ELAPSED_REALTIME_MILLIS);
-        {
-            LocationTimeZoneEvent one = builder1.build();
-            LocationTimeZoneEvent two = builder2.build();
-            assertEquals(one, two);
-            assertEquals(two, one);
-        }
-
-        builder1.setElapsedRealtimeMillis(ARBITRARY_ELAPSED_REALTIME_MILLIS + 1);
-        {
-            LocationTimeZoneEvent one = builder1.build();
-            LocationTimeZoneEvent two = builder2.build();
-            assertNotEquals(one, two);
-            assertNotEquals(two, one);
-        }
-
-        builder2.setElapsedRealtimeMillis(ARBITRARY_ELAPSED_REALTIME_MILLIS + 1);
-        {
-            LocationTimeZoneEvent one = builder1.build();
-            LocationTimeZoneEvent two = builder2.build();
-            assertEquals(one, two);
-            assertEquals(two, one);
-        }
-
-        builder2.setEventType(LocationTimeZoneEvent.EVENT_TYPE_SUCCESS);
-        {
-            LocationTimeZoneEvent one = builder1.build();
-            LocationTimeZoneEvent two = builder2.build();
-            assertNotEquals(one, two);
-            assertNotEquals(two, one);
-        }
-
-        builder1.setEventType(LocationTimeZoneEvent.EVENT_TYPE_SUCCESS);
-        {
-            LocationTimeZoneEvent one = builder1.build();
-            LocationTimeZoneEvent two = builder2.build();
-            assertEquals(one, two);
-            assertEquals(two, one);
-        }
-
-        builder2.setTimeZoneIds(ARBITRARY_TIME_ZONE_IDS);
-        {
-            LocationTimeZoneEvent one = builder1.build();
-            LocationTimeZoneEvent two = builder2.build();
-            assertNotEquals(one, two);
-            assertNotEquals(two, one);
-        }
-
-        builder1.setTimeZoneIds(ARBITRARY_TIME_ZONE_IDS);
-        {
-            LocationTimeZoneEvent one = builder1.build();
-            LocationTimeZoneEvent two = builder2.build();
-            assertEquals(one, two);
-            assertEquals(two, one);
-        }
-    }
-
-    @Test
-    public void testParcelable() {
-        LocationTimeZoneEvent.Builder builder = new LocationTimeZoneEvent.Builder()
-                .setEventType(LocationTimeZoneEvent.EVENT_TYPE_PERMANENT_FAILURE)
-                .setElapsedRealtimeMillis(ARBITRARY_ELAPSED_REALTIME_MILLIS);
-        assertRoundTripParcelable(builder.build());
-
-        builder.setEventType(LocationTimeZoneEvent.EVENT_TYPE_SUCCESS)
-                .setTimeZoneIds(ARBITRARY_TIME_ZONE_IDS);
-        assertRoundTripParcelable(builder.build());
-    }
-}
diff --git a/services/tests/servicestests/src/com/android/internal/location/timezone/LocationTimeZoneProviderRequestTest.java b/services/tests/servicestests/src/com/android/internal/location/timezone/LocationTimeZoneProviderRequestTest.java
deleted file mode 100644
index 95daa36..0000000
--- a/services/tests/servicestests/src/com/android/internal/location/timezone/LocationTimeZoneProviderRequestTest.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.location.timezone;
-
-import static com.android.internal.location.timezone.ParcelableTestSupport.assertRoundTripParcelable;
-
-import org.junit.Test;
-
-import java.time.Duration;
-
-public class LocationTimeZoneProviderRequestTest {
-
-    @Test
-    public void testParcelable() {
-        LocationTimeZoneProviderRequest.Builder builder =
-                new LocationTimeZoneProviderRequest.Builder()
-                        .setReportLocationTimeZone(false);
-        assertRoundTripParcelable(builder.build());
-
-        builder.setReportLocationTimeZone(true)
-                .setInitializationTimeoutMillis(Duration.ofMinutes(5).toMillis());
-
-        assertRoundTripParcelable(builder.build());
-    }
-}
diff --git a/services/tests/servicestests/src/com/android/internal/location/timezone/ParcelableTestSupport.java b/services/tests/servicestests/src/com/android/internal/location/timezone/ParcelableTestSupport.java
deleted file mode 100644
index ece5d00..0000000
--- a/services/tests/servicestests/src/com/android/internal/location/timezone/ParcelableTestSupport.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.internal.location.timezone;
-
-import static org.junit.Assert.assertEquals;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.lang.reflect.Field;
-
-/** Utility methods related to {@link Parcelable} objects used in several tests. */
-final class ParcelableTestSupport {
-
-    private ParcelableTestSupport() {}
-
-    /** Returns the result of parceling and unparceling the argument. */
-    @SuppressWarnings("unchecked")
-    public static <T extends Parcelable> T roundTripParcelable(T parcelable) {
-        Parcel parcel = Parcel.obtain();
-        parcel.writeTypedObject(parcelable, 0);
-        parcel.setDataPosition(0);
-
-        Parcelable.Creator<T> creator;
-        try {
-            Field creatorField = parcelable.getClass().getField("CREATOR");
-            creator = (Parcelable.Creator<T>) creatorField.get(null);
-        } catch (NoSuchFieldException | IllegalAccessException e) {
-            throw new AssertionError(e);
-        }
-        T toReturn = parcel.readTypedObject(creator);
-        parcel.recycle();
-        return toReturn;
-    }
-
-    public static <T extends Parcelable> void assertRoundTripParcelable(T instance) {
-        assertEquals(instance, roundTripParcelable(instance));
-    }
-}
diff --git a/services/tests/servicestests/src/com/android/server/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/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/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/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/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..93ef126b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RecentTasksTest.java
@@ -1006,8 +1006,6 @@
         assertNotRestoreTask(
                 () -> mAtm.setTaskWindowingMode(taskId, WINDOWING_MODE_FULLSCREEN,
                         false/* toTop */));
-        assertNotRestoreTask(
-                () -> mAtm.setTaskWindowingModeSplitScreenPrimary(taskId, false /* toTop */));
     }
 
     @Test
@@ -1144,8 +1142,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/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/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/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/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.