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