Merge "Refactor ServiceWatcher"
This commit is contained in:
committed by
Android (Google) Code Review
commit
a355c8c76e
@@ -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>
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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(",");
|
||||
|
||||
@@ -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() + "]";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 -> {
|
||||
|
||||
Reference in New Issue
Block a user