blob: 3c524b2052f745ef212d175df82a4b695d49f904 [file] [log] [blame]
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.hardware;
import android.Manifest;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
import android.annotation.UserIdInt;
import android.content.Context;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.service.SensorPrivacyIndividualEnabledSensorProto;
import android.service.SensorPrivacyToggleSourceProto;
import android.util.ArrayMap;
import android.util.Log;
import android.util.Pair;
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.concurrent.Executor;
/**
* This class provides information about the microphone and camera toggles.
*/
@SystemService(Context.SENSOR_PRIVACY_SERVICE)
public final class SensorPrivacyManager {
private static final String LOG_TAG = SensorPrivacyManager.class.getSimpleName();
/**
* Unique Id of this manager to identify to the service
* @hide
*/
private IBinder token = new Binder();
/**
* An extra containing a sensor
* @hide
*/
public static final String EXTRA_SENSOR = SensorPrivacyManager.class.getName()
+ ".extra.sensor";
/**
* An extra indicating if all sensors are affected
* @hide
*/
public static final String EXTRA_ALL_SENSORS = SensorPrivacyManager.class.getName()
+ ".extra.all_sensors";
private final SparseArray<Boolean> mToggleSupportCache = new SparseArray<>();
/**
* Individual sensors not listed in {@link Sensors}
*/
public static class Sensors {
private Sensors() {}
/**
* Constant for the microphone
*/
public static final int MICROPHONE = SensorPrivacyIndividualEnabledSensorProto.MICROPHONE;
/**
* Constant for the camera
*/
public static final int CAMERA = SensorPrivacyIndividualEnabledSensorProto.CAMERA;
/**
* Individual sensors not listed in {@link Sensors}
*
* @hide
*/
@IntDef(value = {
MICROPHONE,
CAMERA
})
@Retention(RetentionPolicy.SOURCE)
public @interface Sensor {}
}
/**
* Source through which Privacy Sensor was toggled.
* @hide
*/
@TestApi
public static class Sources {
private Sources() {}
/**
* Constant for the Quick Setting Tile.
*/
public static final int QS_TILE = SensorPrivacyToggleSourceProto.QS_TILE;
/**
* Constant for the Settings.
*/
public static final int SETTINGS = SensorPrivacyToggleSourceProto.SETTINGS;
/**
* Constant for Dialog.
*/
public static final int DIALOG = SensorPrivacyToggleSourceProto.DIALOG;
/**
* Constant for SHELL.
*/
public static final int SHELL = SensorPrivacyToggleSourceProto.SHELL;
/**
* Constant for OTHER.
*/
public static final int OTHER = SensorPrivacyToggleSourceProto.OTHER;
/**
* Source for toggling sensors
*
* @hide
*/
@IntDef(value = {
QS_TILE,
SETTINGS,
DIALOG,
SHELL,
OTHER
})
@Retention(RetentionPolicy.SOURCE)
public @interface Source {}
}
/**
* A class implementing this interface can register with the {@link
* android.hardware.SensorPrivacyManager} to receive notification when the sensor privacy
* state changes.
*
* @hide
*/
@SystemApi
public interface OnSensorPrivacyChangedListener {
/**
* Callback invoked when the sensor privacy state changes.
*
* @param sensor the sensor whose state is changing
* @param enabled true if sensor privacy is enabled, false otherwise.
*/
void onSensorPrivacyChanged(int sensor, boolean enabled);
}
private static final Object sInstanceLock = new Object();
@GuardedBy("sInstanceLock")
private static SensorPrivacyManager sInstance;
@NonNull
private final Context mContext;
@NonNull
private final ISensorPrivacyManager mService;
@NonNull
private final ArrayMap<OnAllSensorPrivacyChangedListener, ISensorPrivacyListener> mListeners;
@NonNull
private final ArrayMap<Pair<OnSensorPrivacyChangedListener, Integer>, ISensorPrivacyListener>
mIndividualListeners;
/**
* Private constructor to ensure only a single instance is created.
*/
private SensorPrivacyManager(Context context, ISensorPrivacyManager service) {
mContext = context;
mService = service;
mListeners = new ArrayMap<>();
mIndividualListeners = new ArrayMap<>();
}
/**
* Returns the single instance of the SensorPrivacyManager.
*
* @hide
*/
public static SensorPrivacyManager getInstance(Context context) {
synchronized (sInstanceLock) {
if (sInstance == null) {
try {
IBinder b = ServiceManager.getServiceOrThrow(Context.SENSOR_PRIVACY_SERVICE);
ISensorPrivacyManager service = ISensorPrivacyManager.Stub.asInterface(b);
sInstance = new SensorPrivacyManager(context, service);
} catch (ServiceManager.ServiceNotFoundException e) {
throw new IllegalStateException(e);
}
}
return sInstance;
}
}
/**
* Checks if the given toggle is supported on this device
* @param sensor The sensor to check
* @return whether the toggle for the sensor is supported on this device.
*/
public boolean supportsSensorToggle(@Sensors.Sensor int sensor) {
try {
Boolean val = mToggleSupportCache.get(sensor);
if (val == null) {
val = mService.supportsSensorToggle(sensor);
mToggleSupportCache.put(sensor, val);
}
return val;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Registers a new listener to receive notification when the state of sensor privacy
* changes.
*
* @param sensor the sensor to listen to changes to
* @param listener the OnSensorPrivacyChangedListener to be notified when the state of sensor
* privacy changes.
*
* @hide
*/
@SystemApi
@RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY)
public void addSensorPrivacyListener(@Sensors.Sensor int sensor,
@NonNull OnSensorPrivacyChangedListener listener) {
addSensorPrivacyListener(sensor, mContext.getMainExecutor(), listener);
}
/**
* Registers a new listener to receive notification when the state of sensor privacy
* changes.
*
* @param sensor the sensor to listen to changes to
* @param userId the user's id
* @param listener the OnSensorPrivacyChangedListener to be notified when the state of sensor
* privacy changes.
*
* @hide
*/
@RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY)
public void addSensorPrivacyListener(@Sensors.Sensor int sensor, @UserIdInt int userId,
@NonNull OnSensorPrivacyChangedListener listener) {
addSensorPrivacyListener(sensor, userId, mContext.getMainExecutor(), listener);
}
/**
* Registers a new listener to receive notification when the state of sensor privacy
* changes.
*
* @param sensor the sensor to listen to changes to
* @param executor the executor to dispatch the callback on
* @param listener the OnSensorPrivacyChangedListener to be notified when the state of sensor
* privacy changes.
*
* @hide
*/
@SystemApi
@RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY)
public void addSensorPrivacyListener(@Sensors.Sensor int sensor, @NonNull Executor executor,
@NonNull OnSensorPrivacyChangedListener listener) {
Pair<OnSensorPrivacyChangedListener, Integer> key = new Pair<>(listener, sensor);
synchronized (mIndividualListeners) {
ISensorPrivacyListener iListener = mIndividualListeners.get(key);
if (iListener == null) {
iListener = new ISensorPrivacyListener.Stub() {
@Override
public void onSensorPrivacyChanged(boolean enabled) {
executor.execute(() -> listener.onSensorPrivacyChanged(sensor, enabled));
}
};
mIndividualListeners.put(key, iListener);
}
try {
mService.addUserGlobalIndividualSensorPrivacyListener(sensor, iListener);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
}
/**
* Registers a new listener to receive notification when the state of sensor privacy
* changes.
*
* @param sensor the sensor to listen to changes to
* @param executor the executor to dispatch the callback on
* @param userId the user's id
* @param listener the OnSensorPrivacyChangedListener to be notified when the state of sensor
* privacy changes.
*
* @hide
*/
@RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY)
public void addSensorPrivacyListener(@Sensors.Sensor int sensor, @UserIdInt int userId,
@NonNull Executor executor, @NonNull OnSensorPrivacyChangedListener listener) {
synchronized (mIndividualListeners) {
ISensorPrivacyListener iListener = mIndividualListeners.get(listener);
if (iListener == null) {
iListener = new ISensorPrivacyListener.Stub() {
@Override
public void onSensorPrivacyChanged(boolean enabled) {
executor.execute(() -> listener.onSensorPrivacyChanged(sensor, enabled));
}
};
mIndividualListeners.put(new Pair<>(listener, sensor), iListener);
}
try {
mService.addIndividualSensorPrivacyListener(userId, sensor,
iListener);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
}
/**
* Unregisters the specified listener from receiving notifications when the state of any sensor
* privacy changes.
*
* @param listener the OnSensorPrivacyChangedListener to be unregistered from notifications when
* sensor privacy changes.
*
* @hide
*/
@SystemApi
@RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY)
public void removeSensorPrivacyListener(@Sensors.Sensor int sensor,
@NonNull OnSensorPrivacyChangedListener listener) {
synchronized (mListeners) {
for (int i = 0; i < mIndividualListeners.size(); i++) {
Pair<OnSensorPrivacyChangedListener, Integer> pair = mIndividualListeners.keyAt(i);
if (pair.second == sensor && pair.first.equals(listener)) {
try {
mService.removeIndividualSensorPrivacyListener(sensor,
mIndividualListeners.valueAt(i));
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
mIndividualListeners.removeAt(i--);
}
}
}
}
/**
* Returns whether sensor privacy is currently enabled for a specific sensor.
*
* @return true if sensor privacy is currently enabled, false otherwise.
*
* @hide
*/
@SystemApi
@RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY)
public boolean isSensorPrivacyEnabled(@Sensors.Sensor int sensor) {
return isSensorPrivacyEnabled(sensor, UserHandle.USER_CURRENT);
}
/**
* Returns whether sensor privacy is currently enabled for a specific sensor.
*
* @return true if sensor privacy is currently enabled, false otherwise.
*
* @hide
*/
@RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY)
public boolean isSensorPrivacyEnabled(@Sensors.Sensor int sensor, @UserIdInt int userId) {
try {
return mService.isIndividualSensorPrivacyEnabled(userId, sensor);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Sets sensor privacy to the specified state for an individual sensor.
*
* @param sensor the sensor which to change the state for
* @param enable the state to which sensor privacy should be set.
*
* @hide
*/
@TestApi
@RequiresPermission(Manifest.permission.MANAGE_SENSOR_PRIVACY)
public void setSensorPrivacy(@Sources.Source int source, @Sensors.Sensor int sensor,
boolean enable) {
setSensorPrivacy(source, sensor, enable, UserHandle.USER_CURRENT);
}
/**
* Sets sensor privacy to the specified state for an individual sensor.
*
* @param sensor the sensor which to change the state for
* @param enable the state to which sensor privacy should be set.
* @param userId the user's id
*
* @hide
*/
@RequiresPermission(Manifest.permission.MANAGE_SENSOR_PRIVACY)
public void setSensorPrivacy(@Sources.Source int source, @Sensors.Sensor int sensor,
boolean enable, @UserIdInt int userId) {
try {
mService.setIndividualSensorPrivacy(userId, source, sensor, enable);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Sets sensor privacy to the specified state for an individual sensor for the profile group of
* context's user.
*
* @param source the source using which the sensor is toggled.
* @param sensor the sensor which to change the state for
* @param enable the state to which sensor privacy should be set.
*
* @hide
*/
@TestApi
@RequiresPermission(Manifest.permission.MANAGE_SENSOR_PRIVACY)
public void setSensorPrivacyForProfileGroup(@Sources.Source int source,
@Sensors.Sensor int sensor, boolean enable) {
setSensorPrivacyForProfileGroup(source , sensor, enable, UserHandle.USER_CURRENT);
}
/**
* Sets sensor privacy to the specified state for an individual sensor for the profile group of
* context's user.
*
* @param source the source using which the sensor is toggled.
* @param sensor the sensor which to change the state for
* @param enable the state to which sensor privacy should be set.
* @param userId the user's id
*
* @hide
*/
@RequiresPermission(Manifest.permission.MANAGE_SENSOR_PRIVACY)
public void setSensorPrivacyForProfileGroup(@Sources.Source int source,
@Sensors.Sensor int sensor, boolean enable, @UserIdInt int userId) {
try {
mService.setIndividualSensorPrivacyForProfileGroup(userId, source, sensor, enable);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Don't show dialogs to turn off sensor privacy for this package.
*
* @param packageName Package name not to show dialogs for
* @param suppress Whether to suppress or re-enable.
*
* @hide
*/
@RequiresPermission(Manifest.permission.MANAGE_SENSOR_PRIVACY)
public void suppressSensorPrivacyReminders(int sensor,
boolean suppress) {
suppressSensorPrivacyReminders(sensor, suppress, UserHandle.USER_CURRENT);
}
/**
* Don't show dialogs to turn off sensor privacy for this package.
*
* @param packageName Package name not to show dialogs for
* @param suppress Whether to suppress or re-enable.
* @param userId the user's id
*
* @hide
*/
@RequiresPermission(Manifest.permission.MANAGE_SENSOR_PRIVACY)
public void suppressSensorPrivacyReminders(int sensor,
boolean suppress, @UserIdInt int userId) {
try {
mService.suppressIndividualSensorPrivacyReminders(userId, sensor,
token, suppress);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* If sensor privacy for the provided sensor is enabled then this call will show the user the
* dialog which is shown when an application attempts to use that sensor. If privacy isn't
* enabled then this does nothing.
*
* This call can only be made by the system uid.
*
* @throws SecurityException when called by someone other than system uid.
*
* @hide
*/
public void showSensorUseDialog(int sensor) {
try {
mService.showSensorUseDialog(sensor);
} catch (RemoteException e) {
Log.e(LOG_TAG, "Received exception while trying to show sensor use dialog", e);
}
}
/**
* A class implementing this interface can register with the {@link
* android.hardware.SensorPrivacyManager} to receive notification when the all-sensor privacy
* state changes.
*
* @hide
*/
public interface OnAllSensorPrivacyChangedListener {
/**
* Callback invoked when the sensor privacy state changes.
*
* @param enabled true if sensor privacy is enabled, false otherwise.
*/
void onAllSensorPrivacyChanged(boolean enabled);
}
/**
* Sets all-sensor privacy to the specified state.
*
* @param enable the state to which sensor privacy should be set.
*
* @hide
*/
@RequiresPermission(Manifest.permission.MANAGE_SENSOR_PRIVACY)
public void setAllSensorPrivacy(boolean enable) {
try {
mService.setSensorPrivacy(enable);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Registers a new listener to receive notification when the state of all-sensor privacy
* changes.
*
* @param listener the OnSensorPrivacyChangedListener to be notified when the state of
* all-sensor privacy changes.
*
* @hide
*/
@RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY)
public void addAllSensorPrivacyListener(
@NonNull final OnAllSensorPrivacyChangedListener listener) {
synchronized (mListeners) {
ISensorPrivacyListener iListener = mListeners.get(listener);
if (iListener == null) {
iListener = new ISensorPrivacyListener.Stub() {
@Override
public void onSensorPrivacyChanged(boolean enabled) {
listener.onAllSensorPrivacyChanged(enabled);
}
};
mListeners.put(listener, iListener);
}
try {
mService.addSensorPrivacyListener(iListener);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
}
/**
* Unregisters the specified listener from receiving notifications when the state of all-sensor
* privacy changes.
*
* @param listener the OnAllSensorPrivacyChangedListener to be unregistered from notifications
* when all-sensor privacy changes.
*
* @hide
*/
@RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY)
public void removeAllSensorPrivacyListener(
@NonNull OnAllSensorPrivacyChangedListener listener) {
synchronized (mListeners) {
ISensorPrivacyListener iListener = mListeners.get(listener);
if (iListener != null) {
mListeners.remove(iListener);
try {
mService.removeSensorPrivacyListener(iListener);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
}
}
/**
* Returns whether all-sensor privacy is currently enabled.
*
* @return true if all-sensor privacy is currently enabled, false otherwise.
*
* @hide
*/
@RequiresPermission(Manifest.permission.OBSERVE_SENSOR_PRIVACY)
public boolean isAllSensorPrivacyEnabled() {
try {
return mService.isSensorPrivacyEnabled();
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
}