1 /*
2  * Copyright 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.display;
18 
19 import android.annotation.Nullable;
20 import android.annotation.UserIdInt;
21 import android.app.ActivityManager;
22 import android.app.ActivityTaskManager;
23 import android.content.BroadcastReceiver;
24 import android.content.ContentResolver;
25 import android.content.Context;
26 import android.content.Intent;
27 import android.content.IntentFilter;
28 import android.content.pm.ParceledListSlice;
29 import android.database.ContentObserver;
30 import android.graphics.PixelFormat;
31 import android.hardware.Sensor;
32 import android.hardware.SensorEvent;
33 import android.hardware.SensorEventListener;
34 import android.hardware.SensorManager;
35 import android.hardware.display.AmbientBrightnessDayStats;
36 import android.hardware.display.BrightnessChangeEvent;
37 import android.hardware.display.BrightnessConfiguration;
38 import android.hardware.display.ColorDisplayManager;
39 import android.hardware.display.DisplayManager;
40 import android.hardware.display.DisplayManagerInternal;
41 import android.hardware.display.DisplayedContentSample;
42 import android.hardware.display.DisplayedContentSamplingAttributes;
43 import android.net.Uri;
44 import android.os.BatteryManager;
45 import android.os.Environment;
46 import android.os.Handler;
47 import android.os.Looper;
48 import android.os.Message;
49 import android.os.PowerManager;
50 import android.os.RemoteException;
51 import android.os.SystemClock;
52 import android.os.UserHandle;
53 import android.os.UserManager;
54 import android.provider.Settings;
55 import android.util.AtomicFile;
56 import android.util.Slog;
57 import android.util.Xml;
58 import android.view.Display;
59 
60 import com.android.internal.annotations.GuardedBy;
61 import com.android.internal.annotations.VisibleForTesting;
62 import com.android.internal.os.BackgroundThread;
63 import com.android.internal.util.FastXmlSerializer;
64 import com.android.internal.util.RingBuffer;
65 import com.android.server.LocalServices;
66 
67 import libcore.io.IoUtils;
68 
69 import org.xmlpull.v1.XmlPullParser;
70 import org.xmlpull.v1.XmlPullParserException;
71 import org.xmlpull.v1.XmlSerializer;
72 
73 import java.io.File;
74 import java.io.FileInputStream;
75 import java.io.FileOutputStream;
76 import java.io.IOException;
77 import java.io.InputStream;
78 import java.io.OutputStream;
79 import java.io.PrintWriter;
80 import java.nio.charset.StandardCharsets;
81 import java.text.SimpleDateFormat;
82 import java.util.ArrayDeque;
83 import java.util.ArrayList;
84 import java.util.Date;
85 import java.util.Deque;
86 import java.util.HashMap;
87 import java.util.Map;
88 import java.util.concurrent.TimeUnit;
89 
90 /**
91  * Class that tracks recent brightness settings changes and stores
92  * associated information such as light sensor readings.
93  */
94 public class BrightnessTracker {
95 
96     static final String TAG = "BrightnessTracker";
97     static final boolean DEBUG = false;
98 
99     private static final String EVENTS_FILE = "brightness_events.xml";
100     private static final String AMBIENT_BRIGHTNESS_STATS_FILE = "ambient_brightness_stats.xml";
101     private static final int MAX_EVENTS = 100;
102     // Discard events when reading or writing that are older than this.
103     private static final long MAX_EVENT_AGE = TimeUnit.DAYS.toMillis(30);
104     // Time over which we keep lux sensor readings.
105     private static final long LUX_EVENT_HORIZON = TimeUnit.SECONDS.toNanos(10);
106 
107     private static final String TAG_EVENTS = "events";
108     private static final String TAG_EVENT = "event";
109     private static final String ATTR_NITS = "nits";
110     private static final String ATTR_TIMESTAMP = "timestamp";
111     private static final String ATTR_PACKAGE_NAME = "packageName";
112     private static final String ATTR_USER = "user";
113     private static final String ATTR_LUX = "lux";
114     private static final String ATTR_LUX_TIMESTAMPS = "luxTimestamps";
115     private static final String ATTR_BATTERY_LEVEL = "batteryLevel";
116     private static final String ATTR_NIGHT_MODE = "nightMode";
117     private static final String ATTR_COLOR_TEMPERATURE = "colorTemperature";
118     private static final String ATTR_LAST_NITS = "lastNits";
119     private static final String ATTR_DEFAULT_CONFIG = "defaultConfig";
120     private static final String ATTR_POWER_SAVE = "powerSaveFactor";
121     private static final String ATTR_USER_POINT = "userPoint";
122     private static final String ATTR_COLOR_SAMPLE_DURATION = "colorSampleDuration";
123     private static final String ATTR_COLOR_VALUE_BUCKETS = "colorValueBuckets";
124 
125     private static final int MSG_BACKGROUND_START = 0;
126     private static final int MSG_BRIGHTNESS_CHANGED = 1;
127     private static final int MSG_STOP_SENSOR_LISTENER = 2;
128     private static final int MSG_START_SENSOR_LISTENER = 3;
129     private static final int MSG_BRIGHTNESS_CONFIG_CHANGED = 4;
130 
131     private static final SimpleDateFormat FORMAT = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
132 
133     private static final long COLOR_SAMPLE_DURATION = TimeUnit.SECONDS.toSeconds(10);
134     // Sample chanel 2 of HSV which is the Value component.
135     private static final int COLOR_SAMPLE_COMPONENT_MASK = 0x1 << 2;
136 
137     // Lock held while accessing mEvents, is held while writing events to flash.
138     private final Object mEventsLock = new Object();
139     @GuardedBy("mEventsLock")
140     private RingBuffer<BrightnessChangeEvent> mEvents
141             = new RingBuffer<>(BrightnessChangeEvent.class, MAX_EVENTS);
142     @GuardedBy("mEventsLock")
143     private boolean mEventsDirty;
144 
145     private volatile boolean mWriteBrightnessTrackerStateScheduled;
146 
147     private AmbientBrightnessStatsTracker mAmbientBrightnessStatsTracker;
148 
149     private final UserManager mUserManager;
150     private final Context mContext;
151     private final ContentResolver mContentResolver;
152     private final Handler mBgHandler;
153 
154     // These members should only be accessed on the mBgHandler thread.
155     private BroadcastReceiver mBroadcastReceiver;
156     private SensorListener mSensorListener;
157     private SettingsObserver mSettingsObserver;
158     private DisplayListener mDisplayListener;
159     private boolean mSensorRegistered;
160     private boolean mColorSamplingEnabled;
161     private int mNoFramesToSample;
162     private float mFrameRate;
163     private BrightnessConfiguration mBrightnessConfiguration;
164     // End of block of members that should only be accessed on the mBgHandler thread.
165 
166     private @UserIdInt int mCurrentUserId = UserHandle.USER_NULL;
167 
168     // Lock held while collecting data related to brightness changes.
169     private final Object mDataCollectionLock = new Object();
170     @GuardedBy("mDataCollectionLock")
171     private Deque<LightData> mLastSensorReadings = new ArrayDeque<>();
172     @GuardedBy("mDataCollectionLock")
173     private float mLastBatteryLevel = Float.NaN;
174     @GuardedBy("mDataCollectionLock")
175     private float mLastBrightness = -1;
176     @GuardedBy("mDataCollectionLock")
177     private boolean mStarted;
178 
179     private final Injector mInjector;
180 
BrightnessTracker(Context context, @Nullable Injector injector)181     public BrightnessTracker(Context context, @Nullable Injector injector) {
182         // Note this will be called very early in boot, other system
183         // services may not be present.
184         mContext = context;
185         mContentResolver = context.getContentResolver();
186         if (injector != null) {
187             mInjector = injector;
188         } else {
189             mInjector = new Injector();
190         }
191         mBgHandler = new TrackerHandler(mInjector.getBackgroundHandler().getLooper());
192         mUserManager = mContext.getSystemService(UserManager.class);
193     }
194 
195     /**
196      * Start listening for brightness slider events
197      *
198      * @param initialBrightness the initial screen brightness
199      */
start(float initialBrightness)200     public void start(float initialBrightness) {
201         if (DEBUG) {
202             Slog.d(TAG, "Start");
203         }
204         mCurrentUserId = ActivityManager.getCurrentUser();
205         mBgHandler.obtainMessage(MSG_BACKGROUND_START, (Float) initialBrightness).sendToTarget();
206     }
207 
208     /**
209      * Update tracker with new brightness configuration.
210      */
setBrightnessConfiguration(BrightnessConfiguration brightnessConfiguration)211     public void setBrightnessConfiguration(BrightnessConfiguration brightnessConfiguration) {
212         mBgHandler.obtainMessage(MSG_BRIGHTNESS_CONFIG_CHANGED,
213                 brightnessConfiguration).sendToTarget();
214     }
215 
backgroundStart(float initialBrightness)216     private void backgroundStart(float initialBrightness) {
217         readEvents();
218         readAmbientBrightnessStats();
219 
220         mSensorListener = new SensorListener();
221 
222         mSettingsObserver = new SettingsObserver(mBgHandler);
223         mInjector.registerBrightnessModeObserver(mContentResolver, mSettingsObserver);
224         startSensorListener();
225 
226         final IntentFilter intentFilter = new IntentFilter();
227         intentFilter.addAction(Intent.ACTION_SHUTDOWN);
228         intentFilter.addAction(Intent.ACTION_BATTERY_CHANGED);
229         intentFilter.addAction(Intent.ACTION_SCREEN_ON);
230         intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
231         mBroadcastReceiver = new Receiver();
232         mInjector.registerReceiver(mContext, mBroadcastReceiver, intentFilter);
233 
234         mInjector.scheduleIdleJob(mContext);
235         synchronized (mDataCollectionLock) {
236             mLastBrightness = initialBrightness;
237             mStarted = true;
238         }
239         enableColorSampling();
240     }
241 
242     /** Stop listening for events */
243     @VisibleForTesting
stop()244     void stop() {
245         if (DEBUG) {
246             Slog.d(TAG, "Stop");
247         }
248         mBgHandler.removeMessages(MSG_BACKGROUND_START);
249         stopSensorListener();
250         mInjector.unregisterSensorListener(mContext, mSensorListener);
251         mInjector.unregisterBrightnessModeObserver(mContext, mSettingsObserver);
252         mInjector.unregisterReceiver(mContext, mBroadcastReceiver);
253         mInjector.cancelIdleJob(mContext);
254 
255         synchronized (mDataCollectionLock) {
256             mStarted = false;
257         }
258         disableColorSampling();
259     }
260 
onSwitchUser(@serIdInt int newUserId)261     public void onSwitchUser(@UserIdInt int newUserId) {
262         if (DEBUG) {
263             Slog.d(TAG, "Used id updated from " + mCurrentUserId + " to " + newUserId);
264         }
265         mCurrentUserId = newUserId;
266     }
267 
268     /**
269      * @param userId userId to fetch data for.
270      * @param includePackage if false we will null out BrightnessChangeEvent.packageName
271      * @return List of recent {@link BrightnessChangeEvent}s
272      */
getEvents(int userId, boolean includePackage)273     public ParceledListSlice<BrightnessChangeEvent> getEvents(int userId, boolean includePackage) {
274         BrightnessChangeEvent[] events;
275         synchronized (mEventsLock) {
276             events = mEvents.toArray();
277         }
278         int[] profiles = mInjector.getProfileIds(mUserManager, userId);
279         Map<Integer, Boolean> toRedact = new HashMap<>();
280         for (int i = 0; i < profiles.length; ++i) {
281             int profileId = profiles[i];
282             // Include slider interactions when a managed profile app is in the
283             // foreground but always redact the package name.
284             boolean redact = (!includePackage) || profileId != userId;
285             toRedact.put(profiles[i], redact);
286         }
287         ArrayList<BrightnessChangeEvent> out = new ArrayList<>(events.length);
288         for (int i = 0; i < events.length; ++i) {
289             Boolean redact = toRedact.get(events[i].userId);
290             if (redact != null) {
291                 if (!redact) {
292                     out.add(events[i]);
293                 } else {
294                     BrightnessChangeEvent event = new BrightnessChangeEvent((events[i]),
295                             /* redactPackage */ true);
296                     out.add(event);
297                 }
298             }
299         }
300         return new ParceledListSlice<>(out);
301     }
302 
persistBrightnessTrackerState()303     public void persistBrightnessTrackerState() {
304         scheduleWriteBrightnessTrackerState();
305     }
306 
307     /**
308      * Notify the BrightnessTracker that the user has changed the brightness of the display.
309      */
notifyBrightnessChanged(float brightness, boolean userInitiated, float powerBrightnessFactor, boolean isUserSetBrightness, boolean isDefaultBrightnessConfig)310     public void notifyBrightnessChanged(float brightness, boolean userInitiated,
311             float powerBrightnessFactor, boolean isUserSetBrightness,
312             boolean isDefaultBrightnessConfig) {
313         if (DEBUG) {
314             Slog.d(TAG, String.format("notifyBrightnessChanged(brightness=%f, userInitiated=%b)",
315                         brightness, userInitiated));
316         }
317         Message m = mBgHandler.obtainMessage(MSG_BRIGHTNESS_CHANGED,
318                 userInitiated ? 1 : 0, 0 /*unused*/, new BrightnessChangeValues(brightness,
319                         powerBrightnessFactor, isUserSetBrightness, isDefaultBrightnessConfig,
320                         mInjector.currentTimeMillis()));
321         m.sendToTarget();
322     }
323 
handleBrightnessChanged(float brightness, boolean userInitiated, float powerBrightnessFactor, boolean isUserSetBrightness, boolean isDefaultBrightnessConfig, long timestamp)324     private void handleBrightnessChanged(float brightness, boolean userInitiated,
325             float powerBrightnessFactor, boolean isUserSetBrightness,
326             boolean isDefaultBrightnessConfig, long timestamp) {
327         BrightnessChangeEvent.Builder builder;
328 
329         synchronized (mDataCollectionLock) {
330             if (!mStarted) {
331                 // Not currently gathering brightness change information
332                 return;
333             }
334 
335             float previousBrightness = mLastBrightness;
336             mLastBrightness = brightness;
337 
338             if (!userInitiated) {
339                 // We want to record what current brightness is so that we know what the user
340                 // changed it from, but if it wasn't user initiated then we don't want to record it
341                 // as a BrightnessChangeEvent.
342                 return;
343             }
344 
345             builder = new BrightnessChangeEvent.Builder();
346             builder.setBrightness(brightness);
347             builder.setTimeStamp(timestamp);
348             builder.setPowerBrightnessFactor(powerBrightnessFactor);
349             builder.setUserBrightnessPoint(isUserSetBrightness);
350             builder.setIsDefaultBrightnessConfig(isDefaultBrightnessConfig);
351 
352             final int readingCount = mLastSensorReadings.size();
353             if (readingCount == 0) {
354                 // No sensor data so ignore this.
355                 return;
356             }
357 
358             float[] luxValues = new float[readingCount];
359             long[] luxTimestamps = new long[readingCount];
360 
361             int pos = 0;
362 
363             // Convert sensor timestamp in elapsed time nanos to current time millis.
364             long currentTimeMillis = mInjector.currentTimeMillis();
365             long elapsedTimeNanos = mInjector.elapsedRealtimeNanos();
366             for (LightData reading : mLastSensorReadings) {
367                 luxValues[pos] = reading.lux;
368                 luxTimestamps[pos] = currentTimeMillis -
369                         TimeUnit.NANOSECONDS.toMillis(elapsedTimeNanos - reading.timestamp);
370                 ++pos;
371             }
372             builder.setLuxValues(luxValues);
373             builder.setLuxTimestamps(luxTimestamps);
374 
375             builder.setBatteryLevel(mLastBatteryLevel);
376             builder.setLastBrightness(previousBrightness);
377         }
378 
379         try {
380             final ActivityManager.StackInfo focusedStack = mInjector.getFocusedStack();
381             if (focusedStack != null && focusedStack.topActivity != null) {
382                 builder.setUserId(focusedStack.userId);
383                 builder.setPackageName(focusedStack.topActivity.getPackageName());
384             } else {
385                 // Ignore the event because we can't determine user / package.
386                 if (DEBUG) {
387                     Slog.d(TAG, "Ignoring event due to null focusedStack.");
388                 }
389                 return;
390             }
391         } catch (RemoteException e) {
392             // Really shouldn't be possible.
393             return;
394         }
395 
396         builder.setNightMode(mInjector.isNightDisplayActivated(mContext));
397         builder.setColorTemperature(mInjector.getNightDisplayColorTemperature(mContext));
398 
399         if (mColorSamplingEnabled) {
400             DisplayedContentSample sample = mInjector.sampleColor(mNoFramesToSample);
401             if (sample != null && sample.getSampleComponent(
402                     DisplayedContentSample.ColorComponent.CHANNEL2) != null) {
403                 float numMillis = (sample.getNumFrames() / mFrameRate) * 1000.0f;
404                 builder.setColorValues(
405                         sample.getSampleComponent(DisplayedContentSample.ColorComponent.CHANNEL2),
406                         Math.round(numMillis));
407             }
408         }
409 
410         BrightnessChangeEvent event = builder.build();
411         if (DEBUG) {
412             Slog.d(TAG, "Event " + event.brightness + " " + event.packageName);
413         }
414         synchronized (mEventsLock) {
415             mEventsDirty = true;
416             mEvents.append(event);
417         }
418     }
419 
startSensorListener()420     private void startSensorListener() {
421         if (!mSensorRegistered
422                 && mInjector.isInteractive(mContext)
423                 && mInjector.isBrightnessModeAutomatic(mContentResolver)) {
424             mAmbientBrightnessStatsTracker.start();
425             mSensorRegistered = true;
426             mInjector.registerSensorListener(mContext, mSensorListener,
427                     mInjector.getBackgroundHandler());
428         }
429     }
430 
stopSensorListener()431     private void stopSensorListener() {
432         if (mSensorRegistered) {
433             mAmbientBrightnessStatsTracker.stop();
434             mInjector.unregisterSensorListener(mContext, mSensorListener);
435             mSensorRegistered = false;
436         }
437     }
438 
scheduleWriteBrightnessTrackerState()439     private void scheduleWriteBrightnessTrackerState() {
440         if (!mWriteBrightnessTrackerStateScheduled) {
441             mBgHandler.post(() -> {
442                 mWriteBrightnessTrackerStateScheduled = false;
443                 writeEvents();
444                 writeAmbientBrightnessStats();
445             });
446             mWriteBrightnessTrackerStateScheduled = true;
447         }
448     }
449 
writeEvents()450     private void writeEvents() {
451         synchronized (mEventsLock) {
452             if (!mEventsDirty) {
453                 // Nothing to write
454                 return;
455             }
456 
457             final AtomicFile writeTo = mInjector.getFile(EVENTS_FILE);
458             if (writeTo == null) {
459                 return;
460             }
461             if (mEvents.isEmpty()) {
462                 if (writeTo.exists()) {
463                     writeTo.delete();
464                 }
465                 mEventsDirty = false;
466             } else {
467                 FileOutputStream output = null;
468                 try {
469                     output = writeTo.startWrite();
470                     writeEventsLocked(output);
471                     writeTo.finishWrite(output);
472                     mEventsDirty = false;
473                 } catch (IOException e) {
474                     writeTo.failWrite(output);
475                     Slog.e(TAG, "Failed to write change mEvents.", e);
476                 }
477             }
478         }
479     }
480 
writeAmbientBrightnessStats()481     private void writeAmbientBrightnessStats() {
482         final AtomicFile writeTo = mInjector.getFile(AMBIENT_BRIGHTNESS_STATS_FILE);
483         if (writeTo == null) {
484             return;
485         }
486         FileOutputStream output = null;
487         try {
488             output = writeTo.startWrite();
489             mAmbientBrightnessStatsTracker.writeStats(output);
490             writeTo.finishWrite(output);
491         } catch (IOException e) {
492             writeTo.failWrite(output);
493             Slog.e(TAG, "Failed to write ambient brightness stats.", e);
494         }
495     }
496 
readEvents()497     private void readEvents() {
498         synchronized (mEventsLock) {
499             // Read might prune events so mark as dirty.
500             mEventsDirty = true;
501             mEvents.clear();
502             final AtomicFile readFrom = mInjector.getFile(EVENTS_FILE);
503             if (readFrom != null && readFrom.exists()) {
504                 FileInputStream input = null;
505                 try {
506                     input = readFrom.openRead();
507                     readEventsLocked(input);
508                 } catch (IOException e) {
509                     readFrom.delete();
510                     Slog.e(TAG, "Failed to read change mEvents.", e);
511                 } finally {
512                     IoUtils.closeQuietly(input);
513                 }
514             }
515         }
516     }
517 
readAmbientBrightnessStats()518     private void readAmbientBrightnessStats() {
519         mAmbientBrightnessStatsTracker = new AmbientBrightnessStatsTracker(mUserManager, null);
520         final AtomicFile readFrom = mInjector.getFile(AMBIENT_BRIGHTNESS_STATS_FILE);
521         if (readFrom != null && readFrom.exists()) {
522             FileInputStream input = null;
523             try {
524                 input = readFrom.openRead();
525                 mAmbientBrightnessStatsTracker.readStats(input);
526             } catch (IOException e) {
527                 readFrom.delete();
528                 Slog.e(TAG, "Failed to read ambient brightness stats.", e);
529             } finally {
530                 IoUtils.closeQuietly(input);
531             }
532         }
533     }
534 
535     @VisibleForTesting
536     @GuardedBy("mEventsLock")
writeEventsLocked(OutputStream stream)537     void writeEventsLocked(OutputStream stream) throws IOException {
538         XmlSerializer out = new FastXmlSerializer();
539         out.setOutput(stream, StandardCharsets.UTF_8.name());
540         out.startDocument(null, true);
541         out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
542 
543         out.startTag(null, TAG_EVENTS);
544         BrightnessChangeEvent[] toWrite = mEvents.toArray();
545         // Clear events, code below will add back the ones that are still within the time window.
546         mEvents.clear();
547         if (DEBUG) {
548             Slog.d(TAG, "Writing events " + toWrite.length);
549         }
550         final long timeCutOff = mInjector.currentTimeMillis() - MAX_EVENT_AGE;
551         for (int i = 0; i < toWrite.length; ++i) {
552             int userSerialNo = mInjector.getUserSerialNumber(mUserManager, toWrite[i].userId);
553             if (userSerialNo != -1 && toWrite[i].timeStamp > timeCutOff) {
554                 mEvents.append(toWrite[i]);
555                 out.startTag(null, TAG_EVENT);
556                 out.attribute(null, ATTR_NITS, Float.toString(toWrite[i].brightness));
557                 out.attribute(null, ATTR_TIMESTAMP, Long.toString(toWrite[i].timeStamp));
558                 out.attribute(null, ATTR_PACKAGE_NAME, toWrite[i].packageName);
559                 out.attribute(null, ATTR_USER, Integer.toString(userSerialNo));
560                 out.attribute(null, ATTR_BATTERY_LEVEL, Float.toString(toWrite[i].batteryLevel));
561                 out.attribute(null, ATTR_NIGHT_MODE, Boolean.toString(toWrite[i].nightMode));
562                 out.attribute(null, ATTR_COLOR_TEMPERATURE, Integer.toString(
563                         toWrite[i].colorTemperature));
564                 out.attribute(null, ATTR_LAST_NITS,
565                         Float.toString(toWrite[i].lastBrightness));
566                 out.attribute(null, ATTR_DEFAULT_CONFIG,
567                         Boolean.toString(toWrite[i].isDefaultBrightnessConfig));
568                 out.attribute(null, ATTR_POWER_SAVE,
569                         Float.toString(toWrite[i].powerBrightnessFactor));
570                 out.attribute(null, ATTR_USER_POINT,
571                         Boolean.toString(toWrite[i].isUserSetBrightness));
572                 StringBuilder luxValues = new StringBuilder();
573                 StringBuilder luxTimestamps = new StringBuilder();
574                 for (int j = 0; j < toWrite[i].luxValues.length; ++j) {
575                     if (j > 0) {
576                         luxValues.append(',');
577                         luxTimestamps.append(',');
578                     }
579                     luxValues.append(Float.toString(toWrite[i].luxValues[j]));
580                     luxTimestamps.append(Long.toString(toWrite[i].luxTimestamps[j]));
581                 }
582                 out.attribute(null, ATTR_LUX, luxValues.toString());
583                 out.attribute(null, ATTR_LUX_TIMESTAMPS, luxTimestamps.toString());
584                 if (toWrite[i].colorValueBuckets != null
585                         && toWrite[i].colorValueBuckets.length > 0) {
586                     out.attribute(null, ATTR_COLOR_SAMPLE_DURATION,
587                             Long.toString(toWrite[i].colorSampleDuration));
588                     StringBuilder buckets = new StringBuilder();
589                     for (int j = 0; j < toWrite[i].colorValueBuckets.length; ++j) {
590                         if (j > 0) {
591                             buckets.append(',');
592                         }
593                         buckets.append(Long.toString(toWrite[i].colorValueBuckets[j]));
594                     }
595                     out.attribute(null, ATTR_COLOR_VALUE_BUCKETS, buckets.toString());
596                 }
597                 out.endTag(null, TAG_EVENT);
598             }
599         }
600         out.endTag(null, TAG_EVENTS);
601         out.endDocument();
602         stream.flush();
603     }
604 
605     @VisibleForTesting
606     @GuardedBy("mEventsLock")
readEventsLocked(InputStream stream)607     void readEventsLocked(InputStream stream) throws IOException {
608         try {
609             XmlPullParser parser = Xml.newPullParser();
610             parser.setInput(stream, StandardCharsets.UTF_8.name());
611 
612             int type;
613             while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
614                     && type != XmlPullParser.START_TAG) {
615             }
616             String tag = parser.getName();
617             if (!TAG_EVENTS.equals(tag)) {
618                 throw new XmlPullParserException(
619                         "Events not found in brightness tracker file " + tag);
620             }
621 
622             final long timeCutOff = mInjector.currentTimeMillis() - MAX_EVENT_AGE;
623 
624             parser.next();
625             int outerDepth = parser.getDepth();
626             while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
627                     && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
628                 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
629                     continue;
630                 }
631                 tag = parser.getName();
632                 if (TAG_EVENT.equals(tag)) {
633                     BrightnessChangeEvent.Builder builder = new BrightnessChangeEvent.Builder();
634 
635                     String brightness = parser.getAttributeValue(null, ATTR_NITS);
636                     builder.setBrightness(Float.parseFloat(brightness));
637                     String timestamp = parser.getAttributeValue(null, ATTR_TIMESTAMP);
638                     builder.setTimeStamp(Long.parseLong(timestamp));
639                     builder.setPackageName(parser.getAttributeValue(null, ATTR_PACKAGE_NAME));
640                     String user = parser.getAttributeValue(null, ATTR_USER);
641                     builder.setUserId(mInjector.getUserId(mUserManager, Integer.parseInt(user)));
642                     String batteryLevel = parser.getAttributeValue(null, ATTR_BATTERY_LEVEL);
643                     builder.setBatteryLevel(Float.parseFloat(batteryLevel));
644                     String nightMode = parser.getAttributeValue(null, ATTR_NIGHT_MODE);
645                     builder.setNightMode(Boolean.parseBoolean(nightMode));
646                     String colorTemperature =
647                             parser.getAttributeValue(null, ATTR_COLOR_TEMPERATURE);
648                     builder.setColorTemperature(Integer.parseInt(colorTemperature));
649                     String lastBrightness = parser.getAttributeValue(null, ATTR_LAST_NITS);
650                     builder.setLastBrightness(Float.parseFloat(lastBrightness));
651 
652                     String luxValue = parser.getAttributeValue(null, ATTR_LUX);
653                     String luxTimestamp = parser.getAttributeValue(null, ATTR_LUX_TIMESTAMPS);
654 
655                     String[] luxValuesStrings = luxValue.split(",");
656                     String[] luxTimestampsStrings = luxTimestamp.split(",");
657                     if (luxValuesStrings.length != luxTimestampsStrings.length) {
658                         continue;
659                     }
660                     float[] luxValues = new float[luxValuesStrings.length];
661                     long[] luxTimestamps = new long[luxValuesStrings.length];
662                     for (int i = 0; i < luxValues.length; ++i) {
663                         luxValues[i] = Float.parseFloat(luxValuesStrings[i]);
664                         luxTimestamps[i] = Long.parseLong(luxTimestampsStrings[i]);
665                     }
666                     builder.setLuxValues(luxValues);
667                     builder.setLuxTimestamps(luxTimestamps);
668 
669                     String defaultConfig = parser.getAttributeValue(null, ATTR_DEFAULT_CONFIG);
670                     if (defaultConfig != null) {
671                         builder.setIsDefaultBrightnessConfig(Boolean.parseBoolean(defaultConfig));
672                     }
673                     String powerSave = parser.getAttributeValue(null, ATTR_POWER_SAVE);
674                     if (powerSave != null) {
675                         builder.setPowerBrightnessFactor(Float.parseFloat(powerSave));
676                     } else {
677                         builder.setPowerBrightnessFactor(1.0f);
678                     }
679                     String userPoint = parser.getAttributeValue(null, ATTR_USER_POINT);
680                     if (userPoint != null) {
681                         builder.setUserBrightnessPoint(Boolean.parseBoolean(userPoint));
682                     }
683 
684                     String colorSampleDurationString =
685                             parser.getAttributeValue(null, ATTR_COLOR_SAMPLE_DURATION);
686                     String colorValueBucketsString =
687                             parser.getAttributeValue(null, ATTR_COLOR_VALUE_BUCKETS);
688                     if (colorSampleDurationString != null && colorValueBucketsString != null) {
689                         long colorSampleDuration = Long.parseLong(colorSampleDurationString);
690                         String[] buckets = colorValueBucketsString.split(",");
691                         long[] bucketValues = new long[buckets.length];
692                         for (int i = 0; i < bucketValues.length; ++i) {
693                             bucketValues[i] = Long.parseLong(buckets[i]);
694                         }
695                         builder.setColorValues(bucketValues, colorSampleDuration);
696                     }
697 
698                     BrightnessChangeEvent event = builder.build();
699                     if (DEBUG) {
700                         Slog.i(TAG, "Read event " + event.brightness
701                                 + " " + event.packageName);
702                     }
703 
704                     if (event.userId != -1 && event.timeStamp > timeCutOff
705                             && event.luxValues.length > 0) {
706                         mEvents.append(event);
707                     }
708                 }
709             }
710         } catch (NullPointerException | NumberFormatException | XmlPullParserException
711                 | IOException e) {
712             // Failed to parse something, just start with an empty event log.
713             mEvents = new RingBuffer<>(BrightnessChangeEvent.class, MAX_EVENTS);
714             Slog.e(TAG, "Failed to parse brightness event", e);
715             // Re-throw so we will delete the bad file.
716             throw new IOException("failed to parse file", e);
717         }
718     }
719 
dump(final PrintWriter pw)720     public void dump(final PrintWriter pw) {
721         pw.println("BrightnessTracker state:");
722         synchronized (mDataCollectionLock) {
723             pw.println("  mStarted=" + mStarted);
724             pw.println("  mLastBatteryLevel=" + mLastBatteryLevel);
725             pw.println("  mLastBrightness=" + mLastBrightness);
726             pw.println("  mLastSensorReadings.size=" + mLastSensorReadings.size());
727             if (!mLastSensorReadings.isEmpty()) {
728                 pw.println("  mLastSensorReadings time span "
729                         + mLastSensorReadings.peekFirst().timestamp + "->"
730                         + mLastSensorReadings.peekLast().timestamp);
731             }
732         }
733         synchronized (mEventsLock) {
734             pw.println("  mEventsDirty=" + mEventsDirty);
735             pw.println("  mEvents.size=" + mEvents.size());
736             BrightnessChangeEvent[] events = mEvents.toArray();
737             for (int i = 0; i < events.length; ++i) {
738                 pw.print("    " + FORMAT.format(new Date(events[i].timeStamp)));
739                 pw.print(", userId=" + events[i].userId);
740                 pw.print(", " + events[i].lastBrightness + "->" + events[i].brightness);
741                 pw.print(", isUserSetBrightness=" + events[i].isUserSetBrightness);
742                 pw.print(", powerBrightnessFactor=" + events[i].powerBrightnessFactor);
743                 pw.print(", isDefaultBrightnessConfig=" + events[i].isDefaultBrightnessConfig);
744                 pw.print(" {");
745                 for (int j = 0; j < events[i].luxValues.length; ++j){
746                     if (j != 0) {
747                         pw.print(", ");
748                     }
749                     pw.print("(" + events[i].luxValues[j] + "," + events[i].luxTimestamps[j] + ")");
750                 }
751                 pw.println("}");
752             }
753         }
754         pw.println("  mWriteBrightnessTrackerStateScheduled="
755                 + mWriteBrightnessTrackerStateScheduled);
756         mBgHandler.runWithScissors(() -> dumpLocal(pw), 1000);
757         if (mAmbientBrightnessStatsTracker != null) {
758             pw.println();
759             mAmbientBrightnessStatsTracker.dump(pw);
760         }
761     }
762 
dumpLocal(PrintWriter pw)763     private void dumpLocal(PrintWriter pw) {
764         pw.println("  mSensorRegistered=" + mSensorRegistered);
765         pw.println("  mColorSamplingEnabled=" + mColorSamplingEnabled);
766         pw.println("  mNoFramesToSample=" + mNoFramesToSample);
767         pw.println("  mFrameRate=" + mFrameRate);
768     }
769 
enableColorSampling()770     private void enableColorSampling() {
771         if (!mInjector.isBrightnessModeAutomatic(mContentResolver)
772                 || !mInjector.isInteractive(mContext)
773                 || mColorSamplingEnabled
774                 || mBrightnessConfiguration == null
775                 || !mBrightnessConfiguration.shouldCollectColorSamples()) {
776             return;
777         }
778 
779         mFrameRate = mInjector.getFrameRate(mContext);
780         if (mFrameRate <= 0) {
781             Slog.wtf(TAG, "Default display has a zero or negative framerate.");
782             return;
783         }
784         mNoFramesToSample = (int) (mFrameRate * COLOR_SAMPLE_DURATION);
785 
786         DisplayedContentSamplingAttributes attributes = mInjector.getSamplingAttributes();
787         if (DEBUG && attributes != null) {
788             Slog.d(TAG, "Color sampling"
789                     + " mask=0x" + Integer.toHexString(attributes.getComponentMask())
790                     + " dataSpace=0x" + Integer.toHexString(attributes.getDataspace())
791                     + " pixelFormat=0x" + Integer.toHexString(attributes.getPixelFormat()));
792         }
793         // Do we support sampling the Value component of HSV
794         if (attributes != null && attributes.getPixelFormat() == PixelFormat.HSV_888
795                 && (attributes.getComponentMask() & COLOR_SAMPLE_COMPONENT_MASK) != 0) {
796 
797             mColorSamplingEnabled = mInjector.enableColorSampling(/* enable= */true,
798                     mNoFramesToSample);
799             if (DEBUG) {
800                 Slog.i(TAG, "turning on color sampling for "
801                         + mNoFramesToSample + " frames, success=" + mColorSamplingEnabled);
802             }
803         }
804         if (mColorSamplingEnabled && mDisplayListener == null) {
805             mDisplayListener = new DisplayListener();
806             mInjector.registerDisplayListener(mContext, mDisplayListener, mBgHandler);
807         }
808     }
809 
disableColorSampling()810     private void disableColorSampling() {
811         if (!mColorSamplingEnabled) {
812             return;
813         }
814         mInjector.enableColorSampling(/* enable= */ false, /* noFrames= */ 0);
815         mColorSamplingEnabled = false;
816         if (mDisplayListener != null) {
817             mInjector.unRegisterDisplayListener(mContext, mDisplayListener);
818             mDisplayListener = null;
819         }
820         if (DEBUG) {
821             Slog.i(TAG, "turning off color sampling");
822         }
823     }
824 
updateColorSampling()825     private void updateColorSampling() {
826         if (!mColorSamplingEnabled) {
827             return;
828         }
829         float frameRate = mInjector.getFrameRate(mContext);
830         if (frameRate != mFrameRate) {
831             disableColorSampling();
832             enableColorSampling();
833         }
834     }
835 
getAmbientBrightnessStats(int userId)836     public ParceledListSlice<AmbientBrightnessDayStats> getAmbientBrightnessStats(int userId) {
837         if (mAmbientBrightnessStatsTracker != null) {
838             ArrayList<AmbientBrightnessDayStats> stats =
839                     mAmbientBrightnessStatsTracker.getUserStats(userId);
840             if (stats != null) {
841                 return new ParceledListSlice<>(stats);
842             }
843         }
844         return ParceledListSlice.emptyList();
845     }
846 
847     // Not allowed to keep the SensorEvent so used to copy the data we care about.
848     private static class LightData {
849         public float lux;
850         // Time in elapsedRealtimeNanos
851         public long timestamp;
852     }
853 
recordSensorEvent(SensorEvent event)854     private void recordSensorEvent(SensorEvent event) {
855         long horizon = mInjector.elapsedRealtimeNanos() - LUX_EVENT_HORIZON;
856         synchronized (mDataCollectionLock) {
857             if (DEBUG) {
858                 Slog.v(TAG, "Sensor event " + event);
859             }
860             if (!mLastSensorReadings.isEmpty()
861                     && event.timestamp < mLastSensorReadings.getLast().timestamp) {
862                 // Ignore event that came out of order.
863                 return;
864             }
865             LightData data = null;
866             while (!mLastSensorReadings.isEmpty()
867                     && mLastSensorReadings.getFirst().timestamp < horizon) {
868                 // Remove data that has fallen out of the window.
869                 data = mLastSensorReadings.removeFirst();
870             }
871             // We put back the last one we removed so we know how long
872             // the first sensor reading was valid for.
873             if (data != null) {
874                 mLastSensorReadings.addFirst(data);
875             }
876 
877             data = new LightData();
878             data.timestamp = event.timestamp;
879             data.lux = event.values[0];
880             mLastSensorReadings.addLast(data);
881         }
882     }
883 
recordAmbientBrightnessStats(SensorEvent event)884     private void recordAmbientBrightnessStats(SensorEvent event) {
885         mAmbientBrightnessStatsTracker.add(mCurrentUserId, event.values[0]);
886     }
887 
batteryLevelChanged(int level, int scale)888     private void batteryLevelChanged(int level, int scale) {
889         synchronized (mDataCollectionLock) {
890             mLastBatteryLevel = (float) level / (float) scale;
891         }
892     }
893 
894     private final class SensorListener implements SensorEventListener {
895         @Override
onSensorChanged(SensorEvent event)896         public void onSensorChanged(SensorEvent event) {
897             recordSensorEvent(event);
898             recordAmbientBrightnessStats(event);
899         }
900 
901         @Override
onAccuracyChanged(Sensor sensor, int accuracy)902         public void onAccuracyChanged(Sensor sensor, int accuracy) {
903 
904         }
905     }
906 
907     private final class DisplayListener implements DisplayManager.DisplayListener {
908 
909         @Override
onDisplayAdded(int displayId)910         public void onDisplayAdded(int displayId) {
911             // Ignore
912         }
913 
914         @Override
onDisplayRemoved(int displayId)915         public void onDisplayRemoved(int displayId) {
916             // Ignore
917         }
918 
919         @Override
onDisplayChanged(int displayId)920         public void onDisplayChanged(int displayId) {
921             if (displayId == Display.DEFAULT_DISPLAY) {
922                 updateColorSampling();
923             }
924         }
925     }
926 
927     private final class SettingsObserver extends ContentObserver {
SettingsObserver(Handler handler)928         public SettingsObserver(Handler handler) {
929             super(handler);
930         }
931 
932         @Override
onChange(boolean selfChange, Uri uri)933         public void onChange(boolean selfChange, Uri uri) {
934             if (DEBUG) {
935                 Slog.v(TAG, "settings change " + uri);
936             }
937             if (mInjector.isBrightnessModeAutomatic(mContentResolver)) {
938                 mBgHandler.obtainMessage(MSG_START_SENSOR_LISTENER).sendToTarget();
939             } else {
940                 mBgHandler.obtainMessage(MSG_STOP_SENSOR_LISTENER).sendToTarget();
941             }
942         }
943     }
944 
945     private final class Receiver extends BroadcastReceiver {
946         @Override
onReceive(Context context, Intent intent)947         public void onReceive(Context context, Intent intent) {
948             if (DEBUG) {
949                 Slog.d(TAG, "Received " + intent.getAction());
950             }
951             String action = intent.getAction();
952             if (Intent.ACTION_SHUTDOWN.equals(action)) {
953                 stop();
954                 scheduleWriteBrightnessTrackerState();
955             } else if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
956                 int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
957                 int scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, 0);
958                 if (level != -1 && scale != 0) {
959                     batteryLevelChanged(level, scale);
960                 }
961             } else if (Intent.ACTION_SCREEN_OFF.equals(action)) {
962                 mBgHandler.obtainMessage(MSG_STOP_SENSOR_LISTENER).sendToTarget();
963             } else if (Intent.ACTION_SCREEN_ON.equals(action)) {
964                 mBgHandler.obtainMessage(MSG_START_SENSOR_LISTENER).sendToTarget();
965             }
966         }
967     }
968 
969     private final class TrackerHandler extends Handler {
TrackerHandler(Looper looper)970         public TrackerHandler(Looper looper) {
971             super(looper, null, true /*async*/);
972         }
handleMessage(Message msg)973         public void handleMessage(Message msg) {
974             switch (msg.what) {
975                 case MSG_BACKGROUND_START:
976                     backgroundStart((float)msg.obj /*initial brightness*/);
977                     break;
978                 case MSG_BRIGHTNESS_CHANGED:
979                     BrightnessChangeValues values = (BrightnessChangeValues) msg.obj;
980                     boolean userInitiatedChange = (msg.arg1 == 1);
981                     handleBrightnessChanged(values.brightness, userInitiatedChange,
982                             values.powerBrightnessFactor, values.isUserSetBrightness,
983                             values.isDefaultBrightnessConfig, values.timestamp);
984                     break;
985                 case MSG_START_SENSOR_LISTENER:
986                     startSensorListener();
987                     enableColorSampling();
988                     break;
989                 case MSG_STOP_SENSOR_LISTENER:
990                     stopSensorListener();
991                     disableColorSampling();
992                     break;
993                 case MSG_BRIGHTNESS_CONFIG_CHANGED:
994                     mBrightnessConfiguration = (BrightnessConfiguration) msg.obj;
995                     boolean shouldCollectColorSamples =
996                             mBrightnessConfiguration != null
997                                     && mBrightnessConfiguration.shouldCollectColorSamples();
998                     if (shouldCollectColorSamples && !mColorSamplingEnabled) {
999                         enableColorSampling();
1000                     } else if (!shouldCollectColorSamples && mColorSamplingEnabled) {
1001                         disableColorSampling();
1002                     }
1003                     break;
1004 
1005             }
1006         }
1007     }
1008 
1009     private static class BrightnessChangeValues {
1010         final float brightness;
1011         final float powerBrightnessFactor;
1012         final boolean isUserSetBrightness;
1013         final boolean isDefaultBrightnessConfig;
1014         final long timestamp;
1015 
BrightnessChangeValues(float brightness, float powerBrightnessFactor, boolean isUserSetBrightness, boolean isDefaultBrightnessConfig, long timestamp)1016         BrightnessChangeValues(float brightness, float powerBrightnessFactor,
1017                 boolean isUserSetBrightness, boolean isDefaultBrightnessConfig,
1018                 long timestamp) {
1019             this.brightness = brightness;
1020             this.powerBrightnessFactor = powerBrightnessFactor;
1021             this.isUserSetBrightness = isUserSetBrightness;
1022             this.isDefaultBrightnessConfig = isDefaultBrightnessConfig;
1023             this.timestamp = timestamp;
1024         }
1025     }
1026 
1027     @VisibleForTesting
1028     static class Injector {
registerSensorListener(Context context, SensorEventListener sensorListener, Handler handler)1029         public void registerSensorListener(Context context,
1030                 SensorEventListener sensorListener, Handler handler) {
1031             SensorManager sensorManager = context.getSystemService(SensorManager.class);
1032             Sensor lightSensor = sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
1033             sensorManager.registerListener(sensorListener,
1034                     lightSensor, SensorManager.SENSOR_DELAY_NORMAL, handler);
1035         }
1036 
unregisterSensorListener(Context context, SensorEventListener sensorListener)1037         public void unregisterSensorListener(Context context, SensorEventListener sensorListener) {
1038             SensorManager sensorManager = context.getSystemService(SensorManager.class);
1039             sensorManager.unregisterListener(sensorListener);
1040         }
1041 
registerBrightnessModeObserver(ContentResolver resolver, ContentObserver settingsObserver)1042         public void registerBrightnessModeObserver(ContentResolver resolver,
1043                 ContentObserver settingsObserver) {
1044             resolver.registerContentObserver(Settings.System.getUriFor(
1045                     Settings.System.SCREEN_BRIGHTNESS_MODE),
1046                     false, settingsObserver, UserHandle.USER_ALL);
1047         }
1048 
unregisterBrightnessModeObserver(Context context, ContentObserver settingsObserver)1049         public void unregisterBrightnessModeObserver(Context context,
1050                 ContentObserver settingsObserver) {
1051             context.getContentResolver().unregisterContentObserver(settingsObserver);
1052         }
1053 
registerReceiver(Context context, BroadcastReceiver receiver, IntentFilter filter)1054         public void registerReceiver(Context context,
1055                 BroadcastReceiver receiver, IntentFilter filter) {
1056             context.registerReceiver(receiver, filter);
1057         }
1058 
unregisterReceiver(Context context, BroadcastReceiver receiver)1059         public void unregisterReceiver(Context context,
1060                 BroadcastReceiver receiver) {
1061             context.unregisterReceiver(receiver);
1062         }
1063 
getBackgroundHandler()1064         public Handler getBackgroundHandler() {
1065             return BackgroundThread.getHandler();
1066         }
1067 
isBrightnessModeAutomatic(ContentResolver resolver)1068         public boolean isBrightnessModeAutomatic(ContentResolver resolver) {
1069             return Settings.System.getIntForUser(resolver, Settings.System.SCREEN_BRIGHTNESS_MODE,
1070                     Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL, UserHandle.USER_CURRENT)
1071                     == Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC;
1072         }
1073 
getSecureIntForUser(ContentResolver resolver, String setting, int defaultValue, int userId)1074         public int getSecureIntForUser(ContentResolver resolver, String setting, int defaultValue,
1075                 int userId) {
1076             return Settings.Secure.getIntForUser(resolver, setting, defaultValue, userId);
1077         }
1078 
getFile(String filename)1079         public AtomicFile getFile(String filename) {
1080             return new AtomicFile(new File(Environment.getDataSystemDeDirectory(), filename));
1081         }
1082 
currentTimeMillis()1083         public long currentTimeMillis() {
1084             return System.currentTimeMillis();
1085         }
1086 
elapsedRealtimeNanos()1087         public long elapsedRealtimeNanos() {
1088             return SystemClock.elapsedRealtimeNanos();
1089         }
1090 
getUserSerialNumber(UserManager userManager, int userId)1091         public int getUserSerialNumber(UserManager userManager, int userId) {
1092             return userManager.getUserSerialNumber(userId);
1093         }
1094 
getUserId(UserManager userManager, int userSerialNumber)1095         public int getUserId(UserManager userManager, int userSerialNumber) {
1096             return userManager.getUserHandle(userSerialNumber);
1097         }
1098 
getProfileIds(UserManager userManager, int userId)1099         public int[] getProfileIds(UserManager userManager, int userId) {
1100             if (userManager != null) {
1101                 return userManager.getProfileIds(userId, false);
1102             } else {
1103                 return new int[]{userId};
1104             }
1105         }
1106 
getFocusedStack()1107         public ActivityManager.StackInfo getFocusedStack() throws RemoteException {
1108             return ActivityTaskManager.getService().getFocusedStackInfo();
1109         }
1110 
scheduleIdleJob(Context context)1111         public void scheduleIdleJob(Context context) {
1112             BrightnessIdleJob.scheduleJob(context);
1113         }
1114 
cancelIdleJob(Context context)1115         public void cancelIdleJob(Context context) {
1116             BrightnessIdleJob.cancelJob(context);
1117         }
1118 
isInteractive(Context context)1119         public boolean isInteractive(Context context) {
1120             return context.getSystemService(PowerManager.class).isInteractive();
1121         }
1122 
getNightDisplayColorTemperature(Context context)1123         public int getNightDisplayColorTemperature(Context context) {
1124             return context.getSystemService(ColorDisplayManager.class)
1125                     .getNightDisplayColorTemperature();
1126         }
1127 
isNightDisplayActivated(Context context)1128         public boolean isNightDisplayActivated(Context context) {
1129             return context.getSystemService(ColorDisplayManager.class).isNightDisplayActivated();
1130         }
1131 
sampleColor(int noFramesToSample)1132         public DisplayedContentSample sampleColor(int noFramesToSample) {
1133             final DisplayManagerInternal displayManagerInternal =
1134                     LocalServices.getService(DisplayManagerInternal.class);
1135             return displayManagerInternal.getDisplayedContentSample(
1136                    Display.DEFAULT_DISPLAY, noFramesToSample, 0);
1137         }
1138 
getFrameRate(Context context)1139         public float getFrameRate(Context context) {
1140             final DisplayManager displayManager = context.getSystemService(DisplayManager.class);
1141             Display display = displayManager.getDisplay(Display.DEFAULT_DISPLAY);
1142             return display.getRefreshRate();
1143         }
1144 
getSamplingAttributes()1145         public DisplayedContentSamplingAttributes getSamplingAttributes() {
1146             final DisplayManagerInternal displayManagerInternal =
1147                     LocalServices.getService(DisplayManagerInternal.class);
1148             return displayManagerInternal.getDisplayedContentSamplingAttributes(
1149                     Display.DEFAULT_DISPLAY);
1150         }
1151 
enableColorSampling(boolean enable, int noFrames)1152         public boolean enableColorSampling(boolean enable, int noFrames) {
1153             final DisplayManagerInternal displayManagerInternal =
1154                     LocalServices.getService(DisplayManagerInternal.class);
1155             return displayManagerInternal.setDisplayedContentSamplingEnabled(
1156                     Display.DEFAULT_DISPLAY, enable, COLOR_SAMPLE_COMPONENT_MASK, noFrames);
1157         }
1158 
registerDisplayListener(Context context, DisplayManager.DisplayListener listener, Handler handler)1159         public void registerDisplayListener(Context context,
1160                 DisplayManager.DisplayListener listener, Handler handler) {
1161             final DisplayManager displayManager = context.getSystemService(DisplayManager.class);
1162             displayManager.registerDisplayListener(listener, handler);
1163         }
1164 
unRegisterDisplayListener(Context context, DisplayManager.DisplayListener listener)1165         public void unRegisterDisplayListener(Context context,
1166                 DisplayManager.DisplayListener listener) {
1167             final DisplayManager displayManager = context.getSystemService(DisplayManager.class);
1168             displayManager.unregisterDisplayListener(listener);
1169         }
1170     }
1171 }
1172