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