Merge "Add metric constants for foldable screen"
diff --git a/Android.bp b/Android.bp
index 2c5fc4e..0210bb3 100644
--- a/Android.bp
+++ b/Android.bp
@@ -451,6 +451,7 @@
         "location/java/android/location/IGpsGeofenceHardware.aidl",
         "location/java/android/location/INetInitiatedListener.aidl",
         "location/java/com/android/internal/location/ILocationProvider.aidl",
+        "location/java/com/android/internal/location/ILocationProviderManager.aidl",
         "media/java/android/media/IAudioFocusDispatcher.aidl",
         "media/java/android/media/IAudioRoutesObserver.aidl",
         "media/java/android/media/IAudioService.aidl",
diff --git a/api/current.txt b/api/current.txt
index 1b6229e..931b9fa 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -22741,8 +22741,8 @@
     method public boolean addNmeaListener(android.location.OnNmeaMessageListener, android.os.Handler);
     method public void addProximityAlert(double, double, float, long, android.app.PendingIntent);
     method public void addTestProvider(java.lang.String, boolean, boolean, boolean, boolean, boolean, boolean, boolean, int, int);
-    method public void clearTestProviderEnabled(java.lang.String);
-    method public void clearTestProviderLocation(java.lang.String);
+    method public deprecated void clearTestProviderEnabled(java.lang.String);
+    method public deprecated void clearTestProviderLocation(java.lang.String);
     method public deprecated void clearTestProviderStatus(java.lang.String);
     method public java.util.List<java.lang.String> getAllProviders();
     method public java.lang.String getBestProvider(android.location.Criteria, boolean);
@@ -22954,6 +22954,7 @@
     method public int getChannelIndexMask();
     method public int getChannelMask();
     method public int getEncoding();
+    method public int getFrameSizeInBytes();
     method public int getSampleRate();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final deprecated int CHANNEL_CONFIGURATION_DEFAULT = 1; // 0x1
@@ -40467,6 +40468,7 @@
     method public android.service.autofill.FillResponse.Builder setHeader(android.widget.RemoteViews);
     method public android.service.autofill.FillResponse.Builder setIgnoredIds(android.view.autofill.AutofillId...);
     method public android.service.autofill.FillResponse.Builder setSaveInfo(android.service.autofill.SaveInfo);
+    method public android.service.autofill.FillResponse.Builder setUserData(android.service.autofill.UserData);
   }
 
   public final class ImageTransformation implements android.os.Parcelable android.service.autofill.Transformation {
@@ -43785,10 +43787,10 @@
     method public static java.lang.String getStrippedReversed(java.lang.String);
     method public static final boolean is12Key(char);
     method public static final boolean isDialable(char);
-    method public static boolean isEmergencyNumber(java.lang.String);
+    method public static deprecated boolean isEmergencyNumber(java.lang.String);
     method public static boolean isGlobalPhoneNumber(java.lang.String);
     method public static boolean isISODigit(char);
-    method public static boolean isLocalEmergencyNumber(android.content.Context, java.lang.String);
+    method public static deprecated boolean isLocalEmergencyNumber(android.content.Context, java.lang.String);
     method public static final boolean isNonSeparator(char);
     method public static final boolean isReallyDialable(char);
     method public static final boolean isStartsPostDial(char);
@@ -44496,11 +44498,13 @@
     method public java.util.List<java.lang.Integer> getEmergencyNumberSources();
     method public java.util.List<java.lang.Integer> getEmergencyServiceCategories();
     method public int getEmergencyServiceCategoryBitmask();
+    method public java.lang.String getMnc();
     method public java.lang.String getNumber();
     method public boolean isFromSources(int);
     method public boolean isInEmergencyServiceCategories(int);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.telephony.emergency.EmergencyNumber> CREATOR;
+    field public static final int EMERGENCY_NUMBER_SOURCE_DATABASE = 16; // 0x10
     field public static final int EMERGENCY_NUMBER_SOURCE_DEFAULT = 8; // 0x8
     field public static final int EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG = 4; // 0x4
     field public static final int EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING = 1; // 0x1
@@ -52483,6 +52487,7 @@
 
   public static final class ConversationActions.Request implements android.os.Parcelable {
     method public int describeContents();
+    method public java.lang.String getCallingPackageName();
     method public java.util.List<android.view.textclassifier.ConversationActions.Message> getConversation();
     method public java.util.List<java.lang.String> getHints();
     method public int getMaxSuggestions();
@@ -52596,6 +52601,7 @@
 
   public static final class TextClassification.Request implements android.os.Parcelable {
     method public int describeContents();
+    method public java.lang.String getCallingPackageName();
     method public android.os.LocaleList getDefaultLocales();
     method public int getEndIndex();
     method public android.os.Bundle getExtras();
@@ -52713,6 +52719,7 @@
 
   public static final class TextLanguage.Request implements android.os.Parcelable {
     method public int describeContents();
+    method public java.lang.String getCallingPackageName();
     method public android.os.Bundle getExtras();
     method public java.lang.CharSequence getText();
     method public void writeToParcel(android.os.Parcel, int);
@@ -52744,6 +52751,7 @@
   public static final class TextLinks.Builder {
     ctor public TextLinks.Builder(java.lang.String);
     method public android.view.textclassifier.TextLinks.Builder addLink(int, int, java.util.Map<java.lang.String, java.lang.Float>);
+    method public android.view.textclassifier.TextLinks.Builder addLink(int, int, java.util.Map<java.lang.String, java.lang.Float>, android.os.Bundle);
     method public android.view.textclassifier.TextLinks build();
     method public android.view.textclassifier.TextLinks.Builder clearTextLinks();
     method public android.view.textclassifier.TextLinks.Builder setExtras(android.os.Bundle);
@@ -52751,6 +52759,7 @@
 
   public static final class TextLinks.Request implements android.os.Parcelable {
     method public int describeContents();
+    method public java.lang.String getCallingPackageName();
     method public android.os.LocaleList getDefaultLocales();
     method public android.view.textclassifier.TextClassifier.EntityConfig getEntityConfig();
     method public android.os.Bundle getExtras();
@@ -52773,6 +52782,7 @@
     method public int getEnd();
     method public java.lang.String getEntity(int);
     method public int getEntityCount();
+    method public android.os.Bundle getExtras();
     method public int getStart();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator<android.view.textclassifier.TextLinks.TextLink> CREATOR;
@@ -52807,6 +52817,7 @@
 
   public static final class TextSelection.Request implements android.os.Parcelable {
     method public int describeContents();
+    method public java.lang.String getCallingPackageName();
     method public android.os.LocaleList getDefaultLocales();
     method public int getEndIndex();
     method public android.os.Bundle getExtras();
diff --git a/api/system-current.txt b/api/system-current.txt
index 7d30529..29089b3 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -7,6 +7,7 @@
     field public static final java.lang.String ACCESS_CHECKIN_PROPERTIES = "android.permission.ACCESS_CHECKIN_PROPERTIES";
     field public static final java.lang.String ACCESS_DRM_CERTIFICATES = "android.permission.ACCESS_DRM_CERTIFICATES";
     field public static final deprecated java.lang.String ACCESS_FM_RADIO = "android.permission.ACCESS_FM_RADIO";
+    field public static final java.lang.String ACCESS_INSTANT_APPS = "android.permission.ACCESS_INSTANT_APPS";
     field public static final java.lang.String ACCESS_MOCK_LOCATION = "android.permission.ACCESS_MOCK_LOCATION";
     field public static final java.lang.String ACCESS_MTP = "android.permission.ACCESS_MTP";
     field public static final java.lang.String ACCESS_NETWORK_CONDITIONS = "android.permission.ACCESS_NETWORK_CONDITIONS";
@@ -315,7 +316,6 @@
     method public android.app.AppOpsManager.HistoricalPackageOps getHistoricalPackagesOps(int, java.lang.String, java.lang.String[], long, long);
     method public static java.lang.String[] getOpStrs();
     method public java.util.List<android.app.AppOpsManager.PackageOps> getOpsForPackage(int, java.lang.String, int[]);
-    method public java.util.List<android.app.AppOpsManager.PackageOps> getPackagesForOpStrs(java.lang.String[]);
     method public static int opToDefaultMode(java.lang.String);
     method public static java.lang.String opToPermission(java.lang.String);
     method public void setMode(java.lang.String, int, java.lang.String, int);
@@ -1253,6 +1253,7 @@
     method public abstract java.util.List<android.content.pm.IntentFilterVerificationInfo> getIntentFilterVerifications(java.lang.String);
     method public abstract int getIntentVerificationStatusAsUser(java.lang.String, int);
     method public abstract int getPermissionFlags(java.lang.String, java.lang.String, android.os.UserHandle);
+    method public java.lang.String getWellbeingPackageName();
     method public abstract void grantRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
     method public abstract int installExistingPackage(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException;
     method public abstract int installExistingPackage(java.lang.String, int) throws android.content.pm.PackageManager.NameNotFoundException;
@@ -1355,6 +1356,7 @@
     field public static final int FLAG_REMOVED = 2; // 0x2
     field public static final int PROTECTION_FLAG_OEM = 16384; // 0x4000
     field public static final int PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER = 65536; // 0x10000
+    field public static final int PROTECTION_FLAG_WELLBEING = 131072; // 0x20000
     field public java.lang.String backgroundPermission;
     field public int requestRes;
   }
@@ -5968,6 +5970,7 @@
     method public int getVoiceActivationState();
     method public boolean handlePinMmi(java.lang.String);
     method public boolean handlePinMmiForSubscriber(int, java.lang.String);
+    method public boolean isCurrentPotentialEmergencyNumber(java.lang.String);
     method public boolean isDataConnectivityPossible();
     method public deprecated boolean isIdle();
     method public deprecated boolean isOffhook();
diff --git a/api/test-current.txt b/api/test-current.txt
index dff3f28..5bedc72 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -374,6 +374,7 @@
   public class PermissionInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable {
     field public static final int PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER = 65536; // 0x10000
     field public static final int PROTECTION_FLAG_VENDOR_PRIVILEGED = 32768; // 0x8000
+    field public static final int PROTECTION_FLAG_WELLBEING = 131072; // 0x20000
     field public java.lang.String backgroundPermission;
   }
 
diff --git a/config/hiddenapi-greylist.txt b/config/hiddenapi-greylist.txt
index 01c7028..56dc4c1 100644
--- a/config/hiddenapi-greylist.txt
+++ b/config/hiddenapi-greylist.txt
@@ -1849,13 +1849,16 @@
 Lcom/android/internal/location/GpsNetInitiatedHandler;->mIsHexInput:Z
 Lcom/android/internal/location/ILocationProvider$Stub;-><init>()V
 Lcom/android/internal/location/ILocationProvider$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/location/ILocationProvider;
-Lcom/android/internal/location/ILocationProvider;->disable()V
-Lcom/android/internal/location/ILocationProvider;->enable()V
-Lcom/android/internal/location/ILocationProvider;->getProperties()Lcom/android/internal/location/ProviderProperties;
 Lcom/android/internal/location/ILocationProvider;->getStatus(Landroid/os/Bundle;)I
 Lcom/android/internal/location/ILocationProvider;->getStatusUpdateTime()J
-Lcom/android/internal/location/ILocationProvider;->sendExtraCommand(Ljava/lang/String;Landroid/os/Bundle;)Z
+Lcom/android/internal/location/ILocationProvider;->sendExtraCommand(Ljava/lang/String;Landroid/os/Bundle;)V
+Lcom/android/internal/location/ILocationProvider;->setLocationProviderManager(Lcom/android/internal/location/ILocationProviderManager;)V
 Lcom/android/internal/location/ILocationProvider;->setRequest(Lcom/android/internal/location/ProviderRequest;Landroid/os/WorkSource;)V
+Lcom/android/internal/location/ILocationProviderManager$Stub;-><init>()V
+Lcom/android/internal/location/ILocationProviderManager$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/location/ILocationProviderManager;
+Lcom/android/internal/location/ILocationProviderManager;->onReportLocation(Landroid/location/Location;)V
+Lcom/android/internal/location/ILocationProviderManager;->onSetEnabled(Z)V
+Lcom/android/internal/location/ILocationProviderManager;->onSetProperties(Lcom/android/internal/location/ProviderProperties;)V
 Lcom/android/internal/logging/MetricsLogger;-><init>()V
 Lcom/android/internal/net/LegacyVpnInfo;-><init>()V
 Lcom/android/internal/net/VpnConfig;-><init>()V
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index f2fb33f..6905cb5 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -2502,30 +2502,6 @@
     }
 
     /**
-     * Retrieve current operation state for all applications.
-     *
-     * @param ops The set of operations you are interested in, or null if you want all of them.
-     * @hide
-     */
-    @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS)
-    @SystemApi
-    public List<AppOpsManager.PackageOps> getPackagesForOpStrs(String[] ops) {
-        if (ops == null) {
-            return getPackagesForOps(null);
-        }
-        final int[] opCodes = new int[ops.length];
-        for (int i = 0; i < ops.length; ++i) {
-            final Integer opCode = sOpStrToOp.get(ops[i]);
-            if (opCode == null) {
-                opCodes[i] = OP_NONE;
-            } else {
-                opCodes[i] = opCode;
-            }
-        }
-        return getPackagesForOps(opCodes);
-    }
-
-    /**
      * Retrieve current operation state for one application.
      *
      * @param uid The uid of the application of interest.
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 7312b2c..67d9ad6e 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -2974,6 +2974,15 @@
     }
 
     @Override
+    public String getWellbeingPackageName() {
+        try {
+            return mPM.getWellbeingPackageName();
+        } catch (RemoteException e) {
+            throw e.rethrowAsRuntimeException();
+        }
+    }
+
+    @Override
     public boolean isPackageStateProtected(String packageName, int userId) {
         try {
             return mPM.isPackageStateProtected(packageName, userId);
diff --git a/core/java/android/app/UiAutomationConnection.java b/core/java/android/app/UiAutomationConnection.java
index dc2f983..31521a3 100644
--- a/core/java/android/app/UiAutomationConnection.java
+++ b/core/java/android/app/UiAutomationConnection.java
@@ -331,7 +331,7 @@
                     writeTo.flush();
                 }
             } catch (IOException ioe) {
-                throw new RuntimeException("Error while reading/writing ", ioe);
+                Log.w(TAG, "Error while reading/writing to streams");
             } finally {
                 IoUtils.closeQuietly(readFrom);
                 IoUtils.closeQuietly(writeTo);
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index dbea821..eea2b88 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -675,6 +675,8 @@
 
     String getSystemTextClassifierPackageName();
 
+    String getWellbeingPackageName();
+
     boolean isPackageStateProtected(String packageName, int userId);
 
     void sendDeviceCustomizationReadyBroadcast();
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index f40be84..0150f6a 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -6434,6 +6434,17 @@
     }
 
     /**
+     * @return the wellbeing app package name, or null if it's not defined by the OEM.
+     *
+     * @hide
+     */
+    @SystemApi
+    public String getWellbeingPackageName() {
+        throw new UnsupportedOperationException(
+                "getWellbeingPackageName not implemented in subclass");
+    }
+
+    /**
      * @return whether a given package's state is protected, e.g. package cannot be disabled,
      *         suspended, hidden or force stopped.
      *
diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java
index b49c447..43c0222 100644
--- a/core/java/android/content/pm/PackageManagerInternal.java
+++ b/core/java/android/content/pm/PackageManagerInternal.java
@@ -53,6 +53,7 @@
     public static final int PACKAGE_BROWSER = 4;
     public static final int PACKAGE_SYSTEM_TEXT_CLASSIFIER = 5;
     public static final int PACKAGE_PERMISSION_CONTROLLER = 6;
+    public static final int PACKAGE_WELLBEING = 7;
     @IntDef(value = {
         PACKAGE_SYSTEM,
         PACKAGE_SETUP_WIZARD,
@@ -61,6 +62,7 @@
         PACKAGE_BROWSER,
         PACKAGE_SYSTEM_TEXT_CLASSIFIER,
         PACKAGE_PERMISSION_CONTROLLER,
+        PACKAGE_WELLBEING,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface KnownPackage {}
diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java
index d9d6b5f..20997d6 100644
--- a/core/java/android/content/pm/PermissionInfo.java
+++ b/core/java/android/content/pm/PermissionInfo.java
@@ -181,6 +181,17 @@
     @TestApi
     public static final int PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER = 0x10000;
 
+    /**
+     * Additional flag for {${link #protectionLevel}, corresponding
+     * to the <code>wellbeing</code> value of
+     * {@link android.R.attr#protectionLevel}.
+     *
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    public static final int PROTECTION_FLAG_WELLBEING = 0x20000;
+
     /** @hide */
     @IntDef(flag = true, prefix = { "PROTECTION_FLAG_" }, value = {
             PROTECTION_FLAG_PRIVILEGED,
@@ -197,6 +208,7 @@
             PROTECTION_FLAG_OEM,
             PROTECTION_FLAG_VENDOR_PRIVILEGED,
             PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER,
+            PROTECTION_FLAG_WELLBEING,
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface ProtectionFlags {}
@@ -386,6 +398,9 @@
         if ((level & PermissionInfo.PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER) != 0) {
             protLevel += "|textClassifier";
         }
+        if ((level & PermissionInfo.PROTECTION_FLAG_WELLBEING) != 0) {
+            protLevel += "|wellbeing";
+        }
         return protLevel;
     }
 
diff --git a/core/java/android/service/autofill/AutofillFieldClassificationService.java b/core/java/android/service/autofill/AutofillFieldClassificationService.java
index 1cd76d2..bd3c3d3 100644
--- a/core/java/android/service/autofill/AutofillFieldClassificationService.java
+++ b/core/java/android/service/autofill/AutofillFieldClassificationService.java
@@ -175,7 +175,7 @@
     public float[][] onGetScores(@Nullable String algorithm,
             @Nullable Bundle algorithmOptions, @NonNull List<AutofillValue> actualValues,
             @NonNull List<String> userDataValues) {
-        Log.e(TAG, "service implementation (" + getClass() + " does not implement onGetScore()");
+        Log.e(TAG, "service implementation (" + getClass() + " does not implement onGetScores()");
         return null;
     }
 
diff --git a/core/java/android/service/autofill/FillResponse.java b/core/java/android/service/autofill/FillResponse.java
index 7bf1f83..d408e9a 100644
--- a/core/java/android/service/autofill/FillResponse.java
+++ b/core/java/android/service/autofill/FillResponse.java
@@ -84,6 +84,7 @@
     private final @Nullable AutofillId[] mFieldClassificationIds;
     private final int mFlags;
     private int mRequestId;
+    private final @Nullable UserData mUserData;
 
     private FillResponse(@NonNull Builder builder) {
         mDatasets = (builder.mDatasets != null) ? new ParceledListSlice<>(builder.mDatasets) : null;
@@ -99,6 +100,7 @@
         mFieldClassificationIds = builder.mFieldClassificationIds;
         mFlags = builder.mFlags;
         mRequestId = INVALID_REQUEST_ID;
+        mUserData = builder.mUserData;
     }
 
     /** @hide */
@@ -157,6 +159,11 @@
     }
 
     /** @hide */
+    public @Nullable UserData getUserData() {
+        return mUserData;
+    }
+
+    /** @hide */
     @TestApi
     public int getFlags() {
         return mFlags;
@@ -198,6 +205,7 @@
         private AutofillId[] mFieldClassificationIds;
         private int mFlags;
         private boolean mDestroyed;
+        private UserData mUserData;
 
         /**
          * Triggers a custom UI before before autofilling the screen with any data set in this
@@ -506,6 +514,21 @@
         }
 
         /**
+         * Sets a specific {@link UserData} for field classification for this request only.
+         *
+         * @return this builder
+         * @throws IllegalStateException if the FillResponse
+         * {@link #setAuthentication(AutofillId[], IntentSender, RemoteViews)
+         * requires authentication}.
+         */
+        public Builder setUserData(@NonNull UserData userData) {
+            throwIfDestroyed();
+            throwIfAuthenticationCalled();
+            mUserData = Preconditions.checkNotNull(userData);
+            return this;
+        }
+
+        /**
          * Builds a new {@link FillResponse} instance.
          *
          * @throws IllegalStateException if any of the following conditions occur:
@@ -599,6 +622,9 @@
         if (mFieldClassificationIds != null) {
             builder.append(Arrays.toString(mFieldClassificationIds));
         }
+        if (mUserData != null) {
+            builder.append(", userData=").append(mUserData);
+        }
         return builder.append("]").toString();
     }
 
@@ -621,6 +647,7 @@
         parcel.writeParcelable(mPresentation, flags);
         parcel.writeParcelable(mHeader, flags);
         parcel.writeParcelable(mFooter, flags);
+        parcel.writeParcelable(mUserData, flags);
         parcel.writeParcelableArray(mIgnoredIds, flags);
         parcel.writeLong(mDisableDuration);
         parcel.writeParcelableArray(mFieldClassificationIds, flags);
@@ -661,6 +688,10 @@
             if (footer != null) {
                 builder.setFooter(footer);
             }
+            final UserData userData = parcel.readParcelable(null);
+            if (userData != null) {
+                builder.setUserData(userData);
+            }
 
             builder.setIgnoredIds(parcel.readParcelableArray(null, AutofillId.class));
             final long disableDuration = parcel.readLong();
diff --git a/core/java/android/view/textclassifier/ConversationActions.java b/core/java/android/view/textclassifier/ConversationActions.java
index 1a7b911..04b94b0 100644
--- a/core/java/android/view/textclassifier/ConversationActions.java
+++ b/core/java/android/view/textclassifier/ConversationActions.java
@@ -30,6 +30,7 @@
 import android.text.SpannedString;
 import android.util.ArraySet;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.Preconditions;
 
 import java.lang.annotation.Retention;
@@ -654,6 +655,8 @@
         @NonNull
         @Hint
         private final List<String> mHints;
+        @Nullable
+        private String mCallingPackageName;
 
         private Request(
                 @NonNull List<Message> conversation,
@@ -666,15 +669,26 @@
             mHints = hints;
         }
 
-        private Request(Parcel in) {
+        private static Request readFromParcel(Parcel in) {
             List<Message> conversation = new ArrayList<>();
             in.readParcelableList(conversation, null);
-            mConversation = Collections.unmodifiableList(conversation);
-            mTypeConfig = in.readParcelable(null);
-            mMaxSuggestions = in.readInt();
+
+            TypeConfig typeConfig = in.readParcelable(null);
+
+            int maxSuggestions = in.readInt();
+
             List<String> hints = new ArrayList<>();
             in.readStringList(hints);
-            mHints = Collections.unmodifiableList(hints);
+
+            String callingPackageName = in.readString();
+
+            Request request = new Request(
+                    conversation,
+                    typeConfig,
+                    maxSuggestions,
+                    hints);
+            request.setCallingPackageName(callingPackageName);
+            return request;
         }
 
         @Override
@@ -683,6 +697,7 @@
             parcel.writeParcelable(mTypeConfig, flags);
             parcel.writeInt(mMaxSuggestions);
             parcel.writeStringList(mHints);
+            parcel.writeString(mCallingPackageName);
         }
 
         @Override
@@ -694,7 +709,7 @@
                 new Creator<Request>() {
                     @Override
                     public Request createFromParcel(Parcel in) {
-                        return new Request(in);
+                        return readFromParcel(in);
                     }
 
                     @Override
@@ -730,6 +745,26 @@
             return mHints;
         }
 
+        /**
+         * Sets the name of the package that is sending this request.
+         * <p>
+         * Package-private for SystemTextClassifier's use.
+         * @hide
+         */
+        @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+        public void setCallingPackageName(@Nullable String callingPackageName) {
+            mCallingPackageName = callingPackageName;
+        }
+
+        /**
+         * Returns the name of the package that sent this request.
+         * This returns {@code null} if no calling package name is set.
+         */
+        @Nullable
+        public String getCallingPackageName() {
+            return mCallingPackageName;
+        }
+
         /** Builder object to construct the {@link Request} object. */
         public static final class Builder {
             @NonNull
diff --git a/core/java/android/view/textclassifier/SystemTextClassifier.java b/core/java/android/view/textclassifier/SystemTextClassifier.java
index f8fce62..c24489c 100644
--- a/core/java/android/view/textclassifier/SystemTextClassifier.java
+++ b/core/java/android/view/textclassifier/SystemTextClassifier.java
@@ -60,7 +60,7 @@
         mSettings = Preconditions.checkNotNull(settings);
         mFallback = context.getSystemService(TextClassificationManager.class)
                 .getTextClassifier(TextClassifier.LOCAL);
-        mPackageName = Preconditions.checkNotNull(context.getPackageName());
+        mPackageName = Preconditions.checkNotNull(context.getOpPackageName());
     }
 
     /**
@@ -72,6 +72,7 @@
         Preconditions.checkNotNull(request);
         Utils.checkMainThread();
         try {
+            request.setCallingPackageName(mPackageName);
             final TextSelectionCallback callback = new TextSelectionCallback();
             mManagerService.onSuggestSelection(mSessionId, request, callback);
             final TextSelection selection = callback.mReceiver.get();
@@ -93,6 +94,7 @@
         Preconditions.checkNotNull(request);
         Utils.checkMainThread();
         try {
+            request.setCallingPackageName(mPackageName);
             final TextClassificationCallback callback = new TextClassificationCallback();
             mManagerService.onClassifyText(mSessionId, request, callback);
             final TextClassification classification = callback.mReceiver.get();
@@ -150,6 +152,7 @@
         Utils.checkMainThread();
 
         try {
+            request.setCallingPackageName(mPackageName);
             final TextLanguageCallback callback = new TextLanguageCallback();
             mManagerService.onDetectLanguage(mSessionId, request, callback);
             final TextLanguage textLanguage = callback.mReceiver.get();
@@ -168,6 +171,7 @@
         Utils.checkMainThread();
 
         try {
+            request.setCallingPackageName(mPackageName);
             final ConversationActionsCallback callback = new ConversationActionsCallback();
             mManagerService.onSuggestConversationActions(mSessionId, request, callback);
             final ConversationActions conversationActions = callback.mReceiver.get();
diff --git a/core/java/android/view/textclassifier/TextClassification.java b/core/java/android/view/textclassifier/TextClassification.java
index e0910c0..d9f7965 100644
--- a/core/java/android/view/textclassifier/TextClassification.java
+++ b/core/java/android/view/textclassifier/TextClassification.java
@@ -37,11 +37,13 @@
 import android.os.LocaleList;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.text.SpannedString;
 import android.util.ArrayMap;
 import android.view.View.OnClickListener;
 import android.view.textclassifier.TextClassifier.EntityType;
 import android.view.textclassifier.TextClassifier.Utils;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.Preconditions;
 
 import java.lang.annotation.Retention;
@@ -518,6 +520,7 @@
         @Nullable private final LocaleList mDefaultLocales;
         @Nullable private final ZonedDateTime mReferenceTime;
         @NonNull private final Bundle mExtras;
+        @Nullable private String mCallingPackageName;
 
         private Request(
                 CharSequence text,
@@ -578,6 +581,26 @@
         }
 
         /**
+         * Sets the name of the package that is sending this request.
+         * <p>
+         * For SystemTextClassifier's use.
+         * @hide
+         */
+        @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+        public void setCallingPackageName(@Nullable String callingPackageName) {
+            mCallingPackageName = callingPackageName;
+        }
+
+        /**
+         * Returns the name of the package that sent this request.
+         * This returns {@code null} if no calling package name is set.
+         */
+        @Nullable
+        public String getCallingPackageName() {
+            return mCallingPackageName;
+        }
+
+        /**
          * Returns the extended data.
          *
          * <p><b>NOTE: </b>Each call to this method returns a new bundle copy so clients should
@@ -660,7 +683,8 @@
              */
             @NonNull
             public Request build() {
-                return new Request(mText, mStartIndex, mEndIndex, mDefaultLocales, mReferenceTime,
+                return new Request(new SpannedString(mText), mStartIndex, mEndIndex,
+                        mDefaultLocales, mReferenceTime,
                         mExtras == null ? Bundle.EMPTY : mExtras.deepCopy());
             }
         }
@@ -672,25 +696,37 @@
 
         @Override
         public void writeToParcel(Parcel dest, int flags) {
-            dest.writeString(mText.toString());
+            dest.writeCharSequence(mText);
             dest.writeInt(mStartIndex);
             dest.writeInt(mEndIndex);
-            dest.writeInt(mDefaultLocales != null ? 1 : 0);
-            if (mDefaultLocales != null) {
-                mDefaultLocales.writeToParcel(dest, flags);
-            }
-            dest.writeInt(mReferenceTime != null ? 1 : 0);
-            if (mReferenceTime != null) {
-                dest.writeString(mReferenceTime.toString());
-            }
+            dest.writeParcelable(mDefaultLocales, flags);
+            dest.writeString(mReferenceTime == null ? null : mReferenceTime.toString());
+            dest.writeString(mCallingPackageName);
             dest.writeBundle(mExtras);
         }
 
+        private static Request readFromParcel(Parcel in) {
+            final CharSequence text = in.readCharSequence();
+            final int startIndex = in.readInt();
+            final int endIndex = in.readInt();
+            final LocaleList defaultLocales = in.readParcelable(null);
+            final String referenceTimeString = in.readString();
+            final ZonedDateTime referenceTime = referenceTimeString == null
+                    ? null : ZonedDateTime.parse(referenceTimeString);
+            final String callingPackageName = in.readString();
+            final Bundle extras = in.readBundle();
+
+            final Request request = new Request(text, startIndex, endIndex,
+                    defaultLocales, referenceTime, extras);
+            request.setCallingPackageName(callingPackageName);
+            return request;
+        }
+
         public static final Parcelable.Creator<Request> CREATOR =
                 new Parcelable.Creator<Request>() {
                     @Override
                     public Request createFromParcel(Parcel in) {
-                        return new Request(in);
+                        return readFromParcel(in);
                     }
 
                     @Override
@@ -698,15 +734,6 @@
                         return new Request[size];
                     }
                 };
-
-        private Request(Parcel in) {
-            mText = in.readString();
-            mStartIndex = in.readInt();
-            mEndIndex = in.readInt();
-            mDefaultLocales = in.readInt() == 0 ? null : LocaleList.CREATOR.createFromParcel(in);
-            mReferenceTime = in.readInt() == 0 ? null : ZonedDateTime.parse(in.readString());
-            mExtras = in.readBundle();
-        }
     }
 
     @Override
diff --git a/core/java/android/view/textclassifier/TextLanguage.java b/core/java/android/view/textclassifier/TextLanguage.java
index d28459e..b1609fc 100644
--- a/core/java/android/view/textclassifier/TextLanguage.java
+++ b/core/java/android/view/textclassifier/TextLanguage.java
@@ -26,6 +26,7 @@
 import android.os.Parcelable;
 import android.util.ArrayMap;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.Preconditions;
 
 import java.util.Locale;
@@ -90,7 +91,7 @@
      * confidence to low confidence.
      *
      * @throws IndexOutOfBoundsException if the specified index is out of range.
-     * @see #getLocaleCount() for the number of locales available.
+     * @see #getLocaleHypothesisCount() for the number of locales available.
      */
     @NonNull
     public ULocale getLocale(int index) {
@@ -222,11 +223,12 @@
         };
 
         private final CharSequence mText;
-        private final Bundle mBundle;
+        private final Bundle mExtra;
+        @Nullable private String mCallingPackageName;
 
         private Request(CharSequence text, Bundle bundle) {
             mText = text;
-            mBundle = bundle;
+            mExtra = bundle;
         }
 
         /**
@@ -238,6 +240,25 @@
         }
 
         /**
+         * Sets the name of the package that is sending this request.
+         * Package-private for SystemTextClassifier's use.
+         * @hide
+         */
+        @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+        public void setCallingPackageName(@Nullable String callingPackageName) {
+            mCallingPackageName = callingPackageName;
+        }
+
+        /**
+         * Returns the name of the package that sent this request.
+         * This returns null if no calling package name is set.
+         */
+        @Nullable
+        public String getCallingPackageName() {
+            return mCallingPackageName;
+        }
+
+        /**
          * Returns a bundle containing non-structured extra information about this request.
          *
          * <p><b>NOTE: </b>Each call to this method returns a new bundle copy so clients should
@@ -246,7 +267,7 @@
          */
         @NonNull
         public Bundle getExtras() {
-            return mBundle.deepCopy();
+            return mExtra.deepCopy();
         }
 
         @Override
@@ -257,13 +278,18 @@
         @Override
         public void writeToParcel(Parcel dest, int flags) {
             dest.writeCharSequence(mText);
-            dest.writeBundle(mBundle);
+            dest.writeString(mCallingPackageName);
+            dest.writeBundle(mExtra);
         }
 
         private static Request readFromParcel(Parcel in) {
-            return new Request(
-                    in.readCharSequence(),
-                    in.readBundle());
+            final CharSequence text = in.readCharSequence();
+            final String callingPackageName = in.readString();
+            final Bundle extra = in.readBundle();
+
+            final Request request = new Request(text, extra);
+            request.setCallingPackageName(callingPackageName);
+            return request;
         }
 
         /**
diff --git a/core/java/android/view/textclassifier/TextLinks.java b/core/java/android/view/textclassifier/TextLinks.java
index 1e42c41..ab34178 100644
--- a/core/java/android/view/textclassifier/TextLinks.java
+++ b/core/java/android/view/textclassifier/TextLinks.java
@@ -30,6 +30,7 @@
 import android.text.style.ClickableSpan;
 import android.text.style.URLSpan;
 import android.view.View;
+import android.view.textclassifier.TextClassifier.EntityConfig;
 import android.view.textclassifier.TextClassifier.EntityType;
 import android.widget.TextView;
 
@@ -205,27 +206,32 @@
         private final EntityConfidence mEntityScores;
         private final int mStart;
         private final int mEnd;
-        @Nullable final URLSpan mUrlSpan;
+        private final Bundle mExtras;
+        @Nullable private final URLSpan mUrlSpan;
 
         /**
          * Create a new TextLink.
          *
          * @param start The start index of the identified subsequence
          * @param end The end index of the identified subsequence
-         * @param entityScores A mapping of entity type to confidence score
+         * @param entityConfidence A mapping of entity type to confidence score
+         * @param extras A bundle containing custom data related to this TextLink
          * @param urlSpan An optional URLSpan to delegate to. NOTE: Not parcelled
          *
-         * @throws IllegalArgumentException if entityScores is null or empty
+         * @throws IllegalArgumentException if {@code entityConfidence} is null or empty
+         * @throws IllegalArgumentException if {@code start} is greater than {@code end}
          */
-        TextLink(int start, int end, Map<String, Float> entityScores,
-                @Nullable URLSpan urlSpan) {
-            Preconditions.checkNotNull(entityScores);
-            Preconditions.checkArgument(!entityScores.isEmpty());
+        private TextLink(int start, int end, @NonNull EntityConfidence entityConfidence,
+                @NonNull Bundle extras, @Nullable URLSpan urlSpan) {
+            Preconditions.checkNotNull(entityConfidence);
+            Preconditions.checkArgument(!entityConfidence.getEntities().isEmpty());
             Preconditions.checkArgument(start <= end);
+            Preconditions.checkNotNull(extras);
             mStart = start;
             mEnd = end;
-            mEntityScores = new EntityConfidence(entityScores);
+            mEntityScores = entityConfidence;
             mUrlSpan = urlSpan;
+            mExtras = extras;
         }
 
         /**
@@ -274,6 +280,13 @@
             return mEntityScores.getConfidenceScore(entityType);
         }
 
+        /**
+         * Returns a bundle containing custom data related to this TextLink.
+         */
+        public Bundle getExtras() {
+            return mExtras;
+        }
+
         @Override
         public String toString() {
             return String.format(Locale.US,
@@ -291,13 +304,22 @@
             mEntityScores.writeToParcel(dest, flags);
             dest.writeInt(mStart);
             dest.writeInt(mEnd);
+            dest.writeBundle(mExtras);
+        }
+
+        private static TextLink readFromParcel(Parcel in) {
+            final EntityConfidence entityConfidence = EntityConfidence.CREATOR.createFromParcel(in);
+            final int start = in.readInt();
+            final int end = in.readInt();
+            final Bundle extras = in.readBundle();
+            return new TextLink(start, end, entityConfidence, extras, null /* urlSpan */);
         }
 
         public static final Parcelable.Creator<TextLink> CREATOR =
                 new Parcelable.Creator<TextLink>() {
                     @Override
                     public TextLink createFromParcel(Parcel in) {
-                        return new TextLink(in);
+                        return readFromParcel(in);
                     }
 
                     @Override
@@ -305,13 +327,6 @@
                         return new TextLink[size];
                     }
                 };
-
-        private TextLink(Parcel in) {
-            mEntityScores = EntityConfidence.CREATOR.createFromParcel(in);
-            mStart = in.readInt();
-            mEnd = in.readInt();
-            mUrlSpan = null;
-        }
     }
 
     /**
@@ -321,23 +336,21 @@
 
         private final CharSequence mText;
         @Nullable private final LocaleList mDefaultLocales;
-        @Nullable private final TextClassifier.EntityConfig mEntityConfig;
+        @Nullable private final EntityConfig mEntityConfig;
         private final boolean mLegacyFallback;
-        private String mCallingPackageName;
+        @Nullable private String mCallingPackageName;
         private final Bundle mExtras;
 
         private Request(
                 CharSequence text,
                 LocaleList defaultLocales,
-                TextClassifier.EntityConfig entityConfig,
+                EntityConfig entityConfig,
                 boolean legacyFallback,
-                String callingPackageName,
                 Bundle extras) {
             mText = text;
             mDefaultLocales = defaultLocales;
             mEntityConfig = entityConfig;
             mLegacyFallback = legacyFallback;
-            mCallingPackageName = callingPackageName;
             mExtras = extras;
         }
 
@@ -360,10 +373,10 @@
 
         /**
          * @return The config representing the set of entities to look for
-         * @see Builder#setEntityConfig(TextClassifier.EntityConfig)
+         * @see Builder#setEntityConfig(EntityConfig)
          */
         @Nullable
-        public TextClassifier.EntityConfig getEntityConfig() {
+        public EntityConfig getEntityConfig() {
             return mEntityConfig;
         }
 
@@ -378,13 +391,26 @@
         }
 
         /**
-         * Sets the name of the package that requested the links to get generated.
+         * Sets the name of the package that is sending this request.
+         * <p>
+         * Package-private for SystemTextClassifier's use.
+         * @hide
          */
-        void setCallingPackageName(@Nullable String callingPackageName) {
+        @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+        public void setCallingPackageName(@Nullable String callingPackageName) {
             mCallingPackageName = callingPackageName;
         }
 
         /**
+         * Returns the name of the package that sent this request.
+         * This returns {@code null} if no calling package name is set.
+         */
+        @Nullable
+        public String getCallingPackageName() {
+            return mCallingPackageName;
+        }
+
+        /**
          * Returns the extended data.
          *
          * <p><b>NOTE: </b>Each call to this method returns a new bundle copy so clients should
@@ -404,9 +430,8 @@
             private final CharSequence mText;
 
             @Nullable private LocaleList mDefaultLocales;
-            @Nullable private TextClassifier.EntityConfig mEntityConfig;
+            @Nullable private EntityConfig mEntityConfig;
             private boolean mLegacyFallback = true; // Use legacy fall back by default.
-            private String mCallingPackageName;
             @Nullable private Bundle mExtras;
 
             public Builder(@NonNull CharSequence text) {
@@ -434,7 +459,7 @@
              * @return this builder
              */
             @NonNull
-            public Builder setEntityConfig(@Nullable TextClassifier.EntityConfig entityConfig) {
+            public Builder setEntityConfig(@Nullable EntityConfig entityConfig) {
                 mEntityConfig = entityConfig;
                 return this;
             }
@@ -455,18 +480,6 @@
             }
 
             /**
-             * Sets the name of the package that requested the links to get generated.
-             *
-             * @return this builder
-             * @hide
-             */
-            @NonNull
-            public Builder setCallingPackageName(@Nullable String callingPackageName) {
-                mCallingPackageName = callingPackageName;
-                return this;
-            }
-
-            /**
              * Sets the extended data.
              *
              * @return this builder
@@ -483,21 +496,11 @@
             public Request build() {
                 return new Request(
                         mText, mDefaultLocales, mEntityConfig,
-                        mLegacyFallback, mCallingPackageName,
+                        mLegacyFallback,
                         mExtras == null ? Bundle.EMPTY : mExtras.deepCopy());
             }
         }
 
-        /**
-         * @return the name of the package that requested the links to get generated.
-         * TODO: make available as system API
-         * @hide
-         */
-        @Nullable
-        public String getCallingPackageName() {
-            return mCallingPackageName;
-        }
-
         @Override
         public int describeContents() {
             return 0;
@@ -506,23 +509,30 @@
         @Override
         public void writeToParcel(Parcel dest, int flags) {
             dest.writeString(mText.toString());
-            dest.writeInt(mDefaultLocales != null ? 1 : 0);
-            if (mDefaultLocales != null) {
-                mDefaultLocales.writeToParcel(dest, flags);
-            }
-            dest.writeInt(mEntityConfig != null ? 1 : 0);
-            if (mEntityConfig != null) {
-                mEntityConfig.writeToParcel(dest, flags);
-            }
+            dest.writeParcelable(mDefaultLocales, flags);
+            dest.writeParcelable(mEntityConfig, flags);
             dest.writeString(mCallingPackageName);
             dest.writeBundle(mExtras);
         }
 
+        private static Request readFromParcel(Parcel in) {
+            final String text = in.readString();
+            final LocaleList defaultLocales = in.readParcelable(null);
+            final EntityConfig entityConfig = in.readParcelable(null);
+            final String callingPackageName = in.readString();
+            final Bundle extras = in.readBundle();
+
+            final Request request = new Request(text, defaultLocales, entityConfig,
+                    /* legacyFallback= */ true, extras);
+            request.setCallingPackageName(callingPackageName);
+            return request;
+        }
+
         public static final Parcelable.Creator<Request> CREATOR =
                 new Parcelable.Creator<Request>() {
                     @Override
                     public Request createFromParcel(Parcel in) {
-                        return new Request(in);
+                        return readFromParcel(in);
                     }
 
                     @Override
@@ -530,16 +540,6 @@
                         return new Request[size];
                     }
                 };
-
-        private Request(Parcel in) {
-            mText = in.readString();
-            mDefaultLocales = in.readInt() == 0 ? null : LocaleList.CREATOR.createFromParcel(in);
-            mEntityConfig = in.readInt() == 0
-                    ? null : TextClassifier.EntityConfig.CREATOR.createFromParcel(in);
-            mLegacyFallback = true;
-            mCallingPackageName = in.readString();
-            mExtras = in.readBundle();
-        }
     }
 
     /**
@@ -645,9 +645,20 @@
          * @throws IllegalArgumentException if entityScores is null or empty.
          */
         @NonNull
-        public Builder addLink(int start, int end, Map<String, Float> entityScores) {
-            mLinks.add(new TextLink(start, end, entityScores, null));
-            return this;
+        public Builder addLink(int start, int end, @NonNull Map<String, Float> entityScores) {
+            return addLink(start, end, entityScores, Bundle.EMPTY, null);
+        }
+
+        /**
+         * Adds a TextLink.
+         *
+         * @see #addLink(int, int, Map)
+         * @param extras An optional bundle containing custom data related to this TextLink
+         */
+        @NonNull
+        public Builder addLink(int start, int end, @NonNull Map<String, Float> entityScores,
+                @NonNull Bundle extras) {
+            return addLink(start, end, entityScores, extras, null);
         }
 
         /**
@@ -655,9 +666,15 @@
          * @param urlSpan An optional URLSpan to delegate to. NOTE: Not parcelled.
          */
         @NonNull
-        Builder addLink(int start, int end, Map<String, Float> entityScores,
+        Builder addLink(int start, int end, @NonNull Map<String, Float> entityScores,
                 @Nullable URLSpan urlSpan) {
-            mLinks.add(new TextLink(start, end, entityScores, urlSpan));
+            return addLink(start, end, entityScores, Bundle.EMPTY, urlSpan);
+        }
+
+        private Builder addLink(int start, int end, @NonNull Map<String, Float> entityScores,
+                @NonNull Bundle extras, @Nullable URLSpan urlSpan) {
+            mLinks.add(new TextLink(
+                    start, end, new EntityConfidence(entityScores), extras, urlSpan));
             return this;
         }
 
diff --git a/core/java/android/view/textclassifier/TextSelection.java b/core/java/android/view/textclassifier/TextSelection.java
index f236915..4a6f3e5 100644
--- a/core/java/android/view/textclassifier/TextSelection.java
+++ b/core/java/android/view/textclassifier/TextSelection.java
@@ -24,10 +24,12 @@
 import android.os.LocaleList;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.text.SpannedString;
 import android.util.ArrayMap;
 import android.view.textclassifier.TextClassifier.EntityType;
 import android.view.textclassifier.TextClassifier.Utils;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.Preconditions;
 
 import java.util.Locale;
@@ -209,6 +211,7 @@
         @Nullable private final LocaleList mDefaultLocales;
         private final boolean mDarkLaunchAllowed;
         private final Bundle mExtras;
+        @Nullable private String mCallingPackageName;
 
         private Request(
                 CharSequence text,
@@ -270,6 +273,26 @@
         }
 
         /**
+         * Sets the name of the package that is sending this request.
+         * <p>
+         * Package-private for SystemTextClassifier's use.
+         * @hide
+         */
+        @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+        public void setCallingPackageName(@Nullable String callingPackageName) {
+            mCallingPackageName = callingPackageName;
+        }
+
+        /**
+         * Returns the name of the package that sent this request.
+         * This returns {@code null} if no calling package name is set.
+         */
+        @Nullable
+        public String getCallingPackageName() {
+            return mCallingPackageName;
+        }
+
+        /**
          * Returns the extended data.
          *
          * <p><b>NOTE: </b>Each call to this method returns a new bundle copy so clients should
@@ -355,7 +378,7 @@
              */
             @NonNull
             public Request build() {
-                return new Request(mText, mStartIndex, mEndIndex,
+                return new Request(new SpannedString(mText), mStartIndex, mEndIndex,
                         mDefaultLocales, mDarkLaunchAllowed,
                         mExtras == null ? Bundle.EMPTY : mExtras.deepCopy());
             }
@@ -368,21 +391,33 @@
 
         @Override
         public void writeToParcel(Parcel dest, int flags) {
-            dest.writeString(mText.toString());
+            dest.writeCharSequence(mText);
             dest.writeInt(mStartIndex);
             dest.writeInt(mEndIndex);
-            dest.writeInt(mDefaultLocales != null ? 1 : 0);
-            if (mDefaultLocales != null) {
-                mDefaultLocales.writeToParcel(dest, flags);
-            }
+            dest.writeParcelable(mDefaultLocales, flags);
+            dest.writeString(mCallingPackageName);
             dest.writeBundle(mExtras);
         }
 
+        private static Request readFromParcel(Parcel in) {
+            final CharSequence text = in.readCharSequence();
+            final int startIndex = in.readInt();
+            final int endIndex = in.readInt();
+            final LocaleList defaultLocales = in.readParcelable(null);
+            final String callingPackageName = in.readString();
+            final Bundle extras = in.readBundle();
+
+            final Request request = new Request(text, startIndex, endIndex, defaultLocales,
+                    /* darkLaunchAllowed= */ false, extras);
+            request.setCallingPackageName(callingPackageName);
+            return request;
+        }
+
         public static final Parcelable.Creator<Request> CREATOR =
                 new Parcelable.Creator<Request>() {
                     @Override
                     public Request createFromParcel(Parcel in) {
-                        return new Request(in);
+                        return readFromParcel(in);
                     }
 
                     @Override
@@ -390,15 +425,6 @@
                         return new Request[size];
                     }
                 };
-
-        private Request(Parcel in) {
-            mText = in.readString();
-            mStartIndex = in.readInt();
-            mEndIndex = in.readInt();
-            mDefaultLocales = in.readInt() == 0 ? null : LocaleList.CREATOR.createFromParcel(in);
-            mDarkLaunchAllowed = false;
-            mExtras = in.readBundle();
-        }
     }
 
     @Override
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 1dd42b8..4dedd49 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1648,7 +1648,7 @@
          @hide
     -->
     <permission android:name="android.permission.SUSPEND_APPS"
-        android:protectionLevel="signature|privileged" />
+        android:protectionLevel="signature|wellbeing" />
 
     <!-- Allows applications to discover and pair bluetooth devices.
          <p>Protection level: normal
@@ -4086,10 +4086,10 @@
          confirmation UI for full backup/restore -->
     <uses-permission android:name="android.permission.CONFIRM_FULL_BACKUP"/>
 
-    <!-- Allows the holder to access and manage instant applications on the device.
-    @hide -->
+    <!-- @SystemApi Allows the holder to access and manage instant applications on the device.
+         @hide -->
     <permission android:name="android.permission.ACCESS_INSTANT_APPS"
-            android:protectionLevel="signature|installer|verifier" />
+            android:protectionLevel="signature|installer|verifier|wellbeing" />
     <uses-permission android:name="android.permission.ACCESS_INSTANT_APPS"/>
 
     <!-- Allows the holder to view the instant applications on the device.
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 854582b..f8004ea 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -268,6 +268,9 @@
         <!-- Additional flag from base permission type: this permission can be automatically
             granted to the system default text classifier -->
         <flag name="textClassifier" value="0x10000" />
+        <!-- Additional flag from base permission type: this permission will be granted to the
+             wellbeing app, as defined by the OEM. -->
+        <flag name="wellbeing" value="0x20000" />
     </attr>
 
     <!-- Flags indicating more context for a permission group. -->
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 62ec5c4..101f92b 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3349,6 +3349,13 @@
          See android.view.textclassifier.TextClassificationManager.
     -->
     <string name="config_defaultTextClassifierPackage" translatable="false"></string>
+    
+    <!-- The package name for the default wellbeing app.
+         This package must be trusted, as it has the permissions to control other applications
+         on the device.
+         Example: "com.android.wellbeing"
+     -->
+    <string name="config_defaultWellbeingPackage" translatable="false"></string>
 
     <!-- The package name for the system's content capture service.
          This service must be trusted, as it can be activated without explicit consent of the user.
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 82a679e..b24cdba 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3265,6 +3265,7 @@
   <java-symbol type="string" name="notification_channel_do_not_disturb" />
   <java-symbol type="string" name="config_defaultAutofillService" />
   <java-symbol type="string" name="config_defaultTextClassifierPackage" />
+  <java-symbol type="string" name="config_defaultWellbeingPackage" />
   <java-symbol type="string" name="config_defaultContentCaptureService" />
   <java-symbol type="string" name="config_defaultAugmentedAutofillService" />
 
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java b/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java
index 91a5440..aaf7312 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextClassificationTest.java
@@ -189,6 +189,7 @@
                 Instant.ofEpochMilli(946771200000L),  // 2000-01-02
                 ZoneId.of("UTC"));
         final String text = "text";
+        final String packageName = "packageName";
 
         final TextClassification.Request reference =
                 new TextClassification.Request.Builder(text, 0, text.length())
@@ -196,6 +197,7 @@
                         .setReferenceTime(referenceTime)
                         .setExtras(BUNDLE)
                         .build();
+        reference.setCallingPackageName(packageName);
 
         // Parcel and unparcel.
         final Parcel parcel = Parcel.obtain();
@@ -204,12 +206,13 @@
         final TextClassification.Request result =
                 TextClassification.Request.CREATOR.createFromParcel(parcel);
 
-        assertEquals(text, result.getText());
+        assertEquals(text, result.getText().toString());
         assertEquals(0, result.getStartIndex());
         assertEquals(text.length(), result.getEndIndex());
         assertEquals(referenceTime, result.getReferenceTime());
         assertEquals("en-US,de-DE", result.getDefaultLocales().toLanguageTags());
         assertEquals(referenceTime, result.getReferenceTime());
         assertEquals(BUNDLE_VALUE, result.getExtras().getString(BUNDLE_KEY));
+        assertEquals(packageName, result.getCallingPackageName());
     }
 }
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextLanguageTest.java b/core/tests/coretests/src/android/view/textclassifier/TextLanguageTest.java
index 75ca769..1dcaed6 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextLanguageTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextLanguageTest.java
@@ -69,10 +69,12 @@
         final String bundleKey = "experiment.str";
         final Bundle bundle = new Bundle();
         bundle.putString(bundleKey, "bundle");
+        final String packageName = "packageName";
 
         final TextLanguage.Request reference = new TextLanguage.Request.Builder(text)
                 .setExtras(bundle)
                 .build();
+        reference.setCallingPackageName(packageName);
 
         final Parcel parcel = Parcel.obtain();
         reference.writeToParcel(parcel, 0);
@@ -81,5 +83,6 @@
 
         assertEquals(text, result.getText());
         assertEquals("bundle", result.getExtras().getString(bundleKey));
+        assertEquals(packageName, result.getCallingPackageName());
     }
 }
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextLinksTest.java b/core/tests/coretests/src/android/view/textclassifier/TextLinksTest.java
index f6ec0e6..f022d04 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextLinksTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextLinksTest.java
@@ -64,7 +64,7 @@
     public void testParcel() {
         final String fullText = "this is just a test";
         final TextLinks reference = new TextLinks.Builder(fullText)
-                .addLink(0, 4, getEntityScores(0.f, 0.f, 1.f))
+                .addLink(0, 4, getEntityScores(0.f, 0.f, 1.f), BUNDLE)
                 .addLink(5, 12, getEntityScores(.8f, .1f, .5f))
                 .setExtras(BUNDLE)
                 .build();
@@ -82,6 +82,7 @@
         assertEquals(1, resultList.get(0).getEntityCount());
         assertEquals(TextClassifier.TYPE_OTHER, resultList.get(0).getEntity(0));
         assertEquals(1.f, resultList.get(0).getConfidenceScore(TextClassifier.TYPE_OTHER), 1e-7f);
+        assertEquals(BUNDLE_VALUE, resultList.get(0).getExtras().getString(BUNDLE_KEY));
         assertEquals(5, resultList.get(1).getStart());
         assertEquals(12, resultList.get(1).getEnd());
         assertEquals(3, resultList.get(1).getEntityCount());
@@ -96,6 +97,7 @@
 
     @Test
     public void testParcelOptions() {
+        final String packageName = "packageName";
         final TextClassifier.EntityConfig entityConfig = TextClassifier.EntityConfig.create(
                 Arrays.asList(TextClassifier.HINT_TEXT_IS_EDITABLE),
                 Arrays.asList("a", "b", "c"),
@@ -105,6 +107,7 @@
                 .setEntityConfig(entityConfig)
                 .setExtras(BUNDLE)
                 .build();
+        reference.setCallingPackageName(packageName);
 
         // Parcel and unparcel.
         final Parcel parcel = Parcel.obtain();
@@ -119,5 +122,6 @@
         assertEquals(new HashSet<String>(Arrays.asList("a", "c")),
                 result.getEntityConfig().resolveEntityListModifications(Collections.emptyList()));
         assertEquals(BUNDLE_VALUE, result.getExtras().getString(BUNDLE_KEY));
+        assertEquals(packageName, result.getCallingPackageName());
     }
 }
diff --git a/core/tests/coretests/src/android/view/textclassifier/TextSelectionTest.java b/core/tests/coretests/src/android/view/textclassifier/TextSelectionTest.java
index 7ea5108b..2ea49f7 100644
--- a/core/tests/coretests/src/android/view/textclassifier/TextSelectionTest.java
+++ b/core/tests/coretests/src/android/view/textclassifier/TextSelectionTest.java
@@ -75,11 +75,13 @@
     @Test
     public void testParcelRequest() {
         final String text = "text";
+        final String packageName = "packageName";
         final TextSelection.Request reference =
                 new TextSelection.Request.Builder(text, 0, text.length())
                         .setDefaultLocales(new LocaleList(Locale.US, Locale.GERMANY))
                         .setExtras(BUNDLE)
                         .build();
+        reference.setCallingPackageName(packageName);
 
         // Parcel and unparcel.
         final Parcel parcel = Parcel.obtain();
@@ -87,10 +89,11 @@
         parcel.setDataPosition(0);
         final TextSelection.Request result = TextSelection.Request.CREATOR.createFromParcel(parcel);
 
-        assertEquals(text, result.getText());
+        assertEquals(text, result.getText().toString());
         assertEquals(0, result.getStartIndex());
         assertEquals(text.length(), result.getEndIndex());
         assertEquals("en-US,de-DE", result.getDefaultLocales().toLanguageTags());
         assertEquals(BUNDLE_VALUE, result.getExtras().getString(BUNDLE_KEY));
+        assertEquals(packageName, result.getCallingPackageName());
     }
 }
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index ae87998..32c7520 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -96,9 +96,7 @@
     void addTestProvider(String name, in ProviderProperties properties, String opPackageName);
     void removeTestProvider(String provider, String opPackageName);
     void setTestProviderLocation(String provider, in Location loc, String opPackageName);
-    void clearTestProviderLocation(String provider, String opPackageName);
     void setTestProviderEnabled(String provider, boolean enabled, String opPackageName);
-    void clearTestProviderEnabled(String provider, String opPackageName);
 
     // --- deprecated ---
     void setTestProviderStatus(String provider, int status, in Bundle extras, long updateTime,
@@ -108,12 +106,8 @@
 
     // --- internal ---
 
-    // Used by location providers to tell the location manager when it has a new location.
-    // Passive is true if the location is coming from the passive provider, in which case
-    // it need not be shared with other providers.
+    // --- deprecated ---
     void reportLocation(in Location location, boolean passive);
-
-    // Used when a (initially Gnss) Location batch arrives
     void reportLocationBatch(in List<Location> locations);
 
     // for reporting callback completion
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 3bf98b3..334170e 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -1537,14 +1537,11 @@
      * mock location app op} is not set to {@link android.app.AppOpsManager#MODE_ALLOWED
      * allowed} for your app.
      * @throws IllegalArgumentException if no provider with the given name exists
+     *
+     * @deprecated This function has always been a no-op, and may be removed in the future.
      */
-    public void clearTestProviderLocation(String provider) {
-        try {
-            mService.clearTestProviderLocation(provider, mContext.getOpPackageName());
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
-    }
+    @Deprecated
+    public void clearTestProviderLocation(String provider) {}
 
     /**
      * Sets a mock enabled value for the given provider.  This value will be used in place
@@ -1575,13 +1572,12 @@
      * mock location app op} is not set to {@link android.app.AppOpsManager#MODE_ALLOWED
      * allowed} for your app.
      * @throws IllegalArgumentException if no provider with the given name exists
+     *
+     * @deprecated Use {@link #setTestProviderEnabled(String, boolean)} instead.
      */
+    @Deprecated
     public void clearTestProviderEnabled(String provider) {
-        try {
-            mService.clearTestProviderEnabled(provider, mContext.getOpPackageName());
-        } catch (RemoteException e) {
-            throw e.rethrowFromSystemServer();
-        }
+        setTestProviderEnabled(provider, true);
     }
 
     /**
diff --git a/location/java/com/android/internal/location/ILocationProvider.aidl b/location/java/com/android/internal/location/ILocationProvider.aidl
index 39c2d92..71b54fb 100644
--- a/location/java/com/android/internal/location/ILocationProvider.aidl
+++ b/location/java/com/android/internal/location/ILocationProvider.aidl
@@ -16,29 +16,26 @@
 
 package com.android.internal.location;
 
-import android.location.Location;
-import android.net.NetworkInfo;
 import android.os.Bundle;
 import android.os.WorkSource;
 
-import com.android.internal.location.ProviderProperties;
+import com.android.internal.location.ILocationProviderManager;
 import com.android.internal.location.ProviderRequest;
 
 /**
- * Binder interface for services that implement location providers.
- * <p>Use {@link LocationProviderBase} as a helper to implement this
- * interface.
+ * Binder interface for services that implement location providers. Do not implement this directly,
+ * extend {@link LocationProviderBase} instead.
  * @hide
  */
 interface ILocationProvider {
-    void enable();
-    void disable();
 
-    void setRequest(in ProviderRequest request, in WorkSource ws);
+    oneway void setLocationProviderManager(in ILocationProviderManager manager);
 
-    // --- deprecated (but still supported) ---
-    ProviderProperties getProperties();
+    oneway void setRequest(in ProviderRequest request, in WorkSource ws);
+
+    oneway void sendExtraCommand(String command, in Bundle extras);
+
+    // --- deprecated and will be removed the future ---
     int getStatus(out Bundle extras);
     long getStatusUpdateTime();
-    boolean sendExtraCommand(String command, inout Bundle extras);
 }
diff --git a/location/java/com/android/internal/location/ILocationProviderManager.aidl b/location/java/com/android/internal/location/ILocationProviderManager.aidl
new file mode 100644
index 0000000..b1b8f0c
--- /dev/null
+++ b/location/java/com/android/internal/location/ILocationProviderManager.aidl
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2009 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;
+
+import android.location.Location;
+
+import com.android.internal.location.ProviderProperties;
+
+/**
+ * Binder interface for manager of all location providers.
+ * @hide
+ */
+interface ILocationProviderManager {
+
+    void onSetEnabled(boolean enabled);
+
+    void onSetProperties(in ProviderProperties properties);
+
+    void onReportLocation(in Location location);
+}
diff --git a/location/java/com/android/internal/location/ProviderRequest.java b/location/java/com/android/internal/location/ProviderRequest.java
index 88919f6..a45c20d 100644
--- a/location/java/com/android/internal/location/ProviderRequest.java
+++ b/location/java/com/android/internal/location/ProviderRequest.java
@@ -16,15 +16,15 @@
 
 package com.android.internal.location;
 
-import java.util.ArrayList;
-import java.util.List;
-
 import android.annotation.UnsupportedAppUsage;
 import android.location.LocationRequest;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.TimeUtils;
 
+import java.util.ArrayList;
+import java.util.List;
+
 /** @hide */
 public final class ProviderRequest implements Parcelable {
     /** Location reporting is requested (true) */
@@ -36,6 +36,13 @@
     public long interval = Long.MAX_VALUE;
 
     /**
+     * When this flag is true, providers should ignore all location settings, user consents, power
+     * restrictions or any other restricting factors and always satisfy this request to the best of
+     * their ability. This flag should only be used in event of an emergency.
+     */
+    public boolean forceLocation = false;
+
+    /**
      * Whether provider shall make stronger than normal tradeoffs to substantially restrict power
      * use.
      */
diff --git a/location/lib/api/current.txt b/location/lib/api/current.txt
index d19559e..10c3447 100644
--- a/location/lib/api/current.txt
+++ b/location/lib/api/current.txt
@@ -8,14 +8,18 @@
   public abstract class LocationProviderBase {
     ctor public LocationProviderBase(java.lang.String, com.android.location.provider.ProviderPropertiesUnbundled);
     method public android.os.IBinder getBinder();
-    method public abstract void onDisable();
-    method public void onDump(java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
-    method public abstract void onEnable();
-    method public deprecated int onGetStatus(android.os.Bundle);
-    method public deprecated long onGetStatusUpdateTime();
-    method public boolean onSendExtraCommand(java.lang.String, android.os.Bundle);
-    method public abstract void onSetRequest(com.android.location.provider.ProviderRequestUnbundled, android.os.WorkSource);
-    method public final void reportLocation(android.location.Location);
+    method public boolean isEnabled();
+    method protected deprecated void onDisable();
+    method protected void onDump(java.io.FileDescriptor, java.io.PrintWriter, java.lang.String[]);
+    method protected deprecated void onEnable();
+    method protected deprecated int onGetStatus(android.os.Bundle);
+    method protected deprecated long onGetStatusUpdateTime();
+    method protected void onInit();
+    method protected boolean onSendExtraCommand(java.lang.String, android.os.Bundle);
+    method protected abstract void onSetRequest(com.android.location.provider.ProviderRequestUnbundled, android.os.WorkSource);
+    method public void reportLocation(android.location.Location);
+    method public void setEnabled(boolean);
+    method public void setProperties(com.android.location.provider.ProviderPropertiesUnbundled);
     field public static final java.lang.String EXTRA_NO_GPS_LOCATION = "noGPSLocation";
     field public static final java.lang.String FUSED_PROVIDER = "fused";
   }
@@ -38,6 +42,7 @@
   }
 
   public final class ProviderRequestUnbundled {
+    method public boolean getForceLocation();
     method public long getInterval();
     method public java.util.List<com.android.location.provider.LocationRequestUnbundled> getLocationRequests();
     method public boolean getReportLocation();
diff --git a/location/lib/java/com/android/location/provider/LocationProviderBase.java b/location/lib/java/com/android/location/provider/LocationProviderBase.java
index d45a4ba..5bcec92 100644
--- a/location/lib/java/com/android/location/provider/LocationProviderBase.java
+++ b/location/lib/java/com/android/location/provider/LocationProviderBase.java
@@ -16,6 +16,7 @@
 
 package com.android.location.provider;
 
+import android.annotation.Nullable;
 import android.content.Context;
 import android.location.ILocationManager;
 import android.location.Location;
@@ -29,12 +30,11 @@
 import android.util.Log;
 
 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.FastPrintWriter;
 
 import java.io.FileDescriptor;
-import java.io.FileOutputStream;
 import java.io.PrintWriter;
 
 /**
@@ -55,12 +55,6 @@
  * of this package for more information.
  */
 public abstract class LocationProviderBase {
-    private final String TAG;
-
-    /** @hide */
-    protected final ILocationManager mLocationManager;
-    private final ProviderProperties mProperties;
-    private final IBinder mBinder;
 
     /**
      * Bundle key for a version of the location containing no GPS data.
@@ -77,49 +71,34 @@
      */
     public static final String FUSED_PROVIDER = LocationManager.FUSED_PROVIDER;
 
-    private final class Service extends ILocationProvider.Stub {
-        @Override
-        public void enable() {
-            onEnable();
-        }
-        @Override
-        public void disable() {
-            onDisable();
-        }
-        @Override
-        public void setRequest(ProviderRequest request, WorkSource ws) {
-            onSetRequest(new ProviderRequestUnbundled(request), ws);
-        }
-        @Override
-        public ProviderProperties getProperties() {
-            return mProperties;
-        }
-        @Override
-        public int getStatus(Bundle extras) {
-            return onGetStatus(extras);
-        }
-        @Override
-        public long getStatusUpdateTime() {
-            return onGetStatusUpdateTime();
-        }
-        @Override
-        public boolean sendExtraCommand(String command, Bundle extras) {
-            return onSendExtraCommand(command, extras);
-        }
-        @Override
-        public void dump(FileDescriptor fd, String[] args) {
-            PrintWriter pw = new FastPrintWriter(new FileOutputStream(fd));
-            onDump(fd, pw, args);
-            pw.flush();
-        }
-    }
+    private final String mTag;
+    private final IBinder mBinder;
+
+    /**
+     * This field may be removed in the future, do not rely on it.
+     *
+     * @deprecated Do not use this field! Use LocationManager APIs instead. If you use this field
+     * you may be broken in the future.
+     * @hide
+     */
+    @Deprecated
+    protected final ILocationManager mLocationManager;
+
+    // write locked on mBinder, read lock is optional depending on atomicity requirements
+    @Nullable private volatile ILocationProviderManager mManager;
+    private volatile ProviderProperties mProperties;
+    private volatile boolean mEnabled;
 
     public LocationProviderBase(String tag, ProviderPropertiesUnbundled properties) {
-        TAG = tag;
-        IBinder b = ServiceManager.getService(Context.LOCATION_SERVICE);
-        mLocationManager = ILocationManager.Stub.asInterface(b);
-        mProperties = properties.getProviderProperties();
+        mTag = tag;
         mBinder = new Service();
+
+        mLocationManager = ILocationManager.Stub.asInterface(
+                ServiceManager.getService(Context.LOCATION_SERVICE));
+
+        mManager = null;
+        mProperties = properties.getProviderProperties();
+        mEnabled = true;
     }
 
     public IBinder getBinder() {
@@ -127,51 +106,116 @@
     }
 
     /**
-     * Used by the location provider to report new locations.
+     * Sets whether this provider is currently enabled or not. Note that this is specific to the
+     * provider only, and is not related to global location settings. This is a hint to the Location
+     * Manager that this provider will generally be unable to fulfill incoming requests. This
+     * provider may still receive callbacks to onSetRequest while not enabled, and must decide
+     * whether to attempt to satisfy those requests or not.
      *
-     * @param location new Location to report
-     *
-     * Requires the android.permission.INSTALL_LOCATION_PROVIDER permission.
+     * Some guidelines: providers should set their own enabled/disabled status based only on state
+     * "owned" by that provider. For instance, providers should not take into account the state of
+     * the location master setting when setting themselves enabled or disabled, as this state is not
+     * owned by a particular provider. If a provider requires some additional user consent that is
+     * particular to the provider, this should be use to set the enabled/disabled state. If the
+     * provider proxies to another provider, the child provider's enabled/disabled state should be
+     * taken into account in the parent's enabled/disabled state. For most providers, it is expected
+     * that they will be always enabled.
      */
-    public final void reportLocation(Location location) {
-        try {
-            mLocationManager.reportLocation(location, false);
-        } catch (RemoteException e) {
-            Log.e(TAG, "RemoteException", e);
-        } catch (Exception e) {
-            // never crash provider, might be running in a system process
-            Log.e(TAG, "Exception", e);
+    public void setEnabled(boolean enabled) {
+        synchronized (mBinder) {
+            if (mEnabled == enabled) {
+                return;
+            }
+
+            mEnabled = enabled;
+        }
+
+        ILocationProviderManager manager = mManager;
+        if (manager != null) {
+            try {
+                manager.onSetEnabled(mEnabled);
+            } catch (RemoteException | RuntimeException e) {
+                Log.w(mTag, e);
+            }
         }
     }
 
     /**
-     * Enable the location provider.
-     * <p>The provider may initialize resources, but does
-     * not yet need to report locations.
+     * Sets the provider properties that may be queried by clients. Generally speaking, providers
+     * should try to avoid changing their properties after construction.
      */
-    public abstract void onEnable();
+    public void setProperties(ProviderPropertiesUnbundled properties) {
+        synchronized (mBinder) {
+            mProperties = properties.getProviderProperties();
+        }
+
+        ILocationProviderManager manager = mManager;
+        if (manager != null) {
+            try {
+                manager.onSetProperties(mProperties);
+            } catch (RemoteException | RuntimeException e) {
+                Log.w(mTag, e);
+            }
+        }
+    }
 
     /**
-     * Disable the location provider.
-     * <p>The provider must release resources, and stop
-     * performing work. It may no longer report locations.
+     * Returns true if this provider has been set as enabled. This will be true unless explicitly
+     * set otherwise.
      */
-    public abstract void onDisable();
+    public boolean isEnabled() {
+        return mEnabled;
+    }
 
     /**
-     * Set the {@link ProviderRequest} requirements for this provider.
-     * <p>Each call to this method overrides all previous requests.
-     * <p>This method might trigger the provider to start returning
-     * locations, or to stop returning locations, depending on the
-     * parameters in the request.
+     * Reports a new location from this provider.
      */
-    public abstract void onSetRequest(ProviderRequestUnbundled request, WorkSource source);
+    public void reportLocation(Location location) {
+        ILocationProviderManager manager = mManager;
+        if (manager != null) {
+            try {
+                manager.onReportLocation(location);
+            } catch (RemoteException | RuntimeException e) {
+                Log.w(mTag, e);
+            }
+        }
+    }
+
+    protected void onInit() {
+        // call once so that providers designed for APIs pre-Q are not broken
+        onEnable();
+    }
+
+    /**
+     * @deprecated This callback will be invoked once when the provider is created to maintain
+     * backwards compatibility with providers not designed for Android Q and above. This method
+     * should only be implemented in location providers that need to support SDKs below Android Q.
+     * Even in this case, it is usually unnecessary to implement this callback with the correct
+     * design. This method may be removed in the future.
+     */
+    @Deprecated
+    protected void onEnable() {}
+
+    /**
+     * @deprecated This callback will be never be invoked on Android Q and above. This method should
+     * only be implemented in location providers that need to support SDKs below Android Q. Even in
+     * this case, it is usually unnecessary to implement this callback with the correct design. This
+     * method may be removed in the future.
+     */
+    @Deprecated
+    protected void onDisable() {}
+
+    /**
+     * Set the {@link ProviderRequest} requirements for this provider. Each call to this method
+     * overrides all previous requests. This method might trigger the provider to start returning
+     * locations, or to stop returning locations, depending on the parameters in the request.
+     */
+    protected abstract void onSetRequest(ProviderRequestUnbundled request, WorkSource source);
 
     /**
      * Dump debug information.
      */
-    public void onDump(FileDescriptor fd, PrintWriter pw, String[] args) {
-    }
+    protected void onDump(FileDescriptor fd, PrintWriter pw, String[] args) {}
 
     /**
      * This method will no longer be invoked.
@@ -187,10 +231,12 @@
      * <p>If extras is non-null, additional status information may be
      * added to it in the form of provider-specific key/value pairs.
      *
-     * @deprecated This method will no longer be invoked.
+     * @deprecated This callback will be never be invoked on Android Q and above. This method should
+     * only be implemented in location providers that need to support SDKs below Android Q. This
+     * method may be removed in the future.
      */
     @Deprecated
-    public int onGetStatus(Bundle extras) {
+    protected int onGetStatus(Bundle extras) {
         return LocationProvider.AVAILABLE;
     }
 
@@ -206,24 +252,64 @@
      *
      * @return time of last status update in millis since last reboot
      *
-     * @deprecated This method will no longer be invoked.
+     * @deprecated This callback will be never be invoked on Android Q and above. This method should
+     * only be implemented in location providers that need to support SDKs below Android Q. This
+     * method may be removed in the future.
      */
     @Deprecated
-    public long onGetStatusUpdateTime() {
+    protected long onGetStatusUpdateTime() {
         return 0;
     }
 
     /**
-     * Implements addditional location provider specific additional commands.
-     *
-     * @param command name of the command to send to the provider.
-     * @param extras optional arguments for the command (or null).
-     * The provider may optionally fill the extras Bundle with results from the command.
-     *
-     * @return true if the command succeeds.
+     * Implements location provider specific custom commands. The return value will be ignored on
+     * Android Q and above.
      */
-    public boolean onSendExtraCommand(String command, Bundle extras) {
-        // default implementation
+    protected boolean onSendExtraCommand(@Nullable String command, @Nullable Bundle extras) {
         return false;
     }
+
+    private final class Service extends ILocationProvider.Stub {
+
+        @Override
+        public void setLocationProviderManager(ILocationProviderManager manager) {
+            synchronized (mBinder) {
+                try {
+                    manager.onSetProperties(mProperties);
+                    manager.onSetEnabled(mEnabled);
+                } catch (RemoteException e) {
+                    Log.w(mTag, e);
+                }
+
+                mManager = manager;
+            }
+
+            onInit();
+        }
+
+        @Override
+        public void setRequest(ProviderRequest request, WorkSource ws) {
+            onSetRequest(new ProviderRequestUnbundled(request), ws);
+        }
+
+        @Override
+        public int getStatus(Bundle extras) {
+            return onGetStatus(extras);
+        }
+
+        @Override
+        public long getStatusUpdateTime() {
+            return onGetStatusUpdateTime();
+        }
+
+        @Override
+        public void sendExtraCommand(String command, Bundle extras) {
+            onSendExtraCommand(command, extras);
+        }
+
+        @Override
+        public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+            onDump(fd, pw, args);
+        }
+    }
 }
diff --git a/location/lib/java/com/android/location/provider/ProviderRequestUnbundled.java b/location/lib/java/com/android/location/provider/ProviderRequestUnbundled.java
index 6a8e618..b825b58 100644
--- a/location/lib/java/com/android/location/provider/ProviderRequestUnbundled.java
+++ b/location/lib/java/com/android/location/provider/ProviderRequestUnbundled.java
@@ -16,13 +16,13 @@
 
 package com.android.location.provider;
 
-import java.util.ArrayList;
-import java.util.List;
-
 import android.location.LocationRequest;
 
 import com.android.internal.location.ProviderRequest;
 
+import java.util.ArrayList;
+import java.util.List;
+
 /**
  * This class is an interface to Provider Requests for unbundled applications.
  *
@@ -46,6 +46,10 @@
         return mRequest.interval;
     }
 
+    public boolean getForceLocation() {
+        return mRequest.forceLocation;
+    }
+
     /**
      * Never null.
      */
diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java
index 24d2725..793aa27 100644
--- a/media/java/android/media/AudioFormat.java
+++ b/media/java/android/media/AudioFormat.java
@@ -213,6 +213,8 @@
  * (e.g.{@link AudioTrack#getPlaybackHeadPosition()
  * AudioTrack.getPlaybackHeadPosition()}),
  * depending on the context where audio frame is used.
+ * For the purposes of {@link AudioFormat#getFrameSizeInBytes()}, a compressed data format
+ * returns a frame size of 1 byte.
  */
 public final class AudioFormat implements Parcelable {
 
@@ -707,6 +709,16 @@
             channelCount = 0; // position and index channel count mismatch
         }
         mChannelCount = channelCount;
+
+        int frameSizeInBytes = 1;
+        try {
+            frameSizeInBytes = getBytesPerSample(mEncoding) * channelCount;
+        } catch (IllegalArgumentException iae) {
+            // ignored
+        }
+        // it is possible that channel count is 0, so ensure we return 1 for
+        // mFrameSizeInBytes for consistency.
+        mFrameSizeInBytes = frameSizeInBytes != 0 ? frameSizeInBytes : 1;
     }
 
     /** @hide */
@@ -734,6 +746,7 @@
 
     // Derived values computed in the constructor, cached here.
     private final int mChannelCount;
+    private final int mFrameSizeInBytes;
 
     /**
      * Return the encoding.
@@ -788,6 +801,25 @@
         return mChannelCount;
     }
 
+    /**
+     * Return the frame size in bytes.
+     *
+     * For PCM or PCM packed compressed data this is the size of a sample multiplied
+     * by the channel count. For all other cases, including invalid/unset channel masks,
+     * this will return 1 byte.
+     * As an example, a stereo 16-bit PCM format would have a frame size of 4 bytes,
+     * an 8 channel float PCM format would have a frame size of 32 bytes,
+     * and a compressed data format (not packed in PCM) would have a frame size of 1 byte.
+     *
+     * Both {@link AudioRecord} or {@link AudioTrack} process data in multiples of
+     * this frame size.
+     *
+     * @return The audio frame size in bytes corresponding to the encoding and the channel mask.
+     */
+    public int getFrameSizeInBytes() {
+        return mFrameSizeInBytes;
+    }
+
     /** @hide */
     public int getPropertySetMask() {
         return mPropertySetMask;
diff --git a/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java b/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java
index 87d6e4a..be817d6 100644
--- a/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java
+++ b/packages/FusedLocation/src/com/android/location/fused/FusedLocationProvider.java
@@ -23,7 +23,6 @@
 import android.location.Criteria;
 import android.os.Handler;
 import android.os.Looper;
-import android.os.Message;
 import android.os.UserHandle;
 import android.os.WorkSource;
 
@@ -34,87 +33,53 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 
-public class FusedLocationProvider extends LocationProviderBase implements FusionEngine.Callback {
+class FusedLocationProvider extends LocationProviderBase implements FusionEngine.Callback {
     private static final String TAG = "FusedLocationProvider";
 
     private static ProviderPropertiesUnbundled PROPERTIES = ProviderPropertiesUnbundled.create(
             false, false, false, false, true, true, true, Criteria.POWER_LOW,
             Criteria.ACCURACY_FINE);
 
-    private static final int MSG_ENABLE = 1;
-    private static final int MSG_DISABLE = 2;
-    private static final int MSG_SET_REQUEST = 3;
-
+    private final Context mContext;
+    private final Handler mHandler;
     private final FusionEngine mEngine;
 
-    private static class RequestWrapper {
-        public ProviderRequestUnbundled request;
-        public WorkSource source;
-        public RequestWrapper(ProviderRequestUnbundled request, WorkSource source) {
-            this.request = request;
-            this.source = source;
-        }
-    }
-
-    public FusedLocationProvider(Context context) {
-        super(TAG, PROPERTIES);
-        mEngine = new FusionEngine(context, Looper.myLooper());
-
-        // listen for user change
-        IntentFilter intentFilter = new IntentFilter();
-        intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
-        context.registerReceiverAsUser(new BroadcastReceiver() {
-            @Override
-            public void onReceive(Context context, Intent intent) {
-                String action = intent.getAction();
-                if (Intent.ACTION_USER_SWITCHED.equals(action)) {
-                    mEngine.switchUser();
-                }
-            }
-        }, UserHandle.ALL, intentFilter, null, mHandler);
-    }
-
-    /**
-     * For serializing requests to mEngine.
-     */
-    private Handler mHandler = new Handler() {
+    private final BroadcastReceiver mUserSwitchReceiver = new BroadcastReceiver() {
         @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case MSG_ENABLE:
-                    mEngine.init(FusedLocationProvider.this);
-                    break;
-                case MSG_DISABLE:
-                    mEngine.deinit();
-                    break;
-                case MSG_SET_REQUEST:
-                    {
-                        RequestWrapper wrapper = (RequestWrapper) msg.obj;
-                        mEngine.setRequest(wrapper.request, wrapper.source);
-                        break;
-                    }
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (Intent.ACTION_USER_SWITCHED.equals(action)) {
+                mEngine.switchUser();
             }
         }
     };
 
-    @Override
-    public void onEnable() {
-        mHandler.sendEmptyMessage(MSG_ENABLE);
+    FusedLocationProvider(Context context) {
+        super(TAG, PROPERTIES);
+
+        mContext = context;
+        mHandler = new Handler(Looper.myLooper());
+        mEngine = new FusionEngine(context, Looper.myLooper(), this);
     }
 
-    @Override
-    public void onDisable() {
-        mHandler.sendEmptyMessage(MSG_DISABLE);
+    void init() {
+        // listen for user change
+        mContext.registerReceiverAsUser(mUserSwitchReceiver, UserHandle.ALL,
+                new IntentFilter(Intent.ACTION_USER_SWITCHED), null, mHandler);
+    }
+
+    void destroy() {
+        mContext.unregisterReceiver(mUserSwitchReceiver);
+        mHandler.post(() -> mEngine.setRequest(null));
     }
 
     @Override
     public void onSetRequest(ProviderRequestUnbundled request, WorkSource source) {
-        mHandler.obtainMessage(MSG_SET_REQUEST, new RequestWrapper(request, source)).sendToTarget();
+        mHandler.post(() -> mEngine.setRequest(request));
     }
 
     @Override
     public void onDump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        // perform synchronously
         mEngine.dump(fd, pw, args);
     }
 }
diff --git a/packages/FusedLocation/src/com/android/location/fused/FusedLocationService.java b/packages/FusedLocation/src/com/android/location/fused/FusedLocationService.java
index 12966cf..75bb5ec 100644
--- a/packages/FusedLocation/src/com/android/location/fused/FusedLocationService.java
+++ b/packages/FusedLocation/src/com/android/location/fused/FusedLocationService.java
@@ -21,27 +21,24 @@
 import android.os.IBinder;
 
 public class FusedLocationService extends Service {
+
     private FusedLocationProvider mProvider;
 
     @Override
     public IBinder onBind(Intent intent) {
         if (mProvider == null) {
-            mProvider = new FusedLocationProvider(getApplicationContext());
+            mProvider = new FusedLocationProvider(this);
+            mProvider.init();
         }
+
         return mProvider.getBinder();
     }
 
     @Override
-    public boolean onUnbind(Intent intent) {
-        // make sure to stop performing work
-        if (mProvider != null) {
-            mProvider.onDisable();
-        }
-      return false;
-    }
-
-    @Override
     public void onDestroy() {
-        mProvider = null;
+        if (mProvider != null) {
+            mProvider.destroy();
+            mProvider = null;
+        }
     }
 }
diff --git a/packages/FusedLocation/src/com/android/location/fused/FusionEngine.java b/packages/FusedLocation/src/com/android/location/fused/FusionEngine.java
index 7a49524..e4610cf 100644
--- a/packages/FusedLocation/src/com/android/location/fused/FusionEngine.java
+++ b/packages/FusedLocation/src/com/android/location/fused/FusionEngine.java
@@ -16,14 +16,6 @@
 
 package com.android.location.fused;
 
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-import java.util.HashMap;
-
-import com.android.location.provider.LocationProviderBase;
-import com.android.location.provider.LocationRequestUnbundled;
-import com.android.location.provider.ProviderRequestUnbundled;
-
 import android.content.Context;
 import android.location.Location;
 import android.location.LocationListener;
@@ -32,9 +24,16 @@
 import android.os.Looper;
 import android.os.Parcelable;
 import android.os.SystemClock;
-import android.os.WorkSource;
 import android.util.Log;
 
+import com.android.location.provider.LocationProviderBase;
+import com.android.location.provider.LocationRequestUnbundled;
+import com.android.location.provider.ProviderRequestUnbundled;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.HashMap;
+
 public class FusionEngine implements LocationListener {
     public interface Callback {
         void reportLocation(Location location);
@@ -47,72 +46,35 @@
 
     public static final long SWITCH_ON_FRESHNESS_CLIFF_NS = 11 * 1000000000L; // 11 seconds
 
-    private final Context mContext;
     private final LocationManager mLocationManager;
     private final Looper mLooper;
+    private final Callback mCallback;
 
     // all fields are only used on mLooper thread. except for in dump() which is not thread-safe
-    private Callback mCallback;
     private Location mFusedLocation;
     private Location mGpsLocation;
     private Location mNetworkLocation;
 
-    private boolean mEnabled;
     private ProviderRequestUnbundled mRequest;
 
     private final HashMap<String, ProviderStats> mStats = new HashMap<>();
 
-    public FusionEngine(Context context, Looper looper) {
-        mContext = context;
+    FusionEngine(Context context, Looper looper, Callback callback) {
         mLocationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
         mNetworkLocation = new Location("");
         mNetworkLocation.setAccuracy(Float.MAX_VALUE);
         mGpsLocation = new Location("");
         mGpsLocation.setAccuracy(Float.MAX_VALUE);
         mLooper = looper;
+        mCallback = callback;
 
         mStats.put(GPS, new ProviderStats());
         mStats.put(NETWORK, new ProviderStats());
-
-    }
-
-    public void init(Callback callback) {
-        Log.i(TAG, "engine started (" + mContext.getPackageName() + ")");
-        mCallback = callback;
-    }
-
-    /**
-     * Called to stop doing any work, and release all resources
-     * This can happen when a better fusion engine is installed
-     * in a different package, and this one is no longer needed.
-     * Called on mLooper thread
-     */
-    public void deinit() {
-        mRequest = null;
-        disable();
-        Log.i(TAG, "engine stopped (" + mContext.getPackageName() + ")");
     }
 
     /** Called on mLooper thread */
-    public void enable() {
-        if (!mEnabled) {
-            mEnabled = true;
-            updateRequirements();
-        }
-    }
-
-    /** Called on mLooper thread */
-    public void disable() {
-        if (mEnabled) {
-            mEnabled = false;
-            updateRequirements();
-        }
-    }
-
-    /** Called on mLooper thread */
-    public void setRequest(ProviderRequestUnbundled request, WorkSource source) {
+    public void setRequest(ProviderRequestUnbundled request) {
         mRequest = request;
-        mEnabled = request.getReportLocation();
         updateRequirements();
     }
 
@@ -120,6 +82,7 @@
         public boolean requested;
         public long requestTime;
         public long minTime;
+
         @Override
         public String toString() {
             return (requested ? " REQUESTED" : " ---");
@@ -154,7 +117,7 @@
     }
 
     private void updateRequirements() {
-        if (!mEnabled || mRequest == null) {
+        if (mRequest == null || !mRequest.getReportLocation()) {
             mRequest = null;
             disableProvider(NETWORK);
             disableProvider(GPS);
@@ -200,29 +163,30 @@
      * Test whether one location (a) is better to use than another (b).
      */
     private static boolean isBetterThan(Location locationA, Location locationB) {
-      if (locationA == null) {
-        return false;
-      }
-      if (locationB == null) {
-        return true;
-      }
-      // A provider is better if the reading is sufficiently newer.  Heading
-      // underground can cause GPS to stop reporting fixes.  In this case it's
-      // appropriate to revert to cell, even when its accuracy is less.
-      if (locationA.getElapsedRealtimeNanos() > locationB.getElapsedRealtimeNanos() + SWITCH_ON_FRESHNESS_CLIFF_NS) {
-        return true;
-      }
+        if (locationA == null) {
+            return false;
+        }
+        if (locationB == null) {
+            return true;
+        }
+        // A provider is better if the reading is sufficiently newer.  Heading
+        // underground can cause GPS to stop reporting fixes.  In this case it's
+        // appropriate to revert to cell, even when its accuracy is less.
+        if (locationA.getElapsedRealtimeNanos()
+                > locationB.getElapsedRealtimeNanos() + SWITCH_ON_FRESHNESS_CLIFF_NS) {
+            return true;
+        }
 
-      // A provider is better if it has better accuracy.  Assuming both readings
-      // are fresh (and by that accurate), choose the one with the smaller
-      // accuracy circle.
-      if (!locationA.hasAccuracy()) {
-        return false;
-      }
-      if (!locationB.hasAccuracy()) {
-        return true;
-      }
-      return locationA.getAccuracy() < locationB.getAccuracy();
+        // A provider is better if it has better accuracy.  Assuming both readings
+        // are fresh (and by that accurate), choose the one with the smaller
+        // accuracy circle.
+        if (!locationA.hasAccuracy()) {
+            return false;
+        }
+        if (!locationB.hasAccuracy()) {
+            return true;
+        }
+        return locationA.getAccuracy() < locationB.getAccuracy();
     }
 
     private void updateFusedLocation() {
@@ -252,9 +216,9 @@
         }
 
         if (mCallback != null) {
-          mCallback.reportLocation(mFusedLocation);
+            mCallback.reportLocation(mFusedLocation);
         } else {
-          Log.w(TAG, "Location updates received while fusion engine not started");
+            Log.w(TAG, "Location updates received while fusion engine not started");
         }
     }
 
@@ -272,19 +236,22 @@
 
     /** Called on mLooper thread */
     @Override
-    public void onStatusChanged(String provider, int status, Bundle extras) {  }
+    public void onStatusChanged(String provider, int status, Bundle extras) {
+    }
 
     /** Called on mLooper thread */
     @Override
-    public void onProviderEnabled(String provider) {  }
+    public void onProviderEnabled(String provider) {
+    }
 
     /** Called on mLooper thread */
     @Override
-    public void onProviderDisabled(String provider) {  }
+    public void onProviderDisabled(String provider) {
+    }
 
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         StringBuilder s = new StringBuilder();
-        s.append("mEnabled=").append(mEnabled).append(' ').append(mRequest).append('\n');
+        s.append(mRequest).append('\n');
         s.append("fused=").append(mFusedLocation).append('\n');
         s.append(String.format("gps %s\n", mGpsLocation));
         s.append("    ").append(mStats.get(GPS)).append('\n');
diff --git a/packages/SettingsLib/ActionButtonsPreference/Android.bp b/packages/SettingsLib/ActionButtonsPreference/Android.bp
index cd3fb0c..e518e0b 100644
--- a/packages/SettingsLib/ActionButtonsPreference/Android.bp
+++ b/packages/SettingsLib/ActionButtonsPreference/Android.bp
@@ -1,5 +1,5 @@
 android_library {
-    name: "SettingsLibActionButtonsPreference",
+    name: "ActionButtonsPreference",
 
     srcs: ["src/**/*.java"],
     resource_dirs: ["res"],
diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp
index e588e921..cc17b25 100644
--- a/packages/SettingsLib/Android.bp
+++ b/packages/SettingsLib/Android.bp
@@ -16,8 +16,8 @@
         "SettingsLibAppPreference",
         "SettingsLibSearchWidget",
         "SettingsLibSettingsSpinner",
-        "SettingsLibLayoutPreference",
-        "SettingsLibActionButtonsPreference",
+        "SettingsLayoutPreference",
+        "ActionButtonsPreference",
     ],
 
     // ANDROIDMK TRANSLATION ERROR: unsupported assignment to LOCAL_SHARED_JAVA_LIBRARIES
diff --git a/packages/SettingsLib/LayoutPreference/Android.bp b/packages/SettingsLib/SettingsLayoutPreference/Android.bp
similarity index 83%
rename from packages/SettingsLib/LayoutPreference/Android.bp
rename to packages/SettingsLib/SettingsLayoutPreference/Android.bp
index a1f9a76..489d360 100644
--- a/packages/SettingsLib/LayoutPreference/Android.bp
+++ b/packages/SettingsLib/SettingsLayoutPreference/Android.bp
@@ -1,5 +1,5 @@
 android_library {
-    name: "SettingsLibLayoutPreference",
+    name: "SettingsLayoutPreference",
 
     srcs: ["src/**/*.java"],
     resource_dirs: ["res"],
diff --git a/packages/SettingsLib/LayoutPreference/AndroidManifest.xml b/packages/SettingsLib/SettingsLayoutPreference/AndroidManifest.xml
similarity index 100%
rename from packages/SettingsLib/LayoutPreference/AndroidManifest.xml
rename to packages/SettingsLib/SettingsLayoutPreference/AndroidManifest.xml
diff --git a/packages/SettingsLib/LayoutPreference/res/layout/layout_preference_frame.xml b/packages/SettingsLib/SettingsLayoutPreference/res/layout/layout_preference_frame.xml
similarity index 100%
rename from packages/SettingsLib/LayoutPreference/res/layout/layout_preference_frame.xml
rename to packages/SettingsLib/SettingsLayoutPreference/res/layout/layout_preference_frame.xml
diff --git a/packages/SettingsLib/LayoutPreference/res/layout/settings_entity_header.xml b/packages/SettingsLib/SettingsLayoutPreference/res/layout/settings_entity_header.xml
similarity index 100%
rename from packages/SettingsLib/LayoutPreference/res/layout/settings_entity_header.xml
rename to packages/SettingsLib/SettingsLayoutPreference/res/layout/settings_entity_header.xml
diff --git a/packages/SettingsLib/LayoutPreference/res/values/styles.xml b/packages/SettingsLib/SettingsLayoutPreference/res/values/styles.xml
similarity index 100%
rename from packages/SettingsLib/LayoutPreference/res/values/styles.xml
rename to packages/SettingsLib/SettingsLayoutPreference/res/values/styles.xml
diff --git a/packages/SettingsLib/LayoutPreference/src/com/android/settingslib/widget/LayoutPreference.java b/packages/SettingsLib/SettingsLayoutPreference/src/com/android/settingslib/widget/LayoutPreference.java
similarity index 100%
rename from packages/SettingsLib/LayoutPreference/src/com/android/settingslib/widget/LayoutPreference.java
rename to packages/SettingsLib/SettingsLayoutPreference/src/com/android/settingslib/widget/LayoutPreference.java
diff --git a/proto/src/wifi.proto b/proto/src/wifi.proto
index 1fda074..e9ce737 100644
--- a/proto/src/wifi.proto
+++ b/proto/src/wifi.proto
@@ -1344,6 +1344,27 @@
 
   // Amount of time wifi is in tx (ms)
   optional int64 tx_time_ms = 5;
+
+  // Amount of time kernel is active because of wifi data (ms)
+  optional int64 wifi_kernel_active_time_ms = 6;
+
+  // Number of packets sent (tx)
+  optional int64 num_packets_tx = 7;
+
+  // Number of bytes sent (tx)
+  optional int64 num_bytes_tx = 8;
+
+  // Number of packets received (rx)
+  optional int64 num_packets_rx = 9;
+
+  // Number of bytes sent (rx)
+  optional int64 num_bytes_rx = 10;
+
+  // Amount of time wifi is in sleep (ms)
+  optional int64 sleep_time_ms = 11;
+
+  // Amount of time wifi is scanning (ms)
+  optional int64 scan_time_ms = 12;
 }
 
 // Metrics for Wifi Wake
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index d76a5df..aef16b1 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -1238,7 +1238,15 @@
             return;
         }
 
-        final UserData userData = mService.getUserData();
+        final UserData packageUserData = lastResponse.getUserData();
+
+        final UserData userData;
+        if (packageUserData != null) {
+            // Replace default userData
+            userData = packageUserData;
+        } else {
+            userData = mService.getUserData();
+        }
 
         for (int i = 0; i < mViewStates.size(); i++) {
             final ViewState viewState = mViewStates.valueAt(i);
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index da19e27..8b992eb 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -17,8 +17,11 @@
 package com.android.server;
 
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.location.LocationProvider.AVAILABLE;
 import static android.provider.Settings.Global.LOCATION_DISABLE_STATUS_CALLBACKS;
 
+import static com.android.internal.util.Preconditions.checkState;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.ActivityManager;
@@ -53,7 +56,6 @@
 import android.location.INetInitiatedListener;
 import android.location.Location;
 import android.location.LocationManager;
-import android.location.LocationProvider;
 import android.location.LocationRequest;
 import android.os.Binder;
 import android.os.Bundle;
@@ -83,6 +85,7 @@
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.DumpUtils;
+import com.android.server.location.AbstractLocationProvider;
 import com.android.server.location.ActivityRecognitionProxy;
 import com.android.server.location.GeocoderProxy;
 import com.android.server.location.GeofenceManager;
@@ -94,7 +97,6 @@
 import com.android.server.location.GnssStatusListenerHelper;
 import com.android.server.location.LocationBlacklist;
 import com.android.server.location.LocationFudger;
-import com.android.server.location.LocationProviderInterface;
 import com.android.server.location.LocationProviderProxy;
 import com.android.server.location.LocationRequestStatistics;
 import com.android.server.location.LocationRequestStatistics.PackageProviderKey;
@@ -131,8 +133,6 @@
     // Location resolution level: fine location data
     private static final int RESOLUTION_LEVEL_FINE = 2;
 
-    private static final String ACCESS_MOCK_LOCATION =
-            android.Manifest.permission.ACCESS_MOCK_LOCATION;
     private static final String ACCESS_LOCATION_EXTRA_COMMANDS =
             android.Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS;
     private static final String INSTALL_LOCATION_PROVIDER =
@@ -192,12 +192,6 @@
     private IGpsGeofenceHardware mGpsGeofenceProxy;
 
     // --- fields below are protected by mLock ---
-    // Set of providers that are explicitly enabled
-    // Only used by passive, fused & test.  Network & GPS are controlled separately, and not listed.
-    private final Set<String> mEnabledProviders = new HashSet<>();
-
-    // Set of providers that are explicitly disabled
-    private final Set<String> mDisabledProviders = new HashSet<>();
 
     // Mock (test) providers
     private final HashMap<String, MockProvider> mMockProviders =
@@ -207,15 +201,15 @@
     private final HashMap<Object, Receiver> mReceivers = new HashMap<>();
 
     // currently installed providers (with mocks replacing real providers)
-    private final ArrayList<LocationProviderInterface> mProviders =
+    private final ArrayList<LocationProvider> mProviders =
             new ArrayList<>();
 
     // real providers, saved here when mocked out
-    private final HashMap<String, LocationProviderInterface> mRealProviders =
+    private final HashMap<String, LocationProvider> mRealProviders =
             new HashMap<>();
 
     // mapping from provider name to provider
-    private final HashMap<String, LocationProviderInterface> mProvidersByName =
+    private final HashMap<String, LocationProvider> mProvidersByName =
             new HashMap<>();
 
     // mapping from provider name to all its UpdateRecords
@@ -270,13 +264,8 @@
         PackageManagerInternal packageManagerInternal = LocalServices.getService(
                 PackageManagerInternal.class);
         packageManagerInternal.setLocationPackagesProvider(
-                new PackageManagerInternal.PackagesProvider() {
-                    @Override
-                    public String[] getPackages(int userId) {
-                        return mContext.getResources().getStringArray(
-                                com.android.internal.R.array.config_locationProviderPackageNames);
-                    }
-                });
+                userId -> mContext.getResources().getStringArray(
+                        com.android.internal.R.array.config_locationProviderPackageNames));
 
         if (D) Log.d(TAG, "Constructed");
 
@@ -321,30 +310,17 @@
             mAppOps.startWatchingMode(AppOpsManager.OP_COARSE_LOCATION, null,
                     AppOpsManager.WATCH_FOREGROUND_CHANGES, callback);
 
-            PackageManager.OnPermissionsChangedListener permissionListener
-                    = new PackageManager.OnPermissionsChangedListener() {
-                @Override
-                public void onPermissionsChanged(final int uid) {
-                    synchronized (mLock) {
-                        applyAllProviderRequirementsLocked();
-                    }
+            PackageManager.OnPermissionsChangedListener permissionListener = uid -> {
+                synchronized (mLock) {
+                    applyAllProviderRequirementsLocked();
                 }
             };
             mPackageManager.addOnPermissionsChangeListener(permissionListener);
 
             // listen for background/foreground changes
-            ActivityManager.OnUidImportanceListener uidImportanceListener
-                    = new ActivityManager.OnUidImportanceListener() {
-                @Override
-                public void onUidImportance(final int uid, final int importance) {
-                    mLocationHandler.post(new Runnable() {
-                        @Override
-                        public void run() {
-                            onUidImportanceChanged(uid, importance);
-                        }
-                    });
-                }
-            };
+            ActivityManager.OnUidImportanceListener uidImportanceListener =
+                    (uid, importance) -> mLocationHandler.post(
+                            () -> onUidImportanceChanged(uid, importance));
             mActivityManager.addOnUidImportanceListener(uidImportanceListener,
                     FOREGROUND_IMPORTANCE_CUTOFF);
 
@@ -356,7 +332,10 @@
 
             // prepare providers
             loadProvidersLocked();
-            updateProvidersLocked();
+            updateProvidersSettingsLocked();
+            for (LocationProvider provider : mProviders) {
+                applyRequirementsLocked(provider.getName());
+            }
         }
 
         // listen for settings changes
@@ -366,7 +345,7 @@
                     @Override
                     public void onChange(boolean selfChange) {
                         synchronized (mLock) {
-                            updateProvidersLocked();
+                            updateProvidersSettingsLocked();
                         }
                     }
                 }, UserHandle.USER_ALL);
@@ -377,7 +356,9 @@
                     @Override
                     public void onChange(boolean selfChange) {
                         synchronized (mLock) {
-                            updateProvidersLocked();
+                            for (LocationProvider provider : mProviders) {
+                                applyRequirementsLocked(provider.getName());
+                            }
                         }
                     }
                 }, UserHandle.USER_ALL);
@@ -402,7 +383,9 @@
                     public void onChange(boolean selfChange) {
                         synchronized (mLock) {
                             updateBackgroundThrottlingWhitelistLocked();
-                            updateProvidersLocked();
+                            for (LocationProvider provider : mProviders) {
+                                applyRequirementsLocked(provider.getName());
+                            }
                         }
                     }
                 }, UserHandle.USER_ALL);
@@ -414,7 +397,6 @@
         intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
         intentFilter.addAction(Intent.ACTION_MANAGED_PROFILE_ADDED);
         intentFilter.addAction(Intent.ACTION_MANAGED_PROFILE_REMOVED);
-        intentFilter.addAction(Intent.ACTION_SHUTDOWN);
 
         mContext.registerReceiverAsUser(new BroadcastReceiver() {
             @Override
@@ -425,12 +407,6 @@
                 } else if (Intent.ACTION_MANAGED_PROFILE_ADDED.equals(action)
                         || Intent.ACTION_MANAGED_PROFILE_REMOVED.equals(action)) {
                     updateUserProfiles(mCurrentUserId);
-                } else if (Intent.ACTION_SHUTDOWN.equals(action)) {
-                    // shutdown only if UserId indicates whole system, not just one user
-                    if (D) Log.d(TAG, "Shutdown received with UserId: " + getSendingUserId());
-                    if (getSendingUserId() == UserHandle.USER_ALL) {
-                        shutdownComponents();
-                    }
                 }
             }
         }, UserHandle.ALL, intentFilter, null, mLocationHandler);
@@ -508,30 +484,13 @@
     }
 
     /**
-     * Provides a way for components held by the {@link LocationManagerService} to clean-up
-     * gracefully on system's shutdown.
-     *
-     * NOTES:
-     * 1) Only provides a chance to clean-up on an opt-in basis. This guarantees back-compat
-     * support for components that do not wish to handle such event.
-     */
-    private void shutdownComponents() {
-        if (D) Log.d(TAG, "Shutting down components...");
-
-        LocationProviderInterface gpsProvider = mProvidersByName.get(LocationManager.GPS_PROVIDER);
-        if (gpsProvider != null && gpsProvider.isEnabled()) {
-            gpsProvider.disable();
-        }
-    }
-
-    /**
      * Makes a list of userids that are related to the current user. This is
      * relevant when using managed profiles. Otherwise the list only contains
      * the current user.
      *
      * @param currentUserId the current user, who might have an alter-ego.
      */
-    void updateUserProfiles(int currentUserId) {
+    private void updateUserProfiles(int currentUserId) {
         int[] profileIds = mUserManager.getProfileIdsWithDisabled(currentUserId);
         synchronized (mLock) {
             mCurrentUserProfiles = profileIds;
@@ -620,22 +579,28 @@
 
     private void loadProvidersLocked() {
         // create a passive location provider, which is always enabled
-        PassiveProvider passiveProvider = new PassiveProvider(this);
-        addProviderLocked(passiveProvider);
-        mEnabledProviders.add(passiveProvider.getName());
+        LocationProvider passiveProviderManager = new LocationProvider(
+                LocationManager.PASSIVE_PROVIDER);
+        PassiveProvider passiveProvider = new PassiveProvider(passiveProviderManager);
+
+        addProviderLocked(passiveProviderManager);
         mPassiveProvider = passiveProvider;
 
         if (GnssLocationProvider.isSupported()) {
             // Create a gps location provider
-            GnssLocationProvider gnssProvider = new GnssLocationProvider(mContext, this,
+            LocationProvider gnssProviderManager = new LocationProvider(
+                    LocationManager.GPS_PROVIDER);
+            GnssLocationProvider gnssProvider = new GnssLocationProvider(mContext,
+                    gnssProviderManager,
                     mLocationHandler.getLooper());
+
             mGnssSystemInfoProvider = gnssProvider.getGnssSystemInfoProvider();
             mGnssBatchingProvider = gnssProvider.getGnssBatchingProvider();
             mGnssMetricsProvider = gnssProvider.getGnssMetricsProvider();
             mGnssStatusProvider = gnssProvider.getGnssStatusProvider();
             mNetInitiatedListener = gnssProvider.getNetInitiatedListener();
-            addProviderLocked(gnssProvider);
-            mRealProviders.put(LocationManager.GPS_PROVIDER, gnssProvider);
+            addProviderLocked(gnssProviderManager);
+            mRealProviders.put(LocationManager.GPS_PROVIDER, gnssProviderManager);
             mGnssMeasurementsProvider = gnssProvider.getGnssMeasurementsProvider();
             mGnssNavigationMessageProvider = gnssProvider.getGnssNavigationMessageProvider();
             mGpsGeofenceProxy = gnssProvider.getGpsGeofenceProxy();
@@ -663,34 +628,38 @@
         ensureFallbackFusedProviderPresentLocked(pkgs);
 
         // bind to network provider
+
+        LocationProvider networkProviderManager = new LocationProvider(
+                LocationManager.NETWORK_PROVIDER);
         LocationProviderProxy networkProvider = LocationProviderProxy.createAndBind(
                 mContext,
-                LocationManager.NETWORK_PROVIDER,
+                networkProviderManager,
                 NETWORK_LOCATION_SERVICE_ACTION,
                 com.android.internal.R.bool.config_enableNetworkLocationOverlay,
                 com.android.internal.R.string.config_networkLocationProviderPackageName,
                 com.android.internal.R.array.config_locationProviderPackageNames);
         if (networkProvider != null) {
-            mRealProviders.put(LocationManager.NETWORK_PROVIDER, networkProvider);
+            mRealProviders.put(LocationManager.NETWORK_PROVIDER, networkProviderManager);
             mProxyProviders.add(networkProvider);
-            addProviderLocked(networkProvider);
+            addProviderLocked(networkProviderManager);
         } else {
             Slog.w(TAG, "no network location provider found");
         }
 
         // bind to fused provider
-        LocationProviderProxy fusedLocationProvider = LocationProviderProxy.createAndBind(
+        LocationProvider fusedProviderManager = new LocationProvider(
+                LocationManager.FUSED_PROVIDER);
+        LocationProviderProxy fusedProvider = LocationProviderProxy.createAndBind(
                 mContext,
-                LocationManager.FUSED_PROVIDER,
+                fusedProviderManager,
                 FUSED_LOCATION_SERVICE_ACTION,
                 com.android.internal.R.bool.config_enableFusedLocationOverlay,
                 com.android.internal.R.string.config_fusedLocationProviderPackageName,
                 com.android.internal.R.array.config_locationProviderPackageNames);
-        if (fusedLocationProvider != null) {
-            addProviderLocked(fusedLocationProvider);
-            mProxyProviders.add(fusedLocationProvider);
-            mEnabledProviders.add(fusedLocationProvider.getName());
-            mRealProviders.put(LocationManager.FUSED_PROVIDER, fusedLocationProvider);
+        if (fusedProvider != null) {
+            addProviderLocked(fusedProviderManager);
+            mProxyProviders.add(fusedProvider);
+            mRealProviders.put(LocationManager.FUSED_PROVIDER, fusedProviderManager);
         } else {
             Slog.e(TAG, "no fused location provider found",
                     new IllegalStateException("Location service needs a fused location provider"));
@@ -771,12 +740,9 @@
         synchronized (mLock) {
             mLastLocation.clear();
             mLastLocationCoarseInterval.clear();
-            for (LocationProviderInterface p : mProviders) {
-                updateProviderListenersLocked(p.getName(), false);
-            }
-            mCurrentUserId = userId;
             updateUserProfiles(userId);
-            updateProvidersLocked();
+            updateProvidersSettingsLocked();
+            mCurrentUserId = userId;
         }
     }
 
@@ -792,6 +758,165 @@
         }
     }
 
+    private class LocationProvider implements AbstractLocationProvider.LocationProviderManager {
+
+        private final String mName;
+        private AbstractLocationProvider mProvider;
+
+        // whether the provider is enabled in location settings
+        private boolean mSettingsEnabled;
+
+        // whether the provider considers itself enabled
+        private volatile boolean mEnabled;
+
+        @Nullable
+        private volatile ProviderProperties mProperties;
+
+        private LocationProvider(String name) {
+            mName = name;
+            // TODO: initialize settings enabled?
+        }
+
+        @Override
+        public void onAttachProvider(AbstractLocationProvider provider, boolean initiallyEnabled) {
+            checkState(mProvider == null);
+
+            // the provider is not yet fully constructed at this point, so we may not do anything
+            // except save a reference for later use here. do not call any provider methods.
+            mProvider = provider;
+            mEnabled = initiallyEnabled;
+            mProperties = null;
+        }
+
+        public String getName() {
+            return mName;
+        }
+
+        public boolean isEnabled() {
+            return mSettingsEnabled && mEnabled;
+        }
+
+        @Nullable
+        public ProviderProperties getProperties() {
+            return mProperties;
+        }
+
+        public void setRequest(ProviderRequest request, WorkSource workSource) {
+            mProvider.setRequest(request, workSource);
+        }
+
+        public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+            pw.println(mName + " provider:");
+            pw.println(" setting=" + mSettingsEnabled);
+            pw.println(" enabled=" + mEnabled);
+            pw.println(" properties=" + mProperties);
+            mProvider.dump(fd, pw, args);
+        }
+
+        public long getStatusUpdateTime() {
+            return mProvider.getStatusUpdateTime();
+        }
+
+        public int getStatus(Bundle extras) {
+            return mProvider.getStatus(extras);
+        }
+
+        public void sendExtraCommand(String command, Bundle extras) {
+            mProvider.sendExtraCommand(command, extras);
+        }
+
+        // called from any thread
+        @Override
+        public void onReportLocation(Location location) {
+            runOnHandler(() -> LocationManagerService.this.reportLocation(location,
+                    mProvider == mPassiveProvider));
+        }
+
+        // called from any thread
+        @Override
+        public void onReportLocation(List<Location> locations) {
+            runOnHandler(() -> LocationManagerService.this.reportLocationBatch(locations));
+        }
+
+        // called from any thread
+        @Override
+        public void onSetEnabled(boolean enabled) {
+            runOnHandler(() -> {
+                if (enabled == mEnabled) {
+                    return;
+                }
+
+                mEnabled = enabled;
+
+                if (!mSettingsEnabled) {
+                    // this provider was disabled in settings anyways, so a change to it's own
+                    // enabled status won't have any affect.
+                    return;
+                }
+
+                // traditionally clients can listen for changes to the LOCATION_PROVIDERS_ALLOWED
+                // setting to detect when providers are enabled or disabled (even though they aren't
+                // supposed to). to continue to support this we must force a change to this setting.
+                // we use the fused provider because this is forced to be always enabled in settings
+                // anyways, and so won't have any visible effect beyond triggering content observers
+                Settings.Secure.putStringForUser(mContext.getContentResolver(),
+                        Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
+                        "+" + LocationManager.FUSED_PROVIDER, mCurrentUserId);
+                Settings.Secure.putStringForUser(mContext.getContentResolver(),
+                        Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
+                        "-" + LocationManager.FUSED_PROVIDER, mCurrentUserId);
+
+                synchronized (mLock) {
+                    if (!enabled) {
+                        // If any provider has been disabled, clear all last locations for all
+                        // providers. This is to be on the safe side in case a provider has location
+                        // derived from this disabled provider.
+                        mLastLocation.clear();
+                        mLastLocationCoarseInterval.clear();
+                    }
+
+                    updateProviderListenersLocked(mName);
+                }
+
+                mContext.sendBroadcastAsUser(new Intent(LocationManager.PROVIDERS_CHANGED_ACTION),
+                        UserHandle.ALL);
+            });
+        }
+
+        @Override
+        public void onSetProperties(ProviderProperties properties) {
+            runOnHandler(() -> mProperties = properties);
+        }
+
+        private void setSettingsEnabled(boolean enabled) {
+            synchronized (mLock) {
+                if (mSettingsEnabled == enabled) {
+                    return;
+                }
+
+                mSettingsEnabled = enabled;
+                if (!mSettingsEnabled) {
+                    // if any provider has been disabled, clear all last locations for all
+                    // providers. this is to be on the safe side in case a provider has location
+                    // derived from this disabled provider.
+                    mLastLocation.clear();
+                    mLastLocationCoarseInterval.clear();
+                    updateProviderListenersLocked(mName);
+                } else if (mEnabled) {
+                    updateProviderListenersLocked(mName);
+                }
+            }
+        }
+
+        private void runOnHandler(Runnable runnable) {
+            if (Looper.myLooper() == mLocationHandler.getLooper()) {
+                runnable.run();
+            } else {
+                mLocationHandler.post(runnable);
+            }
+        }
+    }
+
     /**
      * A wrapper class holding either an ILocationListener or a PendingIntent to receive
      * location updates.
@@ -799,24 +924,24 @@
     private final class Receiver implements IBinder.DeathRecipient, PendingIntent.OnFinished {
         private static final long WAKELOCK_TIMEOUT_MILLIS = 60 * 1000;
         final Identity mIdentity;
-        final int mAllowedResolutionLevel;  // resolution level allowed to receiver
+        private final int mAllowedResolutionLevel;  // resolution level allowed to receiver
 
-        final ILocationListener mListener;
+        private final ILocationListener mListener;
         final PendingIntent mPendingIntent;
         final WorkSource mWorkSource; // WorkSource for battery blame, or null to assign to caller.
-        final boolean mHideFromAppOps; // True if AppOps should not monitor this receiver.
-        final Object mKey;
+        private final boolean mHideFromAppOps; // True if AppOps should not monitor this receiver.
+        private final Object mKey;
 
         final HashMap<String, UpdateRecord> mUpdateRecords = new HashMap<>();
 
         // True if app ops has started monitoring this receiver for locations.
-        boolean mOpMonitoring;
+        private boolean mOpMonitoring;
         // True if app ops has started monitoring this receiver for high power (gps) locations.
-        boolean mOpHighPowerMonitoring;
-        int mPendingBroadcasts;
+        private boolean mOpHighPowerMonitoring;
+        private int mPendingBroadcasts;
         PowerManager.WakeLock mWakeLock;
 
-        Receiver(ILocationListener listener, PendingIntent intent, int pid, int uid,
+        private Receiver(ILocationListener listener, PendingIntent intent, int pid, int uid,
                 String packageName, WorkSource workSource, boolean hideFromAppOps) {
             mListener = listener;
             mPendingIntent = intent;
@@ -891,9 +1016,10 @@
                 // See if receiver has any enabled update records.  Also note if any update records
                 // are high power (has a high power provider with an interval under a threshold).
                 for (UpdateRecord updateRecord : mUpdateRecords.values()) {
-                    if (isAllowedByCurrentUserSettingsLocked(updateRecord.mProvider)) {
+                    if (isAllowedByUserSettingsLockedForUser(updateRecord.mProvider,
+                            mCurrentUserId)) {
                         requestingLocation = true;
-                        LocationProviderInterface locationProvider
+                        LocationManagerService.LocationProvider locationProvider
                                 = mProvidersByName.get(updateRecord.mProvider);
                         ProviderProperties properties = locationProvider != null
                                 ? locationProvider.getProperties() : null;
@@ -1040,7 +1166,7 @@
             return true;
         }
 
-        public boolean callProviderEnabledLocked(String provider, boolean enabled) {
+        private boolean callProviderEnabledLocked(String provider, boolean enabled) {
             // First update AppOp monitoring.
             // An app may get/lose location access as providers are enabled/disabled.
             updateMonitoring(true);
@@ -1242,7 +1368,7 @@
     private class LinkedCallback implements IBinder.DeathRecipient {
         private final IBatchedLocationCallback mCallback;
 
-        public LinkedCallback(@NonNull IBatchedLocationCallback callback) {
+        private LinkedCallback(@NonNull IBatchedLocationCallback callback) {
             mCallback = callback;
         }
 
@@ -1343,7 +1469,7 @@
         checkCallerIsProvider();
 
         // Currently used only for GNSS locations - update permissions check if changed
-        if (isAllowedByCurrentUserSettingsLocked(LocationManager.GPS_PROVIDER)) {
+        if (isAllowedByUserSettingsLockedForUser(LocationManager.GPS_PROVIDER, mCurrentUserId)) {
             if (mGnssBatchingCallback == null) {
                 Slog.e(TAG, "reportLocationBatch() called without active Callback");
                 return;
@@ -1358,29 +1484,17 @@
         }
     }
 
-    private void addProviderLocked(LocationProviderInterface provider) {
+    private void addProviderLocked(LocationProvider provider) {
         mProviders.add(provider);
         mProvidersByName.put(provider.getName(), provider);
     }
 
-    private void removeProviderLocked(LocationProviderInterface provider) {
-        provider.disable();
+    private void removeProviderLocked(LocationProvider provider) {
         mProviders.remove(provider);
         mProvidersByName.remove(provider.getName());
     }
 
     /**
-     * Returns "true" if access to the specified location provider is allowed by the current
-     * user's settings. Access to all location providers is forbidden to non-location-provider
-     * processes belonging to background users.
-     *
-     * @param provider the name of the location provider
-     */
-    private boolean isAllowedByCurrentUserSettingsLocked(String provider) {
-        return isAllowedByUserSettingsLockedForUser(provider, mCurrentUserId);
-    }
-
-    /**
      * Returns "true" if access to the specified location provider is allowed by the specified
      * user's settings. Access to all location providers is forbidden to non-location-provider
      * processes belonging to background users.
@@ -1389,13 +1503,28 @@
      * @param userId   the user id to query
      */
     private boolean isAllowedByUserSettingsLockedForUser(String provider, int userId) {
-        if (mEnabledProviders.contains(provider)) {
-            return true;
+        if (LocationManager.PASSIVE_PROVIDER.equals(provider)) {
+            return isLocationEnabledForUser(userId);
         }
-        if (mDisabledProviders.contains(provider)) {
-            return false;
+        if (LocationManager.FUSED_PROVIDER.equals(provider)) {
+            return isLocationEnabledForUser(userId);
         }
-        return isLocationProviderEnabledForUser(provider, userId);
+        synchronized (mLock) {
+            if (mMockProviders.containsKey(provider)) {
+                return isLocationEnabledForUser(userId);
+            }
+        }
+
+        long identity = Binder.clearCallingIdentity();
+        try {
+            // Use system settings
+            ContentResolver cr = mContext.getContentResolver();
+            String allowedProviders = Settings.Secure.getStringForUser(
+                    cr, Settings.Secure.LOCATION_PROVIDERS_ALLOWED, userId);
+            return TextUtils.delimitedStringContains(allowedProviders, ',', provider);
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
     }
 
 
@@ -1487,9 +1616,11 @@
             // network and fused providers are ok with COARSE or FINE
             return RESOLUTION_LEVEL_COARSE;
         } else {
-            // mock providers
-            LocationProviderInterface lp = mMockProviders.get(provider);
-            if (lp != null) {
+            for (LocationProvider lp : mProviders) {
+                if (!lp.getName().equals(provider)) {
+                    continue;
+                }
+
                 ProviderProperties properties = lp.getProperties();
                 if (properties != null) {
                     if (properties.mRequiresSatellite) {
@@ -1502,6 +1633,7 @@
                 }
             }
         }
+
         return RESOLUTION_LEVEL_FINE; // if in doubt, require FINE
     }
 
@@ -1556,7 +1688,7 @@
     }
 
     private static String resolutionLevelToOpStr(int allowedResolutionLevel) {
-        switch(allowedResolutionLevel) {
+        switch (allowedResolutionLevel) {
             case RESOLUTION_LEVEL_COARSE:
                 return AppOpsManager.OPSTR_COARSE_LOCATION;
             case RESOLUTION_LEVEL_FINE:
@@ -1571,7 +1703,7 @@
         }
     }
 
-    boolean reportLocationAccessNoThrow(
+    private boolean reportLocationAccessNoThrow(
             int pid, int uid, String packageName, int allowedResolutionLevel) {
         int op = resolutionLevelToOp(allowedResolutionLevel);
         if (op >= 0) {
@@ -1583,7 +1715,8 @@
         return getAllowedResolutionLevel(pid, uid) >= allowedResolutionLevel;
     }
 
-    boolean checkLocationAccess(int pid, int uid, String packageName, int allowedResolutionLevel) {
+    private boolean checkLocationAccess(int pid, int uid, String packageName,
+            int allowedResolutionLevel) {
         int op = resolutionLevelToOp(allowedResolutionLevel);
         if (op >= 0) {
             if (mAppOps.noteOp(op, uid, packageName) != AppOpsManager.MODE_ALLOWED) {
@@ -1603,7 +1736,7 @@
         ArrayList<String> out;
         synchronized (mLock) {
             out = new ArrayList<>(mProviders.size());
-            for (LocationProviderInterface provider : mProviders) {
+            for (LocationProvider provider : mProviders) {
                 String name = provider.getName();
                 if (LocationManager.FUSED_PROVIDER.equals(name)) {
                     continue;
@@ -1629,7 +1762,7 @@
         try {
             synchronized (mLock) {
                 out = new ArrayList<>(mProviders.size());
-                for (LocationProviderInterface provider : mProviders) {
+                for (LocationProvider provider : mProviders) {
                     String name = provider.getName();
                     if (LocationManager.FUSED_PROVIDER.equals(name)) {
                         continue;
@@ -1639,7 +1772,8 @@
                                 && !isAllowedByUserSettingsLocked(name, uid, mCurrentUserId)) {
                             continue;
                         }
-                        if (criteria != null && !LocationProvider.propertiesMeetCriteria(
+                        if (criteria != null
+                                && !android.location.LocationProvider.propertiesMeetCriteria(
                                 name, provider.getProperties(), criteria)) {
                             continue;
                         }
@@ -1664,7 +1798,7 @@
      */
     @Override
     public String getBestProvider(Criteria criteria, boolean enabledOnly) {
-        String result = null;
+        String result;
 
         List<String> providers = getProviders(criteria, enabledOnly);
         if (!providers.isEmpty()) {
@@ -1679,7 +1813,7 @@
             return result;
         }
 
-        if (D) Log.d(TAG, "getBestProvider(" + criteria + ", " + enabledOnly + ")=" + result);
+        if (D) Log.d(TAG, "getBestProvider(" + criteria + ", " + enabledOnly + ")=" + null);
         return null;
     }
 
@@ -1695,51 +1829,32 @@
 
     @Override
     public boolean providerMeetsCriteria(String provider, Criteria criteria) {
-        LocationProviderInterface p = mProvidersByName.get(provider);
+        LocationProvider p = mProvidersByName.get(provider);
         if (p == null) {
             throw new IllegalArgumentException("provider=" + provider);
         }
 
-        boolean result = LocationProvider.propertiesMeetCriteria(
+        boolean result = android.location.LocationProvider.propertiesMeetCriteria(
                 p.getName(), p.getProperties(), criteria);
         if (D) Log.d(TAG, "providerMeetsCriteria(" + provider + ", " + criteria + ")=" + result);
         return result;
     }
 
-    private void updateProvidersLocked() {
-        boolean changesMade = false;
-        for (int i = mProviders.size() - 1; i >= 0; i--) {
-            LocationProviderInterface p = mProviders.get(i);
-            boolean isEnabled = p.isEnabled();
-            String name = p.getName();
-            boolean shouldBeEnabled = isAllowedByCurrentUserSettingsLocked(name);
-            if (isEnabled && !shouldBeEnabled) {
-                updateProviderListenersLocked(name, false);
-                // If any provider has been disabled, clear all last locations for all providers.
-                // This is to be on the safe side in case a provider has location derived from
-                // this disabled provider.
-                mLastLocation.clear();
-                mLastLocationCoarseInterval.clear();
-                changesMade = true;
-            } else if (!isEnabled && shouldBeEnabled) {
-                updateProviderListenersLocked(name, true);
-                changesMade = true;
-            }
+    private void updateProvidersSettingsLocked() {
+        for (LocationProvider p : mProviders) {
+            p.setSettingsEnabled(isAllowedByUserSettingsLockedForUser(p.getName(), mCurrentUserId));
         }
-        if (changesMade) {
-            mContext.sendBroadcastAsUser(new Intent(LocationManager.PROVIDERS_CHANGED_ACTION),
-                    UserHandle.ALL);
-            mContext.sendBroadcastAsUser(new Intent(LocationManager.MODE_CHANGED_ACTION),
-                    UserHandle.ALL);
-        }
+
+        mContext.sendBroadcastAsUser(new Intent(LocationManager.MODE_CHANGED_ACTION),
+                UserHandle.ALL);
     }
 
-    private void updateProviderListenersLocked(String provider, boolean enabled) {
-        int listeners = 0;
-
-        LocationProviderInterface p = mProvidersByName.get(provider);
+    private void updateProviderListenersLocked(String provider) {
+        LocationProvider p = mProvidersByName.get(provider);
         if (p == null) return;
 
+        boolean enabled = p.isEnabled();
+
         ArrayList<Receiver> deadReceivers = null;
 
         ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
@@ -1753,7 +1868,6 @@
                         }
                         deadReceivers.add(record.mReceiver);
                     }
-                    listeners++;
                 }
             }
         }
@@ -1764,16 +1878,11 @@
             }
         }
 
-        if (enabled) {
-            p.enable();
-            applyRequirementsLocked(provider);
-        } else {
-            p.disable();
-        }
+        applyRequirementsLocked(provider);
     }
 
     private void applyRequirementsLocked(String provider) {
-        LocationProviderInterface p = mProvidersByName.get(provider);
+        LocationProvider p = mProvidersByName.get(provider);
         if (p == null) return;
 
         ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
@@ -1785,10 +1894,10 @@
                 resolver,
                 Settings.Global.LOCATION_BACKGROUND_THROTTLE_INTERVAL_MS,
                 DEFAULT_BACKGROUND_THROTTLE_INTERVAL_MS);
-        // initialize the low power mode to true and set to false if any of the records requires
 
-        providerRequest.lowPowerMode = true;
-        if (records != null) {
+        if (p.isEnabled() && records != null && !records.isEmpty()) {
+            // initialize the low power mode to true and set to false if any of the records requires
+            providerRequest.lowPowerMode = true;
             for (UpdateRecord record : records) {
                 if (isCurrentProfile(UserHandle.getUserId(record.mReceiver.mIdentity.mUid))) {
                     if (checkLocationAccess(
@@ -1881,7 +1990,7 @@
     public String[] getBackgroundThrottlingWhitelist() {
         synchronized (mLock) {
             return mBackgroundThrottlePackageWhitelist.toArray(
-                    new String[mBackgroundThrottlePackageWhitelist.size()]);
+                    new String[0]);
         }
     }
 
@@ -1928,17 +2037,17 @@
 
     private class UpdateRecord {
         final String mProvider;
-        final LocationRequest mRealRequest;  // original request from client
+        private final LocationRequest mRealRequest;  // original request from client
         LocationRequest mRequest;  // possibly throttled version of the request
-        final Receiver mReceiver;
-        boolean mIsForegroundUid;
-        Location mLastFixBroadcast;
-        long mLastStatusBroadcast;
+        private final Receiver mReceiver;
+        private boolean mIsForegroundUid;
+        private Location mLastFixBroadcast;
+        private long mLastStatusBroadcast;
 
         /**
          * Note: must be constructed with lock held.
          */
-        UpdateRecord(String provider, LocationRequest request, Receiver receiver) {
+        private UpdateRecord(String provider, LocationRequest request, Receiver receiver) {
             mProvider = provider;
             mRealRequest = request;
             mRequest = request;
@@ -1964,7 +2073,7 @@
         /**
          * Method to be called when record changes foreground/background
          */
-        void updateForeground(boolean isForeground){
+        private void updateForeground(boolean isForeground) {
             mIsForegroundUid = isForeground;
             mRequestStatistics.updateForeground(
                     mReceiver.mIdentity.mPackageName, mProvider, isForeground);
@@ -1973,7 +2082,7 @@
         /**
          * Method to be called when a record will no longer be used.
          */
-        void disposeLocked(boolean removeReceiver) {
+        private void disposeLocked(boolean removeReceiver) {
             mRequestStatistics.stopRequesting(mReceiver.mIdentity.mPackageName, mProvider);
 
             // remove from mRecordsByProvider
@@ -1986,13 +2095,11 @@
 
             // remove from Receiver#mUpdateRecords
             HashMap<String, UpdateRecord> receiverRecords = mReceiver.mUpdateRecords;
-            if (receiverRecords != null) {
-                receiverRecords.remove(this.mProvider);
+            receiverRecords.remove(this.mProvider);
 
-                // and also remove the Receiver if it has no more update records
-                if (receiverRecords.size() == 0) {
-                    removeUpdatesLocked(mReceiver);
-                }
+            // and also remove the Receiver if it has no more update records
+            if (receiverRecords.size() == 0) {
+                removeUpdatesLocked(mReceiver);
             }
         }
 
@@ -2076,7 +2183,7 @@
 
     private void checkPackageName(String packageName) {
         if (packageName == null) {
-            throw new SecurityException("invalid package name: " + packageName);
+            throw new SecurityException("invalid package name: " + null);
         }
         int uid = Binder.getCallingUid();
         String[] packages = mPackageManager.getPackagesForUid(uid);
@@ -2091,7 +2198,7 @@
 
     private void checkPendingIntent(PendingIntent intent) {
         if (intent == null) {
-            throw new IllegalArgumentException("invalid pending intent: " + intent);
+            throw new IllegalArgumentException("invalid pending intent: " + null);
         }
     }
 
@@ -2143,7 +2250,7 @@
             synchronized (mLock) {
                 Receiver recevier = checkListenerOrIntentLocked(listener, intent, pid, uid,
                         packageName, workSource, hideFromAppOps);
-                requestLocationUpdatesLocked(sanitizedRequest, recevier, pid, uid, packageName);
+                requestLocationUpdatesLocked(sanitizedRequest, recevier, uid, packageName);
             }
         } finally {
             Binder.restoreCallingIdentity(identity);
@@ -2151,7 +2258,7 @@
     }
 
     private void requestLocationUpdatesLocked(LocationRequest request, Receiver receiver,
-            int pid, int uid, String packageName) {
+            int uid, String packageName) {
         // Figure out the provider. Either its explicitly request (legacy use cases), or
         // use the fused provider
         if (request == null) request = DEFAULT_LOCATION_REQUEST;
@@ -2160,7 +2267,7 @@
             throw new IllegalArgumentException("provider name must not be null");
         }
 
-        LocationProviderInterface provider = mProvidersByName.get(name);
+        LocationProvider provider = mProvidersByName.get(name);
         if (provider == null) {
             throw new IllegalArgumentException("provider doesn't exist: " + name);
         }
@@ -2179,8 +2286,7 @@
             oldRecord.disposeLocked(false);
         }
 
-        boolean isProviderEnabled = isAllowedByUserSettingsLocked(name, uid, mCurrentUserId);
-        if (isProviderEnabled) {
+        if (provider.isEnabled()) {
             applyRequirementsLocked(name);
         } else {
             // Notify the listener that updates are currently disabled
@@ -2200,10 +2306,8 @@
         final int uid = Binder.getCallingUid();
 
         synchronized (mLock) {
-            WorkSource workSource = null;
-            boolean hideFromAppOps = false;
             Receiver receiver = checkListenerOrIntentLocked(listener, intent, pid, uid,
-                    packageName, workSource, hideFromAppOps);
+                    packageName, null, false);
 
             // providers may use public location API's, need to clear identity
             long identity = Binder.clearCallingIdentity();
@@ -2242,22 +2346,12 @@
 
         // update provider
         for (String provider : providers) {
-            // If provider is already disabled, don't need to do anything
-            if (!isAllowedByCurrentUserSettingsLocked(provider)) {
-                continue;
-            }
-
             applyRequirementsLocked(provider);
         }
     }
 
     private void applyAllProviderRequirementsLocked() {
-        for (LocationProviderInterface p : mProviders) {
-            // If provider is already disabled, don't need to do anything
-            if (!isAllowedByCurrentUserSettingsLocked(p.getName())) {
-                continue;
-            }
-
+        for (LocationProvider p : mProviders) {
             applyRequirementsLocked(p.getName());
         }
     }
@@ -2297,7 +2391,7 @@
                 // or use the fused provider
                 String name = request.getProvider();
                 if (name == null) name = LocationManager.FUSED_PROVIDER;
-                LocationProviderInterface provider = mProvidersByName.get(name);
+                LocationProvider provider = mProvidersByName.get(name);
                 if (provider == null) return null;
 
                 if (!isAllowedByUserSettingsLocked(name, uid, mCurrentUserId)) return null;
@@ -2320,7 +2414,7 @@
                         location.getElapsedRealtimeNanos() / NANOS_PER_MILLI;
                 if ((locationAgeMs > mLastLocationMaxAgeMs)
                         && (mAppOps.unsafeCheckOp(op, uid, packageName)
-                            == AppOpsManager.MODE_FOREGROUND)) {
+                        == AppOpsManager.MODE_FOREGROUND)) {
                     return null;
                 }
 
@@ -2364,7 +2458,7 @@
             }
             return false;
         }
-        LocationProviderInterface p = null;
+        LocationProvider p = null;
         String provider = location.getProvider();
         if (provider != null) {
             p = mProvidersByName.get(provider);
@@ -2376,7 +2470,7 @@
             return false;
         }
         synchronized (mLock) {
-            if (!isAllowedByCurrentUserSettingsLocked(provider)) {
+            if (!isAllowedByUserSettingsLockedForUser(provider, mCurrentUserId)) {
                 if (D) {
                     Log.d(TAG, "Location disabled in Settings for current user:" + mCurrentUserId);
                 }
@@ -2496,11 +2590,13 @@
 
     @Override
     public void removeGnssMeasurementsListener(IGnssMeasurementsListener listener) {
-        if (mGnssMeasurementsProvider != null) {
-            synchronized (mLock) {
-                mGnssMeasurementsListeners.remove(listener.asBinder());
-                mGnssMeasurementsProvider.removeListener(listener);
-            }
+        if (mGnssMeasurementsProvider == null) {
+            return;
+        }
+
+        synchronized (mLock) {
+            mGnssMeasurementsListeners.remove(listener.asBinder());
+            mGnssMeasurementsProvider.removeListener(listener);
         }
     }
 
@@ -2559,10 +2655,11 @@
         }
 
         synchronized (mLock) {
-            LocationProviderInterface p = mProvidersByName.get(provider);
+            LocationProvider p = mProvidersByName.get(provider);
             if (p == null) return false;
 
-            return p.sendExtraCommand(command, extras);
+            p.sendExtraCommand(command, extras);
+            return true;
         }
     }
 
@@ -2587,14 +2684,10 @@
      */
     @Override
     public ProviderProperties getProviderProperties(String provider) {
-        if (mProvidersByName.get(provider) == null) {
-            return null;
-        }
-
         checkResolutionLevelIsSufficientForProviderUse(getCallerAllowedResolutionLevel(),
                 provider);
 
-        LocationProviderInterface p;
+        LocationProvider p;
         synchronized (mLock) {
             p = mProvidersByName.get(provider);
         }
@@ -2610,25 +2703,25 @@
      */
     @Override
     public String getNetworkProviderPackage() {
-        LocationProviderInterface p;
+        LocationProvider p;
         synchronized (mLock) {
-            if (mProvidersByName.get(LocationManager.NETWORK_PROVIDER) == null) {
-                return null;
-            }
             p = mProvidersByName.get(LocationManager.NETWORK_PROVIDER);
         }
 
-        if (p instanceof LocationProviderProxy) {
-            return ((LocationProviderProxy) p).getConnectedPackageName();
+        if (p == null) {
+            return null;
+        }
+        if (p.mProvider instanceof LocationProviderProxy) {
+            return ((LocationProviderProxy) p.mProvider).getConnectedPackageName();
         }
         return null;
     }
 
     /**
-     *  Returns the current location enabled/disabled status for a user
+     * Returns the current location enabled/disabled status for a user
      *
-     *  @param userId the id of the user
-     *  @return true if location is enabled
+     * @param userId the id of the user
+     * @return true if location is enabled
      */
     @Override
     public boolean isLocationEnabledForUser(int userId) {
@@ -2646,7 +2739,7 @@
                     return false;
                 }
                 final List<String> providerList = Arrays.asList(allowedProviders.split(","));
-                for(String provider : mRealProviders.keySet()) {
+                for (String provider : mRealProviders.keySet()) {
                     if (provider.equals(LocationManager.PASSIVE_PROVIDER)
                             || provider.equals(LocationManager.FUSED_PROVIDER)) {
                         continue;
@@ -2663,16 +2756,16 @@
     }
 
     /**
-     *  Enable or disable location for a user
+     * Enable or disable location for a user
      *
-     *  @param enabled true to enable location, false to disable location
-     *  @param userId the id of the user
+     * @param enabled true to enable location, false to disable location
+     * @param userId  the id of the user
      */
     @Override
     public void setLocationEnabledForUser(boolean enabled, int userId) {
         mContext.enforceCallingPermission(
-            android.Manifest.permission.WRITE_SECURE_SETTINGS,
-            "Requires WRITE_SECURE_SETTINGS permission");
+                android.Manifest.permission.WRITE_SECURE_SETTINGS,
+                "Requires WRITE_SECURE_SETTINGS permission");
 
         // Check INTERACT_ACROSS_USERS permission if userId is not current user id.
         checkInteractAcrossUsersPermission(userId);
@@ -2687,7 +2780,7 @@
                 allProvidersSet.addAll(allRealProviders);
                 // When disabling location, disable gps and network provider that could have been
                 // enabled by location mode api.
-                if (enabled == false) {
+                if (!enabled) {
                     allProvidersSet.add(LocationManager.GPS_PROVIDER);
                     allProvidersSet.add(LocationManager.NETWORK_PROVIDER);
                 }
@@ -2722,26 +2815,34 @@
     }
 
     /**
-     *  Returns the current enabled/disabled status of a location provider and user
+     * Returns the current enabled/disabled status of a location provider and user
      *
-     *  @param provider name of the provider
-     *  @param userId the id of the user
-     *  @return true if the provider exists and is enabled
+     * @param providerName name of the provider
+     * @param userId       the id of the user
+     * @return true if the provider exists and is enabled
      */
     @Override
-    public boolean isProviderEnabledForUser(String provider, int userId) {
+    public boolean isProviderEnabledForUser(String providerName, int userId) {
         // Check INTERACT_ACROSS_USERS permission if userId is not current user id.
         checkInteractAcrossUsersPermission(userId);
 
+        if (!isLocationEnabledForUser(userId)) {
+            return false;
+        }
+
         // Fused provider is accessed indirectly via criteria rather than the provider-based APIs,
         // so we discourage its use
-        if (LocationManager.FUSED_PROVIDER.equals(provider)) return false;
+        if (LocationManager.FUSED_PROVIDER.equals(providerName)) return false;
 
-        int uid = Binder.getCallingUid();
-        synchronized (mLock) {
-            LocationProviderInterface p = mProvidersByName.get(provider);
-            return p != null
-                    && isAllowedByUserSettingsLocked(provider, uid, userId);
+        long identity = Binder.clearCallingIdentity();
+        try {
+            LocationProvider provider;
+            synchronized (mLock) {
+                provider = mProvidersByName.get(providerName);
+            }
+            return provider != null && provider.isEnabled();
+        } finally {
+            Binder.restoreCallingIdentity(identity);
         }
     }
 
@@ -2749,66 +2850,13 @@
      * Enable or disable a single location provider.
      *
      * @param provider name of the provider
-     * @param enabled true to enable the provider. False to disable the provider
-     * @param userId the id of the user to set
+     * @param enabled  true to enable the provider. False to disable the provider
+     * @param userId   the id of the user to set
      * @return true if the value was set, false on errors
      */
     @Override
     public boolean setProviderEnabledForUser(String provider, boolean enabled, int userId) {
-        mContext.enforceCallingPermission(
-                android.Manifest.permission.WRITE_SECURE_SETTINGS,
-                "Requires WRITE_SECURE_SETTINGS permission");
-
-        // Check INTERACT_ACROSS_USERS permission if userId is not current user id.
-        checkInteractAcrossUsersPermission(userId);
-
-        // Fused provider is accessed indirectly via criteria rather than the provider-based APIs,
-        // so we discourage its use
-        if (LocationManager.FUSED_PROVIDER.equals(provider)) return false;
-
-        long identity = Binder.clearCallingIdentity();
-        try {
-            synchronized (mLock) {
-                // No such provider exists
-                if (!mProvidersByName.containsKey(provider)) return false;
-
-                // If it is a test provider, do not write to Settings.Secure
-                if (mMockProviders.containsKey(provider)) {
-                    setTestProviderEnabled(provider, enabled);
-                    return true;
-                }
-
-                // to ensure thread safety, we write the provider name with a '+' or '-'
-                // and let the SettingsProvider handle it rather than reading and modifying
-                // the list of enabled providers.
-                String providerChange = (enabled ? "+" : "-") + provider;
-                return Settings.Secure.putStringForUser(
-                        mContext.getContentResolver(), Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
-                        providerChange, userId);
-            }
-        } finally {
-            Binder.restoreCallingIdentity(identity);
-        }
-    }
-
-    /**
-     * Read location provider status from Settings.Secure
-     *
-     * @param provider the location provider to query
-     * @param userId the user id to query
-     * @return true if the provider is enabled
-     */
-    private boolean isLocationProviderEnabledForUser(String provider, int userId) {
-        long identity = Binder.clearCallingIdentity();
-        try {
-            // Use system settings
-            ContentResolver cr = mContext.getContentResolver();
-            String allowedProviders = Settings.Secure.getStringForUser(
-                    cr, Settings.Secure.LOCATION_PROVIDERS_ALLOWED, userId);
-            return TextUtils.delimitedStringContains(allowedProviders, ',', provider);
-        } finally {
-            Binder.restoreCallingIdentity(identity);
-        }
+        return false;
     }
 
     /**
@@ -2940,7 +2988,7 @@
         long now = SystemClock.elapsedRealtime();
         String provider = (passive ? LocationManager.PASSIVE_PROVIDER : location.getProvider());
         // Skip if the provider is unknown.
-        LocationProviderInterface p = mProvidersByName.get(provider);
+        LocationProvider p = mProvidersByName.get(provider);
         if (p == null) return;
         updateLastLocationLocked(location, provider);
         // mLastLocation should have been updated from the updateLastLocationLocked call above.
@@ -3052,7 +3100,7 @@
                     LOCATION_DISABLE_STATUS_CALLBACKS, 1) == 0) {
                 long prevStatusUpdateTime = r.mLastStatusBroadcast;
                 if ((newStatusUpdateTime > prevStatusUpdateTime)
-                        && (prevStatusUpdateTime != 0 || status != LocationProvider.AVAILABLE)) {
+                        && (prevStatusUpdateTime != 0 || status != AVAILABLE)) {
 
                     r.mLastStatusBroadcast = newStatusUpdateTime;
                     if (!receiver.callStatusChangedLocked(provider, status, extras)) {
@@ -3097,8 +3145,8 @@
     /**
      * Updates last location with the given location
      *
-     * @param location             new location to update
-     * @param provider             Location provider to update for
+     * @param location new location to update
+     * @param provider Location provider to update for
      */
     private void updateLastLocationLocked(Location location, String provider) {
         Location noGPSLocation = location.getExtraLocation(Location.EXTRA_NO_GPS_LOCATION);
@@ -3153,13 +3201,11 @@
         }
 
         synchronized (mLock) {
-            if (isAllowedByCurrentUserSettingsLocked(provider)) {
-                if (!passive) {
-                    // notify passive provider of the new location
-                    mPassiveProvider.updateLocation(myLocation);
-                }
-                handleLocationChangedLocked(myLocation, passive);
+            if (!passive) {
+                // notify passive provider of the new location
+                mPassiveProvider.updateLocation(myLocation);
             }
+            handleLocationChangedLocked(myLocation, passive);
         }
     }
 
@@ -3244,13 +3290,12 @@
             if (LocationManager.GPS_PROVIDER.equals(name)
                     || LocationManager.NETWORK_PROVIDER.equals(name)
                     || LocationManager.FUSED_PROVIDER.equals(name)) {
-                LocationProviderInterface p = mProvidersByName.get(name);
+                LocationProvider p = mProvidersByName.get(name);
                 if (p != null) {
                     removeProviderLocked(p);
                 }
             }
             addTestProviderLocked(name, properties);
-            updateProvidersLocked();
         }
         Binder.restoreCallingIdentity(identity);
     }
@@ -3259,9 +3304,12 @@
         if (mProvidersByName.get(name) != null) {
             throw new IllegalArgumentException("Provider \"" + name + "\" already exists");
         }
-        MockProvider provider = new MockProvider(name, this, properties);
+
+        LocationProvider provider = new LocationProvider(name);
+        MockProvider mockProvider = new MockProvider(provider, properties);
+
         addProviderLocked(provider);
-        mMockProviders.put(name, provider);
+        mMockProviders.put(name, mockProvider);
         mLastLocation.put(name, null);
         mLastLocationCoarseInterval.put(name, null);
     }
@@ -3273,28 +3321,25 @@
         }
 
         synchronized (mLock) {
-
-            // These methods can't be called after removing the test provider, so first make sure
-            // we don't leave anything dangling.
-            clearTestProviderEnabled(provider, opPackageName);
-            clearTestProviderLocation(provider, opPackageName);
-
             MockProvider mockProvider = mMockProviders.remove(provider);
             if (mockProvider == null) {
                 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
             }
-            long identity = Binder.clearCallingIdentity();
-            removeProviderLocked(mProvidersByName.get(provider));
 
-            // reinstate real provider if available
-            LocationProviderInterface realProvider = mRealProviders.get(provider);
-            if (realProvider != null) {
-                addProviderLocked(realProvider);
+            long identity = Binder.clearCallingIdentity();
+            try {
+                removeProviderLocked(mProvidersByName.get(provider));
+
+                // reinstate real provider if available
+                LocationProvider realProvider = mRealProviders.get(provider);
+                if (realProvider != null) {
+                    addProviderLocked(realProvider);
+                }
+                mLastLocation.put(provider, null);
+                mLastLocationCoarseInterval.put(provider, null);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
             }
-            mLastLocation.put(provider, null);
-            mLastLocationCoarseInterval.put(provider, null);
-            updateProvidersLocked();
-            Binder.restoreCallingIdentity(identity);
         }
     }
 
@@ -3324,23 +3369,11 @@
 
             // clear calling identity so INSTALL_LOCATION_PROVIDER permission is not required
             long identity = Binder.clearCallingIdentity();
-            mockProvider.setLocation(mock);
-            Binder.restoreCallingIdentity(identity);
-        }
-    }
-
-    @Override
-    public void clearTestProviderLocation(String provider, String opPackageName) {
-        if (!canCallerAccessMockLocation(opPackageName)) {
-            return;
-        }
-
-        synchronized (mLock) {
-            MockProvider mockProvider = mMockProviders.get(provider);
-            if (mockProvider == null) {
-                throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
+            try {
+                mockProvider.setLocation(mock);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
             }
-            mockProvider.clearLocation();
         }
     }
 
@@ -3349,36 +3382,6 @@
         if (!canCallerAccessMockLocation(opPackageName)) {
             return;
         }
-        setTestProviderEnabled(provider, enabled);
-    }
-
-    /** Enable or disable a test location provider. */
-    private void setTestProviderEnabled(String provider, boolean enabled) {
-        synchronized (mLock) {
-            MockProvider mockProvider = mMockProviders.get(provider);
-            if (mockProvider == null) {
-                throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
-            }
-            long identity = Binder.clearCallingIdentity();
-            if (enabled) {
-                mockProvider.enable();
-                mEnabledProviders.add(provider);
-                mDisabledProviders.remove(provider);
-            } else {
-                mockProvider.disable();
-                mEnabledProviders.remove(provider);
-                mDisabledProviders.add(provider);
-            }
-            updateProvidersLocked();
-            Binder.restoreCallingIdentity(identity);
-        }
-    }
-
-    @Override
-    public void clearTestProviderEnabled(String provider, String opPackageName) {
-        if (!canCallerAccessMockLocation(opPackageName)) {
-            return;
-        }
 
         synchronized (mLock) {
             MockProvider mockProvider = mMockProviders.get(provider);
@@ -3386,10 +3389,11 @@
                 throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
             }
             long identity = Binder.clearCallingIdentity();
-            mEnabledProviders.remove(provider);
-            mDisabledProviders.remove(provider);
-            updateProvidersLocked();
-            Binder.restoreCallingIdentity(identity);
+            try {
+                mockProvider.setEnabled(enabled);
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
         }
     }
 
@@ -3449,10 +3453,11 @@
                         + identity.mPackageName + ": " + isThrottlingExemptLocked(identity));
             }
             pw.println("  Overlay Provider Packages:");
-            for (LocationProviderInterface provider : mProviders) {
-                if (provider instanceof LocationProviderProxy) {
+            for (LocationProvider provider : mProviders) {
+                if (provider.mProvider instanceof LocationProviderProxy) {
                     pw.println("    " + provider.getName() + ": "
-                            + ((LocationProviderProxy) provider).getConnectedPackageName());
+                            + ((LocationProviderProxy) provider.mProvider)
+                            .getConnectedPackageName());
                 }
             }
             pw.println("  Historical Records by Provider:");
@@ -3478,25 +3483,12 @@
 
             mGeofenceManager.dump(pw);
 
-            if (mEnabledProviders.size() > 0) {
-                pw.println("  Enabled Providers:");
-                for (String i : mEnabledProviders) {
-                    pw.println("    " + i);
-                }
-
-            }
-            if (mDisabledProviders.size() > 0) {
-                pw.println("  Disabled Providers:");
-                for (String i : mDisabledProviders) {
-                    pw.println("    " + i);
-                }
-            }
             pw.append("  ");
             mBlacklist.dump(pw);
             if (mMockProviders.size() > 0) {
                 pw.println("  Mock Providers:");
                 for (Map.Entry<String, MockProvider> i : mMockProviders.entrySet()) {
-                    i.getValue().dump(pw, "      ");
+                    i.getValue().dump(fd, pw, args);
                 }
             }
 
@@ -3513,13 +3505,7 @@
             if (args.length > 0 && "short".equals(args[0])) {
                 return;
             }
-            for (LocationProviderInterface provider : mProviders) {
-                pw.print(provider.getName() + " Internal State");
-                if (provider instanceof LocationProviderProxy) {
-                    LocationProviderProxy proxy = (LocationProviderProxy) provider;
-                    pw.print(" (" + proxy.getConnectedPackageName() + ")");
-                }
-                pw.println(":");
+            for (LocationProvider provider : mProviders) {
                 provider.dump(fd, pw, args);
             }
             if (mGnssBatchingInProgress) {
diff --git a/services/core/java/com/android/server/ServiceWatcher.java b/services/core/java/com/android/server/ServiceWatcher.java
index 574f54a..d09823e 100644
--- a/services/core/java/com/android/server/ServiceWatcher.java
+++ b/services/core/java/com/android/server/ServiceWatcher.java
@@ -16,9 +16,7 @@
 
 package com.android.server;
 
-import android.annotation.MainThread;
 import android.annotation.Nullable;
-import android.annotation.WorkerThread;
 import android.app.ActivityManager;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -168,13 +166,13 @@
     // called on handler thread
     @GuardedBy("mBindLock")
     protected void onBind() {
-
+        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
     }
 
     // called on handler thread
     @GuardedBy("mBindLock")
     protected void onUnbind() {
-
+        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
     }
 
     /**
@@ -205,12 +203,12 @@
 
                 @Override
                 public void onPackageRemoved(String packageName, int uid) {
-                        bindBestPackage(Objects.equals(packageName, getCurrentPackageName()));
+                    bindBestPackage(Objects.equals(packageName, getCurrentPackageName()));
                 }
 
                 @Override
                 public boolean onPackageChanged(String packageName, int uid, String[] components) {
-                        bindBestPackage(Objects.equals(packageName, getCurrentPackageName()));
+                    bindBestPackage(Objects.equals(packageName, getCurrentPackageName()));
                     return super.onPackageChanged(packageName, uid, components);
                 }
             }.register(mContext, UserHandle.ALL, true, mHandler);
@@ -243,20 +241,16 @@
         return true;
     }
 
-    /** Returns thje name of the currently connected package or null. */
+    /** Returns the name of the currently connected package or null. */
     @Nullable
     public String getCurrentPackageName() {
         ComponentName bestComponent = mBestComponent;
         return bestComponent == null ? null : bestComponent.getPackageName();
     }
 
-    public int getCurrentPackageVersion() {
-        return mBestVersion;
-    }
-
     /**
-     * Runs the given BinderRunner if currently connected. Returns true if it was run, and false
-     * otherwise. All invocations to runOnBinder are run serially.
+     * Runs the given BinderRunner if currently connected. All invocations to runOnBinder are run
+     * serially.
      */
     public final void runOnBinder(BinderRunner runner) {
         synchronized (mBindLock) {
@@ -267,7 +261,7 @@
                 } catch (Exception e) {
                     // remote exceptions cannot be allowed to crash system server
                     Log.e(TAG, "exception while while running " + runner + " on " + service
-                            + " from " + mBestComponent.toShortString(), e);
+                            + " from " + this, e);
                 }
             }
         }
@@ -280,14 +274,6 @@
                 UserHandle.USER_SYSTEM).isEmpty();
     }
 
-    /**
-     * Searches and binds to the best package, or do nothing if the best package
-     * is already bound, unless force rebinding is requested.
-     *
-     * @param forceRebind          Force a rebinding to the best package if it's already
-     *                             bound.
-     */
-    @WorkerThread
     private void bindBestPackage(boolean forceRebind) {
         Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
 
@@ -365,7 +351,6 @@
         }
     }
 
-    @WorkerThread
     private void bind(ComponentName component, int version, int userId) {
         Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
 
@@ -382,7 +367,6 @@
                 UserHandle.of(userId));
     }
 
-    @WorkerThread
     private void unbind() {
         Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
 
@@ -396,7 +380,6 @@
         mBestUserId = UserHandle.USER_NULL;
     }
 
-    @MainThread
     @Override
     public final void onServiceConnected(ComponentName component, IBinder binder) {
         mHandler.post(() -> {
@@ -410,7 +393,6 @@
         });
     }
 
-    @MainThread
     @Override
     public final void onServiceDisconnected(ComponentName component) {
         mHandler.post(() -> {
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index a2cbfaa..b04ae17 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -67,7 +67,9 @@
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.NoSuchElementException;
 
 /**
@@ -196,6 +198,8 @@
 
     private ArrayList<List<PhysicalChannelConfig>> mPhysicalChannelConfigs;
 
+    private Map<Integer, List<EmergencyNumber>> mEmergencyNumberList;
+
     private int[] mSrvccState;
 
     private int mDefaultSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
@@ -229,8 +233,9 @@
                     | PhoneStateListener.LISTEN_CELL_INFO;
 
     static final int ENFORCE_PHONE_STATE_PERMISSION_MASK =
-                PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR |
-                PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR;
+                PhoneStateListener.LISTEN_CALL_FORWARDING_INDICATOR
+                        | PhoneStateListener.LISTEN_MESSAGE_WAITING_INDICATOR
+                        | PhoneStateListener.LISTEN_EMERGENCY_NUMBER_LIST;
 
     static final int PRECISE_PHONE_STATE_PERMISSION_MASK =
                 PhoneStateListener.LISTEN_PRECISE_CALL_STATE |
@@ -357,6 +362,7 @@
         mCellInfo = new ArrayList<List<CellInfo>>();
         mSrvccState = new int[numPhones];
         mPhysicalChannelConfigs = new ArrayList<List<PhysicalChannelConfig>>();
+        mEmergencyNumberList = new HashMap<>();
         for (int i = 0; i < numPhones; i++) {
             mCallState[i] =  TelephonyManager.CALL_STATE_IDLE;
             mDataActivity[i] = TelephonyManager.DATA_ACTIVITY_NONE;
@@ -752,6 +758,13 @@
                             remove(r.binder);
                         }
                     }
+                    if ((events & PhoneStateListener.LISTEN_EMERGENCY_NUMBER_LIST) != 0) {
+                        try {
+                            r.callback.onEmergencyNumberListChanged(mEmergencyNumberList);
+                        } catch (RemoteException ex) {
+                            remove(r.binder);
+                        }
+                    }
                     if ((events & PhoneStateListener.LISTEN_PHONE_CAPABILITY_CHANGE) != 0) {
                         try {
                             r.callback.onPhoneCapabilityChanged(mPhoneCapability);
@@ -1665,10 +1678,30 @@
     }
 
     @Override
-    public void notifyEmergencyNumberList(List<EmergencyNumber> emergencyNumberList) {
-        // TODO checkPermission, modify Listener constent documentation
-        // TODO implement multisim emergency number list update in listener
-        // TODO implement PhoneStateListenerTest
+    public void notifyEmergencyNumberList() {
+        if (!checkNotifyPermission("notifyEmergencyNumberList()")) {
+            return;
+        }
+
+        synchronized (mRecords) {
+            mEmergencyNumberList = TelephonyManager.getDefault().getCurrentEmergencyNumberList();
+
+            for (Record r : mRecords) {
+                if (r.matchPhoneStateListenerEvent(
+                        PhoneStateListener.LISTEN_EMERGENCY_NUMBER_LIST)) {
+                    try {
+                        r.callback.onEmergencyNumberListChanged(mEmergencyNumberList);
+                        if (VDBG) {
+                            log("notifyEmergencyNumberList: emergencyNumberList= "
+                                    + mEmergencyNumberList);
+                        }
+                    } catch (RemoteException ex) {
+                        mRemoveList.add(r.binder);
+                    }
+                }
+            }
+            handleRemoveListLocked();
+        }
     }
 
 
@@ -1710,6 +1743,7 @@
             pw.println("mPhoneCapability=" + mPhoneCapability);
             pw.println("mPreferredDataSubId=" + mPreferredDataSubId);
             pw.println("mRadioPowerState=" + mRadioPowerState);
+            pw.println("mEmergencyNumberList=" + mEmergencyNumberList);
 
             pw.decreaseIndent();
 
diff --git a/services/core/java/com/android/server/location/AbstractLocationProvider.java b/services/core/java/com/android/server/location/AbstractLocationProvider.java
new file mode 100644
index 0000000..4c7c420
--- /dev/null
+++ b/services/core/java/com/android/server/location/AbstractLocationProvider.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2010 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;
+
+import android.location.Location;
+import android.location.LocationProvider;
+import android.os.Bundle;
+import android.os.WorkSource;
+
+import com.android.internal.location.ProviderProperties;
+import com.android.internal.location.ProviderRequest;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.List;
+
+/**
+ * Location Manager's interface for location providers.
+ *
+ * @hide
+ */
+public abstract class AbstractLocationProvider {
+
+    /**
+     * Interface for communicating from a location provider back to the location service.
+     */
+    public interface LocationProviderManager {
+
+        /**
+         * Called on location provider construction to make the location service aware of this
+         * provider and what it's initial enabled/disabled state should be.
+         */
+        void onAttachProvider(AbstractLocationProvider locationProvider, boolean initiallyEnabled);
+
+        /**
+         * May be called to inform the location service of a change in this location provider's
+         * enabled/disabled state.
+         */
+        void onSetEnabled(boolean enabled);
+
+        /**
+         * May be called to inform the location service of a change in this location provider's
+         * properties.
+         */
+        void onSetProperties(ProviderProperties properties);
+
+        /**
+         * May be called to inform the location service that this provider has a new location
+         * available.
+         */
+        void onReportLocation(Location location);
+
+        /**
+         * May be called to inform the location service that this provider has a new location
+         * available.
+         */
+        void onReportLocation(List<Location> locations);
+    }
+
+    private final LocationProviderManager mLocationProviderManager;
+
+    protected AbstractLocationProvider(LocationProviderManager locationProviderManager) {
+        this(locationProviderManager, true);
+    }
+
+    protected AbstractLocationProvider(LocationProviderManager locationProviderManager,
+            boolean initiallyEnabled) {
+        mLocationProviderManager = locationProviderManager;
+        mLocationProviderManager.onAttachProvider(this, initiallyEnabled);
+    }
+
+    /**
+     * Call this method to report a new location. May be called from any thread.
+     */
+    protected void reportLocation(Location location) {
+        mLocationProviderManager.onReportLocation(location);
+    }
+
+    /**
+     * Call this method to report a new location. May be called from any thread.
+     */
+    protected void reportLocation(List<Location> locations) {
+        mLocationProviderManager.onReportLocation(locations);
+    }
+
+    /**
+     * Call this method to report a change in provider enabled/disabled status. May be called from
+     * any thread.
+     */
+    protected void setEnabled(boolean enabled) {
+        mLocationProviderManager.onSetEnabled(enabled);
+    }
+
+    /**
+     * Call this method to report a change in provider properties. May be called from
+     * any thread.
+     */
+    protected void setProperties(ProviderProperties properties) {
+        mLocationProviderManager.onSetProperties(properties);
+    }
+
+    /**
+     * Called when the location service delivers a new request for fulfillment to the provider.
+     * Replaces any previous requests completely.
+     */
+    public abstract void setRequest(ProviderRequest request, WorkSource source);
+
+    /**
+     * Called to dump debug or log information.
+     */
+    public abstract void dump(FileDescriptor fd, PrintWriter pw, String[] args);
+
+    /**
+     * Retrieves the current status of the provider.
+     *
+     * @deprecated Will be removed in a future release.
+     */
+    @Deprecated
+    public int getStatus(Bundle extras) {
+        return LocationProvider.AVAILABLE;
+    }
+
+    /**
+     * Retrieves the last update time of the status of the provider.
+     *
+     * @deprecated Will be removed in a future release.
+     */
+    @Deprecated
+    public long getStatusUpdateTime() {
+        return 0;
+    }
+
+    /** Sends a custom command to this provider. */
+    public abstract void sendExtraCommand(String command, Bundle extras);
+}
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index b7c77a2..29e1878 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -32,7 +32,6 @@
 import android.location.GnssNavigationMessage;
 import android.location.GnssStatus;
 import android.location.IGpsGeofenceHardware;
-import android.location.ILocationManager;
 import android.location.INetInitiatedListener;
 import android.location.Location;
 import android.location.LocationListener;
@@ -82,6 +81,10 @@
 import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.PrintWriter;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
@@ -95,8 +98,18 @@
  *
  * {@hide}
  */
-public class GnssLocationProvider extends LocationProviderInterface
-        implements InjectNtpTimeCallback, GnssSatelliteBlacklistCallback {
+public class GnssLocationProvider extends AbstractLocationProvider implements
+        InjectNtpTimeCallback,
+        GnssSatelliteBlacklistCallback {
+
+    /**
+     * 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 {
+    }
 
     private static final String TAG = "GnssLocationProvider";
 
@@ -247,7 +260,7 @@
         }
 
         public void set(int svCount, int meanCn0, int maxCn0) {
-            synchronized(this) {
+            synchronized (this) {
                 mSvCount = svCount;
                 mMeanCn0 = meanCn0;
                 mMaxCn0 = maxCn0;
@@ -256,7 +269,7 @@
         }
 
         public void reset() {
-            set(0,0,0);
+            set(0, 0, 0);
         }
 
         // Also used by outside methods to add to other bundles
@@ -312,7 +325,7 @@
             MAX_RETRY_INTERVAL);
 
     // true if we are enabled, protected by this
-    private boolean mEnabled;
+    private boolean mEnabled = true;
 
     // states for injecting ntp and downloading xtra data
     private static final int STATE_PENDING_NETWORK = 0;
@@ -326,9 +339,6 @@
     // true if GPS is navigating
     private boolean mNavigating;
 
-    // true if GPS engine is on
-    private boolean mEngineOn;
-
     // requested frequency of fixes, in milliseconds
     private int mFixInterval = 1000;
 
@@ -378,7 +388,6 @@
     private boolean mSuplEsEnabled = false;
 
     private final Context mContext;
-    private final ILocationManager mILocationManager;
     private final LocationExtras mLocationExtras = new LocationExtras();
     private final GnssStatusListenerHelper mGnssStatusListenerHelper;
     private final GnssSatelliteBlacklistHelper mGnssSatelliteBlacklistHelper;
@@ -465,17 +474,22 @@
                 return;
             }
 
-            if (action.equals(ALARM_WAKEUP)) {
-                startNavigating(false);
-            } else if (action.equals(ALARM_TIMEOUT)) {
-                hibernate();
-            } else if (PowerManager.ACTION_POWER_SAVE_MODE_CHANGED.equals(action)
-                    || PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED.equals(action)
-                    || Intent.ACTION_SCREEN_OFF.equals(action)
-                    || Intent.ACTION_SCREEN_ON.equals(action)) {
-                updateLowPowerMode();
-            } else if (action.equals(SIM_STATE_CHANGED)) {
-                subscriptionOrSimChanged(context);
+            switch (action) {
+                case ALARM_WAKEUP:
+                    startNavigating(false);
+                    break;
+                case ALARM_TIMEOUT:
+                    hibernate();
+                    break;
+                case PowerManager.ACTION_POWER_SAVE_MODE_CHANGED:
+                case PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED:
+                case Intent.ACTION_SCREEN_OFF:
+                case Intent.ACTION_SCREEN_ON:
+                    updateLowPowerMode();
+                    break;
+                case SIM_STATE_CHANGED:
+                    subscriptionOrSimChanged(context);
+                    break;
             }
         }
     };
@@ -493,9 +507,7 @@
      */
     @Override
     public void onUpdateSatelliteBlacklist(int[] constellations, int[] svids) {
-        mHandler.post(()->{
-            native_set_satellite_blacklist(constellations, svids);
-        });
+        mHandler.post(() -> native_set_satellite_blacklist(constellations, svids));
     }
 
     private void subscriptionOrSimChanged(Context context) {
@@ -558,7 +570,7 @@
     }
 
     interface SetCarrierProperty {
-        public boolean set(int value);
+        boolean set(int value);
     }
 
     private void reloadGpsProperties(Context context, Properties properties) {
@@ -573,7 +585,7 @@
         /*
          * Overlay carrier properties from a debug configuration file.
          */
-        loadPropertiesFromFile(DEBUG_PROPERTIES_FILE, properties);
+        loadPropertiesFromFile(properties);
         // TODO: we should get rid of C2K specific setting.
         setSuplHostPort(properties.getProperty("SUPL_HOST"),
                 properties.getProperty("SUPL_PORT"));
@@ -589,15 +601,15 @@
         if (native_is_gnss_configuration_supported()) {
             Map<String, SetCarrierProperty> map = new HashMap<String, SetCarrierProperty>() {
                 {
-                    put("SUPL_VER", (val) -> native_set_supl_version(val));
-                    put("SUPL_MODE", (val) -> native_set_supl_mode(val));
-                    put("SUPL_ES", (val) -> native_set_supl_es(val));
-                    put("LPP_PROFILE", (val) -> native_set_lpp_profile(val));
+                    put("SUPL_VER", GnssLocationProvider::native_set_supl_version);
+                    put("SUPL_MODE", GnssLocationProvider::native_set_supl_mode);
+                    put("SUPL_ES", GnssLocationProvider::native_set_supl_es);
+                    put("LPP_PROFILE", GnssLocationProvider::native_set_lpp_profile);
                     put("A_GLONASS_POS_PROTOCOL_SELECT",
-                            (val) -> native_set_gnss_pos_protocol_select(val));
+                            GnssLocationProvider::native_set_gnss_pos_protocol_select);
                     put("USE_EMERGENCY_PDN_FOR_EMERGENCY_SUPL",
-                            (val) -> native_set_emergency_supl_pdn(val));
-                    put("GPS_LOCK", (val) -> native_set_gps_lock(val));
+                            GnssLocationProvider::native_set_emergency_supl_pdn);
+                    put("GPS_LOCK", GnssLocationProvider::native_set_gps_lock);
                 }
             };
 
@@ -608,7 +620,7 @@
                     try {
                         int propertyValueInt = Integer.decode(propertyValueString);
                         boolean result = entry.getValue().set(propertyValueInt);
-                        if (result == false) {
+                        if (!result) {
                             Log.e(TAG, "Unable to set " + propertyName);
                         }
                     } catch (NumberFormatException e) {
@@ -650,10 +662,9 @@
         }
     }
 
-    private boolean loadPropertiesFromFile(String filename,
-            Properties properties) {
+    private void loadPropertiesFromFile(Properties properties) {
         try {
-            File file = new File(filename);
+            File file = new File(DEBUG_PROPERTIES_FILE);
             FileInputStream stream = null;
             try {
                 stream = new FileInputStream(file);
@@ -663,16 +674,15 @@
             }
 
         } catch (IOException e) {
-            if (DEBUG) Log.d(TAG, "Could not open GPS configuration file " + filename);
-            return false;
+            if (DEBUG) Log.d(TAG, "Could not open GPS configuration file " + DEBUG_PROPERTIES_FILE);
         }
-        return true;
     }
 
-    public GnssLocationProvider(Context context, ILocationManager ilocationManager,
+    public GnssLocationProvider(Context context, LocationProviderManager locationProviderManager,
             Looper looper) {
+        super(locationProviderManager, true);
+
         mContext = context;
-        mILocationManager = ilocationManager;
 
         // Create a wake lock
         mPowerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
@@ -749,19 +759,20 @@
         mHandler.post(mGnssSatelliteBlacklistHelper::updateSatelliteBlacklist);
         mGnssBatchingProvider = new GnssBatchingProvider();
         mGnssGeofenceProvider = new GnssGeofenceProvider();
-    }
 
-    /**
-     * Returns the name of this provider.
-     */
-    @Override
-    public String getName() {
-        return LocationManager.GPS_PROVIDER;
-    }
+        IntentFilter intentFilter = new IntentFilter();
+        intentFilter.addAction(Intent.ACTION_SHUTDOWN);
+        mContext.registerReceiverAsUser(new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                if (getSendingUserId() == UserHandle.USER_ALL) {
+                    mEnabled = false;
+                    handleDisable();
+                }
+            }
+        }, UserHandle.ALL, intentFilter, null, mHandler);
 
-    @Override
-    public ProviderProperties getProperties() {
-        return PROPERTIES;
+        setProperties(PROPERTIES);
     }
 
     /**
@@ -826,9 +837,9 @@
             locationManager.requestLocationUpdates(provider,
                     LOCATION_UPDATE_MIN_TIME_INTERVAL_MILLIS, /*minDistance=*/ 0,
                     locationListener, mHandler.getLooper());
-            locationListener.numLocationUpdateRequest++;
+            locationListener.mNumLocationUpdateRequest++;
             mHandler.postDelayed(() -> {
-                if (--locationListener.numLocationUpdateRequest == 0) {
+                if (--locationListener.mNumLocationUpdateRequest == 0) {
                     Log.i(TAG,
                             String.format("Removing location updates from %s provider.", provider));
                     locationManager.removeUpdates(locationListener);
@@ -891,43 +902,40 @@
         // hold wake lock while task runs
         mDownloadXtraWakeLock.acquire(DOWNLOAD_XTRA_DATA_TIMEOUT_MS);
         Log.i(TAG, "WakeLock acquired by handleDownloadXtraData()");
-        AsyncTask.THREAD_POOL_EXECUTOR.execute(new Runnable() {
-            @Override
-            public void run() {
-                GpsXtraDownloader xtraDownloader = new GpsXtraDownloader(mProperties);
-                byte[] data = xtraDownloader.downloadXtraData();
-                if (data != null) {
-                    if (DEBUG) Log.d(TAG, "calling native_inject_xtra_data");
-                    native_inject_xtra_data(data, data.length);
-                    mXtraBackOff.reset();
-                }
+        AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
+            GpsXtraDownloader xtraDownloader = new GpsXtraDownloader(mProperties);
+            byte[] data = xtraDownloader.downloadXtraData();
+            if (data != null) {
+                if (DEBUG) Log.d(TAG, "calling native_inject_xtra_data");
+                native_inject_xtra_data(data, data.length);
+                mXtraBackOff.reset();
+            }
 
-                sendMessage(DOWNLOAD_XTRA_DATA_FINISHED, 0, null);
+            sendMessage(DOWNLOAD_XTRA_DATA_FINISHED, 0, null);
 
-                if (data == null) {
-                    // try again later
-                    // since this is delayed and not urgent we do not hold a wake lock here
-                    mHandler.sendEmptyMessageDelayed(DOWNLOAD_XTRA_DATA,
-                            mXtraBackOff.nextBackoffMillis());
-                }
+            if (data == null) {
+                // try again later
+                // since this is delayed and not urgent we do not hold a wake lock here
+                mHandler.sendEmptyMessageDelayed(DOWNLOAD_XTRA_DATA,
+                        mXtraBackOff.nextBackoffMillis());
+            }
 
-                // Release wake lock held by task, synchronize on mLock in case multiple
-                // download tasks overrun.
-                synchronized (mLock) {
-                    if (mDownloadXtraWakeLock.isHeld()) {
-                        // This wakelock may have time-out, if a timeout was specified.
-                        // Catch (and ignore) any timeout exceptions.
-                        try {
-                            mDownloadXtraWakeLock.release();
-                            if (DEBUG) Log.d(TAG, "WakeLock released by handleDownloadXtraData()");
-                        } catch (Exception e) {
-                            Log.i(TAG, "Wakelock timeout & release race exception in "
-                                    + "handleDownloadXtraData()", e);
-                        }
-                    } else {
-                        Log.e(TAG, "WakeLock expired before release in "
-                                + "handleDownloadXtraData()");
+            // Release wake lock held by task, synchronize on mLock in case multiple
+            // download tasks overrun.
+            synchronized (mLock) {
+                if (mDownloadXtraWakeLock.isHeld()) {
+                    // This wakelock may have time-out, if a timeout was specified.
+                    // Catch (and ignore) any timeout exceptions.
+                    try {
+                        mDownloadXtraWakeLock.release();
+                        if (DEBUG) Log.d(TAG, "WakeLock released by handleDownloadXtraData()");
+                    } catch (Exception e) {
+                        Log.i(TAG, "Wakelock timeout & release race exception in "
+                                + "handleDownloadXtraData()", e);
                     }
+                } else {
+                    Log.e(TAG, "WakeLock expired before release in "
+                            + "handleDownloadXtraData()");
                 }
             }
         });
@@ -940,21 +948,6 @@
         }
     }
 
-    /**
-     * Enables this provider.  When enabled, calls to getStatus()
-     * must be handled.  Hardware may be started up
-     * when the provider is enabled.
-     */
-    @Override
-    public void enable() {
-        synchronized (mLock) {
-            if (mEnabled) return;
-            mEnabled = true;
-        }
-
-        sendMessage(ENABLE, 1, null);
-    }
-
     private void setSuplHostPort(String hostString, String portString) {
         if (hostString != null) {
             mSuplServerHost = hostString;
@@ -1038,21 +1031,6 @@
         }
     }
 
-    /**
-     * Disables this provider.  When disabled, calls to getStatus()
-     * need not be handled.  Hardware may be shut
-     * down while the provider is disabled.
-     */
-    @Override
-    public void disable() {
-        synchronized (mLock) {
-            if (!mEnabled) return;
-            mEnabled = false;
-        }
-
-        sendMessage(ENABLE, 0, null);
-    }
-
     private void handleDisable() {
         if (DEBUG) Log.d(TAG, "handleDisable");
 
@@ -1069,7 +1047,6 @@
         mGnssNavigationMessageProvider.onGpsEnabledChanged();
     }
 
-    @Override
     public boolean isEnabled() {
         synchronized (mLock) {
             return mEnabled;
@@ -1133,7 +1110,7 @@
             updateClientUids(mWorkSource);
 
             mFixInterval = (int) mProviderRequest.interval;
-            mLowPowerMode = (boolean) mProviderRequest.lowPowerMode;
+            mLowPowerMode = mProviderRequest.lowPowerMode;
             // check for overflow
             if (mFixInterval != mProviderRequest.interval) {
                 Log.w(TAG, "interval overflow: " + mProviderRequest.interval);
@@ -1157,7 +1134,8 @@
                     // set timer to give up if we do not receive a fix within NO_FIX_TIMEOUT
                     // and our fix interval is not short
                     mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
-                            SystemClock.elapsedRealtime() + NO_FIX_TIMEOUT, mTimeoutIntent);                }
+                            SystemClock.elapsedRealtime() + NO_FIX_TIMEOUT, mTimeoutIntent);
+                }
             }
         } else {
             updateClientUids(new WorkSource());
@@ -1206,16 +1184,14 @@
             List<WorkChain> goneChains = diffs[1];
 
             if (newChains != null) {
-                for (int i = 0; i < newChains.size(); ++i) {
-                    final WorkChain newChain = newChains.get(i);
+                for (WorkChain newChain : newChains) {
                     mAppOps.startOpNoThrow(AppOpsManager.OP_GPS, newChain.getAttributionUid(),
                             newChain.getAttributionTag());
                 }
             }
 
             if (goneChains != null) {
-                for (int i = 0; i < goneChains.size(); i++) {
-                    final WorkChain goneChain = goneChains.get(i);
+                for (WorkChain goneChain : goneChains) {
                     mAppOps.finishOp(AppOpsManager.OP_GPS, goneChain.getAttributionUid(),
                             goneChain.getAttributionTag());
                 }
@@ -1248,32 +1224,27 @@
     }
 
     @Override
-    public boolean sendExtraCommand(String command, Bundle extras) {
+    public void sendExtraCommand(String command, Bundle extras) {
 
         long identity = Binder.clearCallingIdentity();
         try {
-            boolean result = false;
-
             if ("delete_aiding_data".equals(command)) {
-                result = deleteAidingData(extras);
+                deleteAidingData(extras);
             } else if ("force_time_injection".equals(command)) {
                 requestUtcTime();
-                result = true;
             } else if ("force_xtra_injection".equals(command)) {
                 if (mSupportsXtra) {
                     xtraDownloadRequest();
-                    result = true;
                 }
             } else {
                 Log.w(TAG, "sendExtraCommand: unknown command " + command);
             }
-            return result;
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
     }
 
-    private boolean deleteAidingData(Bundle extras) {
+    private void deleteAidingData(Bundle extras) {
         int flags;
 
         if (extras == null) {
@@ -1297,10 +1268,7 @@
 
         if (flags != 0) {
             native_delete_aiding_data(flags);
-            return true;
         }
-
-        return false;
     }
 
     private void startNavigating(boolean singleShot) {
@@ -1344,7 +1312,7 @@
             }
 
             int interval = (hasCapability(GPS_CAPABILITY_SCHEDULING) ? mFixInterval : 1000);
-            mLowPowerMode = (boolean) mProviderRequest.lowPowerMode;
+            mLowPowerMode = mProviderRequest.lowPowerMode;
             if (!setPositionMode(mPositionMode, GPS_POSITION_RECURRENCE_PERIODIC,
                     interval, 0, 0, mLowPowerMode)) {
                 mStarted = false;
@@ -1401,10 +1369,7 @@
         return ((mEngineCapabilities & capability) != 0);
     }
 
-
-    /**
-     * called from native code to update our position.
-     */
+    @NativeEntryPoint
     private void reportLocation(boolean hasLatLong, Location location) {
         sendMessage(REPORT_LOCATION, hasLatLong ? 1 : 0, location);
     }
@@ -1430,11 +1395,7 @@
         location.setElapsedRealtimeNanos(SystemClock.elapsedRealtimeNanos());
         location.setExtras(mLocationExtras.getBundle());
 
-        try {
-            mILocationManager.reportLocation(location, false);
-        } catch (RemoteException e) {
-            Log.e(TAG, "RemoteException calling reportLocation");
-        }
+        reportLocation(location);
 
         if (mStarted) {
             mGnssMetrics.logReceivedLocationStatus(hasLatLong);
@@ -1468,7 +1429,8 @@
 
         if (mStarted && mStatus != LocationProvider.AVAILABLE) {
             // For devices that use framework scheduling, a timer may be set to ensure we don't
-            // spend too much power searching for a location, when the requested update rate is slow.
+            // 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) {
                 mAlarmManager.cancel(mTimeoutIntent);
@@ -1488,9 +1450,7 @@
         }
     }
 
-    /**
-     * called from native code to update our status
-     */
+    @NativeEntryPoint
     private void reportStatus(int status) {
         if (DEBUG) Log.v(TAG, "reportStatus status: " + status);
 
@@ -1498,16 +1458,13 @@
         switch (status) {
             case GPS_STATUS_SESSION_BEGIN:
                 mNavigating = true;
-                mEngineOn = true;
                 break;
             case GPS_STATUS_SESSION_END:
                 mNavigating = false;
                 break;
             case GPS_STATUS_ENGINE_ON:
-                mEngineOn = true;
                 break;
             case GPS_STATUS_ENGINE_OFF:
-                mEngineOn = false;
                 mNavigating = false;
                 break;
         }
@@ -1524,17 +1481,15 @@
 
     // Helper class to carry data to handler for reportSvStatus
     private static class SvStatusInfo {
-        public int mSvCount;
-        public int[] mSvidWithFlags;
-        public float[] mCn0s;
-        public float[] mSvElevations;
-        public float[] mSvAzimuths;
-        public float[] mSvCarrierFreqs;
+        private int mSvCount;
+        private int[] mSvidWithFlags;
+        private float[] mCn0s;
+        private float[] mSvElevations;
+        private float[] mSvAzimuths;
+        private float[] mSvCarrierFreqs;
     }
 
-    /**
-     * called from native code to update SV info
-     */
+    @NativeEntryPoint
     private void reportSvStatus(int svCount, int[] svidWithFlags, float[] cn0s,
             float[] svElevations, float[] svAzimuths, float[] svCarrierFreqs) {
         SvStatusInfo svStatusInfo = new SvStatusInfo();
@@ -1608,16 +1563,12 @@
         }
     }
 
-    /**
-     * called from native code to update AGPS status
-     */
+    @NativeEntryPoint
     private void reportAGpsStatus(int type, int status, byte[] ipaddr) {
         mNetworkConnectivityHandler.onReportAGpsStatus(type, status, ipaddr);
     }
 
-    /**
-     * called from native code to report NMEA data received
-     */
+    @NativeEntryPoint
     private void reportNmea(long timestamp) {
         if (!mItarSpeedLimitExceeded) {
             int length = native_read_nmea(mNmeaBuffer, mNmeaBuffer.length);
@@ -1626,57 +1577,38 @@
         }
     }
 
-    /**
-     * called from native code - GNSS measurements callback
-     */
+    @NativeEntryPoint
     private void reportMeasurementData(GnssMeasurementsEvent event) {
         if (!mItarSpeedLimitExceeded) {
             // send to handler to allow native to return quickly
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    mGnssMeasurementsProvider.onMeasurementsAvailable(event);
-                }
-            });
+            mHandler.post(() -> mGnssMeasurementsProvider.onMeasurementsAvailable(event));
         }
     }
 
-    /**
-     * called from native code - GNSS navigation message callback
-     */
+    @NativeEntryPoint
     private void reportNavigationMessage(GnssNavigationMessage event) {
         if (!mItarSpeedLimitExceeded) {
             // send to handler to allow native to return quickly
-            mHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    mGnssNavigationMessageProvider.onNavigationMessageAvailable(event);
-                }
-            });
+            mHandler.post(() -> mGnssNavigationMessageProvider.onNavigationMessageAvailable(event));
         }
     }
 
-    /**
-     * called from native code to inform us what the GPS engine capabilities are
-     */
+    @NativeEntryPoint
     private void setEngineCapabilities(final int capabilities) {
         // send to handler thread for fast native return, and in-order handling
-        mHandler.post(new Runnable() {
-            @Override
-            public void run() {
-                mEngineCapabilities = capabilities;
+        mHandler.post(() -> {
+            mEngineCapabilities = capabilities;
 
-                if (hasCapability(GPS_CAPABILITY_ON_DEMAND_TIME)) {
-                    mNtpTimeHelper.enablePeriodicTimeInjection();
-                    requestUtcTime();
-                }
-
-                mGnssMeasurementsProvider.onCapabilitiesUpdated(hasCapability(
-                        GPS_CAPABILITY_MEASUREMENTS));
-                mGnssNavigationMessageProvider.onCapabilitiesUpdated(hasCapability(
-                        GPS_CAPABILITY_NAV_MESSAGES));
-                restartRequests();
+            if (hasCapability(GPS_CAPABILITY_ON_DEMAND_TIME)) {
+                mNtpTimeHelper.enablePeriodicTimeInjection();
+                requestUtcTime();
             }
+
+            mGnssMeasurementsProvider.onCapabilitiesUpdated(hasCapability(
+                    GPS_CAPABILITY_MEASUREMENTS));
+            mGnssNavigationMessageProvider.onCapabilitiesUpdated(hasCapability(
+                    GPS_CAPABILITY_NAV_MESSAGES));
+            restartRequests();
         });
     }
 
@@ -1696,27 +1628,21 @@
         updateRequirements();
     }
 
-    /**
-     * Called from native code to inform us the hardware year.
-     */
+    @NativeEntryPoint
     private 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;
     }
 
-    /**
-     * Called from native code to inform us the hardware model name.
-     */
+    @NativeEntryPoint
     private 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;
     }
 
-    /**
-     * Called from native code to inform us GNSS HAL service died.
-     */
+    @NativeEntryPoint
     private void reportGnssServiceDied() {
         if (DEBUG) Log.d(TAG, "reportGnssServiceDied");
         mHandler.post(() -> {
@@ -1736,6 +1662,7 @@
          * Returns the year of underlying GPS hardware.
          */
         int getGnssYearOfHardware();
+
         /**
          * Returns the model name of underlying GPS hardware.
          */
@@ -1751,6 +1678,7 @@
             public int getGnssYearOfHardware() {
                 return mHardwareYear;
             }
+
             @Override
             public String getGnssHardwareModelName() {
                 return mHardwareModelName;
@@ -1776,32 +1704,19 @@
      * @hide
      */
     public GnssMetricsProvider getGnssMetricsProvider() {
-        return new GnssMetricsProvider() {
-            @Override
-            public String getGnssMetricsAsProtoString() {
-                return mGnssMetrics.dumpGnssMetricsAsProtoString();
-            }
-        };
+        return () -> mGnssMetrics.dumpGnssMetricsAsProtoString();
     }
 
-    /**
-     * called from native code - GNSS location batch callback
-     */
+    @NativeEntryPoint
     private void reportLocationBatch(Location[] locationArray) {
         List<Location> locations = new ArrayList<>(Arrays.asList(locationArray));
         if (DEBUG) {
             Log.d(TAG, "Location batch of size " + locationArray.length + " reported");
         }
-        try {
-            mILocationManager.reportLocationBatch(locations);
-        } catch (RemoteException e) {
-            Log.e(TAG, "RemoteException calling reportLocationBatch");
-        }
+        reportLocation(locations);
     }
 
-    /**
-     * called from native code to request XTRA data
-     */
+    @NativeEntryPoint
     private void xtraDownloadRequest() {
         if (DEBUG) Log.d(TAG, "xtraDownloadRequest");
         sendMessage(DOWNLOAD_XTRA_DATA, 0, null);
@@ -1829,10 +1744,7 @@
         }
     }
 
-    /**
-     * Called from native to report GPS Geofence transition
-     * All geofence callbacks are called on the same thread
-     */
+    @NativeEntryPoint
     private void reportGeofenceTransition(int geofenceId, Location location, int transition,
             long transitionTimestamp) {
         mHandler.post(() -> {
@@ -1850,9 +1762,7 @@
         });
     }
 
-    /**
-     * called from native code to report GPS status change.
-     */
+    @NativeEntryPoint
     private void reportGeofenceStatus(int status, Location location) {
         mHandler.post(() -> {
             if (mGeofenceHardwareImpl == null) {
@@ -1870,9 +1780,7 @@
         });
     }
 
-    /**
-     * called from native code - Geofence Add callback
-     */
+    @NativeEntryPoint
     private void reportGeofenceAddStatus(int geofenceId, int status) {
         mHandler.post(() -> {
             if (mGeofenceHardwareImpl == null) {
@@ -1882,9 +1790,7 @@
         });
     }
 
-    /**
-     * called from native code - Geofence Remove callback
-     */
+    @NativeEntryPoint
     private void reportGeofenceRemoveStatus(int geofenceId, int status) {
         mHandler.post(() -> {
             if (mGeofenceHardwareImpl == null) {
@@ -1894,9 +1800,7 @@
         });
     }
 
-    /**
-     * called from native code - Geofence Pause callback
-     */
+    @NativeEntryPoint
     private void reportGeofencePauseStatus(int geofenceId, int status) {
         mHandler.post(() -> {
             if (mGeofenceHardwareImpl == null) {
@@ -1906,9 +1810,7 @@
         });
     }
 
-    /**
-     * called from native code - Geofence Resume callback
-     */
+    @NativeEntryPoint
     private void reportGeofenceResumeStatus(int geofenceId, int status) {
         mHandler.post(() -> {
             if (mGeofenceHardwareImpl == null) {
@@ -1940,7 +1842,8 @@
         return mNetInitiatedListener;
     }
 
-    // Called by JNI function to report an NI request.
+    /** Reports a NI notification. */
+    @NativeEntryPoint
     public void reportNiNotification(
             int notificationId,
             int niType,
@@ -1983,11 +1886,10 @@
     }
 
     /**
-     * Called from native code to request set id info.
      * We should be careful about receiving null string from the TelephonyManager,
      * because sending null String to JNI function would cause a crash.
      */
-
+    @NativeEntryPoint
     private void requestSetID(int flags) {
         TelephonyManager phone = (TelephonyManager)
                 mContext.getSystemService(Context.TELEPHONY_SERVICE);
@@ -2016,9 +1918,7 @@
         native_agps_set_id(type, data);
     }
 
-    /**
-     * Called from native code to request location info.
-     */
+    @NativeEntryPoint
     private void requestLocation(boolean independentFromGnss) {
         if (DEBUG) {
             Log.d(TAG, "requestLocation. independentFromGnss: " + independentFromGnss);
@@ -2026,17 +1926,13 @@
         sendMessage(REQUEST_LOCATION, 0, independentFromGnss);
     }
 
-    /**
-     * Called from native code to request utc time info
-     */
+    @NativeEntryPoint
     private void requestUtcTime() {
         if (DEBUG) Log.d(TAG, "utcTimeRequest");
         sendMessage(INJECT_NTP_TIME, 0, null);
     }
 
-    /**
-     * Called from native code to request reference location info
-     */
+    @NativeEntryPoint
     private void requestRefLocation() {
         TelephonyManager phone = (TelephonyManager)
                 mContext.getSystemService(Context.TELEPHONY_SERVICE);
@@ -2090,11 +1986,7 @@
             int message = msg.what;
             switch (message) {
                 case ENABLE:
-                    if (msg.arg1 == 1) {
-                        handleEnable();
-                    } else {
-                        handleDisable();
-                    }
+                    handleEnable();
                     break;
                 case SET_REQUEST:
                     GpsRequest gpsRequest = (GpsRequest) msg.obj;
@@ -2139,7 +2031,8 @@
         }
 
         /**
-         * This method is bound to {@link #GnssLocationProvider(Context, ILocationManager, Looper)}.
+         * This method is bound to {@link #GnssLocationProvider(Context, LocationProviderManager,
+         * Looper)}.
          * It is in charge of loading properties and registering for events that will be posted to
          * this handler.
          */
@@ -2192,12 +2085,11 @@
                     (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
             long minTime = 0;
             float minDistance = 0;
-            boolean oneShot = false;
             LocationRequest request = LocationRequest.createFromDeprecatedProvider(
                     LocationManager.PASSIVE_PROVIDER,
                     minTime,
                     minDistance,
-                    oneShot);
+                    false);
             // Don't keep track of this request since it's done on behalf of other clients
             // (which are kept track of separately).
             request.setHideFromAppOps(true);
@@ -2205,11 +2097,14 @@
                     request,
                     new NetworkLocationListener(),
                     getLooper());
+
+            // enable gps provider, it will never be disabled (legacy behavior)
+            sendEmptyMessage(ENABLE);
         }
     }
 
     private abstract class LocationChangeListener implements LocationListener {
-        int numLocationUpdateRequest;
+        private int mNumLocationUpdateRequest;
 
         @Override
         public void onStatusChanged(String provider, int status, Bundle extras) {
diff --git a/services/core/java/com/android/server/location/LocationProviderInterface.java b/services/core/java/com/android/server/location/LocationProviderInterface.java
deleted file mode 100644
index 6785964..0000000
--- a/services/core/java/com/android/server/location/LocationProviderInterface.java
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright (C) 2010 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;
-
-import android.location.LocationProvider;
-import android.os.Bundle;
-import android.os.WorkSource;
-
-import com.android.internal.location.ProviderProperties;
-import com.android.internal.location.ProviderRequest;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-
-/**
- * Location Manager's interface for location providers.
- * @hide
- */
-public abstract class LocationProviderInterface {
-
-    /** Get name. */
-    public abstract String getName();
-
-    /** Enable. */
-    public abstract void enable();
-
-    /** Disable. */
-    public abstract void disable();
-
-    /** Is enabled. */
-    public abstract boolean isEnabled();
-
-    /** Set request. */
-    public abstract void setRequest(ProviderRequest request, WorkSource source);
-
-    /** dump. */
-    public abstract void dump(FileDescriptor fd, PrintWriter pw, String[] args);
-
-    /** Get properties. */
-    public abstract ProviderProperties getProperties();
-
-    /**
-     * Get status.
-     *
-     * @deprecated Will be removed in a future release.
-     */
-    @Deprecated
-    public int getStatus(Bundle extras) {
-        return LocationProvider.AVAILABLE;
-    }
-
-    /**
-     * Get status update time.
-     *
-     * @deprecated Will be removed in a future release.
-     */
-    @Deprecated
-    public long getStatusUpdateTime() {
-        return 0;
-    }
-
-    /** Send extra command. */
-    public abstract boolean sendExtraCommand(String command, Bundle extras);
-}
diff --git a/services/core/java/com/android/server/location/LocationProviderProxy.java b/services/core/java/com/android/server/location/LocationProviderProxy.java
index b408414..dfcef70 100644
--- a/services/core/java/com/android/server/location/LocationProviderProxy.java
+++ b/services/core/java/com/android/server/location/LocationProviderProxy.java
@@ -18,6 +18,7 @@
 
 import android.annotation.Nullable;
 import android.content.Context;
+import android.location.Location;
 import android.location.LocationProvider;
 import android.os.Bundle;
 import android.os.IBinder;
@@ -27,6 +28,7 @@
 
 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.os.BackgroundThread;
@@ -41,21 +43,35 @@
 /**
  * Proxy for ILocationProvider implementations.
  */
-public class LocationProviderProxy extends LocationProviderInterface {
+public class LocationProviderProxy extends AbstractLocationProvider {
+
     private static final String TAG = "LocationProviderProxy";
     private static final boolean D = LocationManagerService.D;
 
-    private final ServiceWatcher mServiceWatcher;
-
-    private final String mName;
-
     // used to ensure that updates to mRequest and mWorkSource are atomic
     private final Object mRequestLock = new Object();
 
+    private final ServiceWatcher mServiceWatcher;
 
-    private volatile boolean mEnabled = false;
-    @Nullable
-    private volatile ProviderProperties mProperties;
+    private final ILocationProviderManager.Stub mManager = new ILocationProviderManager.Stub() {
+        // executed on binder thread
+        @Override
+        public void onSetEnabled(boolean enabled) {
+            LocationProviderProxy.this.setEnabled(enabled);
+        }
+
+        // executed on binder thread
+        @Override
+        public void onSetProperties(ProviderProperties properties) {
+            LocationProviderProxy.this.setProperties(properties);
+        }
+
+        // executed on binder thread
+        @Override
+        public void onReportLocation(Location location) {
+            LocationProviderProxy.this.reportLocation(location);
+        }
+    };
 
     @GuardedBy("mRequestLock")
     @Nullable
@@ -69,10 +85,10 @@
      */
     @Nullable
     public static LocationProviderProxy createAndBind(
-            Context context, String name, String action,
+            Context context, LocationProviderManager locationProviderManager, String action,
             int overlaySwitchResId, int defaultServicePackageNameResId,
             int initialPackageNamesResId) {
-        LocationProviderProxy proxy = new LocationProviderProxy(context, name,
+        LocationProviderProxy proxy = new LocationProviderProxy(context, locationProviderManager,
                 action, overlaySwitchResId, defaultServicePackageNameResId,
                 initialPackageNamesResId);
         if (proxy.bind()) {
@@ -82,21 +98,27 @@
         }
     }
 
-    private LocationProviderProxy(Context context, String name,
+    private LocationProviderProxy(Context context, LocationProviderManager locationProviderManager,
             String action, int overlaySwitchResId, int defaultServicePackageNameResId,
             int initialPackageNamesResId) {
+        super(locationProviderManager, false);
 
         mServiceWatcher = new ServiceWatcher(context, TAG, action, overlaySwitchResId,
                 defaultServicePackageNameResId, initialPackageNamesResId,
                 BackgroundThread.getHandler()) {
+
             @Override
             protected void onBind() {
                 runOnBinder(LocationProviderProxy.this::initializeService);
             }
-        };
-        mName = name;
 
-        mProperties = null;
+            @Override
+            protected void onUnbind() {
+                setEnabled(false);
+                setProperties(null);
+            }
+        };
+
         mRequest = null;
         mWorkSource = new WorkSource();
     }
@@ -107,84 +129,27 @@
 
     private void initializeService(IBinder binder) {
         ILocationProvider service = ILocationProvider.Stub.asInterface(binder);
-        if (D) Log.d(TAG, "applying state to connected service");
-
-        ProviderProperties[] properties = new ProviderProperties[1];
-        ProviderRequest request;
-        WorkSource source;
-        synchronized (mRequestLock) {
-            request = mRequest;
-            source = mWorkSource;
-        }
+        if (D) Log.d(TAG, "applying state to connected service " + mServiceWatcher);
 
         try {
-            // load properties from provider
-            properties[0] = service.getProperties();
-            if (properties[0] == null) {
-                Log.e(TAG, mServiceWatcher.getCurrentPackageName()
-                        + " has invalid location provider properties");
-            }
+            service.setLocationProviderManager(mManager);
 
-            // apply current state to new service
-            if (mEnabled) {
-                service.enable();
-                if (request != null) {
-                    service.setRequest(request, source);
+            synchronized (mRequestLock) {
+                if (mRequest != null) {
+                    service.setRequest(mRequest, mWorkSource);
                 }
             }
         } catch (RemoteException e) {
             Log.w(TAG, e);
         }
-
-        mProperties = properties[0];
     }
 
+    @Nullable
     public String getConnectedPackageName() {
         return mServiceWatcher.getCurrentPackageName();
     }
 
     @Override
-    public String getName() {
-        return mName;
-    }
-
-    @Override
-    public ProviderProperties getProperties() {
-        return mProperties;
-    }
-
-    @Override
-    public void enable() {
-        mEnabled = true;
-        mServiceWatcher.runOnBinder(binder -> {
-            ILocationProvider service = ILocationProvider.Stub.asInterface(binder);
-            try {
-                service.enable();
-            } catch (RemoteException e) {
-                Log.w(TAG, e);
-            }
-        });
-    }
-
-    @Override
-    public void disable() {
-        mEnabled = false;
-        mServiceWatcher.runOnBinder(binder -> {
-            ILocationProvider service = ILocationProvider.Stub.asInterface(binder);
-            try {
-                service.disable();
-            } catch (RemoteException e) {
-                Log.w(TAG, e);
-            }
-        });
-    }
-
-    @Override
-    public boolean isEnabled() {
-        return mEnabled;
-    }
-
-    @Override
     public void setRequest(ProviderRequest request, WorkSource source) {
         synchronized (mRequestLock) {
             mRequest = request;
@@ -202,60 +167,53 @@
 
     @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        pw.append("REMOTE SERVICE");
-        pw.append(" name=").append(mName);
-        pw.append(" pkg=").append(mServiceWatcher.getCurrentPackageName());
-        pw.append(" version=").append(Integer.toString(mServiceWatcher.getCurrentPackageVersion()));
-        pw.append('\n');
+        pw.println(" service=" + mServiceWatcher);
         mServiceWatcher.runOnBinder(binder -> {
-            ILocationProvider service = ILocationProvider.Stub.asInterface(binder);
             try {
-                TransferPipe.dumpAsync(service.asBinder(), fd, args);
+                TransferPipe.dumpAsync(binder, fd, args);
             } catch (IOException | RemoteException e) {
-                pw.println("Failed to dump location provider: " + e);
+                pw.println(" failed to dump location provider: " + e);
             }
         });
     }
 
     @Override
     public int getStatus(Bundle extras) {
-        int[] result = new int[]{LocationProvider.TEMPORARILY_UNAVAILABLE};
+        int[] status = new int[] {LocationProvider.TEMPORARILY_UNAVAILABLE};
         mServiceWatcher.runOnBinder(binder -> {
             ILocationProvider service = ILocationProvider.Stub.asInterface(binder);
             try {
-                result[0] = service.getStatus(extras);
+                status[0] = service.getStatus(extras);
             } catch (RemoteException e) {
                 Log.w(TAG, e);
             }
         });
-        return result[0];
+        return status[0];
     }
 
     @Override
     public long getStatusUpdateTime() {
-        long[] result = new long[]{0L};
+        long[] updateTime = new long[] {0L};
         mServiceWatcher.runOnBinder(binder -> {
             ILocationProvider service = ILocationProvider.Stub.asInterface(binder);
             try {
-                result[0] = service.getStatusUpdateTime();
+                updateTime[0] = service.getStatusUpdateTime();
             } catch (RemoteException e) {
                 Log.w(TAG, e);
             }
         });
-        return result[0];
+        return updateTime[0];
     }
 
     @Override
-    public boolean sendExtraCommand(String command, Bundle extras) {
-        boolean[] result = new boolean[]{false};
+    public void sendExtraCommand(String command, Bundle extras) {
         mServiceWatcher.runOnBinder(binder -> {
             ILocationProvider service = ILocationProvider.Stub.asInterface(binder);
             try {
-                result[0] = service.sendExtraCommand(command, extras);
+                service.sendExtraCommand(command, extras);
             } catch (RemoteException e) {
                 Log.w(TAG, e);
             }
         });
-        return result[0];
     }
 }
diff --git a/services/core/java/com/android/server/location/MockProvider.java b/services/core/java/com/android/server/location/MockProvider.java
index 145aee3..bfbebf7 100644
--- a/services/core/java/com/android/server/location/MockProvider.java
+++ b/services/core/java/com/android/server/location/MockProvider.java
@@ -16,14 +16,11 @@
 
 package com.android.server.location;
 
-import android.location.ILocationManager;
+import android.annotation.Nullable;
 import android.location.Location;
 import android.location.LocationProvider;
 import android.os.Bundle;
-import android.os.RemoteException;
 import android.os.WorkSource;
-import android.util.Log;
-import android.util.PrintWriterPrinter;
 
 import com.android.internal.location.ProviderProperties;
 import com.android.internal.location.ProviderRequest;
@@ -36,63 +33,57 @@
  *
  * {@hide}
  */
-public class MockProvider extends LocationProviderInterface {
-    private final String mName;
-    private final ProviderProperties mProperties;
-    private final ILocationManager mLocationManager;
+public class MockProvider extends AbstractLocationProvider {
 
-    private final Location mLocation;
-
-    private boolean mHasLocation;
     private boolean mEnabled;
-
-
+    @Nullable private Location mLocation;
     private int mStatus;
     private long mStatusUpdateTime;
     private Bundle mExtras;
 
-    private static final String TAG = "MockProvider";
+    public MockProvider(
+            LocationProviderManager locationProviderManager, ProviderProperties properties) {
+        super(locationProviderManager, true);
 
-    public MockProvider(String name, ILocationManager locationManager,
-            ProviderProperties properties) {
-        if (properties == null) throw new NullPointerException("properties is null");
-
-        mName = name;
-        mLocationManager = locationManager;
-        mProperties = properties;
-        mLocation = new Location(name);
-
-        mStatus = LocationProvider.AVAILABLE;
-        mStatusUpdateTime = 0L;
-        mExtras = null;
-    }
-
-    @Override
-    public String getName() {
-        return mName;
-    }
-
-    @Override
-    public ProviderProperties getProperties() {
-        return mProperties;
-    }
-
-    @Override
-    public void disable() {
-        mEnabled = false;
-    }
-
-    @Override
-    public void enable() {
         mEnabled = true;
+        mLocation = null;
+        mStatus = LocationProvider.AVAILABLE;
+        mStatusUpdateTime = 0;
+        mExtras = null;
+
+        setProperties(properties);
+    }
+
+    /** Sets the enabled state of this mock provider. */
+    public void setEnabled(boolean enabled) {
+        mEnabled = enabled;
+        super.setEnabled(enabled);
+    }
+
+    /** Sets the location to report for this mock provider. */
+    public void setLocation(Location l) {
+        mLocation = new Location(l);
+        if (mEnabled) {
+            reportLocation(l);
+        }
+    }
+
+    /** Sets the status for this mock provider. */
+    public void setStatus(int status, Bundle extras, long updateTime) {
+        mStatus = status;
+        mStatusUpdateTime = updateTime;
+        mExtras = extras;
     }
 
     @Override
-    public boolean isEnabled() {
-        return mEnabled;
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        pw.println(" last location=" + mLocation);
     }
 
     @Override
+    public void setRequest(ProviderRequest request, WorkSource source) {}
+
+    @Override
     public int getStatus(Bundle extras) {
         if (mExtras != null) {
             extras.clear();
@@ -107,50 +98,6 @@
         return mStatusUpdateTime;
     }
 
-    public void setLocation(Location l) {
-        mLocation.set(l);
-        mHasLocation = true;
-        if (mEnabled) {
-            try {
-                mLocationManager.reportLocation(mLocation, false);
-            } catch (RemoteException e) {
-                Log.e(TAG, "RemoteException calling reportLocation");
-            }
-        }
-    }
-
-    public void clearLocation() {
-        mHasLocation = false;
-    }
-
-    /**
-     * @deprecated Will be removed in a future release.
-     */
-    @Deprecated
-    public void setStatus(int status, Bundle extras, long updateTime) {
-        mStatus = status;
-        mStatusUpdateTime = updateTime;
-        mExtras = extras;
-    }
-
     @Override
-    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        dump(pw, "");
-    }
-
-    public void dump(PrintWriter pw, String prefix) {
-        pw.println(prefix + mName);
-        pw.println(prefix + "mHasLocation=" + mHasLocation);
-        pw.println(prefix + "mLocation:");
-        mLocation.dump(new PrintWriterPrinter(pw), prefix + "  ");
-        pw.println(prefix + "mExtras=" + mExtras);
-    }
-
-    @Override
-    public void setRequest(ProviderRequest request, WorkSource source) { }
-
-    @Override
-    public boolean sendExtraCommand(String command, Bundle extras) {
-        return false;
-    }
+    public void sendExtraCommand(String command, Bundle extras) {}
 }
diff --git a/services/core/java/com/android/server/location/PassiveProvider.java b/services/core/java/com/android/server/location/PassiveProvider.java
index 99c9214..70d64b0 100644
--- a/services/core/java/com/android/server/location/PassiveProvider.java
+++ b/services/core/java/com/android/server/location/PassiveProvider.java
@@ -17,13 +17,9 @@
 package com.android.server.location;
 
 import android.location.Criteria;
-import android.location.ILocationManager;
 import android.location.Location;
-import android.location.LocationManager;
 import android.os.Bundle;
-import android.os.RemoteException;
 import android.os.WorkSource;
-import android.util.Log;
 
 import com.android.internal.location.ProviderProperties;
 import com.android.internal.location.ProviderRequest;
@@ -38,41 +34,20 @@
  *
  * {@hide}
  */
-public class PassiveProvider extends LocationProviderInterface {
-    private static final String TAG = "PassiveProvider";
+public class PassiveProvider extends AbstractLocationProvider {
 
     private static final ProviderProperties PROPERTIES = new ProviderProperties(
             false, false, false, false, false, false, false,
             Criteria.POWER_LOW, Criteria.ACCURACY_COARSE);
 
-    private final ILocationManager mLocationManager;
     private boolean mReportLocation;
 
-    public PassiveProvider(ILocationManager locationManager) {
-        mLocationManager = locationManager;
-    }
+    public PassiveProvider(LocationProviderManager locationProviderManager) {
+        super(locationProviderManager, true);
 
-    @Override
-    public String getName() {
-        return LocationManager.PASSIVE_PROVIDER;
-    }
+        mReportLocation = false;
 
-    @Override
-    public ProviderProperties getProperties() {
-        return PROPERTIES;
-    }
-
-    @Override
-    public boolean isEnabled() {
-        return true;
-    }
-
-    @Override
-    public void enable() {
-    }
-
-    @Override
-    public void disable() {
+        setProperties(PROPERTIES);
     }
 
     @Override
@@ -82,22 +57,15 @@
 
     public void updateLocation(Location location) {
         if (mReportLocation) {
-            try {
-                // pass the location back to the location manager
-                mLocationManager.reportLocation(location, true);
-            } catch (RemoteException e) {
-                Log.e(TAG, "RemoteException calling reportLocation");
-            }
+            reportLocation(location);
         }
     }
 
     @Override
-    public boolean sendExtraCommand(String command, Bundle extras) {
-        return false;
-    }
+    public void sendExtraCommand(String command, Bundle extras) {}
 
     @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        pw.println("mReportLocation=" + mReportLocation);
+        pw.println(" report location=" + mReportLocation);
     }
 }
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index b12d4b6..888675c 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -1301,6 +1301,7 @@
     final @Nullable String mSetupWizardPackage;
     final @Nullable String mStorageManagerPackage;
     final @Nullable String mSystemTextClassifierPackage;
+    final @Nullable String mWellbeingPackage;
     final @NonNull String mServicesSystemSharedLibraryPackageName;
     final @NonNull String mSharedSystemSharedLibraryPackageName;
 
@@ -2790,6 +2791,8 @@
 
             mSystemTextClassifierPackage = getSystemTextClassifierPackageName();
 
+            mWellbeingPackage = getWellbeingPackageName();
+
             // Now that we know all of the shared libraries, update all clients to have
             // the correct library paths.
             updateAllSharedLibrariesLPw(null);
@@ -18307,6 +18310,10 @@
                 continue;
             }
 
+            if (bp.isRemoved()) {
+                continue;
+            }
+
             // If shared user we just reset the state to which only this app contributed.
             if (ps.sharedUser != null) {
                 boolean used = false;
@@ -19564,6 +19571,11 @@
     }
 
     @Override
+    public String getWellbeingPackageName() {
+        return mContext.getString(R.string.config_defaultWellbeingPackage);
+    }
+
+    @Override
     public void setApplicationEnabledSetting(String appPackageName,
             int newState, int flags, int userId, String callingPackage) {
         if (!sUserManager.exists(userId)) return;
@@ -22719,6 +22731,8 @@
                     return mSystemTextClassifierPackage;
                 case PackageManagerInternal.PACKAGE_PERMISSION_CONTROLLER:
                     return mRequiredPermissionControllerPackage;
+                case PackageManagerInternal.PACKAGE_WELLBEING:
+                    return mWellbeingPackage;
             }
             return null;
         }
diff --git a/services/core/java/com/android/server/pm/permission/BasePermission.java b/services/core/java/com/android/server/pm/permission/BasePermission.java
index 2d583ca3..996f42b 100644
--- a/services/core/java/com/android/server/pm/permission/BasePermission.java
+++ b/services/core/java/com/android/server/pm/permission/BasePermission.java
@@ -189,6 +189,11 @@
         return (protectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
                 == PermissionInfo.PROTECTION_DANGEROUS;
     }
+
+    public boolean isRemoved() {
+        return perm.info != null && (perm.info.flags & PermissionInfo.FLAG_REMOVED) != 0;
+    }
+
     public boolean isSignature() {
         return (protectionLevel & PermissionInfo.PROTECTION_MASK_BASE) ==
                 PermissionInfo.PROTECTION_SIGNATURE;
@@ -235,6 +240,9 @@
         return (protectionLevel & PermissionInfo.PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER)
                 != 0;
     }
+    public boolean isWellbeing() {
+        return (protectionLevel & PermissionInfo.PROTECTION_FLAG_WELLBEING) != 0;
+    }
 
     public void transfer(@NonNull String origPackageName, @NonNull String newPackageName) {
         if (!origPackageName.equals(sourcePackageName)) {
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 4406fdd..bc3c18d 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -799,6 +799,10 @@
                     continue;
                 }
 
+                if (bp.isRemoved()) {
+                    continue;
+                }
+
                 // Limit ephemeral apps to ephemeral allowed permissions.
                 if (pkg.applicationInfo.isInstantApp() && !bp.isInstant()) {
                     if (DEBUG_PERMISSIONS) {
@@ -1637,6 +1641,12 @@
                 // Special permissions for the system default text classifier.
                 allowed = true;
             }
+            if (!allowed && bp.isWellbeing()
+                    && pkg.packageName.equals(mPackageManagerInt.getKnownPackageName(
+                    PackageManagerInternal.PACKAGE_WELLBEING, UserHandle.USER_SYSTEM))) {
+                // Special permission granted only to the OEM specified wellbeing app
+                allowed = true;
+            }
         }
         return allowed;
     }
diff --git a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
index c8a68b4..6b0419e 100644
--- a/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
+++ b/services/core/java/com/android/server/textclassifier/TextClassificationManagerService.java
@@ -23,7 +23,6 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
-import android.content.pm.PackageManager;
 import android.os.Binder;
 import android.os.IBinder;
 import android.os.RemoteException;
@@ -136,6 +135,7 @@
             throws RemoteException {
         Preconditions.checkNotNull(request);
         Preconditions.checkNotNull(callback);
+        validateInput(mContext, request.getCallingPackageName());
 
         synchronized (mLock) {
             UserState userState = getCallingUserStateLocked();
@@ -158,6 +158,7 @@
             throws RemoteException {
         Preconditions.checkNotNull(request);
         Preconditions.checkNotNull(callback);
+        validateInput(mContext, request.getCallingPackageName());
 
         synchronized (mLock) {
             UserState userState = getCallingUserStateLocked();
@@ -180,6 +181,7 @@
             throws RemoteException {
         Preconditions.checkNotNull(request);
         Preconditions.checkNotNull(callback);
+        validateInput(mContext, request.getCallingPackageName());
 
         synchronized (mLock) {
             UserState userState = getCallingUserStateLocked();
@@ -199,7 +201,7 @@
     public void onSelectionEvent(
             TextClassificationSessionId sessionId, SelectionEvent event) throws RemoteException {
         Preconditions.checkNotNull(event);
-        validateInput(event.getPackageName(), mContext);
+        validateInput(mContext, event.getPackageName());
 
         synchronized (mLock) {
             UserState userState = getCallingUserStateLocked();
@@ -220,6 +222,7 @@
             ITextLanguageCallback callback) throws RemoteException {
         Preconditions.checkNotNull(request);
         Preconditions.checkNotNull(callback);
+        validateInput(mContext, request.getCallingPackageName());
 
         synchronized (mLock) {
             UserState userState = getCallingUserStateLocked();
@@ -242,6 +245,7 @@
             IConversationActionsCallback callback) throws RemoteException {
         Preconditions.checkNotNull(request);
         Preconditions.checkNotNull(callback);
+        validateInput(mContext, request.getCallingPackageName());
 
         synchronized (mLock) {
             UserState userState = getCallingUserStateLocked();
@@ -263,7 +267,7 @@
             throws RemoteException {
         Preconditions.checkNotNull(sessionId);
         Preconditions.checkNotNull(classificationContext);
-        validateInput(classificationContext.getPackageName(), mContext);
+        validateInput(mContext, classificationContext.getPackageName());
 
         synchronized (mLock) {
             UserState userState = getCallingUserStateLocked();
@@ -398,15 +402,17 @@
                 e -> Slog.d(LOG_TAG, "Error " + opDesc + ": " + e.getMessage()));
     }
 
-    private static void validateInput(String packageName, Context context)
+    private static void validateInput(Context context, @Nullable String packageName)
             throws RemoteException {
+        if (packageName == null) return;
+
         try {
             final int uid = context.getPackageManager()
                     .getPackageUidAsUser(packageName, UserHandle.getCallingUserId());
             Preconditions.checkArgument(Binder.getCallingUid() == uid);
-        } catch (IllegalArgumentException | NullPointerException |
-                PackageManager.NameNotFoundException e) {
-            throw new RemoteException(e.getMessage());
+        } catch (Exception e) {
+            throw new RemoteException(
+                    String.format("Invalid package: name=%s, error=%s", packageName, e));
         }
     }
 
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 2baef6af..88f645d 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -36,7 +36,6 @@
 import android.content.res.Resources.Theme;
 import android.database.sqlite.SQLiteCompatibilityWalFlags;
 import android.database.sqlite.SQLiteGlobal;
-import android.hardware.display.ColorDisplayManager;
 import android.os.BaseBundle;
 import android.os.Binder;
 import android.os.Build;
@@ -1443,11 +1442,9 @@
             mSystemServiceManager.startService(TwilightService.class);
             traceEnd();
 
-            if (ColorDisplayManager.isNightDisplayAvailable(context)) {
-                traceBeginAndSlog("StartColorDisplay");
-                mSystemServiceManager.startService(ColorDisplayService.class);
-                traceEnd();
-            }
+            traceBeginAndSlog("StartColorDisplay");
+            mSystemServiceManager.startService(ColorDisplayService.class);
+            traceEnd();
 
             traceBeginAndSlog("StartJobScheduler");
             mSystemServiceManager.startService(JobSchedulerService.class);
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 0a60e34..60eb18c 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -78,6 +78,15 @@
     // system image, that can be added in packages/apps/CarrierConfig.
 
     /**
+     * Specifies a value that identifies the version of the carrier configuration that is
+     * currently in use. This string is displayed on the UI.
+     * The format of the string is not specified.
+     * @hide
+     */
+    public static final String KEY_CARRIER_CONFIG_VERSION_STRING =
+            "carrier_config_version_string";
+
+    /**
      * This flag specifies whether VoLTE availability is based on provisioning. By default this is
      * false.
      */
@@ -2392,6 +2401,7 @@
 
     static {
         sDefaults = new PersistableBundle();
+        sDefaults.putString(KEY_CARRIER_CONFIG_VERSION_STRING, "");
         sDefaults.putBoolean(KEY_ALLOW_HOLD_IN_IMS_CALL_BOOL, true);
         sDefaults.putBoolean(KEY_CARRIER_ALLOW_DEFLECT_IMS_CALL_BOOL, false);
         sDefaults.putBoolean(KEY_ALWAYS_PLAY_REMOTE_HOLD_TONE_BOOL, false);
diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java
index f6e8d34..c95837e 100644
--- a/telephony/java/android/telephony/PhoneNumberUtils.java
+++ b/telephony/java/android/telephony/PhoneNumberUtils.java
@@ -1738,7 +1738,10 @@
      * @param number the number to look up.
      * @return true if the number is in the list of emergency numbers
      *         listed in the RIL / SIM, otherwise return false.
+     *
+     * @deprecated Please use {@link TelephonyManager#isCurrentEmergencyNumber(String)} instead.
      */
+    @Deprecated
     public static boolean isEmergencyNumber(String number) {
         return isEmergencyNumber(getDefaultVoiceSubId(), number);
     }
@@ -1751,8 +1754,13 @@
      * @param number the number to look up.
      * @return true if the number is in the list of emergency numbers
      *         listed in the RIL / SIM, otherwise return false.
+     *
+     * @deprecated Please use {@link TelephonyManager#isCurrentEmergencyNumber(String)}
+     *             instead.
+     *
      * @hide
      */
+    @Deprecated
     @UnsupportedAppUsage
     public static boolean isEmergencyNumber(int subId, String number) {
         // Return true only if the specified number *exactly* matches
@@ -1778,8 +1786,12 @@
      *         listed in the RIL / SIM, *or* if the number starts with the
      *         same digits as any of those emergency numbers.
      *
+     * @deprecated Please use {@link TelephonyManager#isCurrentPotentialEmergencyNumber(String)}
+     *             instead.
+     *
      * @hide
      */
+    @Deprecated
     public static boolean isPotentialEmergencyNumber(String number) {
         return isPotentialEmergencyNumber(getDefaultVoiceSubId(), number);
     }
@@ -1802,9 +1814,14 @@
      * @return true if the number is in the list of emergency numbers
      *         listed in the RIL / SIM, *or* if the number starts with the
      *         same digits as any of those emergency numbers.
+     *
+     * @deprecated Please use {@link TelephonyManager#isCurrentPotentialEmergencyNumber(String)}
+     *             instead.
+     *
      * @hide
      */
     @UnsupportedAppUsage
+    @Deprecated
     public static boolean isPotentialEmergencyNumber(int subId, String number) {
         // Check against the emergency numbers listed by the RIL / SIM,
         // and *don't* require an exact match.
@@ -1867,8 +1884,12 @@
      * @return if the number is an emergency number for the specific country, then return true,
      * otherwise false
      *
+     * @deprecated Please use {@link TelephonyManager#isCurrentEmergencyNumber(String)}
+     *             instead.
+     *
      * @hide
      */
+    @Deprecated
     @UnsupportedAppUsage
     public static boolean isEmergencyNumber(String number, String defaultCountryIso) {
             return isEmergencyNumber(getDefaultVoiceSubId(), number, defaultCountryIso);
@@ -1882,8 +1903,13 @@
      * @param defaultCountryIso the specific country which the number should be checked against
      * @return if the number is an emergency number for the specific country, then return true,
      * otherwise false
+     *
+     * @deprecated Please use {@link TelephonyManager#isCurrentEmergencyNumber(String)}
+     *             instead.
+     *
      * @hide
      */
+    @Deprecated
     public static boolean isEmergencyNumber(int subId, String number, String defaultCountryIso) {
         return isEmergencyNumberInternal(subId, number,
                                          defaultCountryIso,
@@ -1909,8 +1935,12 @@
      *         country, *or* if the number starts with the same digits as
      *         any of those emergency numbers.
      *
+     * @deprecated Please use {@link TelephonyManager#isCurrentPotentialEmergencyNumber(String)}
+     *             instead.
+     *
      * @hide
      */
+    @Deprecated
     public static boolean isPotentialEmergencyNumber(String number, String defaultCountryIso) {
         return isPotentialEmergencyNumber(getDefaultVoiceSubId(), number, defaultCountryIso);
     }
@@ -1934,8 +1964,13 @@
      * @return true if the number is an emergency number for the specific
      *         country, *or* if the number starts with the same digits as
      *         any of those emergency numbers.
+     *
+     * @deprecated Please use {@link TelephonyManager#isCurrentPotentialEmergencyNumber(String)}
+     *             instead.
+     *
      * @hide
      */
+    @Deprecated
     public static boolean isPotentialEmergencyNumber(int subId, String number,
             String defaultCountryIso) {
         return isEmergencyNumberInternal(subId, number,
@@ -1983,92 +2018,7 @@
     private static boolean isEmergencyNumberInternal(int subId, String number,
                                                      String defaultCountryIso,
                                                      boolean useExactMatch) {
-        // If the number passed in is null, just return false:
-        if (number == null) return false;
-
-        // If the number passed in is a SIP address, return false, since the
-        // concept of "emergency numbers" is only meaningful for calls placed
-        // over the cell network.
-        // (Be sure to do this check *before* calling extractNetworkPortionAlt(),
-        // since the whole point of extractNetworkPortionAlt() is to filter out
-        // any non-dialable characters (which would turn 'abc911def@example.com'
-        // into '911', for example.))
-        if (isUriNumber(number)) {
-            return false;
-        }
-
-        // Strip the separators from the number before comparing it
-        // to the list.
-        number = extractNetworkPortionAlt(number);
-
-        String emergencyNumbers = "";
-        int slotId = SubscriptionManager.getSlotIndex(subId);
-
-        // retrieve the list of emergency numbers
-        // check read-write ecclist property first
-        String ecclist = (slotId <= 0) ? "ril.ecclist" : ("ril.ecclist" + slotId);
-
-        emergencyNumbers = SystemProperties.get(ecclist, "");
-
-        Rlog.d(LOG_TAG, "slotId:" + slotId + " subId:" + subId + " country:"
-                + defaultCountryIso + " emergencyNumbers: " +  emergencyNumbers);
-
-        if (TextUtils.isEmpty(emergencyNumbers)) {
-            // then read-only ecclist property since old RIL only uses this
-            emergencyNumbers = SystemProperties.get("ro.ril.ecclist");
-        }
-
-        if (!TextUtils.isEmpty(emergencyNumbers)) {
-            // searches through the comma-separated list for a match,
-            // return true if one is found.
-            for (String emergencyNum : emergencyNumbers.split(",")) {
-                // It is not possible to append additional digits to an emergency number to dial
-                // the number in Brazil - it won't connect.
-                if (useExactMatch || "BR".equalsIgnoreCase(defaultCountryIso)) {
-                    if (number.equals(emergencyNum)) {
-                        return true;
-                    }
-                } else {
-                    if (number.startsWith(emergencyNum)) {
-                        return true;
-                    }
-                }
-            }
-            // no matches found against the list!
-            return false;
-        }
-
-        Rlog.d(LOG_TAG, "System property doesn't provide any emergency numbers."
-                + " Use embedded logic for determining ones.");
-
-        // If slot id is invalid, means that there is no sim card.
-        // According spec 3GPP TS22.101, the following numbers should be
-        // ECC numbers when SIM/USIM is not present.
-        emergencyNumbers = ((slotId < 0) ? "112,911,000,08,110,118,119,999" : "112,911");
-
-        for (String emergencyNum : emergencyNumbers.split(",")) {
-            if (useExactMatch) {
-                if (number.equals(emergencyNum)) {
-                    return true;
-                }
-            } else {
-                if (number.startsWith(emergencyNum)) {
-                    return true;
-                }
-            }
-        }
-
-        // No ecclist system property, so use our own list.
-        if (defaultCountryIso != null) {
-            ShortNumberInfo info = ShortNumberInfo.getInstance();
-            if (useExactMatch) {
-                return info.isEmergencyNumber(number, defaultCountryIso);
-            } else {
-                return info.connectsToEmergencyNumber(number, defaultCountryIso);
-            }
-        }
-
-        return false;
+        return TelephonyManager.getDefault().isCurrentEmergencyNumber(number);
     }
 
     /**
@@ -2078,7 +2028,11 @@
      * @param context the specific context which the number should be checked against
      * @return true if the specified number is an emergency number for the country the user
      * is currently in.
+     *
+     * @deprecated Please use {@link TelephonyManager#isCurrentEmergencyNumber(String)}
+     *             instead.
      */
+    @Deprecated
     public static boolean isLocalEmergencyNumber(Context context, String number) {
         return isLocalEmergencyNumber(context, getDefaultVoiceSubId(), number);
     }
@@ -2091,8 +2045,13 @@
      * @param context the specific context which the number should be checked against
      * @return true if the specified number is an emergency number for the country the user
      * is currently in.
+     *
+     * @deprecated Please use {@link TelephonyManager#isCurrentEmergencyNumber(String)}
+     *             instead.
+     *
      * @hide
      */
+    @Deprecated
     @UnsupportedAppUsage
     public static boolean isLocalEmergencyNumber(Context context, int subId, String number) {
         return isLocalEmergencyNumberInternal(subId, number,
@@ -2120,8 +2079,13 @@
      *              CountryDetector.
      *
      * @see android.location.CountryDetector
+     *
+     * @deprecated Please use {@link TelephonyManager#isCurrentPotentialEmergencyNumber(String)}
+     *             instead.
+     *
      * @hide
      */
+    @Deprecated
     @UnsupportedAppUsage
     public static boolean isPotentialLocalEmergencyNumber(Context context, String number) {
         return isPotentialLocalEmergencyNumber(context, getDefaultVoiceSubId(), number);
@@ -2147,9 +2111,13 @@
      * @return true if the specified number is an emergency number for a local country, based on the
      *              CountryDetector.
      *
+     * @deprecated Please use {@link TelephonyManager#isCurrentPotentialEmergencyNumber(String)}
+     *             instead.
+     *
      * @hide
      */
     @UnsupportedAppUsage
+    @Deprecated
     public static boolean isPotentialLocalEmergencyNumber(Context context, int subId,
             String number) {
         return isLocalEmergencyNumberInternal(subId, number,
@@ -2217,6 +2185,101 @@
     }
 
     /**
+     * Back-up old logics for {@link #isEmergencyNumberInternal} for legacy and deprecate purpose.
+     *
+     * @hide
+     */
+    public static boolean isEmergencyNumberInternal(String number, boolean useExactMatch,
+                                                    String defaultCountryIso) {
+        // If the number passed in is null, just return false:
+        if (number == null) return false;
+
+        // If the number passed in is a SIP address, return false, since the
+        // concept of "emergency numbers" is only meaningful for calls placed
+        // over the cell network.
+        // (Be sure to do this check *before* calling extractNetworkPortionAlt(),
+        // since the whole point of extractNetworkPortionAlt() is to filter out
+        // any non-dialable characters (which would turn 'abc911def@example.com'
+        // into '911', for example.))
+        if (PhoneNumberUtils.isUriNumber(number)) {
+            return false;
+        }
+
+        // Strip the separators from the number before comparing it
+        // to the list.
+        number = PhoneNumberUtils.extractNetworkPortionAlt(number);
+
+        String emergencyNumbers = "";
+        int slotId = SubscriptionManager.getSlotIndex(getDefaultVoiceSubId());
+
+        // retrieve the list of emergency numbers
+        // check read-write ecclist property first
+        String ecclist = (slotId <= 0) ? "ril.ecclist" : ("ril.ecclist" + slotId);
+
+        emergencyNumbers = SystemProperties.get(ecclist, "");
+
+        Rlog.d(LOG_TAG, "slotId:" + slotId + " country:"
+                + defaultCountryIso + " emergencyNumbers: " +  emergencyNumbers);
+
+        if (TextUtils.isEmpty(emergencyNumbers)) {
+            // then read-only ecclist property since old RIL only uses this
+            emergencyNumbers = SystemProperties.get("ro.ril.ecclist");
+        }
+
+        if (!TextUtils.isEmpty(emergencyNumbers)) {
+            // searches through the comma-separated list for a match,
+            // return true if one is found.
+            for (String emergencyNum : emergencyNumbers.split(",")) {
+                // It is not possible to append additional digits to an emergency number to dial
+                // the number in Brazil - it won't connect.
+                if (useExactMatch || "BR".equalsIgnoreCase(defaultCountryIso)) {
+                    if (number.equals(emergencyNum)) {
+                        return true;
+                    }
+                } else {
+                    if (number.startsWith(emergencyNum)) {
+                        return true;
+                    }
+                }
+            }
+            // no matches found against the list!
+            return false;
+        }
+
+        Rlog.d(LOG_TAG, "System property doesn't provide any emergency numbers."
+                + " Use embedded logic for determining ones.");
+
+        // If slot id is invalid, means that there is no sim card.
+        // According spec 3GPP TS22.101, the following numbers should be
+        // ECC numbers when SIM/USIM is not present.
+        emergencyNumbers = ((slotId < 0) ? "112,911,000,08,110,118,119,999" : "112,911");
+
+        for (String emergencyNum : emergencyNumbers.split(",")) {
+            if (useExactMatch) {
+                if (number.equals(emergencyNum)) {
+                    return true;
+                }
+            } else {
+                if (number.startsWith(emergencyNum)) {
+                    return true;
+                }
+            }
+        }
+
+        // No ecclist system property, so use our own list.
+        if (defaultCountryIso != null) {
+            ShortNumberInfo info = ShortNumberInfo.getInstance();
+            if (useExactMatch) {
+                return info.isEmergencyNumber(number, defaultCountryIso);
+            } else {
+                return info.connectsToEmergencyNumber(number, defaultCountryIso);
+            }
+        }
+
+        return false;
+    }
+
+    /**
      * isVoiceMailNumber: checks a given number against the voicemail
      *   number provided by the RIL and SIM card. The caller must have
      *   the READ_PHONE_STATE credential.
diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java
index e8a28ca..0df0daf 100644
--- a/telephony/java/android/telephony/PhoneStateListener.java
+++ b/telephony/java/android/telephony/PhoneStateListener.java
@@ -27,12 +27,14 @@
 import android.os.Handler;
 import android.os.HandlerExecutor;
 import android.os.Looper;
+import android.telephony.emergency.EmergencyNumber;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.telephony.IPhoneStateListener;
 
 import java.lang.ref.WeakReference;
 import java.util.List;
+import java.util.Map;
 import java.util.concurrent.Executor;
 
 /**
@@ -313,6 +315,8 @@
      *
      * <p>Requires permission {@link android.Manifest.permission#READ_PHONE_STATE} or the calling
      * app has carrier privileges (see {@link TelephonyManager#hasCarrierPrivileges}).
+     *
+     * @see #onEmergencyNumberListChanged
      */
     public static final int LISTEN_EMERGENCY_NUMBER_LIST                   = 0x01000000;
 
@@ -603,6 +607,21 @@
     }
 
     /**
+     * Callback invoked when the current emergency number list has changed
+     *
+     * @param emergencyNumberList Map including the key as the active subscription ID
+     *                           (Note: if there is no active subscription, the key is
+     *                           {@link SubscriptionManager#getDefaultSubscriptionId})
+     *                           and the value as the list of {@link EmergencyNumber};
+     *                           null if this information is not available.
+     * @hide
+     */
+    public void onEmergencyNumberListChanged(
+            @NonNull Map<Integer, List<EmergencyNumber>> emergencyNumberList) {
+        // default implementation empty
+    }
+
+    /**
      * Callback invoked when OEM hook raw event is received. Requires
      * the READ_PRIVILEGED_PHONE_STATE permission.
      * @param rawData is the byte array of the OEM hook raw data.
@@ -859,6 +878,16 @@
                             () -> psl.onPhysicalChannelConfigurationChanged(configs)));
         }
 
+        @Override
+        public void onEmergencyNumberListChanged(Map emergencyNumberList) {
+            PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
+            if (psl == null) return;
+
+            Binder.withCleanCallingIdentity(
+                    () -> mExecutor.execute(
+                            () -> psl.onEmergencyNumberListChanged(emergencyNumberList)));
+        }
+
         public void onPhoneCapabilityChanged(PhoneCapability capability) {
             PhoneStateListener psl = mPhoneStateListenerWeakRef.get();
             if (psl == null) return;
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index 7c52d38..78f05168 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -1423,47 +1423,49 @@
     }
 
     /** @hide */
-    public static int rilRadioTechnologyToNetworkType(@RilRadioTechnology int rt) {
-        switch(rt) {
-        case ServiceState.RIL_RADIO_TECHNOLOGY_GPRS:
-            return TelephonyManager.NETWORK_TYPE_GPRS;
-        case ServiceState.RIL_RADIO_TECHNOLOGY_EDGE:
-            return TelephonyManager.NETWORK_TYPE_EDGE;
-        case ServiceState.RIL_RADIO_TECHNOLOGY_UMTS:
-            return TelephonyManager.NETWORK_TYPE_UMTS;
-        case ServiceState.RIL_RADIO_TECHNOLOGY_HSDPA:
-            return TelephonyManager.NETWORK_TYPE_HSDPA;
-        case ServiceState.RIL_RADIO_TECHNOLOGY_HSUPA:
-            return TelephonyManager.NETWORK_TYPE_HSUPA;
-        case ServiceState.RIL_RADIO_TECHNOLOGY_HSPA:
-            return TelephonyManager.NETWORK_TYPE_HSPA;
-        case ServiceState.RIL_RADIO_TECHNOLOGY_IS95A:
-        case ServiceState.RIL_RADIO_TECHNOLOGY_IS95B:
-            return TelephonyManager.NETWORK_TYPE_CDMA;
-        case ServiceState.RIL_RADIO_TECHNOLOGY_1xRTT:
-            return TelephonyManager.NETWORK_TYPE_1xRTT;
-        case ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_0:
-            return TelephonyManager.NETWORK_TYPE_EVDO_0;
-        case ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_A:
-            return TelephonyManager.NETWORK_TYPE_EVDO_A;
-        case ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_B:
-            return TelephonyManager.NETWORK_TYPE_EVDO_B;
-        case ServiceState.RIL_RADIO_TECHNOLOGY_EHRPD:
-            return TelephonyManager.NETWORK_TYPE_EHRPD;
-        case ServiceState.RIL_RADIO_TECHNOLOGY_LTE:
-            return TelephonyManager.NETWORK_TYPE_LTE;
-        case ServiceState.RIL_RADIO_TECHNOLOGY_HSPAP:
-            return TelephonyManager.NETWORK_TYPE_HSPAP;
-        case ServiceState.RIL_RADIO_TECHNOLOGY_GSM:
-            return TelephonyManager.NETWORK_TYPE_GSM;
-        case ServiceState.RIL_RADIO_TECHNOLOGY_TD_SCDMA:
-            return TelephonyManager.NETWORK_TYPE_TD_SCDMA;
-        case ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN:
-            return TelephonyManager.NETWORK_TYPE_IWLAN;
-        case ServiceState.RIL_RADIO_TECHNOLOGY_LTE_CA:
-            return TelephonyManager.NETWORK_TYPE_LTE_CA;
-        default:
-            return TelephonyManager.NETWORK_TYPE_UNKNOWN;
+    public static int rilRadioTechnologyToNetworkType(@RilRadioTechnology int rat) {
+        switch(rat) {
+            case ServiceState.RIL_RADIO_TECHNOLOGY_GPRS:
+                return TelephonyManager.NETWORK_TYPE_GPRS;
+            case ServiceState.RIL_RADIO_TECHNOLOGY_EDGE:
+                return TelephonyManager.NETWORK_TYPE_EDGE;
+            case ServiceState.RIL_RADIO_TECHNOLOGY_UMTS:
+                return TelephonyManager.NETWORK_TYPE_UMTS;
+            case ServiceState.RIL_RADIO_TECHNOLOGY_HSDPA:
+                return TelephonyManager.NETWORK_TYPE_HSDPA;
+            case ServiceState.RIL_RADIO_TECHNOLOGY_HSUPA:
+                return TelephonyManager.NETWORK_TYPE_HSUPA;
+            case ServiceState.RIL_RADIO_TECHNOLOGY_HSPA:
+                return TelephonyManager.NETWORK_TYPE_HSPA;
+            case ServiceState.RIL_RADIO_TECHNOLOGY_IS95A:
+            case ServiceState.RIL_RADIO_TECHNOLOGY_IS95B:
+                return TelephonyManager.NETWORK_TYPE_CDMA;
+            case ServiceState.RIL_RADIO_TECHNOLOGY_1xRTT:
+                return TelephonyManager.NETWORK_TYPE_1xRTT;
+            case ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_0:
+                return TelephonyManager.NETWORK_TYPE_EVDO_0;
+            case ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_A:
+                return TelephonyManager.NETWORK_TYPE_EVDO_A;
+            case ServiceState.RIL_RADIO_TECHNOLOGY_EVDO_B:
+                return TelephonyManager.NETWORK_TYPE_EVDO_B;
+            case ServiceState.RIL_RADIO_TECHNOLOGY_EHRPD:
+                return TelephonyManager.NETWORK_TYPE_EHRPD;
+            case ServiceState.RIL_RADIO_TECHNOLOGY_LTE:
+                return TelephonyManager.NETWORK_TYPE_LTE;
+            case ServiceState.RIL_RADIO_TECHNOLOGY_HSPAP:
+                return TelephonyManager.NETWORK_TYPE_HSPAP;
+            case ServiceState.RIL_RADIO_TECHNOLOGY_GSM:
+                return TelephonyManager.NETWORK_TYPE_GSM;
+            case ServiceState.RIL_RADIO_TECHNOLOGY_TD_SCDMA:
+                return TelephonyManager.NETWORK_TYPE_TD_SCDMA;
+            case ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN:
+                return TelephonyManager.NETWORK_TYPE_IWLAN;
+            case ServiceState.RIL_RADIO_TECHNOLOGY_LTE_CA:
+                return TelephonyManager.NETWORK_TYPE_LTE_CA;
+            case ServiceState.RIL_RADIO_TECHNOLOGY_NR:
+                return TelephonyManager.NETWORK_TYPE_NR;
+            default:
+                return TelephonyManager.NETWORK_TYPE_UNKNOWN;
         }
     }
 
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index fa9b76d..422e66d 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -810,6 +810,7 @@
      * @see TelephonyManager#NETWORK_TYPE_LTE
      * @see TelephonyManager#NETWORK_TYPE_EHRPD
      * @see TelephonyManager#NETWORK_TYPE_HSPAP
+     * @see TelephonyManager#NETWORK_TYPE_NR
      *
      * <p class="note">
      * Retrieve with
@@ -2328,6 +2329,7 @@
      * @see #NETWORK_TYPE_LTE
      * @see #NETWORK_TYPE_EHRPD
      * @see #NETWORK_TYPE_HSPAP
+     * @see #NETWORK_TYPE_NR
      *
      * @hide
      */
@@ -2379,6 +2381,7 @@
      * @see #NETWORK_TYPE_LTE
      * @see #NETWORK_TYPE_EHRPD
      * @see #NETWORK_TYPE_HSPAP
+     * @see #NETWORK_TYPE_NR
      */
     @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
@@ -2565,6 +2568,8 @@
                 return "IWLAN";
             case NETWORK_TYPE_LTE_CA:
                 return "LTE_CA";
+            case NETWORK_TYPE_NR:
+                return "NR";
             default:
                 return "UNKNOWN";
         }
@@ -9449,8 +9454,13 @@
     /**
      * Get the emergency number list based on current locale, sim, default, modem and network.
      *
-     * <p>The emergency number {@link EmergencyNumber} with higher display priority is located at
-     * the smaller index in the returned list.
+     * <p>In each returned list, the emergency number {@link EmergencyNumber} coming from higher
+     * priority sources will be located at the smaller index; the priority order of sources are:
+     * {@link EmergencyNumber#EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING} >
+     * {@link EmergencyNumber#EMERGENCY_NUMBER_SOURCE_SIM} >
+     * {@link EmergencyNumber#EMERGENCY_NUMBER_SOURCE_DATABASE} >
+     * {@link EmergencyNumber#EMERGENCY_NUMBER_SOURCE_DEFAULT} >
+     * {@link EmergencyNumber#EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG}
      *
      * <p>The subscriptions which the returned list would be based on, are all the active
      * subscriptions, no matter which subscription could be used to create TelephonyManager.
@@ -9459,8 +9469,9 @@
      * app has carrier privileges (see {@link #hasCarrierPrivileges}).
      *
      * @return Map including the key as the active subscription ID (Note: if there is no active
-     * subscription, the key is {@link SubscriptionManager#DEFAULT_SUBSCRIPTION_ID}) and the value
-     * as the list of {@link EmergencyNumber}; null if this information is not available.
+     * subscription, the key is {@link SubscriptionManager#getDefaultSubscriptionId}) and the value
+     * as the list of {@link EmergencyNumber}; null if this information is not available; or throw
+     * a SecurityException if the caller does not have the permission.
      */
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     @Nullable
@@ -9481,8 +9492,13 @@
      * Get the per-category emergency number list based on current locale, sim, default, modem
      * and network.
      *
-     * <p>The emergency number {@link EmergencyNumber} with higher display priority is located at
-     * the smaller index in the returned list.
+     * <p>In each returned list, the emergency number {@link EmergencyNumber} coming from higher
+     * priority sources will be located at the smaller index; the priority order of sources are:
+     * {@link EmergencyNumber#EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING} >
+     * {@link EmergencyNumber#EMERGENCY_NUMBER_SOURCE_SIM} >
+     * {@link EmergencyNumber#EMERGENCY_NUMBER_SOURCE_DATABASE} >
+     * {@link EmergencyNumber#EMERGENCY_NUMBER_SOURCE_DEFAULT} >
+     * {@link EmergencyNumber#EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG}
      *
      * <p>The subscriptions which the returned list would be based on, are all the active
      * subscriptions, no matter which subscription could be used to create TelephonyManager.
@@ -9503,8 +9519,9 @@
      * <li>{@link EmergencyNumber#EMERGENCY_SERVICE_CATEGORY_AIEC} </li>
      * </ol>
      * @return Map including the key as the active subscription ID (Note: if there is no active
-     * subscription, the key is {@link SubscriptionManager#DEFAULT_SUBSCRIPTION_ID}) and the value
-     * as the list of {@link EmergencyNumber}; null if this information is not available.
+     * subscription, the key is {@link SubscriptionManager#getDefaultSubscriptionId}) and the value
+     * as the list of {@link EmergencyNumber}; null if this information is not available; or throw
+     * a SecurityException if the caller does not have the permission.
      */
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     @Nullable
@@ -9551,7 +9568,44 @@
             if (telephony == null) {
                 return false;
             }
-            return telephony.isCurrentEmergencyNumber(number);
+            return telephony.isCurrentEmergencyNumber(number, true);
+        } catch (RemoteException ex) {
+            Log.e(TAG, "isCurrentEmergencyNumber RemoteException", ex);
+        }
+        return false;
+    }
+
+    /**
+     * Checks if the supplied number is an emergency number based on current locale, sim, default,
+     * modem and network.
+     *
+     * <p> Specifically, this method will return {@code true} if the specified number is an
+     * emergency number, *or* if the number simply starts with the same digits as any current
+     * emergency number.
+     *
+     * <p>The subscriptions which the identification would be based on, are all the active
+     * subscriptions, no matter which subscription could be used to create TelephonyManager.
+     *
+     * <p>Requires permission: {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE} or
+     * that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+     *
+     * @param number - the number to look up
+     * @return {@code true} if the given number is an emergency number or it simply starts with
+     * the same digits of any current emergency number based on current locale, sim, modem and
+     * network; {@code false} if it is not; or throw an SecurityException if the caller does not
+     * have the required permission/privileges
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+    public boolean isCurrentPotentialEmergencyNumber(@NonNull String number) {
+        try {
+            ITelephony telephony = getITelephony();
+            if (telephony == null) {
+                return false;
+            }
+            return telephony.isCurrentEmergencyNumber(number, false);
         } catch (RemoteException ex) {
             Log.e(TAG, "isCurrentEmergencyNumber RemoteException", ex);
         }
diff --git a/telephony/java/android/telephony/emergency/EmergencyNumber.java b/telephony/java/android/telephony/emergency/EmergencyNumber.java
index 41f7bd7..fe062d5 100644
--- a/telephony/java/android/telephony/emergency/EmergencyNumber.java
+++ b/telephony/java/android/telephony/emergency/EmergencyNumber.java
@@ -22,6 +22,7 @@
 import android.hardware.radio.V1_4.EmergencyServiceCategory;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.telephony.Rlog;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -150,6 +151,7 @@
     @IntDef(flag = true, prefix = { "EMERGENCY_NUMBER_SOURCE_" }, value = {
             EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING,
             EMERGENCY_NUMBER_SOURCE_SIM,
+            EMERGENCY_NUMBER_SOURCE_DATABASE,
             EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG,
             EMERGENCY_NUMBER_SOURCE_DEFAULT
     })
@@ -169,6 +171,10 @@
      * Reference: 3gpp 22.101, Section 10 - Emergency Calls
      */
     public static final int EMERGENCY_NUMBER_SOURCE_SIM = EmergencyNumberSource.SIM;
+    /**
+     * Bit-field which indicates the number is from the platform-maintained database.
+     */
+    public static final int EMERGENCY_NUMBER_SOURCE_DATABASE =  1 << 4;
     /** Bit-field which indicates the number is from the modem config. */
     public static final int EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG =
             EmergencyNumberSource.MODEM_CONFIG;
@@ -187,21 +193,24 @@
         EMERGENCY_NUMBER_SOURCE_SET = new HashSet<Integer>();
         EMERGENCY_NUMBER_SOURCE_SET.add(EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING);
         EMERGENCY_NUMBER_SOURCE_SET.add(EMERGENCY_NUMBER_SOURCE_SIM);
+        EMERGENCY_NUMBER_SOURCE_SET.add(EMERGENCY_NUMBER_SOURCE_DATABASE);
         EMERGENCY_NUMBER_SOURCE_SET.add(EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG);
         EMERGENCY_NUMBER_SOURCE_SET.add(EMERGENCY_NUMBER_SOURCE_DEFAULT);
     }
 
     private final String mNumber;
     private final String mCountryIso;
+    private final String mMnc;
     private final int mEmergencyServiceCategoryBitmask;
     private final int mEmergencyNumberSourceBitmask;
 
     /** @hide */
     public EmergencyNumber(@NonNull String number, @NonNull String countryIso,
-                           int emergencyServiceCategories,
+                           @NonNull String mnc, int emergencyServiceCategories,
                            int emergencyNumberSources) {
         this.mNumber = number;
         this.mCountryIso = countryIso;
+        this.mMnc = mnc;
         this.mEmergencyServiceCategoryBitmask = emergencyServiceCategories;
         this.mEmergencyNumberSourceBitmask = emergencyNumberSources;
     }
@@ -210,6 +219,7 @@
     public EmergencyNumber(Parcel source) {
         mNumber = source.readString();
         mCountryIso = source.readString();
+        mMnc = source.readString();
         mEmergencyServiceCategoryBitmask = source.readInt();
         mEmergencyNumberSourceBitmask = source.readInt();
     }
@@ -236,6 +246,15 @@
     }
 
     /**
+     * Get the Mobile Network Code of the emergency number.
+     *
+     * @return the Mobile Network Code of the emergency number.
+     */
+    public String getMnc() {
+        return mMnc;
+    }
+
+    /**
      * Returns the bitmask of emergency service categories of the emergency number.
      *
      * @return bitmask of the emergency service categories
@@ -338,6 +357,7 @@
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeString(mNumber);
         dest.writeString(mCountryIso);
+        dest.writeString(mMnc);
         dest.writeInt(mEmergencyServiceCategoryBitmask);
         dest.writeInt(mEmergencyNumberSourceBitmask);
     }
@@ -350,10 +370,10 @@
 
     @Override
     public String toString() {
-        return "EmergencyNumber = " + "[Number]" + mNumber + " / [CountryIso]" + mCountryIso
-                + " / [ServiceCategories]"
-                + Integer.toBinaryString(mEmergencyServiceCategoryBitmask)
-                + " / [Sources]" + Integer.toBinaryString(mEmergencyNumberSourceBitmask);
+        return "EmergencyNumber:" + "Number-" + mNumber + "|CountryIso-" + mCountryIso
+                + "|Mnc-" + mMnc
+                + "|ServiceCategories-" + Integer.toBinaryString(mEmergencyServiceCategoryBitmask)
+                + "|Sources-" + Integer.toBinaryString(mEmergencyNumberSourceBitmask);
     }
 
     @Override
@@ -373,6 +393,7 @@
      * The priority of sources are defined as follows:
      *     EMERGENCY_NUMBER_SOURCE_NETWORK_SIGNALING >
      *     EMERGENCY_NUMBER_SOURCE_SIM >
+     *     EMERGENCY_NUMBER_SOURCE_DATABASE >
      *     EMERGENCY_NUMBER_SOURCE_DEFAULT >
      *     EMERGENCY_NUMBER_SOURCE_MODEM_CONFIG
      *
@@ -385,7 +406,9 @@
         if (this.isFromSources(EMERGENCY_NUMBER_SOURCE_SIM)) {
             score += 1 << 3;
         }
-        // TODO add a score if the number comes from Google's emergency number database
+        if (this.isFromSources(EMERGENCY_NUMBER_SOURCE_DATABASE)) {
+            score += 1 << 2;
+        }
         if (this.isFromSources(EMERGENCY_NUMBER_SOURCE_DEFAULT)) {
             score += 1 << 1;
         }
@@ -412,14 +435,104 @@
                 < emergencyNumber.getDisplayPriorityScore()) {
             return 1;
         } else {
-            /**
-             * TODO if both numbers have the same display priority score, the number matches the
-             * Google's emergency number database has a higher display priority.
-             */
             return 0;
         }
     }
 
+    /**
+     * In-place merge same emergency numbers in the emergency number list.
+     *
+     * A unique EmergencyNumber has a unique combination of ‘number’, ‘mcc’, 'mnc' and
+     * 'categories' fields. Multiple Emergency Number Sources should be merged into one bitfield
+     * for the same EmergencyNumber.
+     *
+     * @param emergencyNumberList the emergency number list to process
+     *
+     * @hide
+     */
+    public static void mergeSameNumbersInEmergencyNumberList(
+            List<EmergencyNumber> emergencyNumberList) {
+        if (emergencyNumberList == null) {
+            return;
+        }
+        Set<EmergencyNumber> mergedEmergencyNumber = new HashSet<>();
+        for (int i = 0; i < emergencyNumberList.size(); i++) {
+            // Skip the check because it was merged.
+            if (mergedEmergencyNumber.contains(emergencyNumberList.get(i))) {
+                continue;
+            }
+            for (int j = i + 1; j < emergencyNumberList.size(); j++) {
+                if (isSameEmergencyNumber(
+                        emergencyNumberList.get(i), emergencyNumberList.get(j))) {
+                    Rlog.e(LOG_TAG, "Found unexpected duplicate numbers: "
+                            + emergencyNumberList.get(i) + " vs " + emergencyNumberList.get(j));
+                    // Set the merged emergency number in the current position
+                    emergencyNumberList.set(i, mergeNumbers(
+                            emergencyNumberList.get(i), emergencyNumberList.get(j)));
+                    // Mark the emergency number has been merged
+                    mergedEmergencyNumber.add(emergencyNumberList.get(j));
+                }
+            }
+        }
+        // Remove the marked emergency number in the orignal list
+        for (int i = 0; i < emergencyNumberList.size(); i++) {
+            if (mergedEmergencyNumber.contains(emergencyNumberList.get(i))) {
+                emergencyNumberList.remove(i--);
+            }
+        }
+    }
+
+    /**
+     * Check if two emergency numbers are the same.
+     *
+     * A unique EmergencyNumber has a unique combination of ‘number’, ‘mcc’, 'mnc' and
+     * 'categories' fields. Multiple Emergency Number Sources should be merged into one bitfield
+     * for the same EmergencyNumber.
+     *
+     * @param first first EmergencyNumber to compare
+     * @param second second EmergencyNumber to compare
+     * @return true if they are the same EmergencyNumbers; false otherwise.
+     *
+     * @hide
+     */
+    public static boolean isSameEmergencyNumber(@NonNull EmergencyNumber first,
+                                                @NonNull EmergencyNumber second) {
+        if (!first.getNumber().equals(second.getNumber())) {
+            return false;
+        }
+        if (!first.getCountryIso().equals(second.getCountryIso())) {
+            return false;
+        }
+        if (!first.getMnc().equals(second.getMnc())) {
+            return false;
+        }
+        if (first.getEmergencyServiceCategoryBitmask()
+                != second.getEmergencyServiceCategoryBitmask()) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Get a merged EmergencyNumber for two numbers if they are the same.
+     *
+     * @param first first EmergencyNumber to compare
+     * @param second second EmergencyNumber to compare
+     * @return a merged EmergencyNumber or null if they are not the same EmergencyNumber
+     *
+     * @hide
+     */
+    public static EmergencyNumber mergeNumbers(@NonNull EmergencyNumber first,
+                                         @NonNull EmergencyNumber second) {
+        if (isSameEmergencyNumber(first, second)) {
+            return new EmergencyNumber(first.getNumber(), first.getCountryIso(), first.getMnc(),
+                    first.getEmergencyServiceCategoryBitmask(),
+                    first.getEmergencyNumberSourceBitmask()
+                            | second.getEmergencyNumberSourceBitmask());
+        }
+        return null;
+    }
+
     public static final Parcelable.Creator<EmergencyNumber> CREATOR =
             new Parcelable.Creator<EmergencyNumber>() {
         @Override
diff --git a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
index 79f0635..78fc0bc4 100644
--- a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
+++ b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
@@ -25,6 +25,7 @@
 import android.telephony.PhysicalChannelConfig;
 import android.telephony.PreciseCallState;
 import android.telephony.PreciseDataConnectionState;
+import android.telephony.emergency.EmergencyNumber;
 
 oneway interface IPhoneStateListener {
     void onServiceStateChanged(in ServiceState serviceState);
@@ -53,5 +54,6 @@
     void onPhoneCapabilityChanged(in PhoneCapability capability);
     void onPreferredDataSubIdChanged(in int subId);
     void onRadioPowerStateChanged(in int state);
+    void onEmergencyNumberListChanged(in Map emergencyNumberList);
 }
 
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 399dc52..88b9302 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -1716,7 +1716,7 @@
     /**
      * Identify if the number is emergency number, based on all the active subscriptions.
      */
-    boolean isCurrentEmergencyNumber(String number);
+    boolean isCurrentEmergencyNumber(String number, boolean exactMatch);
 
     /**
      * Return a list of certs in hex string from loaded carrier privileges access rules.
diff --git a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index 76e7509..d9f5c3f 100644
--- a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -81,5 +81,5 @@
     void notifyPhoneCapabilityChanged(in PhoneCapability capability);
     void notifyPreferredDataSubIdChanged(int preferredSubId);
     void notifyRadioPowerStateChanged(in int state);
-    void notifyEmergencyNumberList(in List<EmergencyNumber> emergencyNumberList);
+    void notifyEmergencyNumberList();
 }
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index cb8269e..c934317 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -483,4 +483,5 @@
     int RIL_UNSOL_HAL_NON_RIL_BASE = 1100;
     int RIL_UNSOL_ICC_SLOT_STATUS = 1100;
     int RIL_UNSOL_PHYSICAL_CHANNEL_CONFIG = 1101;
+    int RIL_UNSOL_EMERGENCY_NUMBER_LIST = 1102;
 }