blob: 61b6fa073be993059f85c6bf1dbdccdaa78ea46e [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;
20import android.app.ActivityManager;
21import android.content.BroadcastReceiver;
22import android.content.ContentResolver;
23import android.content.Context;
24import android.content.Intent;
25import android.content.IntentFilter;
26import android.content.pm.ParceledListSlice;
27import android.database.ContentObserver;
28import android.hardware.Sensor;
29import android.hardware.SensorEvent;
30import android.hardware.SensorEventListener;
31import android.hardware.SensorManager;
32import android.hardware.display.BrightnessChangeEvent;
33import android.net.Uri;
34import android.os.BatteryManager;
35import android.os.Environment;
36import android.os.Handler;
Kenny Guy689ab8f2017-11-29 12:12:06 +000037import android.os.PowerManager;
Kenny Guy22bd0442017-10-26 00:15:54 +010038import android.os.RemoteException;
39import android.os.SystemClock;
40import android.os.UserHandle;
41import android.os.UserManager;
42import android.provider.Settings;
43import android.util.AtomicFile;
44import android.util.Slog;
45import android.util.Xml;
46
47import com.android.internal.annotations.GuardedBy;
48import com.android.internal.annotations.VisibleForTesting;
49import com.android.internal.os.BackgroundThread;
50import com.android.internal.util.FastXmlSerializer;
51import com.android.internal.util.RingBuffer;
52
53import libcore.io.IoUtils;
54
55import org.xmlpull.v1.XmlPullParser;
56import org.xmlpull.v1.XmlPullParserException;
57import org.xmlpull.v1.XmlSerializer;
58
59import java.io.File;
60import java.io.FileInputStream;
61import java.io.FileOutputStream;
62import java.io.IOException;
63import java.io.InputStream;
64import java.io.OutputStream;
Kenny Guycfe7b702017-11-14 21:04:58 +000065import java.io.PrintWriter;
Kenny Guy22bd0442017-10-26 00:15:54 +010066import java.nio.charset.StandardCharsets;
67import java.util.ArrayDeque;
68import java.util.ArrayList;
69
70import java.util.Deque;
Kenny Guy22bd0442017-10-26 00:15:54 +010071import java.util.concurrent.TimeUnit;
72
73/**
74 * Class that tracks recent brightness settings changes and stores
75 * associated information such as light sensor readings.
76 */
77public class BrightnessTracker {
78
Kenny Guycfe7b702017-11-14 21:04:58 +000079 static final String TAG = "BrightnessTracker";
80 static final boolean DEBUG = false;
Kenny Guy22bd0442017-10-26 00:15:54 +010081
82 private static final String EVENTS_FILE = "brightness_events.xml";
83 private static final int MAX_EVENTS = 100;
84 // Discard events when reading or writing that are older than this.
85 private static final long MAX_EVENT_AGE = TimeUnit.DAYS.toMillis(30);
86 // Time over which we keep lux sensor readings.
87 private static final long LUX_EVENT_HORIZON = TimeUnit.SECONDS.toNanos(10);
88
89 private static final String TAG_EVENTS = "events";
90 private static final String TAG_EVENT = "event";
91 private static final String ATTR_BRIGHTNESS = "brightness";
92 private static final String ATTR_TIMESTAMP = "timestamp";
93 private static final String ATTR_PACKAGE_NAME = "packageName";
94 private static final String ATTR_USER = "user";
95 private static final String ATTR_LUX = "lux";
96 private static final String ATTR_LUX_TIMESTAMPS = "luxTimestamps";
97 private static final String ATTR_BATTERY_LEVEL = "batteryLevel";
98 private static final String ATTR_NIGHT_MODE = "nightMode";
99 private static final String ATTR_COLOR_TEMPERATURE = "colorTemperature";
100 private static final String ATTR_LAST_BRIGHTNESS = "lastBrightness";
101
102 // Lock held while accessing mEvents, is held while writing events to flash.
103 private final Object mEventsLock = new Object();
104 @GuardedBy("mEventsLock")
105 private RingBuffer<BrightnessChangeEvent> mEvents
106 = new RingBuffer<>(BrightnessChangeEvent.class, MAX_EVENTS);
Kenny Guycfe7b702017-11-14 21:04:58 +0000107 @GuardedBy("mEventsLock")
108 private boolean mEventsDirty;
Kenny Guy22bd0442017-10-26 00:15:54 +0100109 private final Runnable mEventsWriter = () -> writeEvents();
110 private volatile boolean mWriteEventsScheduled;
111
112 private UserManager mUserManager;
113 private final Context mContext;
114 private final ContentResolver mContentResolver;
115 private Handler mBgHandler;
116 // mSettingsObserver, mBroadcastReceiver and mSensorListener should only be used on
117 // the mBgHandler thread.
118 private SettingsObserver mSettingsObserver;
119 private BroadcastReceiver mBroadcastReceiver;
120 private SensorListener mSensorListener;
121
122 // Lock held while collecting data related to brightness changes.
123 private final Object mDataCollectionLock = new Object();
124 @GuardedBy("mDataCollectionLock")
125 private Deque<LightData> mLastSensorReadings = new ArrayDeque<>();
126 @GuardedBy("mDataCollectionLock")
127 private float mLastBatteryLevel = Float.NaN;
128 @GuardedBy("mDataCollectionLock")
129 private int mIgnoreBrightness = -1;
130 @GuardedBy("mDataCollectionLock")
131 private int mLastBrightness = -1;
132
133 private final Injector mInjector;
134
135 public BrightnessTracker(Context context, @Nullable Injector injector) {
136 // Note this will be called very early in boot, other system
137 // services may not be present.
138 mContext = context;
139 mContentResolver = context.getContentResolver();
140 if (injector != null) {
141 mInjector = injector;
142 } else {
143 mInjector = new Injector();
144 }
145 }
146
147 /** Start listening for brightness slider events */
148 public void start() {
149 if (DEBUG) {
150 Slog.d(TAG, "Start");
151 }
152 mBgHandler = mInjector.getBackgroundHandler();
153 mUserManager = mContext.getSystemService(UserManager.class);
154
155 mBgHandler.post(() -> backgroundStart());
156 }
157
158 private void backgroundStart() {
159 readEvents();
160
161 mLastBrightness = mInjector.getSystemIntForUser(mContentResolver,
162 Settings.System.SCREEN_BRIGHTNESS, -1,
163 UserHandle.USER_CURRENT);
164
165 mSensorListener = new SensorListener();
Kenny Guy689ab8f2017-11-29 12:12:06 +0000166
167 if (mInjector.isInteractive(mContext)) {
168 mInjector.registerSensorListener(mContext, mSensorListener, mBgHandler);
169 }
Kenny Guy22bd0442017-10-26 00:15:54 +0100170
171 mSettingsObserver = new SettingsObserver(mBgHandler);
172 mInjector.registerBrightnessObserver(mContentResolver, mSettingsObserver);
173
174 final IntentFilter intentFilter = new IntentFilter();
175 intentFilter.addAction(Intent.ACTION_SHUTDOWN);
176 intentFilter.addAction(Intent.ACTION_BATTERY_CHANGED);
Kenny Guy689ab8f2017-11-29 12:12:06 +0000177 intentFilter.addAction(Intent.ACTION_SCREEN_ON);
178 intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
Kenny Guy22bd0442017-10-26 00:15:54 +0100179 mBroadcastReceiver = new Receiver();
180 mInjector.registerReceiver(mContext, mBroadcastReceiver, intentFilter);
Kenny Guycfe7b702017-11-14 21:04:58 +0000181
182 mInjector.scheduleIdleJob(mContext);
Kenny Guy22bd0442017-10-26 00:15:54 +0100183 }
184
185 /** Stop listening for events */
186 @VisibleForTesting
187 void stop() {
188 if (DEBUG) {
189 Slog.d(TAG, "Stop");
190 }
191 mInjector.unregisterSensorListener(mContext, mSensorListener);
192 mInjector.unregisterReceiver(mContext, mBroadcastReceiver);
193 mInjector.unregisterBrightnessObserver(mContext, mSettingsObserver);
Kenny Guycfe7b702017-11-14 21:04:58 +0000194 mInjector.cancelIdleJob(mContext);
Kenny Guy22bd0442017-10-26 00:15:54 +0100195 }
196
197 /**
198 * @param userId userId to fetch data for.
Kenny Guy29aa30e2017-11-30 13:43:46 +0000199 * @param includePackage if false we will null out BrightnessChangeEvent.packageName
Kenny Guy22bd0442017-10-26 00:15:54 +0100200 * @return List of recent {@link BrightnessChangeEvent}s
201 */
Kenny Guy29aa30e2017-11-30 13:43:46 +0000202 public ParceledListSlice<BrightnessChangeEvent> getEvents(int userId, boolean includePackage) {
Kenny Guy22bd0442017-10-26 00:15:54 +0100203 // TODO include apps from any managed profiles in the brightness information.
204 BrightnessChangeEvent[] events;
205 synchronized (mEventsLock) {
206 events = mEvents.toArray();
207 }
208 ArrayList<BrightnessChangeEvent> out = new ArrayList<>(events.length);
209 for (int i = 0; i < events.length; ++i) {
210 if (events[i].userId == userId) {
Kenny Guy29aa30e2017-11-30 13:43:46 +0000211 if (includePackage) {
212 out.add(events[i]);
213 } else {
214 BrightnessChangeEvent event = new BrightnessChangeEvent((events[i]));
215 event.packageName = null;
216 out.add(event);
217 }
Kenny Guy22bd0442017-10-26 00:15:54 +0100218 }
219 }
220 return new ParceledListSlice<>(out);
221 }
222
223 /** Sets brightness without logging the brightness change event */
224 public void setBrightness(int brightness, int userId) {
225 synchronized (mDataCollectionLock) {
226 mIgnoreBrightness = brightness;
227 }
228 mInjector.putSystemIntForUser(mContentResolver, Settings.System.SCREEN_BRIGHTNESS,
229 brightness, userId);
230 }
231
Kenny Guycfe7b702017-11-14 21:04:58 +0000232 public void persistEvents() {
233 scheduleWriteEvents();
234 }
235
Kenny Guy22bd0442017-10-26 00:15:54 +0100236 private void handleBrightnessChanged() {
237 if (DEBUG) {
238 Slog.d(TAG, "Brightness change");
239 }
240 final BrightnessChangeEvent event = new BrightnessChangeEvent();
241 event.timeStamp = mInjector.currentTimeMillis();
242
243 int brightness = mInjector.getSystemIntForUser(mContentResolver,
244 Settings.System.SCREEN_BRIGHTNESS, -1,
245 UserHandle.USER_CURRENT);
246
247 synchronized (mDataCollectionLock) {
248 int previousBrightness = mLastBrightness;
249 mLastBrightness = brightness;
250
251 if (brightness == -1 || brightness == mIgnoreBrightness) {
252 // Notified of brightness change but no setting or self change so ignore.
253 mIgnoreBrightness = -1;
254 return;
255 }
256
257 final int readingCount = mLastSensorReadings.size();
258 if (readingCount == 0) {
259 // No sensor data so ignore this.
260 return;
261 }
262
263 event.luxValues = new float[readingCount];
264 event.luxTimestamps = new long[readingCount];
265
266 int pos = 0;
267
268 // Convert sensor timestamp in elapsed time nanos to current time millis.
269 long currentTimeMillis = mInjector.currentTimeMillis();
270 long elapsedTimeNanos = mInjector.elapsedRealtimeNanos();
271 for (LightData reading : mLastSensorReadings) {
272 event.luxValues[pos] = reading.lux;
273 event.luxTimestamps[pos] = currentTimeMillis -
274 TimeUnit.NANOSECONDS.toMillis(elapsedTimeNanos - reading.timestamp);
275 ++pos;
276 }
277
278 event.batteryLevel = mLastBatteryLevel;
279 event.lastBrightness = previousBrightness;
280 }
281
282 event.brightness = brightness;
283
284 try {
285 final ActivityManager.StackInfo focusedStack = mInjector.getFocusedStack();
286 event.userId = focusedStack.userId;
287 event.packageName = focusedStack.topActivity.getPackageName();
288 } catch (RemoteException e) {
289 // Really shouldn't be possible.
290 }
291
292 event.nightMode = mInjector.getSecureIntForUser(mContentResolver,
293 Settings.Secure.NIGHT_DISPLAY_ACTIVATED, 0, UserHandle.USER_CURRENT)
294 == 1;
295 event.colorTemperature = mInjector.getSecureIntForUser(mContentResolver,
296 Settings.Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE,
297 0, UserHandle.USER_CURRENT);
298
299 if (DEBUG) {
300 Slog.d(TAG, "Event " + event.brightness + " " + event.packageName);
301 }
302 synchronized (mEventsLock) {
Kenny Guycfe7b702017-11-14 21:04:58 +0000303 mEventsDirty = true;
Kenny Guy22bd0442017-10-26 00:15:54 +0100304 mEvents.append(event);
305 }
306 }
307
308 private void scheduleWriteEvents() {
309 if (!mWriteEventsScheduled) {
310 mBgHandler.post(mEventsWriter);
311 mWriteEventsScheduled = true;
312 }
313 }
314
315 private void writeEvents() {
316 mWriteEventsScheduled = false;
Kenny Guy22bd0442017-10-26 00:15:54 +0100317 synchronized (mEventsLock) {
Kenny Guycfe7b702017-11-14 21:04:58 +0000318 if (!mEventsDirty) {
319 // Nothing to write
320 return;
321 }
322
Kenny Guy22bd0442017-10-26 00:15:54 +0100323 final AtomicFile writeTo = mInjector.getFile();
324 if (writeTo == null) {
325 return;
326 }
327 if (mEvents.isEmpty()) {
328 if (writeTo.exists()) {
329 writeTo.delete();
330 }
Kenny Guycfe7b702017-11-14 21:04:58 +0000331 mEventsDirty = false;
Kenny Guy22bd0442017-10-26 00:15:54 +0100332 } else {
333 FileOutputStream output = null;
334 try {
335 output = writeTo.startWrite();
336 writeEventsLocked(output);
337 writeTo.finishWrite(output);
Kenny Guycfe7b702017-11-14 21:04:58 +0000338 mEventsDirty = false;
Kenny Guy22bd0442017-10-26 00:15:54 +0100339 } catch (IOException e) {
340 writeTo.failWrite(output);
341 Slog.e(TAG, "Failed to write change mEvents.", e);
342 }
343 }
344 }
345 }
346
347 private void readEvents() {
348 synchronized (mEventsLock) {
Kenny Guycfe7b702017-11-14 21:04:58 +0000349 // Read might prune events so mark as dirty.
350 mEventsDirty = true;
Kenny Guy22bd0442017-10-26 00:15:54 +0100351 mEvents.clear();
352 final AtomicFile readFrom = mInjector.getFile();
353 if (readFrom != null && readFrom.exists()) {
354 FileInputStream input = null;
355 try {
356 input = readFrom.openRead();
357 readEventsLocked(input);
358 } catch (IOException e) {
359 readFrom.delete();
360 Slog.e(TAG, "Failed to read change mEvents.", e);
361 } finally {
362 IoUtils.closeQuietly(input);
363 }
364 }
365 }
366 }
367
368 @VisibleForTesting
369 @GuardedBy("mEventsLock")
370 void writeEventsLocked(OutputStream stream) throws IOException {
371 XmlSerializer out = new FastXmlSerializer();
372 out.setOutput(stream, StandardCharsets.UTF_8.name());
373 out.startDocument(null, true);
374 out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
375
376 out.startTag(null, TAG_EVENTS);
377 BrightnessChangeEvent[] toWrite = mEvents.toArray();
Kenny Guycfe7b702017-11-14 21:04:58 +0000378 // Clear events, code below will add back the ones that are still within the time window.
379 mEvents.clear();
Kenny Guy22bd0442017-10-26 00:15:54 +0100380 if (DEBUG) {
381 Slog.d(TAG, "Writing events " + toWrite.length);
382 }
Kenny Guycfe7b702017-11-14 21:04:58 +0000383 final long timeCutOff = mInjector.currentTimeMillis() - MAX_EVENT_AGE;
Kenny Guy22bd0442017-10-26 00:15:54 +0100384 for (int i = 0; i < toWrite.length; ++i) {
385 int userSerialNo = mInjector.getUserSerialNumber(mUserManager, toWrite[i].userId);
386 if (userSerialNo != -1 && toWrite[i].timeStamp > timeCutOff) {
Kenny Guycfe7b702017-11-14 21:04:58 +0000387 mEvents.append(toWrite[i]);
Kenny Guy22bd0442017-10-26 00:15:54 +0100388 out.startTag(null, TAG_EVENT);
389 out.attribute(null, ATTR_BRIGHTNESS, Integer.toString(toWrite[i].brightness));
390 out.attribute(null, ATTR_TIMESTAMP, Long.toString(toWrite[i].timeStamp));
391 out.attribute(null, ATTR_PACKAGE_NAME, toWrite[i].packageName);
392 out.attribute(null, ATTR_USER, Integer.toString(userSerialNo));
393 out.attribute(null, ATTR_BATTERY_LEVEL, Float.toString(toWrite[i].batteryLevel));
394 out.attribute(null, ATTR_NIGHT_MODE, Boolean.toString(toWrite[i].nightMode));
395 out.attribute(null, ATTR_COLOR_TEMPERATURE, Integer.toString(
396 toWrite[i].colorTemperature));
397 out.attribute(null, ATTR_LAST_BRIGHTNESS,
398 Integer.toString(toWrite[i].lastBrightness));
399 StringBuilder luxValues = new StringBuilder();
400 StringBuilder luxTimestamps = new StringBuilder();
401 for (int j = 0; j < toWrite[i].luxValues.length; ++j) {
402 if (j > 0) {
403 luxValues.append(',');
404 luxTimestamps.append(',');
405 }
406 luxValues.append(Float.toString(toWrite[i].luxValues[j]));
407 luxTimestamps.append(Long.toString(toWrite[i].luxTimestamps[j]));
408 }
409 out.attribute(null, ATTR_LUX, luxValues.toString());
410 out.attribute(null, ATTR_LUX_TIMESTAMPS, luxTimestamps.toString());
411 out.endTag(null, TAG_EVENT);
412 }
413 }
414 out.endTag(null, TAG_EVENTS);
415 out.endDocument();
416 stream.flush();
417 }
418
419 @VisibleForTesting
420 @GuardedBy("mEventsLock")
421 void readEventsLocked(InputStream stream) throws IOException {
422 try {
423 XmlPullParser parser = Xml.newPullParser();
424 parser.setInput(stream, StandardCharsets.UTF_8.name());
425
426 int type;
427 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
428 && type != XmlPullParser.START_TAG) {
429 }
430 String tag = parser.getName();
431 if (!TAG_EVENTS.equals(tag)) {
432 throw new XmlPullParserException(
433 "Events not found in brightness tracker file " + tag);
434 }
435
436 final long timeCutOff = mInjector.currentTimeMillis() - MAX_EVENT_AGE;
437
438 parser.next();
439 int outerDepth = parser.getDepth();
440 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
441 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
442 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
443 continue;
444 }
445 tag = parser.getName();
446 if (TAG_EVENT.equals(tag)) {
447 BrightnessChangeEvent event = new BrightnessChangeEvent();
448
449 String brightness = parser.getAttributeValue(null, ATTR_BRIGHTNESS);
450 event.brightness = Integer.parseInt(brightness);
451 String timestamp = parser.getAttributeValue(null, ATTR_TIMESTAMP);
452 event.timeStamp = Long.parseLong(timestamp);
453 event.packageName = parser.getAttributeValue(null, ATTR_PACKAGE_NAME);
454 String user = parser.getAttributeValue(null, ATTR_USER);
455 event.userId = mInjector.getUserId(mUserManager, Integer.parseInt(user));
456 String batteryLevel = parser.getAttributeValue(null, ATTR_BATTERY_LEVEL);
457 event.batteryLevel = Float.parseFloat(batteryLevel);
458 String nightMode = parser.getAttributeValue(null, ATTR_NIGHT_MODE);
459 event.nightMode = Boolean.parseBoolean(nightMode);
460 String colorTemperature =
461 parser.getAttributeValue(null, ATTR_COLOR_TEMPERATURE);
462 event.colorTemperature = Integer.parseInt(colorTemperature);
463 String lastBrightness = parser.getAttributeValue(null, ATTR_LAST_BRIGHTNESS);
464 event.lastBrightness = Integer.parseInt(lastBrightness);
465
466 String luxValue = parser.getAttributeValue(null, ATTR_LUX);
467 String luxTimestamp = parser.getAttributeValue(null, ATTR_LUX_TIMESTAMPS);
468
469 String[] luxValues = luxValue.split(",");
470 String[] luxTimestamps = luxTimestamp.split(",");
471 if (luxValues.length != luxTimestamps.length) {
472 continue;
473 }
474 event.luxValues = new float[luxValues.length];
475 event.luxTimestamps = new long[luxValues.length];
476 for (int i = 0; i < luxValues.length; ++i) {
477 event.luxValues[i] = Float.parseFloat(luxValues[i]);
478 event.luxTimestamps[i] = Long.parseLong(luxTimestamps[i]);
479 }
480
481 if (DEBUG) {
482 Slog.i(TAG, "Read event " + event.brightness
483 + " " + event.packageName);
484 }
485
486 if (event.userId != -1 && event.timeStamp > timeCutOff
487 && event.luxValues.length > 0) {
488 mEvents.append(event);
489 }
490 }
491 }
492 } catch (NullPointerException | NumberFormatException | XmlPullParserException
493 | IOException e) {
494 // Failed to parse something, just start with an empty event log.
495 mEvents = new RingBuffer<>(BrightnessChangeEvent.class, MAX_EVENTS);
496 Slog.e(TAG, "Failed to parse brightness event", e);
497 // Re-throw so we will delete the bad file.
498 throw new IOException("failed to parse file", e);
499 }
500 }
501
Kenny Guycfe7b702017-11-14 21:04:58 +0000502 public void dump(PrintWriter pw) {
503 synchronized (mEventsLock) {
504 pw.println("BrightnessTracker state:");
505 pw.println(" mEvents.size=" + mEvents.size());
506 pw.println(" mEventsDirty=" + mEventsDirty);
507 }
508 synchronized (mDataCollectionLock) {
509 pw.println(" mLastSensorReadings.size=" + mLastSensorReadings.size());
510 }
511 }
512
Kenny Guy22bd0442017-10-26 00:15:54 +0100513 // Not allowed to keep the SensorEvent so used to copy the data we care about.
514 private static class LightData {
515 public float lux;
516 // Time in elapsedRealtimeNanos
517 public long timestamp;
518 }
519
520 private void recordSensorEvent(SensorEvent event) {
521 long horizon = mInjector.elapsedRealtimeNanos() - LUX_EVENT_HORIZON;
522 synchronized (mDataCollectionLock) {
523 if (DEBUG) {
524 Slog.v(TAG, "Sensor event " + event);
525 }
526 if (!mLastSensorReadings.isEmpty()
527 && event.timestamp < mLastSensorReadings.getLast().timestamp) {
528 // Ignore event that came out of order.
529 return;
530 }
531 LightData data = null;
532 while (!mLastSensorReadings.isEmpty()
533 && mLastSensorReadings.getFirst().timestamp < horizon) {
534 // Remove data that has fallen out of the window.
535 data = mLastSensorReadings.removeFirst();
536 }
537 // We put back the last one we removed so we know how long
538 // the first sensor reading was valid for.
539 if (data != null) {
540 mLastSensorReadings.addFirst(data);
541 }
542
543 data = new LightData();
544 data.timestamp = event.timestamp;
545 data.lux = event.values[0];
546 mLastSensorReadings.addLast(data);
547 }
548 }
549
550 private void batteryLevelChanged(int level, int scale) {
551 synchronized (mDataCollectionLock) {
552 mLastBatteryLevel = (float) level / (float) scale;
553 }
554 }
555
556 private final class SensorListener implements SensorEventListener {
557 @Override
558 public void onSensorChanged(SensorEvent event) {
559 recordSensorEvent(event);
560 }
561
562 @Override
563 public void onAccuracyChanged(Sensor sensor, int accuracy) {
564
565 }
566 }
567
568 private final class SettingsObserver extends ContentObserver {
569 public SettingsObserver(Handler handler) {
570 super(handler);
571 }
572
573 @Override
574 public void onChange(boolean selfChange, Uri uri) {
575 if (DEBUG) {
576 Slog.v(TAG, "settings change " + uri);
577 }
578 // Self change is based on observer passed to notifyObserver, SettingsProvider
579 // passes null so no changes are self changes.
580 handleBrightnessChanged();
581 }
582 }
583
584 private final class Receiver extends BroadcastReceiver {
585 @Override
586 public void onReceive(Context context, Intent intent) {
587 if (DEBUG) {
588 Slog.d(TAG, "Received " + intent.getAction());
589 }
590 String action = intent.getAction();
591 if (Intent.ACTION_SHUTDOWN.equals(action)) {
592 stop();
593 scheduleWriteEvents();
594 } else if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
595 int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
596 int scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, 0);
597 if (level != -1 && scale != 0) {
598 batteryLevelChanged(level, scale);
599 }
Kenny Guy689ab8f2017-11-29 12:12:06 +0000600 } else if (Intent.ACTION_SCREEN_OFF.equals(action)) {
601 mInjector.unregisterSensorListener(mContext, mSensorListener);
602 } else if (Intent.ACTION_SCREEN_ON.equals(action)) {
603 mInjector.registerSensorListener(mContext, mSensorListener,
604 mInjector.getBackgroundHandler());
Kenny Guy22bd0442017-10-26 00:15:54 +0100605 }
606 }
607 }
608
609 @VisibleForTesting
610 static class Injector {
611 public void registerSensorListener(Context context,
Kenny Guy689ab8f2017-11-29 12:12:06 +0000612 SensorEventListener sensorListener, Handler handler) {
Kenny Guy22bd0442017-10-26 00:15:54 +0100613 SensorManager sensorManager = context.getSystemService(SensorManager.class);
614 Sensor lightSensor = sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
615 sensorManager.registerListener(sensorListener,
Kenny Guy689ab8f2017-11-29 12:12:06 +0000616 lightSensor, SensorManager.SENSOR_DELAY_NORMAL, handler);
Kenny Guy22bd0442017-10-26 00:15:54 +0100617 }
618
619 public void unregisterSensorListener(Context context, SensorEventListener sensorListener) {
620 SensorManager sensorManager = context.getSystemService(SensorManager.class);
621 sensorManager.unregisterListener(sensorListener);
622 }
623
624 public void registerBrightnessObserver(ContentResolver resolver,
625 ContentObserver settingsObserver) {
626 resolver.registerContentObserver(Settings.System.getUriFor(
627 Settings.System.SCREEN_BRIGHTNESS),
628 false, settingsObserver, UserHandle.USER_ALL);
629 }
630
631 public void unregisterBrightnessObserver(Context context,
632 ContentObserver settingsObserver) {
633 context.getContentResolver().unregisterContentObserver(settingsObserver);
634 }
635
636 public void registerReceiver(Context context,
637 BroadcastReceiver receiver, IntentFilter filter) {
638 context.registerReceiver(receiver, filter);
639 }
640
641 public void unregisterReceiver(Context context,
642 BroadcastReceiver receiver) {
643 context.unregisterReceiver(receiver);
644 }
645
646 public Handler getBackgroundHandler() {
647 return BackgroundThread.getHandler();
648 }
649
650 public int getSystemIntForUser(ContentResolver resolver, String setting, int defaultValue,
651 int userId) {
652 return Settings.System.getIntForUser(resolver, setting, defaultValue, userId);
653 }
654
655 public void putSystemIntForUser(ContentResolver resolver, String setting, int value,
656 int userId) {
657 Settings.System.putIntForUser(resolver, setting, value, userId);
658 }
659
660 public int getSecureIntForUser(ContentResolver resolver, String setting, int defaultValue,
661 int userId) {
662 return Settings.Secure.getIntForUser(resolver, setting, defaultValue, userId);
663 }
664
665 public AtomicFile getFile() {
666 return new AtomicFile(new File(Environment.getDataSystemDeDirectory(), EVENTS_FILE));
667 }
668
669 public long currentTimeMillis() {
670 return System.currentTimeMillis();
671 }
672
673 public long elapsedRealtimeNanos() {
674 return SystemClock.elapsedRealtimeNanos();
675 }
676
677 public int getUserSerialNumber(UserManager userManager, int userId) {
678 return userManager.getUserSerialNumber(userId);
679 }
680
681 public int getUserId(UserManager userManager, int userSerialNumber) {
682 return userManager.getUserHandle(userSerialNumber);
683 }
684
685 public ActivityManager.StackInfo getFocusedStack() throws RemoteException {
686 return ActivityManager.getService().getFocusedStackInfo();
687 }
Kenny Guycfe7b702017-11-14 21:04:58 +0000688
689 public void scheduleIdleJob(Context context) {
690 BrightnessIdleJob.scheduleJob(context);
691 }
692
693 public void cancelIdleJob(Context context) {
694 BrightnessIdleJob.cancelJob(context);
695 }
Kenny Guy689ab8f2017-11-29 12:12:06 +0000696
697 public boolean isInteractive(Context context) {
698 return context.getSystemService(PowerManager.class).isInteractive();
699 }
Kenny Guy22bd0442017-10-26 00:15:54 +0100700 }
701}