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
diff --git a/res/values/strings.xml b/res/values/strings.xml
index a34f909..6395a61 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -13285,4 +13285,11 @@
 
     <!-- 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>
+
+    <!-- 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>
diff --git a/res/xml/privacy_dashboard_settings.xml b/res/xml/privacy_dashboard_settings.xml
index a1a608d..0cf5f77 100644
--- a/res/xml/privacy_dashboard_settings.xml
+++ b/res/xml/privacy_dashboard_settings.xml
@@ -48,26 +48,40 @@
         <intent android:action="android.intent.action.REVIEW_ACCESSIBILITY_SERVICES"/>
     </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 -->
     <Preference
         android:key="privacy_permissions_usage"
         android:title="@string/permissions_usage_title"
         android:summary="@string/permissions_usage_summary"
+        settings:allowDividerAbove="true"
         settings:searchable="false"
         settings:controller="com.android.settings.privacy.PrivacyHubPreferenceController">
         <intent android:action="android.intent.action.REVIEW_PERMISSION_USAGE"/>
     </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 -->
     <SwitchPreference
         android:key="show_password"
diff --git a/src/com/android/settings/privacy/CameraToggleController.java b/src/com/android/settings/privacy/CameraToggleController.java
new file mode 100644
index 0000000..a922994
--- /dev/null
+++ b/src/com/android/settings/privacy/CameraToggleController.java
@@ -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;
+    }
+}
diff --git a/src/com/android/settings/privacy/MicToggleController.java b/src/com/android/settings/privacy/MicToggleController.java
new file mode 100644
index 0000000..419cbdd
--- /dev/null
+++ b/src/com/android/settings/privacy/MicToggleController.java
@@ -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;
+    }
+
+}
diff --git a/src/com/android/settings/privacy/SensorToggleController.java b/src/com/android/settings/privacy/SensorToggleController.java
new file mode 100644
index 0000000..9e8aca2
--- /dev/null
+++ b/src/com/android/settings/privacy/SensorToggleController.java
@@ -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);
+    }
+}
diff --git a/src/com/android/settings/utils/SensorPrivacyManagerHelper.java b/src/com/android/settings/utils/SensorPrivacyManagerHelper.java
new file mode 100644
index 0000000..13a987d
--- /dev/null
+++ b/src/com/android/settings/utils/SensorPrivacyManagerHelper.java
@@ -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;
+    }
+}
diff --git a/tests/robotests/src/com/android/settings/privacy/SensorToggleControllerTest.java b/tests/robotests/src/com/android/settings/privacy/SensorToggleControllerTest.java
new file mode 100644
index 0000000..b38dbe8
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/privacy/SensorToggleControllerTest.java
@@ -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);
+    }
+}