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