• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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