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