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 package android.hardware.location;
     17 
     18 import android.annotation.RequiresPermission;
     19 import android.annotation.SuppressLint;
     20 import android.annotation.SystemApi;
     21 import android.annotation.SystemService;
     22 import android.content.Context;
     23 import android.os.Handler;
     24 import android.os.Looper;
     25 import android.os.RemoteException;
     26 import android.os.ServiceManager;
     27 import android.os.ServiceManager.ServiceNotFoundException;
     28 import android.util.Log;
     29 
     30 /**
     31  * A class that exposes the Context hubs on a device to applications.
     32  *
     33  * Please note that this class is not expected to be used by unbundled applications. Also, calling
     34  * applications are expected to have LOCATION_HARDWARE permissions to use this class.
     35  *
     36  * @hide
     37  */
     38 @SystemApi
     39 @SystemService(Context.CONTEXTHUB_SERVICE)
     40 public final class ContextHubManager {
     41 
     42     private static final String TAG = "ContextHubManager";
     43 
     44     private final Looper mMainLooper;
     45     private final IContextHubService mService;
     46     private Callback mCallback;
     47     private Handler mCallbackHandler;
     48 
     49     /**
     50      * @deprecated Use {@code mCallback} instead.
     51      */
     52     @Deprecated
     53     private ICallback mLocalCallback;
     54 
     55     /**
     56      * An interface to receive asynchronous communication from the context hub.
     57      */
     58     public abstract static class Callback {
     59         protected Callback() {}
     60 
     61         /**
     62          * Callback function called on message receipt from context hub.
     63          *
     64          * @param hubHandle Handle (system-wide unique identifier) of the hub of the message.
     65          * @param nanoAppHandle Handle (unique identifier) for app instance that sent the message.
     66          * @param message The context hub message.
     67          *
     68          * @see ContextHubMessage
     69          */
     70         public abstract void onMessageReceipt(
     71                 int hubHandle,
     72                 int nanoAppHandle,
     73                 ContextHubMessage message);
     74     }
     75 
     76     /**
     77      * @deprecated Use {@link Callback} instead.
     78      * @hide
     79      */
     80     @Deprecated
     81     public interface ICallback {
     82         /**
     83          * Callback function called on message receipt from context hub.
     84          *
     85          * @param hubHandle Handle (system-wide unique identifier) of the hub of the message.
     86          * @param nanoAppHandle Handle (unique identifier) for app instance that sent the message.
     87          * @param message The context hub message.
     88          *
     89          * @see ContextHubMessage
     90          */
     91         void onMessageReceipt(int hubHandle, int nanoAppHandle, ContextHubMessage message);
     92     }
     93 
     94     /**
     95      * Get a handle to all the context hubs in the system
     96      * @return array of context hub handles
     97      */
     98     @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
     99     public int[] getContextHubHandles() {
    100         try {
    101             return mService.getContextHubHandles();
    102         } catch (RemoteException e) {
    103             throw e.rethrowFromSystemServer();
    104         }
    105     }
    106 
    107     /**
    108      * Get more information about a specific hub.
    109      *
    110      * @param hubHandle Handle (system-wide unique identifier) of a context hub.
    111      * @return ContextHubInfo Information about the requested context hub.
    112      *
    113      * @see ContextHubInfo
    114      */
    115     @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
    116     public ContextHubInfo getContextHubInfo(int hubHandle) {
    117         try {
    118             return mService.getContextHubInfo(hubHandle);
    119         } catch (RemoteException e) {
    120             throw e.rethrowFromSystemServer();
    121         }
    122     }
    123 
    124     /**
    125      * Load a nano app on a specified context hub.
    126      *
    127      * Note that loading is asynchronous.  When we return from this method,
    128      * the nano app (probably) hasn't loaded yet.  Assuming a return of 0
    129      * from this method, then the final success/failure for the load, along
    130      * with the "handle" for the nanoapp, is all delivered in a byte
    131      * string via a call to Callback.onMessageReceipt.
    132      *
    133      * TODO(b/30784270): Provide a better success/failure and "handle" delivery.
    134      *
    135      * @param hubHandle handle of context hub to load the app on.
    136      * @param app the nanoApp to load on the hub
    137      *
    138      * @return 0 if the command for loading was sent to the context hub;
    139      *         -1 otherwise
    140      *
    141      * @see NanoApp
    142      */
    143     @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
    144     public int loadNanoApp(int hubHandle, NanoApp app) {
    145         try {
    146             return mService.loadNanoApp(hubHandle, app);
    147         } catch (RemoteException e) {
    148             throw e.rethrowFromSystemServer();
    149         }
    150     }
    151 
    152     /**
    153      * Unload a specified nanoApp
    154      *
    155      * Note that unloading is asynchronous.  When we return from this method,
    156      * the nano app (probably) hasn't unloaded yet.  Assuming a return of 0
    157      * from this method, then the final success/failure for the unload is
    158      * delivered in a byte string via a call to Callback.onMessageReceipt.
    159      *
    160      * TODO(b/30784270): Provide a better success/failure delivery.
    161      *
    162      * @param nanoAppHandle handle of the nanoApp to unload
    163      *
    164      * @return 0 if the command for unloading was sent to the context hub;
    165      *         -1 otherwise
    166      */
    167     @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
    168     public int unloadNanoApp(int nanoAppHandle) {
    169         try {
    170             return mService.unloadNanoApp(nanoAppHandle);
    171         } catch (RemoteException e) {
    172             throw e.rethrowFromSystemServer();
    173         }
    174     }
    175 
    176     /**
    177      * get information about the nano app instance
    178      *
    179      * NOTE: The returned NanoAppInstanceInfo does _not_ contain correct
    180      * information for several fields, specifically:
    181      * - getName()
    182      * - getPublisher()
    183      * - getNeededExecMemBytes()
    184      * - getNeededReadMemBytes()
    185      * - getNeededWriteMemBytes()
    186      *
    187      * For example, say you call loadNanoApp() with a NanoApp that has
    188      * getName() returning "My Name".  Later, if you call getNanoAppInstanceInfo
    189      * for that nanoapp, the returned NanoAppInstanceInfo's getName()
    190      * method will claim "Preloaded app, unknown", even though you would
    191      * have expected "My Name".  For now, as the user, you'll need to
    192      * separately track the above fields if they are of interest to you.
    193      *
    194      * TODO(b/30943489): Have the returned NanoAppInstanceInfo contain the
    195      *     correct information.
    196      *
    197      * @param nanoAppHandle handle of the nanoAppInstance
    198      * @return NanoAppInstanceInfo Information about the nano app instance.
    199      *
    200      * @see NanoAppInstanceInfo
    201      */
    202     @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
    203     public NanoAppInstanceInfo getNanoAppInstanceInfo(int nanoAppHandle) {
    204         try {
    205             return mService.getNanoAppInstanceInfo(nanoAppHandle);
    206         } catch (RemoteException e) {
    207             throw e.rethrowFromSystemServer();
    208         }
    209     }
    210 
    211     /**
    212      * Find a specified nano app on the system
    213      *
    214      * @param hubHandle handle of hub to search for nano app
    215      * @param filter filter specifying the search criteria for app
    216      *
    217      * @see NanoAppFilter
    218      *
    219      * @return int[] Array of handles to any found nano apps
    220      */
    221     @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
    222     public int[] findNanoAppOnHub(int hubHandle, NanoAppFilter filter) {
    223         try {
    224             return mService.findNanoAppOnHub(hubHandle, filter);
    225         } catch (RemoteException e) {
    226             throw e.rethrowFromSystemServer();
    227         }
    228     }
    229 
    230     /**
    231      * Send a message to a specific nano app instance on a context hub.
    232      *
    233      * Note that the return value of this method only speaks of success
    234      * up to the point of sending this to the Context Hub.  It is not
    235      * an assurance that the Context Hub successfully sent this message
    236      * on to the nanoapp.  If assurance is desired, a protocol should be
    237      * established between your code and the nanoapp, with the nanoapp
    238      * sending a confirmation message (which will be reported via
    239      * Callback.onMessageReceipt).
    240      *
    241      * @param hubHandle handle of the hub to send the message to
    242      * @param nanoAppHandle  handle of the nano app to send to
    243      * @param message Message to be sent
    244      *
    245      * @see ContextHubMessage
    246      *
    247      * @return int 0 on success, -1 otherwise
    248      */
    249     @RequiresPermission(android.Manifest.permission.LOCATION_HARDWARE)
    250     public int sendMessage(int hubHandle, int nanoAppHandle, ContextHubMessage message) {
    251         try {
    252             return mService.sendMessage(hubHandle, nanoAppHandle, message);
    253         } catch (RemoteException e) {
    254             throw e.rethrowFromSystemServer();
    255         }
    256     }
    257 
    258     /**
    259      * Set a callback to receive messages from the context hub
    260      *
    261      * @param callback Callback object
    262      *
    263      * @see Callback
    264      *
    265      * @return int 0 on success, -1 otherwise
    266      */
    267     @SuppressLint("Doclava125")
    268     public int registerCallback(Callback callback) {
    269         return registerCallback(callback, null);
    270     }
    271 
    272     /**
    273      * @deprecated Use {@link #registerCallback(Callback)} instead.
    274      * @hide
    275      */
    276     @Deprecated
    277     public int registerCallback(ICallback callback) {
    278         if (mLocalCallback != null) {
    279             Log.w(TAG, "Max number of local callbacks reached!");
    280             return -1;
    281         }
    282         mLocalCallback = callback;
    283         return 0;
    284     }
    285 
    286     /**
    287      * Set a callback to receive messages from the context hub
    288      *
    289      * @param callback Callback object
    290      * @param handler Handler object
    291      *
    292      * @see Callback
    293      *
    294      * @return int 0 on success, -1 otherwise
    295      */
    296     @SuppressLint("Doclava125")
    297     public int registerCallback(Callback callback, Handler handler) {
    298         synchronized(this) {
    299             if (mCallback != null) {
    300                 Log.w(TAG, "Max number of callbacks reached!");
    301                 return -1;
    302             }
    303             mCallback = callback;
    304             mCallbackHandler = handler;
    305         }
    306         return 0;
    307     }
    308 
    309     /**
    310      * Unregister a callback for receive messages from the context hub.
    311      *
    312      * @see Callback
    313      *
    314      * @param callback method to deregister
    315      *
    316      * @return int 0 on success, -1 otherwise
    317      */
    318     @SuppressLint("Doclava125")
    319     public int unregisterCallback(Callback callback) {
    320       synchronized(this) {
    321           if (callback != mCallback) {
    322               Log.w(TAG, "Cannot recognize callback!");
    323               return -1;
    324           }
    325 
    326           mCallback = null;
    327           mCallbackHandler = null;
    328       }
    329       return 0;
    330     }
    331 
    332     /**
    333      * @deprecated Use {@link #unregisterCallback(Callback)} instead.
    334      * @hide
    335      */
    336     @Deprecated
    337     public synchronized int unregisterCallback(ICallback callback) {
    338         if (callback != mLocalCallback) {
    339             Log.w(TAG, "Cannot recognize local callback!");
    340             return -1;
    341         }
    342         mLocalCallback = null;
    343         return 0;
    344     }
    345 
    346     private final IContextHubCallback.Stub mClientCallback = new IContextHubCallback.Stub() {
    347         @Override
    348         public void onMessageReceipt(final int hubId, final int nanoAppId,
    349                 final ContextHubMessage message) {
    350             if (mCallback != null) {
    351                 synchronized(this) {
    352                     final Callback callback = mCallback;
    353                     Handler handler = mCallbackHandler == null ?
    354                             new Handler(mMainLooper) : mCallbackHandler;
    355                     handler.post(new Runnable() {
    356                         @Override
    357                         public void run() {
    358                             callback.onMessageReceipt(hubId, nanoAppId, message);
    359                         }
    360                     });
    361                 }
    362             } else if (mLocalCallback != null) {
    363                 // we always ensure that mCallback takes precedence, because mLocalCallback is only
    364                 // for internal compatibility
    365                 synchronized (this) {
    366                     mLocalCallback.onMessageReceipt(hubId, nanoAppId, message);
    367                 }
    368             } else {
    369                 Log.d(TAG, "Context hub manager client callback is NULL");
    370             }
    371         }
    372     };
    373 
    374     /** @throws ServiceNotFoundException
    375      * @hide */
    376     public ContextHubManager(Context context, Looper mainLooper) throws ServiceNotFoundException {
    377         mMainLooper = mainLooper;
    378         mService = IContextHubService.Stub.asInterface(
    379                 ServiceManager.getServiceOrThrow(Context.CONTEXTHUB_SERVICE));
    380         try {
    381             mService.registerCallback(mClientCallback);
    382         } catch (RemoteException e) {
    383             Log.w(TAG, "Could not register callback:" + e);
    384         }
    385     }
    386 }
    387