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