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