diff --git a/core/java/android/hardware/SensorPrivacyManagerInternal.java b/core/java/android/hardware/SensorPrivacyManagerInternal.java
index d12e9f8418bcb..f4de96625b56c 100644
--- a/core/java/android/hardware/SensorPrivacyManagerInternal.java
+++ b/core/java/android/hardware/SensorPrivacyManagerInternal.java
@@ -61,4 +61,9 @@ public abstract class SensorPrivacyManagerInternal {
*/
public abstract void addSensorPrivacyListenerForAllUsers(int sensor,
OnUserSensorPrivacyChangedListener listener);
+
+ /**
+ * Set the HW toggle sensor value based on HW switch states, called from InputManagerService
+ */
+ public abstract void setPhysicalToggleSensorPrivacy(int userId, int sensor, boolean enable);
}
diff --git a/core/res/res/values-television/config.xml b/core/res/res/values-television/config.xml
index 92bea34d55242..e15bafaaa115f 100644
--- a/core/res/res/values-television/config.xml
+++ b/core/res/res/values-television/config.xml
@@ -49,6 +49,12 @@
com.android.systemui/com.android.systemui.sensorprivacy.television.TvUnblockSensorActivity
+
+
+ com.android.systemui/com.android.systemui.sensorprivacy.television.TvUnblockSensorActivity
+
+
com.android.systemui/com.android.systemui.usb.tv.TvUsbPermissionActivity
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index c3959fd99e669..607691b7fd638 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2902,6 +2902,11 @@
com.android.systemui/com.android.systemui.sensorprivacy.SensorUseStartedActivity
+
+ com.android.systemui/com.android.systemui.sensorprivacy.SensorUseStartedActivity
+
false
false
+
+ true
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index d24d41f3c9992..3ff9471a32113 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -375,6 +375,7 @@
+
@@ -4648,6 +4649,7 @@
+
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 940c25c08ad1d..c15242a454828 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -41,6 +41,9 @@ import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.database.ContentObserver;
import android.graphics.PointF;
+import android.hardware.SensorPrivacyManager;
+import android.hardware.SensorPrivacyManager.Sensors;
+import android.hardware.SensorPrivacyManagerInternal;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayViewport;
import android.hardware.input.IInputDevicesChangedListener;
@@ -550,6 +553,19 @@ public class InputManagerService extends IInputManager.Stub
}
}
+ // Set the HW mic toggle switch state
+ final int micMuteState = getSwitchState(-1 /* deviceId */, InputDevice.SOURCE_ANY,
+ SW_MUTE_DEVICE);
+ if (micMuteState != InputManager.SWITCH_STATE_UNKNOWN) {
+ setSensorPrivacy(Sensors.MICROPHONE, micMuteState != InputManager.SWITCH_STATE_OFF);
+ }
+ // Set the HW camera toggle switch state
+ final int cameraMuteState = getSwitchState(-1 /* deviceId */, InputDevice.SOURCE_ANY,
+ SW_CAMERA_LENS_COVER);
+ if (cameraMuteState != InputManager.SWITCH_STATE_UNKNOWN) {
+ setSensorPrivacy(Sensors.CAMERA, cameraMuteState != InputManager.SWITCH_STATE_OFF);
+ }
+
IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
@@ -2816,6 +2832,8 @@ public class InputManagerService extends IInputManager.Stub
if ((switchMask & SW_CAMERA_LENS_COVER_BIT) != 0) {
final boolean lensCovered = ((switchValues & SW_CAMERA_LENS_COVER_BIT) != 0);
mWindowManagerCallbacks.notifyCameraLensCoverSwitchChanged(whenNanos, lensCovered);
+ // Use SW_CAMERA_LENS_COVER code for camera privacy toggles
+ setSensorPrivacy(Sensors.CAMERA, lensCovered);
}
if (mUseDevInputEventForAudioJack && (switchMask & SW_JACK_BITS) != 0) {
@@ -2836,9 +2854,20 @@ public class InputManagerService extends IInputManager.Stub
final boolean micMute = ((switchValues & SW_MUTE_DEVICE_BIT) != 0);
AudioManager audioManager = mContext.getSystemService(AudioManager.class);
audioManager.setMicrophoneMuteFromSwitch(micMute);
+
+ setSensorPrivacy(Sensors.MICROPHONE, micMute);
}
}
+ // Set the sensor privacy state based on the hardware toggles switch states
+ private void setSensorPrivacy(@SensorPrivacyManager.Sensors.Sensor int sensor,
+ boolean enablePrivacy) {
+ final SensorPrivacyManagerInternal sensorPrivacyManagerInternal =
+ LocalServices.getService(SensorPrivacyManagerInternal.class);
+ sensorPrivacyManagerInternal.setPhysicalToggleSensorPrivacy(UserHandle.USER_CURRENT, sensor,
+ enablePrivacy);
+ }
+
// Native callback.
@SuppressWarnings("unused")
private void notifyInputChannelBroken(IBinder token) {
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index c0abbf62e2a75..8a8ee8a1327d0 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -3547,6 +3547,10 @@ public class PhoneWindowManager implements WindowManagerPolicy {
if (mCameraLensCoverState == lensCoverState) {
return;
}
+ if (!mContext.getResources().getBoolean(
+ R.bool.config_launchCameraOnCameraLensCoverToggle)) {
+ return;
+ }
if (mCameraLensCoverState == CAMERA_LENS_COVERED &&
lensCoverState == CAMERA_LENS_UNCOVERED) {
Intent intent;
diff --git a/services/core/java/com/android/server/sensorprivacy/PersistedState.java b/services/core/java/com/android/server/sensorprivacy/PersistedState.java
index ce9fff599d435..06f5fc00116fa 100644
--- a/services/core/java/com/android/server/sensorprivacy/PersistedState.java
+++ b/services/core/java/com/android/server/sensorprivacy/PersistedState.java
@@ -295,6 +295,11 @@ class PersistedState {
TypeUserSensor userSensor = states.keyAt(i);
SensorState sensorState = states.valueAt(i);
+ // Do not persist hardware toggle states. Will be restored on reboot
+ if (userSensor.mType != SensorPrivacyManager.ToggleTypes.SOFTWARE) {
+ continue;
+ }
+
serializer.startTag(null, XML_TAG_SENSOR_STATE);
serializer.attributeInt(null, XML_ATTRIBUTE_TOGGLE_TYPE,
userSensor.mType);
diff --git a/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java b/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java
index ecc2779ffb5f1..358f69e120bbe 100644
--- a/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java
+++ b/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java
@@ -128,6 +128,7 @@ import com.android.server.pm.UserManagerInternal;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Objects;
@@ -309,11 +310,11 @@ public final class SensorPrivacyService extends SystemService {
// Reset sensor privacy when restriction is added
if (!prevRestrictions.getBoolean(UserManager.DISALLOW_CAMERA_TOGGLE)
&& newRestrictions.getBoolean(UserManager.DISALLOW_CAMERA_TOGGLE)) {
- setToggleSensorPrivacyUnchecked(userId, OTHER, CAMERA, false);
+ setToggleSensorPrivacyUnchecked(SOFTWARE, userId, OTHER, CAMERA, false);
}
if (!prevRestrictions.getBoolean(UserManager.DISALLOW_MICROPHONE_TOGGLE)
&& newRestrictions.getBoolean(UserManager.DISALLOW_MICROPHONE_TOGGLE)) {
- setToggleSensorPrivacyUnchecked(userId, OTHER, MICROPHONE, false);
+ setToggleSensorPrivacyUnchecked(SOFTWARE, userId, OTHER, MICROPHONE, false);
}
}
@@ -361,7 +362,7 @@ public final class SensorPrivacyService extends SystemService {
*/
private void onSensorUseStarted(int uid, String packageName, int sensor) {
UserHandle user = UserHandle.of(mCurrentUser);
- if (!isToggleSensorPrivacyEnabled(SOFTWARE, sensor)) {
+ if (!isCombinedToggleSensorPrivacyEnabled(sensor)) {
return;
}
@@ -405,8 +406,8 @@ public final class SensorPrivacyService extends SystemService {
}
tasksOfPackageUsingSensor.add(task);
- } else if (task.topActivity.flattenToString().equals(mContext.getResources()
- .getString(R.string.config_sensorUseStartedActivity))
+ } else if (task.topActivity.flattenToString().equals(
+ getSensorUseActivityName(new ArraySet<>(Arrays.asList(sensor))))
&& task.isFocused) {
enqueueSensorUseReminderDialogAsync(task.taskId, user, packageName,
sensor);
@@ -533,9 +534,8 @@ public final class SensorPrivacyService extends SystemService {
return;
}
Intent dialogIntent = new Intent();
- dialogIntent.setComponent(ComponentName.unflattenFromString(
- mContext.getResources().getString(
- R.string.config_sensorUseStartedActivity)));
+ dialogIntent.setComponent(
+ ComponentName.unflattenFromString(getSensorUseActivityName(sensors)));
ActivityOptions options = ActivityOptions.makeBasic();
options.setLaunchTaskId(info.mTaskId);
@@ -558,6 +558,21 @@ public final class SensorPrivacyService extends SystemService {
mContext.startActivityAsUser(dialogIntent, options.toBundle(), info.mUser);
}
+ /**
+ * Get the activity component based on which privacy toggles are enabled.
+ * @param sensors
+ * @return component name to launch
+ */
+ private String getSensorUseActivityName(ArraySet sensors) {
+ for (Integer sensor : sensors) {
+ if (isToggleSensorPrivacyEnabled(HARDWARE, sensor)) {
+ return mContext.getResources().getString(
+ R.string.config_sensorUseStartedActivity_hwToggle);
+ }
+ }
+ return mContext.getResources().getString(R.string.config_sensorUseStartedActivity);
+ }
+
/**
* Show a notification that informs the user that a sensor use or a blocked sensor started.
* The user can then react to this event.
@@ -676,18 +691,18 @@ public final class SensorPrivacyService extends SystemService {
return;
}
- setToggleSensorPrivacyUnchecked(userId, source, sensor, enable);
+ setToggleSensorPrivacyUnchecked(SOFTWARE, userId, source, sensor, enable);
}
- private void setToggleSensorPrivacyUnchecked(int userId, int source, int sensor,
- boolean enable) {
+ private void setToggleSensorPrivacyUnchecked(int toggleType, int userId, int source,
+ int sensor, boolean enable) {
final long[] lastChange = new long[1];
mSensorPrivacyStateController.atomic(() -> {
SensorState sensorState = mSensorPrivacyStateController
- .getState(SOFTWARE, userId, sensor);
+ .getState(toggleType, userId, sensor);
lastChange[0] = sensorState.getLastChange();
mSensorPrivacyStateController.setState(
- SOFTWARE, userId, sensor, enable, mHandler,
+ toggleType, userId, sensor, enable, mHandler,
changeSuccessful -> {
if (changeSuccessful) {
if (userId == mUserManagerInternal.getProfileParentId(userId)) {
@@ -972,7 +987,7 @@ public final class SensorPrivacyService extends SystemService {
if (Binder.getCallingUid() != Process.SYSTEM_UID) {
throw new SecurityException("Can only be called by the system uid");
}
- if (!isToggleSensorPrivacyEnabled(SOFTWARE, sensor)) {
+ if (!isCombinedToggleSensorPrivacyEnabled(sensor)) {
return;
}
enqueueSensorUseReminderDialogAsync(
@@ -980,24 +995,46 @@ public final class SensorPrivacyService extends SystemService {
}
private void userSwitching(int from, int to) {
- final boolean[] micState = new boolean[1];
- final boolean[] camState = new boolean[1];
- final boolean[] prevMicState = new boolean[1];
- final boolean[] prevCamState = new boolean[1];
+ final boolean[] micState = new boolean[2];
+ final boolean[] camState = new boolean[2];
+ final boolean[] prevMicState = new boolean[2];
+ final boolean[] prevCamState = new boolean[2];
+ final int swToggleIdx = 0;
+ final int hwToggleIdx = 1;
+ // Get SW toggles state
mSensorPrivacyStateController.atomic(() -> {
- prevMicState[0] = isToggleSensorPrivacyEnabledInternal(from, SOFTWARE, MICROPHONE);
- prevCamState[0] = isToggleSensorPrivacyEnabledInternal(from, SOFTWARE, CAMERA);
- micState[0] = isToggleSensorPrivacyEnabledInternal(to, SOFTWARE, MICROPHONE);
- camState[0] = isToggleSensorPrivacyEnabledInternal(to, SOFTWARE, CAMERA);
+ prevMicState[swToggleIdx] = isToggleSensorPrivacyEnabledInternal(from, SOFTWARE,
+ MICROPHONE);
+ prevCamState[swToggleIdx] = isToggleSensorPrivacyEnabledInternal(from, SOFTWARE,
+ CAMERA);
+ micState[swToggleIdx] = isToggleSensorPrivacyEnabledInternal(to, SOFTWARE,
+ MICROPHONE);
+ camState[swToggleIdx] = isToggleSensorPrivacyEnabledInternal(to, SOFTWARE, CAMERA);
});
- if (from == USER_NULL || prevMicState[0] != micState[0]) {
- mHandler.handleSensorPrivacyChanged(to, SOFTWARE, MICROPHONE, micState[0]);
- setGlobalRestriction(MICROPHONE, micState[0]);
+ // Get HW toggles state
+ mSensorPrivacyStateController.atomic(() -> {
+ prevMicState[hwToggleIdx] = isToggleSensorPrivacyEnabledInternal(from, HARDWARE,
+ MICROPHONE);
+ prevCamState[hwToggleIdx] = isToggleSensorPrivacyEnabledInternal(from, HARDWARE,
+ CAMERA);
+ micState[hwToggleIdx] = isToggleSensorPrivacyEnabledInternal(to, HARDWARE,
+ MICROPHONE);
+ camState[hwToggleIdx] = isToggleSensorPrivacyEnabledInternal(to, HARDWARE, CAMERA);
+ });
+
+ if (from == USER_NULL || prevMicState[swToggleIdx] != micState[swToggleIdx]
+ || prevMicState[hwToggleIdx] != micState[hwToggleIdx]) {
+ mHandler.handleSensorPrivacyChanged(to, SOFTWARE, MICROPHONE,
+ micState[swToggleIdx]);
+ mHandler.handleSensorPrivacyChanged(to, HARDWARE, MICROPHONE,
+ micState[hwToggleIdx]);
+ setGlobalRestriction(MICROPHONE, micState[swToggleIdx] || micState[hwToggleIdx]);
}
- if (from == USER_NULL || prevCamState[0] != camState[0]) {
- mHandler.handleSensorPrivacyChanged(to, SOFTWARE, CAMERA,
- camState[0]);
- setGlobalRestriction(CAMERA, camState[0]);
+ if (from == USER_NULL || prevCamState[swToggleIdx] != camState[swToggleIdx]
+ || prevCamState[hwToggleIdx] != camState[hwToggleIdx]) {
+ mHandler.handleSensorPrivacyChanged(to, SOFTWARE, CAMERA, camState[swToggleIdx]);
+ mHandler.handleSensorPrivacyChanged(to, HARDWARE, CAMERA, camState[hwToggleIdx]);
+ setGlobalRestriction(CAMERA, camState[swToggleIdx] || camState[hwToggleIdx]);
}
}
@@ -1265,7 +1302,8 @@ public final class SensorPrivacyService extends SystemService {
mSensorPrivacyManagerInternal.dispatch(userId, sensor, enabled);
if (userId == mCurrentUser) {
- mSensorPrivacyServiceImpl.setGlobalRestriction(sensor, enabled);
+ mSensorPrivacyServiceImpl.setGlobalRestriction(sensor,
+ mSensorPrivacyServiceImpl.isCombinedToggleSensorPrivacyEnabled(sensor));
}
if (userId != mCurrentUser) {
@@ -1438,6 +1476,23 @@ public final class SensorPrivacyService extends SystemService {
sensorListeners.add(listener);
}
}
+
+ @Override
+ public void setPhysicalToggleSensorPrivacy(int userId, int sensor, boolean enable) {
+ final SensorPrivacyServiceImpl sps =
+ SensorPrivacyService.this.mSensorPrivacyServiceImpl;
+
+ // Convert userId to actual user Id. mCurrentUser is USER_NULL if toggle state is set
+ // before onUserStarting.
+ userId = (userId == UserHandle.USER_CURRENT ? mCurrentUser : userId);
+ final int realUserId = (userId == UserHandle.USER_NULL ? mContext.getUserId() : userId);
+
+ sps.setToggleSensorPrivacyUnchecked(HARDWARE, realUserId, OTHER, sensor, enable);
+ // Also disable the SW toggle when disabling the HW toggle
+ if (!enable) {
+ sps.setToggleSensorPrivacyUnchecked(SOFTWARE, realUserId, OTHER, sensor, enable);
+ }
+ }
}
private class CallStateHelper {
@@ -1493,7 +1548,7 @@ public final class SensorPrivacyService extends SystemService {
if (mSensorPrivacyServiceImpl
.isToggleSensorPrivacyEnabled(SOFTWARE, MICROPHONE)) {
mSensorPrivacyServiceImpl.setToggleSensorPrivacyUnchecked(
- mCurrentUser, OTHER, MICROPHONE, false);
+ SOFTWARE, mCurrentUser, OTHER, MICROPHONE, false);
mMicUnmutedForEmergencyCall = true;
} else {
mMicUnmutedForEmergencyCall = false;
@@ -1519,7 +1574,7 @@ public final class SensorPrivacyService extends SystemService {
mIsInEmergencyCall = false;
if (mMicUnmutedForEmergencyCall) {
mSensorPrivacyServiceImpl.setToggleSensorPrivacyUnchecked(
- mCurrentUser, OTHER, MICROPHONE, true);
+ SOFTWARE, mCurrentUser, OTHER, MICROPHONE, true);
mMicUnmutedForEmergencyCall = false;
}
}
diff --git a/services/core/java/com/android/server/sensorprivacy/SensorPrivacyStateControllerImpl.java b/services/core/java/com/android/server/sensorprivacy/SensorPrivacyStateControllerImpl.java
index d1ea8e98fe2fa..3dcb4cf996c40 100644
--- a/services/core/java/com/android/server/sensorprivacy/SensorPrivacyStateControllerImpl.java
+++ b/services/core/java/com/android/server/sensorprivacy/SensorPrivacyStateControllerImpl.java
@@ -16,7 +16,6 @@
package com.android.server.sensorprivacy;
-import android.hardware.SensorPrivacyManager;
import android.os.Handler;
import com.android.internal.util.dump.DualDumpOutputStream;
@@ -49,10 +48,6 @@ class SensorPrivacyStateControllerImpl extends SensorPrivacyStateController {
@Override
SensorState getStateLocked(int toggleType, int userId, int sensor) {
- if (toggleType == SensorPrivacyManager.ToggleTypes.HARDWARE) {
- // Device doesn't support hardware state
- return getDefaultSensorState();
- }
SensorState sensorState = mPersistedState.getState(toggleType, userId, sensor);
if (sensorState != null) {
return new SensorState(sensorState);
@@ -67,12 +62,6 @@ class SensorPrivacyStateControllerImpl extends SensorPrivacyStateController {
@Override
void setStateLocked(int toggleType, int userId, int sensor, boolean enabled,
Handler callbackHandler, SetStateResultCallback callback) {
- if (toggleType != SensorPrivacyManager.ToggleTypes.SOFTWARE) {
- // Implementation only supports software switch
- callbackHandler.sendMessage(PooledLambda.obtainMessage(
- SetStateResultCallback::callback, callback, false));
- return;
- }
// Changing the SensorState's mEnabled updates the timestamp of its last change.
// A nonexistent state -> unmuted should not set the timestamp.
SensorState lastState = mPersistedState.getState(toggleType, userId, sensor);