From 8a3115ba6e6735ff78be48b8665484d0cd5bddfa Mon Sep 17 00:00:00 2001 From: Santos Cordon Date: Tue, 18 May 2021 13:27:36 +0100 Subject: [PATCH] Specify refresh rate limits for sensors in config. Some sensors can limit the range of supported refresh rate when enabled. This change adds a way to specify that limit withing the display device config file. Bug: 175793106 Test: Verify that when sensor is enabled, there is an additional refresh rate vote for the rate specified in config file. Test: atest DisplayModeDirectorTest, LocalDisplayAdapterTest Change-Id: Id210c25fe56cd5353a2436b9b97f2ad01980e465 --- .../display/DisplayManagerInternal.java | 86 +++++++++++ .../server/display/DisplayDeviceConfig.java | 60 ++++++-- .../server/display/DisplayManagerService.java | 38 +++++ .../server/display/DisplayModeDirector.java | 142 ++++++++++-------- .../display/DisplayPowerController.java | 44 ++---- .../server/display/utils/SensorUtils.java | 54 +++++++ ...om_android_server_sensor_SensorService.cpp | 8 +- services/core/jni/onload.cpp | 4 +- .../display-device-config.xsd | 20 ++- .../display-device-config/schema/current.txt | 10 ++ .../display/LocalDisplayAdapterTest.java | 9 +- .../display/DisplayManagerServiceTest.java | 4 + .../display/DisplayModeDirectorTest.java | 45 +++++- 13 files changed, 401 insertions(+), 123 deletions(-) create mode 100644 services/core/java/com/android/server/display/utils/SensorUtils.java diff --git a/core/java/android/hardware/display/DisplayManagerInternal.java b/core/java/android/hardware/display/DisplayManagerInternal.java index c739c6a53167b..a39bc4e0bee55 100644 --- a/core/java/android/hardware/display/DisplayManagerInternal.java +++ b/core/java/android/hardware/display/DisplayManagerInternal.java @@ -22,12 +22,15 @@ import android.hardware.SensorManager; import android.os.Handler; import android.os.PowerManager; import android.util.IntArray; +import android.util.Slog; import android.util.SparseArray; import android.view.Display; import android.view.DisplayInfo; import android.view.SurfaceControl; import android.view.SurfaceControl.Transaction; +import java.util.Objects; + /** * Display manager local system service interface. * @@ -292,6 +295,23 @@ public abstract class DisplayManagerInternal { @DisplayManager.SwitchingType public abstract int getRefreshRateSwitchingType(); + /** + * Return the refresh rate restriction for the specified display and sensor pairing. If the + * specified sensor is identified as an associated sensor in the specified display's + * display-device-config file, then return any refresh rate restrictions that it might specify. + * If no restriction is specified, or the sensor is not associated with the display, then null + * will be returned. + * + * @param displayId The display to check against. + * @param name The name of the sensor. + * @param type The type of sensor. + * + * @return The min/max refresh-rate restriction as a {@link Pair} of floats, or null if not + * restricted. + */ + public abstract RefreshRateRange getRefreshRateForDisplayAndSensor( + int displayId, String name, String type); + /** * Describes the requested power state of the display. * @@ -527,4 +547,70 @@ public abstract class DisplayManagerInternal { */ void onDisplayGroupChanged(int groupId); } + + /** + * Information about the min and max refresh rate DM would like to set the display to. + */ + public static final class RefreshRateRange { + public static final String TAG = "RefreshRateRange"; + + // The tolerance within which we consider something approximately equals. + public static final float FLOAT_TOLERANCE = 0.01f; + + /** + * The lowest desired refresh rate. + */ + public float min; + + /** + * The highest desired refresh rate. + */ + public float max; + + public RefreshRateRange() {} + + public RefreshRateRange(float min, float max) { + if (min < 0 || max < 0 || min > max + FLOAT_TOLERANCE) { + Slog.e(TAG, "Wrong values for min and max when initializing RefreshRateRange : " + + min + " " + max); + this.min = this.max = 0; + return; + } + if (min > max) { + // Min and max are within epsilon of each other, but in the wrong order. + float t = min; + min = max; + max = t; + } + this.min = min; + this.max = max; + } + + /** + * Checks whether the two objects have the same values. + */ + @Override + public boolean equals(Object other) { + if (other == this) { + return true; + } + + if (!(other instanceof RefreshRateRange)) { + return false; + } + + RefreshRateRange refreshRateRange = (RefreshRateRange) other; + return (min == refreshRateRange.min && max == refreshRateRange.max); + } + + @Override + public int hashCode() { + return Objects.hash(min, max); + } + + @Override + public String toString() { + return "(" + min + " " + max + ")"; + } + } } diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java index 170564d145dcc..8a8b74e405689 100644 --- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java +++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java @@ -21,6 +21,7 @@ import android.content.Context; import android.content.res.Resources; import android.os.Environment; import android.os.PowerManager; +import android.text.TextUtils; import android.util.MathUtils; import android.util.Slog; import android.util.Spline; @@ -34,6 +35,7 @@ import com.android.server.display.config.HbmTiming; import com.android.server.display.config.HighBrightnessMode; import com.android.server.display.config.NitsMap; import com.android.server.display.config.Point; +import com.android.server.display.config.RefreshRateRange; import com.android.server.display.config.SensorDetails; import com.android.server.display.config.XmlParser; @@ -79,10 +81,10 @@ public class DisplayDeviceConfig { private final Context mContext; // The details of the ambient light sensor associated with this display. - private final SensorIdentifier mAmbientLightSensor = new SensorIdentifier(); + private final SensorData mAmbientLightSensor = new SensorData(); // The details of the proximity sensor associated with this display. - private final SensorIdentifier mProximitySensor = new SensorIdentifier(); + private final SensorData mProximitySensor = new SensorData(); // Nits and backlight values that are loaded from either the display device config file, or // config.xml. These are the raw values and just used for the dumpsys @@ -113,6 +115,7 @@ public class DisplayDeviceConfig { private List mQuirks; private boolean mIsHighBrightnessModeEnabled = false; private HighBrightnessModeData mHbmData; + private String mLoadedFrom = null; private DisplayDeviceConfig(Context context) { mContext = context; @@ -273,11 +276,11 @@ public class DisplayDeviceConfig { return mBrightnessRampSlowIncrease; } - SensorIdentifier getAmbientLightSensor() { + SensorData getAmbientLightSensor() { return mAmbientLightSensor; } - SensorIdentifier getProximitySensor() { + SensorData getProximitySensor() { return mProximitySensor; } @@ -306,7 +309,8 @@ public class DisplayDeviceConfig { @Override public String toString() { String str = "DisplayDeviceConfig{" - + "mBacklight=" + Arrays.toString(mBacklight) + + "mLoadedFrom=" + mLoadedFrom + + ", mBacklight=" + Arrays.toString(mBacklight) + ", mNits=" + Arrays.toString(mNits) + ", mRawBacklight=" + Arrays.toString(mRawBacklight) + ", mRawNits=" + Arrays.toString(mRawNits) @@ -336,9 +340,8 @@ public class DisplayDeviceConfig { final String filename = String.format(CONFIG_FILE_FORMAT, suffix); final File filePath = Environment.buildPath( baseDirectory, ETC_DIR, DISPLAY_CONFIG_DIR, filename); - if (filePath.exists()) { - final DisplayDeviceConfig config = new DisplayDeviceConfig(context); - config.initFromFile(filePath); + final DisplayDeviceConfig config = new DisplayDeviceConfig(context); + if (config.initFromFile(filePath)) { return config; } return null; @@ -356,15 +359,15 @@ public class DisplayDeviceConfig { return config; } - private void initFromFile(File configFile) { + private boolean initFromFile(File configFile) { if (!configFile.exists()) { // Display configuration files aren't required to exist. - return; + return false; } if (!configFile.isFile()) { Slog.e(TAG, "Display configuration is not a file: " + configFile + ", skipping"); - return; + return false; } try (InputStream in = new BufferedInputStream(new FileInputStream(configFile))) { @@ -385,6 +388,8 @@ public class DisplayDeviceConfig { Slog.e(TAG, "Encountered an error while reading/parsing display config file: " + configFile, e); } + mLoadedFrom = configFile.toString(); + return true; } private void initFromGlobalXml() { @@ -395,10 +400,12 @@ public class DisplayDeviceConfig { loadBrightnessRampsFromConfigXml(); loadAmbientLightSensorFromConfigXml(); setProxSensorUnspecified(); + mLoadedFrom = ""; } private void initFromDefaultValues() { // Set all to basic values + mLoadedFrom = "Static values"; mBacklightMinimum = PowerManager.BRIGHTNESS_MIN; mBacklightMaximum = PowerManager.BRIGHTNESS_MAX; mBrightnessDefault = BRIGHTNESS_DEFAULT; @@ -689,6 +696,11 @@ public class DisplayDeviceConfig { if (sensorDetails != null) { mAmbientLightSensor.type = sensorDetails.getType(); mAmbientLightSensor.name = sensorDetails.getName(); + final RefreshRateRange rr = sensorDetails.getRefreshRate(); + if (rr != null) { + mAmbientLightSensor.minRefreshRate = rr.getMinimum().floatValue(); + mAmbientLightSensor.maxRefreshRate = rr.getMaximum().floatValue(); + } } else { loadAmbientLightSensorFromConfigXml(); } @@ -704,22 +716,42 @@ public class DisplayDeviceConfig { if (sensorDetails != null) { mProximitySensor.name = sensorDetails.getName(); mProximitySensor.type = sensorDetails.getType(); + final RefreshRateRange rr = sensorDetails.getRefreshRate(); + if (rr != null) { + mProximitySensor.minRefreshRate = rr.getMinimum().floatValue(); + mProximitySensor.maxRefreshRate = rr.getMaximum().floatValue(); + } } else { setProxSensorUnspecified(); } } - static class SensorIdentifier { + static class SensorData { public String type; public String name; + public float minRefreshRate = 0.0f; + public float maxRefreshRate = Float.POSITIVE_INFINITY; @Override public String toString() { return "Sensor{" - + "type: \"" + type + "\"" - + ", name: \"" + name + "\"" + + "type: " + type + + ", name: " + name + + ", refreshRateRange: [" + minRefreshRate + ", " + maxRefreshRate + "]" + "} "; } + + /** + * @return True if the sensor matches both the specified name and type, or one if only + * one is specified (not-empty). Always returns false if both parameters are null or empty. + */ + public boolean matches(String sensorName, String sensorType) { + final boolean isNameSpecified = !TextUtils.isEmpty(sensorName); + final boolean isTypeSpecified = !TextUtils.isEmpty(sensorType); + return (isNameSpecified || isTypeSpecified) + && (!isNameSpecified || sensorName.equals(name)) + && (!isTypeSpecified || sensorType.equals(type)); + } } /** diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index fe5ee78bca316..bff39a9321056 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -50,6 +50,7 @@ import android.content.res.TypedArray; import android.database.ContentObserver; import android.graphics.ColorSpace; import android.graphics.Point; +import android.hardware.Sensor; import android.hardware.SensorManager; import android.hardware.devicestate.DeviceStateManager; import android.hardware.display.AmbientBrightnessDayStats; @@ -62,6 +63,7 @@ import android.hardware.display.DisplayManagerGlobal; import android.hardware.display.DisplayManagerInternal; import android.hardware.display.DisplayManagerInternal.DisplayGroupListener; import android.hardware.display.DisplayManagerInternal.DisplayTransactionListener; +import android.hardware.display.DisplayManagerInternal.RefreshRateRange; import android.hardware.display.DisplayViewport; import android.hardware.display.DisplayedContentSample; import android.hardware.display.DisplayedContentSamplingAttributes; @@ -119,6 +121,8 @@ import com.android.server.DisplayThread; import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.UiThread; +import com.android.server.display.DisplayDeviceConfig.SensorData; +import com.android.server.display.utils.SensorUtils; import com.android.server.wm.SurfaceAnimationThread; import com.android.server.wm.WindowManagerInternal; @@ -3257,6 +3261,40 @@ public final class DisplayManagerService extends SystemService { public int getRefreshRateSwitchingType() { return getRefreshRateSwitchingTypeInternal(); } + + @Override + public RefreshRateRange getRefreshRateForDisplayAndSensor(int displayId, String sensorName, + String sensorType) { + final SensorManager sensorManager; + synchronized (mSyncRoot) { + sensorManager = mSensorManager; + } + if (sensorManager == null) { + return null; + } + + // Verify that the specified sensor exists. + final Sensor sensor = SensorUtils.findSensor(sensorManager, sensorType, sensorName, + SensorUtils.NO_FALLBACK); + if (sensor == null) { + return null; + } + + synchronized (mSyncRoot) { + final LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(displayId); + final DisplayDevice device = display.getPrimaryDisplayDeviceLocked(); + if (device == null) { + return null; + } + final DisplayDeviceConfig config = device.getDisplayDeviceConfig(); + SensorData sensorData = config.getProximitySensor(); + if (sensorData.matches(sensorName, sensorType)) { + return new RefreshRateRange(sensorData.minRefreshRate, + sensorData.maxRefreshRate); + } + } + return null; + } } class DesiredDisplayModeSpecsObserver diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java index 07d13c251f269..2364a3c7c2ed4 100644 --- a/services/core/java/com/android/server/display/DisplayModeDirector.java +++ b/services/core/java/com/android/server/display/DisplayModeDirector.java @@ -27,6 +27,8 @@ import android.hardware.SensorEvent; import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.hardware.display.DisplayManager; +import android.hardware.display.DisplayManagerInternal; +import android.hardware.display.DisplayManagerInternal.RefreshRateRange; import android.hardware.fingerprint.IUdfpsHbmListener; import android.net.Uri; import android.os.Handler; @@ -51,6 +53,8 @@ import com.android.internal.os.BackgroundThread; import com.android.server.LocalServices; import com.android.server.display.utils.AmbientFilter; import com.android.server.display.utils.AmbientFilterFactory; +import com.android.server.sensors.SensorManagerInternal; +import com.android.server.sensors.SensorManagerInternal.ProximityActiveListener; import com.android.server.statusbar.StatusBarManagerInternal; import com.android.server.utils.DeviceConfigInterface; @@ -85,8 +89,7 @@ public class DisplayModeDirector { private static final int INVALID_DISPLAY_MODE_ID = -1; - // The tolerance within which we consider something approximately equals. - private static final float FLOAT_TOLERANCE = 0.01f; + private static final float FLOAT_TOLERANCE = RefreshRateRange.FLOAT_TOLERANCE; private final Object mLock = new Object(); private final Context mContext; @@ -98,6 +101,7 @@ public class DisplayModeDirector { private final SettingsObserver mSettingsObserver; private final DisplayObserver mDisplayObserver; private final UdfpsObserver mUdfpsObserver; + private final SensorObserver mSensorObserver; private final DeviceConfigInterface mDeviceConfig; private final DeviceConfigDisplaySettings mDeviceConfigDisplaySettings; @@ -139,6 +143,11 @@ public class DisplayModeDirector { mDisplayObserver = new DisplayObserver(context, handler); mBrightnessObserver = new BrightnessObserver(context, handler); mUdfpsObserver = new UdfpsObserver(); + mSensorObserver = new SensorObserver(context, (displayId, priority, vote) -> { + synchronized (mLock) { + updateVoteLocked(displayId, priority, vote); + } + }); mDeviceConfigDisplaySettings = new DeviceConfigDisplaySettings(); mDeviceConfig = injector.getDeviceConfig(); mAlwaysRespectAppRequest = false; @@ -155,6 +164,7 @@ public class DisplayModeDirector { mSettingsObserver.observe(); mDisplayObserver.observe(); mBrightnessObserver.observe(sensorManager); + mSensorObserver.observe(); synchronized (mLock) { // We may have a listener already registered before the call to start, so go ahead and // notify them to pick up our newly initialized state. @@ -585,6 +595,7 @@ public class DisplayModeDirector { mAppRequestObserver.dumpLocked(pw); mBrightnessObserver.dumpLocked(pw); mUdfpsObserver.dumpLocked(pw); + mSensorObserver.dumpLocked(pw); } } @@ -767,66 +778,6 @@ public class DisplayModeDirector { } } - /** - * Information about the min and max refresh rate DM would like to set the display to. - */ - public static final class RefreshRateRange { - /** - * The lowest desired refresh rate. - */ - public float min; - /** - * The highest desired refresh rate. - */ - public float max; - - public RefreshRateRange() {} - - public RefreshRateRange(float min, float max) { - if (min < 0 || max < 0 || min > max + FLOAT_TOLERANCE) { - Slog.e(TAG, "Wrong values for min and max when initializing RefreshRateRange : " - + min + " " + max); - this.min = this.max = 0; - return; - } - if (min > max) { - // Min and max are within epsilon of each other, but in the wrong order. - float t = min; - min = max; - max = t; - } - this.min = min; - this.max = max; - } - - /** - * Checks whether the two objects have the same values. - */ - @Override - public boolean equals(Object other) { - if (other == this) { - return true; - } - - if (!(other instanceof RefreshRateRange)) { - return false; - } - - RefreshRateRange refreshRateRange = (RefreshRateRange) other; - return (min == refreshRateRange.min && max == refreshRateRange.max); - } - - @Override - public int hashCode() { - return Objects.hash(min, max); - } - - @Override - public String toString() { - return "(" + min + " " + max + ")"; - } - } - /** * Information about the desired display mode to be set by the system. Includes the base * mode ID and the primary and app request refresh rate ranges. @@ -987,9 +938,13 @@ public class DisplayModeDirector { // user seeing the display flickering when the switches occur. public static final int PRIORITY_FLICKER_REFRESH_RATE_SWITCH = 8; + // The proximity sensor needs the refresh rate to be locked in order to function, so this is + // set to a high priority. + public static final int PRIORITY_PROXIMITY = 9; + // The Under-Display Fingerprint Sensor (UDFPS) needs the refresh rate to be locked in order // to function, so this needs to be the highest priority of all votes. - public static final int PRIORITY_UDFPS = 9; + public static final int PRIORITY_UDFPS = 10; // Whenever a new priority is added, remember to update MIN_PRIORITY, MAX_PRIORITY, and // APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF, as well as priorityToString. @@ -1086,6 +1041,8 @@ public class DisplayModeDirector { return "PRIORITY_LOW_POWER_MODE"; case PRIORITY_UDFPS: return "PRIORITY_UDFPS"; + case PRIORITY_PROXIMITY: + return "PRIORITY_PROXIMITY"; default: return Integer.toString(priority); @@ -2142,6 +2099,62 @@ public class DisplayModeDirector { } } + private static class SensorObserver implements ProximityActiveListener { + private static final String PROXIMITY_SENSOR_NAME = null; + private static final String PROXIMITY_SENSOR_TYPE = Sensor.STRING_TYPE_PROXIMITY; + + private final BallotBox mBallotBox; + private final Context mContext; + + private DisplayManager mDisplayManager; + private DisplayManagerInternal mDisplayManagerInternal; + private boolean mIsProxActive = false; + + SensorObserver(Context context, BallotBox ballotBox) { + mContext = context; + mBallotBox = ballotBox; + } + + @Override + public void onProximityActive(boolean isActive) { + if (mIsProxActive != isActive) { + mIsProxActive = isActive; + recalculateVotes(); + } + } + + public void observe() { + mDisplayManager = mContext.getSystemService(DisplayManager.class); + mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class); + + final SensorManagerInternal sensorManager = + LocalServices.getService(SensorManagerInternal.class); + sensorManager.addProximityActiveListener(BackgroundThread.getExecutor(), this); + } + + private void recalculateVotes() { + final Display[] displays = mDisplayManager.getDisplays(); + for (Display d : displays) { + int displayId = d.getDisplayId(); + Vote vote = null; + if (mIsProxActive) { + final RefreshRateRange rate = + mDisplayManagerInternal.getRefreshRateForDisplayAndSensor( + displayId, PROXIMITY_SENSOR_NAME, PROXIMITY_SENSOR_TYPE); + if (rate != null) { + vote = Vote.forRefreshRates(rate.min, rate.max); + } + } + mBallotBox.vote(displayId, Vote.PRIORITY_PROXIMITY, vote); + } + } + + void dumpLocked(PrintWriter pw) { + pw.println(" SensorObserver"); + pw.println(" mIsProxActive=" + mIsProxActive); + } + } + private class DeviceConfigDisplaySettings implements DeviceConfig.OnPropertiesChangedListener { public DeviceConfigDisplaySettings() { } @@ -2328,4 +2341,7 @@ public class DisplayModeDirector { } } + interface BallotBox { + void vote(int displayId, int priority, Vote vote); + } } diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index 1b50f0388f9dc..555add4b027f2 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -48,7 +48,6 @@ import android.os.SystemProperties; import android.os.Trace; import android.os.UserHandle; import android.provider.Settings; -import android.text.TextUtils; import android.util.Log; import android.util.MathUtils; import android.util.Slog; @@ -65,13 +64,13 @@ import com.android.server.am.BatteryStatsService; import com.android.server.display.RampAnimator.DualRampAnimator; import com.android.server.display.color.ColorDisplayService.ColorDisplayServiceInternal; import com.android.server.display.color.ColorDisplayService.ReduceBrightColorsListener; +import com.android.server.display.utils.SensorUtils; import com.android.server.display.whitebalance.DisplayWhiteBalanceController; import com.android.server.display.whitebalance.DisplayWhiteBalanceFactory; import com.android.server.display.whitebalance.DisplayWhiteBalanceSettings; import com.android.server.policy.WindowManagerPolicy; import java.io.PrintWriter; -import java.util.List; /** * Controls the power state of the display. @@ -586,26 +585,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mBrightnessMapper.recalculateSplines(mCdsi.isReduceBrightColorsActivated(), adjustedNits); } - private Sensor findSensor(String sensorType, String sensorName, int fallbackType, - boolean useFallback) { - final boolean isNameSpecified = !TextUtils.isEmpty(sensorName); - final boolean isTypeSpecified = !TextUtils.isEmpty(sensorType); - List sensors = mSensorManager.getSensorList(Sensor.TYPE_ALL); - if (isNameSpecified || isTypeSpecified) { - for (Sensor sensor : sensors) { - if ((!isNameSpecified || sensorName.equals(sensor.getName())) - && (!isTypeSpecified || sensorType.equals(sensor.getStringType()))) { - return sensor; - } - } - } - if (useFallback) { - return mSensorManager.getDefaultSensor(fallbackType); - } else { - return null; - } - } - /** * Returns true if the proximity sensor screen-off function is available. */ @@ -1654,24 +1633,23 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call } private void loadAmbientLightSensor() { - DisplayDeviceConfig.SensorIdentifier lightSensor = - mDisplayDeviceConfig.getAmbientLightSensor(); - String lightSensorName = lightSensor.name; - String lightSensorType = lightSensor.type; - mLightSensor = findSensor(lightSensorType, lightSensorName, Sensor.TYPE_LIGHT, - mDisplayId == Display.DEFAULT_DISPLAY); + DisplayDeviceConfig.SensorData lightSensor = mDisplayDeviceConfig.getAmbientLightSensor(); + final int fallbackType = mDisplayId == Display.DEFAULT_DISPLAY + ? Sensor.TYPE_LIGHT : SensorUtils.NO_FALLBACK; + mLightSensor = SensorUtils.findSensor(mSensorManager, lightSensor.type, lightSensor.name, + fallbackType); } private void loadProximitySensor() { if (DEBUG_PRETEND_PROXIMITY_SENSOR_ABSENT) { return; } - final DisplayDeviceConfig.SensorIdentifier proxSensor = + final DisplayDeviceConfig.SensorData proxSensor = mDisplayDeviceConfig.getProximitySensor(); - final String proxSensorName = proxSensor.name; - final String proxSensorType = proxSensor.type; - mProximitySensor = findSensor(proxSensorType, proxSensorName, Sensor.TYPE_PROXIMITY, - mDisplayId == Display.DEFAULT_DISPLAY); + final int fallbackType = mDisplayId == Display.DEFAULT_DISPLAY + ? Sensor.TYPE_PROXIMITY : SensorUtils.NO_FALLBACK; + mProximitySensor = SensorUtils.findSensor(mSensorManager, proxSensor.type, proxSensor.name, + fallbackType); if (mProximitySensor != null) { mProximityThreshold = Math.min(mProximitySensor.getMaximumRange(), TYPICAL_PROXIMITY_THRESHOLD); diff --git a/services/core/java/com/android/server/display/utils/SensorUtils.java b/services/core/java/com/android/server/display/utils/SensorUtils.java new file mode 100644 index 0000000000000..cb40b406899f6 --- /dev/null +++ b/services/core/java/com/android/server/display/utils/SensorUtils.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.display.utils; + +import android.hardware.Sensor; +import android.hardware.SensorManager; +import android.text.TextUtils; + +import java.util.List; + +/** + * Provides utility methods for dealing with sensors. + */ +public class SensorUtils { + public static final int NO_FALLBACK = 0; + + /** + * Finds the specified sensor by type and name using SensorManager. + */ + public static Sensor findSensor(SensorManager sensorManager, String sensorType, + String sensorName, int fallbackType) { + final boolean isNameSpecified = !TextUtils.isEmpty(sensorName); + final boolean isTypeSpecified = !TextUtils.isEmpty(sensorType); + if (isNameSpecified || isTypeSpecified) { + final List sensors = sensorManager.getSensorList(Sensor.TYPE_ALL); + for (Sensor sensor : sensors) { + if ((!isNameSpecified || sensorName.equals(sensor.getName())) + && (!isTypeSpecified || sensorType.equals(sensor.getStringType()))) { + return sensor; + } + } + } + if (fallbackType != NO_FALLBACK) { + return sensorManager.getDefaultSensor(fallbackType); + } + + return null; + } + +} diff --git a/services/core/jni/com_android_server_sensor_SensorService.cpp b/services/core/jni/com_android_server_sensor_SensorService.cpp index d0f56e62f487f..63b7dfbc2a3b3 100644 --- a/services/core/jni/com_android_server_sensor_SensorService.cpp +++ b/services/core/jni/com_android_server_sensor_SensorService.cpp @@ -32,6 +32,7 @@ namespace android { +static JavaVM* sJvm = nullptr; static jmethodID sMethodIdOnProximityActive; class NativeSensorService { @@ -93,8 +94,8 @@ NativeSensorService::ProximityActiveListenerDelegate::~ProximityActiveListenerDe } void NativeSensorService::ProximityActiveListenerDelegate::onProximityActive(bool isActive) { - AndroidRuntime::getJNIEnv()->CallVoidMethod(mListener, sMethodIdOnProximityActive, - static_cast(isActive)); + auto jniEnv = GetOrAttachJNIEnvironment(sJvm); + jniEnv->CallVoidMethod(mListener, sMethodIdOnProximityActive, static_cast(isActive)); } static jlong startSensorServiceNative(JNIEnv* env, jclass, jobject listener) { @@ -128,7 +129,8 @@ static const JNINativeMethod methods[] = { }; -int register_android_server_sensor_SensorService(JNIEnv* env) { +int register_android_server_sensor_SensorService(JavaVM* vm, JNIEnv* env) { + sJvm = vm; jclass listenerClass = FindClassOrDie(env, PROXIMITY_ACTIVE_CLASS); sMethodIdOnProximityActive = GetMethodIDOrDie(env, listenerClass, "onProximityActive", "(Z)V"); return jniRegisterNativeMethods(env, "com/android/server/sensors/SensorService", methods, diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp index b8961d5ba9b0e..ff61abc4ff7fe 100644 --- a/services/core/jni/onload.cpp +++ b/services/core/jni/onload.cpp @@ -62,7 +62,7 @@ int register_android_server_AdbDebuggingManager(JNIEnv* env); int register_android_server_FaceService(JNIEnv* env); int register_android_server_GpuService(JNIEnv* env); int register_android_server_stats_pull_StatsPullAtomService(JNIEnv* env); -int register_android_server_sensor_SensorService(JNIEnv* env); +int register_android_server_sensor_SensorService(JavaVM* vm, JNIEnv* env); }; using namespace android; @@ -118,6 +118,6 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */) register_android_server_FaceService(env); register_android_server_GpuService(env); register_android_server_stats_pull_StatsPullAtomService(env); - register_android_server_sensor_SensorService(env); + register_android_server_sensor_SensorService(vm, env); return JNI_VERSION_1_4; } diff --git a/services/core/xsd/display-device-config/display-device-config.xsd b/services/core/xsd/display-device-config/display-device-config.xsd index c6dfe9dbbfc26..3e2e37c834f11 100644 --- a/services/core/xsd/display-device-config/display-device-config.xsd +++ b/services/core/xsd/display-device-config/display-device-config.xsd @@ -129,14 +129,30 @@ - + - + + + + + + + + + + + + + + + + + diff --git a/services/core/xsd/display-device-config/schema/current.txt b/services/core/xsd/display-device-config/schema/current.txt index 7c2436db8694d..d40854a874536 100644 --- a/services/core/xsd/display-device-config/schema/current.txt +++ b/services/core/xsd/display-device-config/schema/current.txt @@ -65,11 +65,21 @@ package com.android.server.display.config { method public final void setValue(@NonNull java.math.BigDecimal); } + public class RefreshRateRange { + ctor public RefreshRateRange(); + method public final java.math.BigInteger getMaximum(); + method public final java.math.BigInteger getMinimum(); + method public final void setMaximum(java.math.BigInteger); + method public final void setMinimum(java.math.BigInteger); + } + public class SensorDetails { ctor public SensorDetails(); method @Nullable public final String getName(); + method @Nullable public final com.android.server.display.config.RefreshRateRange getRefreshRate(); method @Nullable public final String getType(); method public final void setName(@Nullable String); + method public final void setRefreshRate(@Nullable com.android.server.display.config.RefreshRateRange); method public final void setType(@Nullable String); } diff --git a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java index e1012a9a4cada..0efcc57eeec22 100644 --- a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java @@ -35,6 +35,7 @@ import static org.mockito.Mockito.when; import android.content.Context; import android.content.res.Resources; import android.content.res.TypedArray; +import android.hardware.display.DisplayManagerInternal.RefreshRateRange; import android.os.Binder; import android.os.Handler; import android.os.IBinder; @@ -589,8 +590,8 @@ public class LocalDisplayAdapterTest { new DisplayModeDirector.DesiredDisplayModeSpecs( /*baseModeId*/ baseModeId, /*allowGroupSwitching*/ false, - new DisplayModeDirector.RefreshRateRange(60f, 60f), - new DisplayModeDirector.RefreshRateRange(60f, 60f) + new RefreshRateRange(60f, 60f), + new RefreshRateRange(60f, 60f) )); waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); verify(mSurfaceControlProxy).setDesiredDisplayModeSpecs(display.token, @@ -624,8 +625,8 @@ public class LocalDisplayAdapterTest { new DisplayModeDirector.DesiredDisplayModeSpecs( /*baseModeId*/ baseModeId, /*allowGroupSwitching*/ false, - new DisplayModeDirector.RefreshRateRange(60f, 60f), - new DisplayModeDirector.RefreshRateRange(60f, 60f) + new RefreshRateRange(60f, 60f), + new RefreshRateRange(60f, 60f) )); waitForHandlerToComplete(mHandler, HANDLER_WAIT_MS); diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java index f156779034abf..5ba375b922e23 100644 --- a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java @@ -64,6 +64,7 @@ import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.display.DisplayManagerService.SyncRoot; import com.android.server.lights.LightsManager; +import com.android.server.sensors.SensorManagerInternal; import com.android.server.wm.WindowManagerInternal; import libcore.junit.util.compat.CoreCompatChangeRule.DisableCompatChanges; @@ -150,6 +151,7 @@ public class DisplayManagerServiceTest { @Mock LightsManager mMockLightsManager; @Mock VirtualDisplayAdapter mMockVirtualDisplayAdapter; @Mock IBinder mMockDisplayToken; + @Mock SensorManagerInternal mMockSensorManagerInternal; @Before public void setUp() throws Exception { @@ -161,6 +163,8 @@ public class DisplayManagerServiceTest { LocalServices.addService(WindowManagerInternal.class, mMockWindowManagerInternal); LocalServices.removeServiceForTest(LightsManager.class); LocalServices.addService(LightsManager.class, mMockLightsManager); + LocalServices.removeServiceForTest(SensorManagerInternal.class); + LocalServices.addService(SensorManagerInternal.class, mMockSensorManagerInternal); mContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java index 918979cdd859d..c0df2e33ffeda 100644 --- a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java +++ b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java @@ -49,6 +49,8 @@ import android.hardware.Sensor; import android.hardware.SensorEventListener; import android.hardware.SensorManager; import android.hardware.display.DisplayManager; +import android.hardware.display.DisplayManagerInternal; +import android.hardware.display.DisplayManagerInternal.RefreshRateRange; import android.hardware.fingerprint.IUdfpsHbmListener; import android.os.Handler; import android.os.Looper; @@ -70,6 +72,8 @@ import com.android.server.LocalServices; import com.android.server.display.DisplayModeDirector.BrightnessObserver; import com.android.server.display.DisplayModeDirector.DesiredDisplayModeSpecs; import com.android.server.display.DisplayModeDirector.Vote; +import com.android.server.sensors.SensorManagerInternal; +import com.android.server.sensors.SensorManagerInternal.ProximityActiveListener; import com.android.server.statusbar.StatusBarManagerInternal; import com.android.server.testutils.FakeDeviceConfigInterface; @@ -105,6 +109,10 @@ public class DisplayModeDirectorTest { public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule(); @Mock public StatusBarManagerInternal mStatusBarMock; + @Mock + public SensorManagerInternal mSensorManagerInternalMock; + @Mock + public DisplayManagerInternal mDisplayManagerInternalMock; @Before public void setUp() throws Exception { @@ -117,6 +125,10 @@ public class DisplayModeDirectorTest { LocalServices.removeServiceForTest(StatusBarManagerInternal.class); LocalServices.addService(StatusBarManagerInternal.class, mStatusBarMock); + LocalServices.removeServiceForTest(SensorManagerInternal.class); + LocalServices.addService(SensorManagerInternal.class, mSensorManagerInternalMock); + LocalServices.removeServiceForTest(DisplayManagerInternal.class); + LocalServices.addService(DisplayManagerInternal.class, mDisplayManagerInternalMock); } private DisplayModeDirector createDirectorFromRefreshRateArray( @@ -1214,10 +1226,39 @@ public class DisplayModeDirectorTest { assertThat(desiredSpecs.baseModeId).isEqualTo(8); } + @Test + public void testProximitySensorVoting() throws Exception { + DisplayModeDirector director = + createDirectorFromRefreshRateArray(new float[] {60.f, 90.f}, 0); + director.start(createMockSensorManager()); + + ArgumentCaptor captor = + ArgumentCaptor.forClass(ProximityActiveListener.class); + verify(mSensorManagerInternalMock).addProximityActiveListener(any(Executor.class), + captor.capture()); + ProximityActiveListener listener = captor.getValue(); + + // Verify that there is no proximity vote initially + Vote vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_PROXIMITY); + assertNull(vote); + + when(mDisplayManagerInternalMock.getRefreshRateForDisplayAndSensor(eq(DISPLAY_ID), eq(null), + eq(Sensor.STRING_TYPE_PROXIMITY))).thenReturn(new RefreshRateRange(60, 60)); + + // Set the proximity to active and verify that we added a vote. + listener.onProximityActive(true); + vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_PROXIMITY); + assertVoteForRefreshRate(vote, 60.f); + + // Turn prox off and verify vote is gone. + listener.onProximityActive(false); + vote = director.getVote(DISPLAY_ID, Vote.PRIORITY_PROXIMITY); + assertNull(vote); + } + private void assertVoteForRefreshRate(Vote vote, float refreshRate) { assertThat(vote).isNotNull(); - final DisplayModeDirector.RefreshRateRange expectedRange = - new DisplayModeDirector.RefreshRateRange(refreshRate, refreshRate); + final RefreshRateRange expectedRange = new RefreshRateRange(refreshRate, refreshRate); assertThat(vote.refreshRateRange).isEqualTo(expectedRange); }