From da6d30ab8d051532b04c89181b5b4bdaebdd81d5 Mon Sep 17 00:00:00 2001 From: Evan Severson Date: Wed, 4 Nov 2020 21:09:59 -0800 Subject: [PATCH 1/4] Create camera toggle tile Bug: 162549680 Test: None (tested with subsequent change) Change-Id: I35c2528fa2bf5800c3329a7b5448addf476d12ff --- packages/SystemUI/res/values/config.xml | 2 +- packages/SystemUI/res/xml/tuner_prefs.xml | 4 + .../systemui/qs/tileimpl/QSFactoryImpl.java | 8 +- .../systemui/qs/tiles/CameraToggleTile.java | 109 ++++++++++++++++ .../policy/CameraToggleController.java | 33 +++++ .../policy/CameraToggleControllerImpl.java | 117 ++++++++++++++++++ .../policy/dagger/StatusBarPolicyModule.java | 6 + 7 files changed, 277 insertions(+), 2 deletions(-) create mode 100644 packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java create mode 100644 packages/SystemUI/src/com/android/systemui/statusbar/policy/CameraToggleController.java create mode 100644 packages/SystemUI/src/com/android/systemui/statusbar/policy/CameraToggleControllerImpl.java diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml index 101124ec2a2e6..86817f7bd1c46 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 + wifi,cell,battery,dnd,flashlight,rotation,bt,airplane,location,hotspot,inversion,saver,dark,work,cast,night,screenrecord,reverse,reduce_brightness,cameratoggle diff --git a/packages/SystemUI/res/xml/tuner_prefs.xml b/packages/SystemUI/res/xml/tuner_prefs.xml index 6eec5dc9e1c18..9f229b13f383f 100644 --- a/packages/SystemUI/res/xml/tuner_prefs.xml +++ b/packages/SystemUI/res/xml/tuner_prefs.xml @@ -50,6 +50,10 @@ android:key="bluetooth" android:title="@string/quick_settings_bluetooth_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 ba71fa6a8fb39..69ddd2513a951 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java @@ -30,6 +30,7 @@ import com.android.systemui.qs.external.CustomTile; import com.android.systemui.qs.tiles.AirplaneModeTile; import com.android.systemui.qs.tiles.BatterySaverTile; import com.android.systemui.qs.tiles.BluetoothTile; +import com.android.systemui.qs.tiles.CameraToggleTile; import com.android.systemui.qs.tiles.CastTile; import com.android.systemui.qs.tiles.CellularTile; import com.android.systemui.qs.tiles.ColorInversionTile; @@ -83,6 +84,7 @@ public class QSFactoryImpl implements QSFactory { private final Provider mUiModeNightTileProvider; private final Provider mScreenRecordTileProvider; private final Provider mReduceBrightColorsTileProvider; + private final Provider mCameraToggleTileProvider; private final Lazy mQsHostLazy; private final Provider mCustomTileBuilderProvider; @@ -115,7 +117,8 @@ public class QSFactoryImpl implements QSFactory { Provider memoryTileProvider, Provider uiModeNightTileProvider, Provider screenRecordTileProvider, - Provider reduceBrightColorsTileProvider) { + Provider reduceBrightColorsTileProvider, + Provider cameraToggleTileProvider) { mQsHostLazy = qsHostLazy; mCustomTileBuilderProvider = customTileBuilderProvider; @@ -143,6 +146,7 @@ public class QSFactoryImpl implements QSFactory { mUiModeNightTileProvider = uiModeNightTileProvider; mScreenRecordTileProvider = screenRecordTileProvider; mReduceBrightColorsTileProvider = reduceBrightColorsTileProvider; + mCameraToggleTileProvider = cameraToggleTileProvider; } public QSTile createTile(String tileSpec) { @@ -198,6 +202,8 @@ public class QSFactoryImpl implements QSFactory { return mScreenRecordTileProvider.get(); case "reduce_brightness": return mReduceBrightColorsTileProvider.get(); + case "cameratoggle": + return mCameraToggleTileProvider.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 new file mode 100644 index 0000000000000..d719fc3f0d7b9 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java @@ -0,0 +1,109 @@ +/* + * 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.Context; +import android.content.Intent; +import android.graphics.drawable.Drawable; +import android.os.Handler; +import android.os.Looper; +import android.service.quicksettings.Tile; +import android.widget.Switch; + +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 javax.inject.Inject; + +public class CameraToggleTile extends QSTileImpl { + + private CameraToggleController mCameraToggleController; + + @Inject + protected CameraToggleTile(QSHost host, + @Background Looper backgroundLooper, + @Main Handler mainHandler, + MetricsLogger metricsLogger, + StatusBarStateController statusBarStateController, + ActivityStarter activityStarter, + QSLogger qsLogger, + CameraToggleController cameraToggleController) { + super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController, + activityStarter, qsLogger); + mCameraToggleController = cameraToggleController; + mCameraToggleController.addCallback((b) -> refreshState()); + } + + @Override + public BooleanState newTileState() { + return new BooleanState(); + } + + @Override + protected void handleClick() { + mCameraToggleController.setCameraEnabled(!mCameraToggleController.isCameraEnabled()); + } + + @Override + protected void handleUpdateState(BooleanState state, Object arg) { + state.icon = new CameraToggleTileIcon(); + state.state = mCameraToggleController.isCameraEnabled() + ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE; + state.value = mCameraToggleController.isCameraEnabled(); + state.label = "Camera"; + if (!mCameraToggleController.isCameraAvailable()) { + state.secondaryLabel = "Currently in use"; + } else { + state.secondaryLabel = null; + } + 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() { + return "Camera"; + } + + class CameraToggleTileIcon extends Icon { + + @Override + public Drawable getDrawable(Context context) { + return context.getDrawable(R.drawable.ic_camera); + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CameraToggleController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CameraToggleController.java new file mode 100644 index 0000000000000..544f0050d3526 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CameraToggleController.java @@ -0,0 +1,33 @@ +/* + * 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.statusbar.policy; + +import com.android.systemui.Dumpable; + +public interface CameraToggleController extends CallbackController, + Dumpable { + + boolean isCameraEnabled(); + void setCameraEnabled(boolean enabled); + + boolean isCameraAvailable(); + + interface Callback { + void onCameraEnabledChanged(boolean enable); + } + +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CameraToggleControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CameraToggleControllerImpl.java new file mode 100644 index 0000000000000..7496813247c9c --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CameraToggleControllerImpl.java @@ -0,0 +1,117 @@ +/* + * 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.statusbar.policy; + +import android.content.Context; +import android.hardware.camera2.CameraManager; +import android.hardware.camera2.CameraToggleManager; +import android.os.Looper; +import android.util.ArraySet; +import android.widget.Toast; + +import androidx.annotation.NonNull; + +import com.android.systemui.dagger.qualifiers.Background; +import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.dump.DumpManager; + +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.Set; + +import javax.inject.Inject; + +public class CameraToggleControllerImpl implements CameraToggleController { + + private final Context mContext; + private final DumpManager mDumpManager; + private final CameraToggleManager mCameraToggleManager; + + private boolean mState = true; + + Set mCallbacks = new ArraySet<>(); + + Set mUsedCameras = new ArraySet<>(); + + /** + */ + @Inject + public CameraToggleControllerImpl( + Context context, + DumpManager dumpManager, + @Background Looper bgLooper, + @Main Looper mainLooper) { + mContext = context; + mDumpManager = dumpManager; + mCameraToggleManager = context.getSystemService(CameraToggleManager.class); + mCameraToggleManager.addCameraToggleChangeListener(this::onCameraChanged); + mState = mCameraToggleManager.isCameraEnabled(); + mContext.getSystemService(CameraManager.class).registerAvailabilityCallback( + context.getMainExecutor(), new CameraManager.AvailabilityCallback() { + @Override + public void onCameraAvailable(@NonNull String cameraId) { + mUsedCameras.remove(cameraId); + onCameraChanged(mState); + } + + @Override + public void onCameraUnavailable(@NonNull String cameraId) { + mUsedCameras.add(cameraId); + onCameraChanged(mState); + } + }); + } + + @Override + public boolean isCameraEnabled() { + return mState; + } + + @Override + public void setCameraEnabled(boolean enabled) { + if (!/*mCameraToggleManager.setCameraEnabled(enabled)*/true) { + Toast.makeText(mContext, "Can't disable camera while in use", Toast.LENGTH_LONG); + } + } + + @Override + public boolean isCameraAvailable() { + return false;/*mUsedCameras.isEmpty();*/ + } + + @Override + public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) { + + } + + @Override + public void addCallback(@NonNull Callback listener) { + mCallbacks.add(listener); + } + + @Override + public void removeCallback(@NonNull Callback listener) { + mCallbacks.remove(listener); + } + + private void onCameraChanged(boolean state) { + mState = state; + for (Callback callback : mCallbacks) { + callback.onCameraEnabledChanged(state); + } + } +} 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 069b4051af502..916a1a8e3582d 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,6 +24,8 @@ 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; @@ -123,6 +125,10 @@ public interface StatusBarPolicyModule { NetworkController.AccessPointController provideAccessPointController( AccessPointControllerImpl accessPointControllerImpl); + /** */ + @Binds + CameraToggleController provideCameraToggleController(CameraToggleControllerImpl controllerImpl); + /** */ @SysUISingleton @Provides From bde12fd465e95d9f84cbbacaaf26cbf6aae9f0e7 Mon Sep 17 00:00:00 2001 From: "Philip P. Moltmann" Date: Tue, 5 Jan 2021 14:42:16 -0800 Subject: [PATCH 2/4] Hook up camera toggle QS tile to SensorPrivacyManager Bug: 162549680 Test: Toggled tile and looked at 'dumpsys sensor_privacy` Change-Id: I8b734adf3df7ef57b7c5c88f4b8fabe6ad60b96f --- .../android/content/pm/PackageManager.java | 11 +++ .../res/drawable/ic_camera_blocked.xml | 29 +++++++ packages/SystemUI/res/values/strings.xml | 2 + .../dagger/SystemUIDefaultModule.java | 12 +++ .../systemui/qs/tiles/CameraToggleTile.java | 48 ++++++------ .../policy/CameraToggleController.java | 14 ++-- .../policy/CameraToggleControllerImpl.java | 76 ++++--------------- .../policy/SensorPrivacyController.java | 5 ++ .../policy/SensorPrivacyControllerImpl.java | 13 ++-- .../policy/dagger/StatusBarPolicyModule.java | 5 -- .../android/systemui/tv/TvSystemUIModule.java | 12 +++ .../android/server/SensorPrivacyService.java | 13 +++- 12 files changed, 134 insertions(+), 106 deletions(-) create mode 100644 packages/SystemUI/res/drawable/ic_camera_blocked.xml diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 17c4d25d82d78..03d4d5e10e648 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -3512,6 +3512,17 @@ public abstract class PackageManager { @SdkConstant(SdkConstantType.FEATURE) public static final String FEATURE_TUNER = "android.hardware.tv.tuner"; + /** + * Feature for {@link #getSystemAvailableFeatures} and + * {@link #hasSystemFeature}: The device supports a enabling/disabling sensor privacy for + * camera. When sensory privacy for the camera is enabled no camera data is send to clients, + * e.g. the view finder in a camera app would appear blank. + * + * @hide + */ + @SdkConstant(SdkConstantType.FEATURE) + public static final String FEATURE_CAMERA_TOGGLE = "android.hardware.camera.toggle"; + /** * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: The device has * the necessary changes to support app enumeration. diff --git a/packages/SystemUI/res/drawable/ic_camera_blocked.xml b/packages/SystemUI/res/drawable/ic_camera_blocked.xml new file mode 100644 index 0000000000000..0161bcbd19370 --- /dev/null +++ b/packages/SystemUI/res/drawable/ic_camera_blocked.xml @@ -0,0 +1,29 @@ + + + + + diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 7cdd8b1b80cab..e12342c9d42bb 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -821,6 +821,8 @@ Location Location Off + + Block Camera Media device diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java index 7ca8e63bfae15..754879e59dd99 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java @@ -20,6 +20,7 @@ import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME import static com.android.systemui.Dependency.LEAK_REPORT_EMAIL_NAME; import android.content.Context; +import android.hardware.SensorPrivacyManager; import android.os.Handler; import android.os.PowerManager; @@ -62,6 +63,8 @@ import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.DeviceProvisionedControllerImpl; import com.android.systemui.statusbar.policy.HeadsUpManager; +import com.android.systemui.statusbar.policy.SensorPrivacyController; +import com.android.systemui.statusbar.policy.SensorPrivacyControllerImpl; import javax.inject.Named; @@ -116,6 +119,15 @@ public abstract class SystemUIDefaultModule { return bC; } + @Provides + @SysUISingleton + static SensorPrivacyController provideSensorPrivacyController( + SensorPrivacyManager sensorPrivacyManager) { + SensorPrivacyController spC = new SensorPrivacyControllerImpl(sensorPrivacyManager); + spC.init(); + return spC; + } + @Binds @SysUISingleton public abstract QSFactory bindQSFactory(QSFactoryImpl qsFactoryImpl); 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 d719fc3f0d7b9..093163070dd50 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CameraToggleTile.java @@ -16,11 +16,12 @@ package com.android.systemui.qs.tiles; -import android.content.Context; +import static com.android.systemui.DejankUtils.whitelistIpcs; + import android.content.Intent; -import android.graphics.drawable.Drawable; import android.os.Handler; import android.os.Looper; +import android.provider.DeviceConfig; import android.service.quicksettings.Tile; import android.widget.Switch; @@ -38,7 +39,8 @@ import com.android.systemui.statusbar.policy.CameraToggleController; import javax.inject.Inject; -public class CameraToggleTile extends QSTileImpl { +public class CameraToggleTile extends QSTileImpl implements + CameraToggleController.Callback { private CameraToggleController mCameraToggleController; @@ -54,7 +56,15 @@ public class CameraToggleTile extends QSTileImpl { super(host, backgroundLooper, mainHandler, metricsLogger, statusBarStateController, activityStarter, qsLogger); mCameraToggleController = cameraToggleController; - mCameraToggleController.addCallback((b) -> refreshState()); + mCameraToggleController.observe(getLifecycle(), this); + } + + @Override + public boolean isAvailable() { + return /*getHost().getContext().getPackageManager().hasSystemFeature(FEATURE_CAMERA_TOGGLE) + && */whitelistIpcs(() -> DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, + "camera_toggle_enabled", + false)); } @Override @@ -64,21 +74,18 @@ public class CameraToggleTile extends QSTileImpl { @Override protected void handleClick() { - mCameraToggleController.setCameraEnabled(!mCameraToggleController.isCameraEnabled()); + mCameraToggleController.setCameraBlocked(!mCameraToggleController.isCameraBlocked()); } @Override protected void handleUpdateState(BooleanState state, Object arg) { - state.icon = new CameraToggleTileIcon(); - state.state = mCameraToggleController.isCameraEnabled() - ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE; - state.value = mCameraToggleController.isCameraEnabled(); - state.label = "Camera"; - if (!mCameraToggleController.isCameraAvailable()) { - state.secondaryLabel = "Currently in use"; - } else { - state.secondaryLabel = null; - } + 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(); @@ -96,14 +103,11 @@ public class CameraToggleTile extends QSTileImpl { @Override public CharSequence getTileLabel() { - return "Camera"; + return mContext.getString(R.string.quick_settings_camera_label); } - class CameraToggleTileIcon extends Icon { - - @Override - public Drawable getDrawable(Context context) { - return context.getDrawable(R.drawable.ic_camera); - } + @Override + public void onCameraBlockedChanged(boolean enable) { + refreshState(enable); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CameraToggleController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CameraToggleController.java index 544f0050d3526..b9de21c1fb827 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CameraToggleController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CameraToggleController.java @@ -16,18 +16,14 @@ package com.android.systemui.statusbar.policy; -import com.android.systemui.Dumpable; +public interface CameraToggleController extends + CallbackController { -public interface CameraToggleController extends CallbackController, - Dumpable { + boolean isCameraBlocked(); - boolean isCameraEnabled(); - void setCameraEnabled(boolean enabled); - - boolean isCameraAvailable(); + void setCameraBlocked(boolean blocked); interface Callback { - void onCameraEnabledChanged(boolean enable); + void onCameraBlockedChanged(boolean blocked); } - } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CameraToggleControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CameraToggleControllerImpl.java index 7496813247c9c..3fb1ac56226e9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CameraToggleControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CameraToggleControllerImpl.java @@ -16,86 +16,42 @@ package com.android.systemui.statusbar.policy; +import static android.service.SensorPrivacyIndividualEnabledSensorProto.CAMERA; + import android.content.Context; -import android.hardware.camera2.CameraManager; -import android.hardware.camera2.CameraToggleManager; -import android.os.Looper; +import android.hardware.SensorPrivacyManager; import android.util.ArraySet; -import android.widget.Toast; import androidx.annotation.NonNull; -import com.android.systemui.dagger.qualifiers.Background; -import com.android.systemui.dagger.qualifiers.Main; -import com.android.systemui.dump.DumpManager; - -import java.io.FileDescriptor; -import java.io.PrintWriter; import java.util.Set; import javax.inject.Inject; public class CameraToggleControllerImpl implements CameraToggleController { - private final Context mContext; - private final DumpManager mDumpManager; - private final CameraToggleManager mCameraToggleManager; + private final @NonNull Context mContext; + private final @NonNull SensorPrivacyManager mSensorPrivacyManager; + private boolean mState; + private Set mCallbacks = new ArraySet<>(); - private boolean mState = true; - - Set mCallbacks = new ArraySet<>(); - - Set mUsedCameras = new ArraySet<>(); - - /** - */ @Inject - public CameraToggleControllerImpl( - Context context, - DumpManager dumpManager, - @Background Looper bgLooper, - @Main Looper mainLooper) { + public CameraToggleControllerImpl(@NonNull Context context) { mContext = context; - mDumpManager = dumpManager; - mCameraToggleManager = context.getSystemService(CameraToggleManager.class); - mCameraToggleManager.addCameraToggleChangeListener(this::onCameraChanged); - mState = mCameraToggleManager.isCameraEnabled(); - mContext.getSystemService(CameraManager.class).registerAvailabilityCallback( - context.getMainExecutor(), new CameraManager.AvailabilityCallback() { - @Override - public void onCameraAvailable(@NonNull String cameraId) { - mUsedCameras.remove(cameraId); - onCameraChanged(mState); - } + mSensorPrivacyManager = context.getSystemService(SensorPrivacyManager.class); + mSensorPrivacyManager.addSensorPrivacyListener(CAMERA, this::onCameraPrivacyChanged); - @Override - public void onCameraUnavailable(@NonNull String cameraId) { - mUsedCameras.add(cameraId); - onCameraChanged(mState); - } - }); + mState = mSensorPrivacyManager.isIndividualSensorPrivacyEnabled(CAMERA); } @Override - public boolean isCameraEnabled() { + public boolean isCameraBlocked() { return mState; } @Override - public void setCameraEnabled(boolean enabled) { - if (!/*mCameraToggleManager.setCameraEnabled(enabled)*/true) { - Toast.makeText(mContext, "Can't disable camera while in use", Toast.LENGTH_LONG); - } - } - - @Override - public boolean isCameraAvailable() { - return false;/*mUsedCameras.isEmpty();*/ - } - - @Override - public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) { - + public void setCameraBlocked(boolean blocked) { + mSensorPrivacyManager.setIndividualSensorPrivacyForProfileGroup(CAMERA, blocked); } @Override @@ -108,10 +64,10 @@ public class CameraToggleControllerImpl implements CameraToggleController { mCallbacks.remove(listener); } - private void onCameraChanged(boolean state) { + private void onCameraPrivacyChanged(boolean state) { mState = state; for (Callback callback : mCallbacks) { - callback.onCameraEnabledChanged(state); + callback.onCameraBlockedChanged(mState); } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SensorPrivacyController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SensorPrivacyController.java index 6d5ce60ef621d..4a09234325caf 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SensorPrivacyController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SensorPrivacyController.java @@ -22,6 +22,11 @@ package com.android.systemui.statusbar.policy; public interface SensorPrivacyController extends CallbackController { + /** + * Initialize the controller. Needs to be called after constructing the object + */ + void init(); + /** * Returns whether sensor privacy is enabled. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SensorPrivacyControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SensorPrivacyControllerImpl.java index 20cc46ff6bbdd..a2334f3a23d6e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SensorPrivacyControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SensorPrivacyControllerImpl.java @@ -35,20 +35,21 @@ import javax.inject.Inject; public class SensorPrivacyControllerImpl implements SensorPrivacyController, SensorPrivacyManager.OnSensorPrivacyChangedListener { private SensorPrivacyManager mSensorPrivacyManager; - private final List mListeners; + private final List mListeners = new ArrayList<>(1); private Object mLock = new Object(); private boolean mSensorPrivacyEnabled; /** * Public constructor. */ - @Inject - public SensorPrivacyControllerImpl(Context context) { - mSensorPrivacyManager = (SensorPrivacyManager) context.getSystemService( - Context.SENSOR_PRIVACY_SERVICE); + public SensorPrivacyControllerImpl(@NonNull SensorPrivacyManager sensorPrivacyManager) { + mSensorPrivacyManager = sensorPrivacyManager; + } + + @Override + public void init() { mSensorPrivacyEnabled = mSensorPrivacyManager.isSensorPrivacyEnabled(); mSensorPrivacyManager.addSensorPrivacyListener(this); - mListeners = new ArrayList<>(1); } /** 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 916a1a8e3582d..a6278d689e0fe 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 @@ -107,11 +107,6 @@ public interface StatusBarPolicyModule { @Binds SecurityController provideSecurityController(SecurityControllerImpl controllerImpl); - /** */ - @Binds - SensorPrivacyController provideSensorPrivacyControllerImpl( - SensorPrivacyControllerImpl controllerImpl); - /** */ @Binds UserInfoController provideUserInfoContrller(UserInfoControllerImpl controllerImpl); diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java index 56a4c203e8402..a24f7dd64481b 100644 --- a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java @@ -20,6 +20,7 @@ import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME import static com.android.systemui.Dependency.LEAK_REPORT_EMAIL_NAME; import android.content.Context; +import android.hardware.SensorPrivacyManager; import android.os.Handler; import android.os.PowerManager; @@ -63,6 +64,8 @@ import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.DeviceProvisionedControllerImpl; import com.android.systemui.statusbar.policy.HeadsUpManager; +import com.android.systemui.statusbar.policy.SensorPrivacyController; +import com.android.systemui.statusbar.policy.SensorPrivacyControllerImpl; import com.android.systemui.statusbar.tv.notifications.TvNotificationHandler; import javax.inject.Named; @@ -109,6 +112,15 @@ public abstract class TvSystemUIModule { return bC; } + @Provides + @SysUISingleton + static SensorPrivacyController provideSensorPrivacyController( + SensorPrivacyManager sensorPrivacyManager) { + SensorPrivacyController spC = new SensorPrivacyControllerImpl(sensorPrivacyManager); + spC.init(); + return spC; + } + @Binds @SysUISingleton abstract QSFactory bindQSFactory(QSFactoryImpl qsFactoryImpl); diff --git a/services/core/java/com/android/server/SensorPrivacyService.java b/services/core/java/com/android/server/SensorPrivacyService.java index 9ba71dc5f4f7b..e99bb245a2e03 100644 --- a/services/core/java/com/android/server/SensorPrivacyService.java +++ b/services/core/java/com/android/server/SensorPrivacyService.java @@ -283,11 +283,16 @@ public final class SensorPrivacyService extends SystemService { mIndividualEnabled.put(userId, userIndividualEnabled); if (!enable) { - // Remove any notifications prompting the user to disable sensory privacy - NotificationManager notificationManager = - mContext.getSystemService(NotificationManager.class); + long token = Binder.clearCallingIdentity(); + try { + // Remove any notifications prompting the user to disable sensory privacy + NotificationManager notificationManager = + mContext.getSystemService(NotificationManager.class); - notificationManager.cancel(sensor); + notificationManager.cancel(sensor); + } finally { + Binder.restoreCallingIdentity(token); + } } persistSensorPrivacyState(); } From eca12d241cf098d9823150706fb55f3d4a1f14e2 Mon Sep 17 00:00:00 2001 From: "Philip P. Moltmann" Date: Tue, 5 Jan 2021 18:12:52 -0800 Subject: [PATCH 3/4] 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 From d60e0cc4e15fc975b1cc60eaaef26196da899d02 Mon Sep 17 00:00:00 2001 From: "Philip P. Moltmann" Date: Thu, 14 Jan 2021 13:45:24 -0800 Subject: [PATCH 4/4] Provide IndividualSensorPrivacyController instead of bind it Test: Toggled toggles Bug: 162549680 Change-Id: I7353c36577c1d78bb7111e16a235fce84d53f888 --- .../systemui/dagger/SystemUIDefaultModule.java | 12 ++++++++++++ .../policy/IndividualSensorPrivacyController.java | 1 + .../IndividualSensorPrivacyControllerImpl.java | 14 ++++++-------- .../policy/dagger/StatusBarPolicyModule.java | 7 ------- .../com/android/systemui/tv/TvSystemUIModule.java | 12 ++++++++++++ 5 files changed, 31 insertions(+), 15 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java index 754879e59dd99..239a77eb2f45f 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIDefaultModule.java @@ -63,6 +63,8 @@ import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.DeviceProvisionedControllerImpl; import com.android.systemui.statusbar.policy.HeadsUpManager; +import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController; +import com.android.systemui.statusbar.policy.IndividualSensorPrivacyControllerImpl; import com.android.systemui.statusbar.policy.SensorPrivacyController; import com.android.systemui.statusbar.policy.SensorPrivacyControllerImpl; @@ -128,6 +130,16 @@ public abstract class SystemUIDefaultModule { return spC; } + @Provides + @SysUISingleton + static IndividualSensorPrivacyController provideIndividualSensorPrivacyController( + SensorPrivacyManager sensorPrivacyManager) { + IndividualSensorPrivacyController spC = new IndividualSensorPrivacyControllerImpl( + sensorPrivacyManager); + spC.init(); + return spC; + } + @Binds @SysUISingleton public abstract QSFactory bindQSFactory(QSFactoryImpl qsFactoryImpl); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyController.java index 22e6b072f6a47..a76d08a438f25 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyController.java @@ -20,6 +20,7 @@ import android.hardware.SensorPrivacyManager.IndividualSensor; public interface IndividualSensorPrivacyController extends CallbackController { + void init(); boolean isSensorBlocked(@IndividualSensor int sensor); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java index a7eb5944200f9..231fe08e6a998 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java @@ -19,7 +19,6 @@ 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; @@ -29,22 +28,21 @@ import androidx.annotation.NonNull; import java.util.Set; -import javax.inject.Inject; - 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 final SparseBooleanArray mState = new SparseBooleanArray(); private final Set mCallbacks = new ArraySet<>(); - @Inject - public IndividualSensorPrivacyControllerImpl(@NonNull Context context) { - mContext = context; - mSensorPrivacyManager = context.getSystemService(SensorPrivacyManager.class); + public IndividualSensorPrivacyControllerImpl( + @NonNull SensorPrivacyManager sensorPrivacyManager) { + mSensorPrivacyManager = sensorPrivacyManager; + } + @Override + public void init() { for (int sensor : SENSORS) { mSensorPrivacyManager.addSensorPrivacyListener(sensor, (enabled) -> onSensorPrivacyChanged(sensor, enabled)); 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 366ae5491cd49..7a4b912d40713 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 @@ -32,8 +32,6 @@ 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; @@ -120,11 +118,6 @@ public interface StatusBarPolicyModule { NetworkController.AccessPointController provideAccessPointController( AccessPointControllerImpl accessPointControllerImpl); - /** */ - @Binds - IndividualSensorPrivacyController provideIndividualSensorPrivacyController( - IndividualSensorPrivacyControllerImpl controllerImpl); - /** */ @SysUISingleton @Provides diff --git a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java index a24f7dd64481b..df889f2c2ca61 100644 --- a/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/tv/TvSystemUIModule.java @@ -64,6 +64,8 @@ import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.DeviceProvisionedControllerImpl; import com.android.systemui.statusbar.policy.HeadsUpManager; +import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController; +import com.android.systemui.statusbar.policy.IndividualSensorPrivacyControllerImpl; import com.android.systemui.statusbar.policy.SensorPrivacyController; import com.android.systemui.statusbar.policy.SensorPrivacyControllerImpl; import com.android.systemui.statusbar.tv.notifications.TvNotificationHandler; @@ -121,6 +123,16 @@ public abstract class TvSystemUIModule { return spC; } + @Provides + @SysUISingleton + static IndividualSensorPrivacyController provideIndividualSensorPrivacyController( + SensorPrivacyManager sensorPrivacyManager) { + IndividualSensorPrivacyController spC = new IndividualSensorPrivacyControllerImpl( + sensorPrivacyManager); + spC.init(); + return spC; + } + @Binds @SysUISingleton abstract QSFactory bindQSFactory(QSFactoryImpl qsFactoryImpl);