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