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