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