| /* |
| * Copyright (C) 2016 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.AsyncEventType; |
| import android.hardware.contexthub.V1_0.ContextHub; |
| import android.hardware.contexthub.V1_0.ContextHubMsg; |
| import android.hardware.contexthub.V1_0.HubAppInfo; |
| import android.hardware.contexthub.V1_0.IContexthub; |
| import android.hardware.contexthub.V1_0.IContexthubCallback; |
| import android.hardware.contexthub.V1_0.Result; |
| import android.hardware.contexthub.V1_0.TransactionResult; |
| import android.hardware.location.ContextHubInfo; |
| import android.hardware.location.ContextHubMessage; |
| import android.hardware.location.ContextHubTransaction; |
| 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; |
| import android.hardware.location.NanoAppBinary; |
| import android.hardware.location.NanoAppFilter; |
| import android.hardware.location.NanoAppInstanceInfo; |
| import android.hardware.location.NanoAppMessage; |
| import android.hardware.location.NanoAppState; |
| import android.os.RemoteCallbackList; |
| import android.os.RemoteException; |
| import android.util.Log; |
| |
| import com.android.internal.util.DumpUtils; |
| |
| import java.io.FileDescriptor; |
| import java.io.PrintWriter; |
| import java.nio.ByteBuffer; |
| import java.nio.ByteOrder; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.NoSuchElementException; |
| import java.util.concurrent.ConcurrentHashMap; |
| |
| /** |
| * @hide |
| */ |
| public class ContextHubService extends IContextHubService.Stub { |
| private static final String TAG = "ContextHubService"; |
| |
| /* |
| * Constants for the type of transaction that is defined by ContextHubService. |
| * This is used to report the transaction callback to clients, and is different from |
| * ContextHubTransaction.Type. |
| */ |
| public static final int MSG_ENABLE_NANO_APP = 1; |
| public static final int MSG_DISABLE_NANO_APP = 2; |
| public static final int MSG_LOAD_NANO_APP = 3; |
| public static final int MSG_UNLOAD_NANO_APP = 4; |
| public static final int MSG_QUERY_NANO_APPS = 5; |
| public static final int MSG_QUERY_MEMORY = 6; |
| public static final int MSG_HUB_RESET = 7; |
| |
| private static final String PRE_LOADED_GENERIC_UNKNOWN = "Preloaded app, unknown"; |
| private static final String PRE_LOADED_APP_NAME = PRE_LOADED_GENERIC_UNKNOWN; |
| private static final String PRE_LOADED_APP_PUBLISHER = PRE_LOADED_GENERIC_UNKNOWN; |
| private static final int PRE_LOADED_APP_MEM_REQ = 0; |
| |
| private static final int OS_APP_INSTANCE = -1; |
| |
| private final Context mContext; |
| |
| // TODO(b/69270990): Remove once old ContextHubManager API is deprecated |
| // Service cache maintaining of instance ID to nanoapp infos |
| private final ConcurrentHashMap<Integer, NanoAppInstanceInfo> mNanoAppHash = |
| new ConcurrentHashMap<>(); |
| // The next available instance ID (managed by the service) to assign to a nanoapp |
| private int mNextAvailableInstanceId = 0; |
| // A map of the long nanoapp ID to instance ID managed by the service |
| private final ConcurrentHashMap<Long, Integer> mNanoAppIdToInstanceMap = |
| new ConcurrentHashMap<>(); |
| |
| private final ContextHubInfo[] mContextHubInfo; |
| private final RemoteCallbackList<IContextHubCallback> mCallbacksList = |
| new RemoteCallbackList<>(); |
| |
| // Proxy object to communicate with the Context Hub HAL |
| private final IContexthub mContextHubProxy; |
| |
| // The manager for transaction queue |
| private final ContextHubTransactionManager mTransactionManager; |
| |
| // The manager for sending messages to/from clients |
| private final ContextHubClientManager mClientManager; |
| |
| // The default client for old API clients |
| private final Map<Integer, IContextHubClient> mDefaultClientMap; |
| |
| /** |
| * Class extending the callback to register with a Context Hub. |
| */ |
| private class ContextHubServiceCallback extends IContexthubCallback.Stub { |
| private final int mContextHubId; |
| |
| ContextHubServiceCallback(int contextHubId) { |
| mContextHubId = contextHubId; |
| } |
| |
| @Override |
| public void handleClientMsg(ContextHubMsg message) { |
| handleClientMessageCallback(mContextHubId, message); |
| } |
| |
| @Override |
| public void handleTxnResult(int transactionId, int result) { |
| handleTransactionResultCallback(mContextHubId, transactionId, result); |
| } |
| |
| @Override |
| public void handleHubEvent(int eventType) { |
| handleHubEventCallback(mContextHubId, eventType); |
| } |
| |
| @Override |
| public void handleAppAbort(long nanoAppId, int abortCode) { |
| handleAppAbortCallback(mContextHubId, nanoAppId, abortCode); |
| } |
| |
| @Override |
| public void handleAppsInfo(ArrayList<HubAppInfo> nanoAppInfoList) { |
| handleQueryAppsCallback(mContextHubId, nanoAppInfoList); |
| } |
| } |
| |
| public ContextHubService(Context context) { |
| mContext = context; |
| |
| mContextHubProxy = getContextHubProxy(); |
| if (mContextHubProxy == null) { |
| mTransactionManager = null; |
| mClientManager = null; |
| mDefaultClientMap = Collections.EMPTY_MAP; |
| mContextHubInfo = new ContextHubInfo[0]; |
| return; |
| } |
| |
| mClientManager = new ContextHubClientManager(mContext, mContextHubProxy); |
| mTransactionManager = new ContextHubTransactionManager(mContextHubProxy, mClientManager); |
| |
| List<ContextHub> hubList; |
| try { |
| hubList = mContextHubProxy.getHubs(); |
| } catch (RemoteException e) { |
| Log.e(TAG, "RemoteException while getting Context Hub info", e); |
| hubList = Collections.emptyList(); |
| } |
| mContextHubInfo = ContextHubServiceUtil.createContextHubInfoArray(hubList); |
| |
| HashMap<Integer, IContextHubClient> defaultClientMap = new HashMap<>(); |
| for (ContextHubInfo contextHubInfo : mContextHubInfo) { |
| int contextHubId = contextHubInfo.getId(); |
| |
| IContextHubClient client = mClientManager.registerClient( |
| createDefaultClientCallback(contextHubId), contextHubId); |
| defaultClientMap.put(contextHubId, client); |
| } |
| mDefaultClientMap = Collections.unmodifiableMap(defaultClientMap); |
| |
| for (ContextHubInfo contextHubInfo : mContextHubInfo) { |
| int contextHubId = contextHubInfo.getId(); |
| try { |
| mContextHubProxy.registerCallback( |
| contextHubId, new ContextHubServiceCallback(contextHubId)); |
| } catch (RemoteException e) { |
| Log.e(TAG, "RemoteException while registering service callback for hub (ID = " |
| + contextHubId + ")", e); |
| } |
| } |
| |
| // Do a query to initialize the service cache list of nanoapps |
| // TODO(b/69270990): Remove this when old API is deprecated |
| for (ContextHubInfo contextHubInfo : mContextHubInfo) { |
| queryNanoAppsInternal(contextHubInfo.getId()); |
| } |
| |
| for (int i = 0; i < mContextHubInfo.length; i++) { |
| Log.d(TAG, "ContextHub[" + i + "] id: " + mContextHubInfo[i].getId() |
| + ", name: " + mContextHubInfo[i].getName()); |
| } |
| } |
| |
| /** |
| * Creates a default client callback for old API clients. |
| * |
| * @param contextHubId the ID of the hub to attach this client to |
| * @return the internal callback interface |
| */ |
| private IContextHubClientCallback createDefaultClientCallback(int contextHubId) { |
| return new IContextHubClientCallback.Stub() { |
| @Override |
| public void onMessageFromNanoApp(NanoAppMessage message) { |
| int nanoAppInstanceId = |
| mNanoAppIdToInstanceMap.containsKey(message.getNanoAppId()) ? |
| mNanoAppIdToInstanceMap.get(message.getNanoAppId()) : -1; |
| |
| onMessageReceiptOldApi( |
| message.getMessageType(), contextHubId, nanoAppInstanceId, |
| message.getMessageBody()); |
| } |
| |
| @Override |
| public void onHubReset() { |
| byte[] data = {TransactionResult.SUCCESS}; |
| onMessageReceiptOldApi(MSG_HUB_RESET, contextHubId, OS_APP_INSTANCE, data); |
| } |
| |
| @Override |
| public void onNanoAppAborted(long nanoAppId, int abortCode) { |
| } |
| |
| @Override |
| public void onNanoAppLoaded(long nanoAppId) { |
| } |
| |
| @Override |
| public void onNanoAppUnloaded(long nanoAppId) { |
| } |
| |
| @Override |
| public void onNanoAppEnabled(long nanoAppId) { |
| } |
| |
| @Override |
| public void onNanoAppDisabled(long nanoAppId) { |
| } |
| }; |
| } |
| |
| /** |
| * @return the IContexthub proxy interface |
| */ |
| private IContexthub getContextHubProxy() { |
| IContexthub proxy = null; |
| try { |
| proxy = IContexthub.getService(true /* retry */); |
| } catch (RemoteException e) { |
| Log.e(TAG, "RemoteException while attaching to Context Hub HAL proxy", e); |
| } catch (NoSuchElementException e) { |
| Log.i(TAG, "Context Hub HAL service not found"); |
| } |
| |
| return proxy; |
| } |
| |
| @Override |
| public int registerCallback(IContextHubCallback callback) throws RemoteException { |
| checkPermissions(); |
| mCallbacksList.register(callback); |
| |
| Log.d(TAG, "Added callback, total callbacks " + |
| mCallbacksList.getRegisteredCallbackCount()); |
| return 0; |
| } |
| |
| @Override |
| public int[] getContextHubHandles() throws RemoteException { |
| checkPermissions(); |
| int[] returnArray = new int[mContextHubInfo.length]; |
| |
| Log.d(TAG, "System supports " + returnArray.length + " hubs"); |
| for (int i = 0; i < returnArray.length; ++i) { |
| returnArray[i] = i; |
| Log.d(TAG, String.format("Hub %s is mapped to %d", |
| mContextHubInfo[i].getName(), returnArray[i])); |
| } |
| |
| return returnArray; |
| } |
| |
| @Override |
| public ContextHubInfo getContextHubInfo(int contextHubId) throws RemoteException { |
| checkPermissions(); |
| if (!(contextHubId >= 0 && contextHubId < mContextHubInfo.length)) { |
| Log.e(TAG, "Invalid context hub handle " + contextHubId); |
| return null; // null means fail |
| } |
| |
| return mContextHubInfo[contextHubId]; |
| } |
| |
| /** |
| * Creates an internal load transaction callback to be used for old API clients |
| * |
| * @param contextHubId the ID of the hub to load the binary |
| * @param nanoAppBinary the binary to load |
| * @return the callback interface |
| */ |
| private IContextHubTransactionCallback createLoadTransactionCallback( |
| int contextHubId, NanoAppBinary nanoAppBinary) { |
| return new IContextHubTransactionCallback.Stub() { |
| @Override |
| public void onTransactionComplete(int result) { |
| handleLoadResponseOldApi(contextHubId, result, nanoAppBinary); |
| } |
| |
| @Override |
| public void onQueryResponse(int result, List<NanoAppState> nanoAppStateList) { |
| } |
| }; |
| } |
| |
| /** |
| * Creates an internal unload transaction callback to be used for old API clients |
| * |
| * @param contextHubId the ID of the hub to unload the nanoapp |
| * @param nanoAppId the ID of the nanoapp to unload |
| * @return the callback interface |
| */ |
| private IContextHubTransactionCallback createUnloadTransactionCallback( |
| int contextHubId, long nanoAppId) { |
| return new IContextHubTransactionCallback.Stub() { |
| @Override |
| public void onTransactionComplete(int result) { |
| handleUnloadResponseOldApi(contextHubId, result, nanoAppId); |
| } |
| |
| @Override |
| public void onQueryResponse(int result, List<NanoAppState> nanoAppStateList) { |
| } |
| }; |
| } |
| |
| /** |
| * Creates an internal query transaction callback to be used for old API clients |
| * |
| * @param contextHubId the ID of the hub to query |
| * @return the callback interface |
| */ |
| private IContextHubTransactionCallback createQueryTransactionCallback(int contextHubId) { |
| return new IContextHubTransactionCallback.Stub() { |
| @Override |
| public void onTransactionComplete(int result) { |
| } |
| |
| @Override |
| public void onQueryResponse(int result, List<NanoAppState> nanoAppStateList) { |
| byte[] data = {(byte) result}; |
| onMessageReceiptOldApi(MSG_QUERY_NANO_APPS, contextHubId, OS_APP_INSTANCE, data); |
| } |
| }; |
| } |
| |
| /** |
| * Adds a new transaction to the transaction manager queue |
| * |
| * @param transaction the transaction to add |
| * @return the result of adding the transaction |
| */ |
| private int addTransaction(ContextHubServiceTransaction transaction) { |
| int result = Result.OK; |
| try { |
| mTransactionManager.addTransaction(transaction); |
| } catch (IllegalStateException e) { |
| Log.e(TAG, e.getMessage()); |
| result = Result.TRANSACTION_PENDING; /* failed */ |
| } |
| |
| return result; |
| } |
| |
| @Override |
| public int loadNanoApp(int contextHubId, NanoApp app) throws RemoteException { |
| checkPermissions(); |
| if (mContextHubProxy == null) { |
| return -1; |
| } |
| |
| if (!(contextHubId >= 0 && contextHubId < mContextHubInfo.length)) { |
| Log.e(TAG, "Invalid contextHubhandle " + contextHubId); |
| return -1; |
| } |
| if (app == null) { |
| Log.e(TAG, "Invalid null app"); |
| return -1; |
| } |
| |
| // Create an internal IContextHubTransactionCallback for the old API clients |
| NanoAppBinary nanoAppBinary = new NanoAppBinary(app.getAppBinary()); |
| IContextHubTransactionCallback onCompleteCallback = |
| createLoadTransactionCallback(contextHubId, nanoAppBinary); |
| |
| ContextHubServiceTransaction transaction = mTransactionManager.createLoadTransaction( |
| contextHubId, nanoAppBinary, onCompleteCallback); |
| |
| int result = addTransaction(transaction); |
| if (result != Result.OK) { |
| Log.e(TAG, "Failed to load nanoapp with error code " + result); |
| return -1; |
| } |
| |
| // Do not add an entry to mNanoAppInstance Hash yet. The HAL may reject the app |
| return 0; |
| } |
| |
| @Override |
| public int unloadNanoApp(int nanoAppInstanceHandle) throws RemoteException { |
| checkPermissions(); |
| if (mContextHubProxy == null) { |
| return -1; |
| } |
| |
| NanoAppInstanceInfo info = mNanoAppHash.get(nanoAppInstanceHandle); |
| if (info == null) { |
| Log.e(TAG, "Cannot find app with handle " + nanoAppInstanceHandle); |
| return -1; //means failed |
| } |
| |
| int contextHubId = info.getContexthubId(); |
| long nanoAppId = info.getAppId(); |
| IContextHubTransactionCallback onCompleteCallback = |
| createUnloadTransactionCallback(contextHubId, nanoAppId); |
| ContextHubServiceTransaction transaction = mTransactionManager.createUnloadTransaction( |
| contextHubId, nanoAppId, onCompleteCallback); |
| |
| int result = addTransaction(transaction); |
| if (result != Result.OK) { |
| Log.e(TAG, "Failed to unload nanoapp with error code " + result); |
| return -1; |
| } |
| |
| // Do not add an entry to mNanoAppInstance Hash yet. The HAL may reject the app |
| return 0; |
| } |
| |
| @Override |
| public NanoAppInstanceInfo getNanoAppInstanceInfo(int nanoAppInstanceHandle) |
| throws RemoteException { |
| checkPermissions(); |
| // This assumes that all the nanoAppInfo is current. This is reasonable |
| // for the use cases for tightly controlled nanoApps. |
| if (mNanoAppHash.containsKey(nanoAppInstanceHandle)) { |
| return mNanoAppHash.get(nanoAppInstanceHandle); |
| } else { |
| Log.e(TAG, "Could not find nanoApp with handle " + nanoAppInstanceHandle); |
| return null; |
| } |
| } |
| |
| @Override |
| public int[] findNanoAppOnHub(int hubHandle, NanoAppFilter filter) throws RemoteException { |
| checkPermissions(); |
| ArrayList<Integer> foundInstances = new ArrayList<Integer>(); |
| |
| for (Integer nanoAppInstance : mNanoAppHash.keySet()) { |
| NanoAppInstanceInfo info = mNanoAppHash.get(nanoAppInstance); |
| |
| if (filter.testMatch(info)) { |
| foundInstances.add(nanoAppInstance); |
| } |
| } |
| |
| int[] retArray = new int[foundInstances.size()]; |
| for (int i = 0; i < foundInstances.size(); i++) { |
| retArray[i] = foundInstances.get(i).intValue(); |
| } |
| |
| Log.w(TAG, "Found " + retArray.length + " apps on hub handle " + hubHandle); |
| return retArray; |
| } |
| |
| /** |
| * Performs a query at the specified hub. |
| * |
| * This method should only be invoked internally by the service, either to update the service |
| * cache or as a result of an explicit query requested by a client through the sendMessage API. |
| * |
| * @param contextHubId the ID of the hub to do the query |
| * @return the result of the query |
| */ |
| private int queryNanoAppsInternal(int contextHubId) { |
| if (mContextHubProxy == null) { |
| return Result.UNKNOWN_FAILURE; |
| } |
| |
| IContextHubTransactionCallback onCompleteCallback = |
| createQueryTransactionCallback(contextHubId); |
| ContextHubServiceTransaction transaction = mTransactionManager.createQueryTransaction( |
| contextHubId, onCompleteCallback); |
| |
| return addTransaction(transaction); |
| } |
| |
| @Override |
| public int sendMessage( |
| int hubHandle, int nanoAppHandle, ContextHubMessage msg) throws RemoteException { |
| checkPermissions(); |
| if (mContextHubProxy == null) { |
| return -1; |
| } |
| if (msg == null) { |
| Log.e(TAG, "ContextHubMessage cannot be null"); |
| return -1; |
| } |
| if (msg.getData() == null) { |
| Log.e(TAG, "ContextHubMessage message body cannot be null"); |
| return -1; |
| } |
| if (!mDefaultClientMap.containsKey(hubHandle)) { |
| Log.e(TAG, "Hub with ID " + hubHandle + " does not exist"); |
| return -1; |
| } |
| |
| boolean success = false; |
| if (nanoAppHandle == OS_APP_INSTANCE) { |
| if (msg.getMsgType() == MSG_QUERY_NANO_APPS) { |
| success = (queryNanoAppsInternal(hubHandle) == Result.OK); |
| } else { |
| Log.e(TAG, "Invalid OS message params of type " + msg.getMsgType()); |
| } |
| } else { |
| NanoAppInstanceInfo info = getNanoAppInstanceInfo(nanoAppHandle); |
| if (info != null) { |
| NanoAppMessage message = NanoAppMessage.createMessageToNanoApp( |
| info.getAppId(), msg.getMsgType(), msg.getData()); |
| |
| IContextHubClient client = mDefaultClientMap.get(hubHandle); |
| success = (client.sendMessageToNanoApp(message) == |
| ContextHubTransaction.TRANSACTION_SUCCESS); |
| } else { |
| Log.e(TAG, "Failed to send nanoapp message - nanoapp with instance ID " |
| + nanoAppHandle + " does not exist."); |
| } |
| } |
| |
| return success ? 0 : -1; |
| } |
| |
| /** |
| * Handles a unicast or broadcast message from a nanoapp. |
| * |
| * @param contextHubId the ID of the hub the message came from |
| * @param message the message contents |
| */ |
| private void handleClientMessageCallback(int contextHubId, ContextHubMsg message) { |
| mClientManager.onMessageFromNanoApp(contextHubId, message); |
| } |
| |
| /** |
| * A helper function to handle a load response from the Context Hub for the old API. |
| * |
| * TODO(b/69270990): Remove this once the old APIs are obsolete. |
| */ |
| private void handleLoadResponseOldApi( |
| int contextHubId, int result, NanoAppBinary nanoAppBinary) { |
| if (nanoAppBinary == null) { |
| Log.e(TAG, "Nanoapp binary field was null for a load transaction"); |
| return; |
| } |
| |
| // NOTE: The legacy JNI code used to do a query right after a load success |
| // to synchronize the service cache. Instead store the binary that was requested to |
| // load to update the cache later without doing a query. |
| int instanceId = 0; |
| long nanoAppId = nanoAppBinary.getNanoAppId(); |
| int nanoAppVersion = nanoAppBinary.getNanoAppVersion(); |
| if (result == TransactionResult.SUCCESS) { |
| if (mNanoAppIdToInstanceMap.containsKey(nanoAppId)) { |
| instanceId = mNanoAppIdToInstanceMap.get(nanoAppId); |
| } else { |
| instanceId = mNextAvailableInstanceId++; |
| mNanoAppIdToInstanceMap.put(nanoAppId, instanceId); |
| } |
| |
| addAppInstance(contextHubId, instanceId, nanoAppId, nanoAppVersion); |
| } |
| |
| byte[] data = new byte[5]; |
| data[0] = (byte) result; |
| ByteBuffer.wrap(data, 1, 4).order(ByteOrder.nativeOrder()).putInt(instanceId); |
| |
| onMessageReceiptOldApi(MSG_LOAD_NANO_APP, contextHubId, OS_APP_INSTANCE, data); |
| } |
| |
| /** |
| * A helper function to handle an unload response from the Context Hub for the old API. |
| * |
| * TODO(b/69270990): Remove this once the old APIs are obsolete. |
| */ |
| private void handleUnloadResponseOldApi( |
| int contextHubId, int result, long nanoAppId) { |
| if (result == TransactionResult.SUCCESS) { |
| int instanceId = mNanoAppIdToInstanceMap.get(nanoAppId); |
| deleteAppInstance(instanceId); |
| mNanoAppIdToInstanceMap.remove(nanoAppId); |
| } |
| |
| byte[] data = new byte[1]; |
| data[0] = (byte) result; |
| onMessageReceiptOldApi(MSG_UNLOAD_NANO_APP, contextHubId, OS_APP_INSTANCE, data); |
| } |
| |
| /** |
| * Handles a transaction response from a Context Hub. |
| * |
| * @param contextHubId the ID of the hub the response came from |
| * @param transactionId the ID of the transaction |
| * @param result the result of the transaction reported by the hub |
| */ |
| private void handleTransactionResultCallback(int contextHubId, int transactionId, int result) { |
| mTransactionManager.onTransactionResponse(transactionId, result); |
| } |
| |
| /** |
| * Handles an asynchronous event from a Context Hub. |
| * |
| * @param contextHubId the ID of the hub the response came from |
| * @param eventType the type of the event as defined in Context Hub HAL AsyncEventType |
| */ |
| private void handleHubEventCallback(int contextHubId, int eventType) { |
| if (eventType == AsyncEventType.RESTARTED) { |
| mTransactionManager.onHubReset(); |
| queryNanoAppsInternal(contextHubId); |
| |
| mClientManager.onHubReset(contextHubId); |
| } else { |
| Log.i(TAG, "Received unknown hub event (hub ID = " + contextHubId + ", type = " |
| + eventType + ")"); |
| } |
| } |
| |
| /** |
| * Handles an asynchronous abort event of a nanoapp. |
| * |
| * @param contextHubId the ID of the hub that the nanoapp aborted in |
| * @param nanoAppId the ID of the aborted nanoapp |
| * @param abortCode the nanoapp-specific abort code |
| */ |
| private void handleAppAbortCallback(int contextHubId, long nanoAppId, int abortCode) { |
| // TODO(b/31049861): Implement this |
| } |
| |
| /** |
| * Handles a query response from a Context Hub. |
| * |
| * @param contextHubId the ID of the hub of the response |
| * @param nanoAppInfoList the list of loaded nanoapps |
| */ |
| private void handleQueryAppsCallback(int contextHubId, List<HubAppInfo> nanoAppInfoList) { |
| List<NanoAppState> nanoAppStateList = |
| ContextHubServiceUtil.createNanoAppStateList(nanoAppInfoList); |
| |
| updateServiceCache(contextHubId, nanoAppInfoList); |
| mTransactionManager.onQueryResponse(nanoAppStateList); |
| } |
| |
| /** |
| * Updates the service's cache of the list of loaded nanoapps using a nanoapp list response. |
| * |
| * TODO(b/69270990): Remove this when the old API functionality is removed. |
| * |
| * @param contextHubId the ID of the hub the response came from |
| * @param nanoAppInfoList the list of loaded nanoapps |
| */ |
| private void updateServiceCache(int contextHubId, List<HubAppInfo> nanoAppInfoList) { |
| synchronized (mNanoAppHash) { |
| for (int instanceId : mNanoAppHash.keySet()) { |
| if (mNanoAppHash.get(instanceId).getContexthubId() == contextHubId) { |
| deleteAppInstance(instanceId); |
| } |
| } |
| |
| for (HubAppInfo appInfo : nanoAppInfoList) { |
| int instanceId; |
| long nanoAppId = appInfo.appId; |
| if (mNanoAppIdToInstanceMap.containsKey(nanoAppId)) { |
| instanceId = mNanoAppIdToInstanceMap.get(nanoAppId); |
| } else { |
| instanceId = mNextAvailableInstanceId++; |
| mNanoAppIdToInstanceMap.put(nanoAppId, instanceId); |
| } |
| |
| addAppInstance(contextHubId, instanceId, nanoAppId, appInfo.version); |
| } |
| } |
| } |
| |
| /** |
| * @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; |
| |
| pw.println("Dumping ContextHub Service"); |
| |
| pw.println(""); |
| // dump ContextHubInfo |
| pw.println("=================== CONTEXT HUBS ===================="); |
| for (int i = 0; i < mContextHubInfo.length; i++) { |
| pw.println("Handle " + i + " : " + mContextHubInfo[i].toString()); |
| } |
| pw.println(""); |
| pw.println("=================== NANOAPPS ===================="); |
| // Dump nanoAppHash |
| for (Integer nanoAppInstance : mNanoAppHash.keySet()) { |
| pw.println(nanoAppInstance + " : " + mNanoAppHash.get(nanoAppInstance).toString()); |
| } |
| |
| // dump eventLog |
| } |
| |
| private void checkPermissions() { |
| ContextHubServiceUtil.checkPermissions(mContext); |
| } |
| |
| private int onMessageReceiptOldApi(int msgType, int hubHandle, int appInstance, byte[] data) { |
| if (data == null) { |
| return -1; |
| } |
| |
| int msgVersion = 0; |
| int callbacksCount = mCallbacksList.beginBroadcast(); |
| Log.d(TAG, "Sending message " + msgType + " version " + msgVersion + " from hubHandle " + |
| hubHandle + ", appInstance " + appInstance + ", callBackCount " + callbacksCount); |
| |
| if (callbacksCount < 1) { |
| Log.v(TAG, "No message callbacks registered."); |
| return 0; |
| } |
| |
| ContextHubMessage msg = new ContextHubMessage(msgType, msgVersion, data); |
| for (int i = 0; i < callbacksCount; ++i) { |
| IContextHubCallback callback = mCallbacksList.getBroadcastItem(i); |
| try { |
| callback.onMessageReceipt(hubHandle, appInstance, msg); |
| } catch (RemoteException e) { |
| Log.i(TAG, "Exception (" + e + ") calling remote callback (" + callback + ")."); |
| continue; |
| } |
| } |
| mCallbacksList.finishBroadcast(); |
| return 0; |
| } |
| |
| private int addAppInstance(int hubHandle, int appInstanceHandle, long appId, int appVersion) { |
| // App Id encodes vendor & version |
| NanoAppInstanceInfo appInfo = new NanoAppInstanceInfo(); |
| |
| appInfo.setAppId(appId); |
| appInfo.setAppVersion(appVersion); |
| appInfo.setName(PRE_LOADED_APP_NAME); |
| appInfo.setContexthubId(hubHandle); |
| appInfo.setHandle(appInstanceHandle); |
| appInfo.setPublisher(PRE_LOADED_APP_PUBLISHER); |
| appInfo.setNeededExecMemBytes(PRE_LOADED_APP_MEM_REQ); |
| appInfo.setNeededReadMemBytes(PRE_LOADED_APP_MEM_REQ); |
| appInfo.setNeededWriteMemBytes(PRE_LOADED_APP_MEM_REQ); |
| |
| String action; |
| if (mNanoAppHash.containsKey(appInstanceHandle)) { |
| action = "Updated"; |
| } else { |
| action = "Added"; |
| } |
| |
| mNanoAppHash.put(appInstanceHandle, appInfo); |
| Log.d(TAG, action + " app instance " + appInstanceHandle + " with id 0x" |
| + Long.toHexString(appId) + " version 0x" + Integer.toHexString(appVersion)); |
| |
| return 0; |
| } |
| |
| private int deleteAppInstance(int appInstanceHandle) { |
| if (mNanoAppHash.remove(appInstanceHandle) == null) { |
| return -1; |
| } |
| |
| return 0; |
| } |
| } |