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