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 {
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          */
onMessageReceipt( int hubHandle, int nanoAppHandle, ContextHubMessage message)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          */
onMessageReceipt(int hubHandle, int nanoAppHandle, ContextHubMessage message)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)
getContextHubHandles()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)
getContextHubInfo(int hubHandle)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)
loadNanoApp(int hubHandle, NanoApp app)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)
unloadNanoApp(int nanoAppHandle)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)
getNanoAppInstanceInfo(int nanoAppHandle)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)
findNanoAppOnHub(int hubHandle, NanoAppFilter filter)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)
sendMessage(int hubHandle, int nanoAppHandle, ContextHubMessage message)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")
registerCallback(Callback callback)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
registerCallback(ICallback callback)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")
registerCallback(Callback callback, Handler handler)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")
unregisterCallback(Callback callback)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
unregisterCallback(ICallback callback)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 */
ContextHubManager(Context context, Looper mainLooper)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