Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1b025f37 authored by Evan Severson's avatar Evan Severson Committed by Android (Google) Code Review
Browse files

Merge "Use ambient light sensor to adjust camera privacy light brightness" into tm-dev

parents b3ff8dcf 66149737
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -448,5 +448,6 @@
    <color name="accessibility_color_inversion_background">#546E7A</color>

    <!-- Color of camera light when camera is in use -->
    <color name="camera_privacy_light">#FFFFFF</color>
    <color name="camera_privacy_light_day">#FFFFFF</color>
    <color name="camera_privacy_light_night">#FFFFFF</color>
</resources>
+5 −0
Original line number Diff line number Diff line
@@ -5790,4 +5790,9 @@

    <!-- List of the labels of requestable device state config values -->
    <string-array name="config_deviceStatesAvailableForAppRequests"/>

    <!-- Interval in milliseconds to average light sensor values for camera light brightness -->
    <integer name="config_cameraPrivacyLightAlsAveragingIntervalMillis">3000</integer>
    <!-- Light sensor's lux value to use as the threshold between using day or night brightness -->
    <integer name="config_cameraPrivacyLightAlsNightThreshold">4</integer>
</resources>
+4 −1
Original line number Diff line number Diff line
@@ -4754,7 +4754,10 @@
  <!-- For VirtualDeviceManager -->
  <java-symbol type="string" name="vdm_camera_access_denied" />

  <java-symbol type="color" name="camera_privacy_light"/>
  <java-symbol type="color" name="camera_privacy_light_day"/>
  <java-symbol type="color" name="camera_privacy_light_night"/>
  <java-symbol type="integer" name="config_cameraPrivacyLightAlsAveragingIntervalMillis"/>
  <java-symbol type="integer" name="config_cameraPrivacyLightAlsNightThreshold"/>

  <java-symbol type="bool" name="config_bg_current_drain_monitor_enabled" />
  <java-symbol type="array" name="config_bg_current_drain_threshold_to_restricted_bucket" />
+177 −9
Original line number Diff line number Diff line
@@ -16,44 +16,105 @@

package com.android.server.sensorprivacy;

import static android.hardware.SensorManager.SENSOR_DELAY_NORMAL;

import android.annotation.ColorInt;
import android.app.AppOpsManager;
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.hardware.lights.Light;
import android.hardware.lights.LightState;
import android.hardware.lights.LightsManager;
import android.hardware.lights.LightsRequest;
import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.Looper;
import android.os.SystemClock;
import android.permission.PermissionManager;
import android.util.ArraySet;
import android.util.Pair;

import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.FgThread;

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Executor;

class CameraPrivacyLightController implements AppOpsManager.OnOpActiveChangedListener,
        SensorEventListener {

class CameraPrivacyLightController implements AppOpsManager.OnOpActiveChangedListener {
    @VisibleForTesting
    static final double LIGHT_VALUE_MULTIPLIER = 1 / Math.log(1.1);

    private final Handler mHandler;
    private final Executor mExecutor;
    private final Context mContext;
    private final AppOpsManager mAppOpsManager;
    private final LightsManager mLightsManager;
    private final SensorManager mSensorManager;

    private final Set<String> mActivePackages = new ArraySet<>();
    private final Set<String> mActivePhonePackages = new ArraySet<>();

    private final int mCameraPrivacyLightColor;

    private final List<Light> mCameraLights = new ArrayList<>();
    private final AppOpsManager mAppOpsManager;

    private LightsManager.LightsSession mLightsSession = null;

    @ColorInt
    private final int mDayColor;
    @ColorInt
    private final int mNightColor;

    private final Sensor mLightSensor;

    private boolean mIsAmbientLightListenerRegistered = false;
    private final long mMovingAverageIntervalMillis;
    /** When average of the time integral over the past {@link #mMovingAverageIntervalMillis}
     *  milliseconds of the log_1.1(lux(t)) is greater than this value, use the daytime brightness
     *  else use nighttime brightness. */
    private final long mNightThreshold;
    private final ArrayDeque<Pair<Long, Integer>> mAmbientLightValues = new ArrayDeque<>();
    /** Tracks the Riemann sum of {@link #mAmbientLightValues} to avoid O(n) operations when sum is
     *  needed */
    private long mAlvSum = 0;
    private int mLastLightColor = 0;
    /** The elapsed real time that the ALS was started watching */
    private long mElapsedTimeStartedReading;

    private final Object mDelayedUpdateToken = new Object();

    // Can't mock static native methods, workaround for testing
    private long mElapsedRealTime = -1;

    CameraPrivacyLightController(Context context) {
        this(context, FgThread.get().getLooper());
    }

    @VisibleForTesting
    CameraPrivacyLightController(Context context, Looper looper) {
        mContext = context;

        mHandler = new Handler(looper);
        mExecutor = new HandlerExecutor(mHandler);

        mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
        mLightsManager = mContext.getSystemService(LightsManager.class);
        mSensorManager = mContext.getSystemService(SensorManager.class);

        mCameraPrivacyLightColor = mContext.getColor(R.color.camera_privacy_light);
        mDayColor = mContext.getColor(R.color.camera_privacy_light_day);
        mNightColor = mContext.getColor(R.color.camera_privacy_light_night);
        mMovingAverageIntervalMillis = mContext.getResources()
                .getInteger(R.integer.config_cameraPrivacyLightAlsAveragingIntervalMillis);
        mNightThreshold = (long) (Math.log(mContext.getResources()
                .getInteger(R.integer.config_cameraPrivacyLightAlsNightThreshold))
                * LIGHT_VALUE_MULTIPLIER);

        List<Light> lights = mLightsManager.getLights();
        for (int i = 0; i < lights.size(); i++) {
@@ -64,12 +125,60 @@ class CameraPrivacyLightController implements AppOpsManager.OnOpActiveChangedLis
        }

        if (mCameraLights.isEmpty()) {
            mLightSensor = null;
            return;
        }

        mAppOpsManager.startWatchingActive(
                new String[] {AppOpsManager.OPSTR_CAMERA, AppOpsManager.OPSTR_PHONE_CALL_CAMERA},
                FgThread.getExecutor(), this);
                mExecutor, this);

        // It may be useful in the future to configure devices to know which lights are near which
        // sensors so that we can control individual lights based on their environment.
        mLightSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
    }

    private void addElement(long time, int value) {
        if (mAmbientLightValues.isEmpty()) {
            // Eliminate the size == 1 edge case and assume the light value has been constant for
            // the previous interval
            mAmbientLightValues.add(new Pair<>(time - getCurrentIntervalMillis() - 1, value));
        }
        Pair<Long, Integer> lastElement = mAmbientLightValues.peekLast();
        mAmbientLightValues.add(new Pair<>(time, value));

        mAlvSum += (time - lastElement.first) * lastElement.second;
        removeObsoleteData(time);
    }

    private void removeObsoleteData(long time) {
        while (mAmbientLightValues.size() > 1) {
            Pair<Long, Integer> element0 = mAmbientLightValues.pollFirst(); // NOTICE: POLL
            Pair<Long, Integer> element1 = mAmbientLightValues.peekFirst(); // NOTICE: PEEK
            if (element1.first > time - getCurrentIntervalMillis()) {
                mAmbientLightValues.addFirst(element0);
                break;
            }
            mAlvSum -= (element1.first - element0.first) * element0.second;
        }
    }

    /**
     * Gives the Riemann sum of {@link #mAmbientLightValues} where the part of the interval that
     * stretches outside the time window is removed and the time since the last change is added in.
     */
    private long getLiveAmbientLightTotal() {
        if (mAmbientLightValues.isEmpty()) {
            return mAlvSum;
        }
        long time = getElapsedRealTime();
        removeObsoleteData(time);

        Pair<Long, Integer> firstElement = mAmbientLightValues.peekFirst();
        Pair<Long, Integer> lastElement = mAmbientLightValues.peekLast();

        return mAlvSum - Math.max(0, time - getCurrentIntervalMillis() - firstElement.first)
                * firstElement.second + (time - lastElement.first) * lastElement.second;
    }

    @Override
@@ -93,10 +202,16 @@ class CameraPrivacyLightController implements AppOpsManager.OnOpActiveChangedLis
    }

    private void updateLightSession() {
        if (Looper.myLooper() != mHandler.getLooper()) {
            mHandler.post(this::updateLightSession);
            return;
        }

        Set<String> exemptedPackages = PermissionManager.getIndicatorExemptedPackages(mContext);

        boolean shouldSessionEnd = exemptedPackages.containsAll(mActivePackages)
                && exemptedPackages.containsAll(mActivePhonePackages);
        updateSensorListener(shouldSessionEnd);

        if (shouldSessionEnd) {
            if (mLightsSession == null) {
@@ -106,20 +221,73 @@ class CameraPrivacyLightController implements AppOpsManager.OnOpActiveChangedLis
            mLightsSession.close();
            mLightsSession = null;
        } else {
            if (mLightsSession != null) {
            int lightColor;
            if (mLightSensor != null && getLiveAmbientLightTotal()
                    < getCurrentIntervalMillis() * mNightThreshold) {
                lightColor = mNightColor;
            } else {
                lightColor = mDayColor;
            }

            if (mLastLightColor == lightColor && mLightsSession != null) {
                return;
            }
            mLastLightColor = lightColor;

            LightsRequest.Builder requestBuilder = new LightsRequest.Builder();
            for (int i = 0; i < mCameraLights.size(); i++) {
                requestBuilder.addLight(mCameraLights.get(i),
                        new LightState.Builder()
                                .setColor(mCameraPrivacyLightColor)
                                .setColor(lightColor)
                                .build());
            }

            if (mLightsSession == null) {
                mLightsSession = mLightsManager.openSession(Integer.MAX_VALUE);
            }

            mLightsSession.requestLights(requestBuilder.build());
        }
    }

    private void updateSensorListener(boolean shouldSessionEnd) {
        if (shouldSessionEnd && mIsAmbientLightListenerRegistered) {
            mSensorManager.unregisterListener(this);
            mIsAmbientLightListenerRegistered = false;
        }
        if (!shouldSessionEnd && !mIsAmbientLightListenerRegistered && mLightSensor != null) {
            mSensorManager.registerListener(this, mLightSensor, SENSOR_DELAY_NORMAL, mHandler);
            mIsAmbientLightListenerRegistered = true;
            mElapsedTimeStartedReading = getElapsedRealTime();
        }
    }

    private long getElapsedRealTime() {
        return mElapsedRealTime == -1 ? SystemClock.elapsedRealtime() : mElapsedRealTime;
    }

    @VisibleForTesting
    void setElapsedRealTime(long time) {
        mElapsedRealTime = time;
    }

    @Override
    public void onSensorChanged(SensorEvent event) {
        // Using log space to represent human sensation (Fechner's Law) instead of lux
        // because lux values causes bright flashes to skew the average very high.
        addElement(event.timestamp, Math.max(0,
                (int) (Math.log(event.values[0]) * LIGHT_VALUE_MULTIPLIER)));
        updateLightSession();
        mHandler.removeCallbacksAndMessages(mDelayedUpdateToken);
        mHandler.postDelayed(CameraPrivacyLightController.this::updateLightSession,
                mDelayedUpdateToken, mMovingAverageIntervalMillis);
    }

    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) {}

    private long getCurrentIntervalMillis() {
        return Math.min(mMovingAverageIntervalMillis,
                getElapsedRealTime() - mElapsedTimeStartedReading);
    }
}
+199 −9
Original line number Diff line number Diff line
@@ -24,20 +24,33 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;

import android.app.AppOpsManager;
import android.content.Context;
import android.content.res.Resources;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.hardware.lights.Light;
import android.hardware.lights.LightState;
import android.hardware.lights.LightsManager;
import android.hardware.lights.LightsRequest;
import android.os.Handler;
import android.os.Looper;
import android.permission.PermissionManager;
import android.util.ArraySet;

import com.android.dx.mockito.inline.extended.ExtendedMockito;
import com.android.internal.R;

import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
@@ -53,26 +66,43 @@ import java.util.stream.Collectors;

public class CameraPrivacyLightControllerTest {

    private int mDayColor = 1;
    private int mNightColor = 0;
    private int mCameraPrivacyLightAlsAveragingIntervalMillis = 5000;
    private int mCameraPrivacyLightAlsNightThreshold = (int) getLightSensorValue(15);

    private MockitoSession mMockitoSession;

    @Mock
    private Context mContext;

    @Mock
    private Resources mResources;

    @Mock
    private LightsManager mLightsManager;

    @Mock
    private AppOpsManager mAppOpsManager;

    @Mock
    private SensorManager mSensorManager;

    @Mock
    private LightsManager.LightsSession mLightsSession;

    @Mock
    private Sensor mLightSensor;

    private ArgumentCaptor<AppOpsManager.OnOpActiveChangedListener> mAppOpsListenerCaptor =
            ArgumentCaptor.forClass(AppOpsManager.OnOpActiveChangedListener.class);

    private ArgumentCaptor<LightsRequest> mLightsRequestCaptor =
            ArgumentCaptor.forClass(LightsRequest.class);

    private ArgumentCaptor<SensorEventListener> mLightSensorListenerCaptor =
            ArgumentCaptor.forClass(SensorEventListener.class);

    private Set<String> mExemptedPackages = new ArraySet<>();
    private List<Light> mLights = new ArrayList<>();

@@ -86,11 +116,22 @@ public class CameraPrivacyLightControllerTest {
                .spyStatic(PermissionManager.class)
                .startMocking();

        doReturn(mDayColor).when(mContext).getColor(R.color.camera_privacy_light_day);
        doReturn(mNightColor).when(mContext).getColor(R.color.camera_privacy_light_night);

        doReturn(mResources).when(mContext).getResources();
        doReturn(mCameraPrivacyLightAlsAveragingIntervalMillis).when(mResources)
                .getInteger(R.integer.config_cameraPrivacyLightAlsAveragingIntervalMillis);
        doReturn(mCameraPrivacyLightAlsNightThreshold).when(mResources)
                .getInteger(R.integer.config_cameraPrivacyLightAlsNightThreshold);

        doReturn(mLightsManager).when(mContext).getSystemService(LightsManager.class);
        doReturn(mAppOpsManager).when(mContext).getSystemService(AppOpsManager.class);
        doReturn(mSensorManager).when(mContext).getSystemService(SensorManager.class);

        doReturn(mLights).when(mLightsManager).getLights();
        doReturn(mLightsSession).when(mLightsManager).openSession(anyInt());
        doReturn(mLightSensor).when(mSensorManager).getDefaultSensor(Sensor.TYPE_LIGHT);

        doReturn(mExemptedPackages)
                .when(() -> PermissionManager.getIndicatorExemptedPackages(any()));
@@ -107,7 +148,7 @@ public class CameraPrivacyLightControllerTest {
    @Test
    public void testAppsOpsListenerNotRegisteredWithoutCameraLights() {
        mLights.add(getNextLight(false));
        new CameraPrivacyLightController(mContext);
        createCameraPrivacyLightController();

        verify(mAppOpsManager, times(0)).startWatchingActive(any(), any(), any());
    }
@@ -116,7 +157,7 @@ public class CameraPrivacyLightControllerTest {
    public void testAppsOpsListenerRegisteredWithCameraLight() {
        mLights.add(getNextLight(true));

        new CameraPrivacyLightController(mContext);
        createCameraPrivacyLightController();

        verify(mAppOpsManager, times(1)).startWatchingActive(any(), any(), any());
    }
@@ -128,14 +169,13 @@ public class CameraPrivacyLightControllerTest {
            mLights.add(getNextLight(r.nextBoolean()));
        }

        new CameraPrivacyLightController(mContext);
        createCameraPrivacyLightController();

        // Verify no session has been opened at this point.
        verify(mLightsManager, times(0)).openSession(anyInt());

        // Set camera op as active.
        verify(mAppOpsManager).startWatchingActive(any(), any(), mAppOpsListenerCaptor.capture());
        mAppOpsListenerCaptor.getValue().onOpActiveChanged(OPSTR_CAMERA, 10101, "pkg1", true);
        openCamera();

        // Verify session has been opened exactly once
        verify(mLightsManager, times(1)).openSession(anyInt());
@@ -161,7 +201,7 @@ public class CameraPrivacyLightControllerTest {
    public void testWillOnlyOpenOnceWhenTwoPackagesStartOp() {
        mLights.add(getNextLight(true));

        new CameraPrivacyLightController(mContext);
        createCameraPrivacyLightController();

        verify(mAppOpsManager).startWatchingActive(any(), any(), mAppOpsListenerCaptor.capture());

@@ -176,7 +216,7 @@ public class CameraPrivacyLightControllerTest {
    public void testWillCloseOnFinishOp() {
        mLights.add(getNextLight(true));

        new CameraPrivacyLightController(mContext);
        createCameraPrivacyLightController();

        verify(mAppOpsManager).startWatchingActive(any(), any(), mAppOpsListenerCaptor.capture());

@@ -192,7 +232,7 @@ public class CameraPrivacyLightControllerTest {
    public void testWillCloseOnFinishOpForAllPackages() {
        mLights.add(getNextLight(true));

        new CameraPrivacyLightController(mContext);
        createCameraPrivacyLightController();

        int numUids = 100;
        List<Integer> uids = new ArrayList<>(numUids);
@@ -226,7 +266,7 @@ public class CameraPrivacyLightControllerTest {
        mLights.add(getNextLight(true));
        mExemptedPackages.add("pkg1");

        new CameraPrivacyLightController(mContext);
        createCameraPrivacyLightController();

        verify(mAppOpsManager).startWatchingActive(any(), any(), mAppOpsListenerCaptor.capture());

@@ -235,6 +275,147 @@ public class CameraPrivacyLightControllerTest {
        verify(mLightsManager, times(0)).openSession(anyInt());
    }

    @Test
    public void testNoLightSensor() {
        mLights.add(getNextLight(true));
        doReturn(null).when(mSensorManager).getDefaultSensor(Sensor.TYPE_LIGHT);

        createCameraPrivacyLightController();

        openCamera();

        verify(mLightsSession).requestLights(mLightsRequestCaptor.capture());
        LightsRequest lightsRequest = mLightsRequestCaptor.getValue();
        for (LightState lightState : lightsRequest.getLightStates()) {
            assertEquals(mDayColor, lightState.getColor());
        }
    }

    @Test
    public void testALSListenerNotRegisteredUntilCameraIsOpened() {
        mLights.add(getNextLight(true));
        Sensor sensor = mock(Sensor.class);
        doReturn(sensor).when(mSensorManager).getDefaultSensor(Sensor.TYPE_LIGHT);

        CameraPrivacyLightController cplc = createCameraPrivacyLightController();

        verify(mSensorManager, never()).registerListener(any(SensorEventListener.class),
                any(Sensor.class), anyInt(), any(Handler.class));

        openCamera();

        verify(mSensorManager, times(1)).registerListener(mLightSensorListenerCaptor.capture(),
                any(Sensor.class), anyInt(), any(Handler.class));

        mAppOpsListenerCaptor.getValue().onOpActiveChanged(OPSTR_CAMERA, 10001, "pkg", false);
        verify(mSensorManager, times(1)).unregisterListener(mLightSensorListenerCaptor.getValue());
    }

    @Ignore
    @Test
    public void testDayColor() {
        testBrightnessToColor(20, mDayColor);
    }

    @Ignore
    @Test
    public void testNightColor() {
        testBrightnessToColor(10, mNightColor);
    }

    private void testBrightnessToColor(int brightnessValue, int color) {
        mLights.add(getNextLight(true));
        Sensor sensor = mock(Sensor.class);
        doReturn(sensor).when(mSensorManager).getDefaultSensor(Sensor.TYPE_LIGHT);

        CameraPrivacyLightController cplc = createCameraPrivacyLightController();
        cplc.setElapsedRealTime(0);

        openCamera();

        verify(mSensorManager).registerListener(mLightSensorListenerCaptor.capture(),
                any(Sensor.class), anyInt(), any(Handler.class));
        SensorEventListener sensorListener = mLightSensorListenerCaptor.getValue();
        float[] sensorEventValues = new float[1];
        SensorEvent sensorEvent = new SensorEvent(sensor, 0, 0, sensorEventValues);

        sensorEventValues[0] = getLightSensorValue(brightnessValue);
        sensorListener.onSensorChanged(sensorEvent);

        verify(mLightsSession, atLeastOnce()).requestLights(mLightsRequestCaptor.capture());
        for (LightState lightState : mLightsRequestCaptor.getValue().getLightStates()) {
            assertEquals(color, lightState.getColor());
        }
    }

    @Ignore
    @Test
    public void testDayToNightTransistion() {
        mLights.add(getNextLight(true));
        Sensor sensor = mock(Sensor.class);
        doReturn(sensor).when(mSensorManager).getDefaultSensor(Sensor.TYPE_LIGHT);

        CameraPrivacyLightController cplc = createCameraPrivacyLightController();
        cplc.setElapsedRealTime(0);

        openCamera();
        // There will be an initial call at brightness 0
        verify(mLightsSession, times(1)).requestLights(any(LightsRequest.class));

        verify(mSensorManager).registerListener(mLightSensorListenerCaptor.capture(),
                any(Sensor.class), anyInt(), any(Handler.class));
        SensorEventListener sensorListener = mLightSensorListenerCaptor.getValue();

        onSensorEvent(cplc, sensorListener, sensor, 0, 20);

        // 5 sec avg = 20
        onSensorEvent(cplc, sensorListener, sensor, 5000, 30);

        verify(mLightsSession, times(2)).requestLights(mLightsRequestCaptor.capture());
        for (LightState lightState : mLightsRequestCaptor.getValue().getLightStates()) {
            assertEquals(mDayColor, lightState.getColor());
        }

        // 5 sec avg = 22

        onSensorEvent(cplc, sensorListener, sensor, 6000, 10);

        // 5 sec avg = 18

        onSensorEvent(cplc, sensorListener, sensor, 8000, 5);

        // Should have always been day
        verify(mLightsSession, times(2)).requestLights(mLightsRequestCaptor.capture());
        for (LightState lightState : mLightsRequestCaptor.getValue().getLightStates()) {
            assertEquals(mDayColor, lightState.getColor());
        }

        // 5 sec avg = 12

        onSensorEvent(cplc, sensorListener, sensor, 10000, 5);

        // Should now be night
        verify(mLightsSession, times(3)).requestLights(mLightsRequestCaptor.capture());
        for (LightState lightState : mLightsRequestCaptor.getValue().getLightStates()) {
            assertEquals(mNightColor, lightState.getColor());
        }
    }

    private void onSensorEvent(CameraPrivacyLightController cplc,
            SensorEventListener sensorListener, Sensor sensor, long timestamp, int value) {
        cplc.setElapsedRealTime(timestamp);
        sensorListener.onSensorChanged(new SensorEvent(sensor, 0, timestamp,
                new float[] {getLightSensorValue(value)}));
    }

    // Use the test thread so that the test is deterministic
    private CameraPrivacyLightController createCameraPrivacyLightController() {
        if (Looper.myLooper() == null) {
            Looper.prepare();
        }
        return new CameraPrivacyLightController(mContext, Looper.myLooper());
    }

    private Light getNextLight(boolean cameraType) {
        Light light = ExtendedMockito.mock(Light.class);
        if (cameraType) {
@@ -245,4 +426,13 @@ public class CameraPrivacyLightControllerTest {
        doReturn(mNextLightId++).when(light).getId();
        return light;
    }

    private float getLightSensorValue(int i) {
        return (float) Math.exp(i / CameraPrivacyLightController.LIGHT_VALUE_MULTIPLIER);
    }

    private void openCamera() {
        verify(mAppOpsManager).startWatchingActive(any(), any(), mAppOpsListenerCaptor.capture());
        mAppOpsListenerCaptor.getValue().onOpActiveChanged(OPSTR_CAMERA, 10001, "pkg", true);
    }
}