Refactor ServiceWatcher

1) Take advantage of new binder callback APIs to simplify ServiceWatcher
internally
2) Move resources into subclasses instead of LMS
3) Rename some public APIs of ServiceWatcher
4) Completely remove the signature checking done within ServiceWatcher
and LMS. It doesn't appear to serve any useful purpose anymore, and it's
debatable if it ever did anything useful. Rather than restricting bound
services to a set of packages + packages that share a signature with
those packages at boot, restrict to system packages only.
5) Remove unused config values

Test: presubmits
Change-Id: I870043539d0f96fe443a1da238d109c3fa1930c9
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 7fd444a..ec3208e 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1628,29 +1628,21 @@
          config_timeZoneRulesUpdateTrackingEnabled are true.] -->
     <integer name="config_timeZoneRulesCheckRetryCount">5</integer>
 
-    <!-- Whether to enable network location overlay which allows network
-         location provider to be replaced by an app at run-time. When disabled,
-         only the config_networkLocationProviderPackageName package will be
-         searched for network location provider, otherwise packages whose
-         signature matches the signatures of config_locationProviderPackageNames
-         will be searched, and the service with the highest version number will
-         be picked. Anyone who wants to disable the overlay mechanism can set it
-         to false.
-         -->
+    <!-- Whether to enable network location overlay which allows network location provider to be
+         replaced by an app at run-time. When disabled, only the
+         config_networkLocationProviderPackageName package will be searched for network location
+         provider, otherwise any system package is eligible. Anyone who wants to disable the overlay
+         mechanism can set it to false. -->
     <bool name="config_enableNetworkLocationOverlay" translatable="false">true</bool>
     <!-- Package name providing network location support. Used only when
          config_enableNetworkLocationOverlay is false. -->
     <string name="config_networkLocationProviderPackageName" translatable="false">@null</string>
 
-    <!-- Whether to enable fused location provider overlay which allows fused
-         location provider to be replaced by an app at run-time. When disabled,
-         only the config_fusedLocationProviderPackageName package will be
-         searched for fused location provider, otherwise packages whose
-         signature matches the signatures of config_locationProviderPackageNames
-         will be searched, and the service with the highest version number will
-         be picked. Anyone who wants to disable the overlay mechanism can set it
-         to false.
-         -->
+    <!-- Whether to enable fused location provider overlay which allows fused location provider to
+         be replaced by an app at run-time. When disabled, only the
+         config_fusedLocationProviderPackageName package will be searched for fused location
+         provider, otherwise any system package is eligible. Anyone who wants to disable the overlay
+         mechanism can set it to false. -->
     <bool name="config_enableFusedLocationOverlay" translatable="false">true</bool>
     <!-- Package name providing fused location support. Used only when
          config_enableFusedLocationOverlay is false. -->
@@ -1669,25 +1661,10 @@
      -->
     <string name="config_defaultNetworkRecommendationProviderPackage" translatable="false"></string>
 
-    <!-- Whether to enable Hardware FLP overlay which allows Hardware FLP to be
-         replaced by an app at run-time. When disabled, only the
-         config_hardwareFlpPackageName package will be searched for Hardware Flp,
-         otherwise packages whose signature matches the signatures of
-         config_locationProviderPackageNames will be searched, and the service
-         with the highest version number will be picked. Anyone who wants to
-         disable the overlay mechanism can set it to false.
-         -->
-    <bool name="config_enableHardwareFlpOverlay" translatable="false">true</bool>
-    <!-- Package name providing Hardware Flp. Used only when
-         config_enableHardwareFlpOverlay is false. -->
-    <string name="config_hardwareFlpPackageName" translatable="false">com.android.location.fused</string>
-
     <!-- Whether to enable geocoder overlay which allows geocoder to be replaced
          by an app at run-time. When disabled, only the
          config_geocoderProviderPackageName package will be searched for
-         geocoder, otherwise packages whose signature matches the signatures of
-         config_locationProviderPackageNames will be searched, and the service
-         with the highest version number will be picked. Anyone who wants to
+         geocoder, otherwise any system package is eligible. Anyone who wants to
          disable the overlay mechanism can set it to false.
          -->
     <bool name="config_enableGeocoderOverlay" translatable="false">true</bool>
@@ -1698,9 +1675,7 @@
     <!-- Whether to enable geofence overlay which allows geofence to be replaced
          by an app at run-time. When disabled, only the
          config_geofenceProviderPackageName package will be searched for
-         geofence implementation, otherwise packages whose signature matches the
-         signatures of config_locationProviderPackageNames will be searched, and
-         the service with the highest version number will be picked. Anyone who
+         geofence implementation, otherwise any system package is eligible. Anyone who
          wants to disable the overlay mechanism can set it to false.
          -->
     <bool name="config_enableGeofenceOverlay" translatable="false">true</bool>
@@ -1711,9 +1686,7 @@
     <!-- Whether to enable Hardware Activity-Recognition overlay which allows Hardware
          Activity-Recognition to be replaced by an app at run-time. When disabled, only the
          config_activityRecognitionHardwarePackageName package will be searched for
-         its implementation, otherwise packages whose signature matches the
-         signatures of config_locationProviderPackageNames will be searched, and
-         the service with the highest version number will be picked. Anyone who
+         its implementation, otherwise any system package is eligible. Anyone who
          wants to disable the overlay mechanism can set it to false.
          -->
     <bool name="config_enableActivityRecognitionHardwareOverlay" translatable="false">true</bool>
@@ -1721,19 +1694,8 @@
          config_enableActivityRecognitionHardwareOverlay is false. -->
     <string name="config_activityRecognitionHardwarePackageName" translatable="false">@null</string>
 
-    <!-- Package name(s) containing location provider support.
-         These packages can contain services implementing location providers,
-         such as the Geocode Provider, Network Location Provider, and
-         Fused Location Provider. They will each be searched for
-         service components implementing these providers.
-         It is strongly recommended that the packages explicitly named
-         below are on the system image, so that they will not map to
-         a 3rd party application.
-         The location framework also has support for installation
-         of new location providers at run-time. The new package does not
-         have to be explicitly listed here, however it must have a signature
-         that matches the signature of at least one package on this list.
-         -->
+    <!-- Package name(s) containing location provider support. These packages will be auto-granted
+         several permissions by the system, and should be system packages. -->
     <string-array name="config_locationProviderPackageNames" translatable="false">
         <!-- The standard AOSP fused location provider -->
         <item>com.android.location.fused</item>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 7e6eb5d..6192edb74 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1869,7 +1869,6 @@
   <java-symbol type="bool" name="config_dozeAfterScreenOffByDefault" />
   <java-symbol type="bool" name="config_enableActivityRecognitionHardwareOverlay" />
   <java-symbol type="bool" name="config_enableFusedLocationOverlay" />
-  <java-symbol type="bool" name="config_enableHardwareFlpOverlay" />
   <java-symbol type="bool" name="config_enableGeocoderOverlay" />
   <java-symbol type="bool" name="config_enableGeofenceOverlay" />
   <java-symbol type="bool" name="config_enableNetworkLocationOverlay" />
@@ -2019,7 +2018,6 @@
   <java-symbol type="string" name="config_datause_iface" />
   <java-symbol type="string" name="config_activityRecognitionHardwarePackageName" />
   <java-symbol type="string" name="config_fusedLocationProviderPackageName" />
-  <java-symbol type="string" name="config_hardwareFlpPackageName" />
   <java-symbol type="string" name="config_geocoderProviderPackageName" />
   <java-symbol type="string" name="config_geofenceProviderPackageName" />
   <java-symbol type="string" name="config_networkLocationProviderPackageName" />
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index e9db9c8..003525c9 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -18,6 +18,8 @@
 
 import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
 import static android.Manifest.permission.ACCESS_FINE_LOCATION;
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
+import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
 import static android.location.LocationManager.FUSED_PROVIDER;
 import static android.location.LocationManager.GPS_PROVIDER;
@@ -35,14 +37,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.ResolveInfo;
-import android.content.pm.Signature;
-import android.content.res.Resources;
-import android.hardware.location.ActivityRecognitionHardware;
 import android.location.Address;
 import android.location.Criteria;
 import android.location.GeocoderParams;
@@ -52,6 +47,7 @@
 import android.location.IGnssMeasurementsListener;
 import android.location.IGnssNavigationMessageListener;
 import android.location.IGnssStatusListener;
+import android.location.IGpsGeofenceHardware;
 import android.location.ILocationListener;
 import android.location.ILocationManager;
 import android.location.Location;
@@ -91,11 +87,11 @@
 import com.android.internal.util.Preconditions;
 import com.android.server.location.AbstractLocationProvider;
 import com.android.server.location.AbstractLocationProvider.State;
-import com.android.server.location.ActivityRecognitionProxy;
 import com.android.server.location.CallerIdentity;
 import com.android.server.location.GeocoderProxy;
 import com.android.server.location.GeofenceManager;
 import com.android.server.location.GeofenceProxy;
+import com.android.server.location.HardwareActivityRecognitionProxy;
 import com.android.server.location.LocationFudger;
 import com.android.server.location.LocationProviderProxy;
 import com.android.server.location.LocationRequestStatistics;
@@ -114,7 +110,6 @@
 import java.io.PrintStream;
 import java.io.PrintWriter;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -543,77 +538,6 @@
     }
 
     @GuardedBy("mLock")
-    private void ensureFallbackFusedProviderPresentLocked(String[] pkgs) {
-        PackageManager pm = mContext.getPackageManager();
-        String systemPackageName = mContext.getPackageName();
-        ArrayList<HashSet<Signature>> sigSets = ServiceWatcher.getSignatureSets(mContext, pkgs);
-
-        List<ResolveInfo> rInfos = pm.queryIntentServicesAsUser(
-                new Intent(FUSED_LOCATION_SERVICE_ACTION),
-                PackageManager.GET_META_DATA, mUserInfoStore.getCurrentUserId());
-        for (ResolveInfo rInfo : rInfos) {
-            String packageName = rInfo.serviceInfo.packageName;
-
-            // Check that the signature is in the list of supported sigs. If it's not in
-            // this list the standard provider binding logic won't bind to it.
-            try {
-                PackageInfo pInfo;
-                pInfo = pm.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
-                if (!ServiceWatcher.isSignatureMatch(pInfo.signatures, sigSets)) {
-                    Log.w(TAG, packageName + " resolves service " + FUSED_LOCATION_SERVICE_ACTION +
-                            ", but has wrong signature, ignoring");
-                    continue;
-                }
-            } catch (NameNotFoundException e) {
-                Log.e(TAG, "missing package: " + packageName);
-                continue;
-            }
-
-            // Get the version info
-            if (rInfo.serviceInfo.metaData == null) {
-                Log.w(TAG, "Found fused provider without metadata: " + packageName);
-                continue;
-            }
-
-            int version = rInfo.serviceInfo.metaData.getInt(
-                    ServiceWatcher.EXTRA_SERVICE_VERSION, -1);
-            if (version == 0) {
-                // This should be the fallback fused location provider.
-
-                // Make sure it's in the system partition.
-                if ((rInfo.serviceInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
-                    if (D) Log.d(TAG, "Fallback candidate not in /system: " + packageName);
-                    continue;
-                }
-
-                // Check that the fallback is signed the same as the OS
-                // as a proxy for coreApp="true"
-                if (pm.checkSignatures(systemPackageName, packageName)
-                        != PackageManager.SIGNATURE_MATCH) {
-                    if (D) {
-                        Log.d(TAG, "Fallback candidate not signed the same as system: "
-                                + packageName);
-                    }
-                    continue;
-                }
-
-                // Found a valid fallback.
-                if (D) Log.d(TAG, "Found fallback provider: " + packageName);
-                return;
-            } else {
-                if (D) Log.d(TAG, "Fallback candidate not version 0: " + packageName);
-            }
-        }
-
-        throw new IllegalStateException("Unable to find a fused location provider that is in the "
-                + "system partition with version 0 and signed with the platform certificate. "
-                + "Such a package is needed to provide a default fused location provider in the "
-                + "event that no other fused location provider has been installed or is currently "
-                + "available. For example, coreOnly boot mode when decrypting the data "
-                + "partition. The fallback must also be marked coreApp=\"true\" in the manifest");
-    }
-
-    @GuardedBy("mLock")
     private void initializeProvidersLocked() {
         if (GnssManagerService.isGnssSupported()) {
             mGnssManagerService = new GnssManagerService(this, mContext, mLocationUsageLogger);
@@ -622,33 +546,11 @@
             gnssManager.setRealProvider(mGnssManagerService.getGnssLocationProvider());
         }
 
-        /*
-        Load package name(s) containing location provider support.
-        These packages can contain services implementing location providers:
-        Geocoder Provider, Network Location Provider, and
-        Fused Location Provider. They will each be searched for
-        service components implementing these providers.
-        The location framework also has support for installation
-        of new location providers at run-time. The new package does not
-        have to be explicitly listed here, however it must have a signature
-        that matches the signature of at least one package on this list.
-        */
-        Resources resources = mContext.getResources();
-        String[] pkgs = resources.getStringArray(
-                com.android.internal.R.array.config_locationProviderPackageNames);
-        if (D) {
-            Log.d(TAG, "certificates for location providers pulled from: " +
-                    Arrays.toString(pkgs));
-        }
-
-        ensureFallbackFusedProviderPresentLocked(pkgs);
-
-        LocationProviderProxy networkProvider = LocationProviderProxy.createAndBind(
+        LocationProviderProxy networkProvider = LocationProviderProxy.createAndRegister(
                 mContext,
                 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);
+                com.android.internal.R.string.config_networkLocationProviderPackageName);
         if (networkProvider != null) {
             LocationProviderManager networkManager = new LocationProviderManager(NETWORK_PROVIDER);
             mProviderManagers.add(networkManager);
@@ -657,13 +559,18 @@
             Slog.w(TAG, "no network location provider found");
         }
 
+        // ensure that a fused provider exists which will work in direct boot
+        Preconditions.checkState(!mContext.getPackageManager().queryIntentServicesAsUser(
+                new Intent(FUSED_LOCATION_SERVICE_ACTION),
+                MATCH_DIRECT_BOOT_AWARE | MATCH_SYSTEM_ONLY, UserHandle.USER_SYSTEM).isEmpty(),
+                "Unable to find a direct boot aware fused location provider");
+
         // bind to fused provider
-        LocationProviderProxy fusedProvider = LocationProviderProxy.createAndBind(
+        LocationProviderProxy fusedProvider = LocationProviderProxy.createAndRegister(
                 mContext,
                 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);
+                com.android.internal.R.string.config_fusedLocationProviderPackageName);
         if (fusedProvider != null) {
             LocationProviderManager fusedManager = new LocationProviderManager(FUSED_PROVIDER);
             mProviderManagers.add(fusedManager);
@@ -674,47 +581,30 @@
         }
 
         // bind to geocoder provider
-        mGeocodeProvider = GeocoderProxy.createAndBind(mContext,
-                com.android.internal.R.bool.config_enableGeocoderOverlay,
-                com.android.internal.R.string.config_geocoderProviderPackageName,
-                com.android.internal.R.array.config_locationProviderPackageNames);
+        mGeocodeProvider = GeocoderProxy.createAndRegister(mContext);
         if (mGeocodeProvider == null) {
             Slog.e(TAG, "no geocoder provider found");
         }
 
+        // bind to geofence proxy
         if (mGnssManagerService != null) {
-            // bind to geofence provider
-            GeofenceProxy provider = GeofenceProxy.createAndBind(
-                    mContext, com.android.internal.R.bool.config_enableGeofenceOverlay,
-                    com.android.internal.R.string.config_geofenceProviderPackageName,
-                    com.android.internal.R.array.config_locationProviderPackageNames,
-                    mGnssManagerService.getGpsGeofenceProxy(),
-                    null);
-            if (provider == null) {
-                Slog.d(TAG, "Unable to bind FLP Geofence proxy.");
+            IGpsGeofenceHardware gpsGeofenceHardware = mGnssManagerService.getGpsGeofenceProxy();
+            if (gpsGeofenceHardware != null) {
+                GeofenceProxy provider = GeofenceProxy.createAndBind(mContext, gpsGeofenceHardware);
+                if (provider == null) {
+                    Slog.d(TAG, "unable to bind to GeofenceProxy");
+                }
             }
         }
 
         // bind to hardware activity recognition
-        boolean activityRecognitionHardwareIsSupported = ActivityRecognitionHardware.isSupported();
-        ActivityRecognitionHardware activityRecognitionHardware = null;
-        if (activityRecognitionHardwareIsSupported) {
-            activityRecognitionHardware = ActivityRecognitionHardware.getInstance(mContext);
-        } else {
-            Slog.d(TAG, "Hardware Activity-Recognition not supported.");
-        }
-        ActivityRecognitionProxy proxy = ActivityRecognitionProxy.createAndBind(
-                mContext,
-                activityRecognitionHardwareIsSupported,
-                activityRecognitionHardware,
-                com.android.internal.R.bool.config_enableActivityRecognitionHardwareOverlay,
-                com.android.internal.R.string.config_activityRecognitionHardwarePackageName,
-                com.android.internal.R.array.config_locationProviderPackageNames);
-        if (proxy == null) {
-            Slog.d(TAG, "Unable to bind ActivityRecognitionProxy.");
+        HardwareActivityRecognitionProxy hardwareActivityRecognitionProxy =
+                HardwareActivityRecognitionProxy.createAndRegister(mContext);
+        if (hardwareActivityRecognitionProxy == null) {
+            Log.e(TAG, "unable to bind ActivityRecognitionProxy");
         }
 
-        String[] testProviderStrings = resources.getStringArray(
+        String[] testProviderStrings = mContext.getResources().getStringArray(
                 com.android.internal.R.array.config_testLocationProviders);
         for (String testProviderString : testProviderStrings) {
             String[] fragments = testProviderString.split(",");
diff --git a/services/core/java/com/android/server/ServiceWatcher.java b/services/core/java/com/android/server/ServiceWatcher.java
index 7f51aa9..8564cb4 100644
--- a/services/core/java/com/android/server/ServiceWatcher.java
+++ b/services/core/java/com/android/server/ServiceWatcher.java
@@ -16,7 +16,19 @@
 
 package com.android.server;
 
+import static android.content.Context.BIND_AUTO_CREATE;
+import static android.content.Context.BIND_NOT_FOREGROUND;
+import static android.content.Context.BIND_NOT_VISIBLE;
+import static android.content.pm.PackageManager.GET_META_DATA;
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AUTO;
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
+import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
+import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
+
+import android.annotation.BoolRes;
 import android.annotation.Nullable;
+import android.annotation.StringRes;
+import android.annotation.UserIdInt;
 import android.app.ActivityManager;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -24,11 +36,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.ServiceConnection;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ResolveInfo;
-import android.content.pm.Signature;
 import android.content.res.Resources;
 import android.os.Bundle;
 import android.os.Handler;
@@ -37,15 +45,10 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.util.Log;
-import android.util.Slog;
 
 import com.android.internal.content.PackageMonitor;
 import com.android.internal.util.Preconditions;
 
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Objects;
 import java.util.concurrent.Callable;
@@ -55,16 +58,16 @@
 import java.util.concurrent.TimeoutException;
 
 /**
- * Find the best Service, and bind to it.
- * Handles run-time package changes.
+ * Maintains a binding to the best service that matches the given intent information. Bind and
+ * unbind callbacks, as well as all binder operations, will all be run on the given handler.
  */
 public class ServiceWatcher implements ServiceConnection {
 
     private static final String TAG = "ServiceWatcher";
-    private static final boolean D = false;
+    private static final boolean D = Log.isLoggable(TAG, Log.DEBUG);
 
-    public static final String EXTRA_SERVICE_VERSION = "serviceVersion";
-    public static final String EXTRA_SERVICE_IS_MULTIUSER = "serviceIsMultiuser";
+    private static final String EXTRA_SERVICE_VERSION = "serviceVersion";
+    private static final String EXTRA_SERVICE_IS_MULTIUSER = "serviceIsMultiuser";
 
     private static final long BLOCKING_BINDER_TIMEOUT_MS = 30 * 1000;
 
@@ -83,280 +86,300 @@
         T run(IBinder binder) throws RemoteException;
     }
 
-    public static ArrayList<HashSet<Signature>> getSignatureSets(Context context,
-            String... packageNames) {
-        PackageManager pm = context.getPackageManager();
+    /**
+     * Information on the service ServiceWatcher has selected as the best option for binding.
+     */
+    public static final class ServiceInfo implements Comparable<ServiceInfo> {
 
-        ArrayList<HashSet<Signature>> signatureSets = new ArrayList<>(packageNames.length);
-        for (String packageName : packageNames) {
-            try {
-                Signature[] signatures = pm.getPackageInfo(packageName,
-                        PackageManager.MATCH_SYSTEM_ONLY
-                                | PackageManager.GET_SIGNATURES).signatures;
+        public static final ServiceInfo NONE = new ServiceInfo(Integer.MIN_VALUE, null,
+                UserHandle.USER_NULL);
 
-                HashSet<Signature> set = new HashSet<>();
-                Collections.addAll(set, signatures);
-                signatureSets.add(set);
-            } catch (NameNotFoundException e) {
-                Log.w(TAG, packageName + " not found");
+        public final int version;
+        @Nullable public final ComponentName component;
+        @UserIdInt public final int userId;
+
+        private ServiceInfo(ResolveInfo resolveInfo, int currentUserId) {
+            Preconditions.checkArgument(resolveInfo.serviceInfo.getComponentName() != null);
+
+            Bundle metadata = resolveInfo.serviceInfo.metaData;
+            boolean isMultiuser;
+            if (metadata != null) {
+                version = metadata.getInt(EXTRA_SERVICE_VERSION, Integer.MIN_VALUE);
+                isMultiuser = metadata.getBoolean(EXTRA_SERVICE_IS_MULTIUSER, false);
+            } else {
+                version = Integer.MIN_VALUE;
+                isMultiuser = false;
             }
+
+            component = resolveInfo.serviceInfo.getComponentName();
+            userId = isMultiuser ? UserHandle.USER_SYSTEM : currentUserId;
         }
-        return signatureSets;
-    }
 
-    /** Checks if signatures match. */
-    public static boolean isSignatureMatch(Signature[] signatures,
-            List<HashSet<Signature>> sigSets) {
-        if (signatures == null) return false;
+        private ServiceInfo(int version, @Nullable ComponentName component, int userId) {
+            Preconditions.checkArgument(component != null || version == Integer.MIN_VALUE);
+            this.version = version;
+            this.component = component;
+            this.userId = userId;
+        }
 
-        // build hashset of input to test against
-        HashSet<Signature> inputSet = new HashSet<>();
-        Collections.addAll(inputSet, signatures);
-
-        // test input against each of the signature sets
-        for (HashSet<Signature> referenceSet : sigSets) {
-            if (referenceSet.equals(inputSet)) {
+        @Override
+        public boolean equals(Object o) {
+            if (this == o) {
                 return true;
             }
+            if (!(o instanceof ServiceInfo)) {
+                return false;
+            }
+            ServiceInfo that = (ServiceInfo) o;
+            return version == that.version && userId == that.userId
+                    && Objects.equals(component, that.component);
         }
-        return false;
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(version, component, userId);
+        }
+
+        @Override
+        public int compareTo(ServiceInfo that) {
+            // ServiceInfos with higher version numbers always win (having a version number >
+            // MIN_VALUE implies having a non-null component). if version numbers are equal, a
+            // non-null component wins over a null component. if the version numbers are equal and
+            // both components exist then we prefer components that work for all users vs components
+            // that only work for a single user at a time. otherwise everything's equal.
+            int ret = Integer.compare(version, that.version);
+            if (ret == 0) {
+                if (component == null && that.component != null) {
+                    ret = -1;
+                } else if (component != null && that.component == null) {
+                    ret = 1;
+                } else {
+                    if (userId != UserHandle.USER_SYSTEM && that.userId == UserHandle.USER_SYSTEM) {
+                        ret = -1;
+                    } else if (userId == UserHandle.USER_SYSTEM
+                            && that.userId != UserHandle.USER_SYSTEM) {
+                        ret = 1;
+                    }
+                }
+            }
+            return ret;
+        }
+
+        @Override
+        public String toString() {
+            return component + "@" + version + "[u" + userId + "]";
+        }
     }
 
     private final Context mContext;
-    private final String mTag;
-    private final String mAction;
-    private final String mServicePackageName;
-    private final List<HashSet<Signature>> mSignatureSets;
-
     private final Handler mHandler;
+    private final Intent mIntent;
 
-    // read/write from handler thread
-    private IBinder mBestService;
+    @Nullable private final BinderRunner mOnBind;
+    @Nullable private final Runnable mOnUnbind;
+
+    // read/write from handler thread only
     private int mCurrentUserId;
 
-    // read from any thread, write from handler thread
-    private volatile ComponentName mBestComponent;
-    private volatile int mBestVersion;
-    private volatile int mBestUserId;
+    // write from handler thread only, read anywhere
+    private volatile ServiceInfo mServiceInfo;
 
-    public ServiceWatcher(Context context, String logTag, String action,
-            int overlaySwitchResId, int defaultServicePackageNameResId,
-            int initialPackageNamesResId, Handler handler) {
-        Resources resources = context.getResources();
+    // read/write from handler thread only
+    private IBinder mBinder;
 
+    public ServiceWatcher(Context context, Handler handler, String action,
+            @Nullable BinderRunner onBind, @Nullable Runnable onUnbind,
+            @BoolRes int enableOverlayResId, @StringRes int nonOverlayPackageResId) {
         mContext = context;
-        mTag = logTag;
-        mAction = action;
+        mHandler = FgThread.getHandler();
+        mIntent = new Intent(Objects.requireNonNull(action));
 
-        boolean enableOverlay = resources.getBoolean(overlaySwitchResId);
-        if (enableOverlay) {
-            String[] pkgs = resources.getStringArray(initialPackageNamesResId);
-            mServicePackageName = null;
-            mSignatureSets = getSignatureSets(context, pkgs);
-            if (D) Log.d(mTag, "Overlay enabled, packages=" + Arrays.toString(pkgs));
-        } else {
-            mServicePackageName = resources.getString(defaultServicePackageNameResId);
-            mSignatureSets = getSignatureSets(context, mServicePackageName);
-            if (D) Log.d(mTag, "Overlay disabled, default package=" + mServicePackageName);
+        Resources resources = context.getResources();
+        boolean enableOverlay = resources.getBoolean(enableOverlayResId);
+        if (!enableOverlay) {
+            mIntent.setPackage(resources.getString(nonOverlayPackageResId));
         }
 
-        mHandler = handler;
+        mOnBind = onBind;
+        mOnUnbind = onUnbind;
 
-        mBestComponent = null;
-        mBestVersion = Integer.MIN_VALUE;
-        mBestUserId = UserHandle.USER_NULL;
+        mCurrentUserId = UserHandle.USER_NULL;
 
-        mBestService = null;
+        mServiceInfo = ServiceInfo.NONE;
+        mBinder = null;
     }
 
-    protected void onBind() {}
-
-    protected void onUnbind() {}
-
     /**
-     * Start this watcher, including binding to the current best match and
-     * re-binding to any better matches down the road.
-     * <p>
-     * Note that if there are no matching encryption-aware services, we may not
-     * bind to a real service until after the current user is unlocked.
-     *
-     * @return {@code true} if a potential service implementation was found.
+     * Register this class, which will start the process of determining the best matching service
+     * and maintaining a binding to it. Will return false and fail if there are no possible matching
+     * services at the time this functions is called.
      */
-    public final boolean start() {
-        // if we have to return false, do it before registering anything
-        if (isServiceMissing()) return false;
-
-        // listen for relevant package changes if service overlay is enabled on handler
-        if (mServicePackageName == null) {
-            new PackageMonitor() {
-                @Override
-                public void onPackageUpdateFinished(String packageName, int uid) {
-                    bindBestPackage(Objects.equals(packageName, getCurrentPackageName()));
-                }
-
-                @Override
-                public void onPackageAdded(String packageName, int uid) {
-                    bindBestPackage(Objects.equals(packageName, getCurrentPackageName()));
-                }
-
-                @Override
-                public void onPackageRemoved(String packageName, int uid) {
-                    bindBestPackage(Objects.equals(packageName, getCurrentPackageName()));
-                }
-
-                @Override
-                public boolean onPackageChanged(String packageName, int uid, String[] components) {
-                    bindBestPackage(Objects.equals(packageName, getCurrentPackageName()));
-                    return super.onPackageChanged(packageName, uid, components);
-                }
-            }.register(mContext, UserHandle.ALL, true, mHandler);
+    public boolean register() {
+        if (mContext.getPackageManager().queryIntentServicesAsUser(mIntent,
+                MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE | MATCH_SYSTEM_ONLY,
+                UserHandle.USER_SYSTEM).isEmpty()) {
+            return false;
         }
 
-        // listen for user change on handler
+        new PackageMonitor() {
+            @Override
+            public void onPackageUpdateFinished(String packageName, int uid) {
+                ServiceWatcher.this.onPackageChanged(packageName);
+            }
+
+            @Override
+            public void onPackageAdded(String packageName, int uid) {
+                ServiceWatcher.this.onPackageChanged(packageName);
+            }
+
+            @Override
+            public void onPackageRemoved(String packageName, int uid) {
+                ServiceWatcher.this.onPackageChanged(packageName);
+            }
+
+            @Override
+            public boolean onPackageChanged(String packageName, int uid, String[] components) {
+                ServiceWatcher.this.onPackageChanged(packageName);
+                return super.onPackageChanged(packageName, uid, components);
+            }
+        }.register(mContext, UserHandle.ALL, true, mHandler);
+
         IntentFilter intentFilter = new IntentFilter();
         intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
         intentFilter.addAction(Intent.ACTION_USER_UNLOCKED);
         mContext.registerReceiverAsUser(new BroadcastReceiver() {
             @Override
             public void onReceive(Context context, Intent intent) {
-                final String action = intent.getAction();
-                final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
-                        UserHandle.USER_NULL);
-                if (Intent.ACTION_USER_SWITCHED.equals(action)) {
-                    mCurrentUserId = userId;
-                    bindBestPackage(false);
-                } else if (Intent.ACTION_USER_UNLOCKED.equals(action)) {
-                    if (userId == mCurrentUserId) {
-                        bindBestPackage(false);
-                    }
+                String action = intent.getAction();
+                if (action == null) {
+                    return;
                 }
+                int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
+                if (userId == UserHandle.USER_NULL) {
+                    return;
+                }
+
+                switch (action) {
+                    case Intent.ACTION_USER_SWITCHED:
+                        onUserSwitched(userId);
+                        break;
+                    case Intent.ACTION_USER_UNLOCKED:
+                        onUserUnlocked(userId);
+                        break;
+                    default:
+                        break;
+                }
+
             }
         }, UserHandle.ALL, intentFilter, null, mHandler);
 
         mCurrentUserId = ActivityManager.getCurrentUser();
 
-        mHandler.post(() -> bindBestPackage(false));
+        mHandler.post(() -> onBestServiceChanged(false));
         return true;
     }
 
-    /** Returns the name of the currently connected package or null. */
-    @Nullable
-    public String getCurrentPackageName() {
-        ComponentName bestComponent = mBestComponent;
-        return bestComponent == null ? null : bestComponent.getPackageName();
+    /**
+     * Returns information on the currently selected service.
+     */
+    public ServiceInfo getBoundService() {
+        return mServiceInfo;
     }
 
-    private boolean isServiceMissing() {
-        return mContext.getPackageManager().queryIntentServicesAsUser(new Intent(mAction),
-                PackageManager.MATCH_DIRECT_BOOT_AWARE
-                        | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
-                UserHandle.USER_SYSTEM).isEmpty();
-    }
-
-    private void bindBestPackage(boolean forceRebind) {
+    private void onBestServiceChanged(boolean forceRebind) {
         Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
 
-        Intent intent = new Intent(mAction);
-        if (mServicePackageName != null) {
-            intent.setPackage(mServicePackageName);
-        }
-
-        List<ResolveInfo> rInfos = mContext.getPackageManager().queryIntentServicesAsUser(intent,
-                PackageManager.GET_META_DATA | PackageManager.MATCH_DIRECT_BOOT_AUTO,
+        List<ResolveInfo> resolveInfos = mContext.getPackageManager().queryIntentServicesAsUser(
+                mIntent,
+                GET_META_DATA | MATCH_DIRECT_BOOT_AUTO | MATCH_SYSTEM_ONLY,
                 mCurrentUserId);
-        if (rInfos == null) {
-            rInfos = Collections.emptyList();
-        }
 
-        ComponentName bestComponent = null;
-        int bestVersion = Integer.MIN_VALUE;
-        boolean bestIsMultiuser = false;
-
-        for (ResolveInfo rInfo : rInfos) {
-            ComponentName component = rInfo.serviceInfo.getComponentName();
-            String packageName = component.getPackageName();
-
-            // check signature
-            try {
-                PackageInfo pInfo = mContext.getPackageManager().getPackageInfo(packageName,
-                        PackageManager.GET_SIGNATURES
-                                | PackageManager.MATCH_DIRECT_BOOT_AUTO);
-                if (!isSignatureMatch(pInfo.signatures, mSignatureSets)) {
-                    Log.w(mTag, packageName + " resolves service " + mAction
-                            + ", but has wrong signature, ignoring");
-                    continue;
-                }
-            } catch (NameNotFoundException e) {
-                Log.wtf(mTag, e);
-                continue;
-            }
-
-            // check metadata
-            Bundle metadata = rInfo.serviceInfo.metaData;
-            int version = Integer.MIN_VALUE;
-            boolean isMultiuser = false;
-            if (metadata != null) {
-                version = metadata.getInt(EXTRA_SERVICE_VERSION, Integer.MIN_VALUE);
-                isMultiuser = metadata.getBoolean(EXTRA_SERVICE_IS_MULTIUSER, false);
-            }
-
-            if (version > bestVersion) {
-                bestComponent = component;
-                bestVersion = version;
-                bestIsMultiuser = isMultiuser;
+        ServiceInfo bestServiceInfo = ServiceInfo.NONE;
+        for (ResolveInfo resolveInfo : resolveInfos) {
+            ServiceInfo serviceInfo = new ServiceInfo(resolveInfo, mCurrentUserId);
+            if (serviceInfo.compareTo(bestServiceInfo) > 0) {
+                bestServiceInfo = serviceInfo;
             }
         }
 
-        if (D) {
-            Log.d(mTag, String.format("bindBestPackage for %s : %s found %d, %s", mAction,
-                    (mServicePackageName == null ? ""
-                            : "(" + mServicePackageName + ") "), rInfos.size(),
-                    (bestComponent == null ? "no new best component"
-                            : "new best component: " + bestComponent)));
+        if (forceRebind || !bestServiceInfo.equals(mServiceInfo)) {
+            rebind(bestServiceInfo);
+        }
+    }
+
+    private void rebind(ServiceInfo newServiceInfo) {
+        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
+
+        if (!mServiceInfo.equals(ServiceInfo.NONE)) {
+            if (D) {
+                Log.i(TAG, "[" + mIntent.getAction() + "] unbinding from " + mServiceInfo);
+            }
+
+            mContext.unbindService(this);
+            mServiceInfo = ServiceInfo.NONE;
         }
 
-        if (bestComponent == null) {
-            Slog.w(mTag, "Odd, no component found for service " + mAction);
-            unbind();
+        mServiceInfo = newServiceInfo;
+        if (mServiceInfo.equals(ServiceInfo.NONE)) {
             return;
         }
 
-        int userId = bestIsMultiuser ? UserHandle.USER_SYSTEM : mCurrentUserId;
-        boolean alreadyBound = Objects.equals(bestComponent, mBestComponent)
-                && bestVersion == mBestVersion && userId == mBestUserId;
-        if (forceRebind || !alreadyBound) {
-            unbind();
-            bind(bestComponent, bestVersion, userId);
+        Preconditions.checkState(mServiceInfo.component != null);
+
+        if (D) {
+            Log.i(TAG, getLogPrefix() + " binding to " + mServiceInfo);
+        }
+
+        Intent bindIntent = new Intent(mIntent).setComponent(mServiceInfo.component);
+        mContext.bindServiceAsUser(bindIntent, this,
+                BIND_AUTO_CREATE | BIND_NOT_FOREGROUND | BIND_NOT_VISIBLE,
+                mHandler, UserHandle.of(mServiceInfo.userId));
+    }
+
+    @Override
+    public final void onServiceConnected(ComponentName component, IBinder binder) {
+        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
+
+        if (D) {
+            Log.i(TAG, getLogPrefix() + " connected to " + component);
+        }
+
+        mBinder = binder;
+        if (mOnBind != null) {
+            runOnBinder(mOnBind);
         }
     }
 
-    private void bind(ComponentName component, int version, int userId) {
+    @Override
+    public final void onServiceDisconnected(ComponentName component) {
         Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
 
-        Intent intent = new Intent(mAction);
-        intent.setComponent(component);
-
-        mBestComponent = component;
-        mBestVersion = version;
-        mBestUserId = userId;
-
-        if (D) Log.d(mTag, "binding " + component + " (v" + version + ") (u" + userId + ")");
-        mContext.bindServiceAsUser(intent, this,
-                Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND | Context.BIND_NOT_VISIBLE,
-                UserHandle.of(userId));
-    }
-
-    private void unbind() {
-        Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
-
-        if (mBestComponent != null) {
-            if (D) Log.d(mTag, "unbinding " + mBestComponent);
-            mContext.unbindService(this);
+        if (D) {
+            Log.i(TAG, getLogPrefix() + " disconnected from " + component);
         }
 
-        mBestComponent = null;
-        mBestVersion = Integer.MIN_VALUE;
-        mBestUserId = UserHandle.USER_NULL;
+        mBinder = null;
+        if (mOnUnbind != null) {
+            mOnUnbind.run();
+        }
+    }
+
+    private void onUserSwitched(@UserIdInt int userId) {
+        mCurrentUserId = userId;
+        onBestServiceChanged(false);
+    }
+
+    private void onUserUnlocked(@UserIdInt int userId) {
+        if (userId == mCurrentUserId) {
+            onBestServiceChanged(false);
+        }
+    }
+
+    private void onPackageChanged(String packageName) {
+        // force a rebind if the changed package was the currently connected package
+        String currentPackageName =
+                mServiceInfo.component != null ? mServiceInfo.component.getPackageName() : null;
+        onBestServiceChanged(packageName.equals(currentPackageName));
     }
 
     /**
@@ -365,26 +388,26 @@
      */
     public final void runOnBinder(BinderRunner runner) {
         runOnHandler(() -> {
-            if (mBestService == null) {
+            if (mBinder == null) {
                 return;
             }
 
             try {
-                runner.run(mBestService);
-            } catch (RuntimeException e) {
-                // the code being run is privileged, but may be outside the system server, and thus
-                // we cannot allow runtime exceptions to crash the system server
-                Log.e(TAG, "exception while while running " + runner + " on " + mBestService
-                        + " from " + this, e);
-            } catch (RemoteException e) {
-                // do nothing
+                runner.run(mBinder);
+            } catch (RuntimeException | RemoteException e) {
+                // binders may propagate some specific non-RemoteExceptions from the other side
+                // through the binder as well - we cannot allow those to crash the system server
+                Log.e(TAG, getLogPrefix() + " exception running on " + mServiceInfo, e);
             }
         });
     }
 
     /**
      * Runs the given function synchronously if currently connected, and returns the default value
-     * if not currently connected or if any exception is thrown.
+     * if not currently connected or if any exception is thrown. Do not obtain any locks within the
+     * BlockingBinderRunner, or risk deadlock. The default value will be returned if there is no
+     * service connection when this is run, if a RemoteException occurs, or if the operation times
+     * out.
      *
      * @deprecated Using this function is an indication that your AIDL API is broken. Calls from
      * system server to outside MUST be one-way, and so cannot return any result, and this
@@ -395,13 +418,16 @@
     public final <T> T runOnBinderBlocking(BlockingBinderRunner<T> runner, T defaultValue) {
         try {
             return runOnHandlerBlocking(() -> {
-                if (mBestService == null) {
+                if (mBinder == null) {
                     return defaultValue;
                 }
 
                 try {
-                    return runner.run(mBestService);
-                } catch (RemoteException e) {
+                    return runner.run(mBinder);
+                } catch (RuntimeException | RemoteException e) {
+                    // binders may propagate some specific non-RemoteExceptions from the other side
+                    // through the binder as well - we cannot allow those to crash the system server
+                    Log.e(TAG, getLogPrefix() + " exception running on " + mServiceInfo, e);
                     return defaultValue;
                 }
             });
@@ -410,30 +436,6 @@
         }
     }
 
-    @Override
-    public final void onServiceConnected(ComponentName component, IBinder binder) {
-        runOnHandler(() -> {
-            if (D) Log.d(mTag, component + " connected");
-            mBestService = binder;
-            onBind();
-        });
-    }
-
-    @Override
-    public final void onServiceDisconnected(ComponentName component) {
-        runOnHandler(() -> {
-            if (D) Log.d(mTag, component + " disconnected");
-            mBestService = null;
-            onUnbind();
-        });
-    }
-
-    @Override
-    public String toString() {
-        ComponentName bestComponent = mBestComponent;
-        return bestComponent == null ? "null" : bestComponent.toShortString() + "@" + mBestVersion;
-    }
-
     private void runOnHandler(Runnable r) {
         if (Looper.myLooper() == mHandler.getLooper()) {
             r.run();
@@ -467,4 +469,8 @@
             }
         }
     }
+
+    private String getLogPrefix() {
+        return "[" + mIntent.getAction() + "]";
+    }
 }
diff --git a/services/core/java/com/android/server/location/ActivityRecognitionProxy.java b/services/core/java/com/android/server/location/ActivityRecognitionProxy.java
deleted file mode 100644
index 80ab790..0000000
--- a/services/core/java/com/android/server/location/ActivityRecognitionProxy.java
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * Copyright (C) 2014 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.content.Context;
-import android.hardware.location.ActivityRecognitionHardware;
-import android.hardware.location.IActivityRecognitionHardwareClient;
-import android.hardware.location.IActivityRecognitionHardwareWatcher;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.Log;
-
-import com.android.server.FgThread;
-import com.android.server.ServiceWatcher;
-
-/**
- * Proxy class to bind GmsCore to the ActivityRecognitionHardware.
- *
- * @hide
- */
-public class ActivityRecognitionProxy {
-
-    private static final String TAG = "ActivityRecognitionProxy";
-
-    /**
-     * Creates an instance of the proxy and binds it to the appropriate FusedProvider.
-     *
-     * @return An instance of the proxy if it could be bound, null otherwise.
-     */
-    public static ActivityRecognitionProxy createAndBind(
-            Context context,
-            boolean activityRecognitionHardwareIsSupported,
-            ActivityRecognitionHardware activityRecognitionHardware,
-            int overlaySwitchResId,
-            int defaultServicePackageNameResId,
-            int initialPackageNameResId) {
-        ActivityRecognitionProxy activityRecognitionProxy = new ActivityRecognitionProxy(
-                context,
-                activityRecognitionHardwareIsSupported,
-                activityRecognitionHardware,
-                overlaySwitchResId,
-                defaultServicePackageNameResId,
-                initialPackageNameResId);
-
-        if (activityRecognitionProxy.mServiceWatcher.start()) {
-            return activityRecognitionProxy;
-        } else {
-            return null;
-        }
-    }
-
-    private final ServiceWatcher mServiceWatcher;
-    private final boolean mIsSupported;
-    private final ActivityRecognitionHardware mInstance;
-
-    private ActivityRecognitionProxy(
-            Context context,
-            boolean activityRecognitionHardwareIsSupported,
-            ActivityRecognitionHardware activityRecognitionHardware,
-            int overlaySwitchResId,
-            int defaultServicePackageNameResId,
-            int initialPackageNameResId) {
-        mIsSupported = activityRecognitionHardwareIsSupported;
-        mInstance = activityRecognitionHardware;
-
-        mServiceWatcher = new ServiceWatcher(
-                context,
-                TAG,
-                "com.android.location.service.ActivityRecognitionProvider",
-                overlaySwitchResId,
-                defaultServicePackageNameResId,
-                initialPackageNameResId,
-                FgThread.getHandler()) {
-            @Override
-            protected void onBind() {
-                runOnBinder(ActivityRecognitionProxy.this::initializeService);
-            }
-        };
-    }
-
-    private void initializeService(IBinder binder) {
-        try {
-            String descriptor = binder.getInterfaceDescriptor();
-
-            if (IActivityRecognitionHardwareWatcher.class.getCanonicalName().equals(
-                    descriptor)) {
-                IActivityRecognitionHardwareWatcher watcher =
-                        IActivityRecognitionHardwareWatcher.Stub.asInterface(binder);
-                if (mInstance != null) {
-                    watcher.onInstanceChanged(mInstance);
-                }
-            } else if (IActivityRecognitionHardwareClient.class.getCanonicalName()
-                    .equals(descriptor)) {
-                IActivityRecognitionHardwareClient client =
-                        IActivityRecognitionHardwareClient.Stub.asInterface(binder);
-                client.onAvailabilityChanged(mIsSupported, mInstance);
-            } else {
-                Log.e(TAG, "Invalid descriptor found on connection: " + descriptor);
-            }
-        } catch (RemoteException e) {
-            Log.w(TAG, e);
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/location/GeocoderProxy.java b/services/core/java/com/android/server/location/GeocoderProxy.java
index e6f0ed9..536f95a 100644
--- a/services/core/java/com/android/server/location/GeocoderProxy.java
+++ b/services/core/java/com/android/server/location/GeocoderProxy.java
@@ -16,6 +16,7 @@
 
 package com.android.server.location;
 
+import android.annotation.Nullable;
 import android.content.Context;
 import android.location.Address;
 import android.location.GeocoderParams;
@@ -28,40 +29,38 @@
 
 /**
  * Proxy for IGeocodeProvider implementations.
+ *
+ * @hide
  */
 public class GeocoderProxy {
-    private static final String TAG = "GeocoderProxy";
 
     private static final String SERVICE_ACTION = "com.android.location.service.GeocodeProvider";
 
-    private final ServiceWatcher mServiceWatcher;
-
-    public static GeocoderProxy createAndBind(Context context,
-            int overlaySwitchResId, int defaultServicePackageNameResId,
-            int initialPackageNamesResId) {
-        GeocoderProxy proxy = new GeocoderProxy(context, overlaySwitchResId,
-                defaultServicePackageNameResId, initialPackageNamesResId);
-        if (proxy.bind()) {
+    /**
+     * Creates and registers this proxy. If no suitable service is available for the proxy, returns
+     * null.
+     */
+    @Nullable
+    public static GeocoderProxy createAndRegister(Context context) {
+        GeocoderProxy proxy = new GeocoderProxy(context);
+        if (proxy.register()) {
             return proxy;
         } else {
             return null;
         }
     }
 
-    private GeocoderProxy(Context context,
-            int overlaySwitchResId, int defaultServicePackageNameResId,
-            int initialPackageNamesResId) {
-        mServiceWatcher = new ServiceWatcher(context, TAG, SERVICE_ACTION, overlaySwitchResId,
-                defaultServicePackageNameResId, initialPackageNamesResId,
-                BackgroundThread.getHandler());
+    private final ServiceWatcher mServiceWatcher;
+
+    private GeocoderProxy(Context context) {
+        mServiceWatcher = new ServiceWatcher(context, BackgroundThread.getHandler(), SERVICE_ACTION,
+                null, null,
+                com.android.internal.R.bool.config_enableGeocoderOverlay,
+                com.android.internal.R.string.config_geocoderProviderPackageName);
     }
 
-    private boolean bind() {
-        return mServiceWatcher.start();
-    }
-
-    public String getConnectedPackageName() {
-        return mServiceWatcher.getCurrentPackageName();
+    private boolean register() {
+        return mServiceWatcher.register();
     }
 
     public String getFromLocation(double latitude, double longitude, int maxResults,
@@ -83,5 +82,4 @@
                     maxResults, params, addrs);
         }, "Service not Available");
     }
-
 }
diff --git a/services/core/java/com/android/server/location/GeofenceProxy.java b/services/core/java/com/android/server/location/GeofenceProxy.java
index ce93661..f006fb1 100644
--- a/services/core/java/com/android/server/location/GeofenceProxy.java
+++ b/services/core/java/com/android/server/location/GeofenceProxy.java
@@ -22,7 +22,6 @@
 import android.content.ServiceConnection;
 import android.hardware.location.GeofenceHardwareService;
 import android.hardware.location.IGeofenceHardware;
-import android.location.IFusedGeofenceHardware;
 import android.location.IGeofenceProvider;
 import android.location.IGpsGeofenceHardware;
 import android.os.IBinder;
@@ -33,6 +32,8 @@
 import com.android.server.FgThread;
 import com.android.server.ServiceWatcher;
 
+import java.util.Objects;
+
 /**
  * @hide
  */
@@ -41,64 +42,41 @@
     private static final String TAG = "GeofenceProxy";
     private static final String SERVICE_ACTION = "com.android.location.service.GeofenceProvider";
 
-    private final Context mContext;
-    private final ServiceWatcher mServiceWatcher;
-
     @Nullable
-    private final IGpsGeofenceHardware mGpsGeofenceHardware;
-    @Nullable
-    private final IFusedGeofenceHardware mFusedGeofenceHardware;
-
-    private volatile IGeofenceHardware mGeofenceHardware;
-
-    private final ServiceWatcher.BinderRunner mUpdateGeofenceHardware = (binder) -> {
-        IGeofenceProvider provider = IGeofenceProvider.Stub.asInterface(binder);
-        try {
-            provider.setGeofenceHardware(mGeofenceHardware);
-        } catch (RemoteException e) {
-            Log.w(TAG, e);
-        }
-    };
-
-    public static GeofenceProxy createAndBind(Context context,
-            int overlaySwitchResId, int defaultServicePackageNameResId,
-            int initialPackageNamesResId, @Nullable IGpsGeofenceHardware gpsGeofence,
-            @Nullable IFusedGeofenceHardware fusedGeofenceHardware) {
-        GeofenceProxy proxy = new GeofenceProxy(context, overlaySwitchResId,
-                defaultServicePackageNameResId, initialPackageNamesResId, gpsGeofence,
-                fusedGeofenceHardware);
-
-        if (proxy.bind()) {
+    public static GeofenceProxy createAndBind(Context context, IGpsGeofenceHardware gpsGeofence) {
+        GeofenceProxy proxy = new GeofenceProxy(context, gpsGeofence);
+        if (proxy.register(context)) {
             return proxy;
         } else {
             return null;
         }
     }
 
-    private GeofenceProxy(Context context,
-            int overlaySwitchResId, int defaultServicePackageNameResId,
-            int initialPackageNamesResId, @Nullable IGpsGeofenceHardware gpsGeofence,
-            @Nullable IFusedGeofenceHardware fusedGeofenceHardware) {
-        mContext = context;
-        mServiceWatcher = new ServiceWatcher(context, TAG, SERVICE_ACTION, overlaySwitchResId,
-                defaultServicePackageNameResId, initialPackageNamesResId,
-                FgThread.getHandler()) {
-            @Override
-            protected void onBind() {
-                runOnBinder(mUpdateGeofenceHardware);
-            }
-        };
+    private final IGpsGeofenceHardware mGpsGeofenceHardware;
+    private final ServiceWatcher mServiceWatcher;
 
-        mGpsGeofenceHardware = gpsGeofence;
-        mFusedGeofenceHardware = fusedGeofenceHardware;
+    private volatile IGeofenceHardware mGeofenceHardware;
+
+    private GeofenceProxy(Context context, IGpsGeofenceHardware gpsGeofence) {
+        mGpsGeofenceHardware = Objects.requireNonNull(gpsGeofence);
+        mServiceWatcher = new ServiceWatcher(context, FgThread.getHandler(), SERVICE_ACTION,
+                this::updateGeofenceHardware, null,
+                com.android.internal.R.bool.config_enableGeofenceOverlay,
+                com.android.internal.R.string.config_geofenceProviderPackageName);
 
         mGeofenceHardware = null;
     }
 
-    private boolean bind() {
-        if (mServiceWatcher.start()) {
-            mContext.bindServiceAsUser(new Intent(mContext, GeofenceHardwareService.class),
-                    new GeofenceProxyServiceConnection(), Context.BIND_AUTO_CREATE,
+    private void updateGeofenceHardware(IBinder binder) throws RemoteException {
+        IGeofenceProvider.Stub.asInterface(binder).setGeofenceHardware(mGeofenceHardware);
+    }
+
+    private boolean register(Context context) {
+        if (mServiceWatcher.register()) {
+            context.bindServiceAsUser(
+                    new Intent(context, GeofenceHardwareService.class),
+                    new GeofenceProxyServiceConnection(),
+                    Context.BIND_AUTO_CREATE,
                     UserHandle.SYSTEM);
             return true;
         }
@@ -113,24 +91,18 @@
             IGeofenceHardware geofenceHardware = IGeofenceHardware.Stub.asInterface(service);
 
             try {
-                if (mGpsGeofenceHardware != null) {
-                    geofenceHardware.setGpsGeofenceHardware(mGpsGeofenceHardware);
-                }
-                if (mFusedGeofenceHardware != null) {
-                    geofenceHardware.setFusedGeofenceHardware(mFusedGeofenceHardware);
-                }
-
+                geofenceHardware.setGpsGeofenceHardware(mGpsGeofenceHardware);
                 mGeofenceHardware = geofenceHardware;
-                mServiceWatcher.runOnBinder(mUpdateGeofenceHardware);
-            } catch (Exception e) {
-                Log.w(TAG, e);
+                mServiceWatcher.runOnBinder(GeofenceProxy.this::updateGeofenceHardware);
+            } catch (RemoteException e) {
+                Log.w(TAG, "unable to initialize geofence hardware", e);
             }
         }
 
         @Override
         public void onServiceDisconnected(ComponentName name) {
             mGeofenceHardware = null;
-            mServiceWatcher.runOnBinder(mUpdateGeofenceHardware);
+            mServiceWatcher.runOnBinder(GeofenceProxy.this::updateGeofenceHardware);
         }
     }
 }
diff --git a/services/core/java/com/android/server/location/HardwareActivityRecognitionProxy.java b/services/core/java/com/android/server/location/HardwareActivityRecognitionProxy.java
new file mode 100644
index 0000000..9d9852b
--- /dev/null
+++ b/services/core/java/com/android/server/location/HardwareActivityRecognitionProxy.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2014 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.annotation.Nullable;
+import android.content.Context;
+import android.hardware.location.ActivityRecognitionHardware;
+import android.hardware.location.IActivityRecognitionHardwareClient;
+import android.hardware.location.IActivityRecognitionHardwareWatcher;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.server.FgThread;
+import com.android.server.ServiceWatcher;
+
+/**
+ * Proxy class to bind GmsCore to the ActivityRecognitionHardware.
+ *
+ * @hide
+ */
+public class HardwareActivityRecognitionProxy {
+
+    private static final String TAG = "ARProxy";
+    private static final String SERVICE_ACTION =
+            "com.android.location.service.ActivityRecognitionProvider";
+
+    /**
+     * Creates and registers this proxy. If no suitable service is available for the proxy, returns
+     * null.
+     */
+    @Nullable
+    public static HardwareActivityRecognitionProxy createAndRegister(Context context) {
+        HardwareActivityRecognitionProxy arProxy = new HardwareActivityRecognitionProxy(context);
+        if (arProxy.register()) {
+            return arProxy;
+        } else {
+            return null;
+        }
+    }
+
+    private final boolean mIsSupported;
+    private final ActivityRecognitionHardware mInstance;
+
+    private final ServiceWatcher mServiceWatcher;
+
+    private HardwareActivityRecognitionProxy(Context context) {
+        mIsSupported = ActivityRecognitionHardware.isSupported();
+        if (mIsSupported) {
+            mInstance = ActivityRecognitionHardware.getInstance(context);
+        } else {
+            mInstance = null;
+        }
+
+        mServiceWatcher = new ServiceWatcher(context,
+                FgThread.getHandler(),
+                SERVICE_ACTION,
+                this::onBind,
+                null,
+                com.android.internal.R.bool.config_enableActivityRecognitionHardwareOverlay,
+                com.android.internal.R.string.config_activityRecognitionHardwarePackageName);
+    }
+
+    private boolean register() {
+        return mServiceWatcher.register();
+    }
+
+    private void onBind(IBinder binder) throws RemoteException {
+        String descriptor = binder.getInterfaceDescriptor();
+
+        if (IActivityRecognitionHardwareWatcher.class.getCanonicalName().equals(descriptor)) {
+            IActivityRecognitionHardwareWatcher watcher =
+                    IActivityRecognitionHardwareWatcher.Stub.asInterface(binder);
+            if (mInstance != null) {
+                watcher.onInstanceChanged(mInstance);
+            }
+        } else if (IActivityRecognitionHardwareClient.class.getCanonicalName().equals(descriptor)) {
+            IActivityRecognitionHardwareClient client =
+                    IActivityRecognitionHardwareClient.Stub.asInterface(binder);
+            client.onAvailabilityChanged(mIsSupported, mInstance);
+        } else {
+            Log.e(TAG, "Unknown descriptor: " + descriptor);
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/location/LocationProviderProxy.java b/services/core/java/com/android/server/location/LocationProviderProxy.java
index 8a149af..805b018 100644
--- a/services/core/java/com/android/server/location/LocationProviderProxy.java
+++ b/services/core/java/com/android/server/location/LocationProviderProxy.java
@@ -19,12 +19,11 @@
 import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
 
 import android.annotation.Nullable;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.location.Location;
 import android.os.Bundle;
-import android.os.Handler;
-import android.os.HandlerExecutor;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.ArraySet;
@@ -35,7 +34,6 @@
 import com.android.internal.location.ProviderProperties;
 import com.android.internal.location.ProviderRequest;
 import com.android.server.FgThread;
-import com.android.server.LocationManagerService;
 import com.android.server.ServiceWatcher;
 
 import java.io.FileDescriptor;
@@ -49,17 +47,31 @@
 public class LocationProviderProxy extends AbstractLocationProvider {
 
     private static final String TAG = "LocationProviderProxy";
-    private static final boolean D = LocationManagerService.D;
 
     private static final int MAX_ADDITIONAL_PACKAGES = 2;
 
+    /**
+     * Creates and registers this proxy. If no suitable service is available for the proxy, returns
+     * null.
+     */
+    @Nullable
+    public static LocationProviderProxy createAndRegister(Context context, String action,
+            int enableOverlayResId, int nonOverlayPackageResId) {
+        LocationProviderProxy proxy = new LocationProviderProxy(context, action, enableOverlayResId,
+                nonOverlayPackageResId);
+        if (proxy.register()) {
+            return proxy;
+        } else {
+            return null;
+        }
+    }
+
     private final ILocationProviderManager.Stub mManager = new ILocationProviderManager.Stub() {
         // executed on binder thread
         @Override
         public void onSetAdditionalProviderPackages(List<String> packageNames) {
-            int maxCount = Math.min(MAX_ADDITIONAL_PACKAGES, packageNames.size()) + 1;
+            int maxCount = Math.min(MAX_ADDITIONAL_PACKAGES, packageNames.size());
             ArraySet<String> allPackages = new ArraySet<>(maxCount);
-            allPackages.add(mServiceWatcher.getCurrentPackageName());
             for (String packageName : packageNames) {
                 if (packageNames.size() >= maxCount) {
                     return;
@@ -74,6 +86,12 @@
                 }
             }
 
+            // add the binder package
+            ComponentName service = mServiceWatcher.getBoundService().component;
+            if (service != null) {
+                allPackages.add(service.getPackageName());
+            }
+
             setPackageNames(allPackages);
         }
 
@@ -100,63 +118,39 @@
 
     @Nullable private ProviderRequest mRequest;
 
-    /**
-     * Creates a new LocationProviderProxy and immediately begins binding to the best applicable
-     * service.
-     */
-    @Nullable
-    public static LocationProviderProxy createAndBind(Context context, String action,
-            int overlaySwitchResId, int defaultServicePackageNameResId,
-            int initialPackageNamesResId) {
-        LocationProviderProxy proxy = new LocationProviderProxy(context, FgThread.getHandler(),
-                action, overlaySwitchResId, defaultServicePackageNameResId,
-                initialPackageNamesResId);
-        if (proxy.bind()) {
-            return proxy;
-        } else {
-            return null;
-        }
-    }
+    private LocationProviderProxy(Context context, String action, int enableOverlayResId,
+            int nonOverlayPackageResId) {
+        super(context, FgThread.getExecutor());
 
-    private LocationProviderProxy(Context context, Handler handler, String action,
-            int overlaySwitchResId, int defaultServicePackageNameResId,
-            int initialPackageNamesResId) {
-        super(context, new HandlerExecutor(handler), Collections.emptySet());
-
-        mServiceWatcher = new ServiceWatcher(context, TAG, action, overlaySwitchResId,
-                defaultServicePackageNameResId, initialPackageNamesResId, handler) {
-
-            @Override
-            protected void onBind() {
-                runOnBinder(LocationProviderProxy.this::initializeService);
-            }
-
-            @Override
-            protected void onUnbind() {
-                setState(State.EMPTY_STATE);
-            }
-        };
+        mServiceWatcher = new ServiceWatcher(context, FgThread.getHandler(), action, this::onBind,
+                this::onUnbind, enableOverlayResId, nonOverlayPackageResId);
 
         mRequest = null;
     }
 
-    private boolean bind() {
-        return mServiceWatcher.start();
+    private boolean register() {
+        return mServiceWatcher.register();
     }
 
-    private void initializeService(IBinder binder) throws RemoteException {
-        ILocationProvider service = ILocationProvider.Stub.asInterface(binder);
-        if (D) Log.d(TAG, "applying state to connected service " + mServiceWatcher);
+    private void onBind(IBinder binder) throws RemoteException {
+        ILocationProvider provider = ILocationProvider.Stub.asInterface(binder);
 
-        setPackageNames(Collections.singleton(mServiceWatcher.getCurrentPackageName()));
+        ComponentName service = mServiceWatcher.getBoundService().component;
+        if (service != null) {
+            setPackageNames(Collections.singleton(service.getPackageName()));
+        }
 
-        service.setLocationProviderManager(mManager);
+        provider.setLocationProviderManager(mManager);
 
         if (mRequest != null) {
-            service.setRequest(mRequest, mRequest.workSource);
+            provider.setRequest(mRequest, mRequest.workSource);
         }
     }
 
+    private void onUnbind() {
+        setState(State.EMPTY_STATE);
+    }
+
     @Override
     public void onSetRequest(ProviderRequest request) {
         mServiceWatcher.runOnBinder(binder -> {