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.FlaggedApi; 22 import android.annotation.IntDef; 23 import android.annotation.NonNull; 24 import android.annotation.Nullable; 25 import android.annotation.RequiresFeature; 26 import android.annotation.RequiresPermission; 27 import android.annotation.SuppressLint; 28 import android.annotation.SystemApi; 29 import android.annotation.SystemService; 30 import android.annotation.TestApi; 31 import android.app.ActivityThread; 32 import android.app.PendingIntent; 33 import android.chre.flags.Flags; 34 import android.content.Context; 35 import android.content.pm.PackageManager; 36 import android.hardware.contexthub.ErrorCode; 37 import android.hardware.contexthub.HubDiscoveryInfo; 38 import android.hardware.contexthub.HubEndpoint; 39 import android.hardware.contexthub.HubEndpointDiscoveryCallback; 40 import android.hardware.contexthub.HubEndpointInfo; 41 import android.hardware.contexthub.HubEndpointLifecycleCallback; 42 import android.hardware.contexthub.HubServiceInfo; 43 import android.hardware.contexthub.IContextHubEndpointDiscoveryCallback; 44 import android.os.Handler; 45 import android.os.HandlerExecutor; 46 import android.os.Looper; 47 import android.os.RemoteException; 48 import android.util.Log; 49 50 import java.lang.annotation.Retention; 51 import java.lang.annotation.RetentionPolicy; 52 import java.util.ArrayList; 53 import java.util.List; 54 import java.util.Map; 55 import java.util.Objects; 56 import java.util.concurrent.ConcurrentHashMap; 57 import java.util.concurrent.Executor; 58 59 /** 60 * A class that exposes the Context hubs on a device to applications. 61 * 62 * Please note that this class is not expected to be used by unbundled applications. Also, calling 63 * applications are expected to have the ACCESS_CONTEXT_HUB permission to use this class. 64 * 65 * @hide 66 */ 67 @SystemApi 68 @SystemService(Context.CONTEXTHUB_SERVICE) 69 @RequiresFeature(PackageManager.FEATURE_CONTEXT_HUB) 70 public final class ContextHubManager { 71 private static final String TAG = "ContextHubManager"; 72 73 /** 74 * An extra containing one of the {@code AUTHORIZATION_*} constants such as 75 * {@link #AUTHORIZATION_GRANTED} describing the client's authorization state. 76 */ 77 public static final String EXTRA_CLIENT_AUTHORIZATION_STATE = 78 "android.hardware.location.extra.CLIENT_AUTHORIZATION_STATE"; 79 80 /** 81 * An extra of type {@link ContextHubInfo} describing the source of the event. 82 */ 83 public static final String EXTRA_CONTEXT_HUB_INFO = 84 "android.hardware.location.extra.CONTEXT_HUB_INFO"; 85 86 /** 87 * An extra of type {@link ContextHubManager.Event} describing the event type. 88 */ 89 public static final String EXTRA_EVENT_TYPE = "android.hardware.location.extra.EVENT_TYPE"; 90 91 /** 92 * An extra of type long describing the ID of the nanoapp an event is for. 93 */ 94 public static final String EXTRA_NANOAPP_ID = "android.hardware.location.extra.NANOAPP_ID"; 95 96 /** 97 * An extra of type int describing the nanoapp-specific abort code. 98 */ 99 public static final String EXTRA_NANOAPP_ABORT_CODE = 100 "android.hardware.location.extra.NANOAPP_ABORT_CODE"; 101 102 /** 103 * An extra of type {@link NanoAppMessage} describing contents of a message from a nanoapp. 104 */ 105 public static final String EXTRA_MESSAGE = "android.hardware.location.extra.MESSAGE"; 106 107 /** 108 * Constants describing if a {@link ContextHubClient} and a {@link NanoApp} are authorized to 109 * communicate. 110 * 111 * @hide 112 */ 113 @Retention(RetentionPolicy.SOURCE) 114 @IntDef(prefix = { "AUTHORIZATION_" }, value = { 115 AUTHORIZATION_DENIED, 116 AUTHORIZATION_DENIED_GRACE_PERIOD, 117 AUTHORIZATION_GRANTED, 118 }) 119 public @interface AuthorizationState { } 120 121 /** 122 * Indicates that the {@link ContextHubClient} can no longer communicate with a nanoapp. If the 123 * {@link ContextHubClient} attempts to send messages to the nanoapp, it will continue to 124 * receive this authorization state if the connection is still closed. 125 */ 126 public static final int AUTHORIZATION_DENIED = 0; 127 128 /** 129 * Indicates the {@link ContextHubClient} will soon lose its authorization to communicate with a 130 * nanoapp. After receiving this state event, the {@link ContextHubClient} has one minute to 131 * perform any cleanup with the nanoapp such that the nanoapp is no longer performing work on 132 * behalf of the {@link ContextHubClient}. 133 */ 134 public static final int AUTHORIZATION_DENIED_GRACE_PERIOD = 1; 135 136 /** 137 * The {@link ContextHubClient} is authorized to communicate with the nanoapp. 138 */ 139 public static final int AUTHORIZATION_GRANTED = 2; 140 141 /** 142 * Constants describing the type of events from a Context Hub, as defined in 143 * {@link ContextHubClientCallback}. 144 * {@hide} 145 */ 146 @Retention(RetentionPolicy.SOURCE) 147 @IntDef(prefix = { "EVENT_" }, value = { 148 EVENT_NANOAPP_LOADED, 149 EVENT_NANOAPP_UNLOADED, 150 EVENT_NANOAPP_ENABLED, 151 EVENT_NANOAPP_DISABLED, 152 EVENT_NANOAPP_ABORTED, 153 EVENT_NANOAPP_MESSAGE, 154 EVENT_HUB_RESET, 155 EVENT_CLIENT_AUTHORIZATION, 156 }) 157 public @interface Event { } 158 159 /** 160 * An event describing that a nanoapp has been loaded. Contains the EXTRA_NANOAPP_ID extra. 161 */ 162 public static final int EVENT_NANOAPP_LOADED = 0; 163 164 /** 165 * An event describing that a nanoapp has been unloaded. Contains the EXTRA_NANOAPP_ID extra. 166 */ 167 public static final int EVENT_NANOAPP_UNLOADED = 1; 168 169 /** 170 * An event describing that a nanoapp has been enabled. Contains the EXTRA_NANOAPP_ID extra. 171 */ 172 public static final int EVENT_NANOAPP_ENABLED = 2; 173 174 /** 175 * An event describing that a nanoapp has been disabled. Contains the EXTRA_NANOAPP_ID extra. 176 */ 177 public static final int EVENT_NANOAPP_DISABLED = 3; 178 179 /** 180 * An event describing that a nanoapp has aborted. Contains the EXTRA_NANOAPP_ID and 181 * EXTRA_NANOAPP_ABORT_CODE extras. 182 */ 183 public static final int EVENT_NANOAPP_ABORTED = 4; 184 185 /** 186 * An event containing a message sent from a nanoapp. Contains the EXTRA_NANOAPP_ID and 187 * EXTRA_NANOAPP_MESSAGE extras. 188 */ 189 public static final int EVENT_NANOAPP_MESSAGE = 5; 190 191 /** 192 * An event describing that the Context Hub has reset. 193 */ 194 public static final int EVENT_HUB_RESET = 6; 195 196 /** 197 * An event describing a client authorization state change. See 198 * {@link ContextHubClientCallback#onClientAuthorizationChanged} for more details on when this 199 * event will be sent. Contains the EXTRA_NANOAPP_ID and EXTRA_CLIENT_AUTHORIZATION_STATE 200 * extras. 201 */ 202 public static final int EVENT_CLIENT_AUTHORIZATION = 7; 203 204 private final Looper mMainLooper; 205 private final IContextHubService mService; 206 private Callback mCallback; 207 private Handler mCallbackHandler; 208 209 /** A map of endpoint discovery callbacks currently registered */ 210 private Map<HubEndpointDiscoveryCallback, IContextHubEndpointDiscoveryCallback> 211 mDiscoveryCallbacks = new ConcurrentHashMap<>(); 212 213 /** 214 * @deprecated Use {@code mCallback} instead. 215 */ 216 @Deprecated 217 private ICallback mLocalCallback; 218 219 /** 220 * An interface to receive asynchronous communication from the context hub. 221 * 222 * @deprecated Use the more refined {@link android.hardware.location.ContextHubClientCallback} 223 * instead for notification callbacks. 224 */ 225 @Deprecated 226 public abstract static class Callback { Callback()227 protected Callback() {} 228 229 /** 230 * Callback function called on message receipt from context hub. 231 * 232 * @param hubHandle Handle (system-wide unique identifier) of the hub of the message. 233 * @param nanoAppHandle Handle (unique identifier) for app instance that sent the message. 234 * @param message The context hub message. 235 * 236 * @see ContextHubMessage 237 */ onMessageReceipt( int hubHandle, int nanoAppHandle, @NonNull ContextHubMessage message)238 public abstract void onMessageReceipt( 239 int hubHandle, 240 int nanoAppHandle, 241 @NonNull ContextHubMessage message); 242 } 243 244 /** 245 * @deprecated Use {@link Callback} instead. 246 * @hide 247 */ 248 @Deprecated 249 public interface ICallback { 250 /** 251 * Callback function called on message receipt from context hub. 252 * 253 * @param hubHandle Handle (system-wide unique identifier) of the hub of the message. 254 * @param nanoAppHandle Handle (unique identifier) for app instance that sent the message. 255 * @param message The context hub message. 256 * 257 * @see ContextHubMessage 258 */ onMessageReceipt(int hubHandle, int nanoAppHandle, ContextHubMessage message)259 void onMessageReceipt(int hubHandle, int nanoAppHandle, ContextHubMessage message); 260 } 261 262 /** 263 * Get a handle to all the context hubs in the system 264 * 265 * @return array of context hub handles 266 * 267 * @deprecated Use {@link #getContextHubs()} instead. The use of handles are deprecated in the 268 * new APIs. 269 */ 270 @Deprecated 271 @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) getContextHubHandles()272 public int[] getContextHubHandles() { 273 if (Flags.removeOldContextHubApis()) { 274 return null; 275 } 276 277 try { 278 return mService.getContextHubHandles(); 279 } catch (RemoteException e) { 280 throw e.rethrowFromSystemServer(); 281 } 282 } 283 284 /** 285 * Get more information about a specific hub. 286 * 287 * @param hubHandle Handle (system-wide unique identifier) of a context hub. 288 * @return ContextHubInfo Information about the requested context hub. 289 * 290 * @see ContextHubInfo 291 * 292 * @deprecated Use {@link #getContextHubs()} instead. The use of handles are deprecated in the 293 * new APIs. 294 */ 295 @Deprecated 296 @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) getContextHubInfo(int hubHandle)297 public ContextHubInfo getContextHubInfo(int hubHandle) { 298 if (Flags.removeOldContextHubApis()) { 299 return null; 300 } 301 302 try { 303 return mService.getContextHubInfo(hubHandle); 304 } catch (RemoteException e) { 305 throw e.rethrowFromSystemServer(); 306 } 307 } 308 309 /** 310 * Load a nano app on a specified context hub. 311 * 312 * Note that loading is asynchronous. When we return from this method, 313 * the nano app (probably) hasn't loaded yet. Assuming a return of 0 314 * from this method, then the final success/failure for the load, along 315 * with the "handle" for the nanoapp, is all delivered in a byte 316 * string via a call to Callback.onMessageReceipt. 317 * 318 * TODO(b/30784270): Provide a better success/failure and "handle" delivery. 319 * 320 * @param hubHandle handle of context hub to load the app on. 321 * @param app the nanoApp to load on the hub 322 * 323 * @return 0 if the command for loading was sent to the context hub; 324 * -1 otherwise 325 * 326 * @see NanoApp 327 * 328 * @deprecated Use {@link #loadNanoApp(ContextHubInfo, NanoAppBinary)} instead. 329 */ 330 @Deprecated 331 @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) loadNanoApp(int hubHandle, @NonNull NanoApp app)332 public int loadNanoApp(int hubHandle, @NonNull NanoApp app) { 333 if (Flags.removeOldContextHubApis()) { 334 return -1; 335 } 336 337 try { 338 return mService.loadNanoApp(hubHandle, app); 339 } catch (RemoteException e) { 340 throw e.rethrowFromSystemServer(); 341 } 342 } 343 344 /** 345 * Unload a specified nanoApp 346 * 347 * Note that unloading is asynchronous. When we return from this method, 348 * the nano app (probably) hasn't unloaded yet. Assuming a return of 0 349 * from this method, then the final success/failure for the unload is 350 * delivered in a byte string via a call to Callback.onMessageReceipt. 351 * 352 * TODO(b/30784270): Provide a better success/failure delivery. 353 * 354 * @param nanoAppHandle handle of the nanoApp to unload 355 * 356 * @return 0 if the command for unloading was sent to the context hub; 357 * -1 otherwise 358 * 359 * @deprecated Use {@link #unloadNanoApp(ContextHubInfo, long)} instead. 360 */ 361 @Deprecated 362 @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) unloadNanoApp(int nanoAppHandle)363 public int unloadNanoApp(int nanoAppHandle) { 364 if (Flags.removeOldContextHubApis()) { 365 return -1; 366 } 367 368 try { 369 return mService.unloadNanoApp(nanoAppHandle); 370 } catch (RemoteException e) { 371 throw e.rethrowFromSystemServer(); 372 } 373 } 374 375 /** 376 * get information about the nano app instance 377 * 378 * NOTE: The returned NanoAppInstanceInfo does _not_ contain correct 379 * information for several fields, specifically: 380 * - getName() 381 * - getPublisher() 382 * - getNeededExecMemBytes() 383 * - getNeededReadMemBytes() 384 * - getNeededWriteMemBytes() 385 * 386 * For example, say you call loadNanoApp() with a NanoApp that has 387 * getName() returning "My Name". Later, if you call getNanoAppInstanceInfo 388 * for that nanoapp, the returned NanoAppInstanceInfo's getName() 389 * method will claim "Preloaded app, unknown", even though you would 390 * have expected "My Name". For now, as the user, you'll need to 391 * separately track the above fields if they are of interest to you. 392 * 393 * TODO(b/30943489): Have the returned NanoAppInstanceInfo contain the 394 * correct information. 395 * 396 * @param nanoAppHandle handle of the nanoapp instance 397 * @return NanoAppInstanceInfo the NanoAppInstanceInfo of the nanoapp, or null if the nanoapp 398 * does not exist 399 * 400 * @see NanoAppInstanceInfo 401 * 402 * @deprecated Use {@link #queryNanoApps(ContextHubInfo)} instead to explicitly query the hub 403 * for loaded nanoapps. 404 */ 405 @Deprecated 406 @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) getNanoAppInstanceInfo(int nanoAppHandle)407 @Nullable public NanoAppInstanceInfo getNanoAppInstanceInfo(int nanoAppHandle) { 408 if (Flags.removeOldContextHubApis()) { 409 return null; 410 } 411 412 try { 413 return mService.getNanoAppInstanceInfo(nanoAppHandle); 414 } catch (RemoteException e) { 415 throw e.rethrowFromSystemServer(); 416 } 417 } 418 419 /** 420 * Find a specified nano app on the system 421 * 422 * @param hubHandle handle of hub to search for nano app 423 * @param filter filter specifying the search criteria for app 424 * 425 * @see NanoAppFilter 426 * 427 * @return int[] Array of handles to any found nano apps 428 * 429 * @deprecated Use {@link #queryNanoApps(ContextHubInfo)} instead to explicitly query the hub 430 * for loaded nanoapps. 431 */ 432 @Deprecated 433 @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) findNanoAppOnHub(int hubHandle, @NonNull NanoAppFilter filter)434 @NonNull public int[] findNanoAppOnHub(int hubHandle, @NonNull NanoAppFilter filter) { 435 if (Flags.removeOldContextHubApis()) { 436 return null; 437 } 438 439 try { 440 return mService.findNanoAppOnHub(hubHandle, filter); 441 } catch (RemoteException e) { 442 throw e.rethrowFromSystemServer(); 443 } 444 } 445 446 /** 447 * Send a message to a specific nano app instance on a context hub. 448 * 449 * Note that the return value of this method only speaks of success 450 * up to the point of sending this to the Context Hub. It is not 451 * an assurance that the Context Hub successfully sent this message 452 * on to the nanoapp. If assurance is desired, a protocol should be 453 * established between your code and the nanoapp, with the nanoapp 454 * sending a confirmation message (which will be reported via 455 * Callback.onMessageReceipt). 456 * 457 * @param hubHandle handle of the hub to send the message to 458 * @param nanoAppHandle handle of the nano app to send to 459 * @param message Message to be sent 460 * 461 * @see ContextHubMessage 462 * 463 * @return int 0 on success, -1 otherwise 464 * 465 * @deprecated Use {@link android.hardware.location.ContextHubClient#sendMessageToNanoApp( 466 * NanoAppMessage)} instead, after creating a 467 * {@link android.hardware.location.ContextHubClient} with 468 * {@link #createClient(ContextHubInfo, ContextHubClientCallback, Executor)} 469 * or {@link #createClient(ContextHubInfo, ContextHubClientCallback)}. 470 */ 471 @Deprecated 472 @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) sendMessage(int hubHandle, int nanoAppHandle, @NonNull ContextHubMessage message)473 public int sendMessage(int hubHandle, int nanoAppHandle, @NonNull ContextHubMessage message) { 474 if (Flags.removeOldContextHubApis()) { 475 return -1; 476 } 477 478 try { 479 return mService.sendMessage(hubHandle, nanoAppHandle, message); 480 } catch (RemoteException e) { 481 throw e.rethrowFromSystemServer(); 482 } 483 } 484 485 /** 486 * Returns the list of ContextHubInfo objects describing the available Context Hubs. 487 * 488 * To find the list of hubs that include all Hubs (including both Context Hubs and Vendor Hubs), 489 * use the {@link #getHubs()} method instead. 490 * 491 * @return the list of ContextHubInfo objects 492 * 493 * @see ContextHubInfo 494 */ 495 @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) getContextHubs()496 @NonNull public List<ContextHubInfo> getContextHubs() { 497 try { 498 return mService.getContextHubs(); 499 } catch (RemoteException e) { 500 throw e.rethrowFromSystemServer(); 501 } 502 } 503 504 /** 505 * Returns the list of HubInfo objects describing the available hubs (including Context Hubs and 506 * Vendor Hubs). This method is primarily used for debugging purposes as most clients care about 507 * endpoints and services more than hubs. 508 * 509 * @return the list of HubInfo objects 510 * @see HubInfo 511 * @hide 512 */ 513 @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) 514 @NonNull 515 @FlaggedApi(Flags.FLAG_OFFLOAD_API) getHubs()516 public List<HubInfo> getHubs() { 517 try { 518 return mService.getHubs(); 519 } catch (RemoteException e) { 520 throw e.rethrowFromSystemServer(); 521 } 522 } 523 524 /** 525 * Helper function to generate a stub for a query transaction callback. 526 * 527 * @param transaction the transaction to unblock when complete 528 * @return the callback 529 * @hide 530 */ createQueryCallback( ContextHubTransaction<List<NanoAppState>> transaction)531 private IContextHubTransactionCallback createQueryCallback( 532 ContextHubTransaction<List<NanoAppState>> transaction) { 533 return new IContextHubTransactionCallback.Stub() { 534 @Override 535 public void onQueryResponse(int result, List<NanoAppState> nanoappList) { 536 transaction.setResponse(new ContextHubTransaction.Response<List<NanoAppState>>( 537 result, nanoappList)); 538 } 539 540 @Override 541 public void onTransactionComplete(int result) { 542 Log.e(TAG, "Received a non-query callback on a query request"); 543 transaction.setResponse(new ContextHubTransaction.Response<List<NanoAppState>>( 544 ContextHubTransaction.RESULT_FAILED_SERVICE_INTERNAL_FAILURE, null)); 545 } 546 }; 547 } 548 549 /** 550 * Loads a nanoapp at the specified Context Hub. 551 * 552 * After the nanoapp binary is successfully loaded at the specified hub, the nanoapp will be in 553 * the enabled state. 554 * 555 * @param hubInfo the hub to load the nanoapp on 556 * @param appBinary The app binary to load 557 * 558 * @return the ContextHubTransaction of the request 559 * 560 * @throws NullPointerException if hubInfo or NanoAppBinary is null 561 * 562 * @see NanoAppBinary 563 */ 564 @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) 565 @NonNull public ContextHubTransaction<Void> loadNanoApp( 566 @NonNull ContextHubInfo hubInfo, @NonNull NanoAppBinary appBinary) { 567 Objects.requireNonNull(hubInfo, "ContextHubInfo cannot be null"); 568 Objects.requireNonNull(appBinary, "NanoAppBinary cannot be null"); 569 570 ContextHubTransaction<Void> transaction = 571 new ContextHubTransaction<>(ContextHubTransaction.TYPE_LOAD_NANOAPP); 572 IContextHubTransactionCallback callback = 573 ContextHubTransactionHelper.createTransactionCallback(transaction); 574 575 try { 576 mService.loadNanoAppOnHub(hubInfo.getId(), callback, appBinary); 577 } catch (RemoteException e) { 578 throw e.rethrowFromSystemServer(); 579 } 580 581 return transaction; 582 } 583 584 /** 585 * Unloads a nanoapp at the specified Context Hub. 586 * 587 * @param hubInfo the hub to unload the nanoapp from 588 * @param nanoAppId the app to unload 589 * 590 * @return the ContextHubTransaction of the request 591 * 592 * @throws NullPointerException if hubInfo is null 593 */ 594 @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) 595 @NonNull public ContextHubTransaction<Void> unloadNanoApp( 596 @NonNull ContextHubInfo hubInfo, long nanoAppId) { 597 Objects.requireNonNull(hubInfo, "ContextHubInfo cannot be null"); 598 599 ContextHubTransaction<Void> transaction = 600 new ContextHubTransaction<>(ContextHubTransaction.TYPE_UNLOAD_NANOAPP); 601 IContextHubTransactionCallback callback = 602 ContextHubTransactionHelper.createTransactionCallback(transaction); 603 604 try { 605 mService.unloadNanoAppFromHub(hubInfo.getId(), callback, nanoAppId); 606 } catch (RemoteException e) { 607 throw e.rethrowFromSystemServer(); 608 } 609 610 return transaction; 611 } 612 613 /** 614 * Enables a nanoapp at the specified Context Hub. 615 * 616 * @param hubInfo the hub to enable the nanoapp on 617 * @param nanoAppId the app to enable 618 * 619 * @return the ContextHubTransaction of the request 620 * 621 * @throws NullPointerException if hubInfo is null 622 */ 623 @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) 624 @NonNull public ContextHubTransaction<Void> enableNanoApp( 625 @NonNull ContextHubInfo hubInfo, long nanoAppId) { 626 Objects.requireNonNull(hubInfo, "ContextHubInfo cannot be null"); 627 628 ContextHubTransaction<Void> transaction = 629 new ContextHubTransaction<>(ContextHubTransaction.TYPE_ENABLE_NANOAPP); 630 IContextHubTransactionCallback callback = 631 ContextHubTransactionHelper.createTransactionCallback(transaction); 632 633 try { 634 mService.enableNanoApp(hubInfo.getId(), callback, nanoAppId); 635 } catch (RemoteException e) { 636 throw e.rethrowFromSystemServer(); 637 } 638 639 return transaction; 640 } 641 642 /** 643 * Disables a nanoapp at the specified Context Hub. 644 * 645 * @param hubInfo the hub to disable the nanoapp on 646 * @param nanoAppId the app to disable 647 * 648 * @return the ContextHubTransaction of the request 649 * 650 * @throws NullPointerException if hubInfo is null 651 */ 652 @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) 653 @NonNull public ContextHubTransaction<Void> disableNanoApp( 654 @NonNull ContextHubInfo hubInfo, long nanoAppId) { 655 Objects.requireNonNull(hubInfo, "ContextHubInfo cannot be null"); 656 657 ContextHubTransaction<Void> transaction = 658 new ContextHubTransaction<>(ContextHubTransaction.TYPE_DISABLE_NANOAPP); 659 IContextHubTransactionCallback callback = 660 ContextHubTransactionHelper.createTransactionCallback(transaction); 661 662 try { 663 mService.disableNanoApp(hubInfo.getId(), callback, nanoAppId); 664 } catch (RemoteException e) { 665 throw e.rethrowFromSystemServer(); 666 } 667 668 return transaction; 669 } 670 671 /** 672 * Requests a query for nanoapps loaded at the specified Context Hub. 673 * 674 * @param hubInfo the hub to query a list of nanoapps from 675 * 676 * @return the ContextHubTransaction of the request 677 * 678 * @throws NullPointerException if hubInfo is null 679 */ 680 @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) 681 @NonNull public ContextHubTransaction<List<NanoAppState>> queryNanoApps( 682 @NonNull ContextHubInfo hubInfo) { 683 Objects.requireNonNull(hubInfo, "ContextHubInfo cannot be null"); 684 685 ContextHubTransaction<List<NanoAppState>> transaction = 686 new ContextHubTransaction<>(ContextHubTransaction.TYPE_QUERY_NANOAPPS); 687 IContextHubTransactionCallback callback = createQueryCallback(transaction); 688 689 try { 690 mService.queryNanoApps(hubInfo.getId(), callback); 691 } catch (RemoteException e) { 692 throw e.rethrowFromSystemServer(); 693 } 694 695 return transaction; 696 } 697 698 /** 699 * Find a list of endpoints that matches a specific ID. 700 * 701 * @param endpointId Statically generated ID for an endpoint. 702 * @return A list of {@link HubDiscoveryInfo} objects that represents the result of discovery. 703 * @throws UnsupportedOperationException If the operation is not supported. 704 */ 705 @FlaggedApi(Flags.FLAG_OFFLOAD_API) 706 @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) 707 @NonNull 708 public List<HubDiscoveryInfo> findEndpoints(long endpointId) { 709 try { 710 List<HubEndpointInfo> endpointInfos = mService.findEndpoints(endpointId); 711 List<HubDiscoveryInfo> results = new ArrayList<>(endpointInfos.size()); 712 // Wrap with result type 713 for (HubEndpointInfo endpointInfo : endpointInfos) { 714 results.add(new HubDiscoveryInfo(endpointInfo)); 715 } 716 return results; 717 } catch (RemoteException e) { 718 throw e.rethrowFromSystemServer(); 719 } 720 } 721 722 /** 723 * Find a list of endpoints that provides a specific service. 724 * 725 * <p>Service descriptor should uniquely identify the interface (scoped to type). Convention of 726 * the descriptor depend on interface type. 727 * 728 * <p>Examples: 729 * 730 * <ol> 731 * <li>AOSP-defined AIDL: android.hardware.something.IFoo/default 732 * <li>Vendor-defined AIDL: com.example.something.IBar/default 733 * <li>Pigweed RPC with Protobuf: com.example.proto.ExampleService 734 * </ol> 735 * 736 * @param serviceDescriptor The service descriptor for a service provided by the hub. The value 737 * cannot be null or empty. 738 * @return A list of {@link HubDiscoveryInfo} objects that represents the result of discovery. 739 * @throws IllegalArgumentException if the serviceDescriptor is empty/null. 740 * @throws UnsupportedOperationException If the operation is not supported. 741 */ 742 @FlaggedApi(Flags.FLAG_OFFLOAD_API) 743 @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) 744 @NonNull 745 public List<HubDiscoveryInfo> findEndpoints(@NonNull String serviceDescriptor) { 746 if (serviceDescriptor.isBlank()) { 747 throw new IllegalArgumentException("Invalid service descriptor: " + serviceDescriptor); 748 } 749 try { 750 List<HubEndpointInfo> endpointInfos = 751 mService.findEndpointsWithService(serviceDescriptor); 752 List<HubDiscoveryInfo> results = new ArrayList<>(endpointInfos.size()); 753 // Wrap with result type 754 for (HubEndpointInfo endpointInfo : endpointInfos) { 755 for (HubServiceInfo serviceInfo : endpointInfo.getServiceInfoCollection()) { 756 if (serviceInfo.getServiceDescriptor().equals(serviceDescriptor)) { 757 results.add(new HubDiscoveryInfo(endpointInfo, serviceInfo)); 758 } 759 } 760 } 761 return results; 762 } catch (RemoteException e) { 763 throw e.rethrowFromSystemServer(); 764 } 765 } 766 767 /** 768 * Creates an interface to invoke endpoint discovery callbacks to send down to the service. 769 * 770 * @param executor the executor to invoke callbacks for this client 771 * @param callback the callback to invoke at the client process 772 * @param serviceDescriptor an optional descriptor to match discovery list with 773 * @return the callback interface 774 */ 775 @FlaggedApi(Flags.FLAG_OFFLOAD_API) 776 private IContextHubEndpointDiscoveryCallback createDiscoveryCallback( 777 IContextHubService service, 778 Executor executor, 779 HubEndpointDiscoveryCallback callback, 780 @Nullable String serviceDescriptor) { 781 return new IContextHubEndpointDiscoveryCallback.Stub() { 782 @Override 783 public void onEndpointsStarted(HubEndpointInfo[] hubEndpointInfoList) { 784 if (hubEndpointInfoList.length == 0) { 785 Log.w(TAG, "onEndpointsStarted: received empty discovery list"); 786 invokeCallbackFinished(service); 787 return; 788 } 789 executor.execute( 790 () -> { 791 List<HubDiscoveryInfo> discoveryList = 792 getMatchingEndpointDiscoveryList( 793 hubEndpointInfoList, serviceDescriptor); 794 if (discoveryList.isEmpty()) { 795 Log.w(TAG, "onEndpointsStarted: no matching service descriptor"); 796 } else { 797 callback.onEndpointsStarted(discoveryList); 798 } 799 invokeCallbackFinished(service); 800 }); 801 } 802 803 @Override 804 public void onEndpointsStopped(HubEndpointInfo[] hubEndpointInfoList, int reason) { 805 if (hubEndpointInfoList.length == 0) { 806 Log.w(TAG, "onEndpointsStopped: received empty discovery list"); 807 invokeCallbackFinished(service); 808 return; 809 } 810 executor.execute( 811 () -> { 812 List<HubDiscoveryInfo> discoveryList = 813 getMatchingEndpointDiscoveryList( 814 hubEndpointInfoList, serviceDescriptor); 815 if (discoveryList.isEmpty()) { 816 Log.w(TAG, "onEndpointsStopped: no matching service descriptor"); 817 } else { 818 callback.onEndpointsStopped(discoveryList, reason); 819 } 820 invokeCallbackFinished(service); 821 }); 822 } 823 824 private void invokeCallbackFinished(IContextHubService service) { 825 try { 826 service.onDiscoveryCallbackFinished(); 827 } catch (RemoteException e) { 828 e.rethrowFromSystemServer(); 829 } 830 } 831 }; 832 } 833 834 /** 835 * Generates a list of matching endpoint discovery info, given the list and an (optional) 836 * service descriptor. If service descriptor is null, all endpoints are added to the filtered 837 * output list. 838 * 839 * @param hubEndpointInfoList The hub endpoints to filter. 840 * @param serviceDescriptor The optional service descriptor to match, null if adding all 841 * endpoints. 842 * @return The list of filtered HubDiscoveryInfo which matches the serviceDescriptor. 843 */ 844 @FlaggedApi(Flags.FLAG_OFFLOAD_API) 845 private List<HubDiscoveryInfo> getMatchingEndpointDiscoveryList( 846 HubEndpointInfo[] hubEndpointInfoList, @Nullable String serviceDescriptor) { 847 List<HubDiscoveryInfo> discoveryList = new ArrayList<>(hubEndpointInfoList.length); 848 for (HubEndpointInfo info : hubEndpointInfoList) { 849 if (serviceDescriptor != null) { 850 for (HubServiceInfo sInfo : info.getServiceInfoCollection()) { 851 if (sInfo.getServiceDescriptor().equals(serviceDescriptor)) { 852 discoveryList.add(new HubDiscoveryInfo(info, sInfo)); 853 } 854 } 855 } else { 856 discoveryList.add(new HubDiscoveryInfo(info)); 857 } 858 } 859 return discoveryList; 860 } 861 862 /** 863 * Equivalent to {@link #registerEndpointDiscoveryCallback(Executor, 864 * HubEndpointDiscoveryCallback, long)} with the default executor in the main thread. 865 */ 866 @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) 867 @FlaggedApi(Flags.FLAG_OFFLOAD_API) 868 public void registerEndpointDiscoveryCallback( 869 @NonNull HubEndpointDiscoveryCallback callback, long endpointId) { 870 registerEndpointDiscoveryCallback( 871 new HandlerExecutor(Handler.getMain()), callback, endpointId); 872 } 873 874 /** 875 * Registers a callback to be notified when the hub endpoint with the corresponding endpoint ID 876 * has started or stopped. 877 * 878 * @param executor The executor to invoke the callback on. 879 * @param callback The callback to be invoked. 880 * @param endpointId The identifier of the hub endpoint. 881 * @throws UnsupportedOperationException If the operation is not supported. 882 */ 883 @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) 884 @FlaggedApi(Flags.FLAG_OFFLOAD_API) 885 public void registerEndpointDiscoveryCallback( 886 @NonNull Executor executor, 887 @NonNull HubEndpointDiscoveryCallback callback, 888 long endpointId) { 889 Objects.requireNonNull(executor, "executor cannot be null"); 890 Objects.requireNonNull(callback, "callback cannot be null"); 891 IContextHubEndpointDiscoveryCallback iCallback = 892 createDiscoveryCallback(mService, executor, callback, null); 893 try { 894 mService.registerEndpointDiscoveryCallbackId(endpointId, iCallback); 895 } catch (RemoteException e) { 896 e.rethrowFromSystemServer(); 897 } 898 899 mDiscoveryCallbacks.put(callback, iCallback); 900 } 901 902 /** 903 * Equivalent to {@link #registerEndpointDiscoveryCallback(Executor, 904 * HubEndpointDiscoveryCallback, String)} with the default executor in the main thread. 905 */ 906 @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) 907 @FlaggedApi(Flags.FLAG_OFFLOAD_API) 908 public void registerEndpointDiscoveryCallback( 909 @NonNull HubEndpointDiscoveryCallback callback, @NonNull String serviceDescriptor) { 910 registerEndpointDiscoveryCallback( 911 new HandlerExecutor(Handler.getMain()), callback, serviceDescriptor); 912 } 913 914 /** 915 * Registers a callback to be notified when the hub endpoint with the corresponding service 916 * descriptor has started or stopped. 917 * 918 * @param executor The executor to invoke the callback on. 919 * @param serviceDescriptor The service descriptor of the hub endpoint. 920 * @param callback The callback to be invoked. 921 * @throws IllegalArgumentException if the serviceDescriptor is empty. 922 * @throws UnsupportedOperationException If the operation is not supported. 923 */ 924 @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) 925 @FlaggedApi(Flags.FLAG_OFFLOAD_API) 926 public void registerEndpointDiscoveryCallback( 927 @NonNull Executor executor, 928 @NonNull HubEndpointDiscoveryCallback callback, 929 @NonNull String serviceDescriptor) { 930 Objects.requireNonNull(executor, "executor cannot be null"); 931 Objects.requireNonNull(callback, "callback cannot be null"); 932 Objects.requireNonNull(serviceDescriptor, "serviceDescriptor cannot be null"); 933 if (serviceDescriptor.isBlank()) { 934 throw new IllegalArgumentException("Invalid service descriptor: " + serviceDescriptor); 935 } 936 937 IContextHubEndpointDiscoveryCallback iCallback = 938 createDiscoveryCallback(mService, executor, callback, serviceDescriptor); 939 try { 940 mService.registerEndpointDiscoveryCallbackDescriptor(serviceDescriptor, iCallback); 941 } catch (RemoteException e) { 942 e.rethrowFromSystemServer(); 943 } 944 945 mDiscoveryCallbacks.put(callback, iCallback); 946 } 947 948 /** 949 * Unregisters a previously registered endpoint discovery callback. 950 * 951 * @param callback The callback previously registered. 952 * @throws IllegalArgumentException If the callback was not previously registered. 953 * @throws UnsupportedOperationException If the operation is not supported. 954 */ 955 @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) 956 @FlaggedApi(Flags.FLAG_OFFLOAD_API) 957 public void unregisterEndpointDiscoveryCallback( 958 @NonNull HubEndpointDiscoveryCallback callback) { 959 Objects.requireNonNull(callback, "callback cannot be null"); 960 IContextHubEndpointDiscoveryCallback iCallback = mDiscoveryCallbacks.remove(callback); 961 if (iCallback == null) { 962 throw new IllegalArgumentException("Callback not previously registered"); 963 } 964 965 try { 966 mService.unregisterEndpointDiscoveryCallback(iCallback); 967 } catch (RemoteException e) { 968 e.rethrowFromSystemServer(); 969 } 970 } 971 972 /** 973 * Set a callback to receive messages from the context hub 974 * 975 * @param callback Callback object 976 * 977 * @see Callback 978 * 979 * @return int 0 on success, -1 otherwise 980 * 981 * @deprecated Use {@link #createClient(ContextHubInfo, ContextHubClientCallback, Executor)} 982 * or {@link #createClient(ContextHubInfo, ContextHubClientCallback)} instead to 983 * register a {@link android.hardware.location.ContextHubClientCallback}. 984 */ 985 @Deprecated 986 @SuppressLint("RequiresPermission") 987 public int registerCallback(@NonNull Callback callback) { 988 if (Flags.removeOldContextHubApis()) { 989 return -1; 990 } 991 992 return registerCallback(callback, null); 993 } 994 995 /** 996 * @deprecated Use {@link #registerCallback(Callback)} instead. 997 * @hide 998 */ 999 @Deprecated 1000 public int registerCallback(ICallback callback) { 1001 if (Flags.removeOldContextHubApis()) { 1002 return -1; 1003 } 1004 1005 if (mLocalCallback != null) { 1006 Log.w(TAG, "Max number of local callbacks reached!"); 1007 return -1; 1008 } 1009 mLocalCallback = callback; 1010 return 0; 1011 } 1012 1013 /** 1014 * Set a callback to receive messages from the context hub 1015 * 1016 * @param callback Callback object 1017 * @param handler Handler object, if null uses the Handler of the main Looper 1018 * 1019 * @see Callback 1020 * 1021 * @return int 0 on success, -1 otherwise 1022 * 1023 * @deprecated Use {@link #createClient(ContextHubInfo, ContextHubClientCallback, Executor)} 1024 * or {@link #createClient(ContextHubInfo, ContextHubClientCallback)} instead to 1025 * register a {@link android.hardware.location.ContextHubClientCallback}. 1026 */ 1027 @Deprecated 1028 @SuppressLint("RequiresPermission") 1029 public int registerCallback(Callback callback, Handler handler) { 1030 if (Flags.removeOldContextHubApis()) { 1031 return -1; 1032 } 1033 1034 synchronized(this) { 1035 if (mCallback != null) { 1036 Log.w(TAG, "Max number of callbacks reached!"); 1037 return -1; 1038 } 1039 mCallback = callback; 1040 mCallbackHandler = (handler == null) ? new Handler(mMainLooper) : handler; 1041 } 1042 return 0; 1043 } 1044 1045 /** 1046 * Creates an interface to the ContextHubClient to send down to the service. 1047 * 1048 * @param client the ContextHubClient object associated with this callback 1049 * @param callback the callback to invoke at the client process 1050 * @param executor the executor to invoke callbacks for this client 1051 * 1052 * @return the callback interface 1053 */ 1054 private IContextHubClientCallback createClientCallback( 1055 ContextHubClient client, ContextHubClientCallback callback, Executor executor) { 1056 return new IContextHubClientCallback.Stub() { 1057 @Override 1058 public void onMessageFromNanoApp(NanoAppMessage message) { 1059 executor.execute( 1060 () -> { 1061 callback.onMessageFromNanoApp(client, message); 1062 if (message.isReliable()) { 1063 client.reliableMessageCallbackFinished( 1064 message.getMessageSequenceNumber(), ErrorCode.OK); 1065 } else { 1066 client.callbackFinished(); 1067 } 1068 }); 1069 } 1070 1071 @Override 1072 public void onHubReset() { 1073 executor.execute( 1074 () -> { 1075 callback.onHubReset(client); 1076 client.callbackFinished(); 1077 }); 1078 } 1079 1080 @Override 1081 public void onNanoAppAborted(long nanoAppId, int abortCode) { 1082 executor.execute( 1083 () -> { 1084 callback.onNanoAppAborted(client, nanoAppId, abortCode); 1085 client.callbackFinished(); 1086 }); 1087 } 1088 1089 @Override 1090 public void onNanoAppLoaded(long nanoAppId) { 1091 executor.execute( 1092 () -> { 1093 callback.onNanoAppLoaded(client, nanoAppId); 1094 client.callbackFinished(); 1095 }); 1096 } 1097 1098 @Override 1099 public void onNanoAppUnloaded(long nanoAppId) { 1100 executor.execute( 1101 () -> { 1102 callback.onNanoAppUnloaded(client, nanoAppId); 1103 client.callbackFinished(); 1104 }); 1105 } 1106 1107 @Override 1108 public void onNanoAppEnabled(long nanoAppId) { 1109 executor.execute( 1110 () -> { 1111 callback.onNanoAppEnabled(client, nanoAppId); 1112 client.callbackFinished(); 1113 }); 1114 } 1115 1116 @Override 1117 public void onNanoAppDisabled(long nanoAppId) { 1118 executor.execute( 1119 () -> { 1120 callback.onNanoAppDisabled(client, nanoAppId); 1121 client.callbackFinished(); 1122 }); 1123 } 1124 1125 @Override 1126 public void onClientAuthorizationChanged( 1127 long nanoAppId, @ContextHubManager.AuthorizationState int authorization) { 1128 executor.execute( 1129 () -> { 1130 callback.onClientAuthorizationChanged(client, nanoAppId, authorization); 1131 client.callbackFinished(); 1132 }); 1133 } 1134 }; 1135 } 1136 1137 /** 1138 * Creates and registers a client and its callback with the Context Hub Service. 1139 * 1140 * A client is registered with the Context Hub Service for a specified Context Hub. When the 1141 * registration succeeds, the client can send messages to nanoapps through the returned 1142 * {@link ContextHubClient} object, and receive notifications through the provided callback. 1143 * 1144 * @param context the context of the application 1145 * @param hubInfo the hub to attach this client to 1146 * @param executor the executor to invoke the callback 1147 * @param callback the notification callback to register 1148 * @return the registered client object 1149 * 1150 * @throws IllegalArgumentException if hubInfo does not represent a valid hub 1151 * @throws IllegalStateException if there were too many registered clients at the service 1152 * @throws NullPointerException if callback, hubInfo, or executor is null 1153 * 1154 * @see ContextHubClientCallback 1155 */ 1156 @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) 1157 @NonNull public ContextHubClient createClient( 1158 @Nullable Context context, @NonNull ContextHubInfo hubInfo, 1159 @NonNull @CallbackExecutor Executor executor, 1160 @NonNull ContextHubClientCallback callback) { 1161 Objects.requireNonNull(callback, "Callback cannot be null"); 1162 Objects.requireNonNull(hubInfo, "ContextHubInfo cannot be null"); 1163 Objects.requireNonNull(executor, "Executor cannot be null"); 1164 1165 ContextHubClient client = new ContextHubClient(hubInfo, false /* persistent */); 1166 IContextHubClientCallback clientInterface = createClientCallback( 1167 client, callback, executor); 1168 1169 String attributionTag = null; 1170 if (context != null) { 1171 attributionTag = context.getAttributionTag(); 1172 } 1173 1174 // Workaround for old APIs not providing a context 1175 String packageName; 1176 if (context != null) { 1177 packageName = context.getPackageName(); 1178 } else { 1179 packageName = ActivityThread.currentPackageName(); 1180 } 1181 1182 IContextHubClient clientProxy; 1183 try { 1184 clientProxy = mService.createClient( 1185 hubInfo.getId(), clientInterface, attributionTag, packageName); 1186 } catch (RemoteException e) { 1187 throw e.rethrowFromSystemServer(); 1188 } 1189 1190 client.setClientProxy(clientProxy); 1191 return client; 1192 } 1193 1194 1195 /** 1196 * Equivalent to 1197 * {@link #createClient(Context, ContextHubInfo, Executor, ContextHubClientCallback)} 1198 * with the {@link Context} being set to null. 1199 */ 1200 @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) 1201 @NonNull public ContextHubClient createClient( 1202 @NonNull ContextHubInfo hubInfo, @NonNull ContextHubClientCallback callback, 1203 @NonNull @CallbackExecutor Executor executor) { 1204 return createClient(null /* context */, hubInfo, executor, callback); 1205 } 1206 1207 /** 1208 * Equivalent to {@link #createClient(ContextHubInfo, ContextHubClientCallback, Executor)} 1209 * with the executor using the main thread's Looper. 1210 */ 1211 @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) 1212 @NonNull public ContextHubClient createClient( 1213 @NonNull ContextHubInfo hubInfo, @NonNull ContextHubClientCallback callback) { 1214 return createClient(null /* context */, hubInfo, new HandlerExecutor(Handler.getMain()), 1215 callback); 1216 } 1217 1218 /** 1219 * Creates a ContextHubClient that will receive notifications based on Intent events. 1220 * 1221 * This method should be used instead of {@link #createClient(ContextHubInfo, 1222 * ContextHubClientCallback)} or {@link #createClient(ContextHubInfo, ContextHubClientCallback, 1223 * Executor)} if the caller wants to preserve the messaging endpoint of a ContextHubClient, even 1224 * after a process exits. If the PendingIntent with the provided nanoapp has already been 1225 * registered at the service, then the same ContextHubClient will be regenerated without 1226 * creating a new client connection at the service. Note that the PendingIntent, nanoapp, and 1227 * Context Hub must all match in identifying a previously registered ContextHubClient. 1228 * If a client is regenerated, the host endpoint identifier attached to messages sent to the 1229 * nanoapp remains consistent, even if the original process has exited. 1230 * 1231 * To avoid unintentionally spreading data from the Context Hub to external applications, it is 1232 * strongly recommended that the PendingIntent supplied to this API is an explicit intent. 1233 * 1234 * If registered successfully, intents will be delivered regarding events or messages from the 1235 * specified nanoapp from the attached Context Hub. The intent will have an extra 1236 * {@link ContextHubManager.EXTRA_CONTEXT_HUB_INFO} of type {@link ContextHubInfo}, which 1237 * describes the Context Hub the intent event was for. The intent will also have an extra 1238 * {@link ContextHubManager.EXTRA_EVENT_TYPE} of type {@link ContextHubManager.Event}, which 1239 * will contain the type of the event. See {@link ContextHubManager.Event} for description of 1240 * each event type, along with event-specific extra fields. The client can also use 1241 * {@link ContextHubIntentEvent.fromIntent(Intent)} to parse the Intent generated by the event. 1242 * 1243 * Intent events will be delivered until {@link ContextHubClient.close()} is called. Note that 1244 * the registration of this ContextHubClient at the Context Hub Service will be maintained until 1245 * {@link ContextHubClient.close()} is called. If {@link PendingIntent.cancel()} is called 1246 * on the provided PendingIntent, then the client will be automatically unregistered by the 1247 * service. 1248 * 1249 * Note that the {@link PendingIntent} supplied to this API must be mutable for Intent 1250 * notifications to work. 1251 * 1252 * @param context the context of the application. If a PendingIntent client is recreated, 1253 * the latest state in the context will be used and old state will be discarded 1254 * @param hubInfo the hub to attach this client to 1255 * @param pendingIntent the PendingIntent to register to the client 1256 * @param nanoAppId the ID of the nanoapp that Intent events will be generated for 1257 * @return the registered client object 1258 * 1259 * @throws IllegalArgumentException if hubInfo does not represent a valid hub, or an immutable 1260 * PendingIntent was supplied 1261 * @throws IllegalStateException if there were too many registered clients at the service 1262 * @throws NullPointerException if pendingIntent or hubInfo is null 1263 */ 1264 @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) 1265 @NonNull public ContextHubClient createClient( 1266 @Nullable Context context, @NonNull ContextHubInfo hubInfo, 1267 @NonNull PendingIntent pendingIntent, long nanoAppId) { 1268 Objects.requireNonNull(pendingIntent); 1269 Objects.requireNonNull(hubInfo); 1270 if (pendingIntent.isImmutable()) { 1271 throw new IllegalArgumentException("PendingIntent must be mutable"); 1272 } 1273 1274 ContextHubClient client = new ContextHubClient(hubInfo, true /* persistent */); 1275 1276 String attributionTag = null; 1277 if (context != null) { 1278 attributionTag = context.getAttributionTag(); 1279 } 1280 1281 IContextHubClient clientProxy; 1282 try { 1283 clientProxy = mService.createPendingIntentClient( 1284 hubInfo.getId(), pendingIntent, nanoAppId, attributionTag); 1285 } catch (RemoteException e) { 1286 throw e.rethrowFromSystemServer(); 1287 } 1288 1289 client.setClientProxy(clientProxy); 1290 return client; 1291 } 1292 1293 /** 1294 * Equivalent to {@link #createClient(ContextHubInfo, PendingIntent, long, String)} 1295 * with {@link Context} being set to null. 1296 */ 1297 @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) 1298 @NonNull public ContextHubClient createClient( 1299 @NonNull ContextHubInfo hubInfo, @NonNull PendingIntent pendingIntent, long nanoAppId) { 1300 return createClient(null /* context */, hubInfo, pendingIntent, nanoAppId); 1301 } 1302 1303 /** 1304 * Registers an endpoint and its callback with the Context Hub Service. 1305 * 1306 * <p>An endpoint is registered with the Context Hub Service and published to the HAL. When the 1307 * registration succeeds, the endpoint can receive notifications through the provided callback. 1308 * 1309 * @param hubEndpoint {@link HubEndpoint} object created by {@link HubEndpoint.Builder} 1310 * @throws IllegalStateException if the registration failed, for example if too many endpoints 1311 * are registered at the service 1312 * @throws UnsupportedOperationException if endpoint registration is not supported 1313 */ 1314 @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) 1315 @FlaggedApi(Flags.FLAG_OFFLOAD_API) 1316 public void registerEndpoint(@NonNull HubEndpoint hubEndpoint) { 1317 hubEndpoint.register(mService); 1318 } 1319 1320 /** 1321 * Use a registered endpoint to connect to another endpoint (destination) without specifying a 1322 * service. 1323 * 1324 * <p>Context Hub Service will create the endpoint session and notify the registered endpoint. 1325 * The registered endpoint will receive callbacks on its {@link HubEndpointLifecycleCallback} 1326 * object regarding the lifecycle events of the session. 1327 * 1328 * @param hubEndpoint {@link HubEndpoint} object previously registered via {@link 1329 * ContextHubManager#registerEndpoint(HubEndpoint)}. 1330 * @param destination {@link HubEndpointInfo} object that represents an endpoint from previous 1331 * endpoint discovery results (e.g. from {@link ContextHubManager#findEndpoints(long)}). 1332 */ 1333 @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) 1334 @FlaggedApi(Flags.FLAG_OFFLOAD_API) 1335 public void openSession( 1336 @NonNull HubEndpoint hubEndpoint, @NonNull HubEndpointInfo destination) { 1337 hubEndpoint.openSession(destination, null); 1338 } 1339 1340 /** 1341 * Use a registered endpoint to connect to another endpoint (destination) for a service 1342 * described by a {@link HubServiceInfo} object. 1343 * 1344 * <p>Context Hub Service will create the endpoint session and notify the registered endpoint. 1345 * The registered endpoint will receive callbacks on its {@link HubEndpointLifecycleCallback} 1346 * object regarding the lifecycle events of the session. 1347 * 1348 * @param hubEndpoint {@link HubEndpoint} object previously registered via {@link 1349 * ContextHubManager#registerEndpoint(HubEndpoint)}. 1350 * @param destination {@link HubEndpointInfo} object that represents an endpoint from previous 1351 * endpoint discovery results (e.g. from {@link ContextHubManager#findEndpoints(long)}). 1352 * @param serviceDescriptor A string that describes the service associated with this session. 1353 * The information will be sent to the destination as part of open request. 1354 * @throws IllegalStateException if hubEndpoint was not successfully registered, or if there is 1355 * insufficient capacity for creating a session. 1356 */ 1357 @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) 1358 @FlaggedApi(Flags.FLAG_OFFLOAD_API) 1359 public void openSession( 1360 @NonNull HubEndpoint hubEndpoint, 1361 @NonNull HubEndpointInfo destination, 1362 @NonNull String serviceDescriptor) { 1363 hubEndpoint.openSession(destination, serviceDescriptor); 1364 } 1365 1366 /** 1367 * Unregisters an endpoint and its callback with the Context Hub Service. 1368 * 1369 * <p>An endpoint is unregistered from the HAL. The endpoint object will no longer receive 1370 * notification through the provided callback. 1371 * 1372 * @param hubEndpoint {@link HubEndpoint} object created by {@link HubEndpoint.Builder}. This 1373 * should match a previously registered object via {@link 1374 * ContextHubManager#registerEndpoint(HubEndpoint)}. 1375 */ 1376 @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) 1377 @FlaggedApi(Flags.FLAG_OFFLOAD_API) 1378 public void unregisterEndpoint(@NonNull HubEndpoint hubEndpoint) { 1379 hubEndpoint.unregister(); 1380 } 1381 1382 /** 1383 * Queries for the list of preloaded nanoapp IDs on the system. 1384 * 1385 * @param hubInfo The Context Hub to query a list of nanoapp IDs from. 1386 * 1387 * @return The list of 64-bit IDs of the preloaded nanoapps. 1388 * 1389 * @throws NullPointerException if hubInfo is null 1390 * @hide 1391 */ 1392 @TestApi 1393 @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) 1394 @NonNull public long[] getPreloadedNanoAppIds(@NonNull ContextHubInfo hubInfo) { 1395 Objects.requireNonNull(hubInfo, "hubInfo cannot be null"); 1396 1397 long[] nanoappIds = null; 1398 try { 1399 nanoappIds = mService.getPreloadedNanoAppIds(hubInfo); 1400 } catch (RemoteException e) { 1401 throw e.rethrowFromSystemServer(); 1402 } 1403 1404 if (nanoappIds == null) { 1405 nanoappIds = new long[0]; 1406 } 1407 return nanoappIds; 1408 } 1409 1410 /** 1411 * Puts the Context Hub in test mode. 1412 * 1413 * The purpose of this API is to make testing CHRE/Context Hub more 1414 * predictable and robust. This temporarily unloads all 1415 * nanoapps. 1416 * 1417 * Note that this API must not cause CHRE/Context Hub to behave differently 1418 * in test compared to production. 1419 * 1420 * @return true if the enable test mode operation succeeded. 1421 * @hide 1422 */ 1423 @TestApi 1424 @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) 1425 @NonNull public boolean enableTestMode() { 1426 try { 1427 return mService.setTestMode(true); 1428 } catch (RemoteException e) { 1429 throw e.rethrowFromSystemServer(); 1430 } 1431 } 1432 1433 /** 1434 * Puts the Context Hub out of test mode. 1435 * 1436 * This API will undo any previously made enableTestMode() calls. 1437 * After this API is called, it should restore the state of the system 1438 * to the normal/production mode before any enableTestMode() call was 1439 * made. If the enableTestMode() call unloaded any nanoapps 1440 * to enter test mode, it should reload those nanoapps in this API call. 1441 * 1442 * @return true if the disable operation succeeded. 1443 * @hide 1444 */ 1445 @TestApi 1446 @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) 1447 @NonNull public boolean disableTestMode() { 1448 try { 1449 return mService.setTestMode(false); 1450 } catch (RemoteException e) { 1451 throw e.rethrowFromSystemServer(); 1452 } 1453 } 1454 1455 /** 1456 * Unregister a callback for receive messages from the context hub. 1457 * 1458 * @see Callback 1459 * 1460 * @param callback method to deregister 1461 * 1462 * @return int 0 on success, -1 otherwise 1463 * 1464 * @deprecated Use {@link android.hardware.location.ContextHubClient#close()} to unregister 1465 * a {@link android.hardware.location.ContextHubClientCallback}. 1466 */ 1467 @SuppressLint("RequiresPermission") 1468 @Deprecated 1469 public int unregisterCallback(@NonNull Callback callback) { 1470 if (Flags.removeOldContextHubApis()) { 1471 return -1; 1472 } 1473 1474 synchronized (this) { 1475 if (callback != mCallback) { 1476 Log.w(TAG, "Cannot recognize callback!"); 1477 return -1; 1478 } 1479 1480 mCallback = null; 1481 mCallbackHandler = null; 1482 } 1483 return 0; 1484 } 1485 1486 /** 1487 * @deprecated Use {@link #unregisterCallback(Callback)} instead. 1488 * @hide 1489 */ 1490 @Deprecated 1491 public synchronized int unregisterCallback(ICallback callback) { 1492 if (Flags.removeOldContextHubApis()) { 1493 return -1; 1494 } 1495 1496 if (callback != mLocalCallback) { 1497 Log.w(TAG, "Cannot recognize local callback!"); 1498 return -1; 1499 } 1500 mLocalCallback = null; 1501 return 0; 1502 } 1503 1504 /** 1505 * Invokes the ContextHubManager.Callback callback registered with the ContextHubManager. 1506 * 1507 * @param hubId The ID of the Context Hub the message came from 1508 * @param nanoAppId The instance ID of the nanoapp the message came from 1509 * @param message The message to provide the callback 1510 */ 1511 private synchronized void invokeOnMessageReceiptCallback( 1512 int hubId, int nanoAppId, ContextHubMessage message) { 1513 if (mCallback != null) { 1514 mCallback.onMessageReceipt(hubId, nanoAppId, message); 1515 } 1516 } 1517 1518 private final IContextHubCallback.Stub mClientCallback = new IContextHubCallback.Stub() { 1519 @Override 1520 public void onMessageReceipt( 1521 final int hubId, final int nanoAppId, final ContextHubMessage message) { 1522 synchronized (ContextHubManager.this) { 1523 if (mCallback != null) { 1524 mCallbackHandler.post( 1525 () -> invokeOnMessageReceiptCallback(hubId, nanoAppId, message)); 1526 } else if (mLocalCallback != null) { 1527 // We always ensure that mCallback takes precedence, because mLocalCallback is 1528 // only for internal compatibility 1529 mLocalCallback.onMessageReceipt(hubId, nanoAppId, message); 1530 } 1531 } 1532 } 1533 }; 1534 1535 /** @hide */ 1536 public ContextHubManager(@NonNull IContextHubService service, @NonNull Looper mainLooper) { 1537 requireNonNull(service, "service cannot be null"); 1538 requireNonNull(mainLooper, "mainLooper cannot be null"); 1539 mService = service; 1540 mMainLooper = mainLooper; 1541 1542 try { 1543 mService.registerCallback(mClientCallback); 1544 } catch (RemoteException e) { 1545 throw e.rethrowFromSystemServer(); 1546 } 1547 } 1548 } 1549