From 6d21a4d31ca147ed3a9f6bca9f1388bd25683c91 Mon Sep 17 00:00:00 2001 From: Soonil Nagarkar Date: Wed, 22 Jan 2020 16:21:00 -0800 Subject: [PATCH] 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 --- core/res/res/values/config.xml | 68 +-- core/res/res/values/symbols.xml | 2 - .../server/LocationManagerService.java | 164 +----- .../com/android/server/ServiceWatcher.java | 524 +++++++++--------- .../location/ActivityRecognitionProxy.java | 118 ---- .../server/location/GeocoderProxy.java | 42 +- .../server/location/GeofenceProxy.java | 88 +-- .../HardwareActivityRecognitionProxy.java | 99 ++++ .../location/LocationProviderProxy.java | 92 ++- 9 files changed, 499 insertions(+), 698 deletions(-) delete mode 100644 services/core/java/com/android/server/location/ActivityRecognitionProxy.java create mode 100644 services/core/java/com/android/server/location/HardwareActivityRecognitionProxy.java diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 7fd444a1a4162..ec3208e5b7377 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -1628,29 +1628,21 @@ config_timeZoneRulesUpdateTrackingEnabled are true.] --> 5 - + true @null - + true @@ -1669,25 +1661,10 @@ --> - - true - - com.android.location.fused - true @@ -1698,9 +1675,7 @@ true @@ -1711,9 +1686,7 @@ true @@ -1721,19 +1694,8 @@ config_enableActivityRecognitionHardwareOverlay is false. --> @null - + com.android.location.fused diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 7e6eb5de25a26..6192edb74cb30 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1869,7 +1869,6 @@ - @@ -2019,7 +2018,6 @@ - diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java index e9db9c819ab71..003525c91c32f 100644 --- a/services/core/java/com/android/server/LocationManagerService.java +++ b/services/core/java/com/android/server/LocationManagerService.java @@ -18,6 +18,8 @@ package com.android.server; 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.BroadcastReceiver; 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.IBatchedLocationCallback; 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.IndentingPrintWriter; 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.FileDescriptor; 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; @@ -542,77 +537,6 @@ public class LocationManagerService extends ILocationManager.Stub { } } - @GuardedBy("mLock") - private void ensureFallbackFusedProviderPresentLocked(String[] pkgs) { - PackageManager pm = mContext.getPackageManager(); - String systemPackageName = mContext.getPackageName(); - ArrayList> sigSets = ServiceWatcher.getSignatureSets(mContext, pkgs); - - List 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()) { @@ -622,33 +546,11 @@ public class LocationManagerService extends ILocationManager.Stub { 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 @@ public class LocationManagerService extends ILocationManager.Stub { 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 @@ public class LocationManagerService extends ILocationManager.Stub { } // 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 7f51aa9068fca..8564cb456ba64 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.Context; 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.Looper; 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.TimeUnit; 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 @@ public class ServiceWatcher implements ServiceConnection { T run(IBinder binder) throws RemoteException; } - public static ArrayList> 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 { - ArrayList> 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 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> 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 inputSet = new HashSet<>(); - Collections.addAll(inputSet, signatures); - - // test input against each of the signature sets - for (HashSet 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); + } + + @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 + "]"; } - return false; } private final Context mContext; - private final String mTag; - private final String mAction; - private final String mServicePackageName; - private final List> 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. - *

- * 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 rInfos = mContext.getPackageManager().queryIntentServicesAsUser(intent, - PackageManager.GET_META_DATA | PackageManager.MATCH_DIRECT_BOOT_AUTO, + List 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 class ServiceWatcher implements ServiceConnection { */ 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 class ServiceWatcher implements ServiceConnection { public final T runOnBinderBlocking(BlockingBinderRunner 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 @@ public class ServiceWatcher implements ServiceConnection { } } - @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 @@ public class ServiceWatcher implements ServiceConnection { } } } + + 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 80ab7903cef72..0000000000000 --- 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 e6f0ed9d14b06..536f95a40431b 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 @@ import java.util.List; /** * 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 @@ public class GeocoderProxy { 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 ce93661a8810c..f006fb177382c 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.Intent; 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 android.util.Log; import com.android.server.FgThread; import com.android.server.ServiceWatcher; +import java.util.Objects; + /** * @hide */ @@ -41,64 +42,41 @@ public final class GeofenceProxy { 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 @@ public final class GeofenceProxy { 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 0000000000000..9d9852ba5da3a --- /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 8a149afa62383..805b018a8f45f 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 @@ package com.android.server.location; 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.ILocationProviderManager; 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 @@ import java.util.List; 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 packageNames) { - int maxCount = Math.min(MAX_ADDITIONAL_PACKAGES, packageNames.size()) + 1; + int maxCount = Math.min(MAX_ADDITIONAL_PACKAGES, packageNames.size()); ArraySet allPackages = new ArraySet<>(maxCount); - allPackages.add(mServiceWatcher.getCurrentPackageName()); for (String packageName : packageNames) { if (packageNames.size() >= maxCount) { return; @@ -74,6 +86,12 @@ public class LocationProviderProxy extends AbstractLocationProvider { } } + // add the binder package + ComponentName service = mServiceWatcher.getBoundService().component; + if (service != null) { + allPackages.add(service.getPackageName()); + } + setPackageNames(allPackages); } @@ -100,63 +118,39 @@ public class LocationProviderProxy extends AbstractLocationProvider { @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 -> {