From eca12d241cf098d9823150706fb55f3d4a1f14e2 Mon Sep 17 00:00:00 2001 From: "Philip P. Moltmann" Date: Tue, 5 Jan 2021 18:12:52 -0800 Subject: [PATCH] Add mic sensor privacy QS tile - factor out generic code from CameraToggleTile and - reuse this generic code for MicrophoneToggleTile Bug: 162549680 Test: Toggled tiles and looked at `dumpsys sensor_privacy` Change-Id: Ib45c54c686e271facb78f0f14861045cb97641bf --- .../SystemUI/res/drawable/ic_mic_blocked.xml | 29 +++++ packages/SystemUI/res/values/config.xml | 2 +- packages/SystemUI/res/values/strings.xml | 2 + packages/SystemUI/res/xml/tuner_prefs.xml | 2 +- .../systemui/qs/tileimpl/QSFactoryImpl.java | 8 +- .../systemui/qs/tiles/CameraToggleTile.java | 63 +++------- .../qs/tiles/MicrophoneToggleTile.java | 78 ++++++++++++ .../qs/tiles/SensorPrivacyToggleTile.java | 112 ++++++++++++++++++ ...=> IndividualSensorPrivacyController.java} | 12 +- ...ndividualSensorPrivacyControllerImpl.java} | 36 ++++-- .../policy/dagger/StatusBarPolicyModule.java | 7 +- 11 files changed, 279 insertions(+), 72 deletions(-) create mode 100644 packages/SystemUI/res/drawable/ic_mic_blocked.xml create mode 100644 packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java create mode 100644 packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java rename packages/SystemUI/src/com/android/systemui/statusbar/policy/{CameraToggleController.java => IndividualSensorPrivacyController.java} (63%) rename packages/SystemUI/src/com/android/systemui/statusbar/policy/{CameraToggleControllerImpl.java => IndividualSensorPrivacyControllerImpl.java} (55%) diff --git a/packages/SystemUI/res/drawable/ic_mic_blocked.xml b/packages/SystemUI/res/drawable/ic_mic_blocked.xml new file mode 100644 index 0000000000000..0ce7a581a7e5d --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_mic_blocked.xml @@ -0,0 +1,29 @@ + + + + + diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index 86817f7bd1c46..93d2f751e7542 100644 --- a/packages/SystemUI/res/values/config.xml +++ b/packages/SystemUI/res/values/config.xml @@ -107,7 +107,7 @@ - wifi,cell,battery,dnd,flashlight,rotation,bt,airplane,location,hotspot,inversion,saver,dark,work,cast,night,screenrecord,reverse,reduce_brightness,cameratoggle + wifi,cell,battery,dnd,flashlight,rotation,bt,airplane,location,hotspot,inversion,saver,dark,work,cast,night,screenrecord,reverse,reduce_brightness,cameratoggle,mictoggle diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index e12342c9d42bb..ac2e342b3c34a 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -823,6 +823,8 @@ Location Off Block Camera + + Mute Microphone Media device diff --git a/packages/SystemUI/res/xml/tuner_prefs.xml b/packages/SystemUI/res/xml/tuner_prefs.xml index 9f229b13f383f..902de23a9e2a4 100644 --- a/packages/SystemUI/res/xml/tuner_prefs.xml +++ b/packages/SystemUI/res/xml/tuner_prefs.xml @@ -52,7 +52,7 @@ + android:title="@string/quick_settings_camera_label" /> diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java index 69ddd2513a951..9b3775e72f9a5 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java @@ -40,6 +40,7 @@ import com.android.systemui.qs.tiles.FlashlightTile; import com.android.systemui.qs.tiles.HotspotTile; import com.android.systemui.qs.tiles.InternetTile; import com.android.systemui.qs.tiles.LocationTile; +import com.android.systemui.qs.tiles.MicrophoneToggleTile; import com.android.systemui.qs.tiles.NfcTile; import com.android.systemui.qs.tiles.NightDisplayTile; import com.android.systemui.qs.tiles.ReduceBrightColorsTile; @@ -85,6 +86,7 @@ public class QSFactoryImpl implements QSFactory { private final Provider mScreenRecordTileProvider; private final Provider mReduceBrightColorsTileProvider; private final Provider mCameraToggleTileProvider; + private final Provider mMicrophoneToggleTileProvider; private final Lazy mQsHostLazy; private final Provider mCustomTileBuilderProvider; @@ -118,7 +120,8 @@ public class QSFactoryImpl implements QSFactory { Provider uiModeNightTileProvider, Provider screenRecordTileProvider, Provider reduceBrightColorsTileProvider, - Provider cameraToggleTileProvider) { + Provider cameraToggleTileProvider, + Provider microphoneToggleTileProvider) { mQsHostLazy = qsHostLazy; mCustomTileBuilderProvider = customTileBuilderProvider; @@ -147,6 +150,7 @@ public class QSFactoryImpl implements QSFactory { mScreenRecordTileProvider = screenRecordTileProvider; mReduceBrightColorsTileProvider = reduceBrightColorsTileProvider; mCameraToggleTileProvider = cameraToggleTileProvider; + mMicrophoneToggleTileProvider = microphoneToggleTileProvider; } public QSTile createTile(String tileSpec) { @@ -204,6 +208,8 @@ public class QSFactoryImpl implements QSFactory { return mReduceBrightColorsTileProvider.get(); case "cameratoggle": return mCameraToggleTileProvider.get(); + case "mictoggle": + return mMicrophoneToggleTileProvider.get(); } // Custom tiles diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java index 093163070dd50..98740a20a7c0c 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java @@ -16,33 +16,31 @@ package com.android.systemui.qs.tiles; +import static android.service.SensorPrivacyIndividualEnabledSensorProto.CAMERA; + import static com.android.systemui.DejankUtils.whitelistIpcs; -import android.content.Intent; +import android.annotation.StringRes; import android.os.Handler; import android.os.Looper; import android.provider.DeviceConfig; -import android.service.quicksettings.Tile; -import android.widget.Switch; + +import androidx.annotation.DrawableRes; +import androidx.annotation.NonNull; import com.android.internal.logging.MetricsLogger; import com.android.systemui.R; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.plugins.ActivityStarter; -import com.android.systemui.plugins.qs.QSTile; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.qs.QSHost; import com.android.systemui.qs.logging.QSLogger; -import com.android.systemui.qs.tileimpl.QSTileImpl; -import com.android.systemui.statusbar.policy.CameraToggleController; +import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController; import javax.inject.Inject; -public class CameraToggleTile extends QSTileImpl implements - CameraToggleController.Callback { - - private CameraToggleController mCameraToggleController; +public class CameraToggleTile extends SensorPrivacyToggleTile { @Inject protected CameraToggleTile(QSHost host, @@ -52,11 +50,9 @@ public class CameraToggleTile extends QSTileImpl implements StatusBarStateController statusBarStateController, ActivityStarter activityStarter, QSLogger qsLogger, - CameraToggleController cameraToggleController) { + IndividualSensorPrivacyController sensorPrivacyController) { super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController, - activityStarter, qsLogger); - mCameraToggleController = cameraToggleController; - mCameraToggleController.observe(getLifecycle(), this); + activityStarter, qsLogger, sensorPrivacyController); } @Override @@ -68,46 +64,17 @@ public class CameraToggleTile extends QSTileImpl implements } @Override - public BooleanState newTileState() { - return new BooleanState(); + public @DrawableRes int getIconRes() { + return R.drawable.ic_camera_blocked; } @Override - protected void handleClick() { - mCameraToggleController.setCameraBlocked(!mCameraToggleController.isCameraBlocked()); - } - - @Override - protected void handleUpdateState(BooleanState state, Object arg) { - boolean isBlocked = arg == null ? mCameraToggleController.setCameraBlocked() - : (boolean) arg; - - state.icon = ResourceIcon.get(R.drawable.ic_camera_blocked); - state.state = isBlocked ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE; - state.value = isBlocked; - state.label = getTileLabel(); - state.handlesLongClick = false; - state.contentDescription = state.label; - state.expandedAccessibilityClassName = Switch.class.getName(); - } - - @Override - public int getMetricsCategory() { - return 0; - } - - @Override - public Intent getLongClickIntent() { - return null; - } - - @Override - public CharSequence getTileLabel() { + public @NonNull CharSequence getTileLabel() { return mContext.getString(R.string.quick_settings_camera_label); } @Override - public void onCameraBlockedChanged(boolean enable) { - refreshState(enable); + public int getSensorId() { + return CAMERA; } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java new file mode 100644 index 0000000000000..8cc0d7b4e8b8b --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/MicrophoneToggleTile.java @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2020 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 com.android.systemui.qs.tiles; + +import static android.service.SensorPrivacyIndividualEnabledSensorProto.MICROPHONE; + +import static com.android.systemui.DejankUtils.whitelistIpcs; + +import android.os.Handler; +import android.os.Looper; +import android.provider.DeviceConfig; + +import androidx.annotation.DrawableRes; +import androidx.annotation.NonNull; + +import com.android.internal.logging.MetricsLogger; +import com.android.systemui.R; +import com.android.systemui.dagger.qualifiers.Background; +import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.qs.QSHost; +import com.android.systemui.qs.logging.QSLogger; +import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController; + +import javax.inject.Inject; + +public class MicrophoneToggleTile extends SensorPrivacyToggleTile { + + @Inject + protected MicrophoneToggleTile(QSHost host, + @Background Looper backgroundLooper, + @Main Handler mainHandler, + MetricsLogger metricsLogger, + StatusBarStateController statusBarStateController, + ActivityStarter activityStarter, + QSLogger qsLogger, + IndividualSensorPrivacyController sensorPrivacyController) { + super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController, + activityStarter, qsLogger, sensorPrivacyController); + } + + @Override + public boolean isAvailable() { + return whitelistIpcs(() -> DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, + "mic_toggle_enabled", + false)); + } + + @Override + public @DrawableRes int getIconRes() { + return R.drawable.ic_mic_blocked; + } + + @Override + public @NonNull CharSequence getTileLabel() { + return mContext.getString(R.string.quick_settings_mic_label); + } + + @Override + public int getSensorId() { + return MICROPHONE; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java new file mode 100644 index 0000000000000..12205d6483a8a --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/SensorPrivacyToggleTile.java @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2020 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 com.android.systemui.qs.tiles; + +import android.content.Intent; +import android.hardware.SensorPrivacyManager.IndividualSensor; +import android.os.Handler; +import android.os.Looper; +import android.service.quicksettings.Tile; +import android.widget.Switch; + +import androidx.annotation.DrawableRes; + +import com.android.internal.logging.MetricsLogger; +import com.android.systemui.dagger.qualifiers.Background; +import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.plugins.qs.QSTile; +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.qs.QSHost; +import com.android.systemui.qs.logging.QSLogger; +import com.android.systemui.qs.tileimpl.QSTileImpl; +import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController; + +/** + * Superclass to toggle individual sensor privacy via quick settings tiles + */ +public abstract class SensorPrivacyToggleTile extends QSTileImpl implements + IndividualSensorPrivacyController.Callback { + + private IndividualSensorPrivacyController mSensorPrivacyController; + + /** + * @return Id of the sensor that will be toggled + */ + public abstract @IndividualSensor int getSensorId(); + + /** + * @return icon for the QS tile + */ + public abstract @DrawableRes int getIconRes(); + + protected SensorPrivacyToggleTile(QSHost host, + @Background Looper backgroundLooper, + @Main Handler mainHandler, + MetricsLogger metricsLogger, + StatusBarStateController statusBarStateController, + ActivityStarter activityStarter, + QSLogger qsLogger, + IndividualSensorPrivacyController sensorPrivacyController) { + super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController, + activityStarter, qsLogger); + mSensorPrivacyController = sensorPrivacyController; + mSensorPrivacyController.observe(getLifecycle(), this); + } + + @Override + public BooleanState newTileState() { + return new BooleanState(); + } + + @Override + protected void handleClick() { + mSensorPrivacyController.setSensorBlocked(getSensorId(), + !mSensorPrivacyController.isSensorBlocked(getSensorId())); + } + + @Override + protected void handleUpdateState(BooleanState state, Object arg) { + boolean isBlocked = arg == null ? mSensorPrivacyController.isSensorBlocked(getSensorId()) + : (boolean) arg; + + state.icon = ResourceIcon.get(getIconRes()); + state.state = isBlocked ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE; + state.value = isBlocked; + state.label = getTileLabel(); + state.handlesLongClick = false; + state.contentDescription = state.label; + state.expandedAccessibilityClassName = Switch.class.getName(); + } + + @Override + public int getMetricsCategory() { + return 0; + } + + @Override + public Intent getLongClickIntent() { + return null; + } + + @Override + public void onSensorBlockedChanged(int sensor, boolean blocked) { + if (sensor == getSensorId()) { + refreshState(blocked); + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CameraToggleController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyController.java similarity index 63% rename from packages/SystemUI/src/com/android/systemui/statusbar/policy/CameraToggleController.java rename to packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyController.java index b9de21c1fb827..22e6b072f6a47 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CameraToggleController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyController.java @@ -16,14 +16,16 @@ package com.android.systemui.statusbar.policy; -public interface CameraToggleController extends - CallbackController { +import android.hardware.SensorPrivacyManager.IndividualSensor; - boolean isCameraBlocked(); +public interface IndividualSensorPrivacyController extends + CallbackController { - void setCameraBlocked(boolean blocked); + boolean isSensorBlocked(@IndividualSensor int sensor); + + void setSensorBlocked(@IndividualSensor int sensor, boolean blocked); interface Callback { - void onCameraBlockedChanged(boolean blocked); + void onSensorBlockedChanged(@IndividualSensor int sensor, boolean blocked); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CameraToggleControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java similarity index 55% rename from packages/SystemUI/src/com/android/systemui/statusbar/policy/CameraToggleControllerImpl.java rename to packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java index 3fb1ac56226e9..a7eb5944200f9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CameraToggleControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java @@ -17,10 +17,13 @@ package com.android.systemui.statusbar.policy; import static android.service.SensorPrivacyIndividualEnabledSensorProto.CAMERA; +import static android.service.SensorPrivacyIndividualEnabledSensorProto.MICROPHONE; import android.content.Context; import android.hardware.SensorPrivacyManager; +import android.hardware.SensorPrivacyManager.IndividualSensor; import android.util.ArraySet; +import android.util.SparseBooleanArray; import androidx.annotation.NonNull; @@ -28,30 +31,36 @@ import java.util.Set; import javax.inject.Inject; -public class CameraToggleControllerImpl implements CameraToggleController { +public class IndividualSensorPrivacyControllerImpl implements IndividualSensorPrivacyController { + + private static final int[] SENSORS = new int[] {CAMERA, MICROPHONE}; private final @NonNull Context mContext; private final @NonNull SensorPrivacyManager mSensorPrivacyManager; - private boolean mState; - private Set mCallbacks = new ArraySet<>(); + private final SparseBooleanArray mState = new SparseBooleanArray(); + private final Set mCallbacks = new ArraySet<>(); @Inject - public CameraToggleControllerImpl(@NonNull Context context) { + public IndividualSensorPrivacyControllerImpl(@NonNull Context context) { mContext = context; mSensorPrivacyManager = context.getSystemService(SensorPrivacyManager.class); - mSensorPrivacyManager.addSensorPrivacyListener(CAMERA, this::onCameraPrivacyChanged); - mState = mSensorPrivacyManager.isIndividualSensorPrivacyEnabled(CAMERA); + for (int sensor : SENSORS) { + mSensorPrivacyManager.addSensorPrivacyListener(sensor, + (enabled) -> onSensorPrivacyChanged(sensor, enabled)); + + mState.put(sensor, mSensorPrivacyManager.isIndividualSensorPrivacyEnabled(sensor)); + } } @Override - public boolean isCameraBlocked() { - return mState; + public boolean isSensorBlocked(@IndividualSensor int sensor) { + return mState.get(sensor, false); } @Override - public void setCameraBlocked(boolean blocked) { - mSensorPrivacyManager.setIndividualSensorPrivacyForProfileGroup(CAMERA, blocked); + public void setSensorBlocked(@IndividualSensor int sensor, boolean blocked) { + mSensorPrivacyManager.setIndividualSensorPrivacyForProfileGroup(sensor, blocked); } @Override @@ -64,10 +73,11 @@ public class CameraToggleControllerImpl implements CameraToggleController { mCallbacks.remove(listener); } - private void onCameraPrivacyChanged(boolean state) { - mState = state; + private void onSensorPrivacyChanged(@IndividualSensor int sensor, boolean blocked) { + mState.put(sensor, blocked); + for (Callback callback : mCallbacks) { - callback.onCameraBlockedChanged(mState); + callback.onSensorBlockedChanged(sensor, blocked); } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java index a6278d689e0fe..366ae5491cd49 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/dagger/StatusBarPolicyModule.java @@ -24,8 +24,6 @@ import com.android.systemui.settings.UserTracker; import com.android.systemui.statusbar.policy.AccessPointControllerImpl; import com.android.systemui.statusbar.policy.BluetoothController; import com.android.systemui.statusbar.policy.BluetoothControllerImpl; -import com.android.systemui.statusbar.policy.CameraToggleController; -import com.android.systemui.statusbar.policy.CameraToggleControllerImpl; import com.android.systemui.statusbar.policy.CastController; import com.android.systemui.statusbar.policy.CastControllerImpl; import com.android.systemui.statusbar.policy.ExtensionController; @@ -34,6 +32,8 @@ import com.android.systemui.statusbar.policy.FlashlightController; import com.android.systemui.statusbar.policy.FlashlightControllerImpl; import com.android.systemui.statusbar.policy.HotspotController; import com.android.systemui.statusbar.policy.HotspotControllerImpl; +import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController; +import com.android.systemui.statusbar.policy.IndividualSensorPrivacyControllerImpl; import com.android.systemui.statusbar.policy.KeyguardStateController; import com.android.systemui.statusbar.policy.KeyguardStateControllerImpl; import com.android.systemui.statusbar.policy.LocationController; @@ -122,7 +122,8 @@ public interface StatusBarPolicyModule { /** */ @Binds - CameraToggleController provideCameraToggleController(CameraToggleControllerImpl controllerImpl); + IndividualSensorPrivacyController provideIndividualSensorPrivacyController( + IndividualSensorPrivacyControllerImpl controllerImpl); /** */ @SysUISingleton