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