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