Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a28aa5e6 authored by Kevin DuBois's avatar Kevin DuBois Committed by Android (Google) Code Review
Browse files

Merge "Pipe through color sampling to BrightnessChangeEvent"

parents 1da09e44 908108aa
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -1843,7 +1843,9 @@ package android.hardware.display {
    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;
+2 −0
Original line number Diff line number Diff line
@@ -660,7 +660,9 @@ package android.hardware.display {
    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;
+42 −2
Original line number Diff line number Diff line
@@ -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<BrightnessChangeEvent> 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);
        }
    }
}
+6 −0
Original line number Diff line number Diff line
@@ -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:
+187 −2
Original line number Diff line number Diff line
@@ -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<AmbientBrightnessDayStats> 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);
        }
    }
}
Loading