blob: eea1980012a4fdbacb2f8224b341f4b89b8c0523 [file] [log] [blame]
Kenny Guy22bd0442017-10-26 00:15:54 +01001/*
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
17package com.android.server.display;
18
19import android.annotation.Nullable;
Peeyush Agarwalcc155dd2018-01-10 11:51:33 +000020import android.annotation.UserIdInt;
Kenny Guy22bd0442017-10-26 00:15:54 +010021import android.app.ActivityManager;
Wale Ogunwale04d9cb52018-04-30 13:55:07 -070022import android.app.ActivityTaskManager;
Kenny Guy22bd0442017-10-26 00:15:54 +010023import android.content.BroadcastReceiver;
24import android.content.ContentResolver;
25import android.content.Context;
26import android.content.Intent;
27import android.content.IntentFilter;
28import android.content.pm.ParceledListSlice;
Kenny Guy3d7172c2018-03-19 14:18:23 +000029import android.database.ContentObserver;
Kenny Guy908108a2019-01-17 12:56:34 +000030import android.graphics.PixelFormat;
Kenny Guy22bd0442017-10-26 00:15:54 +010031import android.hardware.Sensor;
32import android.hardware.SensorEvent;
33import android.hardware.SensorEventListener;
34import android.hardware.SensorManager;
Peeyush Agarwalcc155dd2018-01-10 11:51:33 +000035import android.hardware.display.AmbientBrightnessDayStats;
Kenny Guy22bd0442017-10-26 00:15:54 +010036import android.hardware.display.BrightnessChangeEvent;
Kenny Guyd6fd7d62019-11-13 15:33:19 +000037import android.hardware.display.BrightnessConfiguration;
Christine Franks71e003e2019-01-24 14:40:20 -080038import android.hardware.display.ColorDisplayManager;
Kenny Guy908108a2019-01-17 12:56:34 +000039import android.hardware.display.DisplayManager;
40import android.hardware.display.DisplayManagerInternal;
41import android.hardware.display.DisplayedContentSample;
42import android.hardware.display.DisplayedContentSamplingAttributes;
Kenny Guy3d7172c2018-03-19 14:18:23 +000043import android.net.Uri;
Kenny Guy22bd0442017-10-26 00:15:54 +010044import android.os.BatteryManager;
45import android.os.Environment;
46import android.os.Handler;
Michael Wright144aac92017-12-21 18:37:41 +000047import android.os.Looper;
48import android.os.Message;
Kenny Guy689ab8f2017-11-29 12:12:06 +000049import android.os.PowerManager;
Kenny Guy22bd0442017-10-26 00:15:54 +010050import android.os.RemoteException;
51import android.os.SystemClock;
52import android.os.UserHandle;
53import android.os.UserManager;
54import android.provider.Settings;
55import android.util.AtomicFile;
56import android.util.Slog;
57import android.util.Xml;
Kenny Guy908108a2019-01-17 12:56:34 +000058import android.view.Display;
Kenny Guy22bd0442017-10-26 00:15:54 +010059
60import com.android.internal.annotations.GuardedBy;
61import com.android.internal.annotations.VisibleForTesting;
62import com.android.internal.os.BackgroundThread;
63import com.android.internal.util.FastXmlSerializer;
64import com.android.internal.util.RingBuffer;
Kenny Guy908108a2019-01-17 12:56:34 +000065import com.android.server.LocalServices;
Kenny Guy22bd0442017-10-26 00:15:54 +010066
67import libcore.io.IoUtils;
68
69import org.xmlpull.v1.XmlPullParser;
70import org.xmlpull.v1.XmlPullParserException;
71import org.xmlpull.v1.XmlSerializer;
72
73import java.io.File;
74import java.io.FileInputStream;
75import java.io.FileOutputStream;
76import java.io.IOException;
77import java.io.InputStream;
78import java.io.OutputStream;
Kenny Guycfe7b702017-11-14 21:04:58 +000079import java.io.PrintWriter;
Kenny Guy22bd0442017-10-26 00:15:54 +010080import java.nio.charset.StandardCharsets;
Michael Wright2155bc22018-05-01 00:38:32 +010081import java.text.SimpleDateFormat;
Kenny Guy22bd0442017-10-26 00:15:54 +010082import java.util.ArrayDeque;
83import java.util.ArrayList;
Michael Wright2155bc22018-05-01 00:38:32 +010084import java.util.Date;
Kenny Guy22bd0442017-10-26 00:15:54 +010085import java.util.Deque;
Kenny Guy6c90d8a2018-02-20 16:53:27 +000086import java.util.HashMap;
87import java.util.Map;
Kenny Guy22bd0442017-10-26 00:15:54 +010088import 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 */
94public class BrightnessTracker {
95
Kenny Guycfe7b702017-11-14 21:04:58 +000096 static final String TAG = "BrightnessTracker";
97 static final boolean DEBUG = false;
Kenny Guy22bd0442017-10-26 00:15:54 +010098
99 private static final String EVENTS_FILE = "brightness_events.xml";
Peeyush Agarwalcc155dd2018-01-10 11:51:33 +0000100 private static final String AMBIENT_BRIGHTNESS_STATS_FILE = "ambient_brightness_stats.xml";
Kenny Guy22bd0442017-10-26 00:15:54 +0100101 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";
Michael Wright144aac92017-12-21 18:37:41 +0000109 private static final String ATTR_NITS = "nits";
Kenny Guy22bd0442017-10-26 00:15:54 +0100110 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_LUX = "lux";
114 private static final String ATTR_LUX_TIMESTAMPS = "luxTimestamps";
115 private static final String ATTR_BATTERY_LEVEL = "batteryLevel";
116 private static final String ATTR_NIGHT_MODE = "nightMode";
117 private static final String ATTR_COLOR_TEMPERATURE = "colorTemperature";
Michael Wright144aac92017-12-21 18:37:41 +0000118 private static final String ATTR_LAST_NITS = "lastNits";
Kenny Guy53d06612018-01-30 14:19:13 +0000119 private static final String ATTR_DEFAULT_CONFIG = "defaultConfig";
120 private static final String ATTR_POWER_SAVE = "powerSaveFactor";
121 private static final String ATTR_USER_POINT = "userPoint";
Kenny Guy908108a2019-01-17 12:56:34 +0000122 private static final String ATTR_COLOR_SAMPLE_DURATION = "colorSampleDuration";
123 private static final String ATTR_COLOR_VALUE_BUCKETS = "colorValueBuckets";
Michael Wright144aac92017-12-21 18:37:41 +0000124
125 private static final int MSG_BACKGROUND_START = 0;
126 private static final int MSG_BRIGHTNESS_CHANGED = 1;
Kenny Guy3d7172c2018-03-19 14:18:23 +0000127 private static final int MSG_STOP_SENSOR_LISTENER = 2;
128 private static final int MSG_START_SENSOR_LISTENER = 3;
Kenny Guyd6fd7d62019-11-13 15:33:19 +0000129 private static final int MSG_BRIGHTNESS_CONFIG_CHANGED = 4;
Kenny Guy22bd0442017-10-26 00:15:54 +0100130
Michael Wright2155bc22018-05-01 00:38:32 +0100131 private static final SimpleDateFormat FORMAT = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
132
Kenny Guy908108a2019-01-17 12:56:34 +0000133 private static final long COLOR_SAMPLE_DURATION = TimeUnit.SECONDS.toSeconds(10);
134 // Sample chanel 2 of HSV which is the Value component.
135 private static final int COLOR_SAMPLE_COMPONENT_MASK = 0x1 << 2;
136
Kenny Guy22bd0442017-10-26 00:15:54 +0100137 // Lock held while accessing mEvents, is held while writing events to flash.
138 private final Object mEventsLock = new Object();
139 @GuardedBy("mEventsLock")
140 private RingBuffer<BrightnessChangeEvent> mEvents
141 = new RingBuffer<>(BrightnessChangeEvent.class, MAX_EVENTS);
Kenny Guycfe7b702017-11-14 21:04:58 +0000142 @GuardedBy("mEventsLock")
143 private boolean mEventsDirty;
Peeyush Agarwal8c2006d2018-02-08 12:39:39 +0000144
145 private volatile boolean mWriteBrightnessTrackerStateScheduled;
Kenny Guy22bd0442017-10-26 00:15:54 +0100146
Peeyush Agarwalcc155dd2018-01-10 11:51:33 +0000147 private AmbientBrightnessStatsTracker mAmbientBrightnessStatsTracker;
Peeyush Agarwalcc155dd2018-01-10 11:51:33 +0000148
Michael Wright2155bc22018-05-01 00:38:32 +0100149 private final UserManager mUserManager;
Kenny Guy22bd0442017-10-26 00:15:54 +0100150 private final Context mContext;
151 private final ContentResolver mContentResolver;
Michael Wright2155bc22018-05-01 00:38:32 +0100152 private final Handler mBgHandler;
Kenny Guy3d7172c2018-03-19 14:18:23 +0000153
Kenny Guy908108a2019-01-17 12:56:34 +0000154 // These members should only be accessed on the mBgHandler thread.
Kenny Guy22bd0442017-10-26 00:15:54 +0100155 private BroadcastReceiver mBroadcastReceiver;
156 private SensorListener mSensorListener;
Kenny Guy3d7172c2018-03-19 14:18:23 +0000157 private SettingsObserver mSettingsObserver;
Kenny Guy908108a2019-01-17 12:56:34 +0000158 private DisplayListener mDisplayListener;
Kenny Guy3d7172c2018-03-19 14:18:23 +0000159 private boolean mSensorRegistered;
Kenny Guy908108a2019-01-17 12:56:34 +0000160 private boolean mColorSamplingEnabled;
161 private int mNoFramesToSample;
162 private float mFrameRate;
Kenny Guyd6fd7d62019-11-13 15:33:19 +0000163 private BrightnessConfiguration mBrightnessConfiguration;
Kenny Guy908108a2019-01-17 12:56:34 +0000164 // End of block of members that should only be accessed on the mBgHandler thread.
Kenny Guy3d7172c2018-03-19 14:18:23 +0000165
Peeyush Agarwalcc155dd2018-01-10 11:51:33 +0000166 private @UserIdInt int mCurrentUserId = UserHandle.USER_NULL;
Kenny Guy22bd0442017-10-26 00:15:54 +0100167
168 // Lock held while collecting data related to brightness changes.
169 private final Object mDataCollectionLock = new Object();
170 @GuardedBy("mDataCollectionLock")
171 private Deque<LightData> mLastSensorReadings = new ArrayDeque<>();
172 @GuardedBy("mDataCollectionLock")
173 private float mLastBatteryLevel = Float.NaN;
174 @GuardedBy("mDataCollectionLock")
Michael Wright144aac92017-12-21 18:37:41 +0000175 private float mLastBrightness = -1;
Kenny Guy22bd0442017-10-26 00:15:54 +0100176 @GuardedBy("mDataCollectionLock")
Michael Wright144aac92017-12-21 18:37:41 +0000177 private boolean mStarted;
Kenny Guy22bd0442017-10-26 00:15:54 +0100178
179 private final Injector mInjector;
180
181 public BrightnessTracker(Context context, @Nullable Injector injector) {
182 // Note this will be called very early in boot, other system
183 // services may not be present.
184 mContext = context;
185 mContentResolver = context.getContentResolver();
186 if (injector != null) {
187 mInjector = injector;
188 } else {
189 mInjector = new Injector();
190 }
Michael Wright2155bc22018-05-01 00:38:32 +0100191 mBgHandler = new TrackerHandler(mInjector.getBackgroundHandler().getLooper());
192 mUserManager = mContext.getSystemService(UserManager.class);
Kenny Guy22bd0442017-10-26 00:15:54 +0100193 }
194
Michael Wright144aac92017-12-21 18:37:41 +0000195 /**
196 * Start listening for brightness slider events
197 *
Kenny Guy0ff44892018-01-10 14:06:42 +0000198 * @param initialBrightness the initial screen brightness
Michael Wright144aac92017-12-21 18:37:41 +0000199 */
200 public void start(float initialBrightness) {
Kenny Guy22bd0442017-10-26 00:15:54 +0100201 if (DEBUG) {
202 Slog.d(TAG, "Start");
203 }
Peeyush Agarwal8c2006d2018-02-08 12:39:39 +0000204 mCurrentUserId = ActivityManager.getCurrentUser();
Michael Wright144aac92017-12-21 18:37:41 +0000205 mBgHandler.obtainMessage(MSG_BACKGROUND_START, (Float) initialBrightness).sendToTarget();
Kenny Guy22bd0442017-10-26 00:15:54 +0100206 }
207
Kenny Guyd6fd7d62019-11-13 15:33:19 +0000208 /**
209 * Update tracker with new brightness configuration.
210 */
211 public void setBrightnessConfiguration(BrightnessConfiguration brightnessConfiguration) {
212 mBgHandler.obtainMessage(MSG_BRIGHTNESS_CONFIG_CHANGED,
213 brightnessConfiguration).sendToTarget();
214 }
215
Michael Wright144aac92017-12-21 18:37:41 +0000216 private void backgroundStart(float initialBrightness) {
Kenny Guy22bd0442017-10-26 00:15:54 +0100217 readEvents();
Peeyush Agarwalcc155dd2018-01-10 11:51:33 +0000218 readAmbientBrightnessStats();
Kenny Guy22bd0442017-10-26 00:15:54 +0100219
Kenny Guy22bd0442017-10-26 00:15:54 +0100220 mSensorListener = new SensorListener();
Kenny Guy689ab8f2017-11-29 12:12:06 +0000221
Kenny Guy3d7172c2018-03-19 14:18:23 +0000222 mSettingsObserver = new SettingsObserver(mBgHandler);
223 mInjector.registerBrightnessModeObserver(mContentResolver, mSettingsObserver);
224 startSensorListener();
Kenny Guy22bd0442017-10-26 00:15:54 +0100225
Kenny Guy22bd0442017-10-26 00:15:54 +0100226 final IntentFilter intentFilter = new IntentFilter();
227 intentFilter.addAction(Intent.ACTION_SHUTDOWN);
228 intentFilter.addAction(Intent.ACTION_BATTERY_CHANGED);
Kenny Guy689ab8f2017-11-29 12:12:06 +0000229 intentFilter.addAction(Intent.ACTION_SCREEN_ON);
230 intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
Kenny Guy22bd0442017-10-26 00:15:54 +0100231 mBroadcastReceiver = new Receiver();
232 mInjector.registerReceiver(mContext, mBroadcastReceiver, intentFilter);
Kenny Guycfe7b702017-11-14 21:04:58 +0000233
234 mInjector.scheduleIdleJob(mContext);
Michael Wright144aac92017-12-21 18:37:41 +0000235 synchronized (mDataCollectionLock) {
236 mLastBrightness = initialBrightness;
237 mStarted = true;
238 }
Kenny Guy908108a2019-01-17 12:56:34 +0000239 enableColorSampling();
Kenny Guy22bd0442017-10-26 00:15:54 +0100240 }
241
242 /** Stop listening for events */
243 @VisibleForTesting
244 void stop() {
245 if (DEBUG) {
246 Slog.d(TAG, "Stop");
247 }
Michael Wright144aac92017-12-21 18:37:41 +0000248 mBgHandler.removeMessages(MSG_BACKGROUND_START);
Kenny Guy3d7172c2018-03-19 14:18:23 +0000249 stopSensorListener();
Kenny Guy22bd0442017-10-26 00:15:54 +0100250 mInjector.unregisterSensorListener(mContext, mSensorListener);
Kenny Guy3d7172c2018-03-19 14:18:23 +0000251 mInjector.unregisterBrightnessModeObserver(mContext, mSettingsObserver);
Kenny Guy22bd0442017-10-26 00:15:54 +0100252 mInjector.unregisterReceiver(mContext, mBroadcastReceiver);
Kenny Guycfe7b702017-11-14 21:04:58 +0000253 mInjector.cancelIdleJob(mContext);
Michael Wright144aac92017-12-21 18:37:41 +0000254
255 synchronized (mDataCollectionLock) {
256 mStarted = false;
257 }
Kenny Guy908108a2019-01-17 12:56:34 +0000258 disableColorSampling();
Kenny Guy22bd0442017-10-26 00:15:54 +0100259 }
260
Peeyush Agarwalcc155dd2018-01-10 11:51:33 +0000261 public void onSwitchUser(@UserIdInt int newUserId) {
262 if (DEBUG) {
263 Slog.d(TAG, "Used id updated from " + mCurrentUserId + " to " + newUserId);
264 }
265 mCurrentUserId = newUserId;
266 }
267
Kenny Guy22bd0442017-10-26 00:15:54 +0100268 /**
269 * @param userId userId to fetch data for.
Kenny Guy29aa30e2017-11-30 13:43:46 +0000270 * @param includePackage if false we will null out BrightnessChangeEvent.packageName
Kenny Guy22bd0442017-10-26 00:15:54 +0100271 * @return List of recent {@link BrightnessChangeEvent}s
272 */
Kenny Guy29aa30e2017-11-30 13:43:46 +0000273 public ParceledListSlice<BrightnessChangeEvent> getEvents(int userId, boolean includePackage) {
Kenny Guy22bd0442017-10-26 00:15:54 +0100274 BrightnessChangeEvent[] events;
275 synchronized (mEventsLock) {
276 events = mEvents.toArray();
277 }
Kenny Guy6c90d8a2018-02-20 16:53:27 +0000278 int[] profiles = mInjector.getProfileIds(mUserManager, userId);
279 Map<Integer, Boolean> toRedact = new HashMap<>();
280 for (int i = 0; i < profiles.length; ++i) {
281 int profileId = profiles[i];
282 // Include slider interactions when a managed profile app is in the
283 // foreground but always redact the package name.
284 boolean redact = (!includePackage) || profileId != userId;
285 toRedact.put(profiles[i], redact);
286 }
Kenny Guy22bd0442017-10-26 00:15:54 +0100287 ArrayList<BrightnessChangeEvent> out = new ArrayList<>(events.length);
288 for (int i = 0; i < events.length; ++i) {
Kenny Guy6c90d8a2018-02-20 16:53:27 +0000289 Boolean redact = toRedact.get(events[i].userId);
290 if (redact != null) {
291 if (!redact) {
Kenny Guy29aa30e2017-11-30 13:43:46 +0000292 out.add(events[i]);
293 } else {
Kenny Guy0ff44892018-01-10 14:06:42 +0000294 BrightnessChangeEvent event = new BrightnessChangeEvent((events[i]),
295 /* redactPackage */ true);
Kenny Guy29aa30e2017-11-30 13:43:46 +0000296 out.add(event);
297 }
Kenny Guy22bd0442017-10-26 00:15:54 +0100298 }
299 }
300 return new ParceledListSlice<>(out);
301 }
302
Peeyush Agarwalcc155dd2018-01-10 11:51:33 +0000303 public void persistBrightnessTrackerState() {
304 scheduleWriteBrightnessTrackerState();
Kenny Guycfe7b702017-11-14 21:04:58 +0000305 }
306
Michael Wright144aac92017-12-21 18:37:41 +0000307 /**
308 * Notify the BrightnessTracker that the user has changed the brightness of the display.
309 */
Kenny Guy53d06612018-01-30 14:19:13 +0000310 public void notifyBrightnessChanged(float brightness, boolean userInitiated,
311 float powerBrightnessFactor, boolean isUserSetBrightness,
312 boolean isDefaultBrightnessConfig) {
Kenny Guy22bd0442017-10-26 00:15:54 +0100313 if (DEBUG) {
Michael Wright144aac92017-12-21 18:37:41 +0000314 Slog.d(TAG, String.format("notifyBrightnessChanged(brightness=%f, userInitiated=%b)",
315 brightness, userInitiated));
Kenny Guy22bd0442017-10-26 00:15:54 +0100316 }
Michael Wright144aac92017-12-21 18:37:41 +0000317 Message m = mBgHandler.obtainMessage(MSG_BRIGHTNESS_CHANGED,
Kenny Guy53d06612018-01-30 14:19:13 +0000318 userInitiated ? 1 : 0, 0 /*unused*/, new BrightnessChangeValues(brightness,
Kenny Guy2b326bf2018-04-18 18:42:10 +0100319 powerBrightnessFactor, isUserSetBrightness, isDefaultBrightnessConfig,
320 mInjector.currentTimeMillis()));
Michael Wright144aac92017-12-21 18:37:41 +0000321 m.sendToTarget();
322 }
Kenny Guy22bd0442017-10-26 00:15:54 +0100323
Kenny Guy53d06612018-01-30 14:19:13 +0000324 private void handleBrightnessChanged(float brightness, boolean userInitiated,
325 float powerBrightnessFactor, boolean isUserSetBrightness,
Kenny Guy2b326bf2018-04-18 18:42:10 +0100326 boolean isDefaultBrightnessConfig, long timestamp) {
Kenny Guy0ff44892018-01-10 14:06:42 +0000327 BrightnessChangeEvent.Builder builder;
328
Kenny Guy22bd0442017-10-26 00:15:54 +0100329 synchronized (mDataCollectionLock) {
Michael Wright144aac92017-12-21 18:37:41 +0000330 if (!mStarted) {
331 // Not currently gathering brightness change information
Kenny Guy22bd0442017-10-26 00:15:54 +0100332 return;
333 }
334
Michael Wright144aac92017-12-21 18:37:41 +0000335 float previousBrightness = mLastBrightness;
336 mLastBrightness = brightness;
337
338 if (!userInitiated) {
339 // We want to record what current brightness is so that we know what the user
340 // changed it from, but if it wasn't user initiated then we don't want to record it
341 // as a BrightnessChangeEvent.
342 return;
343 }
344
Kenny Guy0ff44892018-01-10 14:06:42 +0000345 builder = new BrightnessChangeEvent.Builder();
346 builder.setBrightness(brightness);
Kenny Guy2b326bf2018-04-18 18:42:10 +0100347 builder.setTimeStamp(timestamp);
Kenny Guy53d06612018-01-30 14:19:13 +0000348 builder.setPowerBrightnessFactor(powerBrightnessFactor);
349 builder.setUserBrightnessPoint(isUserSetBrightness);
350 builder.setIsDefaultBrightnessConfig(isDefaultBrightnessConfig);
Michael Wright144aac92017-12-21 18:37:41 +0000351
Kenny Guy22bd0442017-10-26 00:15:54 +0100352 final int readingCount = mLastSensorReadings.size();
353 if (readingCount == 0) {
354 // No sensor data so ignore this.
355 return;
356 }
357
Kenny Guy0ff44892018-01-10 14:06:42 +0000358 float[] luxValues = new float[readingCount];
359 long[] luxTimestamps = new long[readingCount];
Kenny Guy22bd0442017-10-26 00:15:54 +0100360
361 int pos = 0;
362
363 // Convert sensor timestamp in elapsed time nanos to current time millis.
364 long currentTimeMillis = mInjector.currentTimeMillis();
365 long elapsedTimeNanos = mInjector.elapsedRealtimeNanos();
366 for (LightData reading : mLastSensorReadings) {
Kenny Guy0ff44892018-01-10 14:06:42 +0000367 luxValues[pos] = reading.lux;
368 luxTimestamps[pos] = currentTimeMillis -
Kenny Guy22bd0442017-10-26 00:15:54 +0100369 TimeUnit.NANOSECONDS.toMillis(elapsedTimeNanos - reading.timestamp);
370 ++pos;
371 }
Kenny Guy0ff44892018-01-10 14:06:42 +0000372 builder.setLuxValues(luxValues);
373 builder.setLuxTimestamps(luxTimestamps);
Kenny Guy22bd0442017-10-26 00:15:54 +0100374
Kenny Guy0ff44892018-01-10 14:06:42 +0000375 builder.setBatteryLevel(mLastBatteryLevel);
376 builder.setLastBrightness(previousBrightness);
Kenny Guy22bd0442017-10-26 00:15:54 +0100377 }
378
Kenny Guy22bd0442017-10-26 00:15:54 +0100379 try {
380 final ActivityManager.StackInfo focusedStack = mInjector.getFocusedStack();
Kenny Guy0e875d32018-02-26 14:11:40 +0000381 if (focusedStack != null && focusedStack.topActivity != null) {
382 builder.setUserId(focusedStack.userId);
383 builder.setPackageName(focusedStack.topActivity.getPackageName());
384 } else {
385 // Ignore the event because we can't determine user / package.
386 if (DEBUG) {
387 Slog.d(TAG, "Ignoring event due to null focusedStack.");
388 }
389 return;
390 }
Kenny Guy22bd0442017-10-26 00:15:54 +0100391 } catch (RemoteException e) {
392 // Really shouldn't be possible.
Kenny Guy0ff44892018-01-10 14:06:42 +0000393 return;
Kenny Guy22bd0442017-10-26 00:15:54 +0100394 }
395
Christine Franks71e003e2019-01-24 14:40:20 -0800396 builder.setNightMode(mInjector.isNightDisplayActivated(mContext));
397 builder.setColorTemperature(mInjector.getNightDisplayColorTemperature(mContext));
Kenny Guy22bd0442017-10-26 00:15:54 +0100398
Kenny Guy908108a2019-01-17 12:56:34 +0000399 if (mColorSamplingEnabled) {
400 DisplayedContentSample sample = mInjector.sampleColor(mNoFramesToSample);
401 if (sample != null && sample.getSampleComponent(
402 DisplayedContentSample.ColorComponent.CHANNEL2) != null) {
403 float numMillis = (sample.getNumFrames() / mFrameRate) * 1000.0f;
404 builder.setColorValues(
405 sample.getSampleComponent(DisplayedContentSample.ColorComponent.CHANNEL2),
406 Math.round(numMillis));
407 }
408 }
409
Kenny Guy0ff44892018-01-10 14:06:42 +0000410 BrightnessChangeEvent event = builder.build();
Kenny Guy22bd0442017-10-26 00:15:54 +0100411 if (DEBUG) {
412 Slog.d(TAG, "Event " + event.brightness + " " + event.packageName);
413 }
414 synchronized (mEventsLock) {
Kenny Guycfe7b702017-11-14 21:04:58 +0000415 mEventsDirty = true;
Kenny Guy22bd0442017-10-26 00:15:54 +0100416 mEvents.append(event);
417 }
418 }
419
Kenny Guy3d7172c2018-03-19 14:18:23 +0000420 private void startSensorListener() {
421 if (!mSensorRegistered
422 && mInjector.isInteractive(mContext)
423 && mInjector.isBrightnessModeAutomatic(mContentResolver)) {
424 mAmbientBrightnessStatsTracker.start();
425 mSensorRegistered = true;
426 mInjector.registerSensorListener(mContext, mSensorListener,
427 mInjector.getBackgroundHandler());
428 }
429 }
430
431 private void stopSensorListener() {
432 if (mSensorRegistered) {
433 mAmbientBrightnessStatsTracker.stop();
434 mInjector.unregisterSensorListener(mContext, mSensorListener);
435 mSensorRegistered = false;
436 }
437 }
438
Peeyush Agarwalcc155dd2018-01-10 11:51:33 +0000439 private void scheduleWriteBrightnessTrackerState() {
Peeyush Agarwal8c2006d2018-02-08 12:39:39 +0000440 if (!mWriteBrightnessTrackerStateScheduled) {
441 mBgHandler.post(() -> {
442 mWriteBrightnessTrackerStateScheduled = false;
443 writeEvents();
444 writeAmbientBrightnessStats();
445 });
446 mWriteBrightnessTrackerStateScheduled = true;
Peeyush Agarwalcc155dd2018-01-10 11:51:33 +0000447 }
Kenny Guy22bd0442017-10-26 00:15:54 +0100448 }
449
450 private void writeEvents() {
Kenny Guy22bd0442017-10-26 00:15:54 +0100451 synchronized (mEventsLock) {
Kenny Guycfe7b702017-11-14 21:04:58 +0000452 if (!mEventsDirty) {
453 // Nothing to write
454 return;
455 }
456
Peeyush Agarwalcc155dd2018-01-10 11:51:33 +0000457 final AtomicFile writeTo = mInjector.getFile(EVENTS_FILE);
Kenny Guy22bd0442017-10-26 00:15:54 +0100458 if (writeTo == null) {
459 return;
460 }
461 if (mEvents.isEmpty()) {
462 if (writeTo.exists()) {
463 writeTo.delete();
464 }
Kenny Guycfe7b702017-11-14 21:04:58 +0000465 mEventsDirty = false;
Kenny Guy22bd0442017-10-26 00:15:54 +0100466 } else {
467 FileOutputStream output = null;
468 try {
469 output = writeTo.startWrite();
470 writeEventsLocked(output);
471 writeTo.finishWrite(output);
Kenny Guycfe7b702017-11-14 21:04:58 +0000472 mEventsDirty = false;
Kenny Guy22bd0442017-10-26 00:15:54 +0100473 } catch (IOException e) {
474 writeTo.failWrite(output);
475 Slog.e(TAG, "Failed to write change mEvents.", e);
476 }
477 }
478 }
479 }
480
Peeyush Agarwalcc155dd2018-01-10 11:51:33 +0000481 private void writeAmbientBrightnessStats() {
Peeyush Agarwalcc155dd2018-01-10 11:51:33 +0000482 final AtomicFile writeTo = mInjector.getFile(AMBIENT_BRIGHTNESS_STATS_FILE);
483 if (writeTo == null) {
484 return;
485 }
486 FileOutputStream output = null;
487 try {
488 output = writeTo.startWrite();
489 mAmbientBrightnessStatsTracker.writeStats(output);
490 writeTo.finishWrite(output);
491 } catch (IOException e) {
492 writeTo.failWrite(output);
493 Slog.e(TAG, "Failed to write ambient brightness stats.", e);
494 }
495 }
496
Kenny Guy22bd0442017-10-26 00:15:54 +0100497 private void readEvents() {
498 synchronized (mEventsLock) {
Kenny Guycfe7b702017-11-14 21:04:58 +0000499 // Read might prune events so mark as dirty.
500 mEventsDirty = true;
Kenny Guy22bd0442017-10-26 00:15:54 +0100501 mEvents.clear();
Peeyush Agarwalcc155dd2018-01-10 11:51:33 +0000502 final AtomicFile readFrom = mInjector.getFile(EVENTS_FILE);
Kenny Guy22bd0442017-10-26 00:15:54 +0100503 if (readFrom != null && readFrom.exists()) {
504 FileInputStream input = null;
505 try {
506 input = readFrom.openRead();
507 readEventsLocked(input);
508 } catch (IOException e) {
509 readFrom.delete();
510 Slog.e(TAG, "Failed to read change mEvents.", e);
511 } finally {
512 IoUtils.closeQuietly(input);
513 }
514 }
515 }
516 }
517
Peeyush Agarwalcc155dd2018-01-10 11:51:33 +0000518 private void readAmbientBrightnessStats() {
519 mAmbientBrightnessStatsTracker = new AmbientBrightnessStatsTracker(mUserManager, null);
520 final AtomicFile readFrom = mInjector.getFile(AMBIENT_BRIGHTNESS_STATS_FILE);
521 if (readFrom != null && readFrom.exists()) {
522 FileInputStream input = null;
523 try {
524 input = readFrom.openRead();
525 mAmbientBrightnessStatsTracker.readStats(input);
526 } catch (IOException e) {
527 readFrom.delete();
528 Slog.e(TAG, "Failed to read ambient brightness stats.", e);
529 } finally {
530 IoUtils.closeQuietly(input);
531 }
532 }
533 }
534
Kenny Guy22bd0442017-10-26 00:15:54 +0100535 @VisibleForTesting
536 @GuardedBy("mEventsLock")
537 void writeEventsLocked(OutputStream stream) throws IOException {
538 XmlSerializer out = new FastXmlSerializer();
539 out.setOutput(stream, StandardCharsets.UTF_8.name());
540 out.startDocument(null, true);
541 out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
542
543 out.startTag(null, TAG_EVENTS);
544 BrightnessChangeEvent[] toWrite = mEvents.toArray();
Kenny Guycfe7b702017-11-14 21:04:58 +0000545 // Clear events, code below will add back the ones that are still within the time window.
546 mEvents.clear();
Kenny Guy22bd0442017-10-26 00:15:54 +0100547 if (DEBUG) {
548 Slog.d(TAG, "Writing events " + toWrite.length);
549 }
Kenny Guycfe7b702017-11-14 21:04:58 +0000550 final long timeCutOff = mInjector.currentTimeMillis() - MAX_EVENT_AGE;
Kenny Guy22bd0442017-10-26 00:15:54 +0100551 for (int i = 0; i < toWrite.length; ++i) {
552 int userSerialNo = mInjector.getUserSerialNumber(mUserManager, toWrite[i].userId);
553 if (userSerialNo != -1 && toWrite[i].timeStamp > timeCutOff) {
Kenny Guycfe7b702017-11-14 21:04:58 +0000554 mEvents.append(toWrite[i]);
Kenny Guy22bd0442017-10-26 00:15:54 +0100555 out.startTag(null, TAG_EVENT);
Michael Wright144aac92017-12-21 18:37:41 +0000556 out.attribute(null, ATTR_NITS, Float.toString(toWrite[i].brightness));
Kenny Guy22bd0442017-10-26 00:15:54 +0100557 out.attribute(null, ATTR_TIMESTAMP, Long.toString(toWrite[i].timeStamp));
558 out.attribute(null, ATTR_PACKAGE_NAME, toWrite[i].packageName);
559 out.attribute(null, ATTR_USER, Integer.toString(userSerialNo));
560 out.attribute(null, ATTR_BATTERY_LEVEL, Float.toString(toWrite[i].batteryLevel));
561 out.attribute(null, ATTR_NIGHT_MODE, Boolean.toString(toWrite[i].nightMode));
562 out.attribute(null, ATTR_COLOR_TEMPERATURE, Integer.toString(
563 toWrite[i].colorTemperature));
Michael Wright144aac92017-12-21 18:37:41 +0000564 out.attribute(null, ATTR_LAST_NITS,
565 Float.toString(toWrite[i].lastBrightness));
Kenny Guy53d06612018-01-30 14:19:13 +0000566 out.attribute(null, ATTR_DEFAULT_CONFIG,
567 Boolean.toString(toWrite[i].isDefaultBrightnessConfig));
568 out.attribute(null, ATTR_POWER_SAVE,
569 Float.toString(toWrite[i].powerBrightnessFactor));
570 out.attribute(null, ATTR_USER_POINT,
571 Boolean.toString(toWrite[i].isUserSetBrightness));
Kenny Guy22bd0442017-10-26 00:15:54 +0100572 StringBuilder luxValues = new StringBuilder();
573 StringBuilder luxTimestamps = new StringBuilder();
574 for (int j = 0; j < toWrite[i].luxValues.length; ++j) {
575 if (j > 0) {
576 luxValues.append(',');
577 luxTimestamps.append(',');
578 }
579 luxValues.append(Float.toString(toWrite[i].luxValues[j]));
580 luxTimestamps.append(Long.toString(toWrite[i].luxTimestamps[j]));
581 }
582 out.attribute(null, ATTR_LUX, luxValues.toString());
583 out.attribute(null, ATTR_LUX_TIMESTAMPS, luxTimestamps.toString());
Kenny Guy908108a2019-01-17 12:56:34 +0000584 if (toWrite[i].colorValueBuckets != null
585 && toWrite[i].colorValueBuckets.length > 0) {
586 out.attribute(null, ATTR_COLOR_SAMPLE_DURATION,
587 Long.toString(toWrite[i].colorSampleDuration));
588 StringBuilder buckets = new StringBuilder();
589 for (int j = 0; j < toWrite[i].colorValueBuckets.length; ++j) {
590 if (j > 0) {
591 buckets.append(',');
592 }
593 buckets.append(Long.toString(toWrite[i].colorValueBuckets[j]));
594 }
595 out.attribute(null, ATTR_COLOR_VALUE_BUCKETS, buckets.toString());
596 }
Kenny Guy22bd0442017-10-26 00:15:54 +0100597 out.endTag(null, TAG_EVENT);
598 }
599 }
600 out.endTag(null, TAG_EVENTS);
601 out.endDocument();
602 stream.flush();
603 }
604
605 @VisibleForTesting
606 @GuardedBy("mEventsLock")
607 void readEventsLocked(InputStream stream) throws IOException {
608 try {
609 XmlPullParser parser = Xml.newPullParser();
610 parser.setInput(stream, StandardCharsets.UTF_8.name());
611
612 int type;
613 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
614 && type != XmlPullParser.START_TAG) {
615 }
616 String tag = parser.getName();
617 if (!TAG_EVENTS.equals(tag)) {
618 throw new XmlPullParserException(
619 "Events not found in brightness tracker file " + tag);
620 }
621
622 final long timeCutOff = mInjector.currentTimeMillis() - MAX_EVENT_AGE;
623
624 parser.next();
625 int outerDepth = parser.getDepth();
626 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
627 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
628 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
629 continue;
630 }
631 tag = parser.getName();
632 if (TAG_EVENT.equals(tag)) {
Kenny Guy0ff44892018-01-10 14:06:42 +0000633 BrightnessChangeEvent.Builder builder = new BrightnessChangeEvent.Builder();
Kenny Guy22bd0442017-10-26 00:15:54 +0100634
Michael Wright144aac92017-12-21 18:37:41 +0000635 String brightness = parser.getAttributeValue(null, ATTR_NITS);
Kenny Guy0ff44892018-01-10 14:06:42 +0000636 builder.setBrightness(Float.parseFloat(brightness));
Kenny Guy22bd0442017-10-26 00:15:54 +0100637 String timestamp = parser.getAttributeValue(null, ATTR_TIMESTAMP);
Kenny Guy0ff44892018-01-10 14:06:42 +0000638 builder.setTimeStamp(Long.parseLong(timestamp));
639 builder.setPackageName(parser.getAttributeValue(null, ATTR_PACKAGE_NAME));
Kenny Guy22bd0442017-10-26 00:15:54 +0100640 String user = parser.getAttributeValue(null, ATTR_USER);
Kenny Guy0ff44892018-01-10 14:06:42 +0000641 builder.setUserId(mInjector.getUserId(mUserManager, Integer.parseInt(user)));
Kenny Guy22bd0442017-10-26 00:15:54 +0100642 String batteryLevel = parser.getAttributeValue(null, ATTR_BATTERY_LEVEL);
Kenny Guy0ff44892018-01-10 14:06:42 +0000643 builder.setBatteryLevel(Float.parseFloat(batteryLevel));
Kenny Guy22bd0442017-10-26 00:15:54 +0100644 String nightMode = parser.getAttributeValue(null, ATTR_NIGHT_MODE);
Kenny Guy0ff44892018-01-10 14:06:42 +0000645 builder.setNightMode(Boolean.parseBoolean(nightMode));
Kenny Guy22bd0442017-10-26 00:15:54 +0100646 String colorTemperature =
647 parser.getAttributeValue(null, ATTR_COLOR_TEMPERATURE);
Kenny Guy0ff44892018-01-10 14:06:42 +0000648 builder.setColorTemperature(Integer.parseInt(colorTemperature));
Michael Wright144aac92017-12-21 18:37:41 +0000649 String lastBrightness = parser.getAttributeValue(null, ATTR_LAST_NITS);
Kenny Guy0ff44892018-01-10 14:06:42 +0000650 builder.setLastBrightness(Float.parseFloat(lastBrightness));
Kenny Guy22bd0442017-10-26 00:15:54 +0100651
652 String luxValue = parser.getAttributeValue(null, ATTR_LUX);
653 String luxTimestamp = parser.getAttributeValue(null, ATTR_LUX_TIMESTAMPS);
654
Kenny Guy0ff44892018-01-10 14:06:42 +0000655 String[] luxValuesStrings = luxValue.split(",");
656 String[] luxTimestampsStrings = luxTimestamp.split(",");
657 if (luxValuesStrings.length != luxTimestampsStrings.length) {
Kenny Guy22bd0442017-10-26 00:15:54 +0100658 continue;
659 }
Kenny Guy0ff44892018-01-10 14:06:42 +0000660 float[] luxValues = new float[luxValuesStrings.length];
661 long[] luxTimestamps = new long[luxValuesStrings.length];
Kenny Guy22bd0442017-10-26 00:15:54 +0100662 for (int i = 0; i < luxValues.length; ++i) {
Kenny Guy0ff44892018-01-10 14:06:42 +0000663 luxValues[i] = Float.parseFloat(luxValuesStrings[i]);
664 luxTimestamps[i] = Long.parseLong(luxTimestampsStrings[i]);
Kenny Guy22bd0442017-10-26 00:15:54 +0100665 }
Kenny Guy0ff44892018-01-10 14:06:42 +0000666 builder.setLuxValues(luxValues);
667 builder.setLuxTimestamps(luxTimestamps);
Kenny Guy22bd0442017-10-26 00:15:54 +0100668
Kenny Guy53d06612018-01-30 14:19:13 +0000669 String defaultConfig = parser.getAttributeValue(null, ATTR_DEFAULT_CONFIG);
670 if (defaultConfig != null) {
671 builder.setIsDefaultBrightnessConfig(Boolean.parseBoolean(defaultConfig));
672 }
673 String powerSave = parser.getAttributeValue(null, ATTR_POWER_SAVE);
674 if (powerSave != null) {
675 builder.setPowerBrightnessFactor(Float.parseFloat(powerSave));
676 } else {
677 builder.setPowerBrightnessFactor(1.0f);
678 }
679 String userPoint = parser.getAttributeValue(null, ATTR_USER_POINT);
680 if (userPoint != null) {
681 builder.setUserBrightnessPoint(Boolean.parseBoolean(userPoint));
682 }
683
Kenny Guy908108a2019-01-17 12:56:34 +0000684 String colorSampleDurationString =
685 parser.getAttributeValue(null, ATTR_COLOR_SAMPLE_DURATION);
686 String colorValueBucketsString =
687 parser.getAttributeValue(null, ATTR_COLOR_VALUE_BUCKETS);
688 if (colorSampleDurationString != null && colorValueBucketsString != null) {
689 long colorSampleDuration = Long.parseLong(colorSampleDurationString);
690 String[] buckets = colorValueBucketsString.split(",");
691 long[] bucketValues = new long[buckets.length];
692 for (int i = 0; i < bucketValues.length; ++i) {
693 bucketValues[i] = Long.parseLong(buckets[i]);
694 }
695 builder.setColorValues(bucketValues, colorSampleDuration);
696 }
697
Kenny Guy0ff44892018-01-10 14:06:42 +0000698 BrightnessChangeEvent event = builder.build();
Kenny Guy22bd0442017-10-26 00:15:54 +0100699 if (DEBUG) {
700 Slog.i(TAG, "Read event " + event.brightness
701 + " " + event.packageName);
702 }
703
704 if (event.userId != -1 && event.timeStamp > timeCutOff
705 && event.luxValues.length > 0) {
706 mEvents.append(event);
707 }
708 }
709 }
710 } catch (NullPointerException | NumberFormatException | XmlPullParserException
711 | IOException e) {
712 // Failed to parse something, just start with an empty event log.
713 mEvents = new RingBuffer<>(BrightnessChangeEvent.class, MAX_EVENTS);
714 Slog.e(TAG, "Failed to parse brightness event", e);
715 // Re-throw so we will delete the bad file.
716 throw new IOException("failed to parse file", e);
717 }
718 }
719
Michael Wright2155bc22018-05-01 00:38:32 +0100720 public void dump(final PrintWriter pw) {
Kenny Guye1674e52017-11-30 16:25:26 +0000721 pw.println("BrightnessTracker state:");
Kenny Guycfe7b702017-11-14 21:04:58 +0000722 synchronized (mDataCollectionLock) {
Kenny Guy05ce8092018-01-17 13:44:20 +0000723 pw.println(" mStarted=" + mStarted);
Michael Wright2155bc22018-05-01 00:38:32 +0100724 pw.println(" mLastBatteryLevel=" + mLastBatteryLevel);
725 pw.println(" mLastBrightness=" + mLastBrightness);
Kenny Guycfe7b702017-11-14 21:04:58 +0000726 pw.println(" mLastSensorReadings.size=" + mLastSensorReadings.size());
Kenny Guye1674e52017-11-30 16:25:26 +0000727 if (!mLastSensorReadings.isEmpty()) {
728 pw.println(" mLastSensorReadings time span "
729 + mLastSensorReadings.peekFirst().timestamp + "->"
730 + mLastSensorReadings.peekLast().timestamp);
731 }
732 }
733 synchronized (mEventsLock) {
734 pw.println(" mEventsDirty=" + mEventsDirty);
735 pw.println(" mEvents.size=" + mEvents.size());
736 BrightnessChangeEvent[] events = mEvents.toArray();
737 for (int i = 0; i < events.length; ++i) {
Michael Wright2155bc22018-05-01 00:38:32 +0100738 pw.print(" " + FORMAT.format(new Date(events[i].timeStamp)));
739 pw.print(", userId=" + events[i].userId);
Kenny Guy53d06612018-01-30 14:19:13 +0000740 pw.print(", " + events[i].lastBrightness + "->" + events[i].brightness);
741 pw.print(", isUserSetBrightness=" + events[i].isUserSetBrightness);
742 pw.print(", powerBrightnessFactor=" + events[i].powerBrightnessFactor);
743 pw.print(", isDefaultBrightnessConfig=" + events[i].isDefaultBrightnessConfig);
744 pw.print(" {");
Kenny Guye1674e52017-11-30 16:25:26 +0000745 for (int j = 0; j < events[i].luxValues.length; ++j){
746 if (j != 0) {
747 pw.print(", ");
748 }
749 pw.print("(" + events[i].luxValues[j] + "," + events[i].luxTimestamps[j] + ")");
750 }
751 pw.println("}");
752 }
Kenny Guycfe7b702017-11-14 21:04:58 +0000753 }
Michael Wright2155bc22018-05-01 00:38:32 +0100754 pw.println(" mWriteBrightnessTrackerStateScheduled="
755 + mWriteBrightnessTrackerStateScheduled);
756 mBgHandler.runWithScissors(() -> dumpLocal(pw), 1000);
Peeyush Agarwalcc155dd2018-01-10 11:51:33 +0000757 if (mAmbientBrightnessStatsTracker != null) {
Peeyush Agarwal8c2006d2018-02-08 12:39:39 +0000758 pw.println();
Peeyush Agarwalcc155dd2018-01-10 11:51:33 +0000759 mAmbientBrightnessStatsTracker.dump(pw);
760 }
761 }
762
Michael Wright2155bc22018-05-01 00:38:32 +0100763 private void dumpLocal(PrintWriter pw) {
764 pw.println(" mSensorRegistered=" + mSensorRegistered);
Kenny Guy908108a2019-01-17 12:56:34 +0000765 pw.println(" mColorSamplingEnabled=" + mColorSamplingEnabled);
766 pw.println(" mNoFramesToSample=" + mNoFramesToSample);
767 pw.println(" mFrameRate=" + mFrameRate);
768 }
769
770 private void enableColorSampling() {
771 if (!mInjector.isBrightnessModeAutomatic(mContentResolver)
772 || !mInjector.isInteractive(mContext)
Kenny Guyd6fd7d62019-11-13 15:33:19 +0000773 || mColorSamplingEnabled
774 || mBrightnessConfiguration == null
775 || !mBrightnessConfiguration.shouldCollectColorSamples()) {
Kenny Guy908108a2019-01-17 12:56:34 +0000776 return;
777 }
778
779 mFrameRate = mInjector.getFrameRate(mContext);
780 if (mFrameRate <= 0) {
781 Slog.wtf(TAG, "Default display has a zero or negative framerate.");
782 return;
783 }
784 mNoFramesToSample = (int) (mFrameRate * COLOR_SAMPLE_DURATION);
785
786 DisplayedContentSamplingAttributes attributes = mInjector.getSamplingAttributes();
787 if (DEBUG && attributes != null) {
788 Slog.d(TAG, "Color sampling"
789 + " mask=0x" + Integer.toHexString(attributes.getComponentMask())
790 + " dataSpace=0x" + Integer.toHexString(attributes.getDataspace())
791 + " pixelFormat=0x" + Integer.toHexString(attributes.getPixelFormat()));
792 }
793 // Do we support sampling the Value component of HSV
794 if (attributes != null && attributes.getPixelFormat() == PixelFormat.HSV_888
795 && (attributes.getComponentMask() & COLOR_SAMPLE_COMPONENT_MASK) != 0) {
796
797 mColorSamplingEnabled = mInjector.enableColorSampling(/* enable= */true,
798 mNoFramesToSample);
799 if (DEBUG) {
800 Slog.i(TAG, "turning on color sampling for "
801 + mNoFramesToSample + " frames, success=" + mColorSamplingEnabled);
802 }
803 }
804 if (mColorSamplingEnabled && mDisplayListener == null) {
805 mDisplayListener = new DisplayListener();
806 mInjector.registerDisplayListener(mContext, mDisplayListener, mBgHandler);
807 }
808 }
809
810 private void disableColorSampling() {
811 if (!mColorSamplingEnabled) {
812 return;
813 }
814 mInjector.enableColorSampling(/* enable= */ false, /* noFrames= */ 0);
815 mColorSamplingEnabled = false;
816 if (mDisplayListener != null) {
817 mInjector.unRegisterDisplayListener(mContext, mDisplayListener);
818 mDisplayListener = null;
819 }
820 if (DEBUG) {
821 Slog.i(TAG, "turning off color sampling");
822 }
823 }
824
825 private void updateColorSampling() {
826 if (!mColorSamplingEnabled) {
827 return;
828 }
829 float frameRate = mInjector.getFrameRate(mContext);
830 if (frameRate != mFrameRate) {
831 disableColorSampling();
832 enableColorSampling();
833 }
Michael Wright2155bc22018-05-01 00:38:32 +0100834 }
835
Peeyush Agarwalcc155dd2018-01-10 11:51:33 +0000836 public ParceledListSlice<AmbientBrightnessDayStats> getAmbientBrightnessStats(int userId) {
Kenny Guy637f8be2018-04-03 13:16:58 +0100837 if (mAmbientBrightnessStatsTracker != null) {
838 ArrayList<AmbientBrightnessDayStats> stats =
Michael Wright2155bc22018-05-01 00:38:32 +0100839 mAmbientBrightnessStatsTracker.getUserStats(userId);
Kenny Guy637f8be2018-04-03 13:16:58 +0100840 if (stats != null) {
841 return new ParceledListSlice<>(stats);
842 }
843 }
844 return ParceledListSlice.emptyList();
Kenny Guycfe7b702017-11-14 21:04:58 +0000845 }
846
Kenny Guy22bd0442017-10-26 00:15:54 +0100847 // Not allowed to keep the SensorEvent so used to copy the data we care about.
848 private static class LightData {
849 public float lux;
850 // Time in elapsedRealtimeNanos
851 public long timestamp;
852 }
853
854 private void recordSensorEvent(SensorEvent event) {
855 long horizon = mInjector.elapsedRealtimeNanos() - LUX_EVENT_HORIZON;
856 synchronized (mDataCollectionLock) {
857 if (DEBUG) {
858 Slog.v(TAG, "Sensor event " + event);
859 }
860 if (!mLastSensorReadings.isEmpty()
861 && event.timestamp < mLastSensorReadings.getLast().timestamp) {
862 // Ignore event that came out of order.
863 return;
864 }
865 LightData data = null;
866 while (!mLastSensorReadings.isEmpty()
867 && mLastSensorReadings.getFirst().timestamp < horizon) {
868 // Remove data that has fallen out of the window.
869 data = mLastSensorReadings.removeFirst();
870 }
871 // We put back the last one we removed so we know how long
872 // the first sensor reading was valid for.
873 if (data != null) {
874 mLastSensorReadings.addFirst(data);
875 }
876
877 data = new LightData();
878 data.timestamp = event.timestamp;
879 data.lux = event.values[0];
880 mLastSensorReadings.addLast(data);
881 }
882 }
883
Peeyush Agarwalcc155dd2018-01-10 11:51:33 +0000884 private void recordAmbientBrightnessStats(SensorEvent event) {
885 mAmbientBrightnessStatsTracker.add(mCurrentUserId, event.values[0]);
886 }
887
Kenny Guy22bd0442017-10-26 00:15:54 +0100888 private void batteryLevelChanged(int level, int scale) {
889 synchronized (mDataCollectionLock) {
890 mLastBatteryLevel = (float) level / (float) scale;
891 }
892 }
893
894 private final class SensorListener implements SensorEventListener {
895 @Override
896 public void onSensorChanged(SensorEvent event) {
897 recordSensorEvent(event);
Peeyush Agarwalcc155dd2018-01-10 11:51:33 +0000898 recordAmbientBrightnessStats(event);
Kenny Guy22bd0442017-10-26 00:15:54 +0100899 }
900
901 @Override
902 public void onAccuracyChanged(Sensor sensor, int accuracy) {
903
904 }
905 }
906
Kenny Guy908108a2019-01-17 12:56:34 +0000907 private final class DisplayListener implements DisplayManager.DisplayListener {
908
909 @Override
910 public void onDisplayAdded(int displayId) {
911 // Ignore
912 }
913
914 @Override
915 public void onDisplayRemoved(int displayId) {
916 // Ignore
917 }
918
919 @Override
920 public void onDisplayChanged(int displayId) {
921 if (displayId == Display.DEFAULT_DISPLAY) {
922 updateColorSampling();
923 }
924 }
925 }
926
Kenny Guy3d7172c2018-03-19 14:18:23 +0000927 private final class SettingsObserver extends ContentObserver {
928 public SettingsObserver(Handler handler) {
929 super(handler);
930 }
931
932 @Override
933 public void onChange(boolean selfChange, Uri uri) {
934 if (DEBUG) {
935 Slog.v(TAG, "settings change " + uri);
936 }
937 if (mInjector.isBrightnessModeAutomatic(mContentResolver)) {
938 mBgHandler.obtainMessage(MSG_START_SENSOR_LISTENER).sendToTarget();
939 } else {
940 mBgHandler.obtainMessage(MSG_STOP_SENSOR_LISTENER).sendToTarget();
941 }
942 }
943 }
944
Kenny Guy22bd0442017-10-26 00:15:54 +0100945 private final class Receiver extends BroadcastReceiver {
946 @Override
947 public void onReceive(Context context, Intent intent) {
948 if (DEBUG) {
949 Slog.d(TAG, "Received " + intent.getAction());
950 }
951 String action = intent.getAction();
952 if (Intent.ACTION_SHUTDOWN.equals(action)) {
953 stop();
Peeyush Agarwalcc155dd2018-01-10 11:51:33 +0000954 scheduleWriteBrightnessTrackerState();
Kenny Guy22bd0442017-10-26 00:15:54 +0100955 } else if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
956 int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
957 int scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, 0);
958 if (level != -1 && scale != 0) {
959 batteryLevelChanged(level, scale);
960 }
Kenny Guy689ab8f2017-11-29 12:12:06 +0000961 } else if (Intent.ACTION_SCREEN_OFF.equals(action)) {
Kenny Guy3d7172c2018-03-19 14:18:23 +0000962 mBgHandler.obtainMessage(MSG_STOP_SENSOR_LISTENER).sendToTarget();
Kenny Guy689ab8f2017-11-29 12:12:06 +0000963 } else if (Intent.ACTION_SCREEN_ON.equals(action)) {
Kenny Guy3d7172c2018-03-19 14:18:23 +0000964 mBgHandler.obtainMessage(MSG_START_SENSOR_LISTENER).sendToTarget();
Kenny Guy22bd0442017-10-26 00:15:54 +0100965 }
966 }
967 }
968
Michael Wright144aac92017-12-21 18:37:41 +0000969 private final class TrackerHandler extends Handler {
970 public TrackerHandler(Looper looper) {
971 super(looper, null, true /*async*/);
972 }
973 public void handleMessage(Message msg) {
974 switch (msg.what) {
975 case MSG_BACKGROUND_START:
976 backgroundStart((float)msg.obj /*initial brightness*/);
977 break;
978 case MSG_BRIGHTNESS_CHANGED:
Kenny Guy53d06612018-01-30 14:19:13 +0000979 BrightnessChangeValues values = (BrightnessChangeValues) msg.obj;
Michael Wright144aac92017-12-21 18:37:41 +0000980 boolean userInitiatedChange = (msg.arg1 == 1);
Kenny Guy53d06612018-01-30 14:19:13 +0000981 handleBrightnessChanged(values.brightness, userInitiatedChange,
982 values.powerBrightnessFactor, values.isUserSetBrightness,
Kenny Guy2b326bf2018-04-18 18:42:10 +0100983 values.isDefaultBrightnessConfig, values.timestamp);
Michael Wright144aac92017-12-21 18:37:41 +0000984 break;
Kenny Guy3d7172c2018-03-19 14:18:23 +0000985 case MSG_START_SENSOR_LISTENER:
986 startSensorListener();
Kenny Guy908108a2019-01-17 12:56:34 +0000987 enableColorSampling();
Kenny Guy3d7172c2018-03-19 14:18:23 +0000988 break;
989 case MSG_STOP_SENSOR_LISTENER:
990 stopSensorListener();
Kenny Guy908108a2019-01-17 12:56:34 +0000991 disableColorSampling();
Kenny Guy3d7172c2018-03-19 14:18:23 +0000992 break;
Kenny Guyd6fd7d62019-11-13 15:33:19 +0000993 case MSG_BRIGHTNESS_CONFIG_CHANGED:
994 mBrightnessConfiguration = (BrightnessConfiguration) msg.obj;
995 boolean shouldCollectColorSamples =
996 mBrightnessConfiguration != null
997 && mBrightnessConfiguration.shouldCollectColorSamples();
998 if (shouldCollectColorSamples && !mColorSamplingEnabled) {
999 enableColorSampling();
1000 } else if (!shouldCollectColorSamples && mColorSamplingEnabled) {
1001 disableColorSampling();
1002 }
1003 break;
1004
Michael Wright144aac92017-12-21 18:37:41 +00001005 }
1006 }
1007 }
1008
Kenny Guy53d06612018-01-30 14:19:13 +00001009 private static class BrightnessChangeValues {
1010 final float brightness;
1011 final float powerBrightnessFactor;
1012 final boolean isUserSetBrightness;
1013 final boolean isDefaultBrightnessConfig;
Kenny Guy2b326bf2018-04-18 18:42:10 +01001014 final long timestamp;
Kenny Guy53d06612018-01-30 14:19:13 +00001015
1016 BrightnessChangeValues(float brightness, float powerBrightnessFactor,
Kenny Guy2b326bf2018-04-18 18:42:10 +01001017 boolean isUserSetBrightness, boolean isDefaultBrightnessConfig,
1018 long timestamp) {
Kenny Guy53d06612018-01-30 14:19:13 +00001019 this.brightness = brightness;
1020 this.powerBrightnessFactor = powerBrightnessFactor;
1021 this.isUserSetBrightness = isUserSetBrightness;
1022 this.isDefaultBrightnessConfig = isDefaultBrightnessConfig;
Kenny Guy2b326bf2018-04-18 18:42:10 +01001023 this.timestamp = timestamp;
Kenny Guy53d06612018-01-30 14:19:13 +00001024 }
1025 }
1026
Kenny Guy22bd0442017-10-26 00:15:54 +01001027 @VisibleForTesting
1028 static class Injector {
1029 public void registerSensorListener(Context context,
Kenny Guy689ab8f2017-11-29 12:12:06 +00001030 SensorEventListener sensorListener, Handler handler) {
Kenny Guy22bd0442017-10-26 00:15:54 +01001031 SensorManager sensorManager = context.getSystemService(SensorManager.class);
1032 Sensor lightSensor = sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
1033 sensorManager.registerListener(sensorListener,
Kenny Guy689ab8f2017-11-29 12:12:06 +00001034 lightSensor, SensorManager.SENSOR_DELAY_NORMAL, handler);
Kenny Guy22bd0442017-10-26 00:15:54 +01001035 }
1036
1037 public void unregisterSensorListener(Context context, SensorEventListener sensorListener) {
1038 SensorManager sensorManager = context.getSystemService(SensorManager.class);
1039 sensorManager.unregisterListener(sensorListener);
1040 }
1041
Kenny Guy3d7172c2018-03-19 14:18:23 +00001042 public void registerBrightnessModeObserver(ContentResolver resolver,
1043 ContentObserver settingsObserver) {
1044 resolver.registerContentObserver(Settings.System.getUriFor(
1045 Settings.System.SCREEN_BRIGHTNESS_MODE),
1046 false, settingsObserver, UserHandle.USER_ALL);
1047 }
1048
1049 public void unregisterBrightnessModeObserver(Context context,
1050 ContentObserver settingsObserver) {
1051 context.getContentResolver().unregisterContentObserver(settingsObserver);
1052 }
1053
Kenny Guy22bd0442017-10-26 00:15:54 +01001054 public void registerReceiver(Context context,
1055 BroadcastReceiver receiver, IntentFilter filter) {
1056 context.registerReceiver(receiver, filter);
1057 }
1058
1059 public void unregisterReceiver(Context context,
1060 BroadcastReceiver receiver) {
1061 context.unregisterReceiver(receiver);
1062 }
1063
1064 public Handler getBackgroundHandler() {
1065 return BackgroundThread.getHandler();
1066 }
1067
Kenny Guy3d7172c2018-03-19 14:18:23 +00001068 public boolean isBrightnessModeAutomatic(ContentResolver resolver) {
1069 return Settings.System.getIntForUser(resolver, Settings.System.SCREEN_BRIGHTNESS_MODE,
1070 Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL, UserHandle.USER_CURRENT)
1071 == Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC;
1072 }
1073
Kenny Guy22bd0442017-10-26 00:15:54 +01001074 public int getSecureIntForUser(ContentResolver resolver, String setting, int defaultValue,
1075 int userId) {
1076 return Settings.Secure.getIntForUser(resolver, setting, defaultValue, userId);
1077 }
1078
Peeyush Agarwalcc155dd2018-01-10 11:51:33 +00001079 public AtomicFile getFile(String filename) {
1080 return new AtomicFile(new File(Environment.getDataSystemDeDirectory(), filename));
Kenny Guy22bd0442017-10-26 00:15:54 +01001081 }
1082
1083 public long currentTimeMillis() {
1084 return System.currentTimeMillis();
1085 }
1086
1087 public long elapsedRealtimeNanos() {
1088 return SystemClock.elapsedRealtimeNanos();
1089 }
1090
1091 public int getUserSerialNumber(UserManager userManager, int userId) {
1092 return userManager.getUserSerialNumber(userId);
1093 }
1094
1095 public int getUserId(UserManager userManager, int userSerialNumber) {
1096 return userManager.getUserHandle(userSerialNumber);
1097 }
1098
Kenny Guy6c90d8a2018-02-20 16:53:27 +00001099 public int[] getProfileIds(UserManager userManager, int userId) {
1100 if (userManager != null) {
1101 return userManager.getProfileIds(userId, false);
1102 } else {
1103 return new int[]{userId};
1104 }
1105 }
1106
Kenny Guy22bd0442017-10-26 00:15:54 +01001107 public ActivityManager.StackInfo getFocusedStack() throws RemoteException {
Wale Ogunwale04d9cb52018-04-30 13:55:07 -07001108 return ActivityTaskManager.getService().getFocusedStackInfo();
Kenny Guy22bd0442017-10-26 00:15:54 +01001109 }
Kenny Guycfe7b702017-11-14 21:04:58 +00001110
1111 public void scheduleIdleJob(Context context) {
1112 BrightnessIdleJob.scheduleJob(context);
1113 }
1114
1115 public void cancelIdleJob(Context context) {
1116 BrightnessIdleJob.cancelJob(context);
1117 }
Kenny Guy689ab8f2017-11-29 12:12:06 +00001118
1119 public boolean isInteractive(Context context) {
1120 return context.getSystemService(PowerManager.class).isInteractive();
1121 }
Kenny Guy2385d4f2018-08-15 14:35:11 +01001122
Christine Franks71e003e2019-01-24 14:40:20 -08001123 public int getNightDisplayColorTemperature(Context context) {
1124 return context.getSystemService(ColorDisplayManager.class)
1125 .getNightDisplayColorTemperature();
Kenny Guy2385d4f2018-08-15 14:35:11 +01001126 }
1127
Christine Franks71e003e2019-01-24 14:40:20 -08001128 public boolean isNightDisplayActivated(Context context) {
1129 return context.getSystemService(ColorDisplayManager.class).isNightDisplayActivated();
Kenny Guy2385d4f2018-08-15 14:35:11 +01001130 }
Kenny Guy908108a2019-01-17 12:56:34 +00001131
1132 public DisplayedContentSample sampleColor(int noFramesToSample) {
1133 final DisplayManagerInternal displayManagerInternal =
1134 LocalServices.getService(DisplayManagerInternal.class);
1135 return displayManagerInternal.getDisplayedContentSample(
1136 Display.DEFAULT_DISPLAY, noFramesToSample, 0);
1137 }
1138
1139 public float getFrameRate(Context context) {
1140 final DisplayManager displayManager = context.getSystemService(DisplayManager.class);
1141 Display display = displayManager.getDisplay(Display.DEFAULT_DISPLAY);
1142 return display.getRefreshRate();
1143 }
1144
1145 public DisplayedContentSamplingAttributes getSamplingAttributes() {
1146 final DisplayManagerInternal displayManagerInternal =
1147 LocalServices.getService(DisplayManagerInternal.class);
1148 return displayManagerInternal.getDisplayedContentSamplingAttributes(
1149 Display.DEFAULT_DISPLAY);
1150 }
1151
1152 public boolean enableColorSampling(boolean enable, int noFrames) {
1153 final DisplayManagerInternal displayManagerInternal =
1154 LocalServices.getService(DisplayManagerInternal.class);
1155 return displayManagerInternal.setDisplayedContentSamplingEnabled(
1156 Display.DEFAULT_DISPLAY, enable, COLOR_SAMPLE_COMPONENT_MASK, noFrames);
1157 }
1158
1159 public void registerDisplayListener(Context context,
1160 DisplayManager.DisplayListener listener, Handler handler) {
1161 final DisplayManager displayManager = context.getSystemService(DisplayManager.class);
1162 displayManager.registerDisplayListener(listener, handler);
1163 }
1164
1165 public void unRegisterDisplayListener(Context context,
1166 DisplayManager.DisplayListener listener) {
1167 final DisplayManager displayManager = context.getSystemService(DisplayManager.class);
1168 displayManager.unregisterDisplayListener(listener);
1169 }
Kenny Guy22bd0442017-10-26 00:15:54 +01001170 }
1171}