Set HW toggle states

Propagate HW toggle state from InputManagerService to SensorPrivacyManager.
Enable devices with 2-way (on/off) or 3-way hardware switches (on/mic-only/off)
 to control the microphone and camera privacy toggles state and behavior.

Devices map the hardware toggles GPIO to SW_MUTE_DEVICE
 and SW_CAMERA_LENS_COVER codes, which are handled by InputFlinger -> InputManagerService.

Test: atest SensorPrivacyServiceMockingTest , Manual
Bug: 191745272
Change-Id: If968a2c2292ab73808e7d5735f156515a6d62e03
This commit is contained in:
Valentin Iftime
2022-02-09 21:01:57 +01:00
parent 92e6edf5c2
commit 28dac743f0
9 changed files with 145 additions and 43 deletions

View File

@@ -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);
}

View File

@@ -49,6 +49,12 @@
com.android.systemui/com.android.systemui.sensorprivacy.television.TvUnblockSensorActivity
</string>
<!-- Component name of the activity used to inform a user about a sensory being blocked because
of hardware privacy switches. -->
<string name="config_sensorUseStartedActivity_hwToggle" translatable="false">
com.android.systemui/com.android.systemui.sensorprivacy.television.TvUnblockSensorActivity
</string>
<!-- Component name of the activity that shows the request for access to a usb device. -->
<string name="config_usbPermissionActivity" translatable="false">
com.android.systemui/com.android.systemui.usb.tv.TvUsbPermissionActivity

View File

@@ -2902,6 +2902,11 @@
<string name="config_sensorUseStartedActivity" translatable="false"
>com.android.systemui/com.android.systemui.sensorprivacy.SensorUseStartedActivity</string>
<!-- Component name of the activity used to inform a user about a sensory being blocked because
of hardware privacy switches. -->
<string name="config_sensorUseStartedActivity_hwToggle" translatable="false"
>com.android.systemui/com.android.systemui.sensorprivacy.SensorUseStartedActivity</string>
<!-- Component name of the activity used to ask a user to confirm system language change after
receiving <Set Menu Language> CEC message. -->
<string name="config_hdmiCecSetMenuLanguageActivity"
@@ -5478,6 +5483,8 @@
<bool name="config_supportsHardwareMicToggle">false</bool>
<!-- Whether this device is supporting the hardware camera toggle -->
<bool name="config_supportsHardwareCamToggle">false</bool>
<!-- Whether a camera intent is launched when the lens cover is toggled -->
<bool name="config_launchCameraOnCameraLensCoverToggle">true</bool>
<!-- List containing the allowed install sources for accessibility service. -->
<string-array name="config_accessibility_allowed_install_source" translatable="false"/>

View File

@@ -375,6 +375,7 @@
<java-symbol type="string" name="config_usbConfirmActivity" />
<java-symbol type="string" name="config_usbResolverActivity" />
<java-symbol type="string" name="config_sensorUseStartedActivity" />
<java-symbol type="string" name="config_sensorUseStartedActivity_hwToggle" />
<java-symbol type="string" name="config_hdmiCecSetMenuLanguageActivity" />
<java-symbol type="integer" name="config_minNumVisibleRecentTasks_lowRam" />
<java-symbol type="integer" name="config_maxNumVisibleRecentTasks_lowRam" />
@@ -4648,6 +4649,7 @@
<java-symbol type="bool" name="config_supportsCamToggle" />
<java-symbol type="bool" name="config_supportsHardwareMicToggle" />
<java-symbol type="bool" name="config_supportsHardwareCamToggle" />
<java-symbol type="bool" name="config_launchCameraOnCameraLensCoverToggle" />
<java-symbol type="dimen" name="starting_surface_icon_size" />
<java-symbol type="dimen" name="starting_surface_default_icon_size" />

View File

@@ -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) {

View File

@@ -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;

View File

@@ -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);

View File

@@ -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<Integer> 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;
}
}

View File

@@ -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);