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.CallbackExecutor;
19 import android.annotation.IntDef;
20 import android.annotation.NonNull;
21 import android.annotation.Nullable;
22 import android.annotation.RequiresFeature;
23 import android.annotation.RequiresPermission;
24 import android.annotation.SuppressLint;
25 import android.annotation.SystemApi;
26 import android.annotation.SystemService;
27 import android.annotation.TestApi;
28 import android.app.ActivityThread;
29 import android.app.PendingIntent;
30 import android.content.Context;
31 import android.content.Intent;
32 import android.content.pm.PackageManager;
33 import android.os.Handler;
34 import android.os.HandlerExecutor;
35 import android.os.Looper;
36 import android.os.RemoteException;
37 import android.os.ServiceManager;
38 import android.os.ServiceManager.ServiceNotFoundException;
39 import android.util.Log;
40 
41 import java.lang.annotation.Retention;
42 import java.lang.annotation.RetentionPolicy;
43 import java.util.List;
44 import java.util.Objects;
45 import java.util.concurrent.Executor;
46 
47 /**
48  * A class that exposes the Context hubs on a device to applications.
49  *
50  * Please note that this class is not expected to be used by unbundled applications. Also, calling
51  * applications are expected to have the ACCESS_CONTEXT_HUB permission to use this class.
52  *
53  * @hide
54  */
55 @SystemApi
56 @SystemService(Context.CONTEXTHUB_SERVICE)
57 @RequiresFeature(PackageManager.FEATURE_CONTEXT_HUB)
58 public final class ContextHubManager {
59     private static final String TAG = "ContextHubManager";
60 
61     /**
62      * An extra containing one of the {@code AUTHORIZATION_*} constants such as
63      * {@link #AUTHORIZATION_GRANTED} describing the client's authorization state.
64      */
65     public static final String EXTRA_CLIENT_AUTHORIZATION_STATE =
66             "android.hardware.location.extra.CLIENT_AUTHORIZATION_STATE";
67 
68     /**
69      * An extra of type {@link ContextHubInfo} describing the source of the event.
70      */
71     public static final String EXTRA_CONTEXT_HUB_INFO =
72             "android.hardware.location.extra.CONTEXT_HUB_INFO";
73 
74     /**
75      * An extra of type {@link ContextHubManager.Event} describing the event type.
76      */
77     public static final String EXTRA_EVENT_TYPE = "android.hardware.location.extra.EVENT_TYPE";
78 
79     /**
80      * An extra of type long describing the ID of the nanoapp an event is for.
81      */
82     public static final String EXTRA_NANOAPP_ID = "android.hardware.location.extra.NANOAPP_ID";
83 
84     /**
85      * An extra of type int describing the nanoapp-specific abort code.
86      */
87     public static final String EXTRA_NANOAPP_ABORT_CODE =
88             "android.hardware.location.extra.NANOAPP_ABORT_CODE";
89 
90     /**
91      * An extra of type {@link NanoAppMessage} describing contents of a message from a nanoapp.
92      */
93     public static final String EXTRA_MESSAGE = "android.hardware.location.extra.MESSAGE";
94 
95     /**
96      * Constants describing if a {@link ContextHubClient} and a {@link NanoApp} are authorized to
97      * communicate.
98      *
99      * @hide
100      */
101     @Retention(RetentionPolicy.SOURCE)
102     @IntDef(prefix = { "AUTHORIZATION_" }, value = {
103         AUTHORIZATION_DENIED,
104         AUTHORIZATION_DENIED_GRACE_PERIOD,
105         AUTHORIZATION_GRANTED,
106     })
107     public @interface AuthorizationState { }
108 
109     /**
110      * Indicates that the {@link ContextHubClient} can no longer communicate with a nanoapp. If the
111      * {@link ContextHubClient} attempts to send messages to the nanoapp, it will continue to
112      * receive this authorization state if the connection is still closed.
113      */
114     public static final int AUTHORIZATION_DENIED = 0;
115 
116     /**
117      * Indicates the {@link ContextHubClient} will soon lose its authorization to communicate with a
118      * nanoapp. After receiving this state event, the {@link ContextHubClient} has one minute to
119      * perform any cleanup with the nanoapp such that the nanoapp is no longer performing work on
120      * behalf of the {@link ContextHubClient}.
121      */
122     public static final int AUTHORIZATION_DENIED_GRACE_PERIOD = 1;
123 
124     /**
125      * The {@link ContextHubClient} is authorized to communicate with the nanoapp.
126      */
127     public static final int AUTHORIZATION_GRANTED = 2;
128 
129     /**
130      * Constants describing the type of events from a Context Hub, as defined in
131      * {@link ContextHubClientCallback}.
132      * {@hide}
133      */
134     @Retention(RetentionPolicy.SOURCE)
135     @IntDef(prefix = { "EVENT_" }, value = {
136         EVENT_NANOAPP_LOADED,
137         EVENT_NANOAPP_UNLOADED,
138         EVENT_NANOAPP_ENABLED,
139         EVENT_NANOAPP_DISABLED,
140         EVENT_NANOAPP_ABORTED,
141         EVENT_NANOAPP_MESSAGE,
142         EVENT_HUB_RESET,
143         EVENT_CLIENT_AUTHORIZATION,
144     })
145     public @interface Event { }
146 
147     /**
148      * An event describing that a nanoapp has been loaded. Contains the EXTRA_NANOAPP_ID extra.
149      */
150     public static final int EVENT_NANOAPP_LOADED = 0;
151 
152     /**
153      * An event describing that a nanoapp has been unloaded. Contains the EXTRA_NANOAPP_ID extra.
154      */
155     public static final int EVENT_NANOAPP_UNLOADED = 1;
156 
157     /**
158      * An event describing that a nanoapp has been enabled. Contains the EXTRA_NANOAPP_ID extra.
159      */
160     public static final int EVENT_NANOAPP_ENABLED = 2;
161 
162     /**
163      * An event describing that a nanoapp has been disabled. Contains the EXTRA_NANOAPP_ID extra.
164      */
165     public static final int EVENT_NANOAPP_DISABLED = 3;
166 
167     /**
168      * An event describing that a nanoapp has aborted. Contains the EXTRA_NANOAPP_ID and
169      * EXTRA_NANOAPP_ABORT_CODE extras.
170      */
171     public static final int EVENT_NANOAPP_ABORTED = 4;
172 
173     /**
174      * An event containing a message sent from a nanoapp. Contains the EXTRA_NANOAPP_ID and
175      * EXTRA_NANOAPP_MESSAGE extras.
176      */
177     public static final int EVENT_NANOAPP_MESSAGE = 5;
178 
179     /**
180      * An event describing that the Context Hub has reset.
181      */
182     public static final int EVENT_HUB_RESET = 6;
183 
184     /**
185      * An event describing a client authorization state change. See
186      * {@link ContextHubClientCallback#onClientAuthorizationChanged} for more details on when this
187      * event will be sent. Contains the EXTRA_NANOAPP_ID and EXTRA_CLIENT_AUTHORIZATION_STATE
188      * extras.
189      */
190     public static final int EVENT_CLIENT_AUTHORIZATION = 7;
191 
192     private final Looper mMainLooper;
193     private final IContextHubService mService;
194     private Callback mCallback;
195     private Handler mCallbackHandler;
196 
197     /**
198      * @deprecated Use {@code mCallback} instead.
199      */
200     @Deprecated
201     private ICallback mLocalCallback;
202 
203     /**
204      * An interface to receive asynchronous communication from the context hub.
205      *
206      * @deprecated Use the more refined {@link android.hardware.location.ContextHubClientCallback}
207      *             instead for notification callbacks.
208      */
209     @Deprecated
210     public abstract static class Callback {
Callback()211         protected Callback() {}
212 
213         /**
214          * Callback function called on message receipt from context hub.
215          *
216          * @param hubHandle Handle (system-wide unique identifier) of the hub of the message.
217          * @param nanoAppHandle Handle (unique identifier) for app instance that sent the message.
218          * @param message The context hub message.
219          *
220          * @see ContextHubMessage
221          */
onMessageReceipt( int hubHandle, int nanoAppHandle, @NonNull ContextHubMessage message)222         public abstract void onMessageReceipt(
223                 int hubHandle,
224                 int nanoAppHandle,
225                 @NonNull ContextHubMessage message);
226     }
227 
228     /**
229      * @deprecated Use {@link Callback} instead.
230      * @hide
231      */
232     @Deprecated
233     public interface ICallback {
234         /**
235          * Callback function called on message receipt from context hub.
236          *
237          * @param hubHandle Handle (system-wide unique identifier) of the hub of the message.
238          * @param nanoAppHandle Handle (unique identifier) for app instance that sent the message.
239          * @param message The context hub message.
240          *
241          * @see ContextHubMessage
242          */
onMessageReceipt(int hubHandle, int nanoAppHandle, ContextHubMessage message)243         void onMessageReceipt(int hubHandle, int nanoAppHandle, ContextHubMessage message);
244     }
245 
246     /**
247      * Get a handle to all the context hubs in the system
248      *
249      * @return array of context hub handles
250      *
251      * @deprecated Use {@link #getContextHubs()} instead. The use of handles are deprecated in the
252      *             new APIs.
253      */
254     @Deprecated
255     @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
getContextHubHandles()256     public int[] getContextHubHandles() {
257         try {
258             return mService.getContextHubHandles();
259         } catch (RemoteException e) {
260             throw e.rethrowFromSystemServer();
261         }
262     }
263 
264     /**
265      * Get more information about a specific hub.
266      *
267      * @param hubHandle Handle (system-wide unique identifier) of a context hub.
268      * @return ContextHubInfo Information about the requested context hub.
269      *
270      * @see ContextHubInfo
271      *
272      * @deprecated Use {@link #getContextHubs()} instead. The use of handles are deprecated in the
273      *             new APIs.
274      */
275     @Deprecated
276     @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
getContextHubInfo(int hubHandle)277     public ContextHubInfo getContextHubInfo(int hubHandle) {
278         try {
279             return mService.getContextHubInfo(hubHandle);
280         } catch (RemoteException e) {
281             throw e.rethrowFromSystemServer();
282         }
283     }
284 
285     /**
286      * Load a nano app on a specified context hub.
287      *
288      * Note that loading is asynchronous.  When we return from this method,
289      * the nano app (probably) hasn't loaded yet.  Assuming a return of 0
290      * from this method, then the final success/failure for the load, along
291      * with the "handle" for the nanoapp, is all delivered in a byte
292      * string via a call to Callback.onMessageReceipt.
293      *
294      * TODO(b/30784270): Provide a better success/failure and "handle" delivery.
295      *
296      * @param hubHandle handle of context hub to load the app on.
297      * @param app the nanoApp to load on the hub
298      *
299      * @return 0 if the command for loading was sent to the context hub;
300      *         -1 otherwise
301      *
302      * @see NanoApp
303      *
304      * @deprecated Use {@link #loadNanoApp(ContextHubInfo, NanoAppBinary)} instead.
305      */
306     @Deprecated
307     @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
loadNanoApp(int hubHandle, @NonNull NanoApp app)308     public int loadNanoApp(int hubHandle, @NonNull NanoApp app) {
309         try {
310             return mService.loadNanoApp(hubHandle, app);
311         } catch (RemoteException e) {
312             throw e.rethrowFromSystemServer();
313         }
314     }
315 
316     /**
317      * Unload a specified nanoApp
318      *
319      * Note that unloading is asynchronous.  When we return from this method,
320      * the nano app (probably) hasn't unloaded yet.  Assuming a return of 0
321      * from this method, then the final success/failure for the unload is
322      * delivered in a byte string via a call to Callback.onMessageReceipt.
323      *
324      * TODO(b/30784270): Provide a better success/failure delivery.
325      *
326      * @param nanoAppHandle handle of the nanoApp to unload
327      *
328      * @return 0 if the command for unloading was sent to the context hub;
329      *         -1 otherwise
330      *
331      * @deprecated Use {@link #unloadNanoApp(ContextHubInfo, long)} instead.
332      */
333     @Deprecated
334     @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
unloadNanoApp(int nanoAppHandle)335     public int unloadNanoApp(int nanoAppHandle) {
336         try {
337             return mService.unloadNanoApp(nanoAppHandle);
338         } catch (RemoteException e) {
339             throw e.rethrowFromSystemServer();
340         }
341     }
342 
343     /**
344      * get information about the nano app instance
345      *
346      * NOTE: The returned NanoAppInstanceInfo does _not_ contain correct
347      * information for several fields, specifically:
348      * - getName()
349      * - getPublisher()
350      * - getNeededExecMemBytes()
351      * - getNeededReadMemBytes()
352      * - getNeededWriteMemBytes()
353      *
354      * For example, say you call loadNanoApp() with a NanoApp that has
355      * getName() returning "My Name".  Later, if you call getNanoAppInstanceInfo
356      * for that nanoapp, the returned NanoAppInstanceInfo's getName()
357      * method will claim "Preloaded app, unknown", even though you would
358      * have expected "My Name".  For now, as the user, you'll need to
359      * separately track the above fields if they are of interest to you.
360      *
361      * TODO(b/30943489): Have the returned NanoAppInstanceInfo contain the
362      *     correct information.
363      *
364      * @param nanoAppHandle handle of the nanoapp instance
365      * @return NanoAppInstanceInfo the NanoAppInstanceInfo of the nanoapp, or null if the nanoapp
366      *                             does not exist
367      *
368      * @see NanoAppInstanceInfo
369      *
370      * @deprecated Use {@link #queryNanoApps(ContextHubInfo)} instead to explicitly query the hub
371      *             for loaded nanoapps.
372      */
373     @Deprecated
374     @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
getNanoAppInstanceInfo(int nanoAppHandle)375     @Nullable public NanoAppInstanceInfo getNanoAppInstanceInfo(int nanoAppHandle) {
376         try {
377             return mService.getNanoAppInstanceInfo(nanoAppHandle);
378         } catch (RemoteException e) {
379             throw e.rethrowFromSystemServer();
380         }
381     }
382 
383     /**
384      * Find a specified nano app on the system
385      *
386      * @param hubHandle handle of hub to search for nano app
387      * @param filter filter specifying the search criteria for app
388      *
389      * @see NanoAppFilter
390      *
391      * @return int[] Array of handles to any found nano apps
392      *
393      * @deprecated Use {@link #queryNanoApps(ContextHubInfo)} instead to explicitly query the hub
394      *             for loaded nanoapps.
395      */
396     @Deprecated
397     @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
findNanoAppOnHub(int hubHandle, @NonNull NanoAppFilter filter)398     @NonNull public int[] findNanoAppOnHub(int hubHandle, @NonNull NanoAppFilter filter) {
399         try {
400             return mService.findNanoAppOnHub(hubHandle, filter);
401         } catch (RemoteException e) {
402             throw e.rethrowFromSystemServer();
403         }
404     }
405 
406     /**
407      * Send a message to a specific nano app instance on a context hub.
408      *
409      * Note that the return value of this method only speaks of success
410      * up to the point of sending this to the Context Hub.  It is not
411      * an assurance that the Context Hub successfully sent this message
412      * on to the nanoapp.  If assurance is desired, a protocol should be
413      * established between your code and the nanoapp, with the nanoapp
414      * sending a confirmation message (which will be reported via
415      * Callback.onMessageReceipt).
416      *
417      * @param hubHandle handle of the hub to send the message to
418      * @param nanoAppHandle  handle of the nano app to send to
419      * @param message Message to be sent
420      *
421      * @see ContextHubMessage
422      *
423      * @return int 0 on success, -1 otherwise
424      *
425      * @deprecated Use {@link android.hardware.location.ContextHubClient#sendMessageToNanoApp(
426      *             NanoAppMessage)} instead, after creating a
427      *             {@link android.hardware.location.ContextHubClient} with
428      *             {@link #createClient(ContextHubInfo, ContextHubClientCallback, Executor)}
429      *             or {@link #createClient(ContextHubInfo, ContextHubClientCallback)}.
430      */
431     @Deprecated
432     @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
sendMessage(int hubHandle, int nanoAppHandle, @NonNull ContextHubMessage message)433     public int sendMessage(int hubHandle, int nanoAppHandle, @NonNull ContextHubMessage message) {
434         try {
435             return mService.sendMessage(hubHandle, nanoAppHandle, message);
436         } catch (RemoteException e) {
437             throw e.rethrowFromSystemServer();
438         }
439     }
440 
441     /**
442      * Returns the list of ContextHubInfo objects describing the available Context Hubs.
443      *
444      * @return the list of ContextHubInfo objects
445      *
446      * @see ContextHubInfo
447      */
448     @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
getContextHubs()449     @NonNull public List<ContextHubInfo> getContextHubs() {
450         try {
451             return mService.getContextHubs();
452         } catch (RemoteException e) {
453             throw e.rethrowFromSystemServer();
454         }
455     }
456 
457     /**
458      * Helper function to generate a stub for a non-query transaction callback.
459      *
460      * @param transaction the transaction to unblock when complete
461      *
462      * @return the callback
463      *
464      * @hide
465      */
createTransactionCallback( ContextHubTransaction<Void> transaction)466     private IContextHubTransactionCallback createTransactionCallback(
467             ContextHubTransaction<Void> transaction) {
468         return new IContextHubTransactionCallback.Stub() {
469             @Override
470             public void onQueryResponse(int result, List<NanoAppState> nanoappList) {
471                 Log.e(TAG, "Received a query callback on a non-query request");
472                 transaction.setResponse(new ContextHubTransaction.Response<Void>(
473                         ContextHubTransaction.RESULT_FAILED_SERVICE_INTERNAL_FAILURE, null));
474             }
475 
476             @Override
477             public void onTransactionComplete(int result) {
478                 transaction.setResponse(new ContextHubTransaction.Response<Void>(result, null));
479             }
480         };
481     }
482 
483    /**
484     * Helper function to generate a stub for a query transaction callback.
485     *
486     * @param transaction the transaction to unblock when complete
487     *
488     * @return the callback
489     *
490     * @hide
491     */
492     private IContextHubTransactionCallback createQueryCallback(
493             ContextHubTransaction<List<NanoAppState>> transaction) {
494         return new IContextHubTransactionCallback.Stub() {
495             @Override
496             public void onQueryResponse(int result, List<NanoAppState> nanoappList) {
497                 transaction.setResponse(new ContextHubTransaction.Response<List<NanoAppState>>(
498                         result, nanoappList));
499             }
500 
501             @Override
502             public void onTransactionComplete(int result) {
503                 Log.e(TAG, "Received a non-query callback on a query request");
504                 transaction.setResponse(new ContextHubTransaction.Response<List<NanoAppState>>(
505                         ContextHubTransaction.RESULT_FAILED_SERVICE_INTERNAL_FAILURE, null));
506             }
507         };
508     }
509 
510     /**
511      * Loads a nanoapp at the specified Context Hub.
512      *
513      * After the nanoapp binary is successfully loaded at the specified hub, the nanoapp will be in
514      * the enabled state.
515      *
516      * @param hubInfo the hub to load the nanoapp on
517      * @param appBinary The app binary to load
518      *
519      * @return the ContextHubTransaction of the request
520      *
521      * @throws NullPointerException if hubInfo or NanoAppBinary is null
522      *
523      * @see NanoAppBinary
524      */
525     @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
526     @NonNull public ContextHubTransaction<Void> loadNanoApp(
527             @NonNull ContextHubInfo hubInfo, @NonNull NanoAppBinary appBinary) {
528         Objects.requireNonNull(hubInfo, "ContextHubInfo cannot be null");
529         Objects.requireNonNull(appBinary, "NanoAppBinary cannot be null");
530 
531         ContextHubTransaction<Void> transaction =
532                 new ContextHubTransaction<>(ContextHubTransaction.TYPE_LOAD_NANOAPP);
533         IContextHubTransactionCallback callback = createTransactionCallback(transaction);
534 
535         try {
536             mService.loadNanoAppOnHub(hubInfo.getId(), callback, appBinary);
537         } catch (RemoteException e) {
538             throw e.rethrowFromSystemServer();
539         }
540 
541         return transaction;
542     }
543 
544     /**
545      * Unloads a nanoapp at the specified Context Hub.
546      *
547      * @param hubInfo the hub to unload the nanoapp from
548      * @param nanoAppId the app to unload
549      *
550      * @return the ContextHubTransaction of the request
551      *
552      * @throws NullPointerException if hubInfo is null
553      */
554     @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
555     @NonNull public ContextHubTransaction<Void> unloadNanoApp(
556             @NonNull ContextHubInfo hubInfo, long nanoAppId) {
557         Objects.requireNonNull(hubInfo, "ContextHubInfo cannot be null");
558 
559         ContextHubTransaction<Void> transaction =
560                 new ContextHubTransaction<>(ContextHubTransaction.TYPE_UNLOAD_NANOAPP);
561         IContextHubTransactionCallback callback = createTransactionCallback(transaction);
562 
563         try {
564             mService.unloadNanoAppFromHub(hubInfo.getId(), callback, nanoAppId);
565         } catch (RemoteException e) {
566             throw e.rethrowFromSystemServer();
567         }
568 
569         return transaction;
570     }
571 
572     /**
573      * Enables a nanoapp at the specified Context Hub.
574      *
575      * @param hubInfo the hub to enable the nanoapp on
576      * @param nanoAppId the app to enable
577      *
578      * @return the ContextHubTransaction of the request
579      *
580      * @throws NullPointerException if hubInfo is null
581      */
582     @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
583     @NonNull public ContextHubTransaction<Void> enableNanoApp(
584             @NonNull ContextHubInfo hubInfo, long nanoAppId) {
585         Objects.requireNonNull(hubInfo, "ContextHubInfo cannot be null");
586 
587         ContextHubTransaction<Void> transaction =
588                 new ContextHubTransaction<>(ContextHubTransaction.TYPE_ENABLE_NANOAPP);
589         IContextHubTransactionCallback callback = createTransactionCallback(transaction);
590 
591         try {
592             mService.enableNanoApp(hubInfo.getId(), callback, nanoAppId);
593         } catch (RemoteException e) {
594             throw e.rethrowFromSystemServer();
595         }
596 
597         return transaction;
598     }
599 
600     /**
601      * Disables a nanoapp at the specified Context Hub.
602      *
603      * @param hubInfo the hub to disable the nanoapp on
604      * @param nanoAppId the app to disable
605      *
606      * @return the ContextHubTransaction of the request
607      *
608      * @throws NullPointerException if hubInfo is null
609      */
610     @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
611     @NonNull public ContextHubTransaction<Void> disableNanoApp(
612             @NonNull ContextHubInfo hubInfo, long nanoAppId) {
613         Objects.requireNonNull(hubInfo, "ContextHubInfo cannot be null");
614 
615         ContextHubTransaction<Void> transaction =
616                 new ContextHubTransaction<>(ContextHubTransaction.TYPE_DISABLE_NANOAPP);
617         IContextHubTransactionCallback callback = createTransactionCallback(transaction);
618 
619         try {
620             mService.disableNanoApp(hubInfo.getId(), callback, nanoAppId);
621         } catch (RemoteException e) {
622             throw e.rethrowFromSystemServer();
623         }
624 
625         return transaction;
626     }
627 
628     /**
629      * Requests a query for nanoapps loaded at the specified Context Hub.
630      *
631      * @param hubInfo the hub to query a list of nanoapps from
632      *
633      * @return the ContextHubTransaction of the request
634      *
635      * @throws NullPointerException if hubInfo is null
636      */
637     @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
638     @NonNull public ContextHubTransaction<List<NanoAppState>> queryNanoApps(
639             @NonNull ContextHubInfo hubInfo) {
640         Objects.requireNonNull(hubInfo, "ContextHubInfo cannot be null");
641 
642         ContextHubTransaction<List<NanoAppState>> transaction =
643                 new ContextHubTransaction<>(ContextHubTransaction.TYPE_QUERY_NANOAPPS);
644         IContextHubTransactionCallback callback = createQueryCallback(transaction);
645 
646         try {
647             mService.queryNanoApps(hubInfo.getId(), callback);
648         } catch (RemoteException e) {
649             throw e.rethrowFromSystemServer();
650         }
651 
652         return transaction;
653     }
654 
655     /**
656      * Set a callback to receive messages from the context hub
657      *
658      * @param callback Callback object
659      *
660      * @see Callback
661      *
662      * @return int 0 on success, -1 otherwise
663      *
664      * @deprecated Use {@link #createClient(ContextHubInfo, ContextHubClientCallback, Executor)}
665      *             or {@link #createClient(ContextHubInfo, ContextHubClientCallback)} instead to
666      *             register a {@link android.hardware.location.ContextHubClientCallback}.
667      */
668     @Deprecated
669     @SuppressLint("RequiresPermission")
670     public int registerCallback(@NonNull Callback callback) {
671         return registerCallback(callback, null);
672     }
673 
674     /**
675      * @deprecated Use {@link #registerCallback(Callback)} instead.
676      * @hide
677      */
678     @Deprecated
679     public int registerCallback(ICallback callback) {
680         if (mLocalCallback != null) {
681             Log.w(TAG, "Max number of local callbacks reached!");
682             return -1;
683         }
684         mLocalCallback = callback;
685         return 0;
686     }
687 
688     /**
689      * Set a callback to receive messages from the context hub
690      *
691      * @param callback Callback object
692      * @param handler Handler object, if null uses the Handler of the main Looper
693      *
694      * @see Callback
695      *
696      * @return int 0 on success, -1 otherwise
697      *
698      * @deprecated Use {@link #createClient(ContextHubInfo, ContextHubClientCallback, Executor)}
699      *             or {@link #createClient(ContextHubInfo, ContextHubClientCallback)} instead to
700      *             register a {@link android.hardware.location.ContextHubClientCallback}.
701      */
702     @Deprecated
703     @SuppressLint("RequiresPermission")
704     public int registerCallback(Callback callback, Handler handler) {
705         synchronized(this) {
706             if (mCallback != null) {
707                 Log.w(TAG, "Max number of callbacks reached!");
708                 return -1;
709             }
710             mCallback = callback;
711             mCallbackHandler = (handler == null) ? new Handler(mMainLooper) : handler;
712         }
713         return 0;
714     }
715 
716     /**
717      * Creates an interface to the ContextHubClient to send down to the service.
718      *
719      * @param client the ContextHubClient object associated with this callback
720      * @param callback the callback to invoke at the client process
721      * @param executor the executor to invoke callbacks for this client
722      *
723      * @return the callback interface
724      */
725     private IContextHubClientCallback createClientCallback(
726             ContextHubClient client, ContextHubClientCallback callback, Executor executor) {
727         return new IContextHubClientCallback.Stub() {
728             @Override
729             public void onMessageFromNanoApp(NanoAppMessage message) {
730                 executor.execute(
731                         () -> {
732                             callback.onMessageFromNanoApp(client, message);
733                             client.callbackFinished();
734                         });
735             }
736 
737             @Override
738             public void onHubReset() {
739                 executor.execute(
740                         () -> {
741                             callback.onHubReset(client);
742                             client.callbackFinished();
743                         });
744             }
745 
746             @Override
747             public void onNanoAppAborted(long nanoAppId, int abortCode) {
748                 executor.execute(
749                         () -> {
750                             callback.onNanoAppAborted(client, nanoAppId, abortCode);
751                             client.callbackFinished();
752                         });
753             }
754 
755             @Override
756             public void onNanoAppLoaded(long nanoAppId) {
757                 executor.execute(
758                         () -> {
759                             callback.onNanoAppLoaded(client, nanoAppId);
760                             client.callbackFinished();
761                         });
762             }
763 
764             @Override
765             public void onNanoAppUnloaded(long nanoAppId) {
766                 executor.execute(
767                         () -> {
768                             callback.onNanoAppUnloaded(client, nanoAppId);
769                             client.callbackFinished();
770                         });
771             }
772 
773             @Override
774             public void onNanoAppEnabled(long nanoAppId) {
775                 executor.execute(
776                         () -> {
777                             callback.onNanoAppEnabled(client, nanoAppId);
778                             client.callbackFinished();
779                         });
780             }
781 
782             @Override
783             public void onNanoAppDisabled(long nanoAppId) {
784                 executor.execute(
785                         () -> {
786                             callback.onNanoAppDisabled(client, nanoAppId);
787                             client.callbackFinished();
788                         });
789             }
790 
791             @Override
792             public void onClientAuthorizationChanged(
793                     long nanoAppId, @ContextHubManager.AuthorizationState int authorization) {
794                 executor.execute(
795                         () -> {
796                             callback.onClientAuthorizationChanged(client, nanoAppId, authorization);
797                             client.callbackFinished();
798                         });
799             }
800         };
801     }
802 
803     /**
804      * Creates and registers a client and its callback with the Context Hub Service.
805      *
806      * A client is registered with the Context Hub Service for a specified Context Hub. When the
807      * registration succeeds, the client can send messages to nanoapps through the returned
808      * {@link ContextHubClient} object, and receive notifications through the provided callback.
809      *
810      * @param context  the context of the application
811      * @param hubInfo  the hub to attach this client to
812      * @param executor the executor to invoke the callback
813      * @param callback the notification callback to register
814      * @return the registered client object
815      *
816      * @throws IllegalArgumentException if hubInfo does not represent a valid hub
817      * @throws IllegalStateException    if there were too many registered clients at the service
818      * @throws NullPointerException     if callback, hubInfo, or executor is null
819      *
820      * @see ContextHubClientCallback
821      */
822     @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
823     @NonNull public ContextHubClient createClient(
824             @Nullable Context context, @NonNull ContextHubInfo hubInfo,
825             @NonNull @CallbackExecutor Executor executor,
826             @NonNull ContextHubClientCallback callback) {
827         Objects.requireNonNull(callback, "Callback cannot be null");
828         Objects.requireNonNull(hubInfo, "ContextHubInfo cannot be null");
829         Objects.requireNonNull(executor, "Executor cannot be null");
830 
831         ContextHubClient client = new ContextHubClient(hubInfo, false /* persistent */);
832         IContextHubClientCallback clientInterface = createClientCallback(
833                 client, callback, executor);
834 
835         String attributionTag = null;
836         if (context != null) {
837             attributionTag = context.getAttributionTag();
838         }
839 
840         // Workaround for old APIs not providing a context
841         String packageName;
842         if (context != null) {
843             packageName = context.getPackageName();
844         } else {
845             packageName = ActivityThread.currentPackageName();
846         }
847 
848         IContextHubClient clientProxy;
849         try {
850             clientProxy = mService.createClient(
851                     hubInfo.getId(), clientInterface, attributionTag, packageName);
852         } catch (RemoteException e) {
853             throw e.rethrowFromSystemServer();
854         }
855 
856         client.setClientProxy(clientProxy);
857         return client;
858     }
859 
860 
861     /**
862      * Equivalent to
863      * {@link  #createClient(Context, ContextHubInfo, Executor, ContextHubClientCallback)}
864      * with the {@link Context} being set to null.
865      */
866     @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
867     @NonNull public ContextHubClient createClient(
868             @NonNull ContextHubInfo hubInfo, @NonNull ContextHubClientCallback callback,
869             @NonNull @CallbackExecutor Executor executor) {
870         return createClient(null /* context */, hubInfo, executor, callback);
871     }
872 
873     /**
874      * Equivalent to {@link #createClient(ContextHubInfo, ContextHubClientCallback, Executor)}
875      * with the executor using the main thread's Looper.
876      */
877     @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
878     @NonNull public ContextHubClient createClient(
879             @NonNull ContextHubInfo hubInfo, @NonNull ContextHubClientCallback callback) {
880         return createClient(null /* context */, hubInfo, new HandlerExecutor(Handler.getMain()),
881                             callback);
882     }
883 
884     /**
885      * Creates a ContextHubClient that will receive notifications based on Intent events.
886      *
887      * This method should be used instead of {@link #createClient(ContextHubInfo,
888      * ContextHubClientCallback)} or {@link #createClient(ContextHubInfo, ContextHubClientCallback,
889      * Executor)} if the caller wants to preserve the messaging endpoint of a ContextHubClient, even
890      * after a process exits. If the PendingIntent with the provided nanoapp has already been
891      * registered at the service, then the same ContextHubClient will be regenerated without
892      * creating a new client connection at the service. Note that the PendingIntent, nanoapp, and
893      * Context Hub must all match in identifying a previously registered ContextHubClient.
894      * If a client is regenerated, the host endpoint identifier attached to messages sent to the
895      * nanoapp remains consistent, even if the original process has exited.
896      *
897      * To avoid unintentionally spreading data from the Context Hub to external applications, it is
898      * strongly recommended that the PendingIntent supplied to this API is an explicit intent.
899      *
900      * If registered successfully, intents will be delivered regarding events or messages from the
901      * specified nanoapp from the attached Context Hub. The intent will have an extra
902      * {@link ContextHubManager.EXTRA_CONTEXT_HUB_INFO} of type {@link ContextHubInfo}, which
903      * describes the Context Hub the intent event was for. The intent will also have an extra
904      * {@link ContextHubManager.EXTRA_EVENT_TYPE} of type {@link ContextHubManager.Event}, which
905      * will contain the type of the event. See {@link ContextHubManager.Event} for description of
906      * each event type, along with event-specific extra fields. The client can also use
907      * {@link ContextHubIntentEvent.fromIntent(Intent)} to parse the Intent generated by the event.
908      *
909      * Intent events will be delivered until {@link ContextHubClient.close()} is called. Note that
910      * the registration of this ContextHubClient at the Context Hub Service will be maintained until
911      * {@link ContextHubClient.close()} is called. If {@link PendingIntent.cancel()} is called
912      * on the provided PendingIntent, then the client will be automatically unregistered by the
913      * service.
914      *
915      * Note that the {@link PendingIntent} supplied to this API must be mutable for Intent
916      * notifications to work.
917      *
918      * @param context       the context of the application. If a PendingIntent client is recreated,
919      * the latest state in the context will be used and old state will be discarded
920      * @param hubInfo       the hub to attach this client to
921      * @param pendingIntent the PendingIntent to register to the client
922      * @param nanoAppId     the ID of the nanoapp that Intent events will be generated for
923      * @return the registered client object
924      *
925      * @throws IllegalArgumentException if hubInfo does not represent a valid hub, or an immutable
926      *                                  PendingIntent was supplied
927      * @throws IllegalStateException    if there were too many registered clients at the service
928      * @throws NullPointerException     if pendingIntent or hubInfo is null
929      */
930     @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
931     @NonNull public ContextHubClient createClient(
932             @Nullable Context context, @NonNull ContextHubInfo hubInfo,
933             @NonNull PendingIntent pendingIntent, long nanoAppId) {
934         Objects.requireNonNull(pendingIntent);
935         Objects.requireNonNull(hubInfo);
936         if (pendingIntent.isImmutable()) {
937             throw new IllegalArgumentException("PendingIntent must be mutable");
938         }
939 
940         ContextHubClient client = new ContextHubClient(hubInfo, true /* persistent */);
941 
942         String attributionTag = null;
943         if (context != null) {
944             attributionTag = context.getAttributionTag();
945         }
946 
947         IContextHubClient clientProxy;
948         try {
949             clientProxy = mService.createPendingIntentClient(
950                     hubInfo.getId(), pendingIntent, nanoAppId, attributionTag);
951         } catch (RemoteException e) {
952             throw e.rethrowFromSystemServer();
953         }
954 
955         client.setClientProxy(clientProxy);
956         return client;
957     }
958 
959     /**
960      * Equivalent to {@link #createClient(ContextHubInfo, PendingIntent, long, String)}
961      * with {@link Context} being set to null.
962      */
963     @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
964     @NonNull public ContextHubClient createClient(
965             @NonNull ContextHubInfo hubInfo, @NonNull PendingIntent pendingIntent, long nanoAppId) {
966         return createClient(null /* context */, hubInfo, pendingIntent, nanoAppId);
967     }
968 
969     /**
970      * Queries for the list of preloaded nanoapp IDs on the system.
971      *
972      * @param hubInfo The Context Hub to query a list of nanoapp IDs from.
973      *
974      * @return The list of 64-bit IDs of the preloaded nanoapps.
975      *
976      * @throws NullPointerException if hubInfo is null
977      * @hide
978      */
979     @TestApi
980     @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
981     @NonNull public long[] getPreloadedNanoAppIds(@NonNull ContextHubInfo hubInfo) {
982         Objects.requireNonNull(hubInfo, "hubInfo cannot be null");
983 
984         long[] nanoappIds = null;
985         try {
986             nanoappIds = mService.getPreloadedNanoAppIds(hubInfo);
987         } catch (RemoteException e) {
988             throw e.rethrowFromSystemServer();
989         }
990 
991         if (nanoappIds == null) {
992             nanoappIds = new long[0];
993         }
994         return nanoappIds;
995     }
996 
997     /**
998      * Puts the Context Hub in test mode.
999      *
1000      * The purpose of this API is to make testing CHRE/Context Hub more
1001      * predictable and robust. This temporarily unloads all
1002      * nanoapps.
1003      *
1004      * Note that this API must not cause CHRE/Context Hub to behave differently
1005      * in test compared to production.
1006      *
1007      * @return true if the enable test mode operation succeeded.
1008      * @hide
1009      */
1010     @TestApi
1011     @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
1012     @NonNull public boolean enableTestMode() {
1013         try {
1014             return mService.setTestMode(true);
1015         } catch (RemoteException e) {
1016             throw e.rethrowFromSystemServer();
1017         }
1018     }
1019 
1020     /**
1021      * Puts the Context Hub out of test mode.
1022      *
1023      * This API will undo any previously made enableTestMode() calls.
1024      * After this API is called, it should restore the state of the system
1025      * to the normal/production mode before any enableTestMode() call was
1026      * made. If the enableTestMode() call unloaded any nanoapps
1027      * to enter test mode, it should reload those nanoapps in this API call.
1028      *
1029      * @return true if the disable operation succeeded.
1030      * @hide
1031      */
1032     @TestApi
1033     @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
1034     @NonNull public boolean disableTestMode() {
1035         try {
1036             return mService.setTestMode(false);
1037         } catch (RemoteException e) {
1038             throw e.rethrowFromSystemServer();
1039         }
1040     }
1041 
1042     /**
1043      * Unregister a callback for receive messages from the context hub.
1044      *
1045      * @see Callback
1046      *
1047      * @param callback method to deregister
1048      *
1049      * @return int 0 on success, -1 otherwise
1050      *
1051      * @deprecated Use {@link android.hardware.location.ContextHubClient#close()} to unregister
1052      *             a {@link android.hardware.location.ContextHubClientCallback}.
1053      */
1054     @SuppressLint("RequiresPermission")
1055     @Deprecated
1056     public int unregisterCallback(@NonNull Callback callback) {
1057       synchronized(this) {
1058           if (callback != mCallback) {
1059               Log.w(TAG, "Cannot recognize callback!");
1060               return -1;
1061           }
1062 
1063           mCallback = null;
1064           mCallbackHandler = null;
1065       }
1066       return 0;
1067     }
1068 
1069     /**
1070      * @deprecated Use {@link #unregisterCallback(Callback)} instead.
1071      * @hide
1072      */
1073     @Deprecated
1074     public synchronized int unregisterCallback(ICallback callback) {
1075         if (callback != mLocalCallback) {
1076             Log.w(TAG, "Cannot recognize local callback!");
1077             return -1;
1078         }
1079         mLocalCallback = null;
1080         return 0;
1081     }
1082 
1083     /**
1084      * Invokes the ContextHubManager.Callback callback registered with the ContextHubManager.
1085      *
1086      * @param hubId The ID of the Context Hub the message came from
1087      * @param nanoAppId The instance ID of the nanoapp the message came from
1088      * @param message The message to provide the callback
1089      */
1090     private synchronized void invokeOnMessageReceiptCallback(
1091             int hubId, int nanoAppId, ContextHubMessage message) {
1092         if (mCallback != null) {
1093             mCallback.onMessageReceipt(hubId, nanoAppId, message);
1094         }
1095     }
1096 
1097     private final IContextHubCallback.Stub mClientCallback = new IContextHubCallback.Stub() {
1098         @Override
1099         public void onMessageReceipt(
1100                 final int hubId, final int nanoAppId, final ContextHubMessage message) {
1101             synchronized (ContextHubManager.this) {
1102                 if (mCallback != null) {
1103                     mCallbackHandler.post(
1104                             () -> invokeOnMessageReceiptCallback(hubId, nanoAppId, message));
1105                 } else if (mLocalCallback != null) {
1106                     // We always ensure that mCallback takes precedence, because mLocalCallback is
1107                     // only for internal compatibility
1108                     mLocalCallback.onMessageReceipt(hubId, nanoAppId, message);
1109                 }
1110             }
1111         }
1112     };
1113 
1114     /** @throws ServiceNotFoundException
1115      * @hide */
1116     public ContextHubManager(Context context, Looper mainLooper) throws ServiceNotFoundException {
1117         mMainLooper = mainLooper;
1118         mService = IContextHubService.Stub.asInterface(
1119                 ServiceManager.getServiceOrThrow(Context.CONTEXTHUB_SERVICE));
1120         try {
1121             mService.registerCallback(mClientCallback);
1122         } catch (RemoteException e) {
1123             throw e.rethrowFromSystemServer();
1124         }
1125     }
1126 }
1127