diff --git a/api/system-current.txt b/api/system-current.txt index f203dbb8c3bca..1eaef3a958a80 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -1774,7 +1774,9 @@ package android.hardware.display { field public static final android.os.Parcelable.Creator 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 049e0025a59ba..e79873880c5c9 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -580,7 +580,9 @@ package android.hardware.display { field public static final android.os.Parcelable.Creator 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 02eb28ceb4b97..c6186bb4a58a6 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 @@ public final class BrightnessChangeEvent implements Parcelable { /** 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 @@ public final class BrightnessChangeEvent implements Parcelable { this.lastBrightness = lastBrightness; this.isDefaultBrightnessConfig = isDefaultBrightnessConfig; this.isUserSetBrightness = isUserSetBrightness; + this.colorValueBuckets = colorValueBuckets; + this.colorSampleDuration = colorSampleDuration; } /** @hide */ @@ -108,6 +131,8 @@ public final class BrightnessChangeEvent implements Parcelable { 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 @@ public final class BrightnessChangeEvent implements Parcelable { lastBrightness = source.readFloat(); isDefaultBrightnessConfig = source.readBoolean(); isUserSetBrightness = source.readBoolean(); + colorValueBuckets = source.createLongArray(); + colorSampleDuration = source.readLong(); } public static final Creator CREATOR = @@ -156,6 +183,8 @@ public final class BrightnessChangeEvent implements Parcelable { dest.writeFloat(lastBrightness); dest.writeBoolean(isDefaultBrightnessConfig); dest.writeBoolean(isUserSetBrightness); + dest.writeLongArray(colorValueBuckets); + dest.writeLong(colorSampleDuration); } /** @hide */ @@ -173,6 +202,8 @@ public final class BrightnessChangeEvent implements Parcelable { 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 @@ public final class BrightnessChangeEvent implements Parcelable { 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 96d6eeece0a33..dde757b4eb015 100644 --- a/graphics/java/android/graphics/PixelFormat.java +++ b/graphics/java/android/graphics/PixelFormat.java @@ -90,6 +90,9 @@ public class PixelFormat { 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 @@ public class PixelFormat { info.bytesPerPixel = 4; break; case RGB_888: + case HSV_888: info.bitsPerPixel = 24; info.bytesPerPixel = 3; break; @@ -227,6 +231,8 @@ public class PixelFormat { 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 019d726d8c485..727cf0e9f084e 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.Intent; 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.provider.Settings; 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.app.ColorDisplayController; 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 @@ public class BrightnessTracker { 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 @@ public class BrightnessTracker { 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 @@ public class BrightnessTracker { 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 @@ public class BrightnessTracker { mLastBrightness = initialBrightness; mStarted = true; } + enableColorSampling(); } /** Stop listening for events */ @@ -226,6 +244,7 @@ public class BrightnessTracker { synchronized (mDataCollectionLock) { mStarted = false; } + disableColorSampling(); } public void onSwitchUser(@UserIdInt int newUserId) { @@ -367,6 +386,17 @@ public class BrightnessTracker { 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 @@ public class BrightnessTracker { } 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 @@ public class BrightnessTracker { 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 @@ public class BrightnessTracker { 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 getAmbientBrightnessStats(int userId) { @@ -768,6 +892,26 @@ public class BrightnessTracker { } } + 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 @@ public class BrightnessTracker { break; case MSG_START_SENSOR_LISTENER: startSensorListener(); + enableColorSampling(); break; case MSG_STOP_SENSOR_LISTENER: stopSensorListener(); + disableColorSampling(); break; } } @@ -957,5 +1103,44 @@ public class BrightnessTracker { 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 79a654b2c0f47..e3b1245f20a57 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.SensorEvent; 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.SystemClock; 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 @@ public class BrightnessTrackerTest { 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 @@ public class BrightnessTrackerTest { 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 @@ public class BrightnessTrackerTest { 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 @@ public class BrightnessTrackerTest { + "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. + " mSecureIntSettings = new HashMap<>(); long mCurrentTimeMillis = System.currentTimeMillis(); long mElapsedRealtimeNanos = SystemClock.elapsedRealtimeNanos(); @@ -723,6 +807,12 @@ public class BrightnessTrackerTest { 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 @@ public class BrightnessTrackerTest { 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; + } } }