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 android.hardware; 18 19 import android.Manifest; 20 import android.annotation.IntDef; 21 import android.annotation.NonNull; 22 import android.annotation.RequiresPermission; 23 import android.annotation.SystemApi; 24 import android.annotation.SystemService; 25 import android.annotation.TestApi; 26 import android.annotation.UserIdInt; 27 import android.content.Context; 28 import android.os.Binder; 29 import android.os.IBinder; 30 import android.os.RemoteException; 31 import android.os.ServiceManager; 32 import android.os.UserHandle; 33 import android.service.SensorPrivacyIndividualEnabledSensorProto; 34 import android.service.SensorPrivacyToggleSourceProto; 35 import android.util.ArrayMap; 36 import android.util.Log; 37 import android.util.Pair; 38 import android.util.SparseArray; 39 40 import com.android.internal.annotations.GuardedBy; 41 42 import java.lang.annotation.Retention; 43 import java.lang.annotation.RetentionPolicy; 44 import java.util.concurrent.Executor; 45 46 /** 47 * This class provides information about the microphone and camera toggles. 48 */ 49 @SystemService(Context.SENSOR_PRIVACY_SERVICE) 50 public final class SensorPrivacyManager { 51 52 private static final String LOG_TAG = SensorPrivacyManager.class.getSimpleName(); 53 54 /** 55 * Unique Id of this manager to identify to the service 56 * @hide 57 */ 58 private IBinder token = new Binder(); 59 60 /** 61 * An extra containing a sensor 62 * @hide 63 */ 64 public static final String EXTRA_SENSOR = SensorPrivacyManager.class.getName() 65 + ".extra.sensor"; 66 67 /** 68 * An extra indicating if all sensors are affected 69 * @hide 70 */ 71 public static final String EXTRA_ALL_SENSORS = SensorPrivacyManager.class.getName() 72 + ".extra.all_sensors"; 73 74 private final SparseArray<Boolean> mToggleSupportCache = new SparseArray<>(); 75 76 /** 77 * Individual sensors not listed in {@link Sensors} 78 */ 79 public static class Sensors { 80 Sensors()81 private Sensors() {} 82 83 /** 84 * Constant for the microphone 85 */ 86 public static final int MICROPHONE = SensorPrivacyIndividualEnabledSensorProto.MICROPHONE; 87 88 /** 89 * Constant for the camera 90 */ 91 public static final int CAMERA = SensorPrivacyIndividualEnabledSensorProto.CAMERA; 92 93 /** 94 * Individual sensors not listed in {@link Sensors} 95 * 96 * @hide 97 */ 98 @IntDef(value = { 99 MICROPHONE, 100 CAMERA 101 }) 102 @Retention(RetentionPolicy.SOURCE) 103 public @interface Sensor {} 104 } 105 106 /** 107 * Source through which Privacy Sensor was toggled. 108 * @hide 109 */ 110 @TestApi 111 public static class Sources { Sources()112 private Sources() {} 113 114 /** 115 * Constant for the Quick Setting Tile. 116 */ 117 public static final int QS_TILE = SensorPrivacyToggleSourceProto.QS_TILE; 118 119 /** 120 * Constant for the Settings. 121 */ 122 public static final int SETTINGS = SensorPrivacyToggleSourceProto.SETTINGS; 123 124 /** 125 * Constant for Dialog. 126 */ 127 public static final int DIALOG = SensorPrivacyToggleSourceProto.DIALOG; 128 129 /** 130 * Constant for SHELL. 131 */ 132 public static final int SHELL = SensorPrivacyToggleSourceProto.SHELL; 133 134 /** 135 * Constant for OTHER. 136 */ 137 public static final int OTHER = SensorPrivacyToggleSourceProto.OTHER; 138 139 /** 140 * Source for toggling sensors 141 * 142 * @hide 143 */ 144 @IntDef(value = { 145 QS_TILE, 146 SETTINGS, 147 DIALOG, 148 SHELL, 149 OTHER 150 }) 151 @Retention(RetentionPolicy.SOURCE) 152 public @interface Source {} 153 154 } 155 156 /** 157 * A class implementing this interface can register with the {@link 158 * android.hardware.SensorPrivacyManager} to receive notification when the sensor privacy 159 * state changes. 160 * 161 * @hide 162 */ 163 @SystemApi 164 public interface OnSensorPrivacyChangedListener { 165 /** 166 * Callback invoked when the sensor privacy state changes. 167 * 168 * @param sensor the sensor whose state is changing 169 * @param enabled true if sensor privacy is enabled, false otherwise. 170 */ onSensorPrivacyChanged(int sensor, boolean enabled)171 void onSensorPrivacyChanged(int sensor, boolean enabled); 172 } 173 174 private static final Object sInstanceLock = new Object(); 175 176 @GuardedBy("sInstanceLock") 177 private static SensorPrivacyManager sInstance; 178 179 @NonNull 180 private final Context mContext; 181 182 @NonNull 183 private final ISensorPrivacyManager mService; 184 185 @NonNull 186 private final ArrayMap<OnAllSensorPrivacyChangedListener, ISensorPrivacyListener> mListeners; 187 188 @NonNull 189 private final ArrayMap<Pair<OnSensorPrivacyChangedListener, Integer>, ISensorPrivacyListener> 190 mIndividualListeners; 191 192 /** 193 * Private constructor to ensure only a single instance is created. 194 */ SensorPrivacyManager(Context context, ISensorPrivacyManager service)195 private SensorPrivacyManager(Context context, ISensorPrivacyManager service) { 196 mContext = context; 197 mService = service; 198 mListeners = new ArrayMap<>(); 199 mIndividualListeners = new ArrayMap<>(); 200 } 201 202 /** 203 * Returns the single instance of the SensorPrivacyManager. 204 * 205 * @hide 206 */ getInstance(Context context)207 public static SensorPrivacyManager getInstance(Context context) { 208 synchronized (sInstanceLock) { 209 if (sInstance == null) { 210 try { 211 IBinder b = ServiceManager.getServiceOrThrow(Context.SENSOR_PRIVACY_SERVICE); 212 ISensorPrivacyManager service = ISensorPrivacyManager.Stub.asInterface(b); 213 sInstance = new SensorPrivacyManager(context, service); 214 } catch (ServiceManager.ServiceNotFoundException e) { 215 throw new IllegalStateException(e); 216 } 217 } 218 return sInstance; 219 } 220 } 221 222 /** 223 * Checks if the given toggle is supported on this device 224 * @param sensor The sensor to check 225 * @return whether the toggle for the sensor is supported on this device. 226 */ supportsSensorToggle(@ensors.Sensor int sensor)227 public boolean supportsSensorToggle(@Sensors.Sensor int sensor) { 228 try { 229 Boolean val = mToggleSupportCache.get(sensor); 230 if (val == null) { 231 val = mService.supportsSensorToggle(sensor); 232 mToggleSupportCache.put(sensor, val); 233 } 234 return val; 235 } catch (RemoteException e) { 236 throw e.rethrowFromSystemServer(); 237 } 238 } 239 240 /** 241 * Registers a new listener to receive notification when the state of sensor privacy 242 * changes. 243 * 244 * @param sensor the sensor to listen to changes to 245 * @param listener the OnSensorPrivacyChangedListener to be notified when the state of sensor 246 * privacy changes. 247 * 248 * @hide 249 */ 250 @SystemApi 251 @RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY) addSensorPrivacyListener(@ensors.Sensor int sensor, @NonNull OnSensorPrivacyChangedListener listener)252 public void addSensorPrivacyListener(@Sensors.Sensor int sensor, 253 @NonNull OnSensorPrivacyChangedListener listener) { 254 addSensorPrivacyListener(sensor, mContext.getMainExecutor(), listener); 255 } 256 257 /** 258 * Registers a new listener to receive notification when the state of sensor privacy 259 * changes. 260 * 261 * @param sensor the sensor to listen to changes to 262 * @param userId the user's id 263 * @param listener the OnSensorPrivacyChangedListener to be notified when the state of sensor 264 * privacy changes. 265 * 266 * @hide 267 */ 268 @RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY) addSensorPrivacyListener(@ensors.Sensor int sensor, @UserIdInt int userId, @NonNull OnSensorPrivacyChangedListener listener)269 public void addSensorPrivacyListener(@Sensors.Sensor int sensor, @UserIdInt int userId, 270 @NonNull OnSensorPrivacyChangedListener listener) { 271 addSensorPrivacyListener(sensor, userId, mContext.getMainExecutor(), listener); 272 } 273 274 /** 275 * Registers a new listener to receive notification when the state of sensor privacy 276 * changes. 277 * 278 * @param sensor the sensor to listen to changes to 279 * @param executor the executor to dispatch the callback on 280 * @param listener the OnSensorPrivacyChangedListener to be notified when the state of sensor 281 * privacy changes. 282 * 283 * @hide 284 */ 285 @SystemApi 286 @RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY) addSensorPrivacyListener(@ensors.Sensor int sensor, @NonNull Executor executor, @NonNull OnSensorPrivacyChangedListener listener)287 public void addSensorPrivacyListener(@Sensors.Sensor int sensor, @NonNull Executor executor, 288 @NonNull OnSensorPrivacyChangedListener listener) { 289 Pair<OnSensorPrivacyChangedListener, Integer> key = new Pair<>(listener, sensor); 290 synchronized (mIndividualListeners) { 291 ISensorPrivacyListener iListener = mIndividualListeners.get(key); 292 if (iListener == null) { 293 iListener = new ISensorPrivacyListener.Stub() { 294 @Override 295 public void onSensorPrivacyChanged(boolean enabled) { 296 executor.execute(() -> listener.onSensorPrivacyChanged(sensor, enabled)); 297 } 298 }; 299 mIndividualListeners.put(key, iListener); 300 } 301 302 try { 303 mService.addUserGlobalIndividualSensorPrivacyListener(sensor, iListener); 304 } catch (RemoteException e) { 305 throw e.rethrowFromSystemServer(); 306 } 307 } 308 } 309 310 /** 311 * Registers a new listener to receive notification when the state of sensor privacy 312 * changes. 313 * 314 * @param sensor the sensor to listen to changes to 315 * @param executor the executor to dispatch the callback on 316 * @param userId the user's id 317 * @param listener the OnSensorPrivacyChangedListener to be notified when the state of sensor 318 * privacy changes. 319 * 320 * @hide 321 */ 322 @RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY) addSensorPrivacyListener(@ensors.Sensor int sensor, @UserIdInt int userId, @NonNull Executor executor, @NonNull OnSensorPrivacyChangedListener listener)323 public void addSensorPrivacyListener(@Sensors.Sensor int sensor, @UserIdInt int userId, 324 @NonNull Executor executor, @NonNull OnSensorPrivacyChangedListener listener) { 325 synchronized (mIndividualListeners) { 326 ISensorPrivacyListener iListener = mIndividualListeners.get(listener); 327 if (iListener == null) { 328 iListener = new ISensorPrivacyListener.Stub() { 329 @Override 330 public void onSensorPrivacyChanged(boolean enabled) { 331 executor.execute(() -> listener.onSensorPrivacyChanged(sensor, enabled)); 332 } 333 }; 334 mIndividualListeners.put(new Pair<>(listener, sensor), iListener); 335 } 336 337 try { 338 mService.addIndividualSensorPrivacyListener(userId, sensor, 339 iListener); 340 } catch (RemoteException e) { 341 throw e.rethrowFromSystemServer(); 342 } 343 } 344 } 345 346 /** 347 * Unregisters the specified listener from receiving notifications when the state of any sensor 348 * privacy changes. 349 * 350 * @param listener the OnSensorPrivacyChangedListener to be unregistered from notifications when 351 * sensor privacy changes. 352 * 353 * @hide 354 */ 355 @SystemApi 356 @RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY) removeSensorPrivacyListener(@ensors.Sensor int sensor, @NonNull OnSensorPrivacyChangedListener listener)357 public void removeSensorPrivacyListener(@Sensors.Sensor int sensor, 358 @NonNull OnSensorPrivacyChangedListener listener) { 359 synchronized (mListeners) { 360 for (int i = 0; i < mIndividualListeners.size(); i++) { 361 Pair<OnSensorPrivacyChangedListener, Integer> pair = mIndividualListeners.keyAt(i); 362 if (pair.second == sensor && pair.first.equals(listener)) { 363 try { 364 mService.removeIndividualSensorPrivacyListener(sensor, 365 mIndividualListeners.valueAt(i)); 366 } catch (RemoteException e) { 367 throw e.rethrowFromSystemServer(); 368 } 369 mIndividualListeners.removeAt(i--); 370 } 371 } 372 } 373 } 374 375 /** 376 * Returns whether sensor privacy is currently enabled for a specific sensor. 377 * 378 * @return true if sensor privacy is currently enabled, false otherwise. 379 * 380 * @hide 381 */ 382 @SystemApi 383 @RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY) isSensorPrivacyEnabled(@ensors.Sensor int sensor)384 public boolean isSensorPrivacyEnabled(@Sensors.Sensor int sensor) { 385 return isSensorPrivacyEnabled(sensor, UserHandle.USER_CURRENT); 386 } 387 388 /** 389 * Returns whether sensor privacy is currently enabled for a specific sensor. 390 * 391 * @return true if sensor privacy is currently enabled, false otherwise. 392 * 393 * @hide 394 */ 395 @RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY) isSensorPrivacyEnabled(@ensors.Sensor int sensor, @UserIdInt int userId)396 public boolean isSensorPrivacyEnabled(@Sensors.Sensor int sensor, @UserIdInt int userId) { 397 try { 398 return mService.isIndividualSensorPrivacyEnabled(userId, sensor); 399 } catch (RemoteException e) { 400 throw e.rethrowFromSystemServer(); 401 } 402 } 403 404 /** 405 * Sets sensor privacy to the specified state for an individual sensor. 406 * 407 * @param sensor the sensor which to change the state for 408 * @param enable the state to which sensor privacy should be set. 409 * 410 * @hide 411 */ 412 @TestApi 413 @RequiresPermission(Manifest.permission.MANAGE_SENSOR_PRIVACY) setSensorPrivacy(@ources.Source int source, @Sensors.Sensor int sensor, boolean enable)414 public void setSensorPrivacy(@Sources.Source int source, @Sensors.Sensor int sensor, 415 boolean enable) { 416 setSensorPrivacy(source, sensor, enable, UserHandle.USER_CURRENT); 417 } 418 419 /** 420 * Sets sensor privacy to the specified state for an individual sensor. 421 * 422 * @param sensor the sensor which to change the state for 423 * @param enable the state to which sensor privacy should be set. 424 * @param userId the user's id 425 * 426 * @hide 427 */ 428 @RequiresPermission(Manifest.permission.MANAGE_SENSOR_PRIVACY) setSensorPrivacy(@ources.Source int source, @Sensors.Sensor int sensor, boolean enable, @UserIdInt int userId)429 public void setSensorPrivacy(@Sources.Source int source, @Sensors.Sensor int sensor, 430 boolean enable, @UserIdInt int userId) { 431 try { 432 mService.setIndividualSensorPrivacy(userId, source, sensor, enable); 433 } catch (RemoteException e) { 434 throw e.rethrowFromSystemServer(); 435 } 436 } 437 438 /** 439 * Sets sensor privacy to the specified state for an individual sensor for the profile group of 440 * context's user. 441 * 442 * @param source the source using which the sensor is toggled. 443 * @param sensor the sensor which to change the state for 444 * @param enable the state to which sensor privacy should be set. 445 * 446 * @hide 447 */ 448 @TestApi 449 @RequiresPermission(Manifest.permission.MANAGE_SENSOR_PRIVACY) setSensorPrivacyForProfileGroup(@ources.Source int source, @Sensors.Sensor int sensor, boolean enable)450 public void setSensorPrivacyForProfileGroup(@Sources.Source int source, 451 @Sensors.Sensor int sensor, boolean enable) { 452 setSensorPrivacyForProfileGroup(source , sensor, enable, UserHandle.USER_CURRENT); 453 } 454 455 /** 456 * Sets sensor privacy to the specified state for an individual sensor for the profile group of 457 * context's user. 458 * 459 * @param source the source using which the sensor is toggled. 460 * @param sensor the sensor which to change the state for 461 * @param enable the state to which sensor privacy should be set. 462 * @param userId the user's id 463 * 464 * @hide 465 */ 466 @RequiresPermission(Manifest.permission.MANAGE_SENSOR_PRIVACY) setSensorPrivacyForProfileGroup(@ources.Source int source, @Sensors.Sensor int sensor, boolean enable, @UserIdInt int userId)467 public void setSensorPrivacyForProfileGroup(@Sources.Source int source, 468 @Sensors.Sensor int sensor, boolean enable, @UserIdInt int userId) { 469 try { 470 mService.setIndividualSensorPrivacyForProfileGroup(userId, source, sensor, enable); 471 } catch (RemoteException e) { 472 throw e.rethrowFromSystemServer(); 473 } 474 } 475 476 /** 477 * Don't show dialogs to turn off sensor privacy for this package. 478 * 479 * @param packageName Package name not to show dialogs for 480 * @param suppress Whether to suppress or re-enable. 481 * 482 * @hide 483 */ 484 @RequiresPermission(Manifest.permission.MANAGE_SENSOR_PRIVACY) suppressSensorPrivacyReminders(int sensor, boolean suppress)485 public void suppressSensorPrivacyReminders(int sensor, 486 boolean suppress) { 487 suppressSensorPrivacyReminders(sensor, suppress, UserHandle.USER_CURRENT); 488 } 489 490 /** 491 * Don't show dialogs to turn off sensor privacy for this package. 492 * 493 * @param packageName Package name not to show dialogs for 494 * @param suppress Whether to suppress or re-enable. 495 * @param userId the user's id 496 * 497 * @hide 498 */ 499 @RequiresPermission(Manifest.permission.MANAGE_SENSOR_PRIVACY) suppressSensorPrivacyReminders(int sensor, boolean suppress, @UserIdInt int userId)500 public void suppressSensorPrivacyReminders(int sensor, 501 boolean suppress, @UserIdInt int userId) { 502 try { 503 mService.suppressIndividualSensorPrivacyReminders(userId, sensor, 504 token, suppress); 505 } catch (RemoteException e) { 506 throw e.rethrowFromSystemServer(); 507 } 508 } 509 510 /** 511 * If sensor privacy for the provided sensor is enabled then this call will show the user the 512 * dialog which is shown when an application attempts to use that sensor. If privacy isn't 513 * enabled then this does nothing. 514 * 515 * This call can only be made by the system uid. 516 * 517 * @throws SecurityException when called by someone other than system uid. 518 * 519 * @hide 520 */ showSensorUseDialog(int sensor)521 public void showSensorUseDialog(int sensor) { 522 try { 523 mService.showSensorUseDialog(sensor); 524 } catch (RemoteException e) { 525 Log.e(LOG_TAG, "Received exception while trying to show sensor use dialog", e); 526 } 527 } 528 529 /** 530 * A class implementing this interface can register with the {@link 531 * android.hardware.SensorPrivacyManager} to receive notification when the all-sensor privacy 532 * state changes. 533 * 534 * @hide 535 */ 536 public interface OnAllSensorPrivacyChangedListener { 537 /** 538 * Callback invoked when the sensor privacy state changes. 539 * 540 * @param enabled true if sensor privacy is enabled, false otherwise. 541 */ onAllSensorPrivacyChanged(boolean enabled)542 void onAllSensorPrivacyChanged(boolean enabled); 543 } 544 545 /** 546 * Sets all-sensor privacy to the specified state. 547 * 548 * @param enable the state to which sensor privacy should be set. 549 * 550 * @hide 551 */ 552 @RequiresPermission(Manifest.permission.MANAGE_SENSOR_PRIVACY) setAllSensorPrivacy(boolean enable)553 public void setAllSensorPrivacy(boolean enable) { 554 try { 555 mService.setSensorPrivacy(enable); 556 } catch (RemoteException e) { 557 throw e.rethrowFromSystemServer(); 558 } 559 } 560 561 /** 562 * Registers a new listener to receive notification when the state of all-sensor privacy 563 * changes. 564 * 565 * @param listener the OnSensorPrivacyChangedListener to be notified when the state of 566 * all-sensor privacy changes. 567 * 568 * @hide 569 */ 570 @RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY) addAllSensorPrivacyListener( @onNull final OnAllSensorPrivacyChangedListener listener)571 public void addAllSensorPrivacyListener( 572 @NonNull final OnAllSensorPrivacyChangedListener listener) { 573 synchronized (mListeners) { 574 ISensorPrivacyListener iListener = mListeners.get(listener); 575 if (iListener == null) { 576 iListener = new ISensorPrivacyListener.Stub() { 577 @Override 578 public void onSensorPrivacyChanged(boolean enabled) { 579 listener.onAllSensorPrivacyChanged(enabled); 580 } 581 }; 582 mListeners.put(listener, iListener); 583 } 584 585 try { 586 mService.addSensorPrivacyListener(iListener); 587 } catch (RemoteException e) { 588 throw e.rethrowFromSystemServer(); 589 } 590 } 591 } 592 593 /** 594 * Unregisters the specified listener from receiving notifications when the state of all-sensor 595 * privacy changes. 596 * 597 * @param listener the OnAllSensorPrivacyChangedListener to be unregistered from notifications 598 * when all-sensor privacy changes. 599 * 600 * @hide 601 */ 602 @RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY) removeAllSensorPrivacyListener( @onNull OnAllSensorPrivacyChangedListener listener)603 public void removeAllSensorPrivacyListener( 604 @NonNull OnAllSensorPrivacyChangedListener listener) { 605 synchronized (mListeners) { 606 ISensorPrivacyListener iListener = mListeners.get(listener); 607 if (iListener != null) { 608 mListeners.remove(iListener); 609 try { 610 mService.removeSensorPrivacyListener(iListener); 611 } catch (RemoteException e) { 612 throw e.rethrowFromSystemServer(); 613 } 614 } 615 } 616 } 617 618 /** 619 * Returns whether all-sensor privacy is currently enabled. 620 * 621 * @return true if all-sensor privacy is currently enabled, false otherwise. 622 * 623 * @hide 624 */ 625 @RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY) isAllSensorPrivacyEnabled()626 public boolean isAllSensorPrivacyEnabled() { 627 try { 628 return mService.isSensorPrivacyEnabled(); 629 } catch (RemoteException e) { 630 throw e.rethrowFromSystemServer(); 631 } 632 } 633 634 } 635