1 /* 2 * Copyright (C) 2020 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 17 package com.android.server.location.contexthub; 18 19 import android.annotation.IntDef; 20 import android.annotation.Nullable; 21 import android.app.ActivityManager; 22 import android.app.PendingIntent; 23 import android.bluetooth.BluetoothAdapter; 24 import android.content.BroadcastReceiver; 25 import android.content.Context; 26 import android.content.Intent; 27 import android.content.IntentFilter; 28 import android.content.pm.UserInfo; 29 import android.database.ContentObserver; 30 import android.hardware.SensorPrivacyManager; 31 import android.hardware.SensorPrivacyManagerInternal; 32 import android.hardware.location.ContextHubInfo; 33 import android.hardware.location.ContextHubMessage; 34 import android.hardware.location.ContextHubTransaction; 35 import android.hardware.location.IContextHubCallback; 36 import android.hardware.location.IContextHubClient; 37 import android.hardware.location.IContextHubClientCallback; 38 import android.hardware.location.IContextHubService; 39 import android.hardware.location.IContextHubTransactionCallback; 40 import android.hardware.location.NanoApp; 41 import android.hardware.location.NanoAppBinary; 42 import android.hardware.location.NanoAppFilter; 43 import android.hardware.location.NanoAppInstanceInfo; 44 import android.hardware.location.NanoAppMessage; 45 import android.hardware.location.NanoAppState; 46 import android.location.LocationManager; 47 import android.net.wifi.WifiManager; 48 import android.os.Binder; 49 import android.os.RemoteCallbackList; 50 import android.os.RemoteException; 51 import android.os.ResultReceiver; 52 import android.os.ShellCallback; 53 import android.os.SystemClock; 54 import android.os.UserHandle; 55 import android.os.UserManager; 56 import android.provider.Settings; 57 import android.util.Log; 58 import android.util.Pair; 59 import android.util.proto.ProtoOutputStream; 60 61 import com.android.internal.util.DumpUtils; 62 import com.android.server.LocalServices; 63 import com.android.server.location.ContextHubServiceProto; 64 65 import java.io.FileDescriptor; 66 import java.io.PrintWriter; 67 import java.lang.annotation.Retention; 68 import java.lang.annotation.RetentionPolicy; 69 import java.nio.ByteBuffer; 70 import java.nio.ByteOrder; 71 import java.util.ArrayList; 72 import java.util.Arrays; 73 import java.util.Collections; 74 import java.util.HashMap; 75 import java.util.List; 76 import java.util.Map; 77 import java.util.Objects; 78 import java.util.Set; 79 import java.util.concurrent.ConcurrentHashMap; 80 import java.util.concurrent.ScheduledThreadPoolExecutor; 81 import java.util.concurrent.TimeUnit; 82 import java.util.concurrent.atomic.AtomicLong; 83 84 /** 85 * @hide 86 */ 87 public class ContextHubService extends IContextHubService.Stub { 88 private static final String TAG = "ContextHubService"; 89 90 /* 91 * Constants for the type of transaction that is defined by ContextHubService. 92 * This is used to report the transaction callback to clients, and is different from 93 * ContextHubTransaction.Type. 94 */ 95 public static final int MSG_ENABLE_NANO_APP = 1; 96 public static final int MSG_DISABLE_NANO_APP = 2; 97 public static final int MSG_LOAD_NANO_APP = 3; 98 public static final int MSG_UNLOAD_NANO_APP = 4; 99 public static final int MSG_QUERY_NANO_APPS = 5; 100 public static final int MSG_QUERY_MEMORY = 6; 101 public static final int MSG_HUB_RESET = 7; 102 103 private static final int OS_APP_INSTANCE = -1; 104 105 /** 106 * Constants describing an async event from the Context Hub. 107 * {@hide} 108 */ 109 @Retention(RetentionPolicy.SOURCE) 110 @IntDef(prefix = {"CONTEXT_HUB_EVENT_"}, value = { 111 CONTEXT_HUB_EVENT_UNKNOWN, 112 CONTEXT_HUB_EVENT_RESTARTED, 113 }) 114 public @interface Type { 115 } 116 117 public static final int CONTEXT_HUB_EVENT_UNKNOWN = 0; 118 public static final int CONTEXT_HUB_EVENT_RESTARTED = 1; 119 120 /* 121 * Local flag to enable debug logging. 122 */ 123 private static final boolean DEBUG_LOG_ENABLED = false; 124 125 private final Context mContext; 126 127 private Map<Integer, ContextHubInfo> mContextHubIdToInfoMap; 128 private List<String> mSupportedContextHubPerms; 129 private List<ContextHubInfo> mContextHubInfoList; 130 private final RemoteCallbackList<IContextHubCallback> mCallbacksList = 131 new RemoteCallbackList<>(); 132 133 // Proxy object to communicate with the Context Hub HAL 134 private final IContextHubWrapper mContextHubWrapper; 135 136 // The manager for transaction queue 137 private ContextHubTransactionManager mTransactionManager; 138 139 // The manager for sending messages to/from clients 140 private ContextHubClientManager mClientManager; 141 142 // The default client for old API clients 143 private Map<Integer, IContextHubClient> mDefaultClientMap; 144 145 // The manager for the internal nanoapp state cache 146 private final NanoAppStateManager mNanoAppStateManager = new NanoAppStateManager(); 147 148 // An executor and the future object for scheduling timeout timers 149 private final ScheduledThreadPoolExecutor mDailyMetricTimer = 150 new ScheduledThreadPoolExecutor(1); 151 152 // The period of the recurring time 153 private static final int PERIOD_METRIC_QUERY_DAYS = 1; 154 155 // True if WiFi is available for the Context Hub 156 private boolean mIsWifiAvailable = false; 157 private boolean mIsWifiScanningEnabled = false; 158 private boolean mIsWifiMainEnabled = false; 159 160 // True if BT is available for the Context Hub 161 private boolean mIsBtScanningEnabled = false; 162 private boolean mIsBtMainEnabled = false; 163 164 // A hashmap used to record if a contexthub is waiting for daily query 165 private Set<Integer> mMetricQueryPendingContextHubIds = 166 Collections.newSetFromMap(new ConcurrentHashMap<Integer, Boolean>()); 167 168 // Lock object for sendWifiSettingUpdate() 169 private final Object mSendWifiSettingUpdateLock = new Object(); 170 171 private SensorPrivacyManagerInternal mSensorPrivacyManagerInternal; 172 173 private UserManager mUserManager = null; 174 175 private final Map<Integer, AtomicLong> mLastRestartTimestampMap = new HashMap<>(); 176 177 /** 178 * Class extending the callback to register with a Context Hub. 179 */ 180 private class ContextHubServiceCallback implements IContextHubWrapper.ICallback { 181 private final int mContextHubId; 182 ContextHubServiceCallback(int contextHubId)183 ContextHubServiceCallback(int contextHubId) { 184 mContextHubId = contextHubId; 185 } 186 187 @Override handleTransactionResult(int transactionId, boolean success)188 public void handleTransactionResult(int transactionId, boolean success) { 189 handleTransactionResultCallback(mContextHubId, transactionId, success); 190 } 191 192 @Override handleContextHubEvent(int eventType)193 public void handleContextHubEvent(int eventType) { 194 handleHubEventCallback(mContextHubId, eventType); 195 } 196 197 @Override handleNanoappAbort(long nanoappId, int abortCode)198 public void handleNanoappAbort(long nanoappId, int abortCode) { 199 handleAppAbortCallback(mContextHubId, nanoappId, abortCode); 200 } 201 202 @Override handleNanoappInfo(List<NanoAppState> nanoappStateList)203 public void handleNanoappInfo(List<NanoAppState> nanoappStateList) { 204 handleQueryAppsCallback(mContextHubId, nanoappStateList); 205 } 206 207 @Override handleNanoappMessage(short hostEndpointId, NanoAppMessage message, List<String> nanoappPermissions, List<String> messagePermissions)208 public void handleNanoappMessage(short hostEndpointId, NanoAppMessage message, 209 List<String> nanoappPermissions, List<String> messagePermissions) { 210 handleClientMessageCallback(mContextHubId, hostEndpointId, message, nanoappPermissions, 211 messagePermissions); 212 } 213 214 @Override handleServiceRestart()215 public void handleServiceRestart() { 216 Log.i(TAG, "Starting Context Hub Service restart"); 217 initExistingCallbacks(); 218 resetSettings(); 219 Log.i(TAG, "Finished Context Hub Service restart"); 220 } 221 } 222 ContextHubService(Context context, IContextHubWrapper contextHubWrapper)223 public ContextHubService(Context context, IContextHubWrapper contextHubWrapper) { 224 Log.i(TAG, "Starting Context Hub Service init"); 225 mContext = context; 226 long startTimeNs = SystemClock.elapsedRealtimeNanos(); 227 mContextHubWrapper = contextHubWrapper; 228 if (!initContextHubServiceState(startTimeNs)) { 229 Log.e(TAG, "Failed to initialize the Context Hub Service"); 230 return; 231 } 232 initDefaultClientMap(); 233 234 initLocationSettingNotifications(); 235 initWifiSettingNotifications(); 236 initAirplaneModeSettingNotifications(); 237 initMicrophoneSettingNotifications(); 238 initBtSettingNotifications(); 239 240 scheduleDailyMetricSnapshot(); 241 Log.i(TAG, "Finished Context Hub Service init"); 242 } 243 244 /** 245 * Creates a default client callback for old API clients. 246 * 247 * @param contextHubId the ID of the hub to attach this client to 248 * @return the internal callback interface 249 */ createDefaultClientCallback(int contextHubId)250 private IContextHubClientCallback createDefaultClientCallback(int contextHubId) { 251 return new IContextHubClientCallback.Stub() { 252 private void finishCallback() { 253 try { 254 IContextHubClient client = mDefaultClientMap.get(contextHubId); 255 client.callbackFinished(); 256 } catch (RemoteException e) { 257 Log.e( 258 TAG, 259 "RemoteException while finishing callback for hub (ID = " 260 + contextHubId 261 + ")", 262 e); 263 } 264 } 265 266 @Override 267 public void onMessageFromNanoApp(NanoAppMessage message) { 268 int nanoAppHandle = 269 mNanoAppStateManager.getNanoAppHandle(contextHubId, message.getNanoAppId()); 270 271 onMessageReceiptOldApi( 272 message.getMessageType(), 273 contextHubId, 274 nanoAppHandle, 275 message.getMessageBody()); 276 277 finishCallback(); 278 } 279 280 @Override 281 public void onHubReset() { 282 byte[] data = {android.hardware.contexthub.V1_0.TransactionResult.SUCCESS}; 283 onMessageReceiptOldApi(MSG_HUB_RESET, contextHubId, OS_APP_INSTANCE, data); 284 finishCallback(); 285 } 286 287 @Override 288 public void onNanoAppAborted(long nanoAppId, int abortCode) { 289 finishCallback(); 290 } 291 292 @Override 293 public void onNanoAppLoaded(long nanoAppId) { 294 finishCallback(); 295 } 296 297 @Override 298 public void onNanoAppUnloaded(long nanoAppId) { 299 finishCallback(); 300 } 301 302 @Override 303 public void onNanoAppEnabled(long nanoAppId) { 304 finishCallback(); 305 } 306 307 @Override 308 public void onNanoAppDisabled(long nanoAppId) { 309 finishCallback(); 310 } 311 312 @Override 313 public void onClientAuthorizationChanged(long nanoAppId, int authorization) { 314 finishCallback(); 315 } 316 }; 317 } 318 319 /** 320 * Initializes the private state of the ContextHubService 321 * 322 * @param startTimeNs the start time when init was called 323 * 324 * @return if mContextHubWrapper is not null and a full state init was done 325 */ 326 private boolean initContextHubServiceState(long startTimeNs) { 327 if (mContextHubWrapper == null) { 328 mTransactionManager = null; 329 mClientManager = null; 330 mSensorPrivacyManagerInternal = null; 331 mDefaultClientMap = Collections.emptyMap(); 332 mContextHubIdToInfoMap = Collections.emptyMap(); 333 mSupportedContextHubPerms = Collections.emptyList(); 334 mContextHubInfoList = Collections.emptyList(); 335 return false; 336 } 337 338 Pair<List<ContextHubInfo>, List<String>> hubInfo; 339 try { 340 hubInfo = mContextHubWrapper.getHubs(); 341 } catch (RemoteException e) { 342 Log.e(TAG, "RemoteException while getting Context Hub info", e); 343 hubInfo = new Pair(Collections.emptyList(), Collections.emptyList()); 344 } 345 346 long bootTimeNs = SystemClock.elapsedRealtimeNanos() - startTimeNs; 347 int numContextHubs = hubInfo.first.size(); 348 ContextHubStatsLog.write(ContextHubStatsLog.CONTEXT_HUB_BOOTED, bootTimeNs, 349 numContextHubs); 350 351 mContextHubIdToInfoMap = Collections.unmodifiableMap( 352 ContextHubServiceUtil.createContextHubInfoMap(hubInfo.first)); 353 mSupportedContextHubPerms = hubInfo.second; 354 mContextHubInfoList = new ArrayList<>(mContextHubIdToInfoMap.values()); 355 mClientManager = new ContextHubClientManager(mContext, mContextHubWrapper); 356 mTransactionManager = new ContextHubTransactionManager( 357 mContextHubWrapper, mClientManager, mNanoAppStateManager); 358 mSensorPrivacyManagerInternal = 359 LocalServices.getService(SensorPrivacyManagerInternal.class); 360 return true; 361 } 362 363 /** 364 * Creates the default client map that maps context hub IDs to the associated 365 * ClientManager. The client map is unmodifiable 366 */ 367 private void initDefaultClientMap() { 368 HashMap<Integer, IContextHubClient> defaultClientMap = new HashMap<>(); 369 for (Map.Entry<Integer, ContextHubInfo> entry: mContextHubIdToInfoMap.entrySet()) { 370 int contextHubId = entry.getKey(); 371 ContextHubInfo contextHubInfo = entry.getValue(); 372 373 mLastRestartTimestampMap.put(contextHubId, 374 new AtomicLong(SystemClock.elapsedRealtimeNanos())); 375 376 try { 377 mContextHubWrapper.registerCallback(contextHubId, 378 new ContextHubServiceCallback(contextHubId)); 379 } catch (RemoteException e) { 380 Log.e(TAG, "RemoteException while registering service callback for hub (ID = " 381 + contextHubId + ")", e); 382 } 383 384 IContextHubClient client = mClientManager.registerClient( 385 contextHubInfo, createDefaultClientCallback(contextHubId), 386 /* attributionTag= */ null, mTransactionManager, mContext.getPackageName()); 387 defaultClientMap.put(contextHubId, client); 388 389 // Do a query to initialize the service cache list of nanoapps 390 // TODO(b/194289715): Remove this when old API is deprecated 391 queryNanoAppsInternal(contextHubId); 392 } 393 mDefaultClientMap = Collections.unmodifiableMap(defaultClientMap); 394 } 395 396 /** 397 * Initializes existing callbacks with the mContextHubWrapper for every context hub 398 */ 399 private void initExistingCallbacks() { 400 for (int contextHubId : mContextHubIdToInfoMap.keySet()) { 401 try { 402 mContextHubWrapper.registerExistingCallback(contextHubId); 403 } catch (RemoteException e) { 404 Log.e(TAG, "RemoteException while registering existing service callback for hub " 405 + "(ID = " + contextHubId + ")", e); 406 } 407 } 408 } 409 410 /** 411 * Handles the initialization of location settings notifications 412 */ 413 private void initLocationSettingNotifications() { 414 if (mContextHubWrapper == null 415 || !mContextHubWrapper.supportsLocationSettingNotifications()) { 416 return; 417 } 418 419 sendLocationSettingUpdate(); 420 mContext.getContentResolver().registerContentObserver( 421 Settings.Secure.getUriFor(Settings.Secure.LOCATION_MODE), 422 /* notifyForDescendants= */ true, 423 new ContentObserver(/* handler= */ null) { 424 @Override 425 public void onChange(boolean selfChange) { 426 sendLocationSettingUpdate(); 427 } 428 }, UserHandle.USER_ALL); 429 } 430 431 /** 432 * Handles the initialization of wifi settings notifications 433 */ 434 private void initWifiSettingNotifications() { 435 if (mContextHubWrapper == null || !mContextHubWrapper.supportsWifiSettingNotifications()) { 436 return; 437 } 438 439 sendWifiSettingUpdate(/* forceUpdate= */ true); 440 441 BroadcastReceiver wifiReceiver = new BroadcastReceiver() { 442 @Override 443 public void onReceive(Context context, Intent intent) { 444 if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(intent.getAction()) 445 || WifiManager.ACTION_WIFI_SCAN_AVAILABILITY_CHANGED.equals( 446 intent.getAction())) { 447 sendWifiSettingUpdate(/* forceUpdate= */ false); 448 } 449 } 450 }; 451 IntentFilter filter = new IntentFilter(); 452 filter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION); 453 filter.addAction(WifiManager.ACTION_WIFI_SCAN_AVAILABILITY_CHANGED); 454 mContext.registerReceiver(wifiReceiver, filter); 455 456 mContext.getContentResolver().registerContentObserver( 457 Settings.Global.getUriFor(Settings.Global.WIFI_SCAN_ALWAYS_AVAILABLE), 458 /* notifyForDescendants= */ true, 459 new ContentObserver(/* handler= */ null) { 460 @Override 461 public void onChange(boolean selfChange) { 462 sendWifiSettingUpdate(/* forceUpdate= */ false); 463 } 464 }, UserHandle.USER_ALL); 465 } 466 467 /** 468 * Handles the initialization of airplane mode settings notifications 469 */ 470 private void initAirplaneModeSettingNotifications() { 471 if (mContextHubWrapper == null 472 || !mContextHubWrapper.supportsAirplaneModeSettingNotifications()) { 473 return; 474 } 475 476 sendAirplaneModeSettingUpdate(); 477 mContext.getContentResolver().registerContentObserver( 478 Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON), 479 /* notifyForDescendants= */ true, 480 new ContentObserver(/* handler= */ null) { 481 @Override 482 public void onChange(boolean selfChange) { 483 sendAirplaneModeSettingUpdate(); 484 } 485 }, UserHandle.USER_ALL); 486 } 487 488 /** 489 * Handles the initialization of microphone settings notifications 490 */ 491 private void initMicrophoneSettingNotifications() { 492 if (mContextHubWrapper == null 493 || !mContextHubWrapper.supportsMicrophoneSettingNotifications()) { 494 return; 495 } 496 497 if (mUserManager == null) { 498 mUserManager = mContext.getSystemService(UserManager.class); 499 if (mUserManager == null) { 500 Log.e(TAG, "Unable to get the UserManager service"); 501 return; 502 } 503 } 504 505 sendMicrophoneDisableSettingUpdateForCurrentUser(); 506 if (mSensorPrivacyManagerInternal == null) { 507 Log.e(TAG, "Unable to add a sensor privacy listener for all users"); 508 return; 509 } 510 511 mSensorPrivacyManagerInternal.addSensorPrivacyListenerForAllUsers( 512 SensorPrivacyManager.Sensors.MICROPHONE, (userId, enabled) -> { 513 // If we are in HSUM mode, any user can change the microphone setting 514 if (mUserManager.isHeadlessSystemUserMode() || userId == getCurrentUserId()) { 515 Log.d(TAG, "User: " + userId + " mic privacy: " + enabled); 516 sendMicrophoneDisableSettingUpdate(enabled); 517 } 518 }); 519 } 520 521 /** 522 * Handles the initialization of bluetooth settings notifications 523 */ 524 private void initBtSettingNotifications() { 525 if (mContextHubWrapper == null || !mContextHubWrapper.supportsBtSettingNotifications()) { 526 return; 527 } 528 529 sendBtSettingUpdate(/* forceUpdate= */ true); 530 531 BroadcastReceiver btReceiver = new BroadcastReceiver() { 532 @Override 533 public void onReceive(Context context, Intent intent) { 534 if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(intent.getAction())) { 535 sendBtSettingUpdate(/* forceUpdate= */ false); 536 } 537 } 538 }; 539 IntentFilter filter = new IntentFilter(); 540 filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED); 541 mContext.registerReceiver(btReceiver, filter); 542 543 mContext.getContentResolver().registerContentObserver( 544 Settings.Global.getUriFor(Settings.Global.BLE_SCAN_ALWAYS_AVAILABLE), 545 /* notifyForDescendants= */ false, 546 new ContentObserver(/* handler= */ null) { 547 @Override 548 public void onChange(boolean selfChange) { 549 sendBtSettingUpdate(/* forceUpdate= */ false); 550 } 551 }, UserHandle.USER_ALL); 552 } 553 554 /** 555 * Resets the settings. Called when a context hub restarts or the AIDL HAL dies 556 */ 557 private void resetSettings() { 558 sendLocationSettingUpdate(); 559 sendWifiSettingUpdate(/* forceUpdate= */ true); 560 sendAirplaneModeSettingUpdate(); 561 sendMicrophoneDisableSettingUpdateForCurrentUser(); 562 sendBtSettingUpdate(/* forceUpdate= */ true); 563 } 564 565 @Override 566 public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, 567 String[] args, ShellCallback callback, ResultReceiver result) { 568 new ContextHubShellCommand(mContext, this).exec(this, in, out, err, args, callback, result); 569 } 570 571 @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) 572 @Override 573 public int registerCallback(IContextHubCallback callback) throws RemoteException { 574 super.registerCallback_enforcePermission(); 575 576 mCallbacksList.register(callback); 577 578 Log.d(TAG, "Added callback, total callbacks " + 579 mCallbacksList.getRegisteredCallbackCount()); 580 return 0; 581 } 582 583 @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) 584 @Override 585 public int[] getContextHubHandles() throws RemoteException { 586 super.getContextHubHandles_enforcePermission(); 587 588 return ContextHubServiceUtil.createPrimitiveIntArray(mContextHubIdToInfoMap.keySet()); 589 } 590 591 @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) 592 @Override 593 public ContextHubInfo getContextHubInfo(int contextHubHandle) throws RemoteException { 594 super.getContextHubInfo_enforcePermission(); 595 596 if (!mContextHubIdToInfoMap.containsKey(contextHubHandle)) { 597 Log.e(TAG, "Invalid Context Hub handle " + contextHubHandle + " in getContextHubInfo"); 598 return null; 599 } 600 601 return mContextHubIdToInfoMap.get(contextHubHandle); 602 } 603 604 @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) 605 /** 606 * Returns a List of ContextHubInfo object describing the available hubs. 607 * 608 * @return the List of ContextHubInfo objects 609 */ 610 @Override 611 public List<ContextHubInfo> getContextHubs() throws RemoteException { 612 super.getContextHubs_enforcePermission(); 613 614 return mContextHubInfoList; 615 } 616 617 /** 618 * Creates an internal load transaction callback to be used for old API clients 619 * 620 * @param contextHubId the ID of the hub to load the binary 621 * @param nanoAppBinary the binary to load 622 * @return the callback interface 623 */ 624 private IContextHubTransactionCallback createLoadTransactionCallback( 625 int contextHubId, NanoAppBinary nanoAppBinary) { 626 return new IContextHubTransactionCallback.Stub() { 627 @Override 628 public void onTransactionComplete(int result) { 629 handleLoadResponseOldApi(contextHubId, result, nanoAppBinary); 630 } 631 632 @Override 633 public void onQueryResponse(int result, List<NanoAppState> nanoAppStateList) { 634 } 635 }; 636 } 637 638 /** 639 * Creates an internal unload transaction callback to be used for old API clients 640 * 641 * @param contextHubId the ID of the hub to unload the nanoapp 642 * @return the callback interface 643 */ 644 private IContextHubTransactionCallback createUnloadTransactionCallback(int contextHubId) { 645 return new IContextHubTransactionCallback.Stub() { 646 @Override 647 public void onTransactionComplete(int result) { 648 handleUnloadResponseOldApi(contextHubId, result); 649 } 650 651 @Override 652 public void onQueryResponse(int result, List<NanoAppState> nanoAppStateList) { 653 } 654 }; 655 } 656 657 /** 658 * Creates an internal query transaction callback to be used for old API clients 659 * 660 * @param contextHubId the ID of the hub to query 661 * @return the callback interface 662 */ 663 private IContextHubTransactionCallback createQueryTransactionCallback(int contextHubId) { 664 return new IContextHubTransactionCallback.Stub() { 665 @Override 666 public void onTransactionComplete(int result) { 667 } 668 669 @Override 670 public void onQueryResponse(int result, List<NanoAppState> nanoAppStateList) { 671 byte[] data = {(byte) result}; 672 onMessageReceiptOldApi(MSG_QUERY_NANO_APPS, contextHubId, OS_APP_INSTANCE, data); 673 } 674 }; 675 } 676 677 @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) 678 @Override 679 public int loadNanoApp(int contextHubHandle, NanoApp nanoApp) throws RemoteException { 680 super.loadNanoApp_enforcePermission(); 681 682 if (mContextHubWrapper == null) { 683 return -1; 684 } 685 if (!isValidContextHubId(contextHubHandle)) { 686 Log.e(TAG, "Invalid Context Hub handle " + contextHubHandle + " in loadNanoApp"); 687 return -1; 688 } 689 if (nanoApp == null) { 690 Log.e(TAG, "NanoApp cannot be null in loadNanoApp"); 691 return -1; 692 } 693 694 // Create an internal IContextHubTransactionCallback for the old API clients 695 NanoAppBinary nanoAppBinary = new NanoAppBinary(nanoApp.getAppBinary()); 696 IContextHubTransactionCallback onCompleteCallback = 697 createLoadTransactionCallback(contextHubHandle, nanoAppBinary); 698 699 ContextHubServiceTransaction transaction = mTransactionManager.createLoadTransaction( 700 contextHubHandle, nanoAppBinary, onCompleteCallback, getCallingPackageName()); 701 702 mTransactionManager.addTransaction(transaction); 703 return 0; 704 } 705 706 @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) 707 @Override 708 public int unloadNanoApp(int nanoAppHandle) throws RemoteException { 709 super.unloadNanoApp_enforcePermission(); 710 711 if (mContextHubWrapper == null) { 712 return -1; 713 } 714 715 NanoAppInstanceInfo info = 716 mNanoAppStateManager.getNanoAppInstanceInfo(nanoAppHandle); 717 if (info == null) { 718 Log.e(TAG, "Invalid nanoapp handle " + nanoAppHandle + " in unloadNanoApp"); 719 return -1; 720 } 721 722 int contextHubId = info.getContexthubId(); 723 long nanoAppId = info.getAppId(); 724 IContextHubTransactionCallback onCompleteCallback = 725 createUnloadTransactionCallback(contextHubId); 726 ContextHubServiceTransaction transaction = mTransactionManager.createUnloadTransaction( 727 contextHubId, nanoAppId, onCompleteCallback, getCallingPackageName()); 728 729 mTransactionManager.addTransaction(transaction); 730 return 0; 731 } 732 733 @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) 734 @Override 735 public NanoAppInstanceInfo getNanoAppInstanceInfo(int nanoAppHandle) throws RemoteException { 736 737 super.getNanoAppInstanceInfo_enforcePermission(); 738 739 return mNanoAppStateManager.getNanoAppInstanceInfo(nanoAppHandle); 740 } 741 742 @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) 743 @Override 744 public int[] findNanoAppOnHub( 745 int contextHubHandle, NanoAppFilter filter) throws RemoteException { 746 747 super.findNanoAppOnHub_enforcePermission(); 748 749 ArrayList<Integer> foundInstances = new ArrayList<>(); 750 if (filter != null) { 751 mNanoAppStateManager.foreachNanoAppInstanceInfo((info) -> { 752 if (filter.testMatch(info)) { 753 foundInstances.add(info.getHandle()); 754 } 755 }); 756 } 757 758 int[] retArray = new int[foundInstances.size()]; 759 for (int i = 0; i < foundInstances.size(); i++) { 760 retArray[i] = foundInstances.get(i).intValue(); 761 } 762 return retArray; 763 } 764 765 /** 766 * Performs a query at the specified hub. 767 * <p> 768 * This method should only be invoked internally by the service, either to update the service 769 * cache or as a result of an explicit query requested by a client through the sendMessage API. 770 * 771 * @param contextHubId the ID of the hub to do the query 772 * @return true if the query succeeded 773 * @throws IllegalStateException if the transaction queue is full 774 */ 775 private boolean queryNanoAppsInternal(int contextHubId) { 776 if (mContextHubWrapper == null) { 777 return false; 778 } 779 780 IContextHubTransactionCallback onCompleteCallback = 781 createQueryTransactionCallback(contextHubId); 782 ContextHubServiceTransaction transaction = mTransactionManager.createQueryTransaction( 783 contextHubId, onCompleteCallback, getCallingPackageName()); 784 785 mTransactionManager.addTransaction(transaction); 786 return true; 787 } 788 789 @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) 790 @Override 791 public int sendMessage(int contextHubHandle, int nanoAppHandle, ContextHubMessage msg) 792 throws RemoteException { 793 super.sendMessage_enforcePermission(); 794 795 if (mContextHubWrapper == null) { 796 return -1; 797 } 798 if (msg == null) { 799 Log.e(TAG, "ContextHubMessage cannot be null in sendMessage"); 800 return -1; 801 } 802 if (msg.getData() == null) { 803 Log.e(TAG, "ContextHubMessage message body cannot be null in sendMessage"); 804 return -1; 805 } 806 if (!isValidContextHubId(contextHubHandle)) { 807 Log.e(TAG, "Invalid Context Hub handle " + contextHubHandle + " in sendMessage"); 808 return -1; 809 } 810 811 boolean success = false; 812 if (nanoAppHandle == OS_APP_INSTANCE) { 813 if (msg.getMsgType() == MSG_QUERY_NANO_APPS) { 814 success = queryNanoAppsInternal(contextHubHandle); 815 } else { 816 Log.e(TAG, "Invalid OS message params of type " + msg.getMsgType()); 817 } 818 } else { 819 NanoAppInstanceInfo info = getNanoAppInstanceInfo(nanoAppHandle); 820 if (info != null) { 821 NanoAppMessage message = NanoAppMessage.createMessageToNanoApp( 822 info.getAppId(), msg.getMsgType(), msg.getData()); 823 824 IContextHubClient client = mDefaultClientMap.get(contextHubHandle); 825 success = (client.sendMessageToNanoApp(message) == 826 ContextHubTransaction.RESULT_SUCCESS); 827 } else { 828 Log.e(TAG, "Failed to send nanoapp message - nanoapp with handle " 829 + nanoAppHandle + " does not exist."); 830 } 831 } 832 833 return success ? 0 : -1; 834 } 835 836 /** 837 * Handles a unicast or broadcast message from a nanoapp. 838 * 839 * @param contextHubId the ID of the hub the message came from 840 * @param hostEndpointId the host endpoint ID of the client receiving this message 841 * @param message the message contents 842 * @param nanoappPermissions the set of permissions the nanoapp holds 843 * @param messagePermissions the set of permissions that should be used for attributing 844 * permissions when this message is consumed by a client 845 */ 846 private void handleClientMessageCallback( 847 int contextHubId, 848 short hostEndpointId, 849 NanoAppMessage message, 850 List<String> nanoappPermissions, 851 List<String> messagePermissions) { 852 mClientManager.onMessageFromNanoApp( 853 contextHubId, hostEndpointId, message, nanoappPermissions, messagePermissions); 854 } 855 856 /** 857 * A helper function to handle a load response from the Context Hub for the old API. 858 * TODO(b/194289715): Remove this once the old APIs are obsolete. 859 */ 860 private void handleLoadResponseOldApi( 861 int contextHubId, int result, NanoAppBinary nanoAppBinary) { 862 if (nanoAppBinary == null) { 863 Log.e(TAG, "Nanoapp binary field was null for a load transaction"); 864 return; 865 } 866 867 byte[] data = new byte[5]; 868 data[0] = (byte) result; 869 int nanoAppHandle = mNanoAppStateManager.getNanoAppHandle( 870 contextHubId, nanoAppBinary.getNanoAppId()); 871 ByteBuffer.wrap(data, 1, 4).order(ByteOrder.nativeOrder()).putInt(nanoAppHandle); 872 873 onMessageReceiptOldApi(MSG_LOAD_NANO_APP, contextHubId, OS_APP_INSTANCE, data); 874 } 875 876 /** 877 * A helper function to handle an unload response from the Context Hub for the old API. 878 * <p> 879 * TODO(b/194289715): Remove this once the old APIs are obsolete. 880 */ 881 private void handleUnloadResponseOldApi(int contextHubId, int result) { 882 byte[] data = new byte[1]; 883 data[0] = (byte) result; 884 onMessageReceiptOldApi(MSG_UNLOAD_NANO_APP, contextHubId, OS_APP_INSTANCE, data); 885 } 886 887 /** 888 * Handles a transaction response from a Context Hub. 889 * 890 * @param contextHubId the ID of the hub the response came from 891 * @param transactionId the ID of the transaction 892 * @param success true if the transaction succeeded 893 */ 894 private void handleTransactionResultCallback(int contextHubId, int transactionId, 895 boolean success) { 896 mTransactionManager.onTransactionResponse(transactionId, success); 897 } 898 899 /** 900 * Handles an asynchronous event from a Context Hub. 901 * 902 * @param contextHubId the ID of the hub the response came from 903 * @param eventType the type of the event as in CONTEXT_HUB_EVENT_* 904 */ 905 private void handleHubEventCallback(int contextHubId, int eventType) { 906 if (eventType == CONTEXT_HUB_EVENT_RESTARTED) { 907 long now = SystemClock.elapsedRealtimeNanos(); 908 long lastRestartTimeNs = mLastRestartTimestampMap.get(contextHubId).getAndSet(now); 909 ContextHubStatsLog.write( 910 ContextHubStatsLog.CONTEXT_HUB_RESTARTED, 911 TimeUnit.NANOSECONDS.toMillis(now - lastRestartTimeNs), 912 contextHubId); 913 914 ContextHubEventLogger.getInstance().logContextHubRestart(contextHubId); 915 916 resetSettings(); 917 918 mTransactionManager.onHubReset(); 919 queryNanoAppsInternal(contextHubId); 920 921 mClientManager.onHubReset(contextHubId); 922 } else { 923 Log.i(TAG, "Received unknown hub event (hub ID = " + contextHubId + ", type = " 924 + eventType + ")"); 925 } 926 } 927 928 /** 929 * Handles an asynchronous abort event of a nanoapp. 930 * 931 * @param contextHubId the ID of the hub that the nanoapp aborted in 932 * @param nanoAppId the ID of the aborted nanoapp 933 * @param abortCode the nanoapp-specific abort code 934 */ 935 private void handleAppAbortCallback(int contextHubId, long nanoAppId, int abortCode) { 936 mClientManager.onNanoAppAborted(contextHubId, nanoAppId, abortCode); 937 } 938 939 /** 940 * Handles a query response from a Context Hub. 941 * 942 * @param contextHubId the ID of the hub of the response 943 * @param nanoappStateList the list of loaded nanoapps 944 */ 945 private void handleQueryAppsCallback(int contextHubId, List<NanoAppState> nanoappStateList) { 946 if (mMetricQueryPendingContextHubIds.contains(contextHubId)) { 947 for (NanoAppState nanoappState : nanoappStateList) { 948 ContextHubStatsLog.write( 949 ContextHubStatsLog.CONTEXT_HUB_LOADED_NANOAPP_SNAPSHOT_REPORTED, 950 contextHubId, nanoappState.getNanoAppId(), 951 (int) nanoappState.getNanoAppVersion()); 952 } 953 mMetricQueryPendingContextHubIds.remove(contextHubId); 954 if (mMetricQueryPendingContextHubIds.isEmpty()) { 955 scheduleDailyMetricSnapshot(); 956 } 957 } 958 mNanoAppStateManager.updateCache(contextHubId, nanoappStateList); 959 mTransactionManager.onQueryResponse(nanoappStateList); 960 } 961 962 /** 963 * @param contextHubId the hub ID to validate 964 * @return {@code true} if the ID represents that of an available hub, {@code false} otherwise 965 */ 966 private boolean isValidContextHubId(int contextHubId) { 967 return mContextHubIdToInfoMap.containsKey(contextHubId); 968 } 969 970 @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) 971 /** 972 * Creates and registers a client at the service for the specified Context Hub. 973 * 974 * @param contextHubId the ID of the hub this client is attached to 975 * @param clientCallback the client interface to register with the service 976 * @param attributionTag an optional attribution tag within the given package 977 * @param packageName the name of the package creating this client 978 * @return the generated client interface, null if registration was unsuccessful 979 * @throws IllegalArgumentException if contextHubId is not a valid ID 980 * @throws IllegalStateException if max number of clients have already registered 981 * @throws NullPointerException if clientCallback is null 982 */ 983 @Override 984 public IContextHubClient createClient( 985 int contextHubId, IContextHubClientCallback clientCallback, 986 @Nullable String attributionTag, String packageName) throws RemoteException { 987 super.createClient_enforcePermission(); 988 989 if (!isValidContextHubId(contextHubId)) { 990 throw new IllegalArgumentException("Invalid context hub ID " + contextHubId); 991 } 992 if (clientCallback == null) { 993 throw new NullPointerException("Cannot register client with null callback"); 994 } 995 996 ContextHubInfo contextHubInfo = mContextHubIdToInfoMap.get(contextHubId); 997 return mClientManager.registerClient( 998 contextHubInfo, clientCallback, attributionTag, mTransactionManager, packageName); 999 } 1000 1001 @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) 1002 /** 1003 * Creates and registers a PendingIntent client at the service for the specified Context Hub. 1004 * 1005 * @param contextHubId the ID of the hub this client is attached to 1006 * @param pendingIntent the PendingIntent associated with this client 1007 * @param nanoAppId the ID of the nanoapp PendingIntent events will be sent for 1008 * @param attributionTag an optional attribution tag within the given package 1009 * @return the generated client interface 1010 * @throws IllegalArgumentException if hubInfo does not represent a valid hub 1011 * @throws IllegalStateException if there were too many registered clients at the service 1012 */ 1013 @Override 1014 public IContextHubClient createPendingIntentClient( 1015 int contextHubId, PendingIntent pendingIntent, long nanoAppId, 1016 @Nullable String attributionTag) throws RemoteException { 1017 super.createPendingIntentClient_enforcePermission(); 1018 1019 if (!isValidContextHubId(contextHubId)) { 1020 throw new IllegalArgumentException("Invalid context hub ID " + contextHubId); 1021 } 1022 1023 ContextHubInfo contextHubInfo = mContextHubIdToInfoMap.get(contextHubId); 1024 return mClientManager.registerClient( 1025 contextHubInfo, pendingIntent, nanoAppId, attributionTag, mTransactionManager); 1026 } 1027 1028 @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) 1029 /** 1030 * Loads a nanoapp binary at the specified Context hub. 1031 * 1032 * @param contextHubId the ID of the hub to load the binary 1033 * @param transactionCallback the client-facing transaction callback interface 1034 * @param nanoAppBinary the binary to load 1035 * @throws IllegalStateException if the transaction queue is full 1036 */ 1037 @Override 1038 public void loadNanoAppOnHub( 1039 int contextHubId, IContextHubTransactionCallback transactionCallback, 1040 NanoAppBinary nanoAppBinary) throws RemoteException { 1041 super.loadNanoAppOnHub_enforcePermission(); 1042 1043 if (!checkHalProxyAndContextHubId( 1044 contextHubId, transactionCallback, ContextHubTransaction.TYPE_LOAD_NANOAPP)) { 1045 return; 1046 } 1047 if (nanoAppBinary == null) { 1048 Log.e(TAG, "NanoAppBinary cannot be null in loadNanoAppOnHub"); 1049 transactionCallback.onTransactionComplete( 1050 ContextHubTransaction.RESULT_FAILED_BAD_PARAMS); 1051 return; 1052 } 1053 1054 ContextHubServiceTransaction transaction = mTransactionManager.createLoadTransaction( 1055 contextHubId, nanoAppBinary, transactionCallback, getCallingPackageName()); 1056 mTransactionManager.addTransaction(transaction); 1057 } 1058 1059 @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) 1060 /** 1061 * Unloads a nanoapp from the specified Context Hub. 1062 * 1063 * @param contextHubId the ID of the hub to unload the nanoapp 1064 * @param transactionCallback the client-facing transaction callback interface 1065 * @param nanoAppId the ID of the nanoapp to unload 1066 * @throws IllegalStateException if the transaction queue is full 1067 */ 1068 @Override 1069 public void unloadNanoAppFromHub( 1070 int contextHubId, IContextHubTransactionCallback transactionCallback, long nanoAppId) 1071 throws RemoteException { 1072 super.unloadNanoAppFromHub_enforcePermission(); 1073 1074 if (!checkHalProxyAndContextHubId( 1075 contextHubId, transactionCallback, ContextHubTransaction.TYPE_UNLOAD_NANOAPP)) { 1076 return; 1077 } 1078 1079 ContextHubServiceTransaction transaction = mTransactionManager.createUnloadTransaction( 1080 contextHubId, nanoAppId, transactionCallback, getCallingPackageName()); 1081 mTransactionManager.addTransaction(transaction); 1082 } 1083 1084 @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) 1085 /** 1086 * Enables a nanoapp at the specified Context Hub. 1087 * 1088 * @param contextHubId the ID of the hub to enable the nanoapp 1089 * @param transactionCallback the client-facing transaction callback interface 1090 * @param nanoAppId the ID of the nanoapp to enable 1091 * @throws IllegalStateException if the transaction queue is full 1092 */ 1093 @Override 1094 public void enableNanoApp( 1095 int contextHubId, IContextHubTransactionCallback transactionCallback, long nanoAppId) 1096 throws RemoteException { 1097 super.enableNanoApp_enforcePermission(); 1098 1099 if (!checkHalProxyAndContextHubId( 1100 contextHubId, transactionCallback, ContextHubTransaction.TYPE_ENABLE_NANOAPP)) { 1101 return; 1102 } 1103 1104 ContextHubServiceTransaction transaction = mTransactionManager.createEnableTransaction( 1105 contextHubId, nanoAppId, transactionCallback, getCallingPackageName()); 1106 mTransactionManager.addTransaction(transaction); 1107 } 1108 1109 @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) 1110 /** 1111 * Disables a nanoapp at the specified Context Hub. 1112 * 1113 * @param contextHubId the ID of the hub to disable the nanoapp 1114 * @param transactionCallback the client-facing transaction callback interface 1115 * @param nanoAppId the ID of the nanoapp to disable 1116 * @throws IllegalStateException if the transaction queue is full 1117 */ 1118 @Override 1119 public void disableNanoApp( 1120 int contextHubId, IContextHubTransactionCallback transactionCallback, long nanoAppId) 1121 throws RemoteException { 1122 super.disableNanoApp_enforcePermission(); 1123 1124 if (!checkHalProxyAndContextHubId( 1125 contextHubId, transactionCallback, ContextHubTransaction.TYPE_DISABLE_NANOAPP)) { 1126 return; 1127 } 1128 1129 ContextHubServiceTransaction transaction = mTransactionManager.createDisableTransaction( 1130 contextHubId, nanoAppId, transactionCallback, getCallingPackageName()); 1131 mTransactionManager.addTransaction(transaction); 1132 } 1133 1134 @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) 1135 /** 1136 * Queries for a list of nanoapps from the specified Context hub. 1137 * 1138 * @param contextHubId the ID of the hub to query 1139 * @param transactionCallback the client-facing transaction callback interface 1140 * @throws IllegalStateException if the transaction queue is full 1141 */ 1142 @Override 1143 public void queryNanoApps(int contextHubId, IContextHubTransactionCallback transactionCallback) 1144 throws RemoteException { 1145 super.queryNanoApps_enforcePermission(); 1146 1147 if (!checkHalProxyAndContextHubId( 1148 contextHubId, transactionCallback, ContextHubTransaction.TYPE_QUERY_NANOAPPS)) { 1149 return; 1150 } 1151 1152 ContextHubServiceTransaction transaction = mTransactionManager.createQueryTransaction( 1153 contextHubId, transactionCallback, getCallingPackageName()); 1154 mTransactionManager.addTransaction(transaction); 1155 } 1156 1157 @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) 1158 /** 1159 * Queries for a list of preloaded nanoapp IDs from the specified Context Hub. 1160 * 1161 * @param hubInfo The Context Hub to query a list of nanoapps from. 1162 * @return The list of 64-bit IDs of the preloaded nanoapps. 1163 * @throws NullPointerException if hubInfo is null 1164 */ 1165 @Override 1166 public long[] getPreloadedNanoAppIds(ContextHubInfo hubInfo) throws RemoteException { 1167 super.getPreloadedNanoAppIds_enforcePermission(); 1168 Objects.requireNonNull(hubInfo, "hubInfo cannot be null"); 1169 1170 long[] nanoappIds = mContextHubWrapper.getPreloadedNanoappIds(hubInfo.getId()); 1171 if (nanoappIds == null) { 1172 return new long[0]; 1173 } 1174 return nanoappIds; 1175 } 1176 1177 @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) 1178 /** 1179 * Puts the context hub in and out of test mode. Test mode is a clean state 1180 * where tests can be executed in the same environment. If enable is true, 1181 * this will enable test mode by unloading all nanoapps. If enable is false, 1182 * this will disable test mode and reverse the actions of enabling test mode 1183 * by loading all preloaded nanoapps. This puts CHRE in a normal state. 1184 * 1185 * This should only be used for a test environment, either through a 1186 * @TestApi or development tools. This should not be used in a production 1187 * environment. 1188 * 1189 * @param enable If true, put the context hub in test mode. If false, disable 1190 * test mode. 1191 * @return If true, the operation was successful; false otherwise. 1192 */ 1193 @Override 1194 public boolean setTestMode(boolean enable) { 1195 super.setTestMode_enforcePermission(); 1196 boolean status = mContextHubWrapper.setTestMode(enable); 1197 1198 // Query nanoapps to update service state after test mode state change. 1199 for (int contextHubId: mDefaultClientMap.keySet()) { 1200 queryNanoAppsInternal(contextHubId); 1201 } 1202 return status; 1203 } 1204 1205 @Override 1206 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 1207 if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; 1208 1209 for (String arg : args) { 1210 if ("--proto".equals(arg)) { 1211 dump(new ProtoOutputStream(fd)); 1212 return; 1213 } 1214 } 1215 1216 pw.println("Dumping ContextHub Service"); 1217 1218 pw.println(""); 1219 // dump ContextHubInfo 1220 pw.println("=================== CONTEXT HUBS ===================="); 1221 for (ContextHubInfo hubInfo : mContextHubIdToInfoMap.values()) { 1222 pw.println(hubInfo); 1223 } 1224 pw.println("Supported permissions: " 1225 + Arrays.toString(mSupportedContextHubPerms.toArray())); 1226 pw.println(""); 1227 pw.println("=================== NANOAPPS ===================="); 1228 // Dump nanoAppHash 1229 mNanoAppStateManager.foreachNanoAppInstanceInfo(pw::println); 1230 1231 pw.println(""); 1232 pw.println("=================== PRELOADED NANOAPPS ===================="); 1233 dumpPreloadedNanoapps(pw); 1234 1235 pw.println(""); 1236 pw.println("=================== CLIENTS ===================="); 1237 pw.println(mClientManager); 1238 1239 pw.println(""); 1240 pw.println("=================== TRANSACTIONS ===================="); 1241 pw.println(mTransactionManager); 1242 1243 pw.println(""); 1244 pw.println("=================== EVENTS ===================="); 1245 pw.println(ContextHubEventLogger.getInstance()); 1246 1247 // dump eventLog 1248 } 1249 1250 /* package */ void denyClientAuthState(int contextHubId, String packageName, long nanoAppId) { 1251 Log.i(TAG, "Denying " + packageName + " access to " + Long.toHexString(nanoAppId) 1252 + " on context hub # " + contextHubId); 1253 1254 mClientManager.forEachClientOfHub(contextHubId, client -> { 1255 if (client.getPackageName().equals(packageName)) { 1256 client.updateNanoAppAuthState( 1257 nanoAppId, /* nanoappPermissions= */ Collections.emptyList(), 1258 /* gracePeriodExpired= */ false, /* forceDenied= */ true); 1259 } 1260 }); 1261 } 1262 1263 private void dump(ProtoOutputStream proto) { 1264 mContextHubIdToInfoMap.values().forEach(hubInfo -> { 1265 long token = proto.start(ContextHubServiceProto.CONTEXT_HUB_INFO); 1266 hubInfo.dump(proto); 1267 proto.end(token); 1268 }); 1269 1270 long token = proto.start(ContextHubServiceProto.CLIENT_MANAGER); 1271 mClientManager.dump(proto); 1272 proto.end(token); 1273 1274 proto.flush(); 1275 } 1276 1277 /** Dumps preloaded nanoapps to the console */ 1278 private void dumpPreloadedNanoapps(PrintWriter pw) { 1279 if (mContextHubWrapper == null) { 1280 return; 1281 } 1282 1283 for (int contextHubId: mContextHubIdToInfoMap.keySet()) { 1284 long[] preloadedNanoappIds = mContextHubWrapper.getPreloadedNanoappIds(contextHubId); 1285 if (preloadedNanoappIds == null) { 1286 return; 1287 } 1288 1289 pw.print("Context Hub (id="); 1290 pw.print(contextHubId); 1291 pw.println("):"); 1292 for (long preloadedNanoappId : preloadedNanoappIds) { 1293 pw.print(" ID: 0x"); 1294 pw.println(Long.toHexString(preloadedNanoappId)); 1295 } 1296 } 1297 } 1298 1299 private void checkPermissions() { 1300 ContextHubServiceUtil.checkPermissions(mContext); 1301 } 1302 1303 private int onMessageReceiptOldApi( 1304 int msgType, int contextHubHandle, int appInstance, byte[] data) { 1305 if (data == null) { 1306 return -1; 1307 } 1308 1309 int msgVersion = 0; 1310 // Synchronize access to mCallbacksList to prevent more than one outstanding broadcast as 1311 // that will cause a crash. 1312 synchronized (mCallbacksList) { 1313 int callbacksCount = mCallbacksList.beginBroadcast(); 1314 if (DEBUG_LOG_ENABLED) { 1315 Log.v(TAG, "Sending message " + msgType + " version " + msgVersion 1316 + " from hubHandle " + contextHubHandle + ", appInstance " + appInstance 1317 + ", callBackCount " + callbacksCount); 1318 } 1319 1320 if (callbacksCount < 1) { 1321 if (DEBUG_LOG_ENABLED) { 1322 Log.v(TAG, "No message callbacks registered."); 1323 } 1324 return 0; 1325 } 1326 1327 ContextHubMessage msg = new ContextHubMessage(msgType, msgVersion, data); 1328 for (int i = 0; i < callbacksCount; ++i) { 1329 IContextHubCallback callback = mCallbacksList.getBroadcastItem(i); 1330 try { 1331 callback.onMessageReceipt(contextHubHandle, appInstance, msg); 1332 } catch (RemoteException e) { 1333 Log.i(TAG, "Exception (" + e + ") calling remote callback (" + callback + ")."); 1334 continue; 1335 } 1336 } 1337 mCallbacksList.finishBroadcast(); 1338 } 1339 return 0; 1340 } 1341 1342 /** 1343 * Validates the HAL proxy state and context hub ID to see if we can start the transaction. 1344 * 1345 * @param contextHubId the ID of the hub to start the transaction 1346 * @param callback the client transaction callback interface 1347 * @param transactionType the type of the transaction 1348 * @return {@code true} if mContextHubWrapper and contextHubId is valid, {@code false} otherwise 1349 */ 1350 private boolean checkHalProxyAndContextHubId( 1351 int contextHubId, IContextHubTransactionCallback callback, 1352 @ContextHubTransaction.Type int transactionType) { 1353 if (mContextHubWrapper == null) { 1354 try { 1355 callback.onTransactionComplete( 1356 ContextHubTransaction.RESULT_FAILED_HAL_UNAVAILABLE); 1357 } catch (RemoteException e) { 1358 Log.e(TAG, "RemoteException while calling onTransactionComplete", e); 1359 } 1360 return false; 1361 } 1362 if (!isValidContextHubId(contextHubId)) { 1363 Log.e(TAG, "Cannot start " 1364 + ContextHubTransaction.typeToString(transactionType, /* upperCase= */ false) 1365 + " transaction for invalid hub ID " + contextHubId); 1366 try { 1367 callback.onTransactionComplete(ContextHubTransaction.RESULT_FAILED_BAD_PARAMS); 1368 } catch (RemoteException e) { 1369 Log.e(TAG, "RemoteException while calling onTransactionComplete", e); 1370 } 1371 return false; 1372 } 1373 1374 return true; 1375 } 1376 1377 /** 1378 * Obtains the latest location setting value and notifies the Context Hub. 1379 */ 1380 private void sendLocationSettingUpdate() { 1381 boolean enabled = mContext.getSystemService(LocationManager.class) 1382 .isLocationEnabledForUser(UserHandle.CURRENT); 1383 mContextHubWrapper.onLocationSettingChanged(enabled); 1384 } 1385 1386 /** 1387 * Obtains the latest WiFi availability setting value and notifies the Context Hub. 1388 * 1389 * @param forceUpdate True to force send update to the Context Hub, otherwise only send the 1390 * update when the WiFi availability changes. 1391 */ 1392 private void sendWifiSettingUpdate(boolean forceUpdate) { 1393 synchronized (mSendWifiSettingUpdateLock) { 1394 WifiManager wifiManager = mContext.getSystemService(WifiManager.class); 1395 boolean wifiEnabled = wifiManager.isWifiEnabled(); 1396 boolean wifiScanEnabled = wifiManager.isScanAlwaysAvailable(); 1397 boolean wifiAvailable = wifiEnabled || wifiScanEnabled; 1398 if (forceUpdate || mIsWifiAvailable != wifiAvailable) { 1399 mIsWifiAvailable = wifiAvailable; 1400 mContextHubWrapper.onWifiSettingChanged(wifiAvailable); 1401 } 1402 if (forceUpdate || mIsWifiScanningEnabled != wifiScanEnabled) { 1403 mIsWifiScanningEnabled = wifiScanEnabled; 1404 mContextHubWrapper.onWifiScanningSettingChanged(wifiScanEnabled); 1405 } 1406 if (forceUpdate || mIsWifiMainEnabled != wifiEnabled) { 1407 mIsWifiMainEnabled = wifiEnabled; 1408 mContextHubWrapper.onWifiMainSettingChanged(wifiEnabled); 1409 } 1410 } 1411 } 1412 1413 /** 1414 * Obtains the latest BT availability setting value and notifies the Context Hub. 1415 * 1416 * @param forceUpdate True to force send update to the Context Hub, otherwise only send the 1417 * update when the BT availability changes. 1418 */ 1419 private void sendBtSettingUpdate(boolean forceUpdate) { 1420 final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); 1421 // Adapter may be null if BT is not supported. 1422 if (adapter != null) { 1423 boolean btEnabled = adapter.isEnabled(); 1424 boolean btScanEnabled = adapter.isBleScanAlwaysAvailable(); 1425 if (forceUpdate || mIsBtScanningEnabled != btScanEnabled) { 1426 mIsBtScanningEnabled = btScanEnabled; 1427 mContextHubWrapper.onBtScanningSettingChanged(btScanEnabled); 1428 } 1429 if (forceUpdate || mIsBtMainEnabled != btEnabled) { 1430 mIsBtMainEnabled = btEnabled; 1431 mContextHubWrapper.onBtMainSettingChanged(btEnabled); 1432 } 1433 } else { 1434 Log.d(TAG, "BT adapter not available. Defaulting to disabled"); 1435 if (forceUpdate || mIsBtMainEnabled) { 1436 mIsBtMainEnabled = false; 1437 mContextHubWrapper.onBtMainSettingChanged(mIsBtMainEnabled); 1438 } 1439 if (forceUpdate || mIsBtScanningEnabled) { 1440 mIsBtScanningEnabled = false; 1441 mContextHubWrapper.onBtScanningSettingChanged(mIsBtScanningEnabled); 1442 } 1443 } 1444 } 1445 1446 /** 1447 * Obtains the latest airplane mode setting value and notifies the Context Hub. 1448 */ 1449 private void sendAirplaneModeSettingUpdate() { 1450 boolean enabled = 1451 (Settings.Global.getInt(mContext.getContentResolver(), 1452 Settings.Global.AIRPLANE_MODE_ON, 0) 1453 == 1); 1454 mContextHubWrapper.onAirplaneModeSettingChanged(enabled); 1455 } 1456 1457 /** 1458 * Notifies a microphone disable settings change to the Context Hub. 1459 */ 1460 private void sendMicrophoneDisableSettingUpdate(boolean enabled) { 1461 Log.d(TAG, "Mic Disabled Setting: " + enabled); 1462 // The SensorPrivacyManager reports if microphone privacy was enabled, 1463 // which translates to microphone access being disabled (and vice-versa). 1464 // With this in mind, we flip the argument before piping it to CHRE. 1465 mContextHubWrapper.onMicrophoneSettingChanged(!enabled); 1466 } 1467 1468 /** 1469 * Obtains the latest microphone disabled setting for the current user and notifies the Context 1470 * Hub. 1471 */ 1472 private void sendMicrophoneDisableSettingUpdateForCurrentUser() { 1473 boolean isEnabled = mSensorPrivacyManagerInternal == null ? false : 1474 mSensorPrivacyManagerInternal.isSensorPrivacyEnabled( 1475 getCurrentUserId(), SensorPrivacyManager.Sensors.MICROPHONE); 1476 sendMicrophoneDisableSettingUpdate(isEnabled); 1477 } 1478 1479 /** 1480 * Invokes a daily timer to query all context hubs 1481 */ 1482 private void scheduleDailyMetricSnapshot() { 1483 Runnable queryAllContextHub = () -> { 1484 for (int contextHubId : mContextHubIdToInfoMap.keySet()) { 1485 mMetricQueryPendingContextHubIds.add(contextHubId); 1486 queryNanoAppsInternal(contextHubId); 1487 } 1488 }; 1489 try { 1490 mDailyMetricTimer.schedule(queryAllContextHub, PERIOD_METRIC_QUERY_DAYS, 1491 TimeUnit.DAYS); 1492 } catch (Exception e) { 1493 Log.e(TAG, "Error when schedule a timer", e); 1494 } 1495 } 1496 1497 private String getCallingPackageName() { 1498 return mContext.getPackageManager().getNameForUid(Binder.getCallingUid()); 1499 } 1500 1501 private int getCurrentUserId() { 1502 final long id = Binder.clearCallingIdentity(); 1503 try { 1504 UserInfo currentUser = ActivityManager.getService().getCurrentUser(); 1505 return currentUser.id; 1506 } catch (RemoteException e) { 1507 // Activity manager not running, nothing we can do - assume user 0. 1508 } finally { 1509 Binder.restoreCallingIdentity(id); 1510 } 1511 return UserHandle.USER_SYSTEM; 1512 } 1513 1514 /** 1515 * Send a microphone disable settings update whenever the foreground user changes. We always 1516 * send a settings update regardless of the previous state for the same user since the CHRE 1517 * framework is expected to handle repeated identical setting update. 1518 */ 1519 public void onUserChanged() { 1520 Log.d(TAG, "User changed to id: " + getCurrentUserId()); 1521 sendLocationSettingUpdate(); 1522 sendMicrophoneDisableSettingUpdateForCurrentUser(); 1523 } 1524 } 1525