> mSignatureSets;
-
private final Handler mHandler;
+ private final Intent mIntent;
- // read/write from handler thread
- private IBinder mBestService;
+ @Nullable private final BinderRunner mOnBind;
+ @Nullable private final Runnable mOnUnbind;
+
+ // read/write from handler thread only
private int mCurrentUserId;
- // read from any thread, write from handler thread
- private volatile ComponentName mBestComponent;
- private volatile int mBestVersion;
- private volatile int mBestUserId;
+ // write from handler thread only, read anywhere
+ private volatile ServiceInfo mServiceInfo;
- public ServiceWatcher(Context context, String logTag, String action,
- int overlaySwitchResId, int defaultServicePackageNameResId,
- int initialPackageNamesResId, Handler handler) {
- Resources resources = context.getResources();
+ // read/write from handler thread only
+ private IBinder mBinder;
+ public ServiceWatcher(Context context, Handler handler, String action,
+ @Nullable BinderRunner onBind, @Nullable Runnable onUnbind,
+ @BoolRes int enableOverlayResId, @StringRes int nonOverlayPackageResId) {
mContext = context;
- mTag = logTag;
- mAction = action;
+ mHandler = FgThread.getHandler();
+ mIntent = new Intent(Objects.requireNonNull(action));
- boolean enableOverlay = resources.getBoolean(overlaySwitchResId);
- if (enableOverlay) {
- String[] pkgs = resources.getStringArray(initialPackageNamesResId);
- mServicePackageName = null;
- mSignatureSets = getSignatureSets(context, pkgs);
- if (D) Log.d(mTag, "Overlay enabled, packages=" + Arrays.toString(pkgs));
- } else {
- mServicePackageName = resources.getString(defaultServicePackageNameResId);
- mSignatureSets = getSignatureSets(context, mServicePackageName);
- if (D) Log.d(mTag, "Overlay disabled, default package=" + mServicePackageName);
+ Resources resources = context.getResources();
+ boolean enableOverlay = resources.getBoolean(enableOverlayResId);
+ if (!enableOverlay) {
+ mIntent.setPackage(resources.getString(nonOverlayPackageResId));
}
- mHandler = handler;
+ mOnBind = onBind;
+ mOnUnbind = onUnbind;
- mBestComponent = null;
- mBestVersion = Integer.MIN_VALUE;
- mBestUserId = UserHandle.USER_NULL;
+ mCurrentUserId = UserHandle.USER_NULL;
- mBestService = null;
+ mServiceInfo = ServiceInfo.NONE;
+ mBinder = null;
}
- protected void onBind() {}
-
- protected void onUnbind() {}
-
/**
- * Start this watcher, including binding to the current best match and
- * re-binding to any better matches down the road.
- *
- * Note that if there are no matching encryption-aware services, we may not
- * bind to a real service until after the current user is unlocked.
- *
- * @return {@code true} if a potential service implementation was found.
+ * Register this class, which will start the process of determining the best matching service
+ * and maintaining a binding to it. Will return false and fail if there are no possible matching
+ * services at the time this functions is called.
*/
- public final boolean start() {
- // if we have to return false, do it before registering anything
- if (isServiceMissing()) return false;
-
- // listen for relevant package changes if service overlay is enabled on handler
- if (mServicePackageName == null) {
- new PackageMonitor() {
- @Override
- public void onPackageUpdateFinished(String packageName, int uid) {
- bindBestPackage(Objects.equals(packageName, getCurrentPackageName()));
- }
-
- @Override
- public void onPackageAdded(String packageName, int uid) {
- bindBestPackage(Objects.equals(packageName, getCurrentPackageName()));
- }
-
- @Override
- public void onPackageRemoved(String packageName, int uid) {
- bindBestPackage(Objects.equals(packageName, getCurrentPackageName()));
- }
-
- @Override
- public boolean onPackageChanged(String packageName, int uid, String[] components) {
- bindBestPackage(Objects.equals(packageName, getCurrentPackageName()));
- return super.onPackageChanged(packageName, uid, components);
- }
- }.register(mContext, UserHandle.ALL, true, mHandler);
+ public boolean register() {
+ if (mContext.getPackageManager().queryIntentServicesAsUser(mIntent,
+ MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE | MATCH_SYSTEM_ONLY,
+ UserHandle.USER_SYSTEM).isEmpty()) {
+ return false;
}
- // listen for user change on handler
+ new PackageMonitor() {
+ @Override
+ public void onPackageUpdateFinished(String packageName, int uid) {
+ ServiceWatcher.this.onPackageChanged(packageName);
+ }
+
+ @Override
+ public void onPackageAdded(String packageName, int uid) {
+ ServiceWatcher.this.onPackageChanged(packageName);
+ }
+
+ @Override
+ public void onPackageRemoved(String packageName, int uid) {
+ ServiceWatcher.this.onPackageChanged(packageName);
+ }
+
+ @Override
+ public boolean onPackageChanged(String packageName, int uid, String[] components) {
+ ServiceWatcher.this.onPackageChanged(packageName);
+ return super.onPackageChanged(packageName, uid, components);
+ }
+ }.register(mContext, UserHandle.ALL, true, mHandler);
+
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
intentFilter.addAction(Intent.ACTION_USER_UNLOCKED);
mContext.registerReceiverAsUser(new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- final String action = intent.getAction();
- final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
- UserHandle.USER_NULL);
- if (Intent.ACTION_USER_SWITCHED.equals(action)) {
- mCurrentUserId = userId;
- bindBestPackage(false);
- } else if (Intent.ACTION_USER_UNLOCKED.equals(action)) {
- if (userId == mCurrentUserId) {
- bindBestPackage(false);
- }
+ String action = intent.getAction();
+ if (action == null) {
+ return;
}
+ int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
+ if (userId == UserHandle.USER_NULL) {
+ return;
+ }
+
+ switch (action) {
+ case Intent.ACTION_USER_SWITCHED:
+ onUserSwitched(userId);
+ break;
+ case Intent.ACTION_USER_UNLOCKED:
+ onUserUnlocked(userId);
+ break;
+ default:
+ break;
+ }
+
}
}, UserHandle.ALL, intentFilter, null, mHandler);
mCurrentUserId = ActivityManager.getCurrentUser();
- mHandler.post(() -> bindBestPackage(false));
+ mHandler.post(() -> onBestServiceChanged(false));
return true;
}
- /** Returns the name of the currently connected package or null. */
- @Nullable
- public String getCurrentPackageName() {
- ComponentName bestComponent = mBestComponent;
- return bestComponent == null ? null : bestComponent.getPackageName();
+ /**
+ * Returns information on the currently selected service.
+ */
+ public ServiceInfo getBoundService() {
+ return mServiceInfo;
}
- private boolean isServiceMissing() {
- return mContext.getPackageManager().queryIntentServicesAsUser(new Intent(mAction),
- PackageManager.MATCH_DIRECT_BOOT_AWARE
- | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
- UserHandle.USER_SYSTEM).isEmpty();
- }
-
- private void bindBestPackage(boolean forceRebind) {
+ private void onBestServiceChanged(boolean forceRebind) {
Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
- Intent intent = new Intent(mAction);
- if (mServicePackageName != null) {
- intent.setPackage(mServicePackageName);
- }
-
- List rInfos = mContext.getPackageManager().queryIntentServicesAsUser(intent,
- PackageManager.GET_META_DATA | PackageManager.MATCH_DIRECT_BOOT_AUTO,
+ List resolveInfos = mContext.getPackageManager().queryIntentServicesAsUser(
+ mIntent,
+ GET_META_DATA | MATCH_DIRECT_BOOT_AUTO | MATCH_SYSTEM_ONLY,
mCurrentUserId);
- if (rInfos == null) {
- rInfos = Collections.emptyList();
- }
- ComponentName bestComponent = null;
- int bestVersion = Integer.MIN_VALUE;
- boolean bestIsMultiuser = false;
-
- for (ResolveInfo rInfo : rInfos) {
- ComponentName component = rInfo.serviceInfo.getComponentName();
- String packageName = component.getPackageName();
-
- // check signature
- try {
- PackageInfo pInfo = mContext.getPackageManager().getPackageInfo(packageName,
- PackageManager.GET_SIGNATURES
- | PackageManager.MATCH_DIRECT_BOOT_AUTO);
- if (!isSignatureMatch(pInfo.signatures, mSignatureSets)) {
- Log.w(mTag, packageName + " resolves service " + mAction
- + ", but has wrong signature, ignoring");
- continue;
- }
- } catch (NameNotFoundException e) {
- Log.wtf(mTag, e);
- continue;
- }
-
- // check metadata
- Bundle metadata = rInfo.serviceInfo.metaData;
- int version = Integer.MIN_VALUE;
- boolean isMultiuser = false;
- if (metadata != null) {
- version = metadata.getInt(EXTRA_SERVICE_VERSION, Integer.MIN_VALUE);
- isMultiuser = metadata.getBoolean(EXTRA_SERVICE_IS_MULTIUSER, false);
- }
-
- if (version > bestVersion) {
- bestComponent = component;
- bestVersion = version;
- bestIsMultiuser = isMultiuser;
+ ServiceInfo bestServiceInfo = ServiceInfo.NONE;
+ for (ResolveInfo resolveInfo : resolveInfos) {
+ ServiceInfo serviceInfo = new ServiceInfo(resolveInfo, mCurrentUserId);
+ if (serviceInfo.compareTo(bestServiceInfo) > 0) {
+ bestServiceInfo = serviceInfo;
}
}
- if (D) {
- Log.d(mTag, String.format("bindBestPackage for %s : %s found %d, %s", mAction,
- (mServicePackageName == null ? ""
- : "(" + mServicePackageName + ") "), rInfos.size(),
- (bestComponent == null ? "no new best component"
- : "new best component: " + bestComponent)));
+ if (forceRebind || !bestServiceInfo.equals(mServiceInfo)) {
+ rebind(bestServiceInfo);
+ }
+ }
+
+ private void rebind(ServiceInfo newServiceInfo) {
+ Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
+
+ if (!mServiceInfo.equals(ServiceInfo.NONE)) {
+ if (D) {
+ Log.i(TAG, "[" + mIntent.getAction() + "] unbinding from " + mServiceInfo);
+ }
+
+ mContext.unbindService(this);
+ mServiceInfo = ServiceInfo.NONE;
}
- if (bestComponent == null) {
- Slog.w(mTag, "Odd, no component found for service " + mAction);
- unbind();
+ mServiceInfo = newServiceInfo;
+ if (mServiceInfo.equals(ServiceInfo.NONE)) {
return;
}
- int userId = bestIsMultiuser ? UserHandle.USER_SYSTEM : mCurrentUserId;
- boolean alreadyBound = Objects.equals(bestComponent, mBestComponent)
- && bestVersion == mBestVersion && userId == mBestUserId;
- if (forceRebind || !alreadyBound) {
- unbind();
- bind(bestComponent, bestVersion, userId);
+ Preconditions.checkState(mServiceInfo.component != null);
+
+ if (D) {
+ Log.i(TAG, getLogPrefix() + " binding to " + mServiceInfo);
+ }
+
+ Intent bindIntent = new Intent(mIntent).setComponent(mServiceInfo.component);
+ mContext.bindServiceAsUser(bindIntent, this,
+ BIND_AUTO_CREATE | BIND_NOT_FOREGROUND | BIND_NOT_VISIBLE,
+ mHandler, UserHandle.of(mServiceInfo.userId));
+ }
+
+ @Override
+ public final void onServiceConnected(ComponentName component, IBinder binder) {
+ Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
+
+ if (D) {
+ Log.i(TAG, getLogPrefix() + " connected to " + component);
+ }
+
+ mBinder = binder;
+ if (mOnBind != null) {
+ runOnBinder(mOnBind);
}
}
- private void bind(ComponentName component, int version, int userId) {
+ @Override
+ public final void onServiceDisconnected(ComponentName component) {
Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
- Intent intent = new Intent(mAction);
- intent.setComponent(component);
-
- mBestComponent = component;
- mBestVersion = version;
- mBestUserId = userId;
-
- if (D) Log.d(mTag, "binding " + component + " (v" + version + ") (u" + userId + ")");
- mContext.bindServiceAsUser(intent, this,
- Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND | Context.BIND_NOT_VISIBLE,
- UserHandle.of(userId));
- }
-
- private void unbind() {
- Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
-
- if (mBestComponent != null) {
- if (D) Log.d(mTag, "unbinding " + mBestComponent);
- mContext.unbindService(this);
+ if (D) {
+ Log.i(TAG, getLogPrefix() + " disconnected from " + component);
}
- mBestComponent = null;
- mBestVersion = Integer.MIN_VALUE;
- mBestUserId = UserHandle.USER_NULL;
+ mBinder = null;
+ if (mOnUnbind != null) {
+ mOnUnbind.run();
+ }
+ }
+
+ private void onUserSwitched(@UserIdInt int userId) {
+ mCurrentUserId = userId;
+ onBestServiceChanged(false);
+ }
+
+ private void onUserUnlocked(@UserIdInt int userId) {
+ if (userId == mCurrentUserId) {
+ onBestServiceChanged(false);
+ }
+ }
+
+ private void onPackageChanged(String packageName) {
+ // force a rebind if the changed package was the currently connected package
+ String currentPackageName =
+ mServiceInfo.component != null ? mServiceInfo.component.getPackageName() : null;
+ onBestServiceChanged(packageName.equals(currentPackageName));
}
/**
@@ -365,26 +388,26 @@ public class ServiceWatcher implements ServiceConnection {
*/
public final void runOnBinder(BinderRunner runner) {
runOnHandler(() -> {
- if (mBestService == null) {
+ if (mBinder == null) {
return;
}
try {
- runner.run(mBestService);
- } catch (RuntimeException e) {
- // the code being run is privileged, but may be outside the system server, and thus
- // we cannot allow runtime exceptions to crash the system server
- Log.e(TAG, "exception while while running " + runner + " on " + mBestService
- + " from " + this, e);
- } catch (RemoteException e) {
- // do nothing
+ runner.run(mBinder);
+ } catch (RuntimeException | RemoteException e) {
+ // binders may propagate some specific non-RemoteExceptions from the other side
+ // through the binder as well - we cannot allow those to crash the system server
+ Log.e(TAG, getLogPrefix() + " exception running on " + mServiceInfo, e);
}
});
}
/**
* Runs the given function synchronously if currently connected, and returns the default value
- * if not currently connected or if any exception is thrown.
+ * if not currently connected or if any exception is thrown. Do not obtain any locks within the
+ * BlockingBinderRunner, or risk deadlock. The default value will be returned if there is no
+ * service connection when this is run, if a RemoteException occurs, or if the operation times
+ * out.
*
* @deprecated Using this function is an indication that your AIDL API is broken. Calls from
* system server to outside MUST be one-way, and so cannot return any result, and this
@@ -395,13 +418,16 @@ public class ServiceWatcher implements ServiceConnection {
public final T runOnBinderBlocking(BlockingBinderRunner runner, T defaultValue) {
try {
return runOnHandlerBlocking(() -> {
- if (mBestService == null) {
+ if (mBinder == null) {
return defaultValue;
}
try {
- return runner.run(mBestService);
- } catch (RemoteException e) {
+ return runner.run(mBinder);
+ } catch (RuntimeException | RemoteException e) {
+ // binders may propagate some specific non-RemoteExceptions from the other side
+ // through the binder as well - we cannot allow those to crash the system server
+ Log.e(TAG, getLogPrefix() + " exception running on " + mServiceInfo, e);
return defaultValue;
}
});
@@ -410,30 +436,6 @@ public class ServiceWatcher implements ServiceConnection {
}
}
- @Override
- public final void onServiceConnected(ComponentName component, IBinder binder) {
- runOnHandler(() -> {
- if (D) Log.d(mTag, component + " connected");
- mBestService = binder;
- onBind();
- });
- }
-
- @Override
- public final void onServiceDisconnected(ComponentName component) {
- runOnHandler(() -> {
- if (D) Log.d(mTag, component + " disconnected");
- mBestService = null;
- onUnbind();
- });
- }
-
- @Override
- public String toString() {
- ComponentName bestComponent = mBestComponent;
- return bestComponent == null ? "null" : bestComponent.toShortString() + "@" + mBestVersion;
- }
-
private void runOnHandler(Runnable r) {
if (Looper.myLooper() == mHandler.getLooper()) {
r.run();
@@ -467,4 +469,8 @@ public class ServiceWatcher implements ServiceConnection {
}
}
}
+
+ private String getLogPrefix() {
+ return "[" + mIntent.getAction() + "]";
+ }
}
diff --git a/services/core/java/com/android/server/location/ActivityRecognitionProxy.java b/services/core/java/com/android/server/location/ActivityRecognitionProxy.java
deleted file mode 100644
index 80ab7903cef72..0000000000000
--- a/services/core/java/com/android/server/location/ActivityRecognitionProxy.java
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.server.location;
-
-import android.content.Context;
-import android.hardware.location.ActivityRecognitionHardware;
-import android.hardware.location.IActivityRecognitionHardwareClient;
-import android.hardware.location.IActivityRecognitionHardwareWatcher;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.util.Log;
-
-import com.android.server.FgThread;
-import com.android.server.ServiceWatcher;
-
-/**
- * Proxy class to bind GmsCore to the ActivityRecognitionHardware.
- *
- * @hide
- */
-public class ActivityRecognitionProxy {
-
- private static final String TAG = "ActivityRecognitionProxy";
-
- /**
- * Creates an instance of the proxy and binds it to the appropriate FusedProvider.
- *
- * @return An instance of the proxy if it could be bound, null otherwise.
- */
- public static ActivityRecognitionProxy createAndBind(
- Context context,
- boolean activityRecognitionHardwareIsSupported,
- ActivityRecognitionHardware activityRecognitionHardware,
- int overlaySwitchResId,
- int defaultServicePackageNameResId,
- int initialPackageNameResId) {
- ActivityRecognitionProxy activityRecognitionProxy = new ActivityRecognitionProxy(
- context,
- activityRecognitionHardwareIsSupported,
- activityRecognitionHardware,
- overlaySwitchResId,
- defaultServicePackageNameResId,
- initialPackageNameResId);
-
- if (activityRecognitionProxy.mServiceWatcher.start()) {
- return activityRecognitionProxy;
- } else {
- return null;
- }
- }
-
- private final ServiceWatcher mServiceWatcher;
- private final boolean mIsSupported;
- private final ActivityRecognitionHardware mInstance;
-
- private ActivityRecognitionProxy(
- Context context,
- boolean activityRecognitionHardwareIsSupported,
- ActivityRecognitionHardware activityRecognitionHardware,
- int overlaySwitchResId,
- int defaultServicePackageNameResId,
- int initialPackageNameResId) {
- mIsSupported = activityRecognitionHardwareIsSupported;
- mInstance = activityRecognitionHardware;
-
- mServiceWatcher = new ServiceWatcher(
- context,
- TAG,
- "com.android.location.service.ActivityRecognitionProvider",
- overlaySwitchResId,
- defaultServicePackageNameResId,
- initialPackageNameResId,
- FgThread.getHandler()) {
- @Override
- protected void onBind() {
- runOnBinder(ActivityRecognitionProxy.this::initializeService);
- }
- };
- }
-
- private void initializeService(IBinder binder) {
- try {
- String descriptor = binder.getInterfaceDescriptor();
-
- if (IActivityRecognitionHardwareWatcher.class.getCanonicalName().equals(
- descriptor)) {
- IActivityRecognitionHardwareWatcher watcher =
- IActivityRecognitionHardwareWatcher.Stub.asInterface(binder);
- if (mInstance != null) {
- watcher.onInstanceChanged(mInstance);
- }
- } else if (IActivityRecognitionHardwareClient.class.getCanonicalName()
- .equals(descriptor)) {
- IActivityRecognitionHardwareClient client =
- IActivityRecognitionHardwareClient.Stub.asInterface(binder);
- client.onAvailabilityChanged(mIsSupported, mInstance);
- } else {
- Log.e(TAG, "Invalid descriptor found on connection: " + descriptor);
- }
- } catch (RemoteException e) {
- Log.w(TAG, e);
- }
- }
-}
diff --git a/services/core/java/com/android/server/location/GeocoderProxy.java b/services/core/java/com/android/server/location/GeocoderProxy.java
index e6f0ed9d14b06..536f95a40431b 100644
--- a/services/core/java/com/android/server/location/GeocoderProxy.java
+++ b/services/core/java/com/android/server/location/GeocoderProxy.java
@@ -16,6 +16,7 @@
package com.android.server.location;
+import android.annotation.Nullable;
import android.content.Context;
import android.location.Address;
import android.location.GeocoderParams;
@@ -28,40 +29,38 @@ import java.util.List;
/**
* Proxy for IGeocodeProvider implementations.
+ *
+ * @hide
*/
public class GeocoderProxy {
- private static final String TAG = "GeocoderProxy";
private static final String SERVICE_ACTION = "com.android.location.service.GeocodeProvider";
- private final ServiceWatcher mServiceWatcher;
-
- public static GeocoderProxy createAndBind(Context context,
- int overlaySwitchResId, int defaultServicePackageNameResId,
- int initialPackageNamesResId) {
- GeocoderProxy proxy = new GeocoderProxy(context, overlaySwitchResId,
- defaultServicePackageNameResId, initialPackageNamesResId);
- if (proxy.bind()) {
+ /**
+ * Creates and registers this proxy. If no suitable service is available for the proxy, returns
+ * null.
+ */
+ @Nullable
+ public static GeocoderProxy createAndRegister(Context context) {
+ GeocoderProxy proxy = new GeocoderProxy(context);
+ if (proxy.register()) {
return proxy;
} else {
return null;
}
}
- private GeocoderProxy(Context context,
- int overlaySwitchResId, int defaultServicePackageNameResId,
- int initialPackageNamesResId) {
- mServiceWatcher = new ServiceWatcher(context, TAG, SERVICE_ACTION, overlaySwitchResId,
- defaultServicePackageNameResId, initialPackageNamesResId,
- BackgroundThread.getHandler());
+ private final ServiceWatcher mServiceWatcher;
+
+ private GeocoderProxy(Context context) {
+ mServiceWatcher = new ServiceWatcher(context, BackgroundThread.getHandler(), SERVICE_ACTION,
+ null, null,
+ com.android.internal.R.bool.config_enableGeocoderOverlay,
+ com.android.internal.R.string.config_geocoderProviderPackageName);
}
- private boolean bind() {
- return mServiceWatcher.start();
- }
-
- public String getConnectedPackageName() {
- return mServiceWatcher.getCurrentPackageName();
+ private boolean register() {
+ return mServiceWatcher.register();
}
public String getFromLocation(double latitude, double longitude, int maxResults,
@@ -83,5 +82,4 @@ public class GeocoderProxy {
maxResults, params, addrs);
}, "Service not Available");
}
-
}
diff --git a/services/core/java/com/android/server/location/GeofenceProxy.java b/services/core/java/com/android/server/location/GeofenceProxy.java
index ce93661a8810c..f006fb177382c 100644
--- a/services/core/java/com/android/server/location/GeofenceProxy.java
+++ b/services/core/java/com/android/server/location/GeofenceProxy.java
@@ -22,7 +22,6 @@ import android.content.Intent;
import android.content.ServiceConnection;
import android.hardware.location.GeofenceHardwareService;
import android.hardware.location.IGeofenceHardware;
-import android.location.IFusedGeofenceHardware;
import android.location.IGeofenceProvider;
import android.location.IGpsGeofenceHardware;
import android.os.IBinder;
@@ -33,6 +32,8 @@ import android.util.Log;
import com.android.server.FgThread;
import com.android.server.ServiceWatcher;
+import java.util.Objects;
+
/**
* @hide
*/
@@ -41,64 +42,41 @@ public final class GeofenceProxy {
private static final String TAG = "GeofenceProxy";
private static final String SERVICE_ACTION = "com.android.location.service.GeofenceProvider";
- private final Context mContext;
- private final ServiceWatcher mServiceWatcher;
-
@Nullable
- private final IGpsGeofenceHardware mGpsGeofenceHardware;
- @Nullable
- private final IFusedGeofenceHardware mFusedGeofenceHardware;
-
- private volatile IGeofenceHardware mGeofenceHardware;
-
- private final ServiceWatcher.BinderRunner mUpdateGeofenceHardware = (binder) -> {
- IGeofenceProvider provider = IGeofenceProvider.Stub.asInterface(binder);
- try {
- provider.setGeofenceHardware(mGeofenceHardware);
- } catch (RemoteException e) {
- Log.w(TAG, e);
- }
- };
-
- public static GeofenceProxy createAndBind(Context context,
- int overlaySwitchResId, int defaultServicePackageNameResId,
- int initialPackageNamesResId, @Nullable IGpsGeofenceHardware gpsGeofence,
- @Nullable IFusedGeofenceHardware fusedGeofenceHardware) {
- GeofenceProxy proxy = new GeofenceProxy(context, overlaySwitchResId,
- defaultServicePackageNameResId, initialPackageNamesResId, gpsGeofence,
- fusedGeofenceHardware);
-
- if (proxy.bind()) {
+ public static GeofenceProxy createAndBind(Context context, IGpsGeofenceHardware gpsGeofence) {
+ GeofenceProxy proxy = new GeofenceProxy(context, gpsGeofence);
+ if (proxy.register(context)) {
return proxy;
} else {
return null;
}
}
- private GeofenceProxy(Context context,
- int overlaySwitchResId, int defaultServicePackageNameResId,
- int initialPackageNamesResId, @Nullable IGpsGeofenceHardware gpsGeofence,
- @Nullable IFusedGeofenceHardware fusedGeofenceHardware) {
- mContext = context;
- mServiceWatcher = new ServiceWatcher(context, TAG, SERVICE_ACTION, overlaySwitchResId,
- defaultServicePackageNameResId, initialPackageNamesResId,
- FgThread.getHandler()) {
- @Override
- protected void onBind() {
- runOnBinder(mUpdateGeofenceHardware);
- }
- };
+ private final IGpsGeofenceHardware mGpsGeofenceHardware;
+ private final ServiceWatcher mServiceWatcher;
- mGpsGeofenceHardware = gpsGeofence;
- mFusedGeofenceHardware = fusedGeofenceHardware;
+ private volatile IGeofenceHardware mGeofenceHardware;
+
+ private GeofenceProxy(Context context, IGpsGeofenceHardware gpsGeofence) {
+ mGpsGeofenceHardware = Objects.requireNonNull(gpsGeofence);
+ mServiceWatcher = new ServiceWatcher(context, FgThread.getHandler(), SERVICE_ACTION,
+ this::updateGeofenceHardware, null,
+ com.android.internal.R.bool.config_enableGeofenceOverlay,
+ com.android.internal.R.string.config_geofenceProviderPackageName);
mGeofenceHardware = null;
}
- private boolean bind() {
- if (mServiceWatcher.start()) {
- mContext.bindServiceAsUser(new Intent(mContext, GeofenceHardwareService.class),
- new GeofenceProxyServiceConnection(), Context.BIND_AUTO_CREATE,
+ private void updateGeofenceHardware(IBinder binder) throws RemoteException {
+ IGeofenceProvider.Stub.asInterface(binder).setGeofenceHardware(mGeofenceHardware);
+ }
+
+ private boolean register(Context context) {
+ if (mServiceWatcher.register()) {
+ context.bindServiceAsUser(
+ new Intent(context, GeofenceHardwareService.class),
+ new GeofenceProxyServiceConnection(),
+ Context.BIND_AUTO_CREATE,
UserHandle.SYSTEM);
return true;
}
@@ -113,24 +91,18 @@ public final class GeofenceProxy {
IGeofenceHardware geofenceHardware = IGeofenceHardware.Stub.asInterface(service);
try {
- if (mGpsGeofenceHardware != null) {
- geofenceHardware.setGpsGeofenceHardware(mGpsGeofenceHardware);
- }
- if (mFusedGeofenceHardware != null) {
- geofenceHardware.setFusedGeofenceHardware(mFusedGeofenceHardware);
- }
-
+ geofenceHardware.setGpsGeofenceHardware(mGpsGeofenceHardware);
mGeofenceHardware = geofenceHardware;
- mServiceWatcher.runOnBinder(mUpdateGeofenceHardware);
- } catch (Exception e) {
- Log.w(TAG, e);
+ mServiceWatcher.runOnBinder(GeofenceProxy.this::updateGeofenceHardware);
+ } catch (RemoteException e) {
+ Log.w(TAG, "unable to initialize geofence hardware", e);
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
mGeofenceHardware = null;
- mServiceWatcher.runOnBinder(mUpdateGeofenceHardware);
+ mServiceWatcher.runOnBinder(GeofenceProxy.this::updateGeofenceHardware);
}
}
}
diff --git a/services/core/java/com/android/server/location/HardwareActivityRecognitionProxy.java b/services/core/java/com/android/server/location/HardwareActivityRecognitionProxy.java
new file mode 100644
index 0000000000000..9d9852ba5da3a
--- /dev/null
+++ b/services/core/java/com/android/server/location/HardwareActivityRecognitionProxy.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.hardware.location.ActivityRecognitionHardware;
+import android.hardware.location.IActivityRecognitionHardwareClient;
+import android.hardware.location.IActivityRecognitionHardwareWatcher;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.server.FgThread;
+import com.android.server.ServiceWatcher;
+
+/**
+ * Proxy class to bind GmsCore to the ActivityRecognitionHardware.
+ *
+ * @hide
+ */
+public class HardwareActivityRecognitionProxy {
+
+ private static final String TAG = "ARProxy";
+ private static final String SERVICE_ACTION =
+ "com.android.location.service.ActivityRecognitionProvider";
+
+ /**
+ * Creates and registers this proxy. If no suitable service is available for the proxy, returns
+ * null.
+ */
+ @Nullable
+ public static HardwareActivityRecognitionProxy createAndRegister(Context context) {
+ HardwareActivityRecognitionProxy arProxy = new HardwareActivityRecognitionProxy(context);
+ if (arProxy.register()) {
+ return arProxy;
+ } else {
+ return null;
+ }
+ }
+
+ private final boolean mIsSupported;
+ private final ActivityRecognitionHardware mInstance;
+
+ private final ServiceWatcher mServiceWatcher;
+
+ private HardwareActivityRecognitionProxy(Context context) {
+ mIsSupported = ActivityRecognitionHardware.isSupported();
+ if (mIsSupported) {
+ mInstance = ActivityRecognitionHardware.getInstance(context);
+ } else {
+ mInstance = null;
+ }
+
+ mServiceWatcher = new ServiceWatcher(context,
+ FgThread.getHandler(),
+ SERVICE_ACTION,
+ this::onBind,
+ null,
+ com.android.internal.R.bool.config_enableActivityRecognitionHardwareOverlay,
+ com.android.internal.R.string.config_activityRecognitionHardwarePackageName);
+ }
+
+ private boolean register() {
+ return mServiceWatcher.register();
+ }
+
+ private void onBind(IBinder binder) throws RemoteException {
+ String descriptor = binder.getInterfaceDescriptor();
+
+ if (IActivityRecognitionHardwareWatcher.class.getCanonicalName().equals(descriptor)) {
+ IActivityRecognitionHardwareWatcher watcher =
+ IActivityRecognitionHardwareWatcher.Stub.asInterface(binder);
+ if (mInstance != null) {
+ watcher.onInstanceChanged(mInstance);
+ }
+ } else if (IActivityRecognitionHardwareClient.class.getCanonicalName().equals(descriptor)) {
+ IActivityRecognitionHardwareClient client =
+ IActivityRecognitionHardwareClient.Stub.asInterface(binder);
+ client.onAvailabilityChanged(mIsSupported, mInstance);
+ } else {
+ Log.e(TAG, "Unknown descriptor: " + descriptor);
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/location/LocationProviderProxy.java b/services/core/java/com/android/server/location/LocationProviderProxy.java
index 8a149afa62383..805b018a8f45f 100644
--- a/services/core/java/com/android/server/location/LocationProviderProxy.java
+++ b/services/core/java/com/android/server/location/LocationProviderProxy.java
@@ -19,12 +19,11 @@ package com.android.server.location;
import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
import android.annotation.Nullable;
+import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
import android.location.Location;
import android.os.Bundle;
-import android.os.Handler;
-import android.os.HandlerExecutor;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.ArraySet;
@@ -35,7 +34,6 @@ import com.android.internal.location.ILocationProviderManager;
import com.android.internal.location.ProviderProperties;
import com.android.internal.location.ProviderRequest;
import com.android.server.FgThread;
-import com.android.server.LocationManagerService;
import com.android.server.ServiceWatcher;
import java.io.FileDescriptor;
@@ -49,17 +47,31 @@ import java.util.List;
public class LocationProviderProxy extends AbstractLocationProvider {
private static final String TAG = "LocationProviderProxy";
- private static final boolean D = LocationManagerService.D;
private static final int MAX_ADDITIONAL_PACKAGES = 2;
+ /**
+ * Creates and registers this proxy. If no suitable service is available for the proxy, returns
+ * null.
+ */
+ @Nullable
+ public static LocationProviderProxy createAndRegister(Context context, String action,
+ int enableOverlayResId, int nonOverlayPackageResId) {
+ LocationProviderProxy proxy = new LocationProviderProxy(context, action, enableOverlayResId,
+ nonOverlayPackageResId);
+ if (proxy.register()) {
+ return proxy;
+ } else {
+ return null;
+ }
+ }
+
private final ILocationProviderManager.Stub mManager = new ILocationProviderManager.Stub() {
// executed on binder thread
@Override
public void onSetAdditionalProviderPackages(List packageNames) {
- int maxCount = Math.min(MAX_ADDITIONAL_PACKAGES, packageNames.size()) + 1;
+ int maxCount = Math.min(MAX_ADDITIONAL_PACKAGES, packageNames.size());
ArraySet allPackages = new ArraySet<>(maxCount);
- allPackages.add(mServiceWatcher.getCurrentPackageName());
for (String packageName : packageNames) {
if (packageNames.size() >= maxCount) {
return;
@@ -74,6 +86,12 @@ public class LocationProviderProxy extends AbstractLocationProvider {
}
}
+ // add the binder package
+ ComponentName service = mServiceWatcher.getBoundService().component;
+ if (service != null) {
+ allPackages.add(service.getPackageName());
+ }
+
setPackageNames(allPackages);
}
@@ -100,63 +118,39 @@ public class LocationProviderProxy extends AbstractLocationProvider {
@Nullable private ProviderRequest mRequest;
- /**
- * Creates a new LocationProviderProxy and immediately begins binding to the best applicable
- * service.
- */
- @Nullable
- public static LocationProviderProxy createAndBind(Context context, String action,
- int overlaySwitchResId, int defaultServicePackageNameResId,
- int initialPackageNamesResId) {
- LocationProviderProxy proxy = new LocationProviderProxy(context, FgThread.getHandler(),
- action, overlaySwitchResId, defaultServicePackageNameResId,
- initialPackageNamesResId);
- if (proxy.bind()) {
- return proxy;
- } else {
- return null;
- }
- }
+ private LocationProviderProxy(Context context, String action, int enableOverlayResId,
+ int nonOverlayPackageResId) {
+ super(context, FgThread.getExecutor());
- private LocationProviderProxy(Context context, Handler handler, String action,
- int overlaySwitchResId, int defaultServicePackageNameResId,
- int initialPackageNamesResId) {
- super(context, new HandlerExecutor(handler), Collections.emptySet());
-
- mServiceWatcher = new ServiceWatcher(context, TAG, action, overlaySwitchResId,
- defaultServicePackageNameResId, initialPackageNamesResId, handler) {
-
- @Override
- protected void onBind() {
- runOnBinder(LocationProviderProxy.this::initializeService);
- }
-
- @Override
- protected void onUnbind() {
- setState(State.EMPTY_STATE);
- }
- };
+ mServiceWatcher = new ServiceWatcher(context, FgThread.getHandler(), action, this::onBind,
+ this::onUnbind, enableOverlayResId, nonOverlayPackageResId);
mRequest = null;
}
- private boolean bind() {
- return mServiceWatcher.start();
+ private boolean register() {
+ return mServiceWatcher.register();
}
- private void initializeService(IBinder binder) throws RemoteException {
- ILocationProvider service = ILocationProvider.Stub.asInterface(binder);
- if (D) Log.d(TAG, "applying state to connected service " + mServiceWatcher);
+ private void onBind(IBinder binder) throws RemoteException {
+ ILocationProvider provider = ILocationProvider.Stub.asInterface(binder);
- setPackageNames(Collections.singleton(mServiceWatcher.getCurrentPackageName()));
+ ComponentName service = mServiceWatcher.getBoundService().component;
+ if (service != null) {
+ setPackageNames(Collections.singleton(service.getPackageName()));
+ }
- service.setLocationProviderManager(mManager);
+ provider.setLocationProviderManager(mManager);
if (mRequest != null) {
- service.setRequest(mRequest, mRequest.workSource);
+ provider.setRequest(mRequest, mRequest.workSource);
}
}
+ private void onUnbind() {
+ setState(State.EMPTY_STATE);
+ }
+
@Override
public void onSetRequest(ProviderRequest request) {
mServiceWatcher.runOnBinder(binder -> {