summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Evan Severson <evanseverson@google.com> 2023-08-26 04:57:53 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2023-08-26 04:57:53 +0000
commit58eb008bbb8f305424675b521b87770e8d60923d (patch)
tree02f69f401ab12c30225752341c9f81357120e7dd
parent40e7b45bdb7bf46e71ddf012bca6ead7c00f57d4 (diff)
parentf188f0ab7c8db6d33784100a043f785c3c785472 (diff)
Merge "Add support for multiple lux thresholds for camera privacy light" into udc-qpr-dev
-rw-r--r--core/res/res/values/colors.xml4
-rw-r--r--core/res/res/values/config.xml15
-rw-r--r--core/res/res/values/symbols.xml5
-rw-r--r--services/core/java/com/android/server/sensorprivacy/CameraPrivacyLightController.java68
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/sensorprivacy/CameraPrivacyLightControllerTest.java312
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);