Reload the Ambient Light Sensor on Display Change

This change fixes the multi display scenario - when the
underlying display device changes, we should reload the
appropriate ambient light sensor for the default display.

Bug: 198125195
Test: adb shell dumpsys sensorservice
Test: atest com.android.server.display
Change-Id: I8b2d76d916d79f7dae22d81adc3ce69780bf9bfc
This commit is contained in:
Fiona Campbell
2022-12-02 11:16:28 +00:00
parent 9e9e673b11
commit eec9ec7cba
4 changed files with 139 additions and 43 deletions

View File

@@ -2630,6 +2630,9 @@ public class DisplayDeviceConfig {
} }
} }
/**
* Uniquely identifies a Sensor, with the combination of Type and Name.
*/
static class SensorData { static class SensorData {
public String type; public String type;
public String name; public String name;

View File

@@ -50,7 +50,6 @@ import android.provider.DeviceConfig;
import android.provider.DeviceConfigInterface; import android.provider.DeviceConfigInterface;
import android.provider.Settings; import android.provider.Settings;
import android.sysprop.SurfaceFlingerProperties; import android.sysprop.SurfaceFlingerProperties;
import android.text.TextUtils;
import android.util.IndentingPrintWriter; import android.util.IndentingPrintWriter;
import android.util.Pair; import android.util.Pair;
import android.util.Slog; import android.util.Slog;
@@ -70,6 +69,7 @@ import com.android.internal.os.BackgroundThread;
import com.android.server.LocalServices; import com.android.server.LocalServices;
import com.android.server.display.utils.AmbientFilter; import com.android.server.display.utils.AmbientFilter;
import com.android.server.display.utils.AmbientFilterFactory; import com.android.server.display.utils.AmbientFilterFactory;
import com.android.server.display.utils.SensorUtils;
import com.android.server.sensors.SensorManagerInternal; import com.android.server.sensors.SensorManagerInternal;
import com.android.server.sensors.SensorManagerInternal.ProximityActiveListener; import com.android.server.sensors.SensorManagerInternal.ProximityActiveListener;
import com.android.server.statusbar.StatusBarManagerInternal; import com.android.server.statusbar.StatusBarManagerInternal;
@@ -683,14 +683,20 @@ public class DisplayModeDirector {
} }
/** /**
* A utility to make this class aware of the new display configs whenever the default display is * Called when the underlying display device of the default display is changed.
* changed * Some data in this class relates to the physical display of the device, and so we need to
* reload the configurations based on this.
* E.g. the brightness sensors and refresh rate capabilities depend on the physical display
* device that is being used, so will be reloaded.
*
* @param displayDeviceConfig configurations relating to the underlying display device.
*/ */
public void defaultDisplayDeviceUpdated(DisplayDeviceConfig displayDeviceConfig) { public void defaultDisplayDeviceUpdated(DisplayDeviceConfig displayDeviceConfig) {
mSettingsObserver.setRefreshRates(displayDeviceConfig, mSettingsObserver.setRefreshRates(displayDeviceConfig,
/* attemptLoadingFromDeviceConfig= */ true); /* attemptLoadingFromDeviceConfig= */ true);
mBrightnessObserver.updateBlockingZoneThresholds(displayDeviceConfig, mBrightnessObserver.updateBlockingZoneThresholds(displayDeviceConfig,
/* attemptLoadingFromDeviceConfig= */ true); /* attemptLoadingFromDeviceConfig= */ true);
mBrightnessObserver.reloadLightSensor(displayDeviceConfig);
} }
/** /**
@@ -1739,6 +1745,9 @@ public class DisplayModeDirector {
private SensorManager mSensorManager; private SensorManager mSensorManager;
private Sensor mLightSensor; private Sensor mLightSensor;
private Sensor mRegisteredLightSensor;
private String mLightSensorType;
private String mLightSensorName;
private final LightSensorEventListener mLightSensorListener = private final LightSensorEventListener mLightSensorListener =
new LightSensorEventListener(); new LightSensorEventListener();
// Take it as low brightness before valid sensor data comes // Take it as low brightness before valid sensor data comes
@@ -1899,17 +1908,8 @@ public class DisplayModeDirector {
return mLowAmbientBrightnessThresholds; return mLowAmbientBrightnessThresholds;
} }
public void registerLightSensor(SensorManager sensorManager, Sensor lightSensor) {
mSensorManager = sensorManager;
mLightSensor = lightSensor;
mSensorManager.registerListener(mLightSensorListener,
mLightSensor, LIGHT_SENSOR_RATE_MS * 1000, mHandler);
}
public void observe(SensorManager sensorManager) { public void observe(SensorManager sensorManager) {
mSensorManager = sensorManager; mSensorManager = sensorManager;
final ContentResolver cr = mContext.getContentResolver();
mBrightness = getBrightness(Display.DEFAULT_DISPLAY); mBrightness = getBrightness(Display.DEFAULT_DISPLAY);
// DeviceConfig is accessible after system ready. // DeviceConfig is accessible after system ready.
@@ -2053,6 +2053,10 @@ public class DisplayModeDirector {
pw.println(" mAmbientHighBrightnessThresholds: " + d); pw.println(" mAmbientHighBrightnessThresholds: " + d);
} }
pw.println(" mRegisteredLightSensor: " + mRegisteredLightSensor);
pw.println(" mLightSensor: " + mLightSensor);
pw.println(" mLightSensorName: " + mLightSensorName);
pw.println(" mLightSensorType: " + mLightSensorType);
mLightSensorListener.dumpLocked(pw); mLightSensorListener.dumpLocked(pw);
if (mAmbientFilter != null) { if (mAmbientFilter != null) {
@@ -2106,27 +2110,9 @@ public class DisplayModeDirector {
} }
if (mShouldObserveAmbientLowChange || mShouldObserveAmbientHighChange) { if (mShouldObserveAmbientLowChange || mShouldObserveAmbientHighChange) {
Resources resources = mContext.getResources(); Sensor lightSensor = getLightSensor();
String lightSensorType = resources.getString(
com.android.internal.R.string.config_displayLightSensorType);
Sensor lightSensor = null; if (lightSensor != null && lightSensor != mLightSensor) {
if (!TextUtils.isEmpty(lightSensorType)) {
List<Sensor> sensors = mSensorManager.getSensorList(Sensor.TYPE_ALL);
for (int i = 0; i < sensors.size(); i++) {
Sensor sensor = sensors.get(i);
if (lightSensorType.equals(sensor.getStringType())) {
lightSensor = sensor;
break;
}
}
}
if (lightSensor == null) {
lightSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
}
if (lightSensor != null) {
final Resources res = mContext.getResources(); final Resources res = mContext.getResources();
mAmbientFilter = AmbientFilterFactory.createBrightnessFilter(TAG, res); mAmbientFilter = AmbientFilterFactory.createBrightnessFilter(TAG, res);
@@ -2137,14 +2123,40 @@ public class DisplayModeDirector {
mLightSensor = null; mLightSensor = null;
} }
if (mRefreshRateChangeable) {
updateSensorStatus(); updateSensorStatus();
if (mRefreshRateChangeable) {
synchronized (mLock) { synchronized (mLock) {
onBrightnessChangedLocked(); onBrightnessChangedLocked();
} }
} }
} }
private void reloadLightSensor(DisplayDeviceConfig displayDeviceConfig) {
reloadLightSensorData(displayDeviceConfig);
restartObserver();
}
private void reloadLightSensorData(DisplayDeviceConfig displayDeviceConfig) {
// The displayDeviceConfig (ddc) contains display specific preferences. When loaded,
// it naturally falls back to the global config.xml.
if (displayDeviceConfig != null
&& displayDeviceConfig.getAmbientLightSensor() != null) {
// This covers both the ddc and the config.xml fallback
mLightSensorType = displayDeviceConfig.getAmbientLightSensor().type;
mLightSensorName = displayDeviceConfig.getAmbientLightSensor().name;
} else if (mLightSensorName == null && mLightSensorType == null) {
Resources resources = mContext.getResources();
mLightSensorType = resources.getString(
com.android.internal.R.string.config_displayLightSensorType);
mLightSensorName = "";
}
}
private Sensor getLightSensor() {
return SensorUtils.findSensor(mSensorManager, mLightSensorType,
mLightSensorName, Sensor.TYPE_LIGHT);
}
/** /**
* Checks to see if at least one value is positive, in which case it is necessary to listen * Checks to see if at least one value is positive, in which case it is necessary to listen
* to value changes. * to value changes.
@@ -2288,19 +2300,38 @@ public class DisplayModeDirector {
if ((mShouldObserveAmbientLowChange || mShouldObserveAmbientHighChange) if ((mShouldObserveAmbientLowChange || mShouldObserveAmbientHighChange)
&& isDeviceActive() && !mLowPowerModeEnabled && mRefreshRateChangeable) { && isDeviceActive() && !mLowPowerModeEnabled && mRefreshRateChangeable) {
registerLightSensor();
} else {
unregisterSensorListener();
}
}
private void registerLightSensor() {
if (mRegisteredLightSensor == mLightSensor) {
return;
}
if (mRegisteredLightSensor != null) {
unregisterSensorListener();
}
mSensorManager.registerListener(mLightSensorListener, mSensorManager.registerListener(mLightSensorListener,
mLightSensor, LIGHT_SENSOR_RATE_MS * 1000, mHandler); mLightSensor, LIGHT_SENSOR_RATE_MS * 1000, mHandler);
mRegisteredLightSensor = mLightSensor;
if (mLoggingEnabled) { if (mLoggingEnabled) {
Slog.d(TAG, "updateSensorStatus: registerListener"); Slog.d(TAG, "updateSensorStatus: registerListener");
} }
} else { }
private void unregisterSensorListener() {
mLightSensorListener.removeCallbacks(); mLightSensorListener.removeCallbacks();
mSensorManager.unregisterListener(mLightSensorListener); mSensorManager.unregisterListener(mLightSensorListener);
mRegisteredLightSensor = null;
if (mLoggingEnabled) { if (mLoggingEnabled) {
Slog.d(TAG, "updateSensorStatus: unregisterListener"); Slog.d(TAG, "updateSensorStatus: unregisterListener");
} }
} }
}
private boolean isDeviceActive() { private boolean isDeviceActive() {
return mDefaultDisplayState == Display.STATE_ON; return mDefaultDisplayState == Display.STATE_ON;

View File

@@ -33,6 +33,10 @@ public class SensorUtils {
*/ */
public static Sensor findSensor(SensorManager sensorManager, String sensorType, public static Sensor findSensor(SensorManager sensorManager, String sensorType,
String sensorName, int fallbackType) { String sensorName, int fallbackType) {
if (sensorManager == null) {
return null;
}
if ("".equals(sensorName) && "".equals(sensorType)) { if ("".equals(sensorName) && "".equals(sensorType)) {
return null; return null;
} }

View File

@@ -40,6 +40,7 @@ import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.any; import static org.mockito.Mockito.any;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never; import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy; import static org.mockito.Mockito.spy;
@@ -73,6 +74,7 @@ import android.provider.Settings;
import android.test.mock.MockContentResolver; import android.test.mock.MockContentResolver;
import android.util.Slog; import android.util.Slog;
import android.util.SparseArray; import android.util.SparseArray;
import android.util.TypedValue;
import android.view.Display; import android.view.Display;
import android.view.SurfaceControl.RefreshRateRange; import android.view.SurfaceControl.RefreshRateRange;
import android.view.SurfaceControl.RefreshRateRanges; import android.view.SurfaceControl.RefreshRateRanges;
@@ -102,6 +104,7 @@ import org.mockito.ArgumentCaptor;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.Mockito; import org.mockito.Mockito;
import org.mockito.MockitoAnnotations; import org.mockito.MockitoAnnotations;
import org.mockito.stubbing.Answer;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
@@ -2293,6 +2296,61 @@ public class DisplayModeDirectorTest {
new int[]{20}); new int[]{20});
} }
@Test
public void testSensorReloadOnDeviceSwitch() throws Exception {
// First, configure brightness zones or DMD won't register for sensor data.
final FakeDeviceConfig config = mInjector.getDeviceConfig();
config.setRefreshRateInHighZone(60);
config.setHighDisplayBrightnessThresholds(new int[] { 255 });
config.setHighAmbientBrightnessThresholds(new int[] { 8000 });
DisplayModeDirector director =
createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0);
setPeakRefreshRate(90 /*fps*/);
director.getSettingsObserver().setDefaultRefreshRate(90);
director.getBrightnessObserver().setDefaultDisplayState(Display.STATE_ON);
Sensor lightSensorOne = TestUtils.createSensor(Sensor.TYPE_LIGHT, Sensor.STRING_TYPE_LIGHT);
Sensor lightSensorTwo = TestUtils.createSensor(Sensor.TYPE_LIGHT, Sensor.STRING_TYPE_LIGHT);
SensorManager sensorManager = createMockSensorManager(lightSensorOne, lightSensorTwo);
when(sensorManager.getDefaultSensor(5)).thenReturn(lightSensorOne, lightSensorTwo);
director.start(sensorManager);
ArgumentCaptor<SensorEventListener> listenerCaptor =
ArgumentCaptor.forClass(SensorEventListener.class);
verify(sensorManager, Mockito.timeout(TimeUnit.SECONDS.toMillis(1)))
.registerListener(
listenerCaptor.capture(),
eq(lightSensorOne),
anyInt(),
any(Handler.class));
DisplayDeviceConfig ddcMock = mock(DisplayDeviceConfig.class);
when(ddcMock.getDefaultLowRefreshRate()).thenReturn(50);
when(ddcMock.getDefaultHighRefreshRate()).thenReturn(55);
when(ddcMock.getLowDisplayBrightnessThresholds()).thenReturn(new int[]{25});
when(ddcMock.getLowAmbientBrightnessThresholds()).thenReturn(new int[]{30});
when(ddcMock.getHighDisplayBrightnessThresholds()).thenReturn(new int[]{210});
when(ddcMock.getHighAmbientBrightnessThresholds()).thenReturn(new int[]{2100});
Resources resMock = mock(Resources.class);
when(resMock.getInteger(
com.android.internal.R.integer.config_displayWhiteBalanceBrightnessFilterHorizon))
.thenReturn(3);
ArgumentCaptor<TypedValue> valueArgumentCaptor = ArgumentCaptor.forClass(TypedValue.class);
doAnswer((Answer<Void>) invocation -> {
valueArgumentCaptor.getValue().type = 4;
valueArgumentCaptor.getValue().data = 13;
return null;
}).when(resMock).getValue(anyInt(), valueArgumentCaptor.capture(), eq(true));
when(mContext.getResources()).thenReturn(resMock);
director.defaultDisplayDeviceUpdated(ddcMock);
verify(sensorManager).unregisterListener(any(SensorEventListener.class));
verify(sensorManager).registerListener(any(SensorEventListener.class),
eq(lightSensorTwo), anyInt(), any(Handler.class));
}
private Temperature getSkinTemp(@Temperature.ThrottlingStatus int status) { private Temperature getSkinTemp(@Temperature.ThrottlingStatus int status) {
return new Temperature(30.0f, Temperature.TYPE_SKIN, "test_skin_temp", status); return new Temperature(30.0f, Temperature.TYPE_SKIN, "test_skin_temp", status);
} }