Merge "Pipe through color sampling to BrightnessChangeEvent"
diff --git a/api/system-current.txt b/api/system-current.txt
index cba137a..94dec1c 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -1843,7 +1843,9 @@
field public static final android.os.Parcelable.Creator<android.hardware.display.BrightnessChangeEvent> CREATOR;
field public final float batteryLevel;
field public final float brightness;
+ field public final long colorSampleDuration;
field public final int colorTemperature;
+ field @Nullable public final long[] colorValueBuckets;
field public final boolean isDefaultBrightnessConfig;
field public final boolean isUserSetBrightness;
field public final float lastBrightness;
diff --git a/api/test-current.txt b/api/test-current.txt
index 2a7aa17..81eb1eb 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -660,7 +660,9 @@
field public static final android.os.Parcelable.Creator<android.hardware.display.BrightnessChangeEvent> CREATOR;
field public final float batteryLevel;
field public final float brightness;
+ field public final long colorSampleDuration;
field public final int colorTemperature;
+ field @Nullable public final long[] colorValueBuckets;
field public final boolean isDefaultBrightnessConfig;
field public final boolean isUserSetBrightness;
field public final float lastBrightness;
diff --git a/core/java/android/hardware/display/BrightnessChangeEvent.java b/core/java/android/hardware/display/BrightnessChangeEvent.java
index 02eb28c..c6186bb 100644
--- a/core/java/android/hardware/display/BrightnessChangeEvent.java
+++ b/core/java/android/hardware/display/BrightnessChangeEvent.java
@@ -16,11 +16,15 @@
package android.hardware.display;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.os.Parcel;
import android.os.Parcelable;
+import java.util.Objects;
+
/**
* Data about a brightness settings change.
*
@@ -72,12 +76,29 @@
/** Whether brightness curve includes a user brightness point */
public final boolean isUserSetBrightness;
+ /**
+ * Histogram counting how many times a pixel of a given value was displayed onscreen for the
+ * Value component of HSV if the device supports color sampling, if the device does not support
+ * color sampling the value will be null.
+ * The buckets of the histogram are evenly weighted, the number of buckets is device specific.
+ * For example if we had {10, 6, 4, 1} this means that 10 pixels were in the range
+ * [0x00,0x3f], 6 pixels were in the range [0x40,0x7f] etc.
+ */
+ @Nullable
+ public final long[] colorValueBuckets;
+
+ /**
+ * How many milliseconds of data are contained in the colorValueBuckets.
+ */
+ public final long colorSampleDuration;
+
/** @hide */
private BrightnessChangeEvent(float brightness, long timeStamp, String packageName,
int userId, float[] luxValues, long[] luxTimestamps, float batteryLevel,
float powerBrightnessFactor, boolean nightMode, int colorTemperature,
- float lastBrightness, boolean isDefaultBrightnessConfig, boolean isUserSetBrightness) {
+ float lastBrightness, boolean isDefaultBrightnessConfig, boolean isUserSetBrightness,
+ long[] colorValueBuckets, long colorSampleDuration) {
this.brightness = brightness;
this.timeStamp = timeStamp;
this.packageName = packageName;
@@ -91,6 +112,8 @@
this.lastBrightness = lastBrightness;
this.isDefaultBrightnessConfig = isDefaultBrightnessConfig;
this.isUserSetBrightness = isUserSetBrightness;
+ this.colorValueBuckets = colorValueBuckets;
+ this.colorSampleDuration = colorSampleDuration;
}
/** @hide */
@@ -108,6 +131,8 @@
this.lastBrightness = other.lastBrightness;
this.isDefaultBrightnessConfig = other.isDefaultBrightnessConfig;
this.isUserSetBrightness = other.isUserSetBrightness;
+ this.colorValueBuckets = other.colorValueBuckets;
+ this.colorSampleDuration = other.colorSampleDuration;
}
private BrightnessChangeEvent(Parcel source) {
@@ -124,6 +149,8 @@
lastBrightness = source.readFloat();
isDefaultBrightnessConfig = source.readBoolean();
isUserSetBrightness = source.readBoolean();
+ colorValueBuckets = source.createLongArray();
+ colorSampleDuration = source.readLong();
}
public static final Creator<BrightnessChangeEvent> CREATOR =
@@ -156,6 +183,8 @@
dest.writeFloat(lastBrightness);
dest.writeBoolean(isDefaultBrightnessConfig);
dest.writeBoolean(isUserSetBrightness);
+ dest.writeLongArray(colorValueBuckets);
+ dest.writeLong(colorSampleDuration);
}
/** @hide */
@@ -173,6 +202,8 @@
private float mLastBrightness;
private boolean mIsDefaultBrightnessConfig;
private boolean mIsUserSetBrightness;
+ private long[] mColorValueBuckets;
+ private long mColorSampleDuration;
/** {@see BrightnessChangeEvent#brightness} */
public Builder setBrightness(float brightness) {
@@ -252,12 +283,21 @@
return this;
}
+ /** {@see BrightnessChangeEvent#valueBuckets} */
+ public Builder setColorValues(@NonNull long[] colorValueBuckets, long colorSampleDuration) {
+ Objects.requireNonNull(colorValueBuckets);
+ mColorValueBuckets = colorValueBuckets;
+ mColorSampleDuration = colorSampleDuration;
+ return this;
+ }
+
/** Builds a BrightnessChangeEvent */
public BrightnessChangeEvent build() {
return new BrightnessChangeEvent(mBrightness, mTimeStamp,
mPackageName, mUserId, mLuxValues, mLuxTimestamps, mBatteryLevel,
mPowerBrightnessFactor, mNightMode, mColorTemperature, mLastBrightness,
- mIsDefaultBrightnessConfig, mIsUserSetBrightness);
+ mIsDefaultBrightnessConfig, mIsUserSetBrightness, mColorValueBuckets,
+ mColorSampleDuration);
}
}
}
diff --git a/graphics/java/android/graphics/PixelFormat.java b/graphics/java/android/graphics/PixelFormat.java
index 96d6eee..dde757b 100644
--- a/graphics/java/android/graphics/PixelFormat.java
+++ b/graphics/java/android/graphics/PixelFormat.java
@@ -90,6 +90,9 @@
public static final int RGBA_F16 = 0x16;
public static final int RGBA_1010102 = 0x2B;
+ /** @hide */
+ public static final int HSV_888 = 0x37;
+
/**
* @deprecated use {@link android.graphics.ImageFormat#JPEG
* ImageFormat.JPEG} instead.
@@ -109,6 +112,7 @@
info.bytesPerPixel = 4;
break;
case RGB_888:
+ case HSV_888:
info.bitsPerPixel = 24;
info.bytesPerPixel = 3;
break;
@@ -227,6 +231,8 @@
return "RGBA_F16";
case RGBA_1010102:
return "RGBA_1010102";
+ case HSV_888:
+ return "HSV_888";
case JPEG:
return "JPEG";
default:
diff --git a/services/core/java/com/android/server/display/BrightnessTracker.java b/services/core/java/com/android/server/display/BrightnessTracker.java
index 019d726..727cf0e 100644
--- a/services/core/java/com/android/server/display/BrightnessTracker.java
+++ b/services/core/java/com/android/server/display/BrightnessTracker.java
@@ -27,12 +27,17 @@
import android.content.IntentFilter;
import android.content.pm.ParceledListSlice;
import android.database.ContentObserver;
+import android.graphics.PixelFormat;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.hardware.display.AmbientBrightnessDayStats;
import android.hardware.display.BrightnessChangeEvent;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayManagerInternal;
+import android.hardware.display.DisplayedContentSample;
+import android.hardware.display.DisplayedContentSamplingAttributes;
import android.net.Uri;
import android.os.BatteryManager;
import android.os.Environment;
@@ -48,6 +53,7 @@
import android.util.AtomicFile;
import android.util.Slog;
import android.util.Xml;
+import android.view.Display;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -55,6 +61,7 @@
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.RingBuffer;
+import com.android.server.LocalServices;
import libcore.io.IoUtils;
@@ -111,6 +118,8 @@
private static final String ATTR_DEFAULT_CONFIG = "defaultConfig";
private static final String ATTR_POWER_SAVE = "powerSaveFactor";
private static final String ATTR_USER_POINT = "userPoint";
+ private static final String ATTR_COLOR_SAMPLE_DURATION = "colorSampleDuration";
+ private static final String ATTR_COLOR_VALUE_BUCKETS = "colorValueBuckets";
private static final int MSG_BACKGROUND_START = 0;
private static final int MSG_BRIGHTNESS_CHANGED = 1;
@@ -119,6 +128,10 @@
private static final SimpleDateFormat FORMAT = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
+ private static final long COLOR_SAMPLE_DURATION = TimeUnit.SECONDS.toSeconds(10);
+ // Sample chanel 2 of HSV which is the Value component.
+ private static final int COLOR_SAMPLE_COMPONENT_MASK = 0x1 << 2;
+
// Lock held while accessing mEvents, is held while writing events to flash.
private final Object mEventsLock = new Object();
@GuardedBy("mEventsLock")
@@ -136,12 +149,16 @@
private final ContentResolver mContentResolver;
private final Handler mBgHandler;
- // mBroadcastReceiver, mSensorListener, mSettingsObserver and mSensorRegistered
- // should only be used on the mBgHandler thread.
+ // These members should only be accessed on the mBgHandler thread.
private BroadcastReceiver mBroadcastReceiver;
private SensorListener mSensorListener;
private SettingsObserver mSettingsObserver;
+ private DisplayListener mDisplayListener;
private boolean mSensorRegistered;
+ private boolean mColorSamplingEnabled;
+ private int mNoFramesToSample;
+ private float mFrameRate;
+ // End of block of members that should only be accessed on the mBgHandler thread.
private @UserIdInt int mCurrentUserId = UserHandle.USER_NULL;
@@ -208,6 +225,7 @@
mLastBrightness = initialBrightness;
mStarted = true;
}
+ enableColorSampling();
}
/** Stop listening for events */
@@ -226,6 +244,7 @@
synchronized (mDataCollectionLock) {
mStarted = false;
}
+ disableColorSampling();
}
public void onSwitchUser(@UserIdInt int newUserId) {
@@ -367,6 +386,17 @@
builder.setColorTemperature(mInjector.getColorTemperature(mContext,
UserHandle.USER_CURRENT));
+ if (mColorSamplingEnabled) {
+ DisplayedContentSample sample = mInjector.sampleColor(mNoFramesToSample);
+ if (sample != null && sample.getSampleComponent(
+ DisplayedContentSample.ColorComponent.CHANNEL2) != null) {
+ float numMillis = (sample.getNumFrames() / mFrameRate) * 1000.0f;
+ builder.setColorValues(
+ sample.getSampleComponent(DisplayedContentSample.ColorComponent.CHANNEL2),
+ Math.round(numMillis));
+ }
+ }
+
BrightnessChangeEvent event = builder.build();
if (DEBUG) {
Slog.d(TAG, "Event " + event.brightness + " " + event.packageName);
@@ -541,6 +571,19 @@
}
out.attribute(null, ATTR_LUX, luxValues.toString());
out.attribute(null, ATTR_LUX_TIMESTAMPS, luxTimestamps.toString());
+ if (toWrite[i].colorValueBuckets != null
+ && toWrite[i].colorValueBuckets.length > 0) {
+ out.attribute(null, ATTR_COLOR_SAMPLE_DURATION,
+ Long.toString(toWrite[i].colorSampleDuration));
+ StringBuilder buckets = new StringBuilder();
+ for (int j = 0; j < toWrite[i].colorValueBuckets.length; ++j) {
+ if (j > 0) {
+ buckets.append(',');
+ }
+ buckets.append(Long.toString(toWrite[i].colorValueBuckets[j]));
+ }
+ out.attribute(null, ATTR_COLOR_VALUE_BUCKETS, buckets.toString());
+ }
out.endTag(null, TAG_EVENT);
}
}
@@ -628,6 +671,20 @@
builder.setUserBrightnessPoint(Boolean.parseBoolean(userPoint));
}
+ String colorSampleDurationString =
+ parser.getAttributeValue(null, ATTR_COLOR_SAMPLE_DURATION);
+ String colorValueBucketsString =
+ parser.getAttributeValue(null, ATTR_COLOR_VALUE_BUCKETS);
+ if (colorSampleDurationString != null && colorValueBucketsString != null) {
+ long colorSampleDuration = Long.parseLong(colorSampleDurationString);
+ String[] buckets = colorValueBucketsString.split(",");
+ long[] bucketValues = new long[buckets.length];
+ for (int i = 0; i < bucketValues.length; ++i) {
+ bucketValues[i] = Long.parseLong(buckets[i]);
+ }
+ builder.setColorValues(bucketValues, colorSampleDuration);
+ }
+
BrightnessChangeEvent event = builder.build();
if (DEBUG) {
Slog.i(TAG, "Read event " + event.brightness
@@ -695,6 +752,73 @@
private void dumpLocal(PrintWriter pw) {
pw.println(" mSensorRegistered=" + mSensorRegistered);
+ pw.println(" mColorSamplingEnabled=" + mColorSamplingEnabled);
+ pw.println(" mNoFramesToSample=" + mNoFramesToSample);
+ pw.println(" mFrameRate=" + mFrameRate);
+ }
+
+ private void enableColorSampling() {
+ if (!mInjector.isBrightnessModeAutomatic(mContentResolver)
+ || !mInjector.isInteractive(mContext)
+ || mColorSamplingEnabled) {
+ return;
+ }
+
+ mFrameRate = mInjector.getFrameRate(mContext);
+ if (mFrameRate <= 0) {
+ Slog.wtf(TAG, "Default display has a zero or negative framerate.");
+ return;
+ }
+ mNoFramesToSample = (int) (mFrameRate * COLOR_SAMPLE_DURATION);
+
+ DisplayedContentSamplingAttributes attributes = mInjector.getSamplingAttributes();
+ if (DEBUG && attributes != null) {
+ Slog.d(TAG, "Color sampling"
+ + " mask=0x" + Integer.toHexString(attributes.getComponentMask())
+ + " dataSpace=0x" + Integer.toHexString(attributes.getDataspace())
+ + " pixelFormat=0x" + Integer.toHexString(attributes.getPixelFormat()));
+ }
+ // Do we support sampling the Value component of HSV
+ if (attributes != null && attributes.getPixelFormat() == PixelFormat.HSV_888
+ && (attributes.getComponentMask() & COLOR_SAMPLE_COMPONENT_MASK) != 0) {
+
+ mColorSamplingEnabled = mInjector.enableColorSampling(/* enable= */true,
+ mNoFramesToSample);
+ if (DEBUG) {
+ Slog.i(TAG, "turning on color sampling for "
+ + mNoFramesToSample + " frames, success=" + mColorSamplingEnabled);
+ }
+ }
+ if (mColorSamplingEnabled && mDisplayListener == null) {
+ mDisplayListener = new DisplayListener();
+ mInjector.registerDisplayListener(mContext, mDisplayListener, mBgHandler);
+ }
+ }
+
+ private void disableColorSampling() {
+ if (!mColorSamplingEnabled) {
+ return;
+ }
+ mInjector.enableColorSampling(/* enable= */ false, /* noFrames= */ 0);
+ mColorSamplingEnabled = false;
+ if (mDisplayListener != null) {
+ mInjector.unRegisterDisplayListener(mContext, mDisplayListener);
+ mDisplayListener = null;
+ }
+ if (DEBUG) {
+ Slog.i(TAG, "turning off color sampling");
+ }
+ }
+
+ private void updateColorSampling() {
+ if (!mColorSamplingEnabled) {
+ return;
+ }
+ float frameRate = mInjector.getFrameRate(mContext);
+ if (frameRate != mFrameRate) {
+ disableColorSampling();
+ enableColorSampling();
+ }
}
public ParceledListSlice<AmbientBrightnessDayStats> getAmbientBrightnessStats(int userId) {
@@ -768,6 +892,26 @@
}
}
+ private final class DisplayListener implements DisplayManager.DisplayListener {
+
+ @Override
+ public void onDisplayAdded(int displayId) {
+ // Ignore
+ }
+
+ @Override
+ public void onDisplayRemoved(int displayId) {
+ // Ignore
+ }
+
+ @Override
+ public void onDisplayChanged(int displayId) {
+ if (displayId == Display.DEFAULT_DISPLAY) {
+ updateColorSampling();
+ }
+ }
+ }
+
private final class SettingsObserver extends ContentObserver {
public SettingsObserver(Handler handler) {
super(handler);
@@ -828,9 +972,11 @@
break;
case MSG_START_SENSOR_LISTENER:
startSensorListener();
+ enableColorSampling();
break;
case MSG_STOP_SENSOR_LISTENER:
stopSensorListener();
+ disableColorSampling();
break;
}
}
@@ -957,5 +1103,44 @@
public boolean isNightModeActive(Context context, int userId) {
return new ColorDisplayController(context, userId).isActivated();
}
+
+ public DisplayedContentSample sampleColor(int noFramesToSample) {
+ final DisplayManagerInternal displayManagerInternal =
+ LocalServices.getService(DisplayManagerInternal.class);
+ return displayManagerInternal.getDisplayedContentSample(
+ Display.DEFAULT_DISPLAY, noFramesToSample, 0);
+ }
+
+ public float getFrameRate(Context context) {
+ final DisplayManager displayManager = context.getSystemService(DisplayManager.class);
+ Display display = displayManager.getDisplay(Display.DEFAULT_DISPLAY);
+ return display.getRefreshRate();
+ }
+
+ public DisplayedContentSamplingAttributes getSamplingAttributes() {
+ final DisplayManagerInternal displayManagerInternal =
+ LocalServices.getService(DisplayManagerInternal.class);
+ return displayManagerInternal.getDisplayedContentSamplingAttributes(
+ Display.DEFAULT_DISPLAY);
+ }
+
+ public boolean enableColorSampling(boolean enable, int noFrames) {
+ final DisplayManagerInternal displayManagerInternal =
+ LocalServices.getService(DisplayManagerInternal.class);
+ return displayManagerInternal.setDisplayedContentSamplingEnabled(
+ Display.DEFAULT_DISPLAY, enable, COLOR_SAMPLE_COMPONENT_MASK, noFrames);
+ }
+
+ public void registerDisplayListener(Context context,
+ DisplayManager.DisplayListener listener, Handler handler) {
+ final DisplayManager displayManager = context.getSystemService(DisplayManager.class);
+ displayManager.registerDisplayListener(listener, handler);
+ }
+
+ public void unRegisterDisplayListener(Context context,
+ DisplayManager.DisplayListener listener) {
+ final DisplayManager displayManager = context.getSystemService(DisplayManager.class);
+ displayManager.unregisterDisplayListener(listener);
+ }
}
}
diff --git a/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java b/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
index 79a654b..e3b1245 100644
--- a/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
@@ -37,6 +37,9 @@
import android.hardware.SensorEventListener;
import android.hardware.display.AmbientBrightnessDayStats;
import android.hardware.display.BrightnessChangeEvent;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.DisplayedContentSample;
+import android.hardware.display.DisplayedContentSamplingAttributes;
import android.os.BatteryManager;
import android.os.Handler;
import android.os.HandlerThread;
@@ -47,6 +50,7 @@
import android.os.UserManager;
import android.provider.Settings;
import android.util.AtomicFile;
+import android.view.Display;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
@@ -116,28 +120,81 @@
assertTrue(mInjector.mIdleScheduled);
mInjector.sendScreenChange(/*screen on */ true);
assertNotNull(mInjector.mSensorListener);
+ assertTrue(mInjector.mColorSamplingEnabled);
mInjector.sendScreenChange(/*screen on */ false);
assertNull(mInjector.mSensorListener);
+ assertFalse(mInjector.mColorSamplingEnabled);
// Turn screen on while brightness mode is manual
mInjector.setBrightnessMode(/* isBrightnessModeAutomatic */ false);
mInjector.sendScreenChange(/*screen on */ true);
assertNull(mInjector.mSensorListener);
+ assertFalse(mInjector.mColorSamplingEnabled);
// Set brightness mode to automatic while screen is off.
mInjector.sendScreenChange(/*screen on */ false);
mInjector.setBrightnessMode(/* isBrightnessModeAutomatic */ true);
assertNull(mInjector.mSensorListener);
+ assertFalse(mInjector.mColorSamplingEnabled);
// Turn on screen while brightness mode is automatic.
mInjector.sendScreenChange(/*screen on */ true);
assertNotNull(mInjector.mSensorListener);
+ assertTrue(mInjector.mColorSamplingEnabled);
mTracker.stop();
assertNull(mInjector.mSensorListener);
assertNull(mInjector.mBroadcastReceiver);
assertFalse(mInjector.mIdleScheduled);
+ assertFalse(mInjector.mColorSamplingEnabled);
+ }
+
+ @Test
+ public void testNoColorSampling_WrongPixelFormat() {
+ mInjector.mDefaultSamplingAttributes =
+ new DisplayedContentSamplingAttributes(
+ 0x23,
+ mInjector.mDefaultSamplingAttributes.getDataspace(),
+ mInjector.mDefaultSamplingAttributes.getComponentMask());
+ startTracker(mTracker);
+ assertFalse(mInjector.mColorSamplingEnabled);
+ assertNull(mInjector.mDisplayListener);
+ }
+
+ @Test
+ public void testNoColorSampling_MissingComponent() {
+ mInjector.mDefaultSamplingAttributes =
+ new DisplayedContentSamplingAttributes(
+ mInjector.mDefaultSamplingAttributes.getPixelFormat(),
+ mInjector.mDefaultSamplingAttributes.getDataspace(),
+ 0x2);
+ startTracker(mTracker);
+ assertFalse(mInjector.mColorSamplingEnabled);
+ assertNull(mInjector.mDisplayListener);
+ }
+
+ @Test
+ public void testNoColorSampling_NoSupport() {
+ mInjector.mDefaultSamplingAttributes = null;
+ startTracker(mTracker);
+ assertFalse(mInjector.mColorSamplingEnabled);
+ assertNull(mInjector.mDisplayListener);
+ }
+
+ @Test
+ public void testColorSampling_FrameRateChange() {
+ startTracker(mTracker);
+ assertTrue(mInjector.mColorSamplingEnabled);
+ assertNotNull(mInjector.mDisplayListener);
+ int noFramesSampled = mInjector.mNoColorSamplingFrames;
+ mInjector.mFrameRate = 120.0f;
+ // Wrong display
+ mInjector.mDisplayListener.onDisplayChanged(Display.DEFAULT_DISPLAY + 10);
+ assertEquals(noFramesSampled, mInjector.mNoColorSamplingFrames);
+ // Correct display
+ mInjector.mDisplayListener.onDisplayChanged(Display.DEFAULT_DISPLAY);
+ assertEquals(noFramesSampled * 2, mInjector.mNoColorSamplingFrames);
}
@Test
@@ -149,26 +206,41 @@
assertNotNull(mInjector.mBroadcastReceiver);
assertNotNull(mInjector.mContentObserver);
assertTrue(mInjector.mIdleScheduled);
+ assertFalse(mInjector.mColorSamplingEnabled);
+ assertNull(mInjector.mDisplayListener);
mInjector.setBrightnessMode(/*isBrightnessModeAutomatic*/ true);
assertNotNull(mInjector.mSensorListener);
+ assertTrue(mInjector.mColorSamplingEnabled);
+ assertNotNull(mInjector.mDisplayListener);
SensorEventListener listener = mInjector.mSensorListener;
+ DisplayManager.DisplayListener displayListener = mInjector.mDisplayListener;
mInjector.mSensorListener = null;
+ mInjector.mColorSamplingEnabled = false;
+ mInjector.mDisplayListener = null;
// Duplicate notification
mInjector.setBrightnessMode(/*isBrightnessModeAutomatic*/ true);
// Sensor shouldn't have been registered as it was already registered.
assertNull(mInjector.mSensorListener);
+ assertFalse(mInjector.mColorSamplingEnabled);
+ assertNull(mInjector.mDisplayListener);
mInjector.mSensorListener = listener;
+ mInjector.mDisplayListener = displayListener;
+ mInjector.mColorSamplingEnabled = true;
mInjector.setBrightnessMode(/*isBrightnessModeAutomatic*/ false);
assertNull(mInjector.mSensorListener);
+ assertFalse(mInjector.mColorSamplingEnabled);
+ assertNull(mInjector.mDisplayListener);
mTracker.stop();
assertNull(mInjector.mSensorListener);
assertNull(mInjector.mBroadcastReceiver);
assertNull(mInjector.mContentObserver);
assertFalse(mInjector.mIdleScheduled);
+ assertFalse(mInjector.mColorSamplingEnabled);
+ assertNull(mInjector.mDisplayListener);
}
@Test
@@ -229,6 +301,8 @@
assertEquals(3333, event.colorTemperature);
assertEquals("a.package", event.packageName);
assertEquals(0, event.userId);
+ assertArrayEquals(new long[] {1, 10, 100, 1000, 300, 30, 10, 1}, event.colorValueBuckets);
+ assertEquals(10000, event.colorSampleDuration);
assertEquals(1, eventsNoPackage.size());
assertNull(eventsNoPackage.get(0).packageName);
@@ -342,7 +416,8 @@
+ "lastNits=\"32\" "
+ "batteryLevel=\"0.5\" nightMode=\"true\" colorTemperature=\"3235\"\n"
+ "lux=\"132.2,131.1\" luxTimestamps=\""
- + Long.toString(someTimeAgo) + "," + Long.toString(someTimeAgo) + "\"/>"
+ + Long.toString(someTimeAgo) + "," + Long.toString(someTimeAgo) + "\""
+ + "colorSampleDuration=\"3456\" colorValueBuckets=\"123,598,23,19\"/>"
// Event that is too old so shouldn't show up.
+ "<event nits=\"142\" timestamp=\""
+ Long.toString(twoMonthsAgo) + "\" packageName=\""
@@ -368,6 +443,7 @@
assertTrue(event.isDefaultBrightnessConfig);
assertEquals(0.5f, event.powerBrightnessFactor, FLOAT_DELTA);
assertTrue(event.isUserSetBrightness);
+ assertNull(event.colorValueBuckets);
events = tracker.getEvents(1, true).getList();
assertEquals(1, events.size());
@@ -386,6 +462,8 @@
assertFalse(event.isDefaultBrightnessConfig);
assertEquals(1.0, event.powerBrightnessFactor, FLOAT_DELTA);
assertFalse(event.isUserSetBrightness);
+ assertEquals(3456L, event.colorSampleDuration);
+ assertArrayEquals(new long[] {123L, 598L, 23L, 19L}, event.colorValueBuckets);
// Pretend user 1 is a profile of user 0.
mInjector.mProfiles = new int[]{0, 1};
@@ -481,6 +559,8 @@
assertEquals(0.5f, event.powerBrightnessFactor, FLOAT_DELTA);
assertTrue(event.isUserSetBrightness);
assertFalse(event.isDefaultBrightnessConfig);
+ assertArrayEquals(new long[] {1, 10, 100, 1000, 300, 30, 10, 1}, event.colorValueBuckets);
+ assertEquals(10000, event.colorSampleDuration);
}
@Test
@@ -546,6 +626,7 @@
builder.setNightMode(false);
builder.setColorTemperature(345);
builder.setLastBrightness(50f);
+ builder.setColorValues(new long[] {23, 34, 45}, 1000L);
BrightnessChangeEvent event = builder.build();
event.writeToParcel(parcel, 0);
@@ -568,6 +649,8 @@
assertEquals(event.nightMode, event2.nightMode);
assertEquals(event.colorTemperature, event2.colorTemperature);
assertEquals(event.lastBrightness, event2.lastBrightness, FLOAT_DELTA);
+ assertArrayEquals(event.colorValueBuckets, event2.colorValueBuckets);
+ assertEquals(event.colorSampleDuration, event2.colorSampleDuration);
parcel = Parcel.obtain();
builder.setBatteryLevel(Float.NaN);
@@ -714,6 +797,7 @@
private class TestInjector extends BrightnessTracker.Injector {
SensorEventListener mSensorListener;
BroadcastReceiver mBroadcastReceiver;
+ DisplayManager.DisplayListener mDisplayListener;
Map<String, Integer> mSecureIntSettings = new HashMap<>();
long mCurrentTimeMillis = System.currentTimeMillis();
long mElapsedRealtimeNanos = SystemClock.elapsedRealtimeNanos();
@@ -723,6 +807,12 @@
int[] mProfiles;
ContentObserver mContentObserver;
boolean mIsBrightnessModeAutomatic = true;
+ boolean mColorSamplingEnabled = false;
+ DisplayedContentSamplingAttributes mDefaultSamplingAttributes =
+ new DisplayedContentSamplingAttributes(0x37, 0, 0x4);
+ float mFrameRate = 60.0f;
+ int mNoColorSamplingFrames;
+
public TestInjector(Handler handler) {
mHandler = handler;
@@ -882,5 +972,43 @@
return mSecureIntSettings.getOrDefault(Settings.Secure.NIGHT_DISPLAY_ACTIVATED,
0) == 1;
}
+
+ @Override
+ public DisplayedContentSample sampleColor(int noFramesToSample) {
+ return new DisplayedContentSample(600L,
+ null,
+ null,
+ new long[] {1, 10, 100, 1000, 300, 30, 10, 1},
+ null);
+ }
+
+ @Override
+ public float getFrameRate(Context context) {
+ return mFrameRate;
+ }
+
+ @Override
+ public DisplayedContentSamplingAttributes getSamplingAttributes() {
+ return mDefaultSamplingAttributes;
+ }
+
+ @Override
+ public boolean enableColorSampling(boolean enable, int noFrames) {
+ mColorSamplingEnabled = enable;
+ mNoColorSamplingFrames = noFrames;
+ return true;
+ }
+
+ @Override
+ public void registerDisplayListener(Context context,
+ DisplayManager.DisplayListener listener, Handler handler) {
+ mDisplayListener = listener;
+ }
+
+ @Override
+ public void unRegisterDisplayListener(Context context,
+ DisplayManager.DisplayListener listener) {
+ mDisplayListener = null;
+ }
}
}