diff options
| author | 2023-08-26 04:57:53 +0000 | |
|---|---|---|
| committer | 2023-08-26 04:57:53 +0000 | |
| commit | 58eb008bbb8f305424675b521b87770e8d60923d (patch) | |
| tree | 02f69f401ab12c30225752341c9f81357120e7dd | |
| parent | 40e7b45bdb7bf46e71ddf012bca6ead7c00f57d4 (diff) | |
| parent | f188f0ab7c8db6d33784100a043f785c3c785472 (diff) | |
Merge "Add support for multiple lux thresholds for camera privacy light" into udc-qpr-dev
5 files changed, 182 insertions, 222 deletions
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml index e67ea82cb6da..a6830a6e3793 100644 --- a/core/res/res/values/colors.xml +++ b/core/res/res/values/colors.xml @@ -552,10 +552,6 @@ <color name="accessibility_magnification_thumbnail_container_background_color">#99000000</color> <color name="accessibility_magnification_thumbnail_container_stroke_color">#FFFFFF</color> - <!-- Color of camera light when camera is in use --> - <color name="camera_privacy_light_day">#FFFFFF</color> - <color name="camera_privacy_light_night">#FFFFFF</color> - <!-- Lily Language Picker language item view colors --> <color name="language_picker_item_text_color">#202124</color> <color name="language_picker_item_text_color_secondary">#5F6368</color> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index c7226f69340b..76cf22dd993d 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -6441,8 +6441,19 @@ <!-- 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> + <!-- Ambient Light sensor's lux values to use as the threshold between brightness colors defined + by config_cameraPrivacyLightColors. If the ambient brightness less than the first element + in this array then lights of type "camera" will be set to the color in position 0 of + config_cameraPrivacyLightColors. This array must be strictly increasing and have a length + of zero means there is only one brightness --> + <integer-array name="config_cameraPrivacyLightAlsLuxThresholds"> + </integer-array> + <!-- Colors to configure the camera privacy light at different brightnesses. This array must + have exactly one more entry than config_cameraPrivacyLightAlsLuxThresholds, + or a length of zero if the feature isn't supported. If nonempty and the device doesn't have + an ambient light sensor the last element in this array will be the only one used --> + <array name="config_cameraPrivacyLightColors"> + </array> <!-- List of system components which are allowed to receive ServiceState entries in an un-sanitized form, even if the location toggle is off. This is intended ONLY for system diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 655892da9b9a..11ba6ab87936 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -4963,10 +4963,9 @@ <java-symbol type="string" name="vdm_camera_access_denied" /> <java-symbol type="string" name="vdm_secure_window" /> - <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="array" name="config_cameraPrivacyLightAlsLuxThresholds"/> + <java-symbol type="array" name="config_cameraPrivacyLightColors"/> <java-symbol type="bool" name="config_bg_current_drain_monitor_enabled" /> <java-symbol type="array" name="config_bg_current_drain_threshold_to_restricted_bucket" /> diff --git a/services/core/java/com/android/server/sensorprivacy/CameraPrivacyLightController.java b/services/core/java/com/android/server/sensorprivacy/CameraPrivacyLightController.java index f744d00b2066..565eb6e85559 100644 --- a/services/core/java/com/android/server/sensorprivacy/CameraPrivacyLightController.java +++ b/services/core/java/com/android/server/sensorprivacy/CameraPrivacyLightController.java @@ -18,7 +18,6 @@ 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; @@ -39,6 +38,7 @@ import android.util.Pair; import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.ArrayUtils; import com.android.server.FgThread; import java.util.ArrayDeque; @@ -48,12 +48,10 @@ import java.util.Set; import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; - class CameraPrivacyLightController implements AppOpsManager.OnOpActiveChangedListener, SensorEventListener { - @VisibleForTesting - static final double LIGHT_VALUE_MULTIPLIER = 1 / Math.log(1.1); + private static final double LIGHT_VALUE_MULTIPLIER = 1 / Math.log(1.1); private final Handler mHandler; private final Executor mExecutor; @@ -69,11 +67,6 @@ class CameraPrivacyLightController implements AppOpsManager.OnOpActiveChangedLis private LightsManager.LightsSession mLightsSession = null; - @ColorInt - private final int mDayColor; - @ColorInt - private final int mNightColor; - private final Sensor mLightSensor; private boolean mIsAmbientLightListenerRegistered = false; @@ -81,7 +74,9 @@ class CameraPrivacyLightController implements AppOpsManager.OnOpActiveChangedLis /** 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 long[] mThresholds; + + private final int[] mColors; 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 */ @@ -101,6 +96,20 @@ class CameraPrivacyLightController implements AppOpsManager.OnOpActiveChangedLis @VisibleForTesting CameraPrivacyLightController(Context context, Looper looper) { + mColors = context.getResources().getIntArray(R.array.config_cameraPrivacyLightColors); + if (ArrayUtils.isEmpty(mColors)) { + mHandler = null; + mExecutor = null; + mContext = null; + mAppOpsManager = null; + mLightsManager = null; + mSensorManager = null; + mLightSensor = null; + mMovingAverageIntervalMillis = 0; + mThresholds = null; + // Return here before this class starts interacting with other services. + return; + } mContext = context; mHandler = new Handler(looper); @@ -109,14 +118,20 @@ class CameraPrivacyLightController implements AppOpsManager.OnOpActiveChangedLis mAppOpsManager = mContext.getSystemService(AppOpsManager.class); mLightsManager = mContext.getSystemService(LightsManager.class); mSensorManager = mContext.getSystemService(SensorManager.class); - - 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); + int[] thresholdsLux = mContext.getResources().getIntArray( + R.array.config_cameraPrivacyLightAlsLuxThresholds); + if (thresholdsLux.length != mColors.length - 1) { + throw new IllegalStateException("There must be exactly one more color than thresholds." + + " Found " + mColors.length + " colors and " + thresholdsLux.length + + " thresholds."); + } + mThresholds = new long[thresholdsLux.length]; + for (int i = 0; i < thresholdsLux.length; i++) { + int luxValue = thresholdsLux[i]; + mThresholds[i] = (long) (Math.log(luxValue) * LIGHT_VALUE_MULTIPLIER); + } List<Light> lights = mLightsManager.getLights(); for (int i = 0; i < lights.size(); i++) { @@ -223,13 +238,8 @@ class CameraPrivacyLightController implements AppOpsManager.OnOpActiveChangedLis mLightsSession.close(); mLightsSession = null; } else { - int lightColor; - if (mLightSensor != null && getLiveAmbientLightTotal() - < getCurrentIntervalMillis() * mNightThreshold) { - lightColor = mNightColor; - } else { - lightColor = mDayColor; - } + int lightColor = + mLightSensor == null ? mColors[mColors.length - 1] : computeCurrentLightColor(); if (mLastLightColor == lightColor && mLightsSession != null) { return; @@ -252,6 +262,18 @@ class CameraPrivacyLightController implements AppOpsManager.OnOpActiveChangedLis } } + private int computeCurrentLightColor() { + long liveAmbientLightTotal = getLiveAmbientLightTotal(); + long currentInterval = getCurrentIntervalMillis(); + + for (int i = 0; i < mThresholds.length; i++) { + if (liveAmbientLightTotal < currentInterval * mThresholds[i]) { + return mColors[i]; + } + } + return mColors[mColors.length - 1]; + } + private void updateSensorListener(boolean shouldSessionEnd) { if (shouldSessionEnd && mIsAmbientLightListenerRegistered) { mSensorManager.unregisterListener(this); diff --git a/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/CameraPrivacyLightControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/CameraPrivacyLightControllerTest.java index 20cfd59973c3..dc04b6aea318 100644 --- a/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/CameraPrivacyLightControllerTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/CameraPrivacyLightControllerTest.java @@ -24,10 +24,12 @@ 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.ArgumentMatchers.eq; 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 static org.mockito.Mockito.verifyZeroInteractions; import android.app.AppOpsManager; import android.content.Context; @@ -43,14 +45,13 @@ import android.hardware.lights.LightsRequest; import android.os.Handler; import android.os.Looper; import android.permission.PermissionManager; -import android.util.ArraySet; +import android.testing.TestableLooper; 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; @@ -62,24 +63,19 @@ import java.util.Collections; import java.util.List; import java.util.Random; import java.util.Set; +import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; public class CameraPrivacyLightControllerTest { + private int[] mDefaultColors = {0, 1, 2}; + private int[] mDefaultAlsThresholdsLux = {10, 50}; + private int mDefaultAlsAveragingIntervalMillis = 5000; - private int mDayColor = 1; - private int mNightColor = 0; - private int mCameraPrivacyLightAlsAveragingIntervalMillis = 5000; - private int mCameraPrivacyLightAlsNightThreshold = (int) getLightSensorValue(15); + private TestableLooper mTestableLooper; private MockitoSession mMockitoSession; @Mock - private Context mContext; - - @Mock - private Resources mResources; - - @Mock private LightsManager mLightsManager; @Mock @@ -103,61 +99,98 @@ public class CameraPrivacyLightControllerTest { private ArgumentCaptor<SensorEventListener> mLightSensorListenerCaptor = ArgumentCaptor.forClass(SensorEventListener.class); - private Set<String> mExemptedPackages = new ArraySet<>(); - private List<Light> mLights = new ArrayList<>(); + private Set<String> mExemptedPackages; + private List<Light> mLights; private int mNextLightId = 1; - @Before - public void setUp() { - mMockitoSession = ExtendedMockito.mockitoSession() - .initMocks(this) - .strictness(Strictness.WARN) - .spyStatic(PermissionManager.class) - .startMocking(); + public CameraPrivacyLightController prepareDefaultCameraPrivacyLightController() { + return prepareDefaultCameraPrivacyLightController(List.of(getNextLight(true))); + } - doReturn(mDayColor).when(mContext).getColor(R.color.camera_privacy_light_day); - doReturn(mNightColor).when(mContext).getColor(R.color.camera_privacy_light_night); + public CameraPrivacyLightController prepareDefaultCameraPrivacyLightController( + List<Light> lights) { + return prepareCameraPrivacyLightController(lights, Set.of(), true, mDefaultColors, + mDefaultAlsThresholdsLux, mDefaultAlsAveragingIntervalMillis); + } + + public CameraPrivacyLightController prepareCameraPrivacyLightController(List<Light> lights, + Set<String> exemptedPackages, boolean hasLightSensor, int[] lightColors, + int[] alsThresholds, int averagingInterval) { + Looper looper = Looper.myLooper(); + if (looper == null) { + Looper.prepare(); + looper = Looper.myLooper(); + } + if (mTestableLooper == null) { + try { + mTestableLooper = new TestableLooper(looper); + } catch (Exception e) { + throw new RuntimeException(e); + } + } - doReturn(mResources).when(mContext).getResources(); - doReturn(mCameraPrivacyLightAlsAveragingIntervalMillis).when(mResources) + Context context = mock(Context.class); + Resources resources = mock(Resources.class); + doReturn(resources).when(context).getResources(); + doReturn(lightColors).when(resources).getIntArray(R.array.config_cameraPrivacyLightColors); + doReturn(alsThresholds).when(resources) + .getIntArray(R.array.config_cameraPrivacyLightAlsLuxThresholds); + doReturn(averagingInterval).when(resources) .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(mLightsManager).when(context).getSystemService(LightsManager.class); + doReturn(mAppOpsManager).when(context).getSystemService(AppOpsManager.class); + doReturn(mSensorManager).when(context).getSystemService(SensorManager.class); + mLights = lights; + mExemptedPackages = exemptedPackages; doReturn(mLights).when(mLightsManager).getLights(); doReturn(mLightsSession).when(mLightsManager).openSession(anyInt()); + if (!hasLightSensor) { + mLightSensor = null; + } doReturn(mLightSensor).when(mSensorManager).getDefaultSensor(Sensor.TYPE_LIGHT); - - doReturn(mExemptedPackages) + doReturn(exemptedPackages) .when(() -> PermissionManager.getIndicatorExemptedPackages(any())); + + return new CameraPrivacyLightController(context, looper); + } + + @Before + public void setUp() { + mMockitoSession = ExtendedMockito.mockitoSession() + .initMocks(this) + .strictness(Strictness.WARN) + .spyStatic(PermissionManager.class) + .startMocking(); } @After public void tearDown() { - mExemptedPackages.clear(); - mLights.clear(); - mMockitoSession.finishMocking(); } @Test + public void testNoInteractionsWithServicesIfNoColorsSpecified() { + prepareCameraPrivacyLightController(List.of(getNextLight(true)), Collections.EMPTY_SET, + true, new int[0], mDefaultAlsThresholdsLux, mDefaultAlsAveragingIntervalMillis); + + verifyZeroInteractions(mLightsManager); + verifyZeroInteractions(mAppOpsManager); + verifyZeroInteractions(mSensorManager); + } + + @Test public void testAppsOpsListenerNotRegisteredWithoutCameraLights() { - mLights.add(getNextLight(false)); - createCameraPrivacyLightController(); + prepareDefaultCameraPrivacyLightController(List.of(getNextLight(false))); verify(mAppOpsManager, times(0)).startWatchingActive(any(), any(), any()); } @Test public void testAppsOpsListenerRegisteredWithCameraLight() { - mLights.add(getNextLight(true)); - - createCameraPrivacyLightController(); + prepareDefaultCameraPrivacyLightController(); verify(mAppOpsManager, times(1)).startWatchingActive(any(), any(), any()); } @@ -165,11 +198,12 @@ public class CameraPrivacyLightControllerTest { @Test public void testAllCameraLightsAreRequestedOnOpActive() { Random r = new Random(0); + List<Light> lights = new ArrayList<>(); for (int i = 0; i < 30; i++) { - mLights.add(getNextLight(r.nextBoolean())); + lights.add(getNextLight(r.nextBoolean())); } - createCameraPrivacyLightController(); + prepareDefaultCameraPrivacyLightController(lights); // Verify no session has been opened at this point. verify(mLightsManager, times(0)).openSession(anyInt()); @@ -181,8 +215,6 @@ public class CameraPrivacyLightControllerTest { verify(mLightsManager, times(1)).openSession(anyInt()); verify(mLightsSession).requestLights(mLightsRequestCaptor.capture()); - assertEquals("requestLights() not invoked exactly once", - 1, mLightsRequestCaptor.getAllValues().size()); List<Integer> expectedCameraLightIds = mLights.stream() .filter(l -> l.getType() == Light.LIGHT_TYPE_CAMERA) @@ -199,40 +231,25 @@ public class CameraPrivacyLightControllerTest { @Test public void testWillOnlyOpenOnceWhenTwoPackagesStartOp() { - mLights.add(getNextLight(true)); - - createCameraPrivacyLightController(); - - verify(mAppOpsManager).startWatchingActive(any(), any(), mAppOpsListenerCaptor.capture()); - - AppOpsManager.OnOpActiveChangedListener listener = mAppOpsListenerCaptor.getValue(); - listener.onOpActiveChanged(OPSTR_CAMERA, 10101, "pkg1", true); + prepareDefaultCameraPrivacyLightController(); + notifyCamOpChanged(10101, "pkg1", true); verify(mLightsManager, times(1)).openSession(anyInt()); - listener.onOpActiveChanged(OPSTR_CAMERA, 10102, "pkg2", true); + notifyCamOpChanged(10102, "pkg2", true); verify(mLightsManager, times(1)).openSession(anyInt()); } @Test public void testWillCloseOnFinishOp() { - mLights.add(getNextLight(true)); - - createCameraPrivacyLightController(); - - verify(mAppOpsManager).startWatchingActive(any(), any(), mAppOpsListenerCaptor.capture()); - - AppOpsManager.OnOpActiveChangedListener listener = mAppOpsListenerCaptor.getValue(); - listener.onOpActiveChanged(OPSTR_CAMERA, 10101, "pkg1", true); - + prepareDefaultCameraPrivacyLightController(); + notifyCamOpChanged(10101, "pkg1", true); verify(mLightsSession, times(0)).close(); - listener.onOpActiveChanged(OPSTR_CAMERA, 10101, "pkg1", false); + notifyCamOpChanged(10101, "pkg1", false); verify(mLightsSession, times(1)).close(); } @Test public void testWillCloseOnFinishOpForAllPackages() { - mLights.add(getNextLight(true)); - - createCameraPrivacyLightController(); + prepareDefaultCameraPrivacyLightController(); int numUids = 100; List<Integer> uids = new ArrayList<>(numUids); @@ -240,64 +257,52 @@ public class CameraPrivacyLightControllerTest { uids.add(10001 + i); } - verify(mAppOpsManager).startWatchingActive(any(), any(), mAppOpsListenerCaptor.capture()); - - AppOpsManager.OnOpActiveChangedListener listener = mAppOpsListenerCaptor.getValue(); - for (int i = 0; i < numUids; i++) { - listener.onOpActiveChanged(OPSTR_CAMERA, uids.get(i), "pkg" + (int) uids.get(i), true); + notifyCamOpChanged(uids.get(i), "pkg" + (int) uids.get(i), true); } // Change the order which their ops are finished Collections.shuffle(uids, new Random(0)); for (int i = 0; i < numUids - 1; i++) { - listener.onOpActiveChanged(OPSTR_CAMERA, uids.get(i), "pkg" + (int) uids.get(i), false); + notifyCamOpChanged(uids.get(i), "pkg" + (int) uids.get(i), false); } verify(mLightsSession, times(0)).close(); int lastUid = uids.get(numUids - 1); - listener.onOpActiveChanged(OPSTR_CAMERA, lastUid, "pkg" + lastUid, false); + notifyCamOpChanged(lastUid, "pkg" + lastUid, false); verify(mLightsSession, times(1)).close(); } @Test public void testWontOpenForExemptedPackage() { - mLights.add(getNextLight(true)); - mExemptedPackages.add("pkg1"); - - createCameraPrivacyLightController(); + String exemptPackage = "pkg1"; + prepareCameraPrivacyLightController(List.of(getNextLight(true)), + Set.of(exemptPackage), true, mDefaultColors, mDefaultAlsThresholdsLux, + mDefaultAlsAveragingIntervalMillis); - verify(mAppOpsManager).startWatchingActive(any(), any(), mAppOpsListenerCaptor.capture()); - - AppOpsManager.OnOpActiveChangedListener listener = mAppOpsListenerCaptor.getValue(); - listener.onOpActiveChanged(OPSTR_CAMERA, 10101, "pkg1", true); + notifyCamOpChanged(10101, exemptPackage, true); verify(mLightsManager, times(0)).openSession(anyInt()); } @Test public void testNoLightSensor() { - mLights.add(getNextLight(true)); - doReturn(null).when(mSensorManager).getDefaultSensor(Sensor.TYPE_LIGHT); - - createCameraPrivacyLightController(); + prepareCameraPrivacyLightController(List.of(getNextLight(true)), + Set.of(), true, mDefaultColors, mDefaultAlsThresholdsLux, + mDefaultAlsAveragingIntervalMillis); openCamera(); verify(mLightsSession).requestLights(mLightsRequestCaptor.capture()); LightsRequest lightsRequest = mLightsRequestCaptor.getValue(); for (LightState lightState : lightsRequest.getLightStates()) { - assertEquals(mDayColor, lightState.getColor()); + assertEquals(mDefaultColors[mDefaultColors.length - 1], 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(); + prepareDefaultCameraPrivacyLightController(); verify(mSensorManager, never()).registerListener(any(SensorEventListener.class), any(Sensor.class), anyInt(), any(Handler.class)); @@ -307,113 +312,44 @@ public class CameraPrivacyLightControllerTest { verify(mSensorManager, times(1)).registerListener(mLightSensorListenerCaptor.capture(), any(Sensor.class), anyInt(), any(Handler.class)); - mAppOpsListenerCaptor.getValue().onOpActiveChanged(OPSTR_CAMERA, 10001, "pkg", false); + notifyCamOpChanged(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(); + public void testAlsThresholds() { + CameraPrivacyLightController cplc = prepareDefaultCameraPrivacyLightController(); + long elapsedTime = 0; 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()); + for (int i = 0; i < mDefaultColors.length; i++) { + int expectedColor = mDefaultColors[i]; + int alsLuxValue = i + == mDefaultAlsThresholdsLux.length + ? mDefaultAlsThresholdsLux[i - 1] : mDefaultAlsThresholdsLux[i] - 1; + + notifySensorEvent(cplc, elapsedTime, alsLuxValue); + elapsedTime += mDefaultAlsAveragingIntervalMillis + 1; + notifySensorEvent(cplc, elapsedTime, alsLuxValue); + + verify(mLightsSession, atLeastOnce()).requestLights(mLightsRequestCaptor.capture()); + for (LightState lightState : mLightsRequestCaptor.getValue().getLightStates()) { + assertEquals(expectedColor, 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 notifyCamOpChanged(int uid, String pkg, boolean active) { + verify(mAppOpsManager).startWatchingActive(any(), any(), mAppOpsListenerCaptor.capture()); + mAppOpsListenerCaptor.getValue().onOpActiveChanged(OPSTR_CAMERA, uid, pkg, active); } - private void onSensorEvent(CameraPrivacyLightController cplc, - SensorEventListener sensorListener, Sensor sensor, long timestamp, int value) { + private void notifySensorEvent(CameraPrivacyLightController cplc, 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()); + verify(mSensorManager, atLeastOnce()).registerListener(mLightSensorListenerCaptor.capture(), + eq(mLightSensor), anyInt(), any()); + mLightSensorListenerCaptor.getValue().onSensorChanged(new SensorEvent(mLightSensor, 0, + TimeUnit.MILLISECONDS.toNanos(timestamp), new float[] {value})); } private Light getNextLight(boolean cameraType) { @@ -427,10 +363,6 @@ public class CameraPrivacyLightControllerTest { 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); |