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