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