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