Merge "Introducing clampers to DPC" into udc-qpr-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
83db2408ad
@@ -38,17 +38,24 @@ import com.android.internal.annotations.VisibleForTesting;
|
|||||||
import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData;
|
import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData;
|
||||||
import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData.ThrottlingLevel;
|
import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData.ThrottlingLevel;
|
||||||
import com.android.server.display.feature.DeviceConfigParameterProvider;
|
import com.android.server.display.feature.DeviceConfigParameterProvider;
|
||||||
|
import com.android.server.display.utils.DeviceConfigParsingUtils;
|
||||||
|
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
|
import java.util.function.BiFunction;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class monitors various conditions, such as skin temperature throttling status, and limits
|
* This class monitors various conditions, such as skin temperature throttling status, and limits
|
||||||
* the allowed brightness range accordingly.
|
* the allowed brightness range accordingly.
|
||||||
|
*
|
||||||
|
* @deprecated will be replaced by
|
||||||
|
* {@link com.android.server.display.brightness.clamper.BrightnessThermalClamper}
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
class BrightnessThrottler {
|
class BrightnessThrottler {
|
||||||
private static final String TAG = "BrightnessThrottler";
|
private static final String TAG = "BrightnessThrottler";
|
||||||
private static final boolean DEBUG = false;
|
private static final boolean DEBUG = false;
|
||||||
@@ -93,8 +100,21 @@ class BrightnessThrottler {
|
|||||||
// time the underlying display device changes.
|
// time the underlying display device changes.
|
||||||
// This map is indexed by uniqueDisplayId, to provide maps for throttlingId -> throttlingData.
|
// This map is indexed by uniqueDisplayId, to provide maps for throttlingId -> throttlingData.
|
||||||
// HashMap< uniqueDisplayId, HashMap< throttlingDataId, ThermalBrightnessThrottlingData >>
|
// HashMap< uniqueDisplayId, HashMap< throttlingDataId, ThermalBrightnessThrottlingData >>
|
||||||
private final HashMap<String, HashMap<String, ThermalBrightnessThrottlingData>>
|
private final Map<String, Map<String, ThermalBrightnessThrottlingData>>
|
||||||
mThermalBrightnessThrottlingDataOverride = new HashMap<>(1);
|
mThermalBrightnessThrottlingDataOverride = new HashMap<>();
|
||||||
|
|
||||||
|
private final BiFunction<String, String, ThrottlingLevel> mDataPointMapper = (key, value) -> {
|
||||||
|
try {
|
||||||
|
int status = DeviceConfigParsingUtils.parseThermalStatus(key);
|
||||||
|
float brightnessPoint = DeviceConfigParsingUtils.parseBrightness(value);
|
||||||
|
return new ThrottlingLevel(status, brightnessPoint);
|
||||||
|
} catch (IllegalArgumentException iae) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private final Function<List<ThrottlingLevel>, ThermalBrightnessThrottlingData>
|
||||||
|
mDataSetMapper = ThermalBrightnessThrottlingData::create;
|
||||||
|
|
||||||
BrightnessThrottler(Handler handler, Runnable throttlingChangeCallback, String uniqueDisplayId,
|
BrightnessThrottler(Handler handler, Runnable throttlingChangeCallback, String uniqueDisplayId,
|
||||||
String throttlingDataId,
|
String throttlingDataId,
|
||||||
@@ -257,79 +277,15 @@ class BrightnessThrottler {
|
|||||||
// 456,2,moderate,0.9,critical,0.7,id_2
|
// 456,2,moderate,0.9,critical,0.7,id_2
|
||||||
// displayId, number, <state, val> * number
|
// displayId, number, <state, val> * number
|
||||||
// displayId, <number, <state, val> * number>, throttlingId
|
// displayId, <number, <state, val> * number>, throttlingId
|
||||||
private boolean parseAndAddData(@NonNull String strArray,
|
|
||||||
@NonNull HashMap<String, HashMap<String, ThermalBrightnessThrottlingData>>
|
|
||||||
displayIdToThrottlingIdToBtd) {
|
|
||||||
boolean validConfig = true;
|
|
||||||
String[] items = strArray.split(",");
|
|
||||||
int i = 0;
|
|
||||||
|
|
||||||
try {
|
|
||||||
String uniqueDisplayId = items[i++];
|
|
||||||
|
|
||||||
// number of throttling points
|
|
||||||
int noOfThrottlingPoints = Integer.parseInt(items[i++]);
|
|
||||||
List<ThrottlingLevel> throttlingLevels = new ArrayList<>(noOfThrottlingPoints);
|
|
||||||
|
|
||||||
// throttling level and point
|
|
||||||
for (int j = 0; j < noOfThrottlingPoints; j++) {
|
|
||||||
String severity = items[i++];
|
|
||||||
int status = parseThermalStatus(severity);
|
|
||||||
float brightnessPoint = parseBrightness(items[i++]);
|
|
||||||
throttlingLevels.add(new ThrottlingLevel(status, brightnessPoint));
|
|
||||||
}
|
|
||||||
|
|
||||||
String throttlingDataId = (i < items.length) ? items[i++] : DEFAULT_ID;
|
|
||||||
ThermalBrightnessThrottlingData throttlingLevelsData =
|
|
||||||
DisplayDeviceConfig.ThermalBrightnessThrottlingData.create(throttlingLevels);
|
|
||||||
|
|
||||||
// Add throttlingLevelsData to inner map where necessary.
|
|
||||||
HashMap<String, ThermalBrightnessThrottlingData> throttlingMapForDisplay =
|
|
||||||
displayIdToThrottlingIdToBtd.get(uniqueDisplayId);
|
|
||||||
if (throttlingMapForDisplay == null) {
|
|
||||||
throttlingMapForDisplay = new HashMap<>();
|
|
||||||
throttlingMapForDisplay.put(throttlingDataId, throttlingLevelsData);
|
|
||||||
displayIdToThrottlingIdToBtd.put(uniqueDisplayId, throttlingMapForDisplay);
|
|
||||||
} else if (throttlingMapForDisplay.containsKey(throttlingDataId)) {
|
|
||||||
Slog.e(TAG, "Throttling data for display " + uniqueDisplayId
|
|
||||||
+ "contains duplicate throttling ids: '" + throttlingDataId + "'");
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
throttlingMapForDisplay.put(throttlingDataId, throttlingLevelsData);
|
|
||||||
}
|
|
||||||
} catch (NumberFormatException | IndexOutOfBoundsException
|
|
||||||
| UnknownThermalStatusException e) {
|
|
||||||
Slog.e(TAG, "Throttling data is invalid array: '" + strArray + "'", e);
|
|
||||||
validConfig = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i != items.length) {
|
|
||||||
validConfig = false;
|
|
||||||
}
|
|
||||||
return validConfig;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void loadThermalBrightnessThrottlingDataFromDeviceConfig() {
|
private void loadThermalBrightnessThrottlingDataFromDeviceConfig() {
|
||||||
HashMap<String, HashMap<String, ThermalBrightnessThrottlingData>> tempThrottlingData =
|
|
||||||
new HashMap<>(1);
|
|
||||||
mThermalBrightnessThrottlingDataString =
|
mThermalBrightnessThrottlingDataString =
|
||||||
mConfigParameterProvider.getBrightnessThrottlingData();
|
mConfigParameterProvider.getBrightnessThrottlingData();
|
||||||
boolean validConfig = true;
|
|
||||||
mThermalBrightnessThrottlingDataOverride.clear();
|
mThermalBrightnessThrottlingDataOverride.clear();
|
||||||
if (mThermalBrightnessThrottlingDataString != null) {
|
if (mThermalBrightnessThrottlingDataString != null) {
|
||||||
String[] throttlingDataSplits = mThermalBrightnessThrottlingDataString.split(";");
|
Map<String, Map<String, ThermalBrightnessThrottlingData>> tempThrottlingData =
|
||||||
for (String s : throttlingDataSplits) {
|
DeviceConfigParsingUtils.parseDeviceConfigMap(
|
||||||
if (!parseAndAddData(s, tempThrottlingData)) {
|
mThermalBrightnessThrottlingDataString, mDataPointMapper, mDataSetMapper);
|
||||||
validConfig = false;
|
mThermalBrightnessThrottlingDataOverride.putAll(tempThrottlingData);
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (validConfig) {
|
|
||||||
mThermalBrightnessThrottlingDataOverride.putAll(tempThrottlingData);
|
|
||||||
tempThrottlingData.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
Slog.w(TAG, "DeviceConfig ThermalBrightnessThrottlingData is null");
|
Slog.w(TAG, "DeviceConfig ThermalBrightnessThrottlingData is null");
|
||||||
}
|
}
|
||||||
@@ -395,42 +351,6 @@ class BrightnessThrottler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private float parseBrightness(String intVal) throws NumberFormatException {
|
|
||||||
float value = Float.parseFloat(intVal);
|
|
||||||
if (value < PowerManager.BRIGHTNESS_MIN || value > PowerManager.BRIGHTNESS_MAX) {
|
|
||||||
throw new NumberFormatException("Brightness constraint value out of bounds.");
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
@PowerManager.ThermalStatus private int parseThermalStatus(@NonNull String value)
|
|
||||||
throws UnknownThermalStatusException {
|
|
||||||
switch (value) {
|
|
||||||
case "none":
|
|
||||||
return PowerManager.THERMAL_STATUS_NONE;
|
|
||||||
case "light":
|
|
||||||
return PowerManager.THERMAL_STATUS_LIGHT;
|
|
||||||
case "moderate":
|
|
||||||
return PowerManager.THERMAL_STATUS_MODERATE;
|
|
||||||
case "severe":
|
|
||||||
return PowerManager.THERMAL_STATUS_SEVERE;
|
|
||||||
case "critical":
|
|
||||||
return PowerManager.THERMAL_STATUS_CRITICAL;
|
|
||||||
case "emergency":
|
|
||||||
return PowerManager.THERMAL_STATUS_EMERGENCY;
|
|
||||||
case "shutdown":
|
|
||||||
return PowerManager.THERMAL_STATUS_SHUTDOWN;
|
|
||||||
default:
|
|
||||||
throw new UnknownThermalStatusException("Invalid Thermal Status: " + value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class UnknownThermalStatusException extends Exception {
|
|
||||||
UnknownThermalStatusException(String message) {
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private final class SkinThermalStatusObserver extends IThermalEventListener.Stub {
|
private final class SkinThermalStatusObserver extends IThermalEventListener.Stub {
|
||||||
private final Injector mInjector;
|
private final Injector mInjector;
|
||||||
private final Handler mHandler;
|
private final Handler mHandler;
|
||||||
|
|||||||
@@ -458,7 +458,7 @@ public class DisplayDeviceConfig {
|
|||||||
|
|
||||||
public static final String QUIRK_CAN_SET_BRIGHTNESS_VIA_HWC = "canSetBrightnessViaHwc";
|
public static final String QUIRK_CAN_SET_BRIGHTNESS_VIA_HWC = "canSetBrightnessViaHwc";
|
||||||
|
|
||||||
static final String DEFAULT_ID = "default";
|
public static final String DEFAULT_ID = "default";
|
||||||
|
|
||||||
private static final float BRIGHTNESS_DEFAULT = 0.5f;
|
private static final float BRIGHTNESS_DEFAULT = 0.5f;
|
||||||
private static final String ETC_DIR = "etc";
|
private static final String ETC_DIR = "etc";
|
||||||
@@ -3127,11 +3127,15 @@ public class DisplayDeviceConfig {
|
|||||||
public static class ThermalBrightnessThrottlingData {
|
public static class ThermalBrightnessThrottlingData {
|
||||||
public List<ThrottlingLevel> throttlingLevels;
|
public List<ThrottlingLevel> throttlingLevels;
|
||||||
|
|
||||||
static class ThrottlingLevel {
|
/**
|
||||||
|
* thermal status to brightness cap holder
|
||||||
|
*/
|
||||||
|
public static class ThrottlingLevel {
|
||||||
public @PowerManager.ThermalStatus int thermalStatus;
|
public @PowerManager.ThermalStatus int thermalStatus;
|
||||||
public float brightness;
|
public float brightness;
|
||||||
|
|
||||||
ThrottlingLevel(@PowerManager.ThermalStatus int thermalStatus, float brightness) {
|
public ThrottlingLevel(
|
||||||
|
@PowerManager.ThermalStatus int thermalStatus, float brightness) {
|
||||||
this.thermalStatus = thermalStatus;
|
this.thermalStatus = thermalStatus;
|
||||||
this.brightness = brightness;
|
this.brightness = brightness;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ import android.os.Trace;
|
|||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
import android.util.FloatProperty;
|
import android.util.FloatProperty;
|
||||||
|
import android.util.IndentingPrintWriter;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.util.MathUtils;
|
import android.util.MathUtils;
|
||||||
import android.util.MutableFloat;
|
import android.util.MutableFloat;
|
||||||
@@ -64,7 +65,6 @@ import com.android.internal.display.BrightnessSynchronizer;
|
|||||||
import com.android.internal.logging.MetricsLogger;
|
import com.android.internal.logging.MetricsLogger;
|
||||||
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
|
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
|
||||||
import com.android.internal.util.FrameworkStatsLog;
|
import com.android.internal.util.FrameworkStatsLog;
|
||||||
import com.android.internal.util.IndentingPrintWriter;
|
|
||||||
import com.android.internal.util.RingBuffer;
|
import com.android.internal.util.RingBuffer;
|
||||||
import com.android.server.LocalServices;
|
import com.android.server.LocalServices;
|
||||||
import com.android.server.am.BatteryStatsService;
|
import com.android.server.am.BatteryStatsService;
|
||||||
@@ -73,6 +73,7 @@ import com.android.server.display.brightness.BrightnessEvent;
|
|||||||
import com.android.server.display.brightness.BrightnessReason;
|
import com.android.server.display.brightness.BrightnessReason;
|
||||||
import com.android.server.display.brightness.BrightnessUtils;
|
import com.android.server.display.brightness.BrightnessUtils;
|
||||||
import com.android.server.display.brightness.DisplayBrightnessController;
|
import com.android.server.display.brightness.DisplayBrightnessController;
|
||||||
|
import com.android.server.display.brightness.clamper.BrightnessClamperController;
|
||||||
import com.android.server.display.brightness.strategy.AutomaticBrightnessStrategy;
|
import com.android.server.display.brightness.strategy.AutomaticBrightnessStrategy;
|
||||||
import com.android.server.display.color.ColorDisplayService.ColorDisplayServiceInternal;
|
import com.android.server.display.color.ColorDisplayService.ColorDisplayServiceInternal;
|
||||||
import com.android.server.display.color.ColorDisplayService.ReduceBrightColorsListener;
|
import com.android.server.display.color.ColorDisplayService.ReduceBrightColorsListener;
|
||||||
@@ -380,6 +381,8 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal
|
|||||||
|
|
||||||
private final BrightnessThrottler mBrightnessThrottler;
|
private final BrightnessThrottler mBrightnessThrottler;
|
||||||
|
|
||||||
|
private final BrightnessClamperController mBrightnessClamperController;
|
||||||
|
|
||||||
private final Runnable mOnBrightnessChangeRunnable;
|
private final Runnable mOnBrightnessChangeRunnable;
|
||||||
|
|
||||||
private final BrightnessEvent mLastBrightnessEvent;
|
private final BrightnessEvent mLastBrightnessEvent;
|
||||||
@@ -554,6 +557,13 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal
|
|||||||
mDisplayId, mLogicalDisplay.getDisplayInfoLocked().brightnessDefault,
|
mDisplayId, mLogicalDisplay.getDisplayInfoLocked().brightnessDefault,
|
||||||
brightnessSetting, () -> postBrightnessChangeRunnable(),
|
brightnessSetting, () -> postBrightnessChangeRunnable(),
|
||||||
new HandlerExecutor(mHandler));
|
new HandlerExecutor(mHandler));
|
||||||
|
|
||||||
|
mBrightnessClamperController = new BrightnessClamperController(mHandler,
|
||||||
|
modeChangeCallback::run, new BrightnessClamperController.DisplayDeviceData(
|
||||||
|
mUniqueDisplayId,
|
||||||
|
mThermalBrightnessThrottlingDataId,
|
||||||
|
mDisplayDeviceConfig
|
||||||
|
));
|
||||||
// Seed the cached brightness
|
// Seed the cached brightness
|
||||||
saveBrightnessInfo(getScreenBrightnessSetting());
|
saveBrightnessInfo(getScreenBrightnessSetting());
|
||||||
|
|
||||||
@@ -782,6 +792,10 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal
|
|||||||
final String thermalBrightnessThrottlingDataId =
|
final String thermalBrightnessThrottlingDataId =
|
||||||
mLogicalDisplay.getDisplayInfoLocked().thermalBrightnessThrottlingDataId;
|
mLogicalDisplay.getDisplayInfoLocked().thermalBrightnessThrottlingDataId;
|
||||||
|
|
||||||
|
mBrightnessClamperController.onDisplayChanged(
|
||||||
|
new BrightnessClamperController.DisplayDeviceData(mUniqueDisplayId,
|
||||||
|
mThermalBrightnessThrottlingDataId, config));
|
||||||
|
|
||||||
mHandler.postAtTime(() -> {
|
mHandler.postAtTime(() -> {
|
||||||
boolean changed = false;
|
boolean changed = false;
|
||||||
if (mDisplayDevice != device) {
|
if (mDisplayDevice != device) {
|
||||||
@@ -1187,6 +1201,7 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal
|
|||||||
mDisplayPowerProximityStateController.cleanup();
|
mDisplayPowerProximityStateController.cleanup();
|
||||||
mBrightnessRangeController.stop();
|
mBrightnessRangeController.stop();
|
||||||
mBrightnessThrottler.stop();
|
mBrightnessThrottler.stop();
|
||||||
|
mBrightnessClamperController.stop();
|
||||||
mHandler.removeCallbacksAndMessages(null);
|
mHandler.removeCallbacksAndMessages(null);
|
||||||
|
|
||||||
// Release any outstanding wakelocks we're still holding because of pending messages.
|
// Release any outstanding wakelocks we're still holding because of pending messages.
|
||||||
@@ -1519,6 +1534,8 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal
|
|||||||
// allowed range.
|
// allowed range.
|
||||||
float animateValue = clampScreenBrightness(brightnessState);
|
float animateValue = clampScreenBrightness(brightnessState);
|
||||||
|
|
||||||
|
animateValue = mBrightnessClamperController.clamp(animateValue);
|
||||||
|
|
||||||
// If there are any HDR layers on the screen, we have a special brightness value that we
|
// If there are any HDR layers on the screen, we have a special brightness value that we
|
||||||
// use instead. We still preserve the calculated brightness for Standard Dynamic Range
|
// use instead. We still preserve the calculated brightness for Standard Dynamic Range
|
||||||
// (SDR) layers, but the main brightness value will be the one for HDR.
|
// (SDR) layers, but the main brightness value will be the one for HDR.
|
||||||
@@ -2411,6 +2428,11 @@ final class DisplayPowerController2 implements AutomaticBrightnessController.Cal
|
|||||||
if (mDisplayStateController != null) {
|
if (mDisplayStateController != null) {
|
||||||
mDisplayStateController.dumpsys(pw);
|
mDisplayStateController.dumpsys(pw);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pw.println();
|
||||||
|
if (mBrightnessClamperController != null) {
|
||||||
|
mBrightnessClamperController.dump(ipw);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,55 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 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.brightness.clamper;
|
||||||
|
|
||||||
|
import android.annotation.NonNull;
|
||||||
|
import android.os.PowerManager;
|
||||||
|
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
|
||||||
|
abstract class BrightnessClamper<T> {
|
||||||
|
|
||||||
|
protected float mBrightnessCap = PowerManager.BRIGHTNESS_MAX;
|
||||||
|
protected boolean mIsActive = false;
|
||||||
|
|
||||||
|
float getBrightnessCap() {
|
||||||
|
return mBrightnessCap;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isActive() {
|
||||||
|
return mIsActive;
|
||||||
|
}
|
||||||
|
|
||||||
|
void dump(PrintWriter writer) {
|
||||||
|
writer.println("BrightnessClamper:" + getType());
|
||||||
|
writer.println(" mBrightnessCap: " + mBrightnessCap);
|
||||||
|
writer.println(" mIsActive: " + mIsActive);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
abstract Type getType();
|
||||||
|
|
||||||
|
abstract void onDeviceConfigChanged();
|
||||||
|
|
||||||
|
abstract void onDisplayChanged(T displayData);
|
||||||
|
|
||||||
|
abstract void stop();
|
||||||
|
|
||||||
|
enum Type {
|
||||||
|
THERMAL
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,207 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 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.brightness.clamper;
|
||||||
|
|
||||||
|
import static com.android.server.display.brightness.clamper.BrightnessClamper.Type;
|
||||||
|
|
||||||
|
import android.annotation.NonNull;
|
||||||
|
import android.annotation.Nullable;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.HandlerExecutor;
|
||||||
|
import android.os.PowerManager;
|
||||||
|
import android.provider.DeviceConfig;
|
||||||
|
import android.provider.DeviceConfigInterface;
|
||||||
|
import android.util.IndentingPrintWriter;
|
||||||
|
|
||||||
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
|
import com.android.server.display.DisplayDeviceConfig;
|
||||||
|
import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData;
|
||||||
|
import com.android.server.display.feature.DeviceConfigParameterProvider;
|
||||||
|
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clampers controller, all in DisplayControllerHandler
|
||||||
|
*/
|
||||||
|
public class BrightnessClamperController {
|
||||||
|
|
||||||
|
private static final boolean ENABLED = false;
|
||||||
|
|
||||||
|
private final DeviceConfigParameterProvider mDeviceConfigParameterProvider;
|
||||||
|
private final Handler mHandler;
|
||||||
|
private final ClamperChangeListener mClamperChangeListenerExternal;
|
||||||
|
|
||||||
|
private final Executor mExecutor;
|
||||||
|
private final List<BrightnessClamper<? super DisplayDeviceData>> mClampers = new ArrayList<>();
|
||||||
|
private final DeviceConfig.OnPropertiesChangedListener mOnPropertiesChangedListener =
|
||||||
|
properties -> mClampers.forEach(BrightnessClamper::onDeviceConfigChanged);
|
||||||
|
private float mBrightnessCap = PowerManager.BRIGHTNESS_MAX;
|
||||||
|
@Nullable
|
||||||
|
private Type mClamperType = null;
|
||||||
|
|
||||||
|
public BrightnessClamperController(Handler handler,
|
||||||
|
ClamperChangeListener clamperChangeListener, DisplayDeviceData data) {
|
||||||
|
this(new Injector(), handler, clamperChangeListener, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
BrightnessClamperController(Injector injector, Handler handler,
|
||||||
|
ClamperChangeListener clamperChangeListener, DisplayDeviceData data) {
|
||||||
|
mDeviceConfigParameterProvider = injector.getDeviceConfigParameterProvider();
|
||||||
|
mHandler = handler;
|
||||||
|
mClamperChangeListenerExternal = clamperChangeListener;
|
||||||
|
mExecutor = new HandlerExecutor(handler);
|
||||||
|
|
||||||
|
Runnable clamperChangeRunnableInternal = this::recalculateBrightnessCap;
|
||||||
|
|
||||||
|
ClamperChangeListener clamperChangeListenerInternal = () -> {
|
||||||
|
if (!mHandler.hasCallbacks(clamperChangeRunnableInternal)) {
|
||||||
|
mHandler.post(clamperChangeRunnableInternal);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (ENABLED) {
|
||||||
|
mClampers.add(
|
||||||
|
new BrightnessThermalClamper(handler, clamperChangeListenerInternal, data));
|
||||||
|
start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should be called when display changed. Forwards the call to individual clampers
|
||||||
|
*/
|
||||||
|
public void onDisplayChanged(DisplayDeviceData data) {
|
||||||
|
mClampers.forEach(clamper -> clamper.onDisplayChanged(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies clamping
|
||||||
|
* Called in DisplayControllerHandler
|
||||||
|
*/
|
||||||
|
public float clamp(float value) {
|
||||||
|
return Math.min(value, mBrightnessCap);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used to dump ClampersController state.
|
||||||
|
*/
|
||||||
|
public void dump(PrintWriter writer) {
|
||||||
|
writer.println("BrightnessClampersController:");
|
||||||
|
writer.println(" mBrightnessCap: " + mBrightnessCap);
|
||||||
|
writer.println(" mClamperType: " + mClamperType);
|
||||||
|
IndentingPrintWriter ipw = new IndentingPrintWriter(writer, " ");
|
||||||
|
mClampers.forEach(clamper -> clamper.dump(ipw));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method should be called when the ClamperController is no longer in use.
|
||||||
|
* Called in DisplayControllerHandler
|
||||||
|
*/
|
||||||
|
public void stop() {
|
||||||
|
mDeviceConfigParameterProvider.removeOnPropertiesChangedListener(
|
||||||
|
mOnPropertiesChangedListener);
|
||||||
|
mClampers.forEach(BrightnessClamper::stop);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Called in DisplayControllerHandler
|
||||||
|
private void recalculateBrightnessCap() {
|
||||||
|
float brightnessCap = PowerManager.BRIGHTNESS_MAX;
|
||||||
|
Type clamperType = null;
|
||||||
|
|
||||||
|
BrightnessClamper<?> minClamper = mClampers.stream()
|
||||||
|
.filter(BrightnessClamper::isActive)
|
||||||
|
.min((clamper1, clamper2) -> Float.compare(clamper1.getBrightnessCap(),
|
||||||
|
clamper2.getBrightnessCap())).orElse(null);
|
||||||
|
|
||||||
|
if (minClamper != null) {
|
||||||
|
brightnessCap = minClamper.getBrightnessCap();
|
||||||
|
clamperType = minClamper.getType();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mBrightnessCap != brightnessCap || mClamperType != clamperType) {
|
||||||
|
mBrightnessCap = brightnessCap;
|
||||||
|
mClamperType = clamperType;
|
||||||
|
mClamperChangeListenerExternal.onChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void start() {
|
||||||
|
mDeviceConfigParameterProvider.addOnPropertiesChangedListener(
|
||||||
|
mExecutor, mOnPropertiesChangedListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clampers change listener
|
||||||
|
*/
|
||||||
|
public interface ClamperChangeListener {
|
||||||
|
/**
|
||||||
|
* Notifies that clamper state changed
|
||||||
|
*/
|
||||||
|
void onChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
static class Injector {
|
||||||
|
DeviceConfigParameterProvider getDeviceConfigParameterProvider() {
|
||||||
|
return new DeviceConfigParameterProvider(DeviceConfigInterface.REAL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data for clampers
|
||||||
|
*/
|
||||||
|
public static class DisplayDeviceData implements BrightnessThermalClamper.ThermalData {
|
||||||
|
@NonNull
|
||||||
|
private final String mUniqueDisplayId;
|
||||||
|
@NonNull
|
||||||
|
private final String mThermalThrottlingDataId;
|
||||||
|
|
||||||
|
private final DisplayDeviceConfig mDisplayDeviceConfig;
|
||||||
|
|
||||||
|
public DisplayDeviceData(@NonNull String uniqueDisplayId,
|
||||||
|
@NonNull String thermalThrottlingDataId,
|
||||||
|
@NonNull DisplayDeviceConfig displayDeviceConfig) {
|
||||||
|
mUniqueDisplayId = uniqueDisplayId;
|
||||||
|
mThermalThrottlingDataId = thermalThrottlingDataId;
|
||||||
|
mDisplayDeviceConfig = displayDeviceConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public String getUniqueDisplayId() {
|
||||||
|
return mUniqueDisplayId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public String getThermalThrottlingDataId() {
|
||||||
|
return mThermalThrottlingDataId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public ThermalBrightnessThrottlingData getThermalBrightnessThrottlingData() {
|
||||||
|
return mDisplayDeviceConfig.getThermalBrightnessThrottlingDataMapByThrottlingId().get(
|
||||||
|
mThermalThrottlingDataId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,272 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 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.brightness.clamper;
|
||||||
|
|
||||||
|
import static com.android.server.display.DisplayDeviceConfig.DEFAULT_ID;
|
||||||
|
import static com.android.server.display.brightness.clamper.BrightnessClamperController.ClamperChangeListener;
|
||||||
|
|
||||||
|
import android.annotation.NonNull;
|
||||||
|
import android.annotation.Nullable;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.IThermalEventListener;
|
||||||
|
import android.os.IThermalService;
|
||||||
|
import android.os.PowerManager;
|
||||||
|
import android.os.RemoteException;
|
||||||
|
import android.os.ServiceManager;
|
||||||
|
import android.os.Temperature;
|
||||||
|
import android.provider.DeviceConfigInterface;
|
||||||
|
import android.util.Slog;
|
||||||
|
|
||||||
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
|
import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData;
|
||||||
|
import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData.ThrottlingLevel;
|
||||||
|
import com.android.server.display.feature.DeviceConfigParameterProvider;
|
||||||
|
import com.android.server.display.utils.DeviceConfigParsingUtils;
|
||||||
|
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.BiFunction;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
|
||||||
|
class BrightnessThermalClamper extends
|
||||||
|
BrightnessClamper<BrightnessThermalClamper.ThermalData> {
|
||||||
|
|
||||||
|
private static final String TAG = "BrightnessThermalClamper";
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private final IThermalService mThermalService;
|
||||||
|
@NonNull
|
||||||
|
private final DeviceConfigParameterProvider mConfigParameterProvider;
|
||||||
|
@NonNull
|
||||||
|
private final Handler mHandler;
|
||||||
|
@NonNull
|
||||||
|
private final ClamperChangeListener mChangelistener;
|
||||||
|
// data from DeviceConfig, for all displays, for all dataSets
|
||||||
|
// mapOf(uniqueDisplayId to mapOf(dataSetId to ThermalBrightnessThrottlingData))
|
||||||
|
@NonNull
|
||||||
|
private Map<String, Map<String, ThermalBrightnessThrottlingData>>
|
||||||
|
mThermalThrottlingDataOverride = Map.of();
|
||||||
|
// data from DisplayDeviceConfig, for particular display+dataSet
|
||||||
|
@Nullable
|
||||||
|
private ThermalBrightnessThrottlingData mThermalThrottlingDataFromDeviceConfig = null;
|
||||||
|
// Active data, if mDataOverride contains data for mUniqueDisplayId, mDataId, then use it,
|
||||||
|
// otherwise mDataFromDeviceConfig
|
||||||
|
@Nullable
|
||||||
|
private ThermalBrightnessThrottlingData mThermalThrottlingDataActive = null;
|
||||||
|
private boolean mStarted = false;
|
||||||
|
@Nullable
|
||||||
|
private String mUniqueDisplayId = null;
|
||||||
|
@Nullable
|
||||||
|
private String mDataId = null;
|
||||||
|
@Temperature.ThrottlingStatus
|
||||||
|
private int mThrottlingStatus = Temperature.THROTTLING_NONE;
|
||||||
|
|
||||||
|
private final IThermalEventListener mThermalEventListener = new IThermalEventListener.Stub() {
|
||||||
|
@Override
|
||||||
|
public void notifyThrottling(Temperature temperature) {
|
||||||
|
@Temperature.ThrottlingStatus int status = temperature.getStatus();
|
||||||
|
mHandler.post(() -> thermalStatusChanged(status));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private final BiFunction<String, String, ThrottlingLevel> mDataPointMapper = (key, value) -> {
|
||||||
|
try {
|
||||||
|
int status = DeviceConfigParsingUtils.parseThermalStatus(key);
|
||||||
|
float brightnessPoint = DeviceConfigParsingUtils.parseBrightness(value);
|
||||||
|
return new ThrottlingLevel(status, brightnessPoint);
|
||||||
|
} catch (IllegalArgumentException iae) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private final Function<List<ThrottlingLevel>, ThermalBrightnessThrottlingData>
|
||||||
|
mDataSetMapper = ThermalBrightnessThrottlingData::create;
|
||||||
|
|
||||||
|
|
||||||
|
BrightnessThermalClamper(Handler handler, ClamperChangeListener listener,
|
||||||
|
ThermalData thermalData) {
|
||||||
|
this(new Injector(), handler, listener, thermalData);
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
BrightnessThermalClamper(Injector injector, Handler handler,
|
||||||
|
ClamperChangeListener listener, ThermalData thermalData) {
|
||||||
|
mThermalService = injector.getThermalService();
|
||||||
|
mConfigParameterProvider = injector.getDeviceConfigParameterProvider();
|
||||||
|
mHandler = handler;
|
||||||
|
mChangelistener = listener;
|
||||||
|
mHandler.post(() -> {
|
||||||
|
setDisplayData(thermalData);
|
||||||
|
loadOverrideData();
|
||||||
|
start();
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@NonNull
|
||||||
|
Type getType() {
|
||||||
|
return Type.THERMAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void onDeviceConfigChanged() {
|
||||||
|
mHandler.post(() -> {
|
||||||
|
loadOverrideData();
|
||||||
|
recalculateActiveData();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void onDisplayChanged(ThermalData data) {
|
||||||
|
mHandler.post(() -> {
|
||||||
|
setDisplayData(data);
|
||||||
|
recalculateActiveData();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void stop() {
|
||||||
|
if (!mStarted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
mThermalService.unregisterThermalEventListener(mThermalEventListener);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
Slog.e(TAG, "Failed to unregister thermal status listener", e);
|
||||||
|
}
|
||||||
|
mStarted = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void dump(PrintWriter writer) {
|
||||||
|
writer.println("BrightnessThermalClamper:");
|
||||||
|
writer.println(" mStarted: " + mStarted);
|
||||||
|
if (mThermalService != null) {
|
||||||
|
writer.println(" ThermalService available");
|
||||||
|
} else {
|
||||||
|
writer.println(" ThermalService not available");
|
||||||
|
}
|
||||||
|
writer.println(" mThrottlingStatus: " + mThrottlingStatus);
|
||||||
|
writer.println(" mUniqueDisplayId: " + mUniqueDisplayId);
|
||||||
|
writer.println(" mDataId: " + mDataId);
|
||||||
|
writer.println(" mDataOverride: " + mThermalThrottlingDataOverride);
|
||||||
|
writer.println(" mDataFromDeviceConfig: " + mThermalThrottlingDataFromDeviceConfig);
|
||||||
|
writer.println(" mDataActive: " + mThermalThrottlingDataActive);
|
||||||
|
super.dump(writer);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void recalculateActiveData() {
|
||||||
|
if (mUniqueDisplayId == null || mDataId == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mThermalThrottlingDataActive = mThermalThrottlingDataOverride
|
||||||
|
.getOrDefault(mUniqueDisplayId, Map.of()).getOrDefault(mDataId,
|
||||||
|
mThermalThrottlingDataFromDeviceConfig);
|
||||||
|
|
||||||
|
recalculateBrightnessCap();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadOverrideData() {
|
||||||
|
String throttlingDataOverride = mConfigParameterProvider.getBrightnessThrottlingData();
|
||||||
|
mThermalThrottlingDataOverride = DeviceConfigParsingUtils.parseDeviceConfigMap(
|
||||||
|
throttlingDataOverride, mDataPointMapper, mDataSetMapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setDisplayData(@NonNull ThermalData data) {
|
||||||
|
mUniqueDisplayId = data.getUniqueDisplayId();
|
||||||
|
mDataId = data.getThermalThrottlingDataId();
|
||||||
|
mThermalThrottlingDataFromDeviceConfig = data.getThermalBrightnessThrottlingData();
|
||||||
|
if (mThermalThrottlingDataFromDeviceConfig == null && !DEFAULT_ID.equals(mDataId)) {
|
||||||
|
Slog.wtf(TAG,
|
||||||
|
"Thermal throttling data is missing for thermalThrottlingDataId=" + mDataId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void recalculateBrightnessCap() {
|
||||||
|
float brightnessCap = PowerManager.BRIGHTNESS_MAX;
|
||||||
|
boolean isActive = false;
|
||||||
|
|
||||||
|
if (mThermalThrottlingDataActive != null) {
|
||||||
|
// Throttling levels are sorted by increasing severity
|
||||||
|
for (ThrottlingLevel level : mThermalThrottlingDataActive.throttlingLevels) {
|
||||||
|
if (level.thermalStatus <= mThrottlingStatus) {
|
||||||
|
brightnessCap = level.brightness;
|
||||||
|
isActive = true;
|
||||||
|
} else {
|
||||||
|
// Throttling levels that are greater than the current status are irrelevant
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (brightnessCap != mBrightnessCap || mIsActive != isActive) {
|
||||||
|
mBrightnessCap = brightnessCap;
|
||||||
|
mIsActive = isActive;
|
||||||
|
mChangelistener.onChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void thermalStatusChanged(@Temperature.ThrottlingStatus int status) {
|
||||||
|
if (mThrottlingStatus != status) {
|
||||||
|
mThrottlingStatus = status;
|
||||||
|
recalculateBrightnessCap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void start() {
|
||||||
|
if (mThermalService == null) {
|
||||||
|
Slog.e(TAG, "Could not observe thermal status. Service not available");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
// We get a callback immediately upon registering so there's no need to query
|
||||||
|
// for the current value.
|
||||||
|
mThermalService.registerThermalEventListenerWithType(mThermalEventListener,
|
||||||
|
Temperature.TYPE_SKIN);
|
||||||
|
mStarted = true;
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
Slog.e(TAG, "Failed to register thermal status listener", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ThermalData {
|
||||||
|
@NonNull
|
||||||
|
String getUniqueDisplayId();
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
String getThermalThrottlingDataId();
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
ThermalBrightnessThrottlingData getThermalBrightnessThrottlingData();
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
static class Injector {
|
||||||
|
IThermalService getThermalService() {
|
||||||
|
return IThermalService.Stub.asInterface(
|
||||||
|
ServiceManager.getService(Context.THERMAL_SERVICE));
|
||||||
|
}
|
||||||
|
|
||||||
|
DeviceConfigParameterProvider getDeviceConfigParameterProvider() {
|
||||||
|
return new DeviceConfigParameterProvider(DeviceConfigInterface.REAL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,152 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 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.annotation.NonNull;
|
||||||
|
import android.annotation.Nullable;
|
||||||
|
import android.os.PowerManager;
|
||||||
|
import android.util.Slog;
|
||||||
|
|
||||||
|
import com.android.server.display.DisplayDeviceConfig;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.BiFunction;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides utility methods for DeviceConfig string parsing
|
||||||
|
*/
|
||||||
|
public class DeviceConfigParsingUtils {
|
||||||
|
private static final String TAG = "DeviceConfigParsingUtils";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses map from device config
|
||||||
|
* Data format:
|
||||||
|
* (displayId:String,numberOfPoints:Int,(state:T,value:Float){numberOfPoints},
|
||||||
|
* dataSetId:String)?;)+
|
||||||
|
* result : mapOf(displayId to mapOf(dataSetId to V))
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
public static <T, V> Map<String, Map<String, V>> parseDeviceConfigMap(
|
||||||
|
@Nullable String data,
|
||||||
|
@NonNull BiFunction<String, String, T> dataPointMapper,
|
||||||
|
@NonNull Function<List<T>, V> dataSetMapper) {
|
||||||
|
if (data == null) {
|
||||||
|
return Map.of();
|
||||||
|
}
|
||||||
|
Map<String, Map<String, V>> result = new HashMap<>();
|
||||||
|
String[] dataSets = data.split(";"); // by displayId + dataSetId
|
||||||
|
for (String dataSet : dataSets) {
|
||||||
|
String[] items = dataSet.split(",");
|
||||||
|
int noOfItems = items.length;
|
||||||
|
// Validate number of items, at least: displayId,1,key1,value1
|
||||||
|
if (noOfItems < 4) {
|
||||||
|
Slog.e(TAG, "Invalid dataSet(not enough items):" + dataSet, new Throwable());
|
||||||
|
return Map.of();
|
||||||
|
}
|
||||||
|
int i = 0;
|
||||||
|
String uniqueDisplayId = items[i++];
|
||||||
|
|
||||||
|
String numberOfPointsString = items[i++];
|
||||||
|
int numberOfPoints;
|
||||||
|
try {
|
||||||
|
numberOfPoints = Integer.parseInt(numberOfPointsString);
|
||||||
|
} catch (NumberFormatException nfe) {
|
||||||
|
Slog.e(TAG, "Invalid dataSet(invalid number of points):" + dataSet, nfe);
|
||||||
|
return Map.of();
|
||||||
|
}
|
||||||
|
// Validate number of itmes based on numberOfPoints:
|
||||||
|
// displayId,numberOfPoints,(key,value) x numberOfPoints,dataSetId(optional)
|
||||||
|
int expectedMinItems = 2 + numberOfPoints * 2;
|
||||||
|
if (noOfItems < expectedMinItems || noOfItems > expectedMinItems + 1) {
|
||||||
|
Slog.e(TAG, "Invalid dataSet(wrong number of points):" + dataSet, new Throwable());
|
||||||
|
return Map.of();
|
||||||
|
}
|
||||||
|
// Construct data points
|
||||||
|
List<T> dataPoints = new ArrayList<>();
|
||||||
|
for (int j = 0; j < numberOfPoints; j++) {
|
||||||
|
String key = items[i++];
|
||||||
|
String value = items[i++];
|
||||||
|
T dataPoint = dataPointMapper.apply(key, value);
|
||||||
|
if (dataPoint == null) {
|
||||||
|
Slog.e(TAG,
|
||||||
|
"Invalid dataPoint ,key=" + key + ",value=" + value + ",dataSet="
|
||||||
|
+ dataSet, new Throwable());
|
||||||
|
return Map.of();
|
||||||
|
}
|
||||||
|
dataPoints.add(dataPoint);
|
||||||
|
}
|
||||||
|
// Construct dataSet
|
||||||
|
V dataSetMapped = dataSetMapper.apply(dataPoints);
|
||||||
|
if (dataSetMapped == null) {
|
||||||
|
Slog.e(TAG, "Invalid dataSetMapped dataPoints=" + dataPoints + ",dataSet="
|
||||||
|
+ dataSet, new Throwable());
|
||||||
|
return Map.of();
|
||||||
|
}
|
||||||
|
// Get dataSetId and dataSets map for displayId
|
||||||
|
String dataSetId = (i < items.length) ? items[i] : DisplayDeviceConfig.DEFAULT_ID;
|
||||||
|
Map<String, V> byDisplayId = result.computeIfAbsent(uniqueDisplayId,
|
||||||
|
k -> new HashMap<>());
|
||||||
|
|
||||||
|
// Try to store dataSet in datasets for display
|
||||||
|
if (byDisplayId.put(dataSetId, dataSetMapped) != null) {
|
||||||
|
Slog.e(TAG, "Duplicate dataSetId=" + dataSetId + ",data=" + data, new Throwable());
|
||||||
|
return Map.of();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses thermal string value from device config
|
||||||
|
*/
|
||||||
|
@PowerManager.ThermalStatus
|
||||||
|
public static int parseThermalStatus(@NonNull String value) throws IllegalArgumentException {
|
||||||
|
switch (value) {
|
||||||
|
case "none":
|
||||||
|
return PowerManager.THERMAL_STATUS_NONE;
|
||||||
|
case "light":
|
||||||
|
return PowerManager.THERMAL_STATUS_LIGHT;
|
||||||
|
case "moderate":
|
||||||
|
return PowerManager.THERMAL_STATUS_MODERATE;
|
||||||
|
case "severe":
|
||||||
|
return PowerManager.THERMAL_STATUS_SEVERE;
|
||||||
|
case "critical":
|
||||||
|
return PowerManager.THERMAL_STATUS_CRITICAL;
|
||||||
|
case "emergency":
|
||||||
|
return PowerManager.THERMAL_STATUS_EMERGENCY;
|
||||||
|
case "shutdown":
|
||||||
|
return PowerManager.THERMAL_STATUS_SHUTDOWN;
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException("Invalid Thermal Status: " + value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses brightness value from device config
|
||||||
|
*/
|
||||||
|
public static float parseBrightness(String stringVal) throws IllegalArgumentException {
|
||||||
|
float value = Float.parseFloat(stringVal);
|
||||||
|
if (value < PowerManager.BRIGHTNESS_MIN || value > PowerManager.BRIGHTNESS_MAX) {
|
||||||
|
throw new IllegalArgumentException("Brightness value out of bounds: " + stringVal);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,251 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 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.brightness.clamper;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
|
import android.hardware.display.DisplayManager;
|
||||||
|
import android.os.IThermalEventListener;
|
||||||
|
import android.os.IThermalService;
|
||||||
|
import android.os.PowerManager;
|
||||||
|
import android.os.RemoteException;
|
||||||
|
import android.os.Temperature;
|
||||||
|
import android.provider.DeviceConfig;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import com.android.internal.annotations.Keep;
|
||||||
|
import com.android.server.display.DisplayDeviceConfig;
|
||||||
|
import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData;
|
||||||
|
import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData.ThrottlingLevel;
|
||||||
|
import com.android.server.display.feature.DeviceConfigParameterProvider;
|
||||||
|
import com.android.server.testutils.FakeDeviceConfigInterface;
|
||||||
|
import com.android.server.testutils.TestHandler;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.ArgumentCaptor;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.MockitoAnnotations;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import junitparams.JUnitParamsRunner;
|
||||||
|
import junitparams.Parameters;
|
||||||
|
|
||||||
|
@RunWith(JUnitParamsRunner.class)
|
||||||
|
public class BrightnessThermalClamperTest {
|
||||||
|
|
||||||
|
private static final float FLOAT_TOLERANCE = 0.001f;
|
||||||
|
|
||||||
|
private static final String DISPLAY_ID = "displayId";
|
||||||
|
@Mock
|
||||||
|
private IThermalService mMockThermalService;
|
||||||
|
@Mock
|
||||||
|
private BrightnessClamperController.ClamperChangeListener mMockClamperChangeListener;
|
||||||
|
|
||||||
|
private final FakeDeviceConfigInterface mFakeDeviceConfigInterface =
|
||||||
|
new FakeDeviceConfigInterface();
|
||||||
|
private final TestHandler mTestHandler = new TestHandler(null);
|
||||||
|
private BrightnessThermalClamper mClamper;
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
MockitoAnnotations.initMocks(this);
|
||||||
|
mClamper = new BrightnessThermalClamper(new TestInjector(), mTestHandler,
|
||||||
|
mMockClamperChangeListener, new TestThermalData());
|
||||||
|
mTestHandler.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTypeIsThermal() {
|
||||||
|
assertEquals(BrightnessClamper.Type.THERMAL, mClamper.getType());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNoThrottlingData() {
|
||||||
|
assertFalse(mClamper.isActive());
|
||||||
|
assertEquals(PowerManager.BRIGHTNESS_MAX, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Keep
|
||||||
|
private static Object[][] testThrottlingData() {
|
||||||
|
// throttlingLevels, throttlingStatus, expectedActive, expectedBrightness
|
||||||
|
return new Object[][] {
|
||||||
|
// no throttling data
|
||||||
|
{List.of(), Temperature.THROTTLING_LIGHT, false, PowerManager.BRIGHTNESS_MAX},
|
||||||
|
// throttlingStatus < min throttling data
|
||||||
|
{List.of(
|
||||||
|
new ThrottlingLevel(PowerManager.THERMAL_STATUS_MODERATE, 0.5f),
|
||||||
|
new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL, 0.1f)),
|
||||||
|
Temperature.THROTTLING_LIGHT, false, PowerManager.BRIGHTNESS_MAX},
|
||||||
|
// throttlingStatus = min throttling data
|
||||||
|
{List.of(
|
||||||
|
new ThrottlingLevel(PowerManager.THERMAL_STATUS_MODERATE, 0.5f),
|
||||||
|
new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL, 0.1f)),
|
||||||
|
Temperature.THROTTLING_MODERATE, true, 0.5f},
|
||||||
|
// throttlingStatus between min and max throttling data
|
||||||
|
{List.of(
|
||||||
|
new ThrottlingLevel(PowerManager.THERMAL_STATUS_MODERATE, 0.5f),
|
||||||
|
new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL, 0.1f)),
|
||||||
|
Temperature.THROTTLING_SEVERE, true, 0.5f},
|
||||||
|
// throttlingStatus = max throttling data
|
||||||
|
{List.of(
|
||||||
|
new ThrottlingLevel(PowerManager.THERMAL_STATUS_MODERATE, 0.5f),
|
||||||
|
new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL, 0.1f)),
|
||||||
|
Temperature.THROTTLING_CRITICAL, true, 0.1f},
|
||||||
|
// throttlingStatus > max throttling data
|
||||||
|
{List.of(
|
||||||
|
new ThrottlingLevel(PowerManager.THERMAL_STATUS_MODERATE, 0.5f),
|
||||||
|
new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL, 0.1f)),
|
||||||
|
Temperature.THROTTLING_EMERGENCY, true, 0.1f},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
@Test
|
||||||
|
@Parameters(method = "testThrottlingData")
|
||||||
|
public void testNotifyThrottlingAfterOnDisplayChange(List<ThrottlingLevel> throttlingLevels,
|
||||||
|
@Temperature.ThrottlingStatus int throttlingStatus,
|
||||||
|
boolean expectedActive, float expectedBrightness) throws RemoteException {
|
||||||
|
IThermalEventListener thermalEventListener = captureThermalEventListener();
|
||||||
|
mClamper.onDisplayChanged(new TestThermalData(throttlingLevels));
|
||||||
|
mTestHandler.flush();
|
||||||
|
assertFalse(mClamper.isActive());
|
||||||
|
assertEquals(PowerManager.BRIGHTNESS_MAX, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
|
||||||
|
|
||||||
|
thermalEventListener.notifyThrottling(createTemperature(throttlingStatus));
|
||||||
|
mTestHandler.flush();
|
||||||
|
assertEquals(expectedActive, mClamper.isActive());
|
||||||
|
assertEquals(expectedBrightness, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Parameters(method = "testThrottlingData")
|
||||||
|
public void testOnDisplayChangeAfterNotifyThrottlng(List<ThrottlingLevel> throttlingLevels,
|
||||||
|
@Temperature.ThrottlingStatus int throttlingStatus,
|
||||||
|
boolean expectedActive, float expectedBrightness) throws RemoteException {
|
||||||
|
IThermalEventListener thermalEventListener = captureThermalEventListener();
|
||||||
|
thermalEventListener.notifyThrottling(createTemperature(throttlingStatus));
|
||||||
|
mTestHandler.flush();
|
||||||
|
assertFalse(mClamper.isActive());
|
||||||
|
assertEquals(PowerManager.BRIGHTNESS_MAX, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
|
||||||
|
|
||||||
|
mClamper.onDisplayChanged(new TestThermalData(throttlingLevels));
|
||||||
|
mTestHandler.flush();
|
||||||
|
assertEquals(expectedActive, mClamper.isActive());
|
||||||
|
assertEquals(expectedBrightness, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOverrideData() throws RemoteException {
|
||||||
|
IThermalEventListener thermalEventListener = captureThermalEventListener();
|
||||||
|
thermalEventListener.notifyThrottling(createTemperature(Temperature.THROTTLING_SEVERE));
|
||||||
|
mTestHandler.flush();
|
||||||
|
assertFalse(mClamper.isActive());
|
||||||
|
assertEquals(PowerManager.BRIGHTNESS_MAX, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
|
||||||
|
|
||||||
|
mClamper.onDisplayChanged(new TestThermalData(
|
||||||
|
List.of(new ThrottlingLevel(PowerManager.THERMAL_STATUS_SEVERE, 0.5f))));
|
||||||
|
mTestHandler.flush();
|
||||||
|
assertTrue(mClamper.isActive());
|
||||||
|
assertEquals(0.5f, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
|
||||||
|
|
||||||
|
overrideThrottlingData("displayId,1,emergency,0.4");
|
||||||
|
mClamper.onDeviceConfigChanged();
|
||||||
|
mTestHandler.flush();
|
||||||
|
|
||||||
|
assertFalse(mClamper.isActive());
|
||||||
|
assertEquals(PowerManager.BRIGHTNESS_MAX, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
|
||||||
|
|
||||||
|
overrideThrottlingData("displayId,1,moderate,0.4");
|
||||||
|
mClamper.onDeviceConfigChanged();
|
||||||
|
mTestHandler.flush();
|
||||||
|
|
||||||
|
assertTrue(mClamper.isActive());
|
||||||
|
assertEquals(0.4f, mClamper.getBrightnessCap(), FLOAT_TOLERANCE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private IThermalEventListener captureThermalEventListener() throws RemoteException {
|
||||||
|
ArgumentCaptor<IThermalEventListener> captor = ArgumentCaptor.forClass(
|
||||||
|
IThermalEventListener.class);
|
||||||
|
verify(mMockThermalService).registerThermalEventListenerWithType(captor.capture(), eq(
|
||||||
|
Temperature.TYPE_SKIN));
|
||||||
|
return captor.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Temperature createTemperature(@Temperature.ThrottlingStatus int status) {
|
||||||
|
return new Temperature(100, Temperature.TYPE_SKIN, "test_temperature", status);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void overrideThrottlingData(String data) {
|
||||||
|
mFakeDeviceConfigInterface.putProperty(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
|
||||||
|
DisplayManager.DeviceConfig.KEY_BRIGHTNESS_THROTTLING_DATA, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TestInjector extends BrightnessThermalClamper.Injector {
|
||||||
|
@Override
|
||||||
|
IThermalService getThermalService() {
|
||||||
|
return mMockThermalService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
DeviceConfigParameterProvider getDeviceConfigParameterProvider() {
|
||||||
|
return new DeviceConfigParameterProvider(mFakeDeviceConfigInterface);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class TestThermalData implements BrightnessThermalClamper.ThermalData {
|
||||||
|
|
||||||
|
private final String mUniqueDisplayId;
|
||||||
|
private final String mDataId;
|
||||||
|
private final ThermalBrightnessThrottlingData mData;
|
||||||
|
|
||||||
|
private TestThermalData() {
|
||||||
|
this(DISPLAY_ID, DisplayDeviceConfig.DEFAULT_ID, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private TestThermalData(List<ThrottlingLevel> data) {
|
||||||
|
this(DISPLAY_ID, DisplayDeviceConfig.DEFAULT_ID, data);
|
||||||
|
}
|
||||||
|
private TestThermalData(String uniqueDisplayId, String dataId, List<ThrottlingLevel> data) {
|
||||||
|
mUniqueDisplayId = uniqueDisplayId;
|
||||||
|
mDataId = dataId;
|
||||||
|
mData = ThermalBrightnessThrottlingData.create(data);
|
||||||
|
}
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public String getUniqueDisplayId() {
|
||||||
|
return mUniqueDisplayId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public String getThermalThrottlingDataId() {
|
||||||
|
return mDataId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public ThermalBrightnessThrottlingData getThermalBrightnessThrottlingData() {
|
||||||
|
return mData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,161 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 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 static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertThrows;
|
||||||
|
|
||||||
|
import android.os.PowerManager;
|
||||||
|
import android.util.Pair;
|
||||||
|
|
||||||
|
import androidx.test.filters.SmallTest;
|
||||||
|
|
||||||
|
import com.android.internal.annotations.Keep;
|
||||||
|
import com.android.server.display.DisplayDeviceConfig;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.BiFunction;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
import junitparams.JUnitParamsRunner;
|
||||||
|
import junitparams.Parameters;
|
||||||
|
|
||||||
|
@SmallTest
|
||||||
|
@RunWith(JUnitParamsRunner.class)
|
||||||
|
public class DeviceConfigParsingUtilsTest {
|
||||||
|
private static final String VALID_DATA_STRING = "display1,1,key1,value1";
|
||||||
|
private static final float FLOAT_TOLERANCE = 0.001f;
|
||||||
|
|
||||||
|
private final BiFunction<String, String, Pair<String, String>> mDataPointToPair = Pair::create;
|
||||||
|
private final Function<List<Pair<String, String>>, List<Pair<String, String>>>
|
||||||
|
mDataSetIdentity = (dataSet) -> dataSet;
|
||||||
|
|
||||||
|
@Keep
|
||||||
|
private static Object[][] parseDeviceConfigMapData() {
|
||||||
|
// dataString, expectedMap
|
||||||
|
return new Object[][]{
|
||||||
|
// null
|
||||||
|
{null, Map.of()},
|
||||||
|
// empty string
|
||||||
|
{"", Map.of()},
|
||||||
|
// 1 display, 1 incomplete data point
|
||||||
|
{"display1,1,key1", Map.of()},
|
||||||
|
// 1 display,2 data points required, only 1 present
|
||||||
|
{"display1,2,key1,value1", Map.of()},
|
||||||
|
// 1 display, 1 data point, dataSetId and some extra data
|
||||||
|
{"display1,1,key1,value1,setId1,extraData", Map.of()},
|
||||||
|
// 1 display, random string instead of number of data points
|
||||||
|
{"display1,one,key1,value1", Map.of()},
|
||||||
|
// 1 display, 1 data point no dataSetId
|
||||||
|
{VALID_DATA_STRING, Map.of("display1", Map.of(DisplayDeviceConfig.DEFAULT_ID,
|
||||||
|
List.of(Pair.create("key1", "value1"))))},
|
||||||
|
// 1 display, 1 data point, dataSetId
|
||||||
|
{"display1,1,key1,value1,setId1", Map.of("display1", Map.of("setId1",
|
||||||
|
List.of(Pair.create("key1", "value1"))))},
|
||||||
|
// 1 display, 2 data point, dataSetId
|
||||||
|
{"display1,2,key1,value1,key2,value2,setId1", Map.of("display1", Map.of("setId1",
|
||||||
|
List.of(Pair.create("key1", "value1"), Pair.create("key2", "value2"))))},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Parameters(method = "parseDeviceConfigMapData")
|
||||||
|
public void testParseDeviceConfigMap(String dataString,
|
||||||
|
Map<String, Map<String, List<Pair<String, String>>>> expectedMap) {
|
||||||
|
Map<String, Map<String, List<Pair<String, String>>>> result =
|
||||||
|
DeviceConfigParsingUtils.parseDeviceConfigMap(dataString, mDataPointToPair,
|
||||||
|
mDataSetIdentity);
|
||||||
|
|
||||||
|
assertEquals(expectedMap, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDataPointMapperReturnsNull() {
|
||||||
|
Map<String, Map<String, List<Pair<String, String>>>> result =
|
||||||
|
DeviceConfigParsingUtils.parseDeviceConfigMap(VALID_DATA_STRING, (s1, s2) -> null,
|
||||||
|
mDataSetIdentity);
|
||||||
|
|
||||||
|
assertEquals(Map.of(), result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDataSetMapperReturnsNull() {
|
||||||
|
Map<String, Map<String, List<Pair<String, String>>>> result =
|
||||||
|
DeviceConfigParsingUtils.parseDeviceConfigMap(VALID_DATA_STRING, mDataPointToPair,
|
||||||
|
(dataSet) -> null);
|
||||||
|
|
||||||
|
assertEquals(Map.of(), result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Keep
|
||||||
|
private static Object[][] parseThermalStatusData() {
|
||||||
|
// thermalStatusString, expectedThermalStatus
|
||||||
|
return new Object[][]{
|
||||||
|
{"none", PowerManager.THERMAL_STATUS_NONE},
|
||||||
|
{"light", PowerManager.THERMAL_STATUS_LIGHT},
|
||||||
|
{"moderate", PowerManager.THERMAL_STATUS_MODERATE},
|
||||||
|
{"severe", PowerManager.THERMAL_STATUS_SEVERE},
|
||||||
|
{"critical", PowerManager.THERMAL_STATUS_CRITICAL},
|
||||||
|
{"emergency", PowerManager.THERMAL_STATUS_EMERGENCY},
|
||||||
|
{"shutdown", PowerManager.THERMAL_STATUS_SHUTDOWN},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Parameters(method = "parseThermalStatusData")
|
||||||
|
public void testParseThermalStatus(String thermalStatusString,
|
||||||
|
@PowerManager.ThermalStatus int expectedThermalStatus) {
|
||||||
|
int result = DeviceConfigParsingUtils.parseThermalStatus(thermalStatusString);
|
||||||
|
|
||||||
|
assertEquals(expectedThermalStatus, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testParseThermalStatus_illegalStatus() {
|
||||||
|
Throwable result = assertThrows(IllegalArgumentException.class,
|
||||||
|
() -> DeviceConfigParsingUtils.parseThermalStatus("invalid_status"));
|
||||||
|
|
||||||
|
assertEquals("Invalid Thermal Status: invalid_status", result.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testParseBrightness() {
|
||||||
|
float result = DeviceConfigParsingUtils.parseBrightness("0.65");
|
||||||
|
|
||||||
|
assertEquals(0.65, result, FLOAT_TOLERANCE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testParseBrightness_lessThanMin() {
|
||||||
|
Throwable result = assertThrows(IllegalArgumentException.class,
|
||||||
|
() -> DeviceConfigParsingUtils.parseBrightness("-0.65"));
|
||||||
|
|
||||||
|
assertEquals("Brightness value out of bounds: -0.65", result.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testParseBrightness_moreThanMax() {
|
||||||
|
Throwable result = assertThrows(IllegalArgumentException.class,
|
||||||
|
() -> DeviceConfigParsingUtils.parseBrightness("1.65"));
|
||||||
|
|
||||||
|
assertEquals("Brightness value out of bounds: 1.65", result.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user