Merge changes I9f2d4e1b,Iad9ada1f,Ic8d269b4
* changes:
Implements PendingIntent based createClient API
Adds bindClient method to service AIDL
Close unregistered clients with no PendingIntent
diff --git a/core/java/android/hardware/location/ContextHubManager.java b/core/java/android/hardware/location/ContextHubManager.java
index b0b77f3..9acefa5 100644
--- a/core/java/android/hardware/location/ContextHubManager.java
+++ b/core/java/android/hardware/location/ContextHubManager.java
@@ -808,7 +808,7 @@
*
* @throws IllegalArgumentException if hubInfo does not represent a valid hub, or pendingIntent
* was not associated with a client
- * @throws IllegalStateException if there were too many registered clients at the service
+ * @throws IllegalStateException if the client is already registered to a valid callback
* @throws NullPointerException if pendingIntent, hubInfo, callback, or executor is null
*
* @hide
@@ -818,8 +818,24 @@
@NonNull PendingIntent pendingIntent, @NonNull ContextHubInfo hubInfo,
@NonNull ContextHubClientCallback callback,
@NonNull @CallbackExecutor Executor executor) {
- // TODO: Implement this
- throw new UnsupportedOperationException("Not implemented yet");
+ Preconditions.checkNotNull(pendingIntent, "PendingIntent cannot be null");
+ Preconditions.checkNotNull(callback, "Callback cannot be null");
+ Preconditions.checkNotNull(hubInfo, "ContextHubInfo cannot be null");
+ Preconditions.checkNotNull(executor, "Executor cannot be null");
+
+ ContextHubClient client = new ContextHubClient(hubInfo);
+ IContextHubClientCallback clientInterface = createClientCallback(
+ client, callback, executor);
+
+ IContextHubClient clientProxy;
+ try {
+ clientProxy = mService.bindClient(pendingIntent, clientInterface, hubInfo.getId());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+
+ client.setClientProxy(clientProxy);
+ return client;
}
/**
@@ -833,7 +849,7 @@
*
* @throws IllegalArgumentException if hubInfo does not represent a valid hub, or pendingIntent
* was not associated with a client
- * @throws IllegalStateException if there were too many registered clients at the service
+ * @throws IllegalStateException if the client is already registered to a valid callback
* @throws NullPointerException if pendingIntent, hubInfo, or callback is null
*
* @hide
diff --git a/core/java/android/hardware/location/IContextHubService.aidl b/core/java/android/hardware/location/IContextHubService.aidl
index 233e857..9b0acdf 100644
--- a/core/java/android/hardware/location/IContextHubService.aidl
+++ b/core/java/android/hardware/location/IContextHubService.aidl
@@ -17,6 +17,7 @@
package android.hardware.location;
// Declare any non-default types here with import statements
+import android.app.PendingIntent;
import android.hardware.location.ContextHubInfo;
import android.hardware.location.ContextHubMessage;
import android.hardware.location.NanoApp;
@@ -60,6 +61,11 @@
// Creates a client to send and receive messages
IContextHubClient createClient(in IContextHubClientCallback client, int contextHubId);
+ // Binds an existing client to a new callback interface, provided a previously registered
+ // PendingIntent
+ IContextHubClient bindClient(
+ in PendingIntent pendingIntent, in IContextHubClientCallback client, int contextHubId);
+
// Returns a list of ContextHub objects of available hubs
List<ContextHubInfo> getContextHubs();
diff --git a/services/core/java/com/android/server/location/ContextHubClientBroker.java b/services/core/java/com/android/server/location/ContextHubClientBroker.java
index 60f70c7..6423470 100644
--- a/services/core/java/com/android/server/location/ContextHubClientBroker.java
+++ b/services/core/java/com/android/server/location/ContextHubClientBroker.java
@@ -159,25 +159,12 @@
/* package */ ContextHubClientBroker(
Context context, IContexthub contextHubProxy, ContextHubClientManager clientManager,
- ContextHubInfo contextHubInfo, short hostEndPointId,
- IContextHubClientCallback callback) {
+ ContextHubInfo contextHubInfo, short hostEndPointId) {
mContext = context;
mContextHubProxy = contextHubProxy;
mClientManager = clientManager;
mAttachedContextHubInfo = contextHubInfo;
mHostEndPointId = hostEndPointId;
- mCallbackInterface = callback;
- }
-
- /**
- * Attaches a death recipient for this client
- *
- * @throws RemoteException if the client has already died
- */
- /* package */ synchronized void attachDeathRecipient() throws RemoteException {
- if (mCallbackInterface != null) {
- mCallbackInterface.asBinder().linkToDeath(this, 0 /* flags */);
- }
}
/**
@@ -245,9 +232,15 @@
public boolean unregisterIntent(PendingIntent pendingIntent) {
ContextHubServiceUtil.checkPermissions(mContext);
+ boolean success = false;
synchronized (this) {
- return mPendingIntentRequest.unregister(pendingIntent);
+ success = mPendingIntentRequest.unregister(pendingIntent);
+ if (mCallbackInterface == null) {
+ close();
+ }
}
+
+ return success;
}
/**
@@ -276,6 +269,37 @@
}
/**
+ * Sets the callback interface for this client, only if the callback is currently unregistered.
+ *
+ * Also attaches a death recipient to a ContextHubClientBroker object. If unsuccessful, the
+ * connection is closed.
+ *
+ * @param callback the callback interface
+ * @return true if the callback was successfully set, false otherwise
+ *
+ * @throws IllegalStateException if the client has already been registered to a callback
+ */
+ /* package */
+ synchronized boolean setCallback(IContextHubClientCallback callback) {
+ boolean success = false;
+ if (mCallbackInterface != null) {
+ throw new IllegalStateException("Client is already registered with a callback");
+ } else {
+ mCallbackInterface = callback;
+ try {
+ mCallbackInterface.asBinder().linkToDeath(this, 0 /* flags */);
+ success = true;
+ } catch (RemoteException e) {
+ // The client process has died, so we close the connection.
+ Log.e(TAG, "Failed to attach death recipient to client");
+ close();
+ }
+ }
+
+ return success;
+ }
+
+ /**
* @return the ID of the context hub this client is attached to
*/
/* package */ int getAttachedContextHubId() {
@@ -347,6 +371,18 @@
}
/**
+ * @param intent the PendingIntent to compare to
+ * @return true if the given PendingIntent is currently registered, false otherwise
+ */
+ /* package */ boolean hasPendingIntent(PendingIntent intent) {
+ PendingIntent pendingIntent = null;
+ synchronized (this) {
+ pendingIntent = mPendingIntentRequest.getPendingIntent();
+ }
+ return (pendingIntent != null) && pendingIntent.equals(intent);
+ }
+
+ /**
* Helper function to invoke a specified client callback, if the connection is open.
*
* @param consumer the consumer specifying the callback to invoke
@@ -407,6 +443,9 @@
Log.w(TAG, "PendingIntent has been canceled, unregistering from client"
+ " (host endpoint ID " + mHostEndPointId + ")");
mPendingIntentRequest.clear();
+ if (mCallbackInterface == null) {
+ close();
+ }
}
}
}
diff --git a/services/core/java/com/android/server/location/ContextHubClientManager.java b/services/core/java/com/android/server/location/ContextHubClientManager.java
index eda8c6f..72879dd 100644
--- a/services/core/java/com/android/server/location/ContextHubClientManager.java
+++ b/services/core/java/com/android/server/location/ContextHubClientManager.java
@@ -16,6 +16,7 @@
package com.android.server.location;
+import android.app.PendingIntent;
import android.content.Context;
import android.hardware.contexthub.V1_0.ContextHubMsg;
import android.hardware.contexthub.V1_0.IContexthub;
@@ -88,15 +89,9 @@
*/
/* package */ IContextHubClient registerClient(
IContextHubClientCallback clientCallback, ContextHubInfo contextHubInfo) {
- ContextHubClientBroker broker = createNewClientBroker(clientCallback, contextHubInfo);
-
- try {
- broker.attachDeathRecipient();
- } catch (RemoteException e) {
- // The client process has died, so we close the connection and return null.
- Log.e(TAG, "Failed to attach death recipient to client");
- broker.close();
- return null;
+ ContextHubClientBroker broker = createNewClientBroker(contextHubInfo);
+ if (!broker.setCallback(clientCallback)) {
+ return null; // Client process has died, so we return null
}
Log.d(TAG, "Registered client with host endpoint ID " + broker.getHostEndPointId());
@@ -104,6 +99,36 @@
}
/**
+ * Binds a existing and registered client with a new callback interface, provided a previously
+ * registered PendingIntent.
+ *
+ * @param pendingIntent a previously registered PendingIntent for a registered client
+ * @param clientCallback the callback interface of the client to bind to
+ * @param contextHubId the ID of the hub this client is attached to
+ *
+ * @return the client interface
+ *
+ * @throws IllegalArgumentException if no matching client is found
+ * @throws IllegalStateException if the client has already been registered to a callback
+ */
+ /* package */ IContextHubClient bindClient(
+ PendingIntent pendingIntent, IContextHubClientCallback clientCallback,
+ int contextHubId) {
+ ContextHubClientBroker broker = getClientBroker(pendingIntent, contextHubId);
+ if (broker == null) {
+ throw new IllegalArgumentException("Could not find client of Context Hub (ID = "
+ + contextHubId + ") with PendingIntent");
+ }
+
+ if (!broker.setCallback(clientCallback)) {
+ return null; // Client process has died, so we return null
+ }
+
+ Log.d(TAG, "Re-registered client with host endpoint ID " + broker.getHostEndPointId());
+ return IContextHubClient.Stub.asInterface(broker);
+ }
+
+ /**
* Handles a message sent from a nanoapp.
*
* @param contextHubId the ID of the hub where the nanoapp sent the message from
@@ -182,7 +207,6 @@
* Creates a new ContextHubClientBroker object for a client and registers it with the client
* manager.
*
- * @param clientCallback the callback interface of the client to register
* @param contextHubInfo the object describing the hub this client is attached to
*
* @return the ContextHubClientBroker object
@@ -190,7 +214,7 @@
* @throws IllegalStateException if max number of clients have already registered
*/
private synchronized ContextHubClientBroker createNewClientBroker(
- IContextHubClientCallback clientCallback, ContextHubInfo contextHubInfo) {
+ ContextHubInfo contextHubInfo) {
if (mHostEndPointIdToClientMap.size() == MAX_CLIENT_ID + 1) {
throw new IllegalStateException("Could not register client - max limit exceeded");
}
@@ -200,8 +224,7 @@
for (int i = 0; i <= MAX_CLIENT_ID; i++) {
if (!mHostEndPointIdToClientMap.containsKey((short) id)) {
broker = new ContextHubClientBroker(
- mContext, mContextHubProxy, this, contextHubInfo, (short) id,
- clientCallback);
+ mContext, mContextHubProxy, this, contextHubInfo, (short) id);
mHostEndPointIdToClientMap.put((short) id, broker);
mNextHostEndpointId = (id == MAX_CLIENT_ID) ? 0 : id + 1;
break;
@@ -236,4 +259,22 @@
}
}
}
+
+ /**
+ * Retrieves a ContextHubClientBroker object with a matching PendingIntent and Context Hub ID.
+ *
+ * @param pendingIntent the PendingIntent to match
+ * @param contextHubId the ID of the Context Hub the client is attached to
+ * @return the matching ContextHubClientBroker, null if not found
+ */
+ private ContextHubClientBroker getClientBroker(PendingIntent pendingIntent, int contextHubId) {
+ for (ContextHubClientBroker broker : mHostEndPointIdToClientMap.values()) {
+ if (broker.hasPendingIntent(pendingIntent)
+ && broker.getAttachedContextHubId() == contextHubId) {
+ return broker;
+ }
+ }
+
+ return null;
+ }
}
diff --git a/services/core/java/com/android/server/location/ContextHubService.java b/services/core/java/com/android/server/location/ContextHubService.java
index e3c2863..215e67c 100644
--- a/services/core/java/com/android/server/location/ContextHubService.java
+++ b/services/core/java/com/android/server/location/ContextHubService.java
@@ -16,6 +16,7 @@
package com.android.server.location;
+import android.app.PendingIntent;
import android.content.Context;
import android.hardware.contexthub.V1_0.AsyncEventType;
import android.hardware.contexthub.V1_0.ContextHub;
@@ -631,6 +632,37 @@
}
/**
+ * Recreates and binds a IContextHubClientCallback interface to an existing and registered
+ * client at the service for the specified Context Hub, provided a previously registered
+ * PendingIntent.
+ *
+ * @param pendingIntent the PendingIntent previously registered for the client
+ * @param clientCallback the client interface to register with the service
+ * @param contextHubId the ID of the hub this client is attached to
+ * @return the generated client interface, null if registration was unsuccessful
+ *
+ * @throws IllegalArgumentException if contextHubId is not a valid ID
+ * @throws NullPointerException if clientCallback or pendingIntent is null
+ */
+ @Override
+ public IContextHubClient bindClient(
+ PendingIntent pendingIntent, IContextHubClientCallback clientCallback,
+ int contextHubId) throws RemoteException {
+ checkPermissions();
+ if (!isValidContextHubId(contextHubId)) {
+ throw new IllegalArgumentException("Invalid context hub ID " + contextHubId);
+ }
+ if (pendingIntent == null) {
+ throw new NullPointerException("Cannot create client with null pending intent");
+ }
+ if (clientCallback == null) {
+ throw new NullPointerException("Cannot create client with null callback");
+ }
+
+ return mClientManager.bindClient(pendingIntent, clientCallback, contextHubId);
+ }
+
+ /**
* Loads a nanoapp binary at the specified Context hub.
*
* @param contextHubId the ID of the hub to load the binary