Merge "Refactor ServiceWatcher"

This commit is contained in:
TreeHugger Robot
2020-01-23 02:41:02 +00:00
committed by Android (Google) Code Review
9 changed files with 499 additions and 698 deletions

View File

@@ -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>

View File

@@ -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" />

View File

@@ -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<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()) {
@@ -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(",");

View File

@@ -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<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);
}
@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<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 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> 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 @@ 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() + "]";
}
}

View File

@@ -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);
}
}
}

View File

@@ -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");
}
}

View File

@@ -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);
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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<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 @@ 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 -> {