1 /* 2 * Copyright (C) 2018 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.sensorprivacy; 18 19 import static android.Manifest.permission.MANAGE_SENSOR_PRIVACY; 20 import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_CAMERA; 21 import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_MICROPHONE; 22 import static android.app.ActivityManager.RunningServiceInfo; 23 import static android.app.ActivityManager.RunningTaskInfo; 24 import static android.app.AppOpsManager.MODE_IGNORED; 25 import static android.app.AppOpsManager.OP_CAMERA; 26 import static android.app.AppOpsManager.OP_PHONE_CALL_CAMERA; 27 import static android.app.AppOpsManager.OP_PHONE_CALL_MICROPHONE; 28 import static android.app.AppOpsManager.OP_RECEIVE_AMBIENT_TRIGGER_AUDIO; 29 import static android.app.AppOpsManager.OP_RECORD_AUDIO; 30 import static android.content.Intent.EXTRA_PACKAGE_NAME; 31 import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS; 32 import static android.content.Intent.FLAG_ACTIVITY_NO_USER_ACTION; 33 import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY; 34 import static android.content.pm.PackageManager.PERMISSION_GRANTED; 35 import static android.hardware.SensorPrivacyManager.EXTRA_ALL_SENSORS; 36 import static android.hardware.SensorPrivacyManager.EXTRA_SENSOR; 37 import static android.hardware.SensorPrivacyManager.Sensors.CAMERA; 38 import static android.hardware.SensorPrivacyManager.Sensors.MICROPHONE; 39 import static android.hardware.SensorPrivacyManager.Sources.DIALOG; 40 import static android.hardware.SensorPrivacyManager.Sources.OTHER; 41 import static android.hardware.SensorPrivacyManager.Sources.QS_TILE; 42 import static android.hardware.SensorPrivacyManager.Sources.SETTINGS; 43 import static android.hardware.SensorPrivacyManager.Sources.SHELL; 44 import static android.hardware.SensorPrivacyManager.TOGGLE_TYPE_HARDWARE; 45 import static android.hardware.SensorPrivacyManager.TOGGLE_TYPE_SOFTWARE; 46 import static android.os.UserHandle.USER_NULL; 47 import static android.service.SensorPrivacyIndividualEnabledSensorProto.UNKNOWN; 48 49 import static com.android.internal.util.FrameworkStatsLog.PRIVACY_SENSOR_TOGGLE_INTERACTION; 50 import static com.android.internal.util.FrameworkStatsLog.PRIVACY_SENSOR_TOGGLE_INTERACTION__ACTION__ACTION_UNKNOWN; 51 import static com.android.internal.util.FrameworkStatsLog.PRIVACY_SENSOR_TOGGLE_INTERACTION__ACTION__TOGGLE_OFF; 52 import static com.android.internal.util.FrameworkStatsLog.PRIVACY_SENSOR_TOGGLE_INTERACTION__ACTION__TOGGLE_ON; 53 import static com.android.internal.util.FrameworkStatsLog.PRIVACY_SENSOR_TOGGLE_INTERACTION__SENSOR__CAMERA; 54 import static com.android.internal.util.FrameworkStatsLog.PRIVACY_SENSOR_TOGGLE_INTERACTION__SENSOR__MICROPHONE; 55 import static com.android.internal.util.FrameworkStatsLog.PRIVACY_SENSOR_TOGGLE_INTERACTION__SENSOR__SENSOR_UNKNOWN; 56 import static com.android.internal.util.FrameworkStatsLog.PRIVACY_SENSOR_TOGGLE_INTERACTION__SOURCE__DIALOG; 57 import static com.android.internal.util.FrameworkStatsLog.PRIVACY_SENSOR_TOGGLE_INTERACTION__SOURCE__QS_TILE; 58 import static com.android.internal.util.FrameworkStatsLog.PRIVACY_SENSOR_TOGGLE_INTERACTION__SOURCE__SETTINGS; 59 import static com.android.internal.util.FrameworkStatsLog.PRIVACY_SENSOR_TOGGLE_INTERACTION__SOURCE__SOURCE_UNKNOWN; 60 import static com.android.internal.util.FrameworkStatsLog.write; 61 62 import android.annotation.NonNull; 63 import android.annotation.Nullable; 64 import android.annotation.UserIdInt; 65 import android.app.ActivityManager; 66 import android.app.ActivityManagerInternal; 67 import android.app.ActivityOptions; 68 import android.app.ActivityTaskManager; 69 import android.app.AppOpsManager; 70 import android.app.AppOpsManagerInternal; 71 import android.app.KeyguardManager; 72 import android.app.Notification; 73 import android.app.NotificationChannel; 74 import android.app.NotificationManager; 75 import android.app.PendingIntent; 76 import android.content.BroadcastReceiver; 77 import android.content.ComponentName; 78 import android.content.Context; 79 import android.content.Intent; 80 import android.content.IntentFilter; 81 import android.content.pm.PackageManager; 82 import android.content.pm.PackageManagerInternal; 83 import android.content.res.Configuration; 84 import android.graphics.drawable.Icon; 85 import android.hardware.ISensorPrivacyListener; 86 import android.hardware.ISensorPrivacyManager; 87 import android.hardware.SensorPrivacyManager; 88 import android.hardware.SensorPrivacyManagerInternal; 89 import android.os.Binder; 90 import android.os.Bundle; 91 import android.os.Handler; 92 import android.os.IBinder; 93 import android.os.Looper; 94 import android.os.Process; 95 import android.os.RemoteCallbackList; 96 import android.os.RemoteException; 97 import android.os.ResultReceiver; 98 import android.os.ShellCallback; 99 import android.os.ShellCommand; 100 import android.os.SystemClock; 101 import android.os.UserHandle; 102 import android.os.UserManager; 103 import android.provider.Settings; 104 import android.service.voice.VoiceInteractionManagerInternal; 105 import android.telephony.TelephonyCallback; 106 import android.telephony.TelephonyManager; 107 import android.telephony.emergency.EmergencyNumber; 108 import android.text.Html; 109 import android.text.Spanned; 110 import android.text.TextUtils; 111 import android.util.ArrayMap; 112 import android.util.ArraySet; 113 import android.util.IndentingPrintWriter; 114 import android.util.Log; 115 import android.util.Pair; 116 import android.util.proto.ProtoOutputStream; 117 118 import com.android.internal.R; 119 import com.android.internal.annotations.GuardedBy; 120 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; 121 import com.android.internal.os.BackgroundThread; 122 import com.android.internal.util.DumpUtils; 123 import com.android.internal.util.FunctionalUtils; 124 import com.android.internal.util.dump.DualDumpOutputStream; 125 import com.android.internal.util.function.pooled.PooledLambda; 126 import com.android.server.FgThread; 127 import com.android.server.LocalServices; 128 import com.android.server.SystemService; 129 import com.android.server.pm.UserManagerInternal; 130 131 import java.io.FileDescriptor; 132 import java.io.PrintWriter; 133 import java.util.ArrayList; 134 import java.util.Arrays; 135 import java.util.List; 136 import java.util.NoSuchElementException; 137 import java.util.Objects; 138 139 /** @hide */ 140 public final class SensorPrivacyService extends SystemService { 141 142 private static final String TAG = SensorPrivacyService.class.getSimpleName(); 143 private static final boolean DEBUG = false; 144 private static final boolean DEBUG_LOGGING = false; 145 146 private static final String SENSOR_PRIVACY_CHANNEL_ID = Context.SENSOR_PRIVACY_SERVICE; 147 private static final String ACTION_DISABLE_TOGGLE_SENSOR_PRIVACY = 148 SensorPrivacyService.class.getName() + ".action.disable_sensor_privacy"; 149 150 public static final int REMINDER_DIALOG_DELAY_MILLIS = 500; 151 152 private final Context mContext; 153 private final SensorPrivacyServiceImpl mSensorPrivacyServiceImpl; 154 private final UserManagerInternal mUserManagerInternal; 155 private final ActivityManager mActivityManager; 156 private final ActivityManagerInternal mActivityManagerInternal; 157 private final ActivityTaskManager mActivityTaskManager; 158 private final AppOpsManager mAppOpsManager; 159 private final AppOpsManagerInternal mAppOpsManagerInternal; 160 private final TelephonyManager mTelephonyManager; 161 private final PackageManagerInternal mPackageManagerInternal; 162 163 private CameraPrivacyLightController mCameraPrivacyLightController; 164 165 private final IBinder mAppOpsRestrictionToken = new Binder(); 166 167 private SensorPrivacyManagerInternalImpl mSensorPrivacyManagerInternal; 168 169 private CallStateHelper mCallStateHelper; 170 private KeyguardManager mKeyguardManager; 171 172 private int mCurrentUser = USER_NULL; 173 SensorPrivacyService(Context context)174 public SensorPrivacyService(Context context) { 175 super(context); 176 177 mContext = context; 178 mAppOpsManager = context.getSystemService(AppOpsManager.class); 179 mAppOpsManagerInternal = getLocalService(AppOpsManagerInternal.class); 180 mUserManagerInternal = getLocalService(UserManagerInternal.class); 181 mActivityManager = context.getSystemService(ActivityManager.class); 182 mActivityManagerInternal = getLocalService(ActivityManagerInternal.class); 183 mActivityTaskManager = context.getSystemService(ActivityTaskManager.class); 184 mTelephonyManager = context.getSystemService(TelephonyManager.class); 185 mPackageManagerInternal = getLocalService(PackageManagerInternal.class); 186 mSensorPrivacyServiceImpl = new SensorPrivacyServiceImpl(); 187 } 188 189 @Override onStart()190 public void onStart() { 191 publishBinderService(Context.SENSOR_PRIVACY_SERVICE, mSensorPrivacyServiceImpl); 192 mSensorPrivacyManagerInternal = new SensorPrivacyManagerInternalImpl(); 193 publishLocalService(SensorPrivacyManagerInternal.class, 194 mSensorPrivacyManagerInternal); 195 } 196 197 @Override onBootPhase(int phase)198 public void onBootPhase(int phase) { 199 if (phase == PHASE_SYSTEM_SERVICES_READY) { 200 mKeyguardManager = mContext.getSystemService(KeyguardManager.class); 201 mCallStateHelper = new CallStateHelper(); 202 } else if (phase == PHASE_ACTIVITY_MANAGER_READY) { 203 mCameraPrivacyLightController = new CameraPrivacyLightController(mContext); 204 } 205 } 206 207 @Override onUserStarting(TargetUser user)208 public void onUserStarting(TargetUser user) { 209 if (mCurrentUser == USER_NULL) { 210 mCurrentUser = user.getUserIdentifier(); 211 mSensorPrivacyServiceImpl.userSwitching(USER_NULL, user.getUserIdentifier()); 212 } 213 } 214 215 @Override onUserSwitching(TargetUser from, TargetUser to)216 public void onUserSwitching(TargetUser from, TargetUser to) { 217 mCurrentUser = to.getUserIdentifier(); 218 mSensorPrivacyServiceImpl.userSwitching(from.getUserIdentifier(), to.getUserIdentifier()); 219 } 220 221 class SensorPrivacyServiceImpl extends ISensorPrivacyManager.Stub implements 222 AppOpsManager.OnOpNotedListener, AppOpsManager.OnOpStartedListener, 223 IBinder.DeathRecipient, UserManagerInternal.UserRestrictionsListener { 224 225 private final SensorPrivacyHandler mHandler; 226 private final Object mLock = new Object(); 227 228 private SensorPrivacyStateController mSensorPrivacyStateController; 229 230 /** 231 * Packages for which not to show sensor use reminders. 232 * 233 * <Package, User> -> list of suppressor tokens 234 */ 235 @GuardedBy("mLock") 236 private ArrayMap<Pair<Integer, UserHandle>, ArrayList<IBinder>> mSuppressReminders = 237 new ArrayMap<>(); 238 239 private final ArrayMap<SensorUseReminderDialogInfo, ArraySet<Integer>> 240 mQueuedSensorUseReminderDialogs = new ArrayMap<>(); 241 242 private class SensorUseReminderDialogInfo { 243 private int mTaskId; 244 private UserHandle mUser; 245 private String mPackageName; 246 SensorUseReminderDialogInfo(int taskId, UserHandle user, String packageName)247 SensorUseReminderDialogInfo(int taskId, UserHandle user, String packageName) { 248 mTaskId = taskId; 249 mUser = user; 250 mPackageName = packageName; 251 } 252 253 @Override equals(Object o)254 public boolean equals(Object o) { 255 if (this == o) return true; 256 if (o == null || !(o instanceof SensorUseReminderDialogInfo)) return false; 257 SensorUseReminderDialogInfo that = (SensorUseReminderDialogInfo) o; 258 return mTaskId == that.mTaskId 259 && Objects.equals(mUser, that.mUser) 260 && Objects.equals(mPackageName, that.mPackageName); 261 } 262 263 @Override hashCode()264 public int hashCode() { 265 return Objects.hash(mTaskId, mUser, mPackageName); 266 } 267 } 268 SensorPrivacyServiceImpl()269 SensorPrivacyServiceImpl() { 270 mHandler = new SensorPrivacyHandler(FgThread.get().getLooper(), mContext); 271 mSensorPrivacyStateController = SensorPrivacyStateController.getInstance(); 272 273 int[] micAndCameraOps = new int[]{OP_RECORD_AUDIO, OP_PHONE_CALL_MICROPHONE, 274 OP_CAMERA, OP_PHONE_CALL_CAMERA}; 275 mAppOpsManager.startWatchingNoted(micAndCameraOps, this); 276 mAppOpsManager.startWatchingStarted(micAndCameraOps, this); 277 278 279 280 mContext.registerReceiver(new BroadcastReceiver() { 281 @Override 282 public void onReceive(Context context, Intent intent) { 283 setToggleSensorPrivacy( 284 ((UserHandle) intent.getParcelableExtra( 285 Intent.EXTRA_USER)).getIdentifier(), OTHER, 286 intent.getIntExtra(EXTRA_SENSOR, UNKNOWN), false); 287 } 288 }, new IntentFilter(ACTION_DISABLE_TOGGLE_SENSOR_PRIVACY), 289 MANAGE_SENSOR_PRIVACY, null, Context.RECEIVER_EXPORTED); 290 291 mContext.registerReceiver(new BroadcastReceiver() { 292 @Override 293 public void onReceive(Context context, Intent intent) { 294 mSensorPrivacyStateController.forEachState( 295 (toggleType, userId, sensor, state) -> 296 logSensorPrivacyToggle(OTHER, sensor, state.isEnabled(), 297 state.getLastChange(), true) 298 ); 299 } 300 }, new IntentFilter(Intent.ACTION_SHUTDOWN)); 301 302 mUserManagerInternal.addUserRestrictionsListener(this); 303 304 mSensorPrivacyStateController.setAllSensorPrivacyListener( 305 mHandler, mHandler::handleSensorPrivacyChanged); 306 mSensorPrivacyStateController.setSensorPrivacyListener( 307 mHandler, 308 (toggleType, userId, sensor, state) -> mHandler.handleSensorPrivacyChanged( 309 userId, toggleType, sensor, state.isEnabled())); 310 } 311 312 @Override onUserRestrictionsChanged(int userId, Bundle newRestrictions, Bundle prevRestrictions)313 public void onUserRestrictionsChanged(int userId, Bundle newRestrictions, 314 Bundle prevRestrictions) { 315 // Reset sensor privacy when restriction is added 316 if (!prevRestrictions.getBoolean(UserManager.DISALLOW_CAMERA_TOGGLE) 317 && newRestrictions.getBoolean(UserManager.DISALLOW_CAMERA_TOGGLE)) { 318 setToggleSensorPrivacyUnchecked(TOGGLE_TYPE_SOFTWARE, userId, OTHER, CAMERA, false); 319 } 320 if (!prevRestrictions.getBoolean(UserManager.DISALLOW_MICROPHONE_TOGGLE) 321 && newRestrictions.getBoolean(UserManager.DISALLOW_MICROPHONE_TOGGLE)) { 322 setToggleSensorPrivacyUnchecked(TOGGLE_TYPE_SOFTWARE, userId, OTHER, MICROPHONE, 323 false); 324 } 325 } 326 327 @Override onOpStarted(int code, int uid, String packageName, String attributionTag, @AppOpsManager.OpFlags int flags, @AppOpsManager.Mode int result)328 public void onOpStarted(int code, int uid, String packageName, String attributionTag, 329 @AppOpsManager.OpFlags int flags, @AppOpsManager.Mode int result) { 330 onOpNoted(code, uid, packageName, attributionTag, flags, result); 331 } 332 333 @Override onOpNoted(int code, int uid, String packageName, String attributionTag, @AppOpsManager.OpFlags int flags, @AppOpsManager.Mode int result)334 public void onOpNoted(int code, int uid, String packageName, 335 String attributionTag, @AppOpsManager.OpFlags int flags, 336 @AppOpsManager.Mode int result) { 337 if ((flags & AppOpsManager.OP_FLAGS_ALL_TRUSTED) == 0) { 338 return; 339 } 340 341 int sensor; 342 if (result == MODE_IGNORED) { 343 if (code == OP_RECORD_AUDIO || code == OP_PHONE_CALL_MICROPHONE) { 344 sensor = MICROPHONE; 345 } else if (code == OP_CAMERA || code == OP_PHONE_CALL_CAMERA) { 346 sensor = CAMERA; 347 } else { 348 return; 349 } 350 } else { 351 return; 352 } 353 354 final long token = Binder.clearCallingIdentity(); 355 try { 356 onSensorUseStarted(uid, packageName, sensor); 357 } finally { 358 Binder.restoreCallingIdentity(token); 359 } 360 } 361 362 /** 363 * Called when a sensor protected by toggle sensor privacy is attempting to get used. 364 * 365 * @param uid The uid of the app using the sensor 366 * @param packageName The package name of the app using the sensor 367 * @param sensor The sensor that is attempting to be used 368 */ onSensorUseStarted(int uid, String packageName, int sensor)369 private void onSensorUseStarted(int uid, String packageName, int sensor) { 370 UserHandle user = UserHandle.of(mCurrentUser); 371 if (!isCombinedToggleSensorPrivacyEnabled(sensor)) { 372 return; 373 } 374 375 if (uid == Process.SYSTEM_UID) { 376 // If the system uid is being blamed for sensor access, the ui must be shown 377 // explicitly using SensorPrivacyManager#showSensorUseDialog 378 return; 379 } 380 381 synchronized (mLock) { 382 if (mSuppressReminders.containsKey(new Pair<>(sensor, user))) { 383 Log.d(TAG, 384 "Suppressed sensor privacy reminder for " + packageName + "/" 385 + user); 386 return; 387 } 388 } 389 390 // TODO: Handle reminders with multiple sensors 391 392 // - If we have a likely activity that triggered the sensor use overlay a dialog over 393 // it. This should be the most common case. 394 // - If there is no use visible entity that triggered the sensor don't show anything as 395 // this is - from the point of the user - a background usage 396 // - Otherwise show a notification as we are not quite sure where to display the dialog. 397 398 List<RunningTaskInfo> tasksOfPackageUsingSensor = new ArrayList<>(); 399 400 List<RunningTaskInfo> tasks = mActivityTaskManager.getTasks(Integer.MAX_VALUE); 401 int numTasks = tasks.size(); 402 for (int taskNum = 0; taskNum < numTasks; taskNum++) { 403 RunningTaskInfo task = tasks.get(taskNum); 404 405 if (task.isVisible) { 406 if (task.topActivity.getPackageName().equals(packageName)) { 407 if (task.isFocused) { 408 // There is the one focused activity 409 enqueueSensorUseReminderDialogAsync(task.taskId, user, packageName, 410 sensor); 411 return; 412 } 413 414 tasksOfPackageUsingSensor.add(task); 415 } else if (task.topActivity.flattenToString().equals( 416 getSensorUseActivityName(new ArraySet<>(Arrays.asList(sensor)))) 417 && task.isFocused) { 418 enqueueSensorUseReminderDialogAsync(task.taskId, user, packageName, 419 sensor); 420 } 421 } 422 } 423 424 // TODO: Test this case 425 // There is one or more non-focused activity 426 if (tasksOfPackageUsingSensor.size() == 1) { 427 enqueueSensorUseReminderDialogAsync(tasksOfPackageUsingSensor.get(0).taskId, user, 428 packageName, sensor); 429 return; 430 } else if (tasksOfPackageUsingSensor.size() > 1) { 431 showSensorUseReminderNotification(user, packageName, sensor); 432 return; 433 } 434 435 // TODO: Test this case 436 // Check if there is a foreground service for this package 437 List<RunningServiceInfo> services = mActivityManager.getRunningServices( 438 Integer.MAX_VALUE); 439 int numServices = services.size(); 440 for (int serviceNum = 0; serviceNum < numServices; serviceNum++) { 441 RunningServiceInfo service = services.get(serviceNum); 442 443 if (service.foreground && service.service.getPackageName().equals(packageName)) { 444 showSensorUseReminderNotification(user, packageName, sensor); 445 return; 446 } 447 } 448 449 String inputMethodComponent = Settings.Secure.getStringForUser( 450 mContext.getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD, 451 user.getIdentifier()); 452 String inputMethodPackageName = null; 453 if (inputMethodComponent != null) { 454 inputMethodPackageName = ComponentName.unflattenFromString( 455 inputMethodComponent).getPackageName(); 456 } 457 458 int capability; 459 try { 460 capability = mActivityManagerInternal.getUidCapability(uid); 461 } catch (IllegalArgumentException e) { 462 Log.w(TAG, e); 463 return; 464 } 465 466 if (sensor == MICROPHONE) { 467 VoiceInteractionManagerInternal voiceInteractionManagerInternal = 468 LocalServices.getService(VoiceInteractionManagerInternal.class); 469 if (voiceInteractionManagerInternal != null 470 && voiceInteractionManagerInternal.hasActiveSession(packageName)) { 471 enqueueSensorUseReminderDialogAsync(-1, user, packageName, sensor); 472 return; 473 } 474 475 if (TextUtils.equals(packageName, inputMethodPackageName) 476 && (capability & PROCESS_CAPABILITY_FOREGROUND_MICROPHONE) != 0) { 477 enqueueSensorUseReminderDialogAsync(-1, user, packageName, sensor); 478 return; 479 } 480 } 481 482 if (sensor == CAMERA && TextUtils.equals(packageName, inputMethodPackageName) 483 && (capability & PROCESS_CAPABILITY_FOREGROUND_CAMERA) != 0) { 484 enqueueSensorUseReminderDialogAsync(-1, user, packageName, sensor); 485 return; 486 } 487 488 Log.i(TAG, packageName + "/" + uid + " started using sensor " + sensor 489 + " but no activity or foreground service was running. The user will not be" 490 + " informed. System components should check if sensor privacy is enabled for" 491 + " the sensor before accessing it."); 492 } 493 494 /** 495 * Show a dialog that informs the user that a sensor use or a blocked sensor started. 496 * The user can then react to this event. 497 * 498 * @param taskId The task this dialog should be overlaid on. 499 * @param user The user of the package using the sensor. 500 * @param packageName The name of the package using the sensor. 501 * @param sensor The sensor that is being used. 502 */ enqueueSensorUseReminderDialogAsync(int taskId, @NonNull UserHandle user, @NonNull String packageName, int sensor)503 private void enqueueSensorUseReminderDialogAsync(int taskId, @NonNull UserHandle user, 504 @NonNull String packageName, int sensor) { 505 mHandler.sendMessage(PooledLambda.obtainMessage( 506 SensorPrivacyServiceImpl::enqueueSensorUseReminderDialog, this, taskId, user, 507 packageName, sensor)); 508 } 509 enqueueSensorUseReminderDialog(int taskId, @NonNull UserHandle user, @NonNull String packageName, int sensor)510 private void enqueueSensorUseReminderDialog(int taskId, @NonNull UserHandle user, 511 @NonNull String packageName, int sensor) { 512 SensorUseReminderDialogInfo info = 513 new SensorUseReminderDialogInfo(taskId, user, packageName); 514 if (!mQueuedSensorUseReminderDialogs.containsKey(info)) { 515 ArraySet<Integer> sensors = new ArraySet<>(); 516 if (sensor == MICROPHONE && mSuppressReminders.containsKey(new Pair<>(CAMERA, user)) 517 || sensor == CAMERA && mSuppressReminders 518 .containsKey(new Pair<>(MICROPHONE, user))) { 519 sensors.add(MICROPHONE); 520 sensors.add(CAMERA); 521 } else { 522 sensors.add(sensor); 523 } 524 mQueuedSensorUseReminderDialogs.put(info, sensors); 525 mHandler.sendMessageDelayed(PooledLambda.obtainMessage( 526 SensorPrivacyServiceImpl::showSensorUserReminderDialog, this, info), 527 REMINDER_DIALOG_DELAY_MILLIS); 528 return; 529 } 530 ArraySet<Integer> sensors = mQueuedSensorUseReminderDialogs.get(info); 531 sensors.add(sensor); 532 } 533 showSensorUserReminderDialog(@onNull SensorUseReminderDialogInfo info)534 private void showSensorUserReminderDialog(@NonNull SensorUseReminderDialogInfo info) { 535 ArraySet<Integer> sensors = mQueuedSensorUseReminderDialogs.get(info); 536 mQueuedSensorUseReminderDialogs.remove(info); 537 if (sensors == null) { 538 Log.e(TAG, "Unable to show sensor use dialog because sensor set is null." 539 + " Was the dialog queue modified from outside the handler thread?"); 540 return; 541 } 542 Intent dialogIntent = new Intent(); 543 dialogIntent.setComponent( 544 ComponentName.unflattenFromString(getSensorUseActivityName(sensors))); 545 546 ActivityOptions options = ActivityOptions.makeBasic(); 547 options.setLaunchTaskId(info.mTaskId); 548 options.setTaskOverlay(true, true); 549 550 dialogIntent.addFlags( 551 FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS | FLAG_ACTIVITY_NO_USER_ACTION); 552 553 dialogIntent.putExtra(EXTRA_PACKAGE_NAME, info.mPackageName); 554 if (sensors.size() == 1) { 555 dialogIntent.putExtra(EXTRA_SENSOR, sensors.valueAt(0)); 556 } else if (sensors.size() == 2) { 557 dialogIntent.putExtra(EXTRA_ALL_SENSORS, true); 558 } else { 559 // Currently the only cases can be 1 or two 560 Log.e(TAG, "Attempted to show sensor use dialog for " + sensors.size() 561 + " sensors"); 562 return; 563 } 564 mContext.startActivityAsUser(dialogIntent, options.toBundle(), UserHandle.SYSTEM); 565 } 566 567 /** 568 * Get the activity component based on which privacy toggles are enabled. 569 * @param sensors 570 * @return component name to launch 571 */ getSensorUseActivityName(ArraySet<Integer> sensors)572 private String getSensorUseActivityName(ArraySet<Integer> sensors) { 573 for (Integer sensor : sensors) { 574 if (isToggleSensorPrivacyEnabled(TOGGLE_TYPE_HARDWARE, sensor)) { 575 return mContext.getResources().getString( 576 R.string.config_sensorUseStartedActivity_hwToggle); 577 } 578 } 579 return mContext.getResources().getString(R.string.config_sensorUseStartedActivity); 580 } 581 582 /** 583 * Show a notification that informs the user that a sensor use or a blocked sensor started. 584 * The user can then react to this event. 585 * 586 * @param user The user of the package using the sensor. 587 * @param packageName The name of the package using the sensor. 588 * @param sensor The sensor that is being used. 589 */ showSensorUseReminderNotification(@onNull UserHandle user, @NonNull String packageName, int sensor)590 private void showSensorUseReminderNotification(@NonNull UserHandle user, 591 @NonNull String packageName, int sensor) { 592 int iconRes; 593 int messageRes; 594 int notificationId; 595 596 CharSequence packageLabel; 597 try { 598 packageLabel = getUiContext().getPackageManager() 599 .getApplicationInfoAsUser(packageName, 0, user) 600 .loadLabel(mContext.getPackageManager()); 601 } catch (PackageManager.NameNotFoundException e) { 602 Log.e(TAG, "Cannot show sensor use notification for " + packageName); 603 return; 604 } 605 606 if (sensor == MICROPHONE) { 607 iconRes = R.drawable.ic_mic_blocked; 608 messageRes = R.string.sensor_privacy_start_use_mic_notification_content_title; 609 notificationId = SystemMessage.NOTE_UNBLOCK_MIC_TOGGLE; 610 } else { 611 iconRes = R.drawable.ic_camera_blocked; 612 messageRes = R.string.sensor_privacy_start_use_camera_notification_content_title; 613 notificationId = SystemMessage.NOTE_UNBLOCK_CAM_TOGGLE; 614 } 615 616 NotificationManager notificationManager = 617 mContext.getSystemService(NotificationManager.class); 618 NotificationChannel channel = new NotificationChannel( 619 SENSOR_PRIVACY_CHANNEL_ID, 620 getUiContext().getString(R.string.sensor_privacy_notification_channel_label), 621 NotificationManager.IMPORTANCE_HIGH); 622 channel.setSound(null, null); 623 channel.setBypassDnd(true); 624 channel.enableVibration(false); 625 channel.setBlockable(false); 626 627 notificationManager.createNotificationChannel(channel); 628 629 Icon icon = Icon.createWithResource(getUiContext().getResources(), iconRes); 630 631 String contentTitle = getUiContext().getString(messageRes); 632 Spanned contentText = Html.fromHtml(getUiContext().getString( 633 R.string.sensor_privacy_start_use_notification_content_text, packageLabel), 0); 634 PendingIntent contentIntent = PendingIntent.getActivity(mContext, sensor, 635 new Intent(Settings.ACTION_PRIVACY_SETTINGS), 636 PendingIntent.FLAG_IMMUTABLE 637 | PendingIntent.FLAG_UPDATE_CURRENT); 638 639 String actionTitle = getUiContext().getString( 640 R.string.sensor_privacy_start_use_dialog_turn_on_button); 641 PendingIntent actionIntent = PendingIntent.getBroadcast(mContext, sensor, 642 new Intent(ACTION_DISABLE_TOGGLE_SENSOR_PRIVACY) 643 .setPackage(mContext.getPackageName()) 644 .putExtra(EXTRA_SENSOR, sensor) 645 .putExtra(Intent.EXTRA_USER, user), 646 PendingIntent.FLAG_IMMUTABLE 647 | PendingIntent.FLAG_UPDATE_CURRENT); 648 notificationManager.notify(notificationId, 649 new Notification.Builder(mContext, SENSOR_PRIVACY_CHANNEL_ID) 650 .setContentTitle(contentTitle) 651 .setContentText(contentText) 652 .setSmallIcon(icon) 653 .addAction(new Notification.Action.Builder(icon, 654 actionTitle, actionIntent).build()) 655 .setContentIntent(contentIntent) 656 .extend(new Notification.TvExtender()) 657 .setTimeoutAfter(isTelevision(mContext) 658 ? /* dismiss immediately */ 1 659 : /* no timeout */ 0) 660 .build()); 661 } 662 isTelevision(Context context)663 private boolean isTelevision(Context context) { 664 int uiMode = context.getResources().getConfiguration().uiMode; 665 return (uiMode & Configuration.UI_MODE_TYPE_MASK) 666 == Configuration.UI_MODE_TYPE_TELEVISION; 667 } 668 669 /** 670 * Sets the sensor privacy to the provided state and notifies all listeners of the new 671 * state. 672 */ 673 @Override setSensorPrivacy(boolean enable)674 public void setSensorPrivacy(boolean enable) { 675 enforceManageSensorPrivacyPermission(); 676 mSensorPrivacyStateController.setAllSensorState(enable); 677 } 678 679 @Override setToggleSensorPrivacy(@serIdInt int userId, @SensorPrivacyManager.Sources.Source int source, int sensor, boolean enable)680 public void setToggleSensorPrivacy(@UserIdInt int userId, 681 @SensorPrivacyManager.Sources.Source int source, int sensor, boolean enable) { 682 if (DEBUG) { 683 Log.d(TAG, "callingUid=" + Binder.getCallingUid() 684 + " callingPid=" + Binder.getCallingPid() 685 + " setToggleSensorPrivacy(" 686 + "userId=" + userId 687 + " source=" + source 688 + " sensor=" + sensor 689 + " enable=" + enable 690 + ")"); 691 } 692 enforceManageSensorPrivacyPermission(); 693 if (userId == UserHandle.USER_CURRENT) { 694 userId = mCurrentUser; 695 } 696 if (!canChangeToggleSensorPrivacy(userId, sensor)) { 697 return; 698 } 699 700 setToggleSensorPrivacyUnchecked(TOGGLE_TYPE_SOFTWARE, userId, source, sensor, enable); 701 } 702 setToggleSensorPrivacyUnchecked(int toggleType, int userId, int source, int sensor, boolean enable)703 private void setToggleSensorPrivacyUnchecked(int toggleType, int userId, int source, 704 int sensor, boolean enable) { 705 final long[] lastChange = new long[1]; 706 mSensorPrivacyStateController.atomic(() -> { 707 SensorState sensorState = mSensorPrivacyStateController 708 .getState(toggleType, userId, sensor); 709 lastChange[0] = sensorState.getLastChange(); 710 mSensorPrivacyStateController.setState( 711 toggleType, userId, sensor, enable, mHandler, 712 changeSuccessful -> { 713 if (changeSuccessful) { 714 if (userId == mUserManagerInternal.getProfileParentId(userId)) { 715 mHandler.sendMessage(PooledLambda.obtainMessage( 716 SensorPrivacyServiceImpl::logSensorPrivacyToggle, this, 717 source, sensor, enable, lastChange[0], false)); 718 } 719 } 720 }); 721 }); 722 } 723 canChangeToggleSensorPrivacy(@serIdInt int userId, int sensor)724 private boolean canChangeToggleSensorPrivacy(@UserIdInt int userId, int sensor) { 725 if (sensor == MICROPHONE && mCallStateHelper.isInEmergencyCall()) { 726 // During emergency call the microphone toggle managed automatically 727 Log.i(TAG, "Can't change mic toggle during an emergency call"); 728 return false; 729 } 730 731 if (requiresAuthentication() && mKeyguardManager != null 732 && mKeyguardManager.isDeviceLocked(userId)) { 733 Log.i(TAG, "Can't change mic/cam toggle while device is locked"); 734 return false; 735 } 736 737 if (sensor == MICROPHONE && mUserManagerInternal.getUserRestriction(userId, 738 UserManager.DISALLOW_MICROPHONE_TOGGLE)) { 739 Log.i(TAG, "Can't change mic toggle due to admin restriction"); 740 return false; 741 } 742 743 if (sensor == CAMERA && mUserManagerInternal.getUserRestriction(userId, 744 UserManager.DISALLOW_CAMERA_TOGGLE)) { 745 Log.i(TAG, "Can't change camera toggle due to admin restriction"); 746 return false; 747 } 748 return true; 749 } 750 logSensorPrivacyToggle(int source, int sensor, boolean enabled, long lastChange, boolean onShutDown)751 private void logSensorPrivacyToggle(int source, int sensor, boolean enabled, 752 long lastChange, boolean onShutDown) { 753 long logMins = Math.max(0, (getCurrentTimeMillis() - lastChange) / (1000 * 60)); 754 755 int logAction = -1; 756 if (onShutDown) { 757 // TODO ACTION_POWER_OFF_WHILE_(ON/OFF) 758 if (enabled) { 759 logAction = PRIVACY_SENSOR_TOGGLE_INTERACTION__ACTION__ACTION_UNKNOWN; 760 } else { 761 logAction = PRIVACY_SENSOR_TOGGLE_INTERACTION__ACTION__ACTION_UNKNOWN; 762 } 763 } else { 764 if (enabled) { 765 logAction = PRIVACY_SENSOR_TOGGLE_INTERACTION__ACTION__TOGGLE_OFF; 766 } else { 767 logAction = PRIVACY_SENSOR_TOGGLE_INTERACTION__ACTION__TOGGLE_ON; 768 } 769 } 770 771 int logSensor = -1; 772 switch(sensor) { 773 case CAMERA: 774 logSensor = PRIVACY_SENSOR_TOGGLE_INTERACTION__SENSOR__CAMERA; 775 break; 776 case MICROPHONE: 777 logSensor = PRIVACY_SENSOR_TOGGLE_INTERACTION__SENSOR__MICROPHONE; 778 break; 779 default: 780 logSensor = PRIVACY_SENSOR_TOGGLE_INTERACTION__SENSOR__SENSOR_UNKNOWN; 781 } 782 783 int logSource = -1; 784 switch(source) { 785 case QS_TILE : 786 logSource = PRIVACY_SENSOR_TOGGLE_INTERACTION__SOURCE__QS_TILE; 787 break; 788 case DIALOG : 789 logSource = PRIVACY_SENSOR_TOGGLE_INTERACTION__SOURCE__DIALOG; 790 break; 791 case SETTINGS: 792 logSource = PRIVACY_SENSOR_TOGGLE_INTERACTION__SOURCE__SETTINGS; 793 break; 794 default: 795 logSource = PRIVACY_SENSOR_TOGGLE_INTERACTION__SOURCE__SOURCE_UNKNOWN; 796 } 797 798 if (DEBUG || DEBUG_LOGGING) { 799 Log.d(TAG, "Logging sensor toggle interaction:" + " logSensor=" + logSensor 800 + " logAction=" + logAction + " logSource=" + logSource + " logMins=" 801 + logMins); 802 } 803 write(PRIVACY_SENSOR_TOGGLE_INTERACTION, logSensor, logAction, logSource, logMins); 804 805 } 806 807 @Override setToggleSensorPrivacyForProfileGroup(@serIdInt int userId, @SensorPrivacyManager.Sources.Source int source, int sensor, boolean enable)808 public void setToggleSensorPrivacyForProfileGroup(@UserIdInt int userId, 809 @SensorPrivacyManager.Sources.Source int source, int sensor, boolean enable) { 810 enforceManageSensorPrivacyPermission(); 811 if (userId == UserHandle.USER_CURRENT) { 812 userId = mCurrentUser; 813 } 814 int parentId = mUserManagerInternal.getProfileParentId(userId); 815 forAllUsers(userId2 -> { 816 if (parentId == mUserManagerInternal.getProfileParentId(userId2)) { 817 setToggleSensorPrivacy(userId2, source, sensor, enable); 818 } 819 }); 820 } 821 822 /** 823 * Enforces the caller contains the necessary permission to change the state of sensor 824 * privacy. 825 */ enforceManageSensorPrivacyPermission()826 private void enforceManageSensorPrivacyPermission() { 827 enforcePermission(android.Manifest.permission.MANAGE_SENSOR_PRIVACY, 828 "Changing sensor privacy requires the following permission: " 829 + MANAGE_SENSOR_PRIVACY); 830 } 831 832 /** 833 * Enforces the caller contains the necessary permission to observe changes to the sate of 834 * sensor privacy. 835 */ enforceObserveSensorPrivacyPermission()836 private void enforceObserveSensorPrivacyPermission() { 837 String systemUIPackage = mContext.getString(R.string.config_systemUi); 838 if (Binder.getCallingUid() == mPackageManagerInternal 839 .getPackageUid(systemUIPackage, MATCH_SYSTEM_ONLY, UserHandle.USER_SYSTEM)) { 840 // b/221782106, possible race condition with role grant might bootloop device. 841 return; 842 } 843 enforcePermission(android.Manifest.permission.OBSERVE_SENSOR_PRIVACY, 844 "Observing sensor privacy changes requires the following permission: " 845 + android.Manifest.permission.OBSERVE_SENSOR_PRIVACY); 846 } 847 enforcePermission(String permission, String message)848 private void enforcePermission(String permission, String message) { 849 if (mContext.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED) { 850 return; 851 } 852 throw new SecurityException(message); 853 } 854 855 /** 856 * Returns whether sensor privacy is enabled. 857 */ 858 @Override isSensorPrivacyEnabled()859 public boolean isSensorPrivacyEnabled() { 860 enforceObserveSensorPrivacyPermission(); 861 return mSensorPrivacyStateController.getAllSensorState(); 862 } 863 864 @Override isToggleSensorPrivacyEnabled(int toggleType, int sensor)865 public boolean isToggleSensorPrivacyEnabled(int toggleType, int sensor) { 866 if (DEBUG) { 867 Log.d(TAG, "callingUid=" + Binder.getCallingUid() 868 + " callingPid=" + Binder.getCallingPid() 869 + " isToggleSensorPrivacyEnabled(" 870 + "toggleType=" + toggleType 871 + " sensor=" + sensor 872 + ")"); 873 } 874 enforceObserveSensorPrivacyPermission(); 875 876 return mSensorPrivacyStateController.getState(toggleType, mCurrentUser, sensor) 877 .isEnabled(); 878 } 879 880 @Override isCombinedToggleSensorPrivacyEnabled(int sensor)881 public boolean isCombinedToggleSensorPrivacyEnabled(int sensor) { 882 return isToggleSensorPrivacyEnabled(TOGGLE_TYPE_SOFTWARE, sensor) 883 || isToggleSensorPrivacyEnabled(TOGGLE_TYPE_HARDWARE, sensor); 884 } 885 isToggleSensorPrivacyEnabledInternal(int userId, int toggleType, int sensor)886 private boolean isToggleSensorPrivacyEnabledInternal(int userId, int toggleType, 887 int sensor) { 888 889 return mSensorPrivacyStateController.getState(toggleType, 890 userId, sensor).isEnabled(); 891 } 892 893 @Override supportsSensorToggle(int toggleType, int sensor)894 public boolean supportsSensorToggle(int toggleType, int sensor) { 895 if (toggleType == TOGGLE_TYPE_SOFTWARE) { 896 if (sensor == MICROPHONE) { 897 return mContext.getResources() 898 .getBoolean(R.bool.config_supportsMicToggle); 899 } else if (sensor == CAMERA) { 900 return mContext.getResources() 901 .getBoolean(R.bool.config_supportsCamToggle); 902 } 903 } else if (toggleType == TOGGLE_TYPE_HARDWARE) { 904 if (sensor == MICROPHONE) { 905 return mContext.getResources() 906 .getBoolean(R.bool.config_supportsHardwareMicToggle); 907 } else if (sensor == CAMERA) { 908 return mContext.getResources() 909 .getBoolean(R.bool.config_supportsHardwareCamToggle); 910 } 911 } 912 throw new IllegalArgumentException("Invalid arguments. " 913 + "toggleType=" + toggleType + " sensor=" + sensor); 914 } 915 916 /** 917 * Registers a listener to be notified when the sensor privacy state changes. 918 */ 919 @Override addSensorPrivacyListener(ISensorPrivacyListener listener)920 public void addSensorPrivacyListener(ISensorPrivacyListener listener) { 921 enforceObserveSensorPrivacyPermission(); 922 if (listener == null) { 923 throw new NullPointerException("listener cannot be null"); 924 } 925 mHandler.addListener(listener); 926 } 927 928 /** 929 * Registers a listener to be notified when the sensor privacy state changes. 930 */ 931 @Override addToggleSensorPrivacyListener(ISensorPrivacyListener listener)932 public void addToggleSensorPrivacyListener(ISensorPrivacyListener listener) { 933 enforceObserveSensorPrivacyPermission(); 934 if (listener == null) { 935 throw new IllegalArgumentException("listener cannot be null"); 936 } 937 mHandler.addToggleListener(listener); 938 } 939 940 /** 941 * Unregisters a listener from sensor privacy state change notifications. 942 */ 943 @Override removeSensorPrivacyListener(ISensorPrivacyListener listener)944 public void removeSensorPrivacyListener(ISensorPrivacyListener listener) { 945 enforceObserveSensorPrivacyPermission(); 946 if (listener == null) { 947 throw new NullPointerException("listener cannot be null"); 948 } 949 mHandler.removeListener(listener); 950 } 951 952 /** 953 * Unregisters a listener from sensor privacy state change notifications. 954 */ 955 @Override removeToggleSensorPrivacyListener(ISensorPrivacyListener listener)956 public void removeToggleSensorPrivacyListener(ISensorPrivacyListener listener) { 957 enforceObserveSensorPrivacyPermission(); 958 if (listener == null) { 959 throw new IllegalArgumentException("listener cannot be null"); 960 } 961 mHandler.removeToggleListener(listener); 962 } 963 964 @Override suppressToggleSensorPrivacyReminders(int userId, int sensor, IBinder token, boolean suppress)965 public void suppressToggleSensorPrivacyReminders(int userId, int sensor, 966 IBinder token, boolean suppress) { 967 enforceManageSensorPrivacyPermission(); 968 if (userId == UserHandle.USER_CURRENT) { 969 userId = mCurrentUser; 970 } 971 Objects.requireNonNull(token); 972 973 Pair<Integer, UserHandle> key = new Pair<>(sensor, UserHandle.of(userId)); 974 975 synchronized (mLock) { 976 if (suppress) { 977 try { 978 token.linkToDeath(this, 0); 979 } catch (RemoteException e) { 980 Log.e(TAG, "Could not suppress sensor use reminder", e); 981 return; 982 } 983 984 ArrayList<IBinder> suppressPackageReminderTokens = mSuppressReminders.get(key); 985 if (suppressPackageReminderTokens == null) { 986 suppressPackageReminderTokens = new ArrayList<>(1); 987 mSuppressReminders.put(key, suppressPackageReminderTokens); 988 } 989 990 suppressPackageReminderTokens.add(token); 991 } else { 992 mHandler.removeSuppressPackageReminderToken(key, token); 993 } 994 } 995 } 996 997 @Override requiresAuthentication()998 public boolean requiresAuthentication() { 999 enforceObserveSensorPrivacyPermission(); 1000 return mContext.getResources() 1001 .getBoolean(R.bool.config_sensorPrivacyRequiresAuthentication); 1002 } 1003 1004 @Override showSensorUseDialog(int sensor)1005 public void showSensorUseDialog(int sensor) { 1006 if (Binder.getCallingUid() != Process.SYSTEM_UID) { 1007 throw new SecurityException("Can only be called by the system uid"); 1008 } 1009 if (!isCombinedToggleSensorPrivacyEnabled(sensor)) { 1010 return; 1011 } 1012 enqueueSensorUseReminderDialogAsync( 1013 -1, UserHandle.of(mCurrentUser), "android", sensor); 1014 } 1015 userSwitching(int from, int to)1016 private void userSwitching(int from, int to) { 1017 final boolean[] micState = new boolean[2]; 1018 final boolean[] camState = new boolean[2]; 1019 final boolean[] prevMicState = new boolean[2]; 1020 final boolean[] prevCamState = new boolean[2]; 1021 final int swToggleIdx = 0; 1022 final int hwToggleIdx = 1; 1023 // Get SW toggles state 1024 mSensorPrivacyStateController.atomic(() -> { 1025 prevMicState[swToggleIdx] = isToggleSensorPrivacyEnabledInternal(from, 1026 TOGGLE_TYPE_SOFTWARE, MICROPHONE); 1027 prevCamState[swToggleIdx] = isToggleSensorPrivacyEnabledInternal(from, 1028 TOGGLE_TYPE_SOFTWARE, CAMERA); 1029 micState[swToggleIdx] = isToggleSensorPrivacyEnabledInternal(to, 1030 TOGGLE_TYPE_SOFTWARE, MICROPHONE); 1031 camState[swToggleIdx] = isToggleSensorPrivacyEnabledInternal(to, 1032 TOGGLE_TYPE_SOFTWARE, CAMERA); 1033 }); 1034 // Get HW toggles state 1035 mSensorPrivacyStateController.atomic(() -> { 1036 prevMicState[hwToggleIdx] = isToggleSensorPrivacyEnabledInternal(from, 1037 TOGGLE_TYPE_HARDWARE, MICROPHONE); 1038 prevCamState[hwToggleIdx] = isToggleSensorPrivacyEnabledInternal(from, 1039 TOGGLE_TYPE_HARDWARE, CAMERA); 1040 micState[hwToggleIdx] = isToggleSensorPrivacyEnabledInternal(to, 1041 TOGGLE_TYPE_HARDWARE, MICROPHONE); 1042 camState[hwToggleIdx] = isToggleSensorPrivacyEnabledInternal(to, 1043 TOGGLE_TYPE_HARDWARE, CAMERA); 1044 }); 1045 1046 if (from == USER_NULL || prevMicState[swToggleIdx] != micState[swToggleIdx] 1047 || prevMicState[hwToggleIdx] != micState[hwToggleIdx]) { 1048 mHandler.handleSensorPrivacyChanged(to, TOGGLE_TYPE_SOFTWARE, MICROPHONE, 1049 micState[swToggleIdx]); 1050 mHandler.handleSensorPrivacyChanged(to, TOGGLE_TYPE_HARDWARE, MICROPHONE, 1051 micState[hwToggleIdx]); 1052 setGlobalRestriction(MICROPHONE, micState[swToggleIdx] || micState[hwToggleIdx]); 1053 } 1054 if (from == USER_NULL || prevCamState[swToggleIdx] != camState[swToggleIdx] 1055 || prevCamState[hwToggleIdx] != camState[hwToggleIdx]) { 1056 mHandler.handleSensorPrivacyChanged(to, TOGGLE_TYPE_SOFTWARE, CAMERA, 1057 camState[swToggleIdx]); 1058 mHandler.handleSensorPrivacyChanged(to, TOGGLE_TYPE_HARDWARE, CAMERA, 1059 camState[hwToggleIdx]); 1060 setGlobalRestriction(CAMERA, camState[swToggleIdx] || camState[hwToggleIdx]); 1061 } 1062 } 1063 setGlobalRestriction(int sensor, boolean enabled)1064 private void setGlobalRestriction(int sensor, boolean enabled) { 1065 switch(sensor) { 1066 case MICROPHONE: 1067 mAppOpsManagerInternal.setGlobalRestriction(OP_RECORD_AUDIO, enabled, 1068 mAppOpsRestrictionToken); 1069 mAppOpsManagerInternal.setGlobalRestriction(OP_PHONE_CALL_MICROPHONE, enabled, 1070 mAppOpsRestrictionToken); 1071 // We don't show the dialog for RECEIVE_SOUNDTRIGGER_AUDIO, but still want to 1072 // restrict it when the microphone is disabled 1073 mAppOpsManagerInternal.setGlobalRestriction(OP_RECEIVE_AMBIENT_TRIGGER_AUDIO, 1074 enabled, mAppOpsRestrictionToken); 1075 break; 1076 case CAMERA: 1077 mAppOpsManagerInternal.setGlobalRestriction(OP_CAMERA, enabled, 1078 mAppOpsRestrictionToken); 1079 mAppOpsManagerInternal.setGlobalRestriction(OP_PHONE_CALL_CAMERA, enabled, 1080 mAppOpsRestrictionToken); 1081 break; 1082 } 1083 } 1084 1085 /** 1086 * Remove a sensor use reminder suppression token. 1087 * 1088 * @param key Key the token is in 1089 * @param token The token to remove 1090 */ removeSuppressPackageReminderToken(@onNull Pair<Integer, UserHandle> key, @NonNull IBinder token)1091 private void removeSuppressPackageReminderToken(@NonNull Pair<Integer, UserHandle> key, 1092 @NonNull IBinder token) { 1093 synchronized (mLock) { 1094 ArrayList<IBinder> suppressPackageReminderTokens = 1095 mSuppressReminders.get(key); 1096 if (suppressPackageReminderTokens == null) { 1097 Log.e(TAG, "No tokens for " + key); 1098 return; 1099 } 1100 1101 boolean wasRemoved = suppressPackageReminderTokens.remove(token); 1102 if (wasRemoved) { 1103 token.unlinkToDeath(this, 0); 1104 1105 if (suppressPackageReminderTokens.isEmpty()) { 1106 mSuppressReminders.remove(key); 1107 } 1108 } else { 1109 Log.w(TAG, "Could not remove sensor use reminder suppression token " + token 1110 + " from " + key); 1111 } 1112 } 1113 } 1114 1115 /** 1116 * A owner of a suppressor token died. Clean up. 1117 * 1118 * @param token The token that is invalid now. 1119 */ 1120 @Override binderDied(@onNull IBinder token)1121 public void binderDied(@NonNull IBinder token) { 1122 synchronized (mLock) { 1123 for (Pair<Integer, UserHandle> key : mSuppressReminders.keySet()) { 1124 removeSuppressPackageReminderToken(key, token); 1125 } 1126 } 1127 } 1128 1129 @Override binderDied()1130 public void binderDied() { 1131 // Handled in binderDied(IBinder) 1132 } 1133 1134 @Override dump(FileDescriptor fd, PrintWriter pw, String[] args)1135 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 1136 Objects.requireNonNull(fd); 1137 1138 if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; 1139 1140 int opti = 0; 1141 boolean dumpAsProto = false; 1142 while (opti < args.length) { 1143 String opt = args[opti]; 1144 if (opt == null || opt.length() <= 0 || opt.charAt(0) != '-') { 1145 break; 1146 } 1147 opti++; 1148 if ("--proto".equals(opt)) { 1149 dumpAsProto = true; 1150 } else { 1151 pw.println("Unknown argument: " + opt + "; use -h for help"); 1152 } 1153 } 1154 1155 final long identity = Binder.clearCallingIdentity(); 1156 try { 1157 if (dumpAsProto) { 1158 mSensorPrivacyStateController.dump( 1159 new DualDumpOutputStream(new ProtoOutputStream(fd))); 1160 } else { 1161 pw.println("SENSOR PRIVACY MANAGER STATE (dumpsys " 1162 + Context.SENSOR_PRIVACY_SERVICE + ")"); 1163 1164 mSensorPrivacyStateController.dump( 1165 new DualDumpOutputStream(new IndentingPrintWriter(pw, " "))); 1166 } 1167 } finally { 1168 Binder.restoreCallingIdentity(identity); 1169 } 1170 } 1171 1172 /** 1173 * Convert a string into a {@link SensorPrivacyManager.Sensors.Sensor id}. 1174 * 1175 * @param sensor The name to convert 1176 * 1177 * @return The id corresponding to the name 1178 */ sensorStrToId(@ullable String sensor)1179 private @SensorPrivacyManager.Sensors.Sensor int sensorStrToId(@Nullable String sensor) { 1180 if (sensor == null) { 1181 return UNKNOWN; 1182 } 1183 1184 switch (sensor) { 1185 case "microphone": 1186 return MICROPHONE; 1187 case "camera": 1188 return CAMERA; 1189 default: { 1190 return UNKNOWN; 1191 } 1192 } 1193 } 1194 1195 @Override onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, String[] args, ShellCallback callback, ResultReceiver resultReceiver)1196 public void onShellCommand(FileDescriptor in, FileDescriptor out, 1197 FileDescriptor err, String[] args, ShellCallback callback, 1198 ResultReceiver resultReceiver) { 1199 (new ShellCommand() { 1200 @Override 1201 public int onCommand(String cmd) { 1202 if (cmd == null) { 1203 return handleDefaultCommands(cmd); 1204 } 1205 1206 int userId = Integer.parseInt(getNextArgRequired()); 1207 1208 final PrintWriter pw = getOutPrintWriter(); 1209 switch (cmd) { 1210 case "enable" : { 1211 int sensor = sensorStrToId(getNextArgRequired()); 1212 if (sensor == UNKNOWN) { 1213 pw.println("Invalid sensor"); 1214 return -1; 1215 } 1216 1217 setToggleSensorPrivacy(userId, SHELL, sensor, true); 1218 } 1219 break; 1220 case "disable" : { 1221 int sensor = sensorStrToId(getNextArgRequired()); 1222 if (sensor == UNKNOWN) { 1223 pw.println("Invalid sensor"); 1224 return -1; 1225 } 1226 1227 setToggleSensorPrivacy(userId, SHELL, sensor, false); 1228 } 1229 break; 1230 default: 1231 return handleDefaultCommands(cmd); 1232 } 1233 1234 return 0; 1235 } 1236 1237 @Override 1238 public void onHelp() { 1239 final PrintWriter pw = getOutPrintWriter(); 1240 1241 pw.println("Sensor privacy manager (" + Context.SENSOR_PRIVACY_SERVICE 1242 + ") commands:"); 1243 pw.println(" help"); 1244 pw.println(" Print this help text."); 1245 pw.println(""); 1246 pw.println(" enable USER_ID SENSOR"); 1247 pw.println(" Enable privacy for a certain sensor."); 1248 pw.println(""); 1249 pw.println(" disable USER_ID SENSOR"); 1250 pw.println(" Disable privacy for a certain sensor."); 1251 pw.println(""); 1252 } 1253 }).exec(this, in, out, err, args, callback, resultReceiver); 1254 } 1255 } 1256 1257 /** 1258 * Handles sensor privacy state changes and notifying listeners of the change. 1259 */ 1260 private final class SensorPrivacyHandler extends Handler { 1261 private static final int MESSAGE_SENSOR_PRIVACY_CHANGED = 1; 1262 1263 private final Object mListenerLock = new Object(); 1264 1265 @GuardedBy("mListenerLock") 1266 private final RemoteCallbackList<ISensorPrivacyListener> mListeners = 1267 new RemoteCallbackList<>(); 1268 @GuardedBy("mListenerLock") 1269 private final RemoteCallbackList<ISensorPrivacyListener> 1270 mToggleSensorListeners = new RemoteCallbackList<>(); 1271 @GuardedBy("mListenerLock") 1272 private final ArrayMap<ISensorPrivacyListener, Pair<DeathRecipient, Integer>> 1273 mDeathRecipients; 1274 private final Context mContext; 1275 SensorPrivacyHandler(Looper looper, Context context)1276 SensorPrivacyHandler(Looper looper, Context context) { 1277 super(looper); 1278 mDeathRecipients = new ArrayMap<>(); 1279 mContext = context; 1280 } 1281 addListener(ISensorPrivacyListener listener)1282 public void addListener(ISensorPrivacyListener listener) { 1283 synchronized (mListenerLock) { 1284 if (mListeners.register(listener)) { 1285 addDeathRecipient(listener); 1286 } 1287 } 1288 } 1289 addToggleListener(ISensorPrivacyListener listener)1290 public void addToggleListener(ISensorPrivacyListener listener) { 1291 synchronized (mListenerLock) { 1292 if (mToggleSensorListeners.register(listener)) { 1293 addDeathRecipient(listener); 1294 } 1295 } 1296 } 1297 removeListener(ISensorPrivacyListener listener)1298 public void removeListener(ISensorPrivacyListener listener) { 1299 synchronized (mListenerLock) { 1300 if (mListeners.unregister(listener)) { 1301 removeDeathRecipient(listener); 1302 } 1303 } 1304 } 1305 removeToggleListener(ISensorPrivacyListener listener)1306 public void removeToggleListener(ISensorPrivacyListener listener) { 1307 synchronized (mListenerLock) { 1308 if (mToggleSensorListeners.unregister(listener)) { 1309 removeDeathRecipient(listener); 1310 } 1311 } 1312 } 1313 handleSensorPrivacyChanged(boolean enabled)1314 public void handleSensorPrivacyChanged(boolean enabled) { 1315 final int count = mListeners.beginBroadcast(); 1316 for (int i = 0; i < count; i++) { 1317 ISensorPrivacyListener listener = mListeners.getBroadcastItem(i); 1318 try { 1319 listener.onSensorPrivacyChanged(-1, -1, enabled); 1320 } catch (RemoteException e) { 1321 Log.e(TAG, "Caught an exception notifying listener " + listener + ": ", e); 1322 } 1323 } 1324 mListeners.finishBroadcast(); 1325 } 1326 handleSensorPrivacyChanged(int userId, int toggleType, int sensor, boolean enabled)1327 public void handleSensorPrivacyChanged(int userId, int toggleType, int sensor, 1328 boolean enabled) { 1329 mSensorPrivacyManagerInternal.dispatch(userId, sensor, enabled); 1330 1331 if (userId == mCurrentUser) { 1332 mSensorPrivacyServiceImpl.setGlobalRestriction(sensor, 1333 mSensorPrivacyServiceImpl.isCombinedToggleSensorPrivacyEnabled(sensor)); 1334 } 1335 1336 if (userId != mCurrentUser) { 1337 return; 1338 } 1339 synchronized (mListenerLock) { 1340 try { 1341 final int count = mToggleSensorListeners.beginBroadcast(); 1342 for (int i = 0; i < count; i++) { 1343 ISensorPrivacyListener listener = mToggleSensorListeners.getBroadcastItem( 1344 i); 1345 try { 1346 listener.onSensorPrivacyChanged(toggleType, sensor, enabled); 1347 } catch (RemoteException e) { 1348 Log.e(TAG, "Caught an exception notifying listener " + listener + ": ", 1349 e); 1350 } 1351 } 1352 } finally { 1353 mToggleSensorListeners.finishBroadcast(); 1354 } 1355 } 1356 } 1357 removeSuppressPackageReminderToken(Pair<Integer, UserHandle> key, IBinder token)1358 public void removeSuppressPackageReminderToken(Pair<Integer, UserHandle> key, 1359 IBinder token) { 1360 sendMessage(PooledLambda.obtainMessage( 1361 SensorPrivacyServiceImpl::removeSuppressPackageReminderToken, 1362 mSensorPrivacyServiceImpl, key, token)); 1363 } 1364 addDeathRecipient(ISensorPrivacyListener listener)1365 private void addDeathRecipient(ISensorPrivacyListener listener) { 1366 Pair<DeathRecipient, Integer> deathRecipient = mDeathRecipients.get(listener); 1367 if (deathRecipient == null) { 1368 deathRecipient = new Pair<>(new DeathRecipient(listener), 1); 1369 } else { 1370 int newRefCount = deathRecipient.second + 1; 1371 deathRecipient = new Pair<>(deathRecipient.first, newRefCount); 1372 } 1373 mDeathRecipients.put(listener, deathRecipient); 1374 } 1375 removeDeathRecipient(ISensorPrivacyListener listener)1376 private void removeDeathRecipient(ISensorPrivacyListener listener) { 1377 Pair<DeathRecipient, Integer> deathRecipient = mDeathRecipients.get(listener); 1378 if (deathRecipient == null) { 1379 return; 1380 } else { 1381 int newRefCount = deathRecipient.second - 1; 1382 if (newRefCount == 0) { 1383 mDeathRecipients.remove(listener); 1384 deathRecipient.first.destroy(); 1385 return; 1386 } 1387 deathRecipient = new Pair<>(deathRecipient.first, newRefCount); 1388 } 1389 mDeathRecipients.put(listener, deathRecipient); 1390 } 1391 } 1392 1393 private final class DeathRecipient implements IBinder.DeathRecipient { 1394 1395 private ISensorPrivacyListener mListener; 1396 DeathRecipient(ISensorPrivacyListener listener)1397 DeathRecipient(ISensorPrivacyListener listener) { 1398 mListener = listener; 1399 try { 1400 mListener.asBinder().linkToDeath(this, 0); 1401 } catch (RemoteException e) { 1402 } 1403 } 1404 1405 @Override binderDied()1406 public void binderDied() { 1407 mSensorPrivacyServiceImpl.removeSensorPrivacyListener(mListener); 1408 } 1409 destroy()1410 public void destroy() { 1411 try { 1412 mListener.asBinder().unlinkToDeath(this, 0); 1413 } catch (NoSuchElementException e) { 1414 } 1415 } 1416 } 1417 forAllUsers(FunctionalUtils.ThrowingConsumer<Integer> c)1418 private void forAllUsers(FunctionalUtils.ThrowingConsumer<Integer> c) { 1419 int[] userIds = mUserManagerInternal.getUserIds(); 1420 for (int i = 0; i < userIds.length; i++) { 1421 c.accept(userIds[i]); 1422 } 1423 } 1424 1425 private class SensorPrivacyManagerInternalImpl extends SensorPrivacyManagerInternal { 1426 1427 private ArrayMap<Integer, ArrayMap<Integer, ArraySet<OnSensorPrivacyChangedListener>>> 1428 mListeners = new ArrayMap<>(); 1429 private ArrayMap<Integer, ArraySet<OnUserSensorPrivacyChangedListener>> mAllUserListeners = 1430 new ArrayMap<>(); 1431 1432 private final Object mLock = new Object(); 1433 dispatch(int userId, int sensor, boolean enabled)1434 private void dispatch(int userId, int sensor, boolean enabled) { 1435 synchronized (mLock) { 1436 ArraySet<OnUserSensorPrivacyChangedListener> allUserSensorListeners = 1437 mAllUserListeners.get(sensor); 1438 if (allUserSensorListeners != null) { 1439 for (int i = 0; i < allUserSensorListeners.size(); i++) { 1440 OnUserSensorPrivacyChangedListener listener = 1441 allUserSensorListeners.valueAt(i); 1442 BackgroundThread.getHandler().post(() -> 1443 listener.onSensorPrivacyChanged(userId, enabled)); 1444 } 1445 } 1446 1447 ArrayMap<Integer, ArraySet<OnSensorPrivacyChangedListener>> userSensorListeners = 1448 mListeners.get(userId); 1449 if (userSensorListeners != null) { 1450 ArraySet<OnSensorPrivacyChangedListener> sensorListeners = 1451 userSensorListeners.get(sensor); 1452 if (sensorListeners != null) { 1453 for (int i = 0; i < sensorListeners.size(); i++) { 1454 OnSensorPrivacyChangedListener listener = sensorListeners.valueAt(i); 1455 BackgroundThread.getHandler().post(() -> 1456 listener.onSensorPrivacyChanged(enabled)); 1457 } 1458 } 1459 } 1460 } 1461 } 1462 1463 @Override isSensorPrivacyEnabled(int userId, int sensor)1464 public boolean isSensorPrivacyEnabled(int userId, int sensor) { 1465 return SensorPrivacyService.this 1466 .mSensorPrivacyServiceImpl.isToggleSensorPrivacyEnabledInternal(userId, 1467 TOGGLE_TYPE_SOFTWARE, sensor); 1468 } 1469 1470 @Override addSensorPrivacyListener(int userId, int sensor, OnSensorPrivacyChangedListener listener)1471 public void addSensorPrivacyListener(int userId, int sensor, 1472 OnSensorPrivacyChangedListener listener) { 1473 synchronized (mLock) { 1474 ArrayMap<Integer, ArraySet<OnSensorPrivacyChangedListener>> userSensorListeners = 1475 mListeners.get(userId); 1476 if (userSensorListeners == null) { 1477 userSensorListeners = new ArrayMap<>(); 1478 mListeners.put(userId, userSensorListeners); 1479 } 1480 1481 ArraySet<OnSensorPrivacyChangedListener> sensorListeners = 1482 userSensorListeners.get(sensor); 1483 if (sensorListeners == null) { 1484 sensorListeners = new ArraySet<>(); 1485 userSensorListeners.put(sensor, sensorListeners); 1486 } 1487 1488 sensorListeners.add(listener); 1489 } 1490 } 1491 1492 @Override addSensorPrivacyListenerForAllUsers(int sensor, OnUserSensorPrivacyChangedListener listener)1493 public void addSensorPrivacyListenerForAllUsers(int sensor, 1494 OnUserSensorPrivacyChangedListener listener) { 1495 synchronized (mLock) { 1496 ArraySet<OnUserSensorPrivacyChangedListener> sensorListeners = 1497 mAllUserListeners.get(sensor); 1498 if (sensorListeners == null) { 1499 sensorListeners = new ArraySet<>(); 1500 mAllUserListeners.put(sensor, sensorListeners); 1501 } 1502 1503 sensorListeners.add(listener); 1504 } 1505 } 1506 1507 @Override setPhysicalToggleSensorPrivacy(int userId, int sensor, boolean enable)1508 public void setPhysicalToggleSensorPrivacy(int userId, int sensor, boolean enable) { 1509 final SensorPrivacyServiceImpl sps = 1510 SensorPrivacyService.this.mSensorPrivacyServiceImpl; 1511 1512 // Convert userId to actual user Id. mCurrentUser is USER_NULL if toggle state is set 1513 // before onUserStarting. 1514 userId = (userId == UserHandle.USER_CURRENT ? mCurrentUser : userId); 1515 final int realUserId = (userId == UserHandle.USER_NULL ? mContext.getUserId() : userId); 1516 1517 sps.setToggleSensorPrivacyUnchecked(TOGGLE_TYPE_HARDWARE, realUserId, OTHER, sensor, 1518 enable); 1519 // Also disable the SW toggle when disabling the HW toggle 1520 if (!enable) { 1521 sps.setToggleSensorPrivacyUnchecked(TOGGLE_TYPE_SOFTWARE, realUserId, OTHER, sensor, 1522 enable); 1523 } 1524 } 1525 } 1526 1527 private class CallStateHelper { 1528 private OutgoingEmergencyStateCallback mEmergencyStateCallback; 1529 private CallStateCallback mCallStateCallback; 1530 1531 private boolean mIsInEmergencyCall; 1532 private boolean mMicUnmutedForEmergencyCall; 1533 1534 private Object mCallStateLock = new Object(); 1535 CallStateHelper()1536 CallStateHelper() { 1537 mEmergencyStateCallback = new OutgoingEmergencyStateCallback(); 1538 mCallStateCallback = new CallStateCallback(); 1539 1540 mTelephonyManager.registerTelephonyCallback(FgThread.getExecutor(), 1541 mEmergencyStateCallback); 1542 mTelephonyManager.registerTelephonyCallback(FgThread.getExecutor(), 1543 mCallStateCallback); 1544 } 1545 isInEmergencyCall()1546 boolean isInEmergencyCall() { 1547 synchronized (mCallStateLock) { 1548 return mIsInEmergencyCall; 1549 } 1550 } 1551 1552 private class OutgoingEmergencyStateCallback extends TelephonyCallback implements 1553 TelephonyCallback.OutgoingEmergencyCallListener { 1554 @Override onOutgoingEmergencyCall(EmergencyNumber placedEmergencyNumber, int subscriptionId)1555 public void onOutgoingEmergencyCall(EmergencyNumber placedEmergencyNumber, 1556 int subscriptionId) { 1557 onEmergencyCall(); 1558 } 1559 } 1560 1561 private class CallStateCallback extends TelephonyCallback implements 1562 TelephonyCallback.CallStateListener { 1563 @Override onCallStateChanged(int state)1564 public void onCallStateChanged(int state) { 1565 if (state == TelephonyManager.CALL_STATE_IDLE) { 1566 onCallOver(); 1567 } else { 1568 onCall(); 1569 } 1570 } 1571 } 1572 onEmergencyCall()1573 private void onEmergencyCall() { 1574 synchronized (mCallStateLock) { 1575 if (!mIsInEmergencyCall) { 1576 mIsInEmergencyCall = true; 1577 if (mSensorPrivacyServiceImpl 1578 .isToggleSensorPrivacyEnabled(TOGGLE_TYPE_SOFTWARE, MICROPHONE)) { 1579 mSensorPrivacyServiceImpl.setToggleSensorPrivacyUnchecked( 1580 TOGGLE_TYPE_SOFTWARE, mCurrentUser, OTHER, MICROPHONE, false); 1581 mMicUnmutedForEmergencyCall = true; 1582 } else { 1583 mMicUnmutedForEmergencyCall = false; 1584 } 1585 } 1586 } 1587 } 1588 onCall()1589 private void onCall() { 1590 long token = Binder.clearCallingIdentity(); 1591 try { 1592 synchronized (mCallStateLock) { 1593 mSensorPrivacyServiceImpl.showSensorUseDialog(MICROPHONE); 1594 } 1595 } finally { 1596 Binder.restoreCallingIdentity(token); 1597 } 1598 } 1599 onCallOver()1600 private void onCallOver() { 1601 synchronized (mCallStateLock) { 1602 if (mIsInEmergencyCall) { 1603 mIsInEmergencyCall = false; 1604 if (mMicUnmutedForEmergencyCall) { 1605 mSensorPrivacyServiceImpl.setToggleSensorPrivacyUnchecked( 1606 TOGGLE_TYPE_SOFTWARE, mCurrentUser, OTHER, MICROPHONE, true); 1607 mMicUnmutedForEmergencyCall = false; 1608 } 1609 } 1610 } 1611 } 1612 } 1613 getCurrentTimeMillis()1614 static long getCurrentTimeMillis() { 1615 return SystemClock.elapsedRealtime(); 1616 } 1617 } 1618