Home | History | Annotate | Download | only in location
      1 /*
      2  * Copyright (C) 2016 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.server.location;
     18 
     19 import android.content.Context;
     20 import android.hardware.contexthub.V1_0.AsyncEventType;
     21 import android.hardware.contexthub.V1_0.ContextHub;
     22 import android.hardware.contexthub.V1_0.ContextHubMsg;
     23 import android.hardware.contexthub.V1_0.HubAppInfo;
     24 import android.hardware.contexthub.V1_0.IContexthub;
     25 import android.hardware.contexthub.V1_0.IContexthubCallback;
     26 import android.hardware.contexthub.V1_0.Result;
     27 import android.hardware.contexthub.V1_0.TransactionResult;
     28 import android.hardware.location.ContextHubInfo;
     29 import android.hardware.location.ContextHubMessage;
     30 import android.hardware.location.ContextHubTransaction;
     31 import android.hardware.location.IContextHubCallback;
     32 import android.hardware.location.IContextHubClient;
     33 import android.hardware.location.IContextHubClientCallback;
     34 import android.hardware.location.IContextHubService;
     35 import android.hardware.location.IContextHubTransactionCallback;
     36 import android.hardware.location.NanoApp;
     37 import android.hardware.location.NanoAppBinary;
     38 import android.hardware.location.NanoAppFilter;
     39 import android.hardware.location.NanoAppInstanceInfo;
     40 import android.hardware.location.NanoAppMessage;
     41 import android.hardware.location.NanoAppState;
     42 import android.os.RemoteCallbackList;
     43 import android.os.RemoteException;
     44 import android.util.Log;
     45 
     46 import com.android.internal.util.DumpUtils;
     47 
     48 import java.io.FileDescriptor;
     49 import java.io.PrintWriter;
     50 import java.nio.ByteBuffer;
     51 import java.nio.ByteOrder;
     52 import java.util.ArrayList;
     53 import java.util.Collections;
     54 import java.util.HashMap;
     55 import java.util.List;
     56 import java.util.Map;
     57 import java.util.NoSuchElementException;
     58 
     59 /**
     60  * @hide
     61  */
     62 public class ContextHubService extends IContextHubService.Stub {
     63     private static final String TAG = "ContextHubService";
     64 
     65     /*
     66      * Constants for the type of transaction that is defined by ContextHubService.
     67      * This is used to report the transaction callback to clients, and is different from
     68      * ContextHubTransaction.Type.
     69      */
     70     public static final int MSG_ENABLE_NANO_APP = 1;
     71     public static final int MSG_DISABLE_NANO_APP = 2;
     72     public static final int MSG_LOAD_NANO_APP = 3;
     73     public static final int MSG_UNLOAD_NANO_APP = 4;
     74     public static final int MSG_QUERY_NANO_APPS = 5;
     75     public static final int MSG_QUERY_MEMORY = 6;
     76     public static final int MSG_HUB_RESET = 7;
     77 
     78     private static final int OS_APP_INSTANCE = -1;
     79 
     80     private final Context mContext;
     81 
     82     private final Map<Integer, ContextHubInfo> mContextHubIdToInfoMap;
     83     private final List<ContextHubInfo> mContextHubInfoList;
     84     private final RemoteCallbackList<IContextHubCallback> mCallbacksList =
     85             new RemoteCallbackList<>();
     86 
     87     // Proxy object to communicate with the Context Hub HAL
     88     private final IContexthub mContextHubProxy;
     89 
     90     // The manager for transaction queue
     91     private final ContextHubTransactionManager mTransactionManager;
     92 
     93     // The manager for sending messages to/from clients
     94     private final ContextHubClientManager mClientManager;
     95 
     96     // The default client for old API clients
     97     private final Map<Integer, IContextHubClient> mDefaultClientMap;
     98 
     99     // The manager for the internal nanoapp state cache
    100     private final NanoAppStateManager mNanoAppStateManager = new NanoAppStateManager();
    101 
    102     /**
    103      * Class extending the callback to register with a Context Hub.
    104      */
    105     private class ContextHubServiceCallback extends IContexthubCallback.Stub {
    106         private final int mContextHubId;
    107 
    108         ContextHubServiceCallback(int contextHubId) {
    109             mContextHubId = contextHubId;
    110         }
    111 
    112         @Override
    113         public void handleClientMsg(ContextHubMsg message) {
    114             handleClientMessageCallback(mContextHubId, message);
    115         }
    116 
    117         @Override
    118         public void handleTxnResult(int transactionId, int result) {
    119             handleTransactionResultCallback(mContextHubId, transactionId, result);
    120         }
    121 
    122         @Override
    123         public void handleHubEvent(int eventType) {
    124             handleHubEventCallback(mContextHubId, eventType);
    125         }
    126 
    127         @Override
    128         public void handleAppAbort(long nanoAppId, int abortCode) {
    129             handleAppAbortCallback(mContextHubId, nanoAppId, abortCode);
    130         }
    131 
    132         @Override
    133         public void handleAppsInfo(ArrayList<HubAppInfo> nanoAppInfoList) {
    134             handleQueryAppsCallback(mContextHubId, nanoAppInfoList);
    135         }
    136     }
    137 
    138     public ContextHubService(Context context) {
    139         mContext = context;
    140 
    141         mContextHubProxy = getContextHubProxy();
    142         if (mContextHubProxy == null) {
    143             mTransactionManager = null;
    144             mClientManager = null;
    145             mDefaultClientMap = Collections.emptyMap();
    146             mContextHubIdToInfoMap = Collections.emptyMap();
    147             mContextHubInfoList = Collections.emptyList();
    148             return;
    149         }
    150 
    151         mClientManager = new ContextHubClientManager(mContext, mContextHubProxy);
    152         mTransactionManager = new ContextHubTransactionManager(
    153                 mContextHubProxy, mClientManager, mNanoAppStateManager);
    154 
    155         List<ContextHub> hubList;
    156         try {
    157             hubList = mContextHubProxy.getHubs();
    158         } catch (RemoteException e) {
    159             Log.e(TAG, "RemoteException while getting Context Hub info", e);
    160             hubList = Collections.emptyList();
    161         }
    162         mContextHubIdToInfoMap = Collections.unmodifiableMap(
    163                 ContextHubServiceUtil.createContextHubInfoMap(hubList));
    164         mContextHubInfoList = new ArrayList<>(mContextHubIdToInfoMap.values());
    165 
    166         HashMap<Integer, IContextHubClient> defaultClientMap = new HashMap<>();
    167         for (int contextHubId : mContextHubIdToInfoMap.keySet()) {
    168             IContextHubClient client = mClientManager.registerClient(
    169                     createDefaultClientCallback(contextHubId), contextHubId);
    170             defaultClientMap.put(contextHubId, client);
    171 
    172             try {
    173                 mContextHubProxy.registerCallback(
    174                         contextHubId, new ContextHubServiceCallback(contextHubId));
    175             } catch (RemoteException e) {
    176                 Log.e(TAG, "RemoteException while registering service callback for hub (ID = "
    177                         + contextHubId + ")", e);
    178             }
    179 
    180             // Do a query to initialize the service cache list of nanoapps
    181             // TODO(b/69270990): Remove this when old API is deprecated
    182             queryNanoAppsInternal(contextHubId);
    183         }
    184         mDefaultClientMap = Collections.unmodifiableMap(defaultClientMap);
    185     }
    186 
    187     /**
    188      * Creates a default client callback for old API clients.
    189      *
    190      * @param contextHubId the ID of the hub to attach this client to
    191      * @return the internal callback interface
    192      */
    193     private IContextHubClientCallback createDefaultClientCallback(int contextHubId) {
    194         return new IContextHubClientCallback.Stub() {
    195             @Override
    196             public void onMessageFromNanoApp(NanoAppMessage message) {
    197                 int nanoAppHandle = mNanoAppStateManager.getNanoAppHandle(
    198                         contextHubId, message.getNanoAppId());
    199 
    200                 onMessageReceiptOldApi(
    201                         message.getMessageType(), contextHubId, nanoAppHandle,
    202                         message.getMessageBody());
    203             }
    204 
    205             @Override
    206             public void onHubReset() {
    207                 byte[] data = {TransactionResult.SUCCESS};
    208                 onMessageReceiptOldApi(MSG_HUB_RESET, contextHubId, OS_APP_INSTANCE, data);
    209             }
    210 
    211             @Override
    212             public void onNanoAppAborted(long nanoAppId, int abortCode) {
    213             }
    214 
    215             @Override
    216             public void onNanoAppLoaded(long nanoAppId) {
    217             }
    218 
    219             @Override
    220             public void onNanoAppUnloaded(long nanoAppId) {
    221             }
    222 
    223             @Override
    224             public void onNanoAppEnabled(long nanoAppId) {
    225             }
    226 
    227             @Override
    228             public void onNanoAppDisabled(long nanoAppId) {
    229             }
    230         };
    231     }
    232 
    233     /**
    234      * @return the IContexthub proxy interface
    235      */
    236     private IContexthub getContextHubProxy() {
    237         IContexthub proxy = null;
    238         try {
    239             proxy = IContexthub.getService(true /* retry */);
    240         } catch (RemoteException e) {
    241             Log.e(TAG, "RemoteException while attaching to Context Hub HAL proxy", e);
    242         } catch (NoSuchElementException e) {
    243             Log.i(TAG, "Context Hub HAL service not found");
    244         }
    245 
    246         return proxy;
    247     }
    248 
    249     @Override
    250     public int registerCallback(IContextHubCallback callback) throws RemoteException {
    251         checkPermissions();
    252         mCallbacksList.register(callback);
    253 
    254         Log.d(TAG, "Added callback, total callbacks " +
    255                 mCallbacksList.getRegisteredCallbackCount());
    256         return 0;
    257     }
    258 
    259     @Override
    260     public int[] getContextHubHandles() throws RemoteException {
    261         checkPermissions();
    262         return ContextHubServiceUtil.createPrimitiveIntArray(mContextHubIdToInfoMap.keySet());
    263     }
    264 
    265     @Override
    266     public ContextHubInfo getContextHubInfo(int contextHubHandle) throws RemoteException {
    267         checkPermissions();
    268         if (!mContextHubIdToInfoMap.containsKey(contextHubHandle)) {
    269             Log.e(TAG, "Invalid Context Hub handle " + contextHubHandle + " in getContextHubInfo");
    270             return null;
    271         }
    272 
    273         return mContextHubIdToInfoMap.get(contextHubHandle);
    274     }
    275 
    276     /**
    277      * Returns a List of ContextHubInfo object describing the available hubs.
    278      *
    279      * @return the List of ContextHubInfo objects
    280      */
    281     @Override
    282     public List<ContextHubInfo> getContextHubs() throws RemoteException {
    283         checkPermissions();
    284         return mContextHubInfoList;
    285     }
    286 
    287     /**
    288      * Creates an internal load transaction callback to be used for old API clients
    289      *
    290      * @param contextHubId  the ID of the hub to load the binary
    291      * @param nanoAppBinary the binary to load
    292      * @return the callback interface
    293      */
    294     private IContextHubTransactionCallback createLoadTransactionCallback(
    295             int contextHubId, NanoAppBinary nanoAppBinary) {
    296         return new IContextHubTransactionCallback.Stub() {
    297             @Override
    298             public void onTransactionComplete(int result) {
    299                 handleLoadResponseOldApi(contextHubId, result, nanoAppBinary);
    300             }
    301 
    302             @Override
    303             public void onQueryResponse(int result, List<NanoAppState> nanoAppStateList) {
    304             }
    305         };
    306     }
    307 
    308     /**
    309      * Creates an internal unload transaction callback to be used for old API clients
    310      *
    311      * @param contextHubId the ID of the hub to unload the nanoapp
    312      * @return the callback interface
    313      */
    314     private IContextHubTransactionCallback createUnloadTransactionCallback(int contextHubId) {
    315         return new IContextHubTransactionCallback.Stub() {
    316             @Override
    317             public void onTransactionComplete(int result) {
    318                 handleUnloadResponseOldApi(contextHubId, result);
    319             }
    320 
    321             @Override
    322             public void onQueryResponse(int result, List<NanoAppState> nanoAppStateList) {
    323             }
    324         };
    325     }
    326 
    327     /**
    328      * Creates an internal query transaction callback to be used for old API clients
    329      *
    330      * @param contextHubId the ID of the hub to query
    331      * @return the callback interface
    332      */
    333     private IContextHubTransactionCallback createQueryTransactionCallback(int contextHubId) {
    334         return new IContextHubTransactionCallback.Stub() {
    335             @Override
    336             public void onTransactionComplete(int result) {
    337             }
    338 
    339             @Override
    340             public void onQueryResponse(int result, List<NanoAppState> nanoAppStateList) {
    341                 byte[] data = {(byte) result};
    342                 onMessageReceiptOldApi(MSG_QUERY_NANO_APPS, contextHubId, OS_APP_INSTANCE, data);
    343             }
    344         };
    345     }
    346 
    347     @Override
    348     public int loadNanoApp(int contextHubHandle, NanoApp nanoApp) throws RemoteException {
    349         checkPermissions();
    350         if (mContextHubProxy == null) {
    351             return -1;
    352         }
    353         if (!isValidContextHubId(contextHubHandle)) {
    354             Log.e(TAG, "Invalid Context Hub handle " + contextHubHandle + " in loadNanoApp");
    355             return -1;
    356         }
    357         if (nanoApp == null) {
    358             Log.e(TAG, "NanoApp cannot be null in loadNanoApp");
    359             return -1;
    360         }
    361 
    362         // Create an internal IContextHubTransactionCallback for the old API clients
    363         NanoAppBinary nanoAppBinary = new NanoAppBinary(nanoApp.getAppBinary());
    364         IContextHubTransactionCallback onCompleteCallback =
    365                 createLoadTransactionCallback(contextHubHandle, nanoAppBinary);
    366 
    367         ContextHubServiceTransaction transaction = mTransactionManager.createLoadTransaction(
    368                 contextHubHandle, nanoAppBinary, onCompleteCallback);
    369 
    370         mTransactionManager.addTransaction(transaction);
    371         return 0;
    372     }
    373 
    374     @Override
    375     public int unloadNanoApp(int nanoAppHandle) throws RemoteException {
    376         checkPermissions();
    377         if (mContextHubProxy == null) {
    378             return -1;
    379         }
    380 
    381         NanoAppInstanceInfo info =
    382                 mNanoAppStateManager.getNanoAppInstanceInfo(nanoAppHandle);
    383         if (info == null) {
    384             Log.e(TAG, "Invalid nanoapp handle " + nanoAppHandle + " in unloadNanoApp");
    385             return -1;
    386         }
    387 
    388         int contextHubId = info.getContexthubId();
    389         long nanoAppId = info.getAppId();
    390         IContextHubTransactionCallback onCompleteCallback =
    391                 createUnloadTransactionCallback(contextHubId);
    392         ContextHubServiceTransaction transaction = mTransactionManager.createUnloadTransaction(
    393                 contextHubId, nanoAppId, onCompleteCallback);
    394 
    395         mTransactionManager.addTransaction(transaction);
    396         return 0;
    397     }
    398 
    399     @Override
    400     public NanoAppInstanceInfo getNanoAppInstanceInfo(int nanoAppHandle) throws RemoteException {
    401         checkPermissions();
    402 
    403         return mNanoAppStateManager.getNanoAppInstanceInfo(nanoAppHandle);
    404     }
    405 
    406     @Override
    407     public int[] findNanoAppOnHub(
    408             int contextHubHandle, NanoAppFilter filter) throws RemoteException {
    409         checkPermissions();
    410 
    411         ArrayList<Integer> foundInstances = new ArrayList<>();
    412         for (NanoAppInstanceInfo info : mNanoAppStateManager.getNanoAppInstanceInfoCollection()) {
    413             if (filter.testMatch(info)) {
    414                 foundInstances.add(info.getHandle());
    415             }
    416         }
    417 
    418         int[] retArray = new int[foundInstances.size()];
    419         for (int i = 0; i < foundInstances.size(); i++) {
    420             retArray[i] = foundInstances.get(i).intValue();
    421         }
    422         return retArray;
    423     }
    424 
    425     /**
    426      * Performs a query at the specified hub.
    427      *
    428      * This method should only be invoked internally by the service, either to update the service
    429      * cache or as a result of an explicit query requested by a client through the sendMessage API.
    430      *
    431      * @param contextHubId the ID of the hub to do the query
    432      * @return the result of the query
    433      *
    434      * @throws IllegalStateException if the transaction queue is full
    435      */
    436     private int queryNanoAppsInternal(int contextHubId) {
    437         if (mContextHubProxy == null) {
    438             return Result.UNKNOWN_FAILURE;
    439         }
    440 
    441         IContextHubTransactionCallback onCompleteCallback =
    442                 createQueryTransactionCallback(contextHubId);
    443         ContextHubServiceTransaction transaction = mTransactionManager.createQueryTransaction(
    444                 contextHubId, onCompleteCallback);
    445 
    446         mTransactionManager.addTransaction(transaction);
    447         return Result.OK;
    448     }
    449 
    450     @Override
    451     public int sendMessage(int contextHubHandle, int nanoAppHandle, ContextHubMessage msg)
    452             throws RemoteException {
    453         checkPermissions();
    454         if (mContextHubProxy == null) {
    455             return -1;
    456         }
    457         if (msg == null) {
    458             Log.e(TAG, "ContextHubMessage cannot be null in sendMessage");
    459             return -1;
    460         }
    461         if (msg.getData() == null) {
    462             Log.e(TAG, "ContextHubMessage message body cannot be null in sendMessage");
    463             return -1;
    464         }
    465         if (!isValidContextHubId(contextHubHandle)) {
    466             Log.e(TAG, "Invalid Context Hub handle " + contextHubHandle + " in sendMessage");
    467             return -1;
    468         }
    469 
    470         boolean success = false;
    471         if (nanoAppHandle == OS_APP_INSTANCE) {
    472             if (msg.getMsgType() == MSG_QUERY_NANO_APPS) {
    473                 success = (queryNanoAppsInternal(contextHubHandle) == Result.OK);
    474             } else {
    475                 Log.e(TAG, "Invalid OS message params of type " + msg.getMsgType());
    476             }
    477         } else {
    478             NanoAppInstanceInfo info = getNanoAppInstanceInfo(nanoAppHandle);
    479             if (info != null) {
    480                 NanoAppMessage message = NanoAppMessage.createMessageToNanoApp(
    481                         info.getAppId(), msg.getMsgType(), msg.getData());
    482 
    483                 IContextHubClient client = mDefaultClientMap.get(contextHubHandle);
    484                 success = (client.sendMessageToNanoApp(message) ==
    485                         ContextHubTransaction.RESULT_SUCCESS);
    486             } else {
    487                 Log.e(TAG, "Failed to send nanoapp message - nanoapp with handle "
    488                         + nanoAppHandle + " does not exist.");
    489             }
    490         }
    491 
    492         return success ? 0 : -1;
    493     }
    494 
    495     /**
    496      * Handles a unicast or broadcast message from a nanoapp.
    497      *
    498      * @param contextHubId the ID of the hub the message came from
    499      * @param message      the message contents
    500      */
    501     private void handleClientMessageCallback(int contextHubId, ContextHubMsg message) {
    502         mClientManager.onMessageFromNanoApp(contextHubId, message);
    503     }
    504 
    505     /**
    506      * A helper function to handle a load response from the Context Hub for the old API.
    507      *
    508      * TODO(b/69270990): Remove this once the old APIs are obsolete.
    509      */
    510     private void handleLoadResponseOldApi(
    511             int contextHubId, int result, NanoAppBinary nanoAppBinary) {
    512         if (nanoAppBinary == null) {
    513             Log.e(TAG, "Nanoapp binary field was null for a load transaction");
    514             return;
    515         }
    516 
    517         byte[] data = new byte[5];
    518         data[0] = (byte) result;
    519         int nanoAppHandle = mNanoAppStateManager.getNanoAppHandle(
    520                 contextHubId, nanoAppBinary.getNanoAppId());
    521         ByteBuffer.wrap(data, 1, 4).order(ByteOrder.nativeOrder()).putInt(nanoAppHandle);
    522 
    523         onMessageReceiptOldApi(MSG_LOAD_NANO_APP, contextHubId, OS_APP_INSTANCE, data);
    524     }
    525 
    526     /**
    527      * A helper function to handle an unload response from the Context Hub for the old API.
    528      *
    529      * TODO(b/69270990): Remove this once the old APIs are obsolete.
    530      */
    531     private void handleUnloadResponseOldApi(int contextHubId, int result) {
    532         byte[] data = new byte[1];
    533         data[0] = (byte) result;
    534         onMessageReceiptOldApi(MSG_UNLOAD_NANO_APP, contextHubId, OS_APP_INSTANCE, data);
    535     }
    536 
    537     /**
    538      * Handles a transaction response from a Context Hub.
    539      *
    540      * @param contextHubId  the ID of the hub the response came from
    541      * @param transactionId the ID of the transaction
    542      * @param result        the result of the transaction reported by the hub
    543      */
    544     private void handleTransactionResultCallback(int contextHubId, int transactionId, int result) {
    545         mTransactionManager.onTransactionResponse(transactionId, result);
    546     }
    547 
    548     /**
    549      * Handles an asynchronous event from a Context Hub.
    550      *
    551      * @param contextHubId the ID of the hub the response came from
    552      * @param eventType    the type of the event as defined in Context Hub HAL AsyncEventType
    553      */
    554     private void handleHubEventCallback(int contextHubId, int eventType) {
    555         if (eventType == AsyncEventType.RESTARTED) {
    556             mTransactionManager.onHubReset();
    557             queryNanoAppsInternal(contextHubId);
    558 
    559             mClientManager.onHubReset(contextHubId);
    560         } else {
    561             Log.i(TAG, "Received unknown hub event (hub ID = " + contextHubId + ", type = "
    562                     + eventType + ")");
    563         }
    564     }
    565 
    566     /**
    567      * Handles an asynchronous abort event of a nanoapp.
    568      *
    569      * @param contextHubId the ID of the hub that the nanoapp aborted in
    570      * @param nanoAppId    the ID of the aborted nanoapp
    571      * @param abortCode    the nanoapp-specific abort code
    572      */
    573     private void handleAppAbortCallback(int contextHubId, long nanoAppId, int abortCode) {
    574         mClientManager.onNanoAppAborted(contextHubId, nanoAppId, abortCode);
    575     }
    576 
    577     /**
    578      * Handles a query response from a Context Hub.
    579      *
    580      * @param contextHubId    the ID of the hub of the response
    581      * @param nanoAppInfoList the list of loaded nanoapps
    582      */
    583     private void handleQueryAppsCallback(int contextHubId, List<HubAppInfo> nanoAppInfoList) {
    584         List<NanoAppState> nanoAppStateList =
    585                 ContextHubServiceUtil.createNanoAppStateList(nanoAppInfoList);
    586 
    587         mNanoAppStateManager.updateCache(contextHubId, nanoAppInfoList);
    588         mTransactionManager.onQueryResponse(nanoAppStateList);
    589     }
    590 
    591     /**
    592      * @param contextHubId the hub ID to validate
    593      * @return {@code true} if the ID represents that of an available hub, {@code false} otherwise
    594      */
    595     private boolean isValidContextHubId(int contextHubId) {
    596         return mContextHubIdToInfoMap.containsKey(contextHubId);
    597     }
    598 
    599     /**
    600      * Creates and registers a client at the service for the specified Context Hub.
    601      *
    602      * @param clientCallback the client interface to register with the service
    603      * @param contextHubId   the ID of the hub this client is attached to
    604      * @return the generated client interface, null if registration was unsuccessful
    605      *
    606      * @throws IllegalArgumentException if contextHubId is not a valid ID
    607      * @throws IllegalStateException if max number of clients have already registered
    608      * @throws NullPointerException if clientCallback is null
    609      */
    610     @Override
    611     public IContextHubClient createClient(
    612             IContextHubClientCallback clientCallback, int contextHubId) throws RemoteException {
    613         checkPermissions();
    614         if (!isValidContextHubId(contextHubId)) {
    615             throw new IllegalArgumentException("Invalid context hub ID " + contextHubId);
    616         }
    617         if (clientCallback == null) {
    618             throw new NullPointerException("Cannot register client with null callback");
    619         }
    620 
    621         return mClientManager.registerClient(clientCallback, contextHubId);
    622     }
    623 
    624     /**
    625      * Loads a nanoapp binary at the specified Context hub.
    626      *
    627      * @param contextHubId        the ID of the hub to load the binary
    628      * @param transactionCallback the client-facing transaction callback interface
    629      * @param nanoAppBinary       the binary to load
    630      *
    631      * @throws IllegalStateException if the transaction queue is full
    632      */
    633     @Override
    634     public void loadNanoAppOnHub(
    635             int contextHubId, IContextHubTransactionCallback transactionCallback,
    636             NanoAppBinary nanoAppBinary) throws RemoteException {
    637         checkPermissions();
    638         if (!checkHalProxyAndContextHubId(
    639                 contextHubId, transactionCallback, ContextHubTransaction.TYPE_LOAD_NANOAPP)) {
    640             return;
    641         }
    642         if (nanoAppBinary == null) {
    643             Log.e(TAG, "NanoAppBinary cannot be null in loadNanoAppOnHub");
    644             transactionCallback.onTransactionComplete(
    645                     ContextHubTransaction.RESULT_FAILED_BAD_PARAMS);
    646             return;
    647         }
    648 
    649         ContextHubServiceTransaction transaction = mTransactionManager.createLoadTransaction(
    650                 contextHubId, nanoAppBinary, transactionCallback);
    651         mTransactionManager.addTransaction(transaction);
    652     }
    653 
    654     /**
    655      * Unloads a nanoapp from the specified Context Hub.
    656      *
    657      * @param contextHubId        the ID of the hub to unload the nanoapp
    658      * @param transactionCallback the client-facing transaction callback interface
    659      * @param nanoAppId           the ID of the nanoapp to unload
    660      *
    661      * @throws IllegalStateException if the transaction queue is full
    662      */
    663     @Override
    664     public void unloadNanoAppFromHub(
    665             int contextHubId, IContextHubTransactionCallback transactionCallback, long nanoAppId)
    666             throws RemoteException {
    667         checkPermissions();
    668         if (!checkHalProxyAndContextHubId(
    669                 contextHubId, transactionCallback, ContextHubTransaction.TYPE_UNLOAD_NANOAPP)) {
    670             return;
    671         }
    672 
    673         ContextHubServiceTransaction transaction = mTransactionManager.createUnloadTransaction(
    674                 contextHubId, nanoAppId, transactionCallback);
    675         mTransactionManager.addTransaction(transaction);
    676     }
    677 
    678     /**
    679      * Enables a nanoapp at the specified Context Hub.
    680      *
    681      * @param contextHubId        the ID of the hub to enable the nanoapp
    682      * @param transactionCallback the client-facing transaction callback interface
    683      * @param nanoAppId           the ID of the nanoapp to enable
    684      *
    685      * @throws IllegalStateException if the transaction queue is full
    686      */
    687     @Override
    688     public void enableNanoApp(
    689             int contextHubId, IContextHubTransactionCallback transactionCallback, long nanoAppId)
    690             throws RemoteException {
    691         checkPermissions();
    692         if (!checkHalProxyAndContextHubId(
    693                 contextHubId, transactionCallback, ContextHubTransaction.TYPE_ENABLE_NANOAPP)) {
    694             return;
    695         }
    696 
    697         ContextHubServiceTransaction transaction = mTransactionManager.createEnableTransaction(
    698                 contextHubId, nanoAppId, transactionCallback);
    699         mTransactionManager.addTransaction(transaction);
    700     }
    701 
    702     /**
    703      * Disables a nanoapp at the specified Context Hub.
    704      *
    705      * @param contextHubId        the ID of the hub to disable the nanoapp
    706      * @param transactionCallback the client-facing transaction callback interface
    707      * @param nanoAppId           the ID of the nanoapp to disable
    708      *
    709      * @throws IllegalStateException if the transaction queue is full
    710      */
    711     @Override
    712     public void disableNanoApp(
    713             int contextHubId, IContextHubTransactionCallback transactionCallback, long nanoAppId)
    714             throws RemoteException {
    715         checkPermissions();
    716         if (!checkHalProxyAndContextHubId(
    717                 contextHubId, transactionCallback, ContextHubTransaction.TYPE_DISABLE_NANOAPP)) {
    718             return;
    719         }
    720 
    721         ContextHubServiceTransaction transaction = mTransactionManager.createDisableTransaction(
    722                 contextHubId, nanoAppId, transactionCallback);
    723         mTransactionManager.addTransaction(transaction);
    724     }
    725 
    726     /**
    727      * Queries for a list of nanoapps from the specified Context hub.
    728      *
    729      * @param contextHubId        the ID of the hub to query
    730      * @param transactionCallback the client-facing transaction callback interface
    731      *
    732      * @throws IllegalStateException if the transaction queue is full
    733      */
    734     @Override
    735     public void queryNanoApps(int contextHubId, IContextHubTransactionCallback transactionCallback)
    736             throws RemoteException {
    737         checkPermissions();
    738         if (!checkHalProxyAndContextHubId(
    739                 contextHubId, transactionCallback, ContextHubTransaction.TYPE_QUERY_NANOAPPS)) {
    740             return;
    741         }
    742 
    743         ContextHubServiceTransaction transaction = mTransactionManager.createQueryTransaction(
    744                 contextHubId, transactionCallback);
    745         mTransactionManager.addTransaction(transaction);
    746     }
    747 
    748     @Override
    749     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
    750         if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
    751 
    752         pw.println("Dumping ContextHub Service");
    753 
    754         pw.println("");
    755         // dump ContextHubInfo
    756         pw.println("=================== CONTEXT HUBS ====================");
    757         for (ContextHubInfo hubInfo : mContextHubIdToInfoMap.values()) {
    758             pw.println(hubInfo);
    759         }
    760         pw.println("");
    761         pw.println("=================== NANOAPPS ====================");
    762         // Dump nanoAppHash
    763         for (NanoAppInstanceInfo info : mNanoAppStateManager.getNanoAppInstanceInfoCollection()) {
    764             pw.println(info);
    765         }
    766 
    767         // dump eventLog
    768     }
    769 
    770     private void checkPermissions() {
    771         ContextHubServiceUtil.checkPermissions(mContext);
    772     }
    773 
    774     private int onMessageReceiptOldApi(
    775             int msgType, int contextHubHandle, int appInstance, byte[] data) {
    776         if (data == null) {
    777             return -1;
    778         }
    779 
    780         int msgVersion = 0;
    781         int callbacksCount = mCallbacksList.beginBroadcast();
    782         Log.d(TAG, "Sending message " + msgType + " version " + msgVersion + " from hubHandle " +
    783                 contextHubHandle + ", appInstance " + appInstance + ", callBackCount "
    784                 + callbacksCount);
    785 
    786         if (callbacksCount < 1) {
    787             Log.v(TAG, "No message callbacks registered.");
    788             return 0;
    789         }
    790 
    791         ContextHubMessage msg = new ContextHubMessage(msgType, msgVersion, data);
    792         for (int i = 0; i < callbacksCount; ++i) {
    793             IContextHubCallback callback = mCallbacksList.getBroadcastItem(i);
    794             try {
    795                 callback.onMessageReceipt(contextHubHandle, appInstance, msg);
    796             } catch (RemoteException e) {
    797                 Log.i(TAG, "Exception (" + e + ") calling remote callback (" + callback + ").");
    798                 continue;
    799             }
    800         }
    801         mCallbacksList.finishBroadcast();
    802         return 0;
    803     }
    804 
    805     /**
    806      * Validates the HAL proxy state and context hub ID to see if we can start the transaction.
    807      *
    808      * @param contextHubId    the ID of the hub to start the transaction
    809      * @param callback        the client transaction callback interface
    810      * @param transactionType the type of the transaction
    811      *
    812      * @return {@code true} if mContextHubProxy and contextHubId is valid, {@code false} otherwise
    813      */
    814     private boolean checkHalProxyAndContextHubId(
    815             int contextHubId, IContextHubTransactionCallback callback,
    816             @ContextHubTransaction.Type int transactionType) {
    817         if (mContextHubProxy == null) {
    818             try {
    819                 callback.onTransactionComplete(
    820                         ContextHubTransaction.RESULT_FAILED_HAL_UNAVAILABLE);
    821             } catch (RemoteException e) {
    822                 Log.e(TAG, "RemoteException while calling onTransactionComplete", e);
    823             }
    824             return false;
    825         }
    826         if (!isValidContextHubId(contextHubId)) {
    827             Log.e(TAG, "Cannot start "
    828                     + ContextHubTransaction.typeToString(transactionType, false /* upperCase */)
    829                     + " transaction for invalid hub ID " + contextHubId);
    830             try {
    831                 callback.onTransactionComplete(ContextHubTransaction.RESULT_FAILED_BAD_PARAMS);
    832             } catch (RemoteException e) {
    833                 Log.e(TAG, "RemoteException while calling onTransactionComplete", e);
    834             }
    835             return false;
    836         }
    837 
    838         return true;
    839     }
    840 }
    841