Merge "Update BrightnessSynchronizer Logic" into tm-dev am: 5b0c795a65
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/18334422 Change-Id: I57735cd1bc24078a2d27bc37f8411ecc19d852c2 Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
@@ -51,6 +51,12 @@ filegroup {
|
||||
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
|
||||
// android.test.mock library. The implementation of android.test.mock references
|
||||
// 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.Message;
|
||||
import android.os.PowerManager;
|
||||
import android.os.SystemClock;
|
||||
import android.os.UserHandle;
|
||||
import android.provider.Settings;
|
||||
import android.util.MathUtils;
|
||||
import android.util.Slog;
|
||||
import android.view.Display;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
|
||||
/**
|
||||
* 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
|
||||
* observes for when one of the settings is changed and syncs this with the other.
|
||||
*/
|
||||
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 boolean DEBUG = false;
|
||||
private static final Uri BRIGHTNESS_URI =
|
||||
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.
|
||||
// This value is approximately 1/3 of the smallest possible brightness value.
|
||||
public static final float EPSILON = 0.001f;
|
||||
|
||||
private DisplayManager mDisplayManager;
|
||||
private static int sBrightnessUpdateCount = 1;
|
||||
|
||||
private final Context mContext;
|
||||
private final BrightnessSyncObserver mBrightnessSyncObserver;
|
||||
private final Clock mClock;
|
||||
private final Handler mHandler;
|
||||
|
||||
private final Handler mHandler = new Handler(Looper.getMainLooper()) {
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case MSG_UPDATE_FLOAT:
|
||||
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;
|
||||
private DisplayManager mDisplayManager;
|
||||
private int mLatestIntBrightness;
|
||||
private float mLatestFloatBrightness;
|
||||
private BrightnessUpdate mCurrentUpdate;
|
||||
private BrightnessUpdate mPendingUpdate;
|
||||
|
||||
public BrightnessSynchronizer(Context context) {
|
||||
this(context, Looper.getMainLooper(), SystemClock::uptimeMillis);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public BrightnessSynchronizer(Context context, Looper looper, Clock clock) {
|
||||
mContext = context;
|
||||
mClock = clock;
|
||||
mBrightnessSyncObserver = new BrightnessSyncObserver();
|
||||
mHandler = new BrightnessSynchronizerHandler(looper);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -90,24 +93,41 @@ public class BrightnessSynchronizer {
|
||||
if (mDisplayManager == null) {
|
||||
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;
|
||||
brightnessSyncObserver = new BrightnessSyncObserver();
|
||||
brightnessSyncObserver.startObserving();
|
||||
|
||||
final float currentFloatBrightness = getScreenBrightnessFloat();
|
||||
final int currentIntBrightness = getScreenBrightnessInt(mContext);
|
||||
|
||||
if (!Float.isNaN(currentFloatBrightness)) {
|
||||
updateBrightnessIntFromFloat(currentFloatBrightness);
|
||||
} else if (currentIntBrightness != -1) {
|
||||
updateBrightnessFloatFromInt(currentIntBrightness);
|
||||
if (!Float.isNaN(mLatestFloatBrightness)) {
|
||||
mPendingUpdate = new BrightnessUpdate(BrightnessUpdate.TYPE_FLOAT,
|
||||
mLatestFloatBrightness);
|
||||
} else if (mLatestIntBrightness != PowerManager.BRIGHTNESS_INVALID) {
|
||||
mPendingUpdate = new BrightnessUpdate(BrightnessUpdate.TYPE_INT,
|
||||
mLatestIntBrightness);
|
||||
} else {
|
||||
final float defaultBrightness = mContext.getResources().getFloat(
|
||||
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.
|
||||
* @return brightness
|
||||
@@ -164,76 +256,14 @@ public class BrightnessSynchronizer {
|
||||
|
||||
/**
|
||||
* Gets the stored screen brightness int from the system settings.
|
||||
* @param context for accessing settings
|
||||
*
|
||||
* @return brightness
|
||||
*/
|
||||
private static int getScreenBrightnessInt(Context context) {
|
||||
return Settings.System.getIntForUser(context.getContentResolver(),
|
||||
private int getScreenBrightnessInt() {
|
||||
return Settings.System.getIntForUser(mContext.getContentResolver(),
|
||||
Settings.System.SCREEN_BRIGHTNESS, PowerManager.BRIGHTNESS_INVALID,
|
||||
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
|
||||
* 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 boolean mIsObserving;
|
||||
|
||||
private final DisplayListener mListener = new DisplayListener() {
|
||||
@Override
|
||||
public void onDisplayAdded(int displayId) {}
|
||||
@@ -263,10 +478,7 @@ public class BrightnessSynchronizer {
|
||||
|
||||
@Override
|
||||
public void onDisplayChanged(int displayId) {
|
||||
float currentFloat = getScreenBrightnessFloat();
|
||||
int toSend = Float.floatToIntBits(currentFloat);
|
||||
mHandler.removeMessages(MSG_UPDATE_INT);
|
||||
mHandler.obtainMessage(MSG_UPDATE_INT, toSend, 0).sendToTarget();
|
||||
handleBrightnessChangeFloat(getScreenBrightnessFloat());
|
||||
}
|
||||
};
|
||||
|
||||
@@ -277,28 +489,23 @@ public class BrightnessSynchronizer {
|
||||
return;
|
||||
}
|
||||
if (BRIGHTNESS_URI.equals(uri)) {
|
||||
int currentBrightness = getScreenBrightnessInt(mContext);
|
||||
mHandler.removeMessages(MSG_UPDATE_FLOAT);
|
||||
mHandler.obtainMessage(MSG_UPDATE_FLOAT, currentBrightness, 0).sendToTarget();
|
||||
handleBrightnessChangeInt(getScreenBrightnessInt());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public void startObserving() {
|
||||
boolean isObserving() {
|
||||
return mIsObserving;
|
||||
}
|
||||
|
||||
void startObserving() {
|
||||
final ContentResolver cr = mContext.getContentResolver();
|
||||
cr.unregisterContentObserver(mContentObserver);
|
||||
cr.registerContentObserver(BRIGHTNESS_URI, false, mContentObserver,
|
||||
UserHandle.USER_ALL);
|
||||
|
||||
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();
|
||||
mDisplayModeDirector.dump(pw);
|
||||
mBrightnessSynchronizer.dump(pw);
|
||||
}
|
||||
|
||||
private static float[] getFloatArray(TypedArray array) {
|
||||
|
||||
@@ -37,6 +37,7 @@ android_test {
|
||||
srcs: [
|
||||
"src/**/*.java",
|
||||
"src/**/*.kt",
|
||||
":framework-internal-display-sources",
|
||||
],
|
||||
|
||||
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