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