Update the settings privacy page to the latest mocks
Add the mic and camera toggles to the page. Also create an helper class to keep binder calls to the system_server at a minimum. Test: make RunSettingsRoboTests Test: atest SettingsUnitTests Test: Manual Bug: 183985427 Change-Id: Iff6ee1c9a2c30095307f636decbcfcf298ed31b0
This commit is contained in:
@@ -13285,4 +13285,11 @@
|
|||||||
|
|
||||||
<!-- Summary for UWB preference when airplane mode is disabled. [CHAR_LIMIT=NONE]-->
|
<!-- Summary for UWB preference when airplane mode is disabled. [CHAR_LIMIT=NONE]-->
|
||||||
<string name="uwb_settings_summary_airplane_mode">Turn off airplane mode to use UWB </string>
|
<string name="uwb_settings_summary_airplane_mode">Turn off airplane mode to use UWB </string>
|
||||||
|
|
||||||
|
<!-- Label for the camera use toggle [CHAR LIMIT=40] -->
|
||||||
|
<string name="camera_toggle_title">Camera access</string>
|
||||||
|
<!-- Label for the camera use toggle [CHAR LIMIT=40] -->
|
||||||
|
<string name="mic_toggle_title">Microphone access</string>
|
||||||
|
<!-- Describes what is affected by the camera or mic toggle [CHAR LIMIT=NONE] -->
|
||||||
|
<string name="sensor_toggle_description">For all apps and services</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
@@ -48,26 +48,40 @@
|
|||||||
<intent android:action="android.intent.action.REVIEW_ACCESSIBILITY_SERVICES"/>
|
<intent android:action="android.intent.action.REVIEW_ACCESSIBILITY_SERVICES"/>
|
||||||
</Preference>
|
</Preference>
|
||||||
|
|
||||||
<!-- App permissions -->
|
|
||||||
<Preference
|
|
||||||
android:key="privacy_manage_perms"
|
|
||||||
android:title="@string/app_permissions"
|
|
||||||
android:summary="@string/runtime_permissions_summary_control_app_access"
|
|
||||||
settings:allowDividerAbove="true"
|
|
||||||
settings:searchable="false">
|
|
||||||
<intent android:action="android.intent.action.MANAGE_PERMISSIONS"/>
|
|
||||||
</Preference>
|
|
||||||
|
|
||||||
<!-- Permissions usage -->
|
<!-- Permissions usage -->
|
||||||
<Preference
|
<Preference
|
||||||
android:key="privacy_permissions_usage"
|
android:key="privacy_permissions_usage"
|
||||||
android:title="@string/permissions_usage_title"
|
android:title="@string/permissions_usage_title"
|
||||||
android:summary="@string/permissions_usage_summary"
|
android:summary="@string/permissions_usage_summary"
|
||||||
|
settings:allowDividerAbove="true"
|
||||||
settings:searchable="false"
|
settings:searchable="false"
|
||||||
settings:controller="com.android.settings.privacy.PrivacyHubPreferenceController">
|
settings:controller="com.android.settings.privacy.PrivacyHubPreferenceController">
|
||||||
<intent android:action="android.intent.action.REVIEW_PERMISSION_USAGE"/>
|
<intent android:action="android.intent.action.REVIEW_PERMISSION_USAGE"/>
|
||||||
</Preference>
|
</Preference>
|
||||||
|
|
||||||
|
<!-- App permissions -->
|
||||||
|
<Preference
|
||||||
|
android:key="privacy_manage_perms"
|
||||||
|
android:title="@string/app_permissions"
|
||||||
|
android:summary="@string/runtime_permissions_summary_control_app_access"
|
||||||
|
settings:searchable="false">
|
||||||
|
<intent android:action="android.intent.action.MANAGE_PERMISSIONS"/>
|
||||||
|
</Preference>
|
||||||
|
|
||||||
|
<!-- Camera toggle -->
|
||||||
|
<SwitchPreference
|
||||||
|
android:key="privacy_camera_toggle"
|
||||||
|
android:title="@string/camera_toggle_title"
|
||||||
|
android:summary="@string/sensor_toggle_description"
|
||||||
|
settings:controller="com.android.settings.privacy.CameraToggleController"/>
|
||||||
|
|
||||||
|
<!-- Microphone toggle -->
|
||||||
|
<SwitchPreference
|
||||||
|
android:key="privacy_mic_toggle"
|
||||||
|
android:title="@string/mic_toggle_title"
|
||||||
|
android:summary="@string/sensor_toggle_description"
|
||||||
|
settings:controller="com.android.settings.privacy.MicToggleController"/>
|
||||||
|
|
||||||
<!-- Show passwords -->
|
<!-- Show passwords -->
|
||||||
<SwitchPreference
|
<SwitchPreference
|
||||||
android:key="show_password"
|
android:key="show_password"
|
||||||
|
43
src/com/android/settings/privacy/CameraToggleController.java
Normal file
43
src/com/android/settings/privacy/CameraToggleController.java
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 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.settings.privacy;
|
||||||
|
|
||||||
|
import static com.android.settings.utils.SensorPrivacyManagerHelper.CAMERA;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.provider.DeviceConfig;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controller for microphone toggle
|
||||||
|
*/
|
||||||
|
public class CameraToggleController extends SensorToggleController {
|
||||||
|
public CameraToggleController(Context context, String preferenceKey) {
|
||||||
|
super(context, preferenceKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getSensor() {
|
||||||
|
return CAMERA;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getAvailabilityStatus() {
|
||||||
|
return mSensorPrivacyManagerHelper.supportsSensorToggle(getSensor())
|
||||||
|
&& DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, "camera_toggle_enabled",
|
||||||
|
true) ? AVAILABLE_UNSEARCHABLE : UNSUPPORTED_ON_DEVICE;
|
||||||
|
}
|
||||||
|
}
|
44
src/com/android/settings/privacy/MicToggleController.java
Normal file
44
src/com/android/settings/privacy/MicToggleController.java
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 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.settings.privacy;
|
||||||
|
|
||||||
|
import static com.android.settings.utils.SensorPrivacyManagerHelper.MICROPHONE;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.provider.DeviceConfig;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controller for camera toggle
|
||||||
|
*/
|
||||||
|
public class MicToggleController extends SensorToggleController {
|
||||||
|
public MicToggleController(Context context, String preferenceKey) {
|
||||||
|
super(context, preferenceKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getSensor() {
|
||||||
|
return MICROPHONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getAvailabilityStatus() {
|
||||||
|
return mSensorPrivacyManagerHelper.supportsSensorToggle(getSensor())
|
||||||
|
&& DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, "mic_toggle_enabled",
|
||||||
|
true) ? AVAILABLE_UNSEARCHABLE : UNSUPPORTED_ON_DEVICE;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
66
src/com/android/settings/privacy/SensorToggleController.java
Normal file
66
src/com/android/settings/privacy/SensorToggleController.java
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 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.settings.privacy;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
|
||||||
|
import androidx.preference.PreferenceScreen;
|
||||||
|
|
||||||
|
import com.android.settings.core.TogglePreferenceController;
|
||||||
|
import com.android.settings.utils.SensorPrivacyManagerHelper;
|
||||||
|
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class for sensor toggle controllers
|
||||||
|
*/
|
||||||
|
public abstract class SensorToggleController extends TogglePreferenceController {
|
||||||
|
|
||||||
|
protected final SensorPrivacyManagerHelper mSensorPrivacyManagerHelper;
|
||||||
|
private final Executor mCallbackExecutor;
|
||||||
|
|
||||||
|
public SensorToggleController(Context context, String preferenceKey) {
|
||||||
|
super(context, preferenceKey);
|
||||||
|
mSensorPrivacyManagerHelper = SensorPrivacyManagerHelper.getInstance(context);
|
||||||
|
mCallbackExecutor = context.getMainExecutor();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The sensor id, defined in SensorPrivacyManagerHelper, which an implementing class controls
|
||||||
|
*/
|
||||||
|
public abstract int getSensor();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isChecked() {
|
||||||
|
return !mSensorPrivacyManagerHelper.isSensorBlocked(getSensor());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean setChecked(boolean isChecked) {
|
||||||
|
mSensorPrivacyManagerHelper.setSensorBlocked(getSensor(), !isChecked);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void displayPreference(PreferenceScreen screen) {
|
||||||
|
super.displayPreference(screen);
|
||||||
|
mSensorPrivacyManagerHelper.addSensorBlockedListener(
|
||||||
|
getSensor(),
|
||||||
|
(sensor, blocked) -> updateState(screen.findPreference(mPreferenceKey)),
|
||||||
|
mCallbackExecutor);
|
||||||
|
}
|
||||||
|
}
|
270
src/com/android/settings/utils/SensorPrivacyManagerHelper.java
Normal file
270
src/com/android/settings/utils/SensorPrivacyManagerHelper.java
Normal file
@@ -0,0 +1,270 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2021 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.settings.utils;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.hardware.SensorPrivacyManager;
|
||||||
|
import android.hardware.SensorPrivacyManager.OnSensorPrivacyChangedListener;
|
||||||
|
import android.util.ArraySet;
|
||||||
|
import android.util.SparseArray;
|
||||||
|
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A class to help with calls to the sensor privacy manager. This class caches state when needed and
|
||||||
|
* multiplexes multiple listeners to a minimal set of binder calls.
|
||||||
|
*/
|
||||||
|
public class SensorPrivacyManagerHelper {
|
||||||
|
|
||||||
|
public static final int MICROPHONE = SensorPrivacyManager.Sensors.MICROPHONE;
|
||||||
|
public static final int CAMERA = SensorPrivacyManager.Sensors.CAMERA;
|
||||||
|
|
||||||
|
private static SensorPrivacyManagerHelper sInstance;
|
||||||
|
|
||||||
|
private final SensorPrivacyManager mSensorPrivacyManager;
|
||||||
|
|
||||||
|
private final SparseArray<Boolean> mCurrentUserCachedState = new SparseArray<>();
|
||||||
|
private final SparseArray<SparseArray<Boolean>> mCachedState = new SparseArray<>();
|
||||||
|
|
||||||
|
private final SparseArray<OnSensorPrivacyChangedListener>
|
||||||
|
mCurrentUserServiceListeners = new SparseArray<>();
|
||||||
|
private final SparseArray<SparseArray<OnSensorPrivacyChangedListener>>
|
||||||
|
mServiceListeners = new SparseArray<>();
|
||||||
|
|
||||||
|
private final ArraySet<CallbackInfo> mCallbacks = new ArraySet<>();
|
||||||
|
|
||||||
|
private final Object mLock = new Object();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback for when the state of the sensor privacy changes.
|
||||||
|
*/
|
||||||
|
public interface Callback {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method invoked when the sensor privacy changes.
|
||||||
|
* @param sensor The sensor which changed
|
||||||
|
* @param blocked If the sensor is blocked
|
||||||
|
*/
|
||||||
|
void onSensorPrivacyChanged(int sensor, boolean blocked);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class CallbackInfo {
|
||||||
|
static final int CURRENT_USER = -1;
|
||||||
|
|
||||||
|
Callback mCallback;
|
||||||
|
Executor mExecutor;
|
||||||
|
int mSensor;
|
||||||
|
int mUserId;
|
||||||
|
|
||||||
|
CallbackInfo(Callback callback, Executor executor, int sensor, int userId) {
|
||||||
|
mCallback = callback;
|
||||||
|
mExecutor = executor;
|
||||||
|
mSensor = sensor;
|
||||||
|
mUserId = userId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the singleton instance
|
||||||
|
* @param context The context which is needed if the instance hasn't been created
|
||||||
|
* @return the instance
|
||||||
|
*/
|
||||||
|
public static SensorPrivacyManagerHelper getInstance(Context context) {
|
||||||
|
if (sInstance == null) {
|
||||||
|
sInstance = new SensorPrivacyManagerHelper(context);
|
||||||
|
}
|
||||||
|
return sInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Only to be used in tests
|
||||||
|
*/
|
||||||
|
private static void clearInstance() {
|
||||||
|
sInstance = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private SensorPrivacyManagerHelper(Context context) {
|
||||||
|
mSensorPrivacyManager = context.getSystemService(SensorPrivacyManager.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the given toggle is supported on this device
|
||||||
|
* @param sensor The sensor to check
|
||||||
|
* @return whether the toggle for the sensor is supported on this device.
|
||||||
|
*/
|
||||||
|
public boolean supportsSensorToggle(int sensor) {
|
||||||
|
return mSensorPrivacyManager.supportsSensorToggle(sensor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the sensor is blocked for the current user. If the user switches and the state of
|
||||||
|
* the new user is different, this value will change.
|
||||||
|
* @param sensor the sensor to check
|
||||||
|
* @return true if the sensor is blocked for the current user
|
||||||
|
*/
|
||||||
|
public boolean isSensorBlocked(int sensor) {
|
||||||
|
synchronized (mLock) {
|
||||||
|
Boolean blocked = mCurrentUserCachedState.get(sensor);
|
||||||
|
if (blocked == null) {
|
||||||
|
registerCurrentUserListenerIfNeeded(sensor);
|
||||||
|
|
||||||
|
blocked = mSensorPrivacyManager.isSensorPrivacyEnabled(sensor);
|
||||||
|
mCurrentUserCachedState.put(sensor, blocked);
|
||||||
|
}
|
||||||
|
|
||||||
|
return blocked;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the sensor is or would be blocked if the given user is the foreground user
|
||||||
|
* @param sensor the sensor to check
|
||||||
|
* @param userId the user to check
|
||||||
|
* @return true if the sensor is or would be blocked if the given user is the foreground user
|
||||||
|
*/
|
||||||
|
public boolean isSensorBlocked(int sensor, int userId) {
|
||||||
|
synchronized (mLock) {
|
||||||
|
SparseArray<Boolean> userCachedState = createUserCachedStateIfNeededLocked(userId);
|
||||||
|
Boolean blocked = userCachedState.get(sensor);
|
||||||
|
if (blocked == null) {
|
||||||
|
registerListenerIfNeeded(sensor, userId);
|
||||||
|
|
||||||
|
blocked = mSensorPrivacyManager.isSensorPrivacyEnabled(sensor);
|
||||||
|
userCachedState.put(sensor, blocked);
|
||||||
|
}
|
||||||
|
|
||||||
|
return blocked;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the sensor privacy for the current user.
|
||||||
|
* @param sensor The sensor to set for
|
||||||
|
* @param blocked The state to set to
|
||||||
|
*/
|
||||||
|
public void setSensorBlocked(int sensor, boolean blocked) {
|
||||||
|
mSensorPrivacyManager.setSensorPrivacy(sensor, blocked);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the sensor privacy for the given user.
|
||||||
|
* @param sensor The sensor to set for
|
||||||
|
* @param blocked The state to set to
|
||||||
|
* @param userId The user to set for
|
||||||
|
*/
|
||||||
|
public void setSensorBlocked(int sensor, boolean blocked, int userId) {
|
||||||
|
mSensorPrivacyManager.setSensorPrivacy(sensor, blocked, userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a listener for the state of the current user. If the current user changes and the state
|
||||||
|
* of the new user is different, a callback will be received.
|
||||||
|
* @param sensor The sensor to watch
|
||||||
|
* @param callback The callback to invoke
|
||||||
|
* @param executor The executor to invoke on
|
||||||
|
*/
|
||||||
|
public void addSensorBlockedListener(int sensor, Callback callback, Executor executor) {
|
||||||
|
synchronized (mLock) {
|
||||||
|
mCallbacks.add(new CallbackInfo(callback, executor, sensor, CallbackInfo.CURRENT_USER));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a listener for the state of the given user
|
||||||
|
* @param sensor The sensor to watch
|
||||||
|
* @param callback The callback to invoke
|
||||||
|
* @param executor The executor to invoke on
|
||||||
|
*/
|
||||||
|
public void addSensorBlockedListener(int sensor, int userId, Callback callback,
|
||||||
|
Executor executor) {
|
||||||
|
synchronized (mLock) {
|
||||||
|
mCallbacks.add(new CallbackInfo(callback, executor, sensor, userId));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a callback
|
||||||
|
* @param callback The callback to remove
|
||||||
|
*/
|
||||||
|
public void removeBlockedListener(Callback callback) {
|
||||||
|
synchronized (mLock) {
|
||||||
|
mCallbacks.removeIf(callbackInfo -> callbackInfo.mCallback == callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void registerCurrentUserListenerIfNeeded(int sensor) {
|
||||||
|
synchronized (mLock) {
|
||||||
|
if (!mCurrentUserServiceListeners.contains(sensor)) {
|
||||||
|
OnSensorPrivacyChangedListener listener = (s, enabled) -> {
|
||||||
|
mCurrentUserCachedState.put(sensor, enabled);
|
||||||
|
dispatchStateChangedLocked(sensor, enabled, CallbackInfo.CURRENT_USER);
|
||||||
|
};
|
||||||
|
mCurrentUserServiceListeners.put(sensor, listener);
|
||||||
|
mSensorPrivacyManager.addSensorPrivacyListener(sensor, listener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void registerListenerIfNeeded(int sensor, int userId) {
|
||||||
|
synchronized (mLock) {
|
||||||
|
SparseArray<OnSensorPrivacyChangedListener>
|
||||||
|
userServiceListeners = createUserServiceListenersIfNeededLocked(userId);
|
||||||
|
|
||||||
|
if (!userServiceListeners.contains(sensor)) {
|
||||||
|
OnSensorPrivacyChangedListener listener = (s, enabled) -> {
|
||||||
|
SparseArray<Boolean> userCachedState =
|
||||||
|
createUserCachedStateIfNeededLocked(userId);
|
||||||
|
userCachedState.put(sensor, enabled);
|
||||||
|
dispatchStateChangedLocked(sensor, enabled, userId);
|
||||||
|
};
|
||||||
|
mCurrentUserServiceListeners.put(sensor, listener);
|
||||||
|
mSensorPrivacyManager.addSensorPrivacyListener(sensor, listener);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void dispatchStateChangedLocked(int sensor, boolean blocked, int userId) {
|
||||||
|
for (CallbackInfo callbackInfo : mCallbacks) {
|
||||||
|
if (callbackInfo.mUserId == userId && callbackInfo.mSensor == sensor) {
|
||||||
|
Callback callback = callbackInfo.mCallback;
|
||||||
|
Executor executor = callbackInfo.mExecutor;
|
||||||
|
|
||||||
|
executor.execute(() -> callback.onSensorPrivacyChanged(sensor, blocked));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private SparseArray<Boolean> createUserCachedStateIfNeededLocked(int userId) {
|
||||||
|
SparseArray<Boolean> userCachedState = mCachedState.get(userId);
|
||||||
|
if (userCachedState == null) {
|
||||||
|
userCachedState = new SparseArray<>();
|
||||||
|
mCachedState.put(userId, userCachedState);
|
||||||
|
}
|
||||||
|
return userCachedState;
|
||||||
|
}
|
||||||
|
|
||||||
|
private SparseArray<OnSensorPrivacyChangedListener> createUserServiceListenersIfNeededLocked(
|
||||||
|
int userId) {
|
||||||
|
SparseArray<OnSensorPrivacyChangedListener> userServiceListeners =
|
||||||
|
mServiceListeners.get(userId);
|
||||||
|
if (userServiceListeners == null) {
|
||||||
|
userServiceListeners = new SparseArray<>();
|
||||||
|
mServiceListeners.put(userId, userServiceListeners);
|
||||||
|
}
|
||||||
|
return userServiceListeners;
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,206 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2019 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.settings.privacy;
|
||||||
|
|
||||||
|
import static android.hardware.SensorPrivacyManager.Sensors.CAMERA;
|
||||||
|
import static android.hardware.SensorPrivacyManager.Sensors.MICROPHONE;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||||
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
|
import static org.mockito.Mockito.doAnswer;
|
||||||
|
import static org.mockito.Mockito.doReturn;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.hardware.SensorPrivacyManager;
|
||||||
|
import android.hardware.SensorPrivacyManager.OnSensorPrivacyChangedListener;
|
||||||
|
import android.util.ArraySet;
|
||||||
|
|
||||||
|
import com.android.settings.utils.SensorPrivacyManagerHelper;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.Mockito;
|
||||||
|
import org.mockito.MockitoAnnotations;
|
||||||
|
import org.robolectric.RobolectricTestRunner;
|
||||||
|
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
@RunWith(RobolectricTestRunner.class)
|
||||||
|
public class SensorToggleControllerTest {
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private Context mContext;
|
||||||
|
@Mock
|
||||||
|
private SensorPrivacyManager mSensorPrivacyManager;
|
||||||
|
private boolean mMicState;
|
||||||
|
private boolean mCamState;
|
||||||
|
private Set<OnSensorPrivacyChangedListener> mMicListeners;
|
||||||
|
private Set<OnSensorPrivacyChangedListener> mCamListeners;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
MockitoAnnotations.initMocks(this);
|
||||||
|
mContext = Mockito.mock(Context.class);
|
||||||
|
mSensorPrivacyManager = Mockito.mock(SensorPrivacyManager.class);
|
||||||
|
|
||||||
|
try {
|
||||||
|
Method clearInstance =
|
||||||
|
SensorPrivacyManagerHelper.class.getDeclaredMethod("clearInstance");
|
||||||
|
clearInstance.setAccessible(true);
|
||||||
|
clearInstance.invoke(null);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
mMicState = false;
|
||||||
|
mCamState = false;
|
||||||
|
mMicListeners = new ArraySet<>();
|
||||||
|
mCamListeners = new ArraySet<>();
|
||||||
|
|
||||||
|
doReturn(0).when(mContext).getUserId();
|
||||||
|
doReturn(mSensorPrivacyManager).when(mContext)
|
||||||
|
.getSystemService(SensorPrivacyManager.class);
|
||||||
|
|
||||||
|
doAnswer(invocation -> mMicState)
|
||||||
|
.when(mSensorPrivacyManager).isSensorPrivacyEnabled(eq(MICROPHONE));
|
||||||
|
doAnswer(invocation -> mCamState)
|
||||||
|
.when(mSensorPrivacyManager).isSensorPrivacyEnabled(eq(CAMERA));
|
||||||
|
|
||||||
|
doAnswer(invocation -> {
|
||||||
|
mMicState = invocation.getArgument(1);
|
||||||
|
for (OnSensorPrivacyChangedListener listener : mMicListeners) {
|
||||||
|
listener.onSensorPrivacyChanged(MICROPHONE, mMicState);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}).when(mSensorPrivacyManager).setSensorPrivacy(eq(MICROPHONE), anyBoolean());
|
||||||
|
doAnswer(invocation -> {
|
||||||
|
mCamState = invocation.getArgument(1);
|
||||||
|
for (OnSensorPrivacyChangedListener listener : mMicListeners) {
|
||||||
|
listener.onSensorPrivacyChanged(CAMERA, mMicState);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}).when(mSensorPrivacyManager).setSensorPrivacy(eq(CAMERA), anyBoolean());
|
||||||
|
|
||||||
|
doAnswer(invocation -> mMicListeners.add(invocation.getArgument(1)))
|
||||||
|
.when(mSensorPrivacyManager).addSensorPrivacyListener(eq(MICROPHONE), any());
|
||||||
|
doAnswer(invocation -> mCamListeners.add(invocation.getArgument(1)))
|
||||||
|
.when(mSensorPrivacyManager).addSensorPrivacyListener(eq(CAMERA), any());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void isChecked_disableMicrophoneSensorPrivacy_returnTrue() {
|
||||||
|
mSensorPrivacyManager.setSensorPrivacy(MICROPHONE, false);
|
||||||
|
MicToggleController micToggleController = new MicToggleController(mContext, "mic_toggle");
|
||||||
|
assertTrue(micToggleController.isChecked());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void isChecked_enableMicrophoneSensorPrivacy_returnFalse() {
|
||||||
|
mSensorPrivacyManager.setSensorPrivacy(MICROPHONE, true);
|
||||||
|
MicToggleController micToggleController = new MicToggleController(mContext, "mic_toggle");
|
||||||
|
assertFalse(micToggleController.isChecked());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void isChecked_disableMicrophoneSensorPrivacyThenChanged_returnFalse() {
|
||||||
|
mSensorPrivacyManager.setSensorPrivacy(MICROPHONE, false);
|
||||||
|
MicToggleController micToggleController = new MicToggleController(mContext, "mic_toggle");
|
||||||
|
mSensorPrivacyManager.setSensorPrivacy(MICROPHONE, true);
|
||||||
|
assertFalse(micToggleController.isChecked());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void isChecked_enableMicrophoneSensorPrivacyThenChanged_returnTrue() {
|
||||||
|
mSensorPrivacyManager.setSensorPrivacy(MICROPHONE, true);
|
||||||
|
MicToggleController micToggleController = new MicToggleController(mContext, "mic_toggle");
|
||||||
|
mSensorPrivacyManager.setSensorPrivacy(MICROPHONE, false);
|
||||||
|
assertTrue(micToggleController.isChecked());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void isMicrophoneSensorPrivacyEnabled_uncheckMicToggle_returnTrue() {
|
||||||
|
mSensorPrivacyManager.setSensorPrivacy(MICROPHONE, false);
|
||||||
|
MicToggleController micToggleController = new MicToggleController(mContext, "mic_toggle");
|
||||||
|
micToggleController.setChecked(false);
|
||||||
|
assertTrue(mMicState);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void isMicrophoneSensorPrivacyEnabled_checkMicToggle_returnFalse() {
|
||||||
|
mSensorPrivacyManager.setSensorPrivacy(MICROPHONE, true);
|
||||||
|
MicToggleController micToggleController = new MicToggleController(mContext, "mic_toggle");
|
||||||
|
micToggleController.setChecked(true);
|
||||||
|
assertFalse(mMicState);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void isChecked_disableCameraSensorPrivacy_returnTrue() {
|
||||||
|
mSensorPrivacyManager.setSensorPrivacy(CAMERA, false);
|
||||||
|
CameraToggleController camToggleController =
|
||||||
|
new CameraToggleController(mContext, "cam_toggle");
|
||||||
|
assertTrue(camToggleController.isChecked());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void isChecked_enableCameraSensorPrivacy_returnFalse() {
|
||||||
|
mSensorPrivacyManager.setSensorPrivacy(CAMERA, true);
|
||||||
|
CameraToggleController camToggleController =
|
||||||
|
new CameraToggleController(mContext, "cam_toggle");
|
||||||
|
assertFalse(camToggleController.isChecked());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void isChecked_disableCameraSensorPrivacyThenChanged_returnFalse() {
|
||||||
|
mSensorPrivacyManager.setSensorPrivacy(CAMERA, false);
|
||||||
|
CameraToggleController camToggleController =
|
||||||
|
new CameraToggleController(mContext, "cam_toggle");
|
||||||
|
mSensorPrivacyManager.setSensorPrivacy(CAMERA, true);
|
||||||
|
assertFalse(camToggleController.isChecked());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void isChecked_enableCameraSensorPrivacyThenChanged_returnTrue() {
|
||||||
|
mSensorPrivacyManager.setSensorPrivacy(CAMERA, true);
|
||||||
|
CameraToggleController camToggleController =
|
||||||
|
new CameraToggleController(mContext, "cam_toggle");
|
||||||
|
mSensorPrivacyManager.setSensorPrivacy(CAMERA, false);
|
||||||
|
assertTrue(camToggleController.isChecked());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void isCameraSensorPrivacyEnabled_uncheckMicToggle_returnTrue() {
|
||||||
|
mSensorPrivacyManager.setSensorPrivacy(CAMERA, false);
|
||||||
|
CameraToggleController camToggleController =
|
||||||
|
new CameraToggleController(mContext, "cam_toggle");
|
||||||
|
camToggleController.setChecked(false);
|
||||||
|
assertTrue(mCamState);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void isCameraSensorPrivacyEnabled_checkMicToggle_returnFalse() {
|
||||||
|
mSensorPrivacyManager.setSensorPrivacy(CAMERA, true);
|
||||||
|
CameraToggleController camToggleController =
|
||||||
|
new CameraToggleController(mContext, "cam_toggle");
|
||||||
|
camToggleController.setChecked(true);
|
||||||
|
assertFalse(mCamState);
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user