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