From 4e39aa1e1956ef0b363f38630865acd3ef65761f Mon Sep 17 00:00:00 2001 From: Arthur Ishiguro Date: Tue, 14 Nov 2017 14:59:08 -0800 Subject: [PATCH] Creates framework for new API messaging - Creates IContextHubClientCallback as a proxy to invoke messaging/life-cycle callbacks for the client. - Creates IContextHubClient as a proxy to send messages at the client side. - Creates a ContextHubClientManager at the service to manage clients and messaging/lifecycle event callbacks. - Implements generation of clients at ContextHubManager and sending messages through ContextHubClient Bug: 67734082 Test: make from root Change-Id: I3a7e69f89cd70ea59160a651dcdb729e18027e9f --- Android.mk | 2 + .../hardware/location/ContextHubClient.java | 32 ++-- .../hardware/location/ContextHubManager.java | 78 +++++++- .../hardware/location/IContextHubClient.aidl | 28 +++ .../location/IContextHubClientCallback.aidl | 49 +++++ .../hardware/location/IContextHubService.aidl | 9 +- .../location/ContextHubClientBroker.java | 124 +++++++++++++ .../location/ContextHubClientManager.java | 170 ++++++++++++++++++ .../server/location/ContextHubService.java | 75 ++++++-- .../location/ContextHubServiceUtil.java | 43 +++++ 10 files changed, 570 insertions(+), 40 deletions(-) create mode 100644 core/java/android/hardware/location/IContextHubClient.aidl create mode 100644 core/java/android/hardware/location/IContextHubClientCallback.aidl create mode 100644 services/core/java/com/android/server/location/ContextHubClientBroker.java create mode 100644 services/core/java/com/android/server/location/ContextHubClientManager.java diff --git a/Android.mk b/Android.mk index 24e717d2fa6b1..8f852529c62a2 100644 --- a/Android.mk +++ b/Android.mk @@ -219,6 +219,8 @@ LOCAL_SRC_FILES += \ core/java/android/hardware/location/IGeofenceHardwareCallback.aidl \ core/java/android/hardware/location/IGeofenceHardwareMonitorCallback.aidl \ core/java/android/hardware/location/IContextHubCallback.aidl \ + core/java/android/hardware/location/IContextHubClient.aidl \ + core/java/android/hardware/location/IContextHubClientCallback.aidl \ core/java/android/hardware/location/IContextHubService.aidl \ core/java/android/hardware/location/IContextHubTransactionCallback.aidl \ core/java/android/hardware/radio/IRadioService.aidl \ diff --git a/core/java/android/hardware/location/ContextHubClient.java b/core/java/android/hardware/location/ContextHubClient.java index b7e353a4860c3..32ec1138e8f32 100644 --- a/core/java/android/hardware/location/ContextHubClient.java +++ b/core/java/android/hardware/location/ContextHubClient.java @@ -16,7 +16,7 @@ package android.hardware.location; import android.annotation.RequiresPermission; -import android.os.Handler; +import android.os.RemoteException; import java.io.Closeable; @@ -29,29 +29,25 @@ import java.io.Closeable; */ public class ContextHubClient implements Closeable { /* - * The ContextHubClient interface associated with this client. + * The proxy to the client interface at the service. */ - // TODO: Implement this interface and associate with ContextHubClient object - // private final IContextHubClient mClientInterface; + private final IContextHubClient mClientProxy; /* - * The listening callback associated with this client. + * The callback interface associated with this client. */ - private ContextHubClientCallback mCallback; + private final IContextHubClientCallback mCallbackInterface; /* * The Context Hub that this client is attached to. */ - private ContextHubInfo mAttachedHub; + private final ContextHubInfo mAttachedHub; - /* - * The handler to invoke mCallback. - */ - private Handler mCallbackHandler; - - ContextHubClient(ContextHubClientCallback callback, Handler handler, ContextHubInfo hubInfo) { - mCallback = callback; - mCallbackHandler = handler; + /* package */ ContextHubClient( + IContextHubClient clientProxy, IContextHubClientCallback callback, + ContextHubInfo hubInfo) { + mClientProxy = clientProxy; + mCallbackInterface = callback; mAttachedHub = hubInfo; } @@ -90,6 +86,10 @@ public class ContextHubClient implements Closeable { @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE) @ContextHubTransaction.Result public int sendMessageToNanoApp(NanoAppMessage message) { - throw new UnsupportedOperationException("TODO: Implement this"); + try { + return mClientProxy.sendMessageToNanoApp(message); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } } } diff --git a/core/java/android/hardware/location/ContextHubManager.java b/core/java/android/hardware/location/ContextHubManager.java index 241172784262e..b31c7bcdb31e5 100644 --- a/core/java/android/hardware/location/ContextHubManager.java +++ b/core/java/android/hardware/location/ContextHubManager.java @@ -455,6 +455,54 @@ public final class ContextHubManager { return 0; } + /** + * Creates an interface to the ContextHubClient to send down to the service. + * + * @param callback the callback to invoke at the client process + * @param handler the handler to post callbacks for this client + * + * @return the callback interface + */ + private IContextHubClientCallback createClientCallback( + ContextHubClientCallback callback, Handler handler) { + return new IContextHubClientCallback.Stub() { + @Override + public void onMessageFromNanoApp(NanoAppMessage message) { + handler.post(() -> callback.onMessageFromNanoApp(message)); + } + + @Override + public void onHubReset() { + handler.post(() -> callback.onHubReset()); + } + + @Override + public void onNanoAppAborted(long nanoAppId, int abortCode) { + handler.post(() -> callback.onNanoAppAborted(nanoAppId, abortCode)); + } + + @Override + public void onNanoAppLoaded(long nanoAppId) { + handler.post(() -> callback.onNanoAppLoaded(nanoAppId)); + } + + @Override + public void onNanoAppUnloaded(long nanoAppId) { + handler.post(() -> callback.onNanoAppUnloaded(nanoAppId)); + } + + @Override + public void onNanoAppEnabled(long nanoAppId) { + handler.post(() -> callback.onNanoAppEnabled(nanoAppId)); + } + + @Override + public void onNanoAppDisabled(long nanoAppId) { + handler.post(() -> callback.onNanoAppDisabled(nanoAppId)); + } + }; + } + /** * Creates and registers a client and its callback with the Context Hub Service. * @@ -463,19 +511,37 @@ public final class ContextHubManager { * {@link ContextHubClient} object, and receive notifications through the provided callback. * * @param callback the notification callback to register - * @param hubInfo the hub to attach this client to - * @param handler the handler to invoke the callback, if null uses the main thread's Looper - * + * @param hubInfo the hub to attach this client to + * @param handler the handler to invoke the callback, if null uses the main thread's Looper * @return the registered client object * - * @see ContextHubClientCallback + * @throws IllegalArgumentException if hubInfo does not represent a valid hub + * @throws IllegalStateException if there were too many registered clients at the service + * @throws NullPointerException if callback or hubInfo is null * * @hide + * @see ContextHubClientCallback */ public ContextHubClient createClient( ContextHubClientCallback callback, ContextHubInfo hubInfo, @Nullable Handler handler) { - throw new UnsupportedOperationException( - "TODO: Implement this, and throw an exception on error"); + if (callback == null) { + throw new NullPointerException("Callback cannot be null"); + } + if (hubInfo == null) { + throw new NullPointerException("Hub info cannot be null"); + } + + Handler realHandler = (handler == null) ? new Handler(mMainLooper) : handler; + IContextHubClientCallback clientInterface = createClientCallback(callback, realHandler); + + IContextHubClient client; + try { + client = mService.createClient(clientInterface, hubInfo.getId()); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + + return new ContextHubClient(client, clientInterface, hubInfo); } /** diff --git a/core/java/android/hardware/location/IContextHubClient.aidl b/core/java/android/hardware/location/IContextHubClient.aidl new file mode 100644 index 0000000000000..cdaa15f0d59af --- /dev/null +++ b/core/java/android/hardware/location/IContextHubClient.aidl @@ -0,0 +1,28 @@ +/* + * Copyright 2017 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 android.hardware.location; + +import android.hardware.location.NanoAppMessage; + +/** + * @hide + */ +interface IContextHubClient { + + // Sends a message to a nanoapp + int sendMessageToNanoApp(in NanoAppMessage message); +} diff --git a/core/java/android/hardware/location/IContextHubClientCallback.aidl b/core/java/android/hardware/location/IContextHubClientCallback.aidl new file mode 100644 index 0000000000000..1c76bcbe18ce4 --- /dev/null +++ b/core/java/android/hardware/location/IContextHubClientCallback.aidl @@ -0,0 +1,49 @@ +/* + * Copyright 2017 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 android.hardware.location; + +import android.hardware.location.NanoAppMessage; + +/** + * An interface used by the Context Hub Service to invoke callbacks for lifecycle notifications of a + * Context Hub and nanoapps, as well as for nanoapp messaging. + * + * @hide + */ +oneway interface IContextHubClientCallback { + + // Callback invoked when receiving a message from a nanoapp. + void onMessageFromNanoApp(in NanoAppMessage message); + + // Callback invoked when the attached Context Hub has reset. + void onHubReset(); + + // Callback invoked when a nanoapp aborts at the attached Context Hub. + void onNanoAppAborted(long nanoAppId, int abortCode); + + // Callback invoked when a nanoapp is loaded at the attached Context Hub. + void onNanoAppLoaded(long nanoAppId); + + // Callback invoked when a nanoapp is unloaded from the attached Context Hub. + void onNanoAppUnloaded(long nanoAppId); + + // Callback invoked when a nanoapp is enabled at the attached Context Hub. + void onNanoAppEnabled(long nanoAppId); + + // Callback invoked when a nanoapp is disabled at the attached Context Hub. + void onNanoAppDisabled(long nanoAppId); +} diff --git a/core/java/android/hardware/location/IContextHubService.aidl b/core/java/android/hardware/location/IContextHubService.aidl index ff8c1d07ce2c1..1bb7c8fbb6e72 100644 --- a/core/java/android/hardware/location/IContextHubService.aidl +++ b/core/java/android/hardware/location/IContextHubService.aidl @@ -17,12 +17,14 @@ package android.hardware.location; // Declare any non-default types here with import statements -import android.hardware.location.ContextHubMessage; import android.hardware.location.ContextHubInfo; +import android.hardware.location.ContextHubMessage; import android.hardware.location.NanoApp; -import android.hardware.location.NanoAppInstanceInfo; import android.hardware.location.NanoAppFilter; +import android.hardware.location.NanoAppInstanceInfo; import android.hardware.location.IContextHubCallback; +import android.hardware.location.IContextHubClient; +import android.hardware.location.IContextHubClientCallback; /** * @hide @@ -52,4 +54,7 @@ interface IContextHubService { // send a message to a nanoApp int sendMessage(int hubHandle, int nanoAppHandle, in ContextHubMessage msg); + + // Creates a client to send and receive messages + IContextHubClient createClient(in IContextHubClientCallback client, int contextHubId); } diff --git a/services/core/java/com/android/server/location/ContextHubClientBroker.java b/services/core/java/com/android/server/location/ContextHubClientBroker.java new file mode 100644 index 0000000000000..e78460bf9cfd2 --- /dev/null +++ b/services/core/java/com/android/server/location/ContextHubClientBroker.java @@ -0,0 +1,124 @@ +/* + * Copyright 2017 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.contexthub.V1_0.ContextHubMsg; +import android.hardware.contexthub.V1_0.IContexthub; +import android.hardware.contexthub.V1_0.Result; +import android.hardware.location.ContextHubTransaction; +import android.hardware.location.IContextHubClient; +import android.hardware.location.IContextHubClientCallback; +import android.hardware.location.NanoAppMessage; +import android.os.RemoteException; +import android.util.Log; + +/** + * A broker for the ContextHubClient that handles messaging and life-cycle notification callbacks. + * + * @hide + */ +public class ContextHubClientBroker extends IContextHubClient.Stub { + private static final String TAG = "ContextHubClientBroker"; + + /* + * The context of the service. + */ + private final Context mContext; + + /* + * The proxy to talk to the Context Hub HAL. + */ + private final IContexthub mContextHubProxy; + + /* + * The ID of the hub that this client is attached to. + */ + private final int mAttachedContextHubId; + + /* + * The host end point ID of this client. + */ + private final short mHostEndPointId; + + /* + * The remote callback interface for this client. + */ + private final IContextHubClientCallback mCallbackInterface; + + /* package */ ContextHubClientBroker( + Context context, IContexthub contextHubProxy, int contextHubId, short hostEndPointId, + IContextHubClientCallback callback) { + mContext = context; + mContextHubProxy = contextHubProxy; + mAttachedContextHubId = contextHubId; + mHostEndPointId = hostEndPointId; + mCallbackInterface = callback; + } + + /** + * Sends from this client to a nanoapp. + * + * @param message the message to send + * @return the error code of sending the message + */ + @ContextHubTransaction.Result + @Override + public int sendMessageToNanoApp(NanoAppMessage message) { + ContextHubServiceUtil.checkPermissions(mContext); + ContextHubMsg messageToNanoApp = + ContextHubServiceUtil.createHidlContextHubMessage(mHostEndPointId, message); + + int result; + try { + result = mContextHubProxy.sendMessageToHub(mAttachedContextHubId, messageToNanoApp); + } catch (RemoteException e) { + Log.e(TAG, "RemoteException in sendMessageToNanoApp (target hub ID = " + + mAttachedContextHubId + ")", e); + result = Result.UNKNOWN_FAILURE; + } + return ContextHubServiceUtil.toTransactionResult(result); + } + + /** + * @return the ID of the context hub this client is attached to + */ + /* package */ int getAttachedContextHubId() { + return mAttachedContextHubId; + } + + /** + * @return the host endpoint ID of this client + */ + /* package */ short getHostEndPointId() { + return mHostEndPointId; + } + + /** + * Sends a message to the client associated with this object. + * + * @param message the message that came from a nanoapp + */ + /* package */ void sendMessageToClient(NanoAppMessage message) { + try { + mCallbackInterface.onMessageFromNanoApp(message); + } catch (RemoteException e) { + Log.e(TAG, "RemoteException while sending message to client (host endpoint ID = " + + mHostEndPointId + ")", e); + } + } +} diff --git a/services/core/java/com/android/server/location/ContextHubClientManager.java b/services/core/java/com/android/server/location/ContextHubClientManager.java new file mode 100644 index 0000000000000..e12686fa90165 --- /dev/null +++ b/services/core/java/com/android/server/location/ContextHubClientManager.java @@ -0,0 +1,170 @@ +/* + * Copyright 2017 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.contexthub.V1_0.ContextHubMsg; +import android.hardware.contexthub.V1_0.IContexthub; +import android.hardware.location.IContextHubClient; +import android.hardware.location.IContextHubClientCallback; +import android.hardware.location.NanoAppMessage; +import android.util.Log; + +import java.util.NoSuchElementException; +import java.util.concurrent.ConcurrentHashMap; + +/** + * A class that manages registration/unregistration of clients and manages messages to/from clients. + * + * @hide + */ +/* package */ class ContextHubClientManager { + private static final String TAG = "ContextHubClientManager"; + + /* + * The maximum host endpoint ID value that a client can be assigned. + */ + private static final int MAX_CLIENT_ID = 0x7fff; + + /* + * Local flag to enable debug logging. + */ + private static final boolean DEBUG_LOG_ENABLED = true; + + /* + * The context of the service. + */ + private final Context mContext; + + /* + * The proxy to talk to the Context Hub. + */ + private final IContexthub mContextHubProxy; + + /* + * A mapping of host endpoint IDs to the ContextHubClientBroker object of registered clients. + * A concurrent data structure is used since the registration/unregistration can occur in + * multiple threads. + */ + private final ConcurrentHashMap mHostEndPointIdToClientMap = + new ConcurrentHashMap<>(); + + /* + * The next host endpoint ID to start iterating for the next available host endpoint ID. + */ + private int mNextHostEndpointId = 0; + + /* package */ ContextHubClientManager( + Context context, IContexthub contextHubProxy) { + mContext = context; + mContextHubProxy = contextHubProxy; + } + + /** + * Registers a new client with the service. + * + * @param clientCallback the callback interface of the client to register + * @param contextHubId the ID of the hub this client is attached to + * + * @return the client interface + * + * @throws IllegalStateException if max number of clients have already registered + */ + /* package */ IContextHubClient registerClient( + IContextHubClientCallback clientCallback, int contextHubId) { + ContextHubClientBroker broker = createNewClientBroker(clientCallback, contextHubId); + + Log.d(TAG, "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 + * @param message the message send by a nanoapp + */ + /* package */ void onMessageFromNanoApp(int contextHubId, ContextHubMsg message) { + NanoAppMessage clientMessage = ContextHubServiceUtil.createNanoAppMessage(message); + + if (DEBUG_LOG_ENABLED) { + String targetAudience = clientMessage.isBroadcastMessage() ? "broadcast" : "unicast"; + Log.v(TAG, "Received a " + targetAudience + " message from nanoapp 0x" + + Long.toHexString(clientMessage.getNanoAppId())); + } + + if (clientMessage.isBroadcastMessage()) { + broadcastMessage(contextHubId, clientMessage); + } else { + ContextHubClientBroker proxy = mHostEndPointIdToClientMap.get(message.hostEndPoint); + if (proxy != null) { + proxy.sendMessageToClient(clientMessage); + } else { + Log.e(TAG, "Cannot send message to unregistered client (host endpoint ID = " + + message.hostEndPoint + ")"); + } + } + } + + /** + * 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 contextHubId the ID of the hub this client is attached to + * + * @return the ContextHubClientBroker object + * + * @throws IllegalStateException if max number of clients have already registered + */ + private synchronized ContextHubClientBroker createNewClientBroker( + IContextHubClientCallback clientCallback, int contextHubId) { + if (mHostEndPointIdToClientMap.size() == MAX_CLIENT_ID + 1) { + throw new IllegalStateException("Could not register client - max limit exceeded"); + } + + ContextHubClientBroker broker = null; + int id = mNextHostEndpointId; + for (int i = 0; i <= MAX_CLIENT_ID; i++) { + if (!mHostEndPointIdToClientMap.containsKey(id)) { + broker = new ContextHubClientBroker( + mContext, mContextHubProxy, contextHubId, (short)id, clientCallback); + mHostEndPointIdToClientMap.put((short)id, broker); + mNextHostEndpointId = (id == MAX_CLIENT_ID) ? 0 : id + 1; + break; + } + + id = (id == MAX_CLIENT_ID) ? 0 : id + 1; + } + + return broker; + } + + /** + * Broadcasts a message from a nanoapp to all clients attached to the associated hub. + * + * @param contextHubId the ID of the hub where the nanoapp sent the message from + * @param message the message send by a nanoapp + */ + private void broadcastMessage(int contextHubId, NanoAppMessage message) { + for (ContextHubClientBroker proxy : mHostEndPointIdToClientMap.values()) { + if (proxy.getAttachedContextHubId() == contextHubId) { + proxy.sendMessageToClient(message); + } + } + } +} diff --git a/services/core/java/com/android/server/location/ContextHubService.java b/services/core/java/com/android/server/location/ContextHubService.java index da481a8c2b474..45842a2988e47 100644 --- a/services/core/java/com/android/server/location/ContextHubService.java +++ b/services/core/java/com/android/server/location/ContextHubService.java @@ -16,7 +16,6 @@ package com.android.server.location; -import android.Manifest; import android.content.Context; import android.hardware.contexthub.V1_0.AsyncEventType; import android.hardware.contexthub.V1_0.ContextHub; @@ -30,6 +29,8 @@ import android.hardware.contexthub.V1_0.TransactionResult; import android.hardware.location.ContextHubInfo; import android.hardware.location.ContextHubMessage; import android.hardware.location.IContextHubCallback; +import android.hardware.location.IContextHubClient; +import android.hardware.location.IContextHubClientCallback; import android.hardware.location.IContextHubService; import android.hardware.location.IContextHubTransactionCallback; import android.hardware.location.NanoApp; @@ -58,9 +59,6 @@ import java.util.concurrent.ConcurrentHashMap; */ public class ContextHubService extends IContextHubService.Stub { private static final String TAG = "ContextHubService"; - private static final String HARDWARE_PERMISSION = Manifest.permission.LOCATION_HARDWARE; - private static final String ENFORCE_HW_PERMISSION_MESSAGE = "Permission '" - + HARDWARE_PERMISSION + "' not granted to access ContextHub Hardware"; /* * Constants for the type of transaction that is defined by ContextHubService. @@ -104,6 +102,9 @@ public class ContextHubService extends IContextHubService.Stub { // The manager for transaction queue private final ContextHubTransactionManager mTransactionManager; + // The manager for sending messages to/from clients + private final ContextHubClientManager mClientManager; + /** * Class extending the callback to register with a Context Hub. */ @@ -146,17 +147,19 @@ public class ContextHubService extends IContextHubService.Stub { mContextHubProxy = getContextHubProxy(); if (mContextHubProxy == null) { mTransactionManager = null; + mClientManager = null; mContextHubInfo = new ContextHubInfo[0]; return; } + mClientManager = new ContextHubClientManager(mContext, mContextHubProxy); mTransactionManager = new ContextHubTransactionManager(mContextHubProxy); List hubList; try { hubList = mContextHubProxy.getHubs(); } catch (RemoteException e) { - Log.e(TAG, "RemoteException while getting Context Hub info"); + Log.e(TAG, "RemoteException while getting Context Hub info", e); hubList = Collections.emptyList(); } mContextHubInfo = ContextHubServiceUtil.createContextHubInfoArray(hubList); @@ -168,7 +171,7 @@ public class ContextHubService extends IContextHubService.Stub { contextHubId, new ContextHubServiceCallback(contextHubId)); } catch (RemoteException e) { Log.e(TAG, "RemoteException while registering service callback for hub (ID = " - + contextHubId + ")"); + + contextHubId + ")", e); } } @@ -192,7 +195,7 @@ public class ContextHubService extends IContextHubService.Stub { try { proxy = IContexthub.getService(true /* retry */); } catch (RemoteException e) { - Log.e(TAG, "RemoteException while attaching to Context Hub HAL proxy"); + Log.e(TAG, "RemoteException while attaching to Context Hub HAL proxy", e); } catch (NoSuchElementException e) { Log.i(TAG, "Context Hub HAL service not found"); } @@ -292,7 +295,7 @@ public class ContextHubService extends IContextHubService.Stub { @Override public void onQueryResponse(int result, List nanoAppStateList) { byte[] data = {(byte) result}; - onMessageReceipt(MSG_QUERY_NANO_APPS, contextHubId, OS_APP_INSTANCE, data); + onMessageReceiptOldApi(MSG_QUERY_NANO_APPS, contextHubId, OS_APP_INSTANCE, data); } }; } @@ -473,7 +476,7 @@ public class ContextHubService extends IContextHubService.Stub { try { result = mContextHubProxy.sendMessageToHub(hubHandle, hubMessage); } catch (RemoteException e) { - Log.e(TAG, "Failed to send nanoapp message - RemoteException"); + Log.e(TAG, "Failed to send nanoapp message - RemoteException", e); result = Result.UNKNOWN_FAILURE; } } else { @@ -493,12 +496,13 @@ public class ContextHubService extends IContextHubService.Stub { * @param message the message contents */ private void handleClientMessageCallback(int contextHubId, ContextHubMsg message) { - // TODO(b/67734082): Send to new API clients byte[] data = ContextHubServiceUtil.createPrimitiveByteArray(message.msg); int nanoAppInstanceId = mNanoAppIdToInstanceMap.containsKey(message.appName) ? mNanoAppIdToInstanceMap.get(message.appName) : -1; - onMessageReceipt(message.msgType, contextHubId, nanoAppInstanceId, data); + onMessageReceiptOldApi(message.msgType, contextHubId, nanoAppInstanceId, data); + + mClientManager.onMessageFromNanoApp(contextHubId, message); } /** @@ -534,7 +538,7 @@ public class ContextHubService extends IContextHubService.Stub { data[0] = (byte) result; ByteBuffer.wrap(data, 1, 4).order(ByteOrder.nativeOrder()).putInt(instanceId); - onMessageReceipt(MSG_LOAD_NANO_APP, contextHubId, OS_APP_INSTANCE, data); + onMessageReceiptOldApi(MSG_LOAD_NANO_APP, contextHubId, OS_APP_INSTANCE, data); } /** @@ -552,7 +556,7 @@ public class ContextHubService extends IContextHubService.Stub { byte[] data = new byte[1]; data[0] = (byte) result; - onMessageReceipt(MSG_UNLOAD_NANO_APP, contextHubId, OS_APP_INSTANCE, data); + onMessageReceiptOldApi(MSG_UNLOAD_NANO_APP, contextHubId, OS_APP_INSTANCE, data); } /** @@ -578,7 +582,7 @@ public class ContextHubService extends IContextHubService.Stub { queryNanoAppsInternal(contextHubId); byte[] data = {TransactionResult.SUCCESS}; - onMessageReceipt(MSG_HUB_RESET, contextHubId, OS_APP_INSTANCE, data); + onMessageReceiptOldApi(MSG_HUB_RESET, contextHubId, OS_APP_INSTANCE, data); } else { Log.i(TAG, "Received unknown hub event (hub ID = " + contextHubId + ", type = " + eventType + ")"); @@ -641,6 +645,45 @@ public class ContextHubService extends IContextHubService.Stub { } } + /** + * @param contextHubId the hub ID to validate + * @return {@code true} if the ID represents that of an available hub, {@code false} otherwise + */ + private boolean isValidContextHubId(int contextHubId) { + for (ContextHubInfo hubInfo : mContextHubInfo) { + if (hubInfo.getId() == contextHubId) { + return true; + } + } + + return false; + } + + /** + * Creates and registers a client at the service for the specified Context Hub. + * + * @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 IllegalStateException if max number of clients have already registered + * @throws NullPointerException if clientCallback is null + */ + @Override + public IContextHubClient createClient( + IContextHubClientCallback clientCallback, int contextHubId) throws RemoteException { + checkPermissions(); + if (!isValidContextHubId(contextHubId)) { + throw new IllegalArgumentException("Invalid context hub ID " + contextHubId); + } + if (clientCallback == null) { + throw new NullPointerException("Cannot register client with null callback"); + } + + return mClientManager.registerClient(clientCallback, contextHubId); + } + @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; @@ -664,10 +707,10 @@ public class ContextHubService extends IContextHubService.Stub { } private void checkPermissions() { - mContext.enforceCallingPermission(HARDWARE_PERMISSION, ENFORCE_HW_PERMISSION_MESSAGE); + ContextHubServiceUtil.checkPermissions(mContext); } - private int onMessageReceipt(int msgType, int hubHandle, int appInstance, byte[] data) { + private int onMessageReceiptOldApi(int msgType, int hubHandle, int appInstance, byte[] data) { if (data == null) { return -1; } diff --git a/services/core/java/com/android/server/location/ContextHubServiceUtil.java b/services/core/java/com/android/server/location/ContextHubServiceUtil.java index ddbaf86beff23..6faeb72e2a4d9 100644 --- a/services/core/java/com/android/server/location/ContextHubServiceUtil.java +++ b/services/core/java/com/android/server/location/ContextHubServiceUtil.java @@ -16,11 +16,15 @@ package com.android.server.location; +import android.Manifest; +import android.content.Context; import android.hardware.contexthub.V1_0.ContextHub; import android.hardware.contexthub.V1_0.ContextHubMsg; import android.hardware.contexthub.V1_0.HostEndPoint; import android.hardware.contexthub.V1_0.HubAppInfo; +import android.hardware.contexthub.V1_0.Result; import android.hardware.location.ContextHubInfo; +import android.hardware.location.ContextHubTransaction; import android.hardware.location.NanoAppBinary; import android.hardware.location.NanoAppMessage; import android.hardware.location.NanoAppState; @@ -34,6 +38,9 @@ import java.util.ArrayList; */ /* package */ class ContextHubServiceUtil { private static final String TAG = "ContextHubServiceUtil"; + private static final String HARDWARE_PERMISSION = Manifest.permission.LOCATION_HARDWARE; + private static final String ENFORCE_HW_PERMISSION_MESSAGE = "Permission '" + + HARDWARE_PERMISSION + "' not granted to access ContextHub Hardware"; /** * Creates a ContextHubInfo array from an ArrayList of HIDL ContextHub objects. @@ -165,4 +172,40 @@ import java.util.ArrayList; message.appName, message.msgType, messageArray, message.hostEndPoint == HostEndPoint.BROADCAST); } + + /** + * Checks for location hardware permissions. + * + * @param context the context of the service + */ + /* package */ + static void checkPermissions(Context context) { + context.enforceCallingPermission(HARDWARE_PERMISSION, ENFORCE_HW_PERMISSION_MESSAGE); + } + + /** + * Helper function to convert from the HAL Result enum error code to the + * ContextHubTransaction.Result type. + * + * @param halResult the Result enum error code + * @return the ContextHubTransaction.Result equivalent + */ + @ContextHubTransaction.Result + /* package */ + static int toTransactionResult(int halResult) { + switch (halResult) { + case Result.OK: + return ContextHubTransaction.TRANSACTION_SUCCESS; + case Result.BAD_PARAMS: + return ContextHubTransaction.TRANSACTION_FAILED_BAD_PARAMS; + case Result.NOT_INIT: + return ContextHubTransaction.TRANSACTION_FAILED_UNINITIALIZED; + case Result.TRANSACTION_PENDING: + return ContextHubTransaction.TRANSACTION_FAILED_PENDING; + case Result.TRANSACTION_FAILED: + case Result.UNKNOWN_FAILURE: + default: /* fall through */ + return ContextHubTransaction.TRANSACTION_FAILED_UNKNOWN; + } + } }