Merge "Update BrightnessSynchronizer Logic" into tm-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
5b0c795a65
@@ -51,6 +51,12 @@ filegroup {
|
|||||||
srcs: ["android/tracing/TraceReportParams.aidl"],
|
srcs: ["android/tracing/TraceReportParams.aidl"],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
filegroup {
|
||||||
|
name: "framework-internal-display-sources",
|
||||||
|
srcs: ["com/android/internal/display/BrightnessSynchronizer.java"],
|
||||||
|
visibility: ["//frameworks/base/services/tests/mockingservicestests"],
|
||||||
|
}
|
||||||
|
|
||||||
// These are subset of framework-core-sources that are needed by the
|
// These are subset of framework-core-sources that are needed by the
|
||||||
// android.test.mock library. The implementation of android.test.mock references
|
// android.test.mock library. The implementation of android.test.mock references
|
||||||
// private members of various components to allow mocking of classes that cannot
|
// private members of various components to allow mocking of classes that cannot
|
||||||
|
|||||||
@@ -26,57 +26,60 @@ import android.os.Handler;
|
|||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.os.Message;
|
import android.os.Message;
|
||||||
import android.os.PowerManager;
|
import android.os.PowerManager;
|
||||||
|
import android.os.SystemClock;
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
import android.util.MathUtils;
|
import android.util.MathUtils;
|
||||||
|
import android.util.Slog;
|
||||||
import android.view.Display;
|
import android.view.Display;
|
||||||
|
|
||||||
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
|
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* BrightnessSynchronizer helps convert between the int (old) system and float
|
* BrightnessSynchronizer helps convert between the int (old) system and float
|
||||||
* (new) system for storing the brightness. It has methods to convert between the two and also
|
* (new) system for storing the brightness. It has methods to convert between the two and also
|
||||||
* observes for when one of the settings is changed and syncs this with the other.
|
* observes for when one of the settings is changed and syncs this with the other.
|
||||||
*/
|
*/
|
||||||
public class BrightnessSynchronizer {
|
public class BrightnessSynchronizer {
|
||||||
|
|
||||||
private static final int MSG_UPDATE_FLOAT = 1;
|
|
||||||
private static final int MSG_UPDATE_INT = 2;
|
|
||||||
private static final int MSG_UPDATE_BOTH = 3;
|
|
||||||
|
|
||||||
private static final String TAG = "BrightnessSynchronizer";
|
private static final String TAG = "BrightnessSynchronizer";
|
||||||
|
|
||||||
|
private static final boolean DEBUG = false;
|
||||||
private static final Uri BRIGHTNESS_URI =
|
private static final Uri BRIGHTNESS_URI =
|
||||||
Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS);
|
Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS);
|
||||||
|
|
||||||
|
private static final long WAIT_FOR_RESPONSE_MILLIS = 200;
|
||||||
|
|
||||||
|
private static final int MSG_RUN_UPDATE = 1;
|
||||||
|
|
||||||
// The tolerance within which we consider brightness values approximately equal to eachother.
|
// The tolerance within which we consider brightness values approximately equal to eachother.
|
||||||
// This value is approximately 1/3 of the smallest possible brightness value.
|
// This value is approximately 1/3 of the smallest possible brightness value.
|
||||||
public static final float EPSILON = 0.001f;
|
public static final float EPSILON = 0.001f;
|
||||||
|
|
||||||
private DisplayManager mDisplayManager;
|
private static int sBrightnessUpdateCount = 1;
|
||||||
|
|
||||||
private final Context mContext;
|
private final Context mContext;
|
||||||
|
private final BrightnessSyncObserver mBrightnessSyncObserver;
|
||||||
|
private final Clock mClock;
|
||||||
|
private final Handler mHandler;
|
||||||
|
|
||||||
private final Handler mHandler = new Handler(Looper.getMainLooper()) {
|
private DisplayManager mDisplayManager;
|
||||||
@Override
|
private int mLatestIntBrightness;
|
||||||
public void handleMessage(Message msg) {
|
private float mLatestFloatBrightness;
|
||||||
switch (msg.what) {
|
private BrightnessUpdate mCurrentUpdate;
|
||||||
case MSG_UPDATE_FLOAT:
|
private BrightnessUpdate mPendingUpdate;
|
||||||
updateBrightnessFloatFromInt(msg.arg1);
|
|
||||||
break;
|
|
||||||
case MSG_UPDATE_INT:
|
|
||||||
updateBrightnessIntFromFloat(Float.intBitsToFloat(msg.arg1));
|
|
||||||
break;
|
|
||||||
case MSG_UPDATE_BOTH:
|
|
||||||
updateBoth(Float.intBitsToFloat(msg.arg1));
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
super.handleMessage(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private float mPreferredSettingValue;
|
|
||||||
|
|
||||||
public BrightnessSynchronizer(Context context) {
|
public BrightnessSynchronizer(Context context) {
|
||||||
|
this(context, Looper.getMainLooper(), SystemClock::uptimeMillis);
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
public BrightnessSynchronizer(Context context, Looper looper, Clock clock) {
|
||||||
mContext = context;
|
mContext = context;
|
||||||
|
mClock = clock;
|
||||||
|
mBrightnessSyncObserver = new BrightnessSyncObserver();
|
||||||
|
mHandler = new BrightnessSynchronizerHandler(looper);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -90,24 +93,41 @@ public class BrightnessSynchronizer {
|
|||||||
if (mDisplayManager == null) {
|
if (mDisplayManager == null) {
|
||||||
mDisplayManager = mContext.getSystemService(DisplayManager.class);
|
mDisplayManager = mContext.getSystemService(DisplayManager.class);
|
||||||
}
|
}
|
||||||
|
if (mBrightnessSyncObserver.isObserving()) {
|
||||||
|
Slog.wtf(TAG, "Brightness sync observer requesting synchronization a second time.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mLatestFloatBrightness = getScreenBrightnessFloat();
|
||||||
|
mLatestIntBrightness = getScreenBrightnessInt();
|
||||||
|
Slog.i(TAG, "Initial brightness readings: " + mLatestIntBrightness + "(int), "
|
||||||
|
+ mLatestFloatBrightness + "(float)");
|
||||||
|
|
||||||
final BrightnessSyncObserver brightnessSyncObserver;
|
if (!Float.isNaN(mLatestFloatBrightness)) {
|
||||||
brightnessSyncObserver = new BrightnessSyncObserver();
|
mPendingUpdate = new BrightnessUpdate(BrightnessUpdate.TYPE_FLOAT,
|
||||||
brightnessSyncObserver.startObserving();
|
mLatestFloatBrightness);
|
||||||
|
} else if (mLatestIntBrightness != PowerManager.BRIGHTNESS_INVALID) {
|
||||||
final float currentFloatBrightness = getScreenBrightnessFloat();
|
mPendingUpdate = new BrightnessUpdate(BrightnessUpdate.TYPE_INT,
|
||||||
final int currentIntBrightness = getScreenBrightnessInt(mContext);
|
mLatestIntBrightness);
|
||||||
|
|
||||||
if (!Float.isNaN(currentFloatBrightness)) {
|
|
||||||
updateBrightnessIntFromFloat(currentFloatBrightness);
|
|
||||||
} else if (currentIntBrightness != -1) {
|
|
||||||
updateBrightnessFloatFromInt(currentIntBrightness);
|
|
||||||
} else {
|
} else {
|
||||||
final float defaultBrightness = mContext.getResources().getFloat(
|
final float defaultBrightness = mContext.getResources().getFloat(
|
||||||
com.android.internal.R.dimen.config_screenBrightnessSettingDefaultFloat);
|
com.android.internal.R.dimen.config_screenBrightnessSettingDefaultFloat);
|
||||||
mDisplayManager.setBrightness(Display.DEFAULT_DISPLAY, defaultBrightness);
|
mPendingUpdate = new BrightnessUpdate(BrightnessUpdate.TYPE_FLOAT, defaultBrightness);
|
||||||
|
Slog.i(TAG, "Setting initial brightness to default value of: " + defaultBrightness);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mBrightnessSyncObserver.startObserving();
|
||||||
|
mHandler.sendEmptyMessageAtTime(MSG_RUN_UPDATE, mClock.uptimeMillis());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prints data on dumpsys.
|
||||||
|
*/
|
||||||
|
public void dump(PrintWriter pw) {
|
||||||
|
pw.println("BrightnessSynchronizer");
|
||||||
|
pw.println(" mLatestIntBrightness=" + mLatestIntBrightness);
|
||||||
|
pw.println(" mLatestFloatBrightness=" + mLatestFloatBrightness);
|
||||||
|
pw.println(" mCurrentUpdate=" + mCurrentUpdate);
|
||||||
|
pw.println(" mPendingUpdate=" + mPendingUpdate);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -154,6 +174,78 @@ public class BrightnessSynchronizer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Consumes a brightness change event for the float-based brightness.
|
||||||
|
*
|
||||||
|
* @param brightness Float brightness.
|
||||||
|
*/
|
||||||
|
private void handleBrightnessChangeFloat(float brightness) {
|
||||||
|
mLatestFloatBrightness = brightness;
|
||||||
|
handleBrightnessChange(BrightnessUpdate.TYPE_FLOAT, brightness);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Consumes a brightness change event for the int-based brightness.
|
||||||
|
*
|
||||||
|
* @param brightness Int brightness.
|
||||||
|
*/
|
||||||
|
private void handleBrightnessChangeInt(int brightness) {
|
||||||
|
mLatestIntBrightness = brightness;
|
||||||
|
handleBrightnessChange(BrightnessUpdate.TYPE_INT, brightness);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Consumes a brightness change event.
|
||||||
|
*
|
||||||
|
* @param type Type of the brightness change (int/float)
|
||||||
|
* @param brightness brightness.
|
||||||
|
*/
|
||||||
|
private void handleBrightnessChange(int type, float brightness) {
|
||||||
|
boolean swallowUpdate = mCurrentUpdate != null
|
||||||
|
&& mCurrentUpdate.swallowUpdate(type, brightness);
|
||||||
|
BrightnessUpdate prevUpdate = null;
|
||||||
|
if (!swallowUpdate) {
|
||||||
|
prevUpdate = mPendingUpdate;
|
||||||
|
mPendingUpdate = new BrightnessUpdate(type, brightness);
|
||||||
|
}
|
||||||
|
runUpdate();
|
||||||
|
|
||||||
|
// If we created a new update and it is still pending after the update, add a log.
|
||||||
|
if (!swallowUpdate && mPendingUpdate != null) {
|
||||||
|
Slog.i(TAG, "New PendingUpdate: " + mPendingUpdate + ", prev=" + prevUpdate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs updates for current and pending BrightnessUpdates.
|
||||||
|
*/
|
||||||
|
private void runUpdate() {
|
||||||
|
if (DEBUG) {
|
||||||
|
Slog.d(TAG, "Running update mCurrent=" + mCurrentUpdate
|
||||||
|
+ ", mPending=" + mPendingUpdate);
|
||||||
|
}
|
||||||
|
|
||||||
|
// do-while instead of while to allow mCurrentUpdate to get set if there's a pending update.
|
||||||
|
do {
|
||||||
|
if (mCurrentUpdate != null) {
|
||||||
|
mCurrentUpdate.update();
|
||||||
|
if (mCurrentUpdate.isRunning()) {
|
||||||
|
break; // current update is still running, nothing to do.
|
||||||
|
} else if (mCurrentUpdate.isCompleted()) {
|
||||||
|
if (mCurrentUpdate.madeUpdates()) {
|
||||||
|
Slog.i(TAG, "Completed Update: " + mCurrentUpdate);
|
||||||
|
}
|
||||||
|
mCurrentUpdate = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// No current update any more, lets start the next update if there is one.
|
||||||
|
if (mCurrentUpdate == null && mPendingUpdate != null) {
|
||||||
|
mCurrentUpdate = mPendingUpdate;
|
||||||
|
mPendingUpdate = null;
|
||||||
|
}
|
||||||
|
} while (mCurrentUpdate != null);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the stored screen brightness float value from the display brightness setting.
|
* Gets the stored screen brightness float value from the display brightness setting.
|
||||||
* @return brightness
|
* @return brightness
|
||||||
@@ -164,76 +256,14 @@ public class BrightnessSynchronizer {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the stored screen brightness int from the system settings.
|
* Gets the stored screen brightness int from the system settings.
|
||||||
* @param context for accessing settings
|
|
||||||
*
|
|
||||||
* @return brightness
|
* @return brightness
|
||||||
*/
|
*/
|
||||||
private static int getScreenBrightnessInt(Context context) {
|
private int getScreenBrightnessInt() {
|
||||||
return Settings.System.getIntForUser(context.getContentResolver(),
|
return Settings.System.getIntForUser(mContext.getContentResolver(),
|
||||||
Settings.System.SCREEN_BRIGHTNESS, PowerManager.BRIGHTNESS_INVALID,
|
Settings.System.SCREEN_BRIGHTNESS, PowerManager.BRIGHTNESS_INVALID,
|
||||||
UserHandle.USER_CURRENT);
|
UserHandle.USER_CURRENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the settings based on a passed in int value. This is called whenever the int
|
|
||||||
* setting changes. mPreferredSettingValue holds the most recently updated brightness value
|
|
||||||
* as a float that we would like the display to be set to.
|
|
||||||
*
|
|
||||||
* We then schedule an update to both the int and float settings, but, remove all the other
|
|
||||||
* messages to update all, to prevent us getting stuck in a loop.
|
|
||||||
*
|
|
||||||
* @param value Brightness value as int to store in the float setting.
|
|
||||||
*/
|
|
||||||
private void updateBrightnessFloatFromInt(int value) {
|
|
||||||
if (brightnessFloatToInt(mPreferredSettingValue) == value) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
mPreferredSettingValue = brightnessIntToFloat(value);
|
|
||||||
final int newBrightnessAsIntBits = Float.floatToIntBits(mPreferredSettingValue);
|
|
||||||
mHandler.removeMessages(MSG_UPDATE_BOTH);
|
|
||||||
mHandler.obtainMessage(MSG_UPDATE_BOTH, newBrightnessAsIntBits, 0).sendToTarget();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the settings based on a passed in float value. This is called whenever the float
|
|
||||||
* setting changes. mPreferredSettingValue holds the most recently updated brightness value
|
|
||||||
* as a float that we would like the display to be set to.
|
|
||||||
*
|
|
||||||
* We then schedule an update to both the int and float settings, but, remove all the other
|
|
||||||
* messages to update all, to prevent us getting stuck in a loop.
|
|
||||||
*
|
|
||||||
* @param value Brightness setting as float to store in int setting.
|
|
||||||
*/
|
|
||||||
private void updateBrightnessIntFromFloat(float value) {
|
|
||||||
if (floatEquals(mPreferredSettingValue, value)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
mPreferredSettingValue = value;
|
|
||||||
final int newBrightnessAsIntBits = Float.floatToIntBits(mPreferredSettingValue);
|
|
||||||
mHandler.removeMessages(MSG_UPDATE_BOTH);
|
|
||||||
mHandler.obtainMessage(MSG_UPDATE_BOTH, newBrightnessAsIntBits, 0).sendToTarget();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates both setting values if they have changed
|
|
||||||
* mDisplayManager.setBrightness automatically checks for changes
|
|
||||||
* Settings.System.putIntForUser needs to be checked, to prevent an extra callback to this class
|
|
||||||
*
|
|
||||||
* @param newBrightnessFloat Brightness setting as float to store in both settings
|
|
||||||
*/
|
|
||||||
private void updateBoth(float newBrightnessFloat) {
|
|
||||||
int newBrightnessInt = brightnessFloatToInt(newBrightnessFloat);
|
|
||||||
mDisplayManager.setBrightness(Display.DEFAULT_DISPLAY, newBrightnessFloat);
|
|
||||||
if (getScreenBrightnessInt(mContext) != newBrightnessInt) {
|
|
||||||
Settings.System.putIntForUser(mContext.getContentResolver(),
|
|
||||||
Settings.System.SCREEN_BRIGHTNESS, newBrightnessInt, UserHandle.USER_CURRENT);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests whether two brightness float values are within a small enough tolerance
|
* Tests whether two brightness float values are within a small enough tolerance
|
||||||
* of each other.
|
* of each other.
|
||||||
@@ -253,7 +283,192 @@ public class BrightnessSynchronizer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encapsulates a brightness change event and contains logic for synchronizing the appropriate
|
||||||
|
* settings for the specified brightness change.
|
||||||
|
*/
|
||||||
|
@VisibleForTesting
|
||||||
|
public class BrightnessUpdate {
|
||||||
|
static final int TYPE_INT = 0x1;
|
||||||
|
static final int TYPE_FLOAT = 0x2;
|
||||||
|
|
||||||
|
private static final int STATE_NOT_STARTED = 1;
|
||||||
|
private static final int STATE_RUNNING = 2;
|
||||||
|
private static final int STATE_COMPLETED = 3;
|
||||||
|
|
||||||
|
private final int mSourceType;
|
||||||
|
private final float mBrightness;
|
||||||
|
|
||||||
|
private long mTimeUpdated;
|
||||||
|
private int mState;
|
||||||
|
private int mUpdatedTypes;
|
||||||
|
private int mConfirmedTypes;
|
||||||
|
private int mId;
|
||||||
|
|
||||||
|
BrightnessUpdate(int sourceType, float brightness) {
|
||||||
|
mId = sBrightnessUpdateCount++;
|
||||||
|
mSourceType = sourceType;
|
||||||
|
mBrightness = brightness;
|
||||||
|
mTimeUpdated = 0;
|
||||||
|
mUpdatedTypes = 0x0;
|
||||||
|
mConfirmedTypes = 0x0;
|
||||||
|
mState = STATE_NOT_STARTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "{[" + mId + "] " + toStringLabel(mSourceType, mBrightness)
|
||||||
|
+ ", mUpdatedTypes=" + mUpdatedTypes + ", mConfirmedTypes=" + mConfirmedTypes
|
||||||
|
+ ", mTimeUpdated=" + mTimeUpdated + "}";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs the synchronization process, moving forward through the internal state machine.
|
||||||
|
*/
|
||||||
|
void update() {
|
||||||
|
if (mState == STATE_NOT_STARTED) {
|
||||||
|
mState = STATE_RUNNING;
|
||||||
|
|
||||||
|
// check if we need to update int
|
||||||
|
int brightnessInt = getBrightnessAsInt();
|
||||||
|
if (mLatestIntBrightness != brightnessInt) {
|
||||||
|
Settings.System.putIntForUser(mContext.getContentResolver(),
|
||||||
|
Settings.System.SCREEN_BRIGHTNESS, brightnessInt,
|
||||||
|
UserHandle.USER_CURRENT);
|
||||||
|
mLatestIntBrightness = brightnessInt;
|
||||||
|
mUpdatedTypes |= TYPE_INT;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if we need to update float
|
||||||
|
float brightnessFloat = getBrightnessAsFloat();
|
||||||
|
if (!floatEquals(mLatestFloatBrightness, brightnessFloat)) {
|
||||||
|
mDisplayManager.setBrightness(Display.DEFAULT_DISPLAY, brightnessFloat);
|
||||||
|
mLatestFloatBrightness = brightnessFloat;
|
||||||
|
mUpdatedTypes |= TYPE_FLOAT;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we made updates, lets wait for responses.
|
||||||
|
if (mUpdatedTypes != 0x0) {
|
||||||
|
// Give some time for our updates to return a confirmation response. If they
|
||||||
|
// don't return by that time, MSG_RUN_UPDATE will get sent and we will stop
|
||||||
|
// listening for responses and mark this update as complete.
|
||||||
|
if (DEBUG) {
|
||||||
|
Slog.d(TAG, "Sending MSG_RUN_UPDATE for "
|
||||||
|
+ toStringLabel(mSourceType, mBrightness));
|
||||||
|
}
|
||||||
|
Slog.i(TAG, "[" + mId + "] New Update "
|
||||||
|
+ toStringLabel(mSourceType, mBrightness) + " set brightness values: "
|
||||||
|
+ toStringLabel(mUpdatedTypes & TYPE_FLOAT, brightnessFloat) + " "
|
||||||
|
+ toStringLabel(mUpdatedTypes & TYPE_INT, brightnessInt));
|
||||||
|
|
||||||
|
mHandler.sendEmptyMessageAtTime(MSG_RUN_UPDATE,
|
||||||
|
mClock.uptimeMillis() + WAIT_FOR_RESPONSE_MILLIS);
|
||||||
|
}
|
||||||
|
mTimeUpdated = mClock.uptimeMillis();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mState == STATE_RUNNING) {
|
||||||
|
// If we're not waiting on any more confirmations or the time has expired, move to
|
||||||
|
// completed state.
|
||||||
|
if (mConfirmedTypes == mUpdatedTypes
|
||||||
|
|| (mTimeUpdated + WAIT_FOR_RESPONSE_MILLIS) < mClock.uptimeMillis()) {
|
||||||
|
mState = STATE_COMPLETED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempts to consume the specified brightness change if it is determined that the change
|
||||||
|
* is a notification of a change previously made by this class.
|
||||||
|
*
|
||||||
|
* @param type The type of change (int|float)
|
||||||
|
* @param brightness The brightness value.
|
||||||
|
* @return True if the change was caused by this class, thus swallowed.
|
||||||
|
*/
|
||||||
|
boolean swallowUpdate(int type, float brightness) {
|
||||||
|
if ((mUpdatedTypes & type) != type || (mConfirmedTypes & type) != 0x0) {
|
||||||
|
// It's either a type we didn't update, or one we've already confirmed.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
final boolean floatUpdateConfirmed =
|
||||||
|
type == TYPE_FLOAT && floatEquals(getBrightnessAsFloat(), brightness);
|
||||||
|
final boolean intUpdateConfirmed =
|
||||||
|
type == TYPE_INT && getBrightnessAsInt() == (int) brightness;
|
||||||
|
|
||||||
|
if (floatUpdateConfirmed || intUpdateConfirmed) {
|
||||||
|
mConfirmedTypes |= type;
|
||||||
|
Slog.i(TAG, "Swallowing update of " + toStringLabel(type, brightness)
|
||||||
|
+ " by update: " + this);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isRunning() {
|
||||||
|
return mState == STATE_RUNNING;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isCompleted() {
|
||||||
|
return mState == STATE_COMPLETED;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean madeUpdates() {
|
||||||
|
return mUpdatedTypes != 0x0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getBrightnessAsInt() {
|
||||||
|
if (mSourceType == TYPE_INT) {
|
||||||
|
return (int) mBrightness;
|
||||||
|
}
|
||||||
|
return brightnessFloatToInt(mBrightness);
|
||||||
|
}
|
||||||
|
|
||||||
|
private float getBrightnessAsFloat() {
|
||||||
|
if (mSourceType == TYPE_FLOAT) {
|
||||||
|
return mBrightness;
|
||||||
|
}
|
||||||
|
return brightnessIntToFloat((int) mBrightness);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String toStringLabel(int type, float brightness) {
|
||||||
|
return (type == TYPE_INT) ? ((int) brightness) + "(i)"
|
||||||
|
: ((type == TYPE_FLOAT) ? brightness + "(f)"
|
||||||
|
: "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Functional interface for providing time. */
|
||||||
|
@VisibleForTesting
|
||||||
|
public interface Clock {
|
||||||
|
/** @return system uptime in milliseconds. */
|
||||||
|
long uptimeMillis();
|
||||||
|
}
|
||||||
|
|
||||||
|
class BrightnessSynchronizerHandler extends Handler {
|
||||||
|
BrightnessSynchronizerHandler(Looper looper) {
|
||||||
|
super(looper);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleMessage(Message msg) {
|
||||||
|
switch (msg.what) {
|
||||||
|
case MSG_RUN_UPDATE:
|
||||||
|
if (DEBUG) {
|
||||||
|
Slog.d(TAG, "MSG_RUN_UPDATE");
|
||||||
|
}
|
||||||
|
runUpdate();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
super.handleMessage(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
private class BrightnessSyncObserver {
|
private class BrightnessSyncObserver {
|
||||||
|
private boolean mIsObserving;
|
||||||
|
|
||||||
private final DisplayListener mListener = new DisplayListener() {
|
private final DisplayListener mListener = new DisplayListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onDisplayAdded(int displayId) {}
|
public void onDisplayAdded(int displayId) {}
|
||||||
@@ -263,10 +478,7 @@ public class BrightnessSynchronizer {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDisplayChanged(int displayId) {
|
public void onDisplayChanged(int displayId) {
|
||||||
float currentFloat = getScreenBrightnessFloat();
|
handleBrightnessChangeFloat(getScreenBrightnessFloat());
|
||||||
int toSend = Float.floatToIntBits(currentFloat);
|
|
||||||
mHandler.removeMessages(MSG_UPDATE_INT);
|
|
||||||
mHandler.obtainMessage(MSG_UPDATE_INT, toSend, 0).sendToTarget();
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -277,28 +489,23 @@ public class BrightnessSynchronizer {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (BRIGHTNESS_URI.equals(uri)) {
|
if (BRIGHTNESS_URI.equals(uri)) {
|
||||||
int currentBrightness = getScreenBrightnessInt(mContext);
|
handleBrightnessChangeInt(getScreenBrightnessInt());
|
||||||
mHandler.removeMessages(MSG_UPDATE_FLOAT);
|
|
||||||
mHandler.obtainMessage(MSG_UPDATE_FLOAT, currentBrightness, 0).sendToTarget();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
public void startObserving() {
|
boolean isObserving() {
|
||||||
|
return mIsObserving;
|
||||||
|
}
|
||||||
|
|
||||||
|
void startObserving() {
|
||||||
final ContentResolver cr = mContext.getContentResolver();
|
final ContentResolver cr = mContext.getContentResolver();
|
||||||
cr.unregisterContentObserver(mContentObserver);
|
|
||||||
cr.registerContentObserver(BRIGHTNESS_URI, false, mContentObserver,
|
cr.registerContentObserver(BRIGHTNESS_URI, false, mContentObserver,
|
||||||
UserHandle.USER_ALL);
|
UserHandle.USER_ALL);
|
||||||
|
|
||||||
mDisplayManager.registerDisplayListener(mListener, mHandler,
|
mDisplayManager.registerDisplayListener(mListener, mHandler,
|
||||||
DisplayManager.EVENT_FLAG_DISPLAY_CHANGED
|
DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS);
|
||||||
| DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS);
|
mIsObserving = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void stopObserving() {
|
|
||||||
final ContentResolver cr = mContext.getContentResolver();
|
|
||||||
cr.unregisterContentObserver(mContentObserver);
|
|
||||||
mDisplayManager.unregisterDisplayListener(mListener);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2503,6 +2503,7 @@ public final class DisplayManagerService extends SystemService {
|
|||||||
}
|
}
|
||||||
pw.println();
|
pw.println();
|
||||||
mDisplayModeDirector.dump(pw);
|
mDisplayModeDirector.dump(pw);
|
||||||
|
mBrightnessSynchronizer.dump(pw);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static float[] getFloatArray(TypedArray array) {
|
private static float[] getFloatArray(TypedArray array) {
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ android_test {
|
|||||||
srcs: [
|
srcs: [
|
||||||
"src/**/*.java",
|
"src/**/*.java",
|
||||||
"src/**/*.kt",
|
"src/**/*.kt",
|
||||||
|
":framework-internal-display-sources",
|
||||||
],
|
],
|
||||||
|
|
||||||
static_libs: [
|
static_libs: [
|
||||||
|
|||||||
@@ -0,0 +1,255 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2022 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;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.mockito.Mockito.eq;
|
||||||
|
import static org.mockito.Mockito.isA;
|
||||||
|
import static org.mockito.Mockito.spy;
|
||||||
|
import static org.mockito.Mockito.times;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.ContextWrapper;
|
||||||
|
import android.database.ContentObserver;
|
||||||
|
import android.hardware.display.DisplayManager;
|
||||||
|
import android.hardware.display.DisplayManager.DisplayListener;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.UserHandle;
|
||||||
|
import android.os.test.TestLooper;
|
||||||
|
import android.provider.Settings;
|
||||||
|
import android.test.mock.MockContentResolver;
|
||||||
|
import android.view.Display;
|
||||||
|
|
||||||
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
|
import androidx.test.filters.SmallTest;
|
||||||
|
import androidx.test.runner.AndroidJUnit4;
|
||||||
|
|
||||||
|
import com.android.internal.display.BrightnessSynchronizer;
|
||||||
|
import com.android.internal.util.test.FakeSettingsProvider;
|
||||||
|
import com.android.server.testutils.OffsettableClock;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.ArgumentCaptor;
|
||||||
|
import org.mockito.Captor;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.MockitoAnnotations;
|
||||||
|
|
||||||
|
@SmallTest
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
public class BrightnessSynchronizerTest {
|
||||||
|
private static final float EPSILON = 0.00001f;
|
||||||
|
private static final Uri BRIGHTNESS_URI =
|
||||||
|
Settings.System.getUriFor(Settings.System.SCREEN_BRIGHTNESS);
|
||||||
|
|
||||||
|
private Context mContext;
|
||||||
|
private MockContentResolver mContentResolverSpy;
|
||||||
|
private OffsettableClock mClock;
|
||||||
|
private DisplayListener mDisplayListener;
|
||||||
|
private ContentObserver mContentObserver;
|
||||||
|
private TestLooper mTestLooper;
|
||||||
|
|
||||||
|
@Mock private DisplayManager mDisplayManagerMock;
|
||||||
|
@Captor private ArgumentCaptor<DisplayListener> mDisplayListenerCaptor;
|
||||||
|
@Captor private ArgumentCaptor<ContentObserver> mContentObserverCaptor;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
MockitoAnnotations.initMocks(this);
|
||||||
|
mContext = spy(new ContextWrapper(ApplicationProvider.getApplicationContext()));
|
||||||
|
mContentResolverSpy = spy(new MockContentResolver(mContext));
|
||||||
|
mContentResolverSpy.addProvider(Settings.AUTHORITY, new FakeSettingsProvider());
|
||||||
|
when(mContext.getContentResolver()).thenReturn(mContentResolverSpy);
|
||||||
|
when(mContext.getSystemService(DisplayManager.class)).thenReturn(mDisplayManagerMock);
|
||||||
|
mClock = new OffsettableClock.Stopped();
|
||||||
|
mTestLooper = new TestLooper(mClock::now);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSetFloat() throws Exception {
|
||||||
|
putFloatSetting(0.5f);
|
||||||
|
putIntSetting(128);
|
||||||
|
start();
|
||||||
|
|
||||||
|
// Set float brightness to 0.4
|
||||||
|
putFloatSetting(0.4f);
|
||||||
|
advanceTime(10);
|
||||||
|
verifyIntWasSetTo(fToI(0.4f));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSetInt() {
|
||||||
|
putFloatSetting(0.5f);
|
||||||
|
putIntSetting(128);
|
||||||
|
start();
|
||||||
|
|
||||||
|
// Set int brightness to 64
|
||||||
|
putIntSetting(64);
|
||||||
|
advanceTime(10);
|
||||||
|
verifyFloatWasSetTo(iToF(64));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSetIntQuickSuccession() {
|
||||||
|
putFloatSetting(0.5f);
|
||||||
|
putIntSetting(128);
|
||||||
|
start();
|
||||||
|
|
||||||
|
putIntSetting(50);
|
||||||
|
putIntSetting(40);
|
||||||
|
advanceTime(10);
|
||||||
|
|
||||||
|
verifyFloatWasSetTo(iToF(50));
|
||||||
|
|
||||||
|
// now confirm the first value (via callback) so that we can process the second one.
|
||||||
|
putFloatSetting(iToF(50));
|
||||||
|
advanceTime(10);
|
||||||
|
verifyFloatWasSetTo(iToF(40));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSetSameIntValue_nothingUpdated() {
|
||||||
|
putFloatSetting(0.5f);
|
||||||
|
putIntSetting(128);
|
||||||
|
start();
|
||||||
|
|
||||||
|
putIntSetting(128);
|
||||||
|
advanceTime(10);
|
||||||
|
verify(mDisplayManagerMock, times(0)).setBrightness(
|
||||||
|
eq(Display.DEFAULT_DISPLAY), eq(iToF(128)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testUpdateDuringResponseIsNotOverwritten() {
|
||||||
|
putFloatSetting(0.5f);
|
||||||
|
putIntSetting(128);
|
||||||
|
start();
|
||||||
|
|
||||||
|
// First, change the float to 0.4f
|
||||||
|
putFloatSetting(0.4f);
|
||||||
|
advanceTime(10);
|
||||||
|
|
||||||
|
// Now set the int to something else (not equal to 0.4f)
|
||||||
|
putIntSetting(20);
|
||||||
|
advanceTime(10);
|
||||||
|
|
||||||
|
// Verify that this update did not get sent to float, because synchronizer
|
||||||
|
// is still waiting for confirmation of its first value.
|
||||||
|
verify(mDisplayManagerMock, times(0)).setBrightness(
|
||||||
|
eq(Display.DEFAULT_DISPLAY), eq(iToF(20)));
|
||||||
|
|
||||||
|
// Send the confirmation of the initial change. This should trigger the new value to
|
||||||
|
// finally be processed and we can verify that the new value (20) is sent.
|
||||||
|
putIntSetting(fToI(0.4f));
|
||||||
|
advanceTime(10);
|
||||||
|
verify(mDisplayManagerMock).setBrightness(
|
||||||
|
eq(Display.DEFAULT_DISPLAY), eq(iToF(20)));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSetFloat_outOfTimeForResponse() {
|
||||||
|
putFloatSetting(0.5f);
|
||||||
|
putIntSetting(128);
|
||||||
|
start();
|
||||||
|
advanceTime(210);
|
||||||
|
|
||||||
|
// First, change the float to 0.4f
|
||||||
|
putFloatSetting(0.4f);
|
||||||
|
advanceTime(10);
|
||||||
|
|
||||||
|
// Now set the int to something else (not equal to 0.4f)
|
||||||
|
putIntSetting(20);
|
||||||
|
|
||||||
|
// Now, go beyond the timeout so that the last 20 event gets executed.
|
||||||
|
advanceTime(200);
|
||||||
|
|
||||||
|
// Verify that the new value gets sent because the timeout expired.
|
||||||
|
verify(mDisplayManagerMock).setBrightness(
|
||||||
|
eq(Display.DEFAULT_DISPLAY), eq(iToF(20)));
|
||||||
|
|
||||||
|
// Send a confirmation of the initial event, BrightnessSynchronizer should treat this as a
|
||||||
|
// new event because the timeout had already expired
|
||||||
|
putIntSetting(fToI(0.4f));
|
||||||
|
// Because the previous setting will be treated as a new event, we actually want to send
|
||||||
|
// confirmation of the setBrightness() we just verified so that it can be executed as well.
|
||||||
|
putFloatSetting(iToF(20));
|
||||||
|
advanceTime(10);
|
||||||
|
|
||||||
|
// Verify we sent what would have been the confirmation as a new event to displaymanager.
|
||||||
|
// We do both fToI and iToF because the conversions are not symmetric.
|
||||||
|
verify(mDisplayManagerMock).setBrightness(
|
||||||
|
eq(Display.DEFAULT_DISPLAY), eq(iToF(fToI(0.4f))));
|
||||||
|
}
|
||||||
|
|
||||||
|
private BrightnessSynchronizer start() {
|
||||||
|
BrightnessSynchronizer bs = new BrightnessSynchronizer(mContext, mTestLooper.getLooper(),
|
||||||
|
mClock::now);
|
||||||
|
bs.startSynchronizing();
|
||||||
|
verify(mDisplayManagerMock).registerDisplayListener(mDisplayListenerCaptor.capture(),
|
||||||
|
isA(Handler.class), eq(DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS));
|
||||||
|
mDisplayListener = mDisplayListenerCaptor.getValue();
|
||||||
|
|
||||||
|
verify(mContentResolverSpy).registerContentObserver(eq(BRIGHTNESS_URI), eq(false),
|
||||||
|
mContentObserverCaptor.capture(), eq(UserHandle.USER_ALL));
|
||||||
|
mContentObserver = mContentObserverCaptor.getValue();
|
||||||
|
return bs;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getIntSetting() throws Exception {
|
||||||
|
return Settings.System.getInt(mContentResolverSpy, Settings.System.SCREEN_BRIGHTNESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void putIntSetting(int brightness) {
|
||||||
|
Settings.System.putInt(mContentResolverSpy, Settings.System.SCREEN_BRIGHTNESS, brightness);
|
||||||
|
if (mContentObserver != null) {
|
||||||
|
mContentObserver.onChange(false /*=selfChange*/, BRIGHTNESS_URI);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void putFloatSetting(float brightness) {
|
||||||
|
when(mDisplayManagerMock.getBrightness(eq(Display.DEFAULT_DISPLAY))).thenReturn(brightness);
|
||||||
|
if (mDisplayListener != null) {
|
||||||
|
mDisplayListener.onDisplayChanged(Display.DEFAULT_DISPLAY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void verifyIntWasSetTo(int brightness) throws Exception {
|
||||||
|
assertEquals(brightness, getIntSetting());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void verifyFloatWasSetTo(float brightness) {
|
||||||
|
verify(mDisplayManagerMock).setBrightness(eq(Display.DEFAULT_DISPLAY), eq(brightness));
|
||||||
|
}
|
||||||
|
|
||||||
|
private int fToI(float brightness) {
|
||||||
|
return BrightnessSynchronizer.brightnessFloatToInt(brightness);
|
||||||
|
}
|
||||||
|
|
||||||
|
private float iToF(int brightness) {
|
||||||
|
return BrightnessSynchronizer.brightnessIntToFloat(brightness);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void advanceTime(long timeMs) {
|
||||||
|
mClock.fastForward(timeMs);
|
||||||
|
mTestLooper.dispatchAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user