1 /* 2 * Copyright 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.display; 18 19 import android.annotation.Nullable; 20 import android.annotation.UserIdInt; 21 import android.app.ActivityManager; 22 import android.content.BroadcastReceiver; 23 import android.content.ContentResolver; 24 import android.content.Context; 25 import android.content.Intent; 26 import android.content.IntentFilter; 27 import android.content.pm.ParceledListSlice; 28 import android.database.ContentObserver; 29 import android.hardware.Sensor; 30 import android.hardware.SensorEvent; 31 import android.hardware.SensorEventListener; 32 import android.hardware.SensorManager; 33 import android.hardware.display.AmbientBrightnessDayStats; 34 import android.hardware.display.BrightnessChangeEvent; 35 import android.net.Uri; 36 import android.os.BatteryManager; 37 import android.os.Environment; 38 import android.os.Handler; 39 import android.os.Looper; 40 import android.os.Message; 41 import android.os.PowerManager; 42 import android.os.RemoteException; 43 import android.os.SystemClock; 44 import android.os.UserHandle; 45 import android.os.UserManager; 46 import android.provider.Settings; 47 import android.util.AtomicFile; 48 import android.util.Slog; 49 import android.util.Xml; 50 51 import com.android.internal.annotations.GuardedBy; 52 import com.android.internal.annotations.VisibleForTesting; 53 import com.android.internal.os.BackgroundThread; 54 import com.android.internal.util.FastXmlSerializer; 55 import com.android.internal.util.RingBuffer; 56 57 import libcore.io.IoUtils; 58 59 import org.xmlpull.v1.XmlPullParser; 60 import org.xmlpull.v1.XmlPullParserException; 61 import org.xmlpull.v1.XmlSerializer; 62 63 import java.io.File; 64 import java.io.FileInputStream; 65 import java.io.FileOutputStream; 66 import java.io.IOException; 67 import java.io.InputStream; 68 import java.io.OutputStream; 69 import java.io.PrintWriter; 70 import java.nio.charset.StandardCharsets; 71 import java.text.SimpleDateFormat; 72 import java.util.ArrayDeque; 73 import java.util.ArrayList; 74 import java.util.Collections; 75 import java.util.Date; 76 import java.util.Deque; 77 import java.util.HashMap; 78 import java.util.Map; 79 import java.util.concurrent.TimeUnit; 80 81 /** 82 * Class that tracks recent brightness settings changes and stores 83 * associated information such as light sensor readings. 84 */ 85 public class BrightnessTracker { 86 87 static final String TAG = "BrightnessTracker"; 88 static final boolean DEBUG = false; 89 90 private static final String EVENTS_FILE = "brightness_events.xml"; 91 private static final String AMBIENT_BRIGHTNESS_STATS_FILE = "ambient_brightness_stats.xml"; 92 private static final int MAX_EVENTS = 100; 93 // Discard events when reading or writing that are older than this. 94 private static final long MAX_EVENT_AGE = TimeUnit.DAYS.toMillis(30); 95 // Time over which we keep lux sensor readings. 96 private static final long LUX_EVENT_HORIZON = TimeUnit.SECONDS.toNanos(10); 97 98 private static final String TAG_EVENTS = "events"; 99 private static final String TAG_EVENT = "event"; 100 private static final String ATTR_NITS = "nits"; 101 private static final String ATTR_TIMESTAMP = "timestamp"; 102 private static final String ATTR_PACKAGE_NAME = "packageName"; 103 private static final String ATTR_USER = "user"; 104 private static final String ATTR_LUX = "lux"; 105 private static final String ATTR_LUX_TIMESTAMPS = "luxTimestamps"; 106 private static final String ATTR_BATTERY_LEVEL = "batteryLevel"; 107 private static final String ATTR_NIGHT_MODE = "nightMode"; 108 private static final String ATTR_COLOR_TEMPERATURE = "colorTemperature"; 109 private static final String ATTR_LAST_NITS = "lastNits"; 110 private static final String ATTR_DEFAULT_CONFIG = "defaultConfig"; 111 private static final String ATTR_POWER_SAVE = "powerSaveFactor"; 112 private static final String ATTR_USER_POINT = "userPoint"; 113 114 private static final int MSG_BACKGROUND_START = 0; 115 private static final int MSG_BRIGHTNESS_CHANGED = 1; 116 private static final int MSG_STOP_SENSOR_LISTENER = 2; 117 private static final int MSG_START_SENSOR_LISTENER = 3; 118 119 private static final SimpleDateFormat FORMAT = new SimpleDateFormat("MM-dd HH:mm:ss.SSS"); 120 121 // Lock held while accessing mEvents, is held while writing events to flash. 122 private final Object mEventsLock = new Object(); 123 @GuardedBy("mEventsLock") 124 private RingBuffer<BrightnessChangeEvent> mEvents 125 = new RingBuffer<>(BrightnessChangeEvent.class, MAX_EVENTS); 126 @GuardedBy("mEventsLock") 127 private boolean mEventsDirty; 128 129 private volatile boolean mWriteBrightnessTrackerStateScheduled; 130 131 private AmbientBrightnessStatsTracker mAmbientBrightnessStatsTracker; 132 133 private final UserManager mUserManager; 134 private final Context mContext; 135 private final ContentResolver mContentResolver; 136 private final Handler mBgHandler; 137 138 // mBroadcastReceiver, mSensorListener, mSettingsObserver and mSensorRegistered 139 // should only be used on the mBgHandler thread. 140 private BroadcastReceiver mBroadcastReceiver; 141 private SensorListener mSensorListener; 142 private SettingsObserver mSettingsObserver; 143 private boolean mSensorRegistered; 144 145 private @UserIdInt int mCurrentUserId = UserHandle.USER_NULL; 146 147 // Lock held while collecting data related to brightness changes. 148 private final Object mDataCollectionLock = new Object(); 149 @GuardedBy("mDataCollectionLock") 150 private Deque<LightData> mLastSensorReadings = new ArrayDeque<>(); 151 @GuardedBy("mDataCollectionLock") 152 private float mLastBatteryLevel = Float.NaN; 153 @GuardedBy("mDataCollectionLock") 154 private float mLastBrightness = -1; 155 @GuardedBy("mDataCollectionLock") 156 private boolean mStarted; 157 158 private final Injector mInjector; 159 160 public BrightnessTracker(Context context, @Nullable Injector injector) { 161 // Note this will be called very early in boot, other system 162 // services may not be present. 163 mContext = context; 164 mContentResolver = context.getContentResolver(); 165 if (injector != null) { 166 mInjector = injector; 167 } else { 168 mInjector = new Injector(); 169 } 170 mBgHandler = new TrackerHandler(mInjector.getBackgroundHandler().getLooper()); 171 mUserManager = mContext.getSystemService(UserManager.class); 172 } 173 174 /** 175 * Start listening for brightness slider events 176 * 177 * @param initialBrightness the initial screen brightness 178 */ 179 public void start(float initialBrightness) { 180 if (DEBUG) { 181 Slog.d(TAG, "Start"); 182 } 183 mCurrentUserId = ActivityManager.getCurrentUser(); 184 mBgHandler.obtainMessage(MSG_BACKGROUND_START, (Float) initialBrightness).sendToTarget(); 185 } 186 187 private void backgroundStart(float initialBrightness) { 188 readEvents(); 189 readAmbientBrightnessStats(); 190 191 mSensorListener = new SensorListener(); 192 193 mSettingsObserver = new SettingsObserver(mBgHandler); 194 mInjector.registerBrightnessModeObserver(mContentResolver, mSettingsObserver); 195 startSensorListener(); 196 197 final IntentFilter intentFilter = new IntentFilter(); 198 intentFilter.addAction(Intent.ACTION_SHUTDOWN); 199 intentFilter.addAction(Intent.ACTION_BATTERY_CHANGED); 200 intentFilter.addAction(Intent.ACTION_SCREEN_ON); 201 intentFilter.addAction(Intent.ACTION_SCREEN_OFF); 202 mBroadcastReceiver = new Receiver(); 203 mInjector.registerReceiver(mContext, mBroadcastReceiver, intentFilter); 204 205 mInjector.scheduleIdleJob(mContext); 206 synchronized (mDataCollectionLock) { 207 mLastBrightness = initialBrightness; 208 mStarted = true; 209 } 210 } 211 212 /** Stop listening for events */ 213 @VisibleForTesting 214 void stop() { 215 if (DEBUG) { 216 Slog.d(TAG, "Stop"); 217 } 218 mBgHandler.removeMessages(MSG_BACKGROUND_START); 219 stopSensorListener(); 220 mInjector.unregisterSensorListener(mContext, mSensorListener); 221 mInjector.unregisterBrightnessModeObserver(mContext, mSettingsObserver); 222 mInjector.unregisterReceiver(mContext, mBroadcastReceiver); 223 mInjector.cancelIdleJob(mContext); 224 225 synchronized (mDataCollectionLock) { 226 mStarted = false; 227 } 228 } 229 230 public void onSwitchUser(@UserIdInt int newUserId) { 231 if (DEBUG) { 232 Slog.d(TAG, "Used id updated from " + mCurrentUserId + " to " + newUserId); 233 } 234 mCurrentUserId = newUserId; 235 } 236 237 /** 238 * @param userId userId to fetch data for. 239 * @param includePackage if false we will null out BrightnessChangeEvent.packageName 240 * @return List of recent {@link BrightnessChangeEvent}s 241 */ 242 public ParceledListSlice<BrightnessChangeEvent> getEvents(int userId, boolean includePackage) { 243 BrightnessChangeEvent[] events; 244 synchronized (mEventsLock) { 245 events = mEvents.toArray(); 246 } 247 int[] profiles = mInjector.getProfileIds(mUserManager, userId); 248 Map<Integer, Boolean> toRedact = new HashMap<>(); 249 for (int i = 0; i < profiles.length; ++i) { 250 int profileId = profiles[i]; 251 // Include slider interactions when a managed profile app is in the 252 // foreground but always redact the package name. 253 boolean redact = (!includePackage) || profileId != userId; 254 toRedact.put(profiles[i], redact); 255 } 256 ArrayList<BrightnessChangeEvent> out = new ArrayList<>(events.length); 257 for (int i = 0; i < events.length; ++i) { 258 Boolean redact = toRedact.get(events[i].userId); 259 if (redact != null) { 260 if (!redact) { 261 out.add(events[i]); 262 } else { 263 BrightnessChangeEvent event = new BrightnessChangeEvent((events[i]), 264 /* redactPackage */ true); 265 out.add(event); 266 } 267 } 268 } 269 return new ParceledListSlice<>(out); 270 } 271 272 public void persistBrightnessTrackerState() { 273 scheduleWriteBrightnessTrackerState(); 274 } 275 276 /** 277 * Notify the BrightnessTracker that the user has changed the brightness of the display. 278 */ 279 public void notifyBrightnessChanged(float brightness, boolean userInitiated, 280 float powerBrightnessFactor, boolean isUserSetBrightness, 281 boolean isDefaultBrightnessConfig) { 282 if (DEBUG) { 283 Slog.d(TAG, String.format("notifyBrightnessChanged(brightness=%f, userInitiated=%b)", 284 brightness, userInitiated)); 285 } 286 Message m = mBgHandler.obtainMessage(MSG_BRIGHTNESS_CHANGED, 287 userInitiated ? 1 : 0, 0 /*unused*/, new BrightnessChangeValues(brightness, 288 powerBrightnessFactor, isUserSetBrightness, isDefaultBrightnessConfig, 289 mInjector.currentTimeMillis())); 290 m.sendToTarget(); 291 } 292 293 private void handleBrightnessChanged(float brightness, boolean userInitiated, 294 float powerBrightnessFactor, boolean isUserSetBrightness, 295 boolean isDefaultBrightnessConfig, long timestamp) { 296 BrightnessChangeEvent.Builder builder; 297 298 synchronized (mDataCollectionLock) { 299 if (!mStarted) { 300 // Not currently gathering brightness change information 301 return; 302 } 303 304 float previousBrightness = mLastBrightness; 305 mLastBrightness = brightness; 306 307 if (!userInitiated) { 308 // We want to record what current brightness is so that we know what the user 309 // changed it from, but if it wasn't user initiated then we don't want to record it 310 // as a BrightnessChangeEvent. 311 return; 312 } 313 314 builder = new BrightnessChangeEvent.Builder(); 315 builder.setBrightness(brightness); 316 builder.setTimeStamp(timestamp); 317 builder.setPowerBrightnessFactor(powerBrightnessFactor); 318 builder.setUserBrightnessPoint(isUserSetBrightness); 319 builder.setIsDefaultBrightnessConfig(isDefaultBrightnessConfig); 320 321 final int readingCount = mLastSensorReadings.size(); 322 if (readingCount == 0) { 323 // No sensor data so ignore this. 324 return; 325 } 326 327 float[] luxValues = new float[readingCount]; 328 long[] luxTimestamps = new long[readingCount]; 329 330 int pos = 0; 331 332 // Convert sensor timestamp in elapsed time nanos to current time millis. 333 long currentTimeMillis = mInjector.currentTimeMillis(); 334 long elapsedTimeNanos = mInjector.elapsedRealtimeNanos(); 335 for (LightData reading : mLastSensorReadings) { 336 luxValues[pos] = reading.lux; 337 luxTimestamps[pos] = currentTimeMillis - 338 TimeUnit.NANOSECONDS.toMillis(elapsedTimeNanos - reading.timestamp); 339 ++pos; 340 } 341 builder.setLuxValues(luxValues); 342 builder.setLuxTimestamps(luxTimestamps); 343 344 builder.setBatteryLevel(mLastBatteryLevel); 345 builder.setLastBrightness(previousBrightness); 346 } 347 348 try { 349 final ActivityManager.StackInfo focusedStack = mInjector.getFocusedStack(); 350 if (focusedStack != null && focusedStack.topActivity != null) { 351 builder.setUserId(focusedStack.userId); 352 builder.setPackageName(focusedStack.topActivity.getPackageName()); 353 } else { 354 // Ignore the event because we can't determine user / package. 355 if (DEBUG) { 356 Slog.d(TAG, "Ignoring event due to null focusedStack."); 357 } 358 return; 359 } 360 } catch (RemoteException e) { 361 // Really shouldn't be possible. 362 return; 363 } 364 365 builder.setNightMode(mInjector.getSecureIntForUser(mContentResolver, 366 Settings.Secure.NIGHT_DISPLAY_ACTIVATED, 0, UserHandle.USER_CURRENT) 367 == 1); 368 builder.setColorTemperature(mInjector.getSecureIntForUser(mContentResolver, 369 Settings.Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE, 370 0, UserHandle.USER_CURRENT)); 371 372 BrightnessChangeEvent event = builder.build(); 373 if (DEBUG) { 374 Slog.d(TAG, "Event " + event.brightness + " " + event.packageName); 375 } 376 synchronized (mEventsLock) { 377 mEventsDirty = true; 378 mEvents.append(event); 379 } 380 } 381 382 private void startSensorListener() { 383 if (!mSensorRegistered 384 && mInjector.isInteractive(mContext) 385 && mInjector.isBrightnessModeAutomatic(mContentResolver)) { 386 mAmbientBrightnessStatsTracker.start(); 387 mSensorRegistered = true; 388 mInjector.registerSensorListener(mContext, mSensorListener, 389 mInjector.getBackgroundHandler()); 390 } 391 } 392 393 private void stopSensorListener() { 394 if (mSensorRegistered) { 395 mAmbientBrightnessStatsTracker.stop(); 396 mInjector.unregisterSensorListener(mContext, mSensorListener); 397 mSensorRegistered = false; 398 } 399 } 400 401 private void scheduleWriteBrightnessTrackerState() { 402 if (!mWriteBrightnessTrackerStateScheduled) { 403 mBgHandler.post(() -> { 404 mWriteBrightnessTrackerStateScheduled = false; 405 writeEvents(); 406 writeAmbientBrightnessStats(); 407 }); 408 mWriteBrightnessTrackerStateScheduled = true; 409 } 410 } 411 412 private void writeEvents() { 413 synchronized (mEventsLock) { 414 if (!mEventsDirty) { 415 // Nothing to write 416 return; 417 } 418 419 final AtomicFile writeTo = mInjector.getFile(EVENTS_FILE); 420 if (writeTo == null) { 421 return; 422 } 423 if (mEvents.isEmpty()) { 424 if (writeTo.exists()) { 425 writeTo.delete(); 426 } 427 mEventsDirty = false; 428 } else { 429 FileOutputStream output = null; 430 try { 431 output = writeTo.startWrite(); 432 writeEventsLocked(output); 433 writeTo.finishWrite(output); 434 mEventsDirty = false; 435 } catch (IOException e) { 436 writeTo.failWrite(output); 437 Slog.e(TAG, "Failed to write change mEvents.", e); 438 } 439 } 440 } 441 } 442 443 private void writeAmbientBrightnessStats() { 444 final AtomicFile writeTo = mInjector.getFile(AMBIENT_BRIGHTNESS_STATS_FILE); 445 if (writeTo == null) { 446 return; 447 } 448 FileOutputStream output = null; 449 try { 450 output = writeTo.startWrite(); 451 mAmbientBrightnessStatsTracker.writeStats(output); 452 writeTo.finishWrite(output); 453 } catch (IOException e) { 454 writeTo.failWrite(output); 455 Slog.e(TAG, "Failed to write ambient brightness stats.", e); 456 } 457 } 458 459 private void readEvents() { 460 synchronized (mEventsLock) { 461 // Read might prune events so mark as dirty. 462 mEventsDirty = true; 463 mEvents.clear(); 464 final AtomicFile readFrom = mInjector.getFile(EVENTS_FILE); 465 if (readFrom != null && readFrom.exists()) { 466 FileInputStream input = null; 467 try { 468 input = readFrom.openRead(); 469 readEventsLocked(input); 470 } catch (IOException e) { 471 readFrom.delete(); 472 Slog.e(TAG, "Failed to read change mEvents.", e); 473 } finally { 474 IoUtils.closeQuietly(input); 475 } 476 } 477 } 478 } 479 480 private void readAmbientBrightnessStats() { 481 mAmbientBrightnessStatsTracker = new AmbientBrightnessStatsTracker(mUserManager, null); 482 final AtomicFile readFrom = mInjector.getFile(AMBIENT_BRIGHTNESS_STATS_FILE); 483 if (readFrom != null && readFrom.exists()) { 484 FileInputStream input = null; 485 try { 486 input = readFrom.openRead(); 487 mAmbientBrightnessStatsTracker.readStats(input); 488 } catch (IOException e) { 489 readFrom.delete(); 490 Slog.e(TAG, "Failed to read ambient brightness stats.", e); 491 } finally { 492 IoUtils.closeQuietly(input); 493 } 494 } 495 } 496 497 @VisibleForTesting 498 @GuardedBy("mEventsLock") 499 void writeEventsLocked(OutputStream stream) throws IOException { 500 XmlSerializer out = new FastXmlSerializer(); 501 out.setOutput(stream, StandardCharsets.UTF_8.name()); 502 out.startDocument(null, true); 503 out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); 504 505 out.startTag(null, TAG_EVENTS); 506 BrightnessChangeEvent[] toWrite = mEvents.toArray(); 507 // Clear events, code below will add back the ones that are still within the time window. 508 mEvents.clear(); 509 if (DEBUG) { 510 Slog.d(TAG, "Writing events " + toWrite.length); 511 } 512 final long timeCutOff = mInjector.currentTimeMillis() - MAX_EVENT_AGE; 513 for (int i = 0; i < toWrite.length; ++i) { 514 int userSerialNo = mInjector.getUserSerialNumber(mUserManager, toWrite[i].userId); 515 if (userSerialNo != -1 && toWrite[i].timeStamp > timeCutOff) { 516 mEvents.append(toWrite[i]); 517 out.startTag(null, TAG_EVENT); 518 out.attribute(null, ATTR_NITS, Float.toString(toWrite[i].brightness)); 519 out.attribute(null, ATTR_TIMESTAMP, Long.toString(toWrite[i].timeStamp)); 520 out.attribute(null, ATTR_PACKAGE_NAME, toWrite[i].packageName); 521 out.attribute(null, ATTR_USER, Integer.toString(userSerialNo)); 522 out.attribute(null, ATTR_BATTERY_LEVEL, Float.toString(toWrite[i].batteryLevel)); 523 out.attribute(null, ATTR_NIGHT_MODE, Boolean.toString(toWrite[i].nightMode)); 524 out.attribute(null, ATTR_COLOR_TEMPERATURE, Integer.toString( 525 toWrite[i].colorTemperature)); 526 out.attribute(null, ATTR_LAST_NITS, 527 Float.toString(toWrite[i].lastBrightness)); 528 out.attribute(null, ATTR_DEFAULT_CONFIG, 529 Boolean.toString(toWrite[i].isDefaultBrightnessConfig)); 530 out.attribute(null, ATTR_POWER_SAVE, 531 Float.toString(toWrite[i].powerBrightnessFactor)); 532 out.attribute(null, ATTR_USER_POINT, 533 Boolean.toString(toWrite[i].isUserSetBrightness)); 534 StringBuilder luxValues = new StringBuilder(); 535 StringBuilder luxTimestamps = new StringBuilder(); 536 for (int j = 0; j < toWrite[i].luxValues.length; ++j) { 537 if (j > 0) { 538 luxValues.append(','); 539 luxTimestamps.append(','); 540 } 541 luxValues.append(Float.toString(toWrite[i].luxValues[j])); 542 luxTimestamps.append(Long.toString(toWrite[i].luxTimestamps[j])); 543 } 544 out.attribute(null, ATTR_LUX, luxValues.toString()); 545 out.attribute(null, ATTR_LUX_TIMESTAMPS, luxTimestamps.toString()); 546 out.endTag(null, TAG_EVENT); 547 } 548 } 549 out.endTag(null, TAG_EVENTS); 550 out.endDocument(); 551 stream.flush(); 552 } 553 554 @VisibleForTesting 555 @GuardedBy("mEventsLock") 556 void readEventsLocked(InputStream stream) throws IOException { 557 try { 558 XmlPullParser parser = Xml.newPullParser(); 559 parser.setInput(stream, StandardCharsets.UTF_8.name()); 560 561 int type; 562 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT 563 && type != XmlPullParser.START_TAG) { 564 } 565 String tag = parser.getName(); 566 if (!TAG_EVENTS.equals(tag)) { 567 throw new XmlPullParserException( 568 "Events not found in brightness tracker file " + tag); 569 } 570 571 final long timeCutOff = mInjector.currentTimeMillis() - MAX_EVENT_AGE; 572 573 parser.next(); 574 int outerDepth = parser.getDepth(); 575 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT 576 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { 577 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { 578 continue; 579 } 580 tag = parser.getName(); 581 if (TAG_EVENT.equals(tag)) { 582 BrightnessChangeEvent.Builder builder = new BrightnessChangeEvent.Builder(); 583 584 String brightness = parser.getAttributeValue(null, ATTR_NITS); 585 builder.setBrightness(Float.parseFloat(brightness)); 586 String timestamp = parser.getAttributeValue(null, ATTR_TIMESTAMP); 587 builder.setTimeStamp(Long.parseLong(timestamp)); 588 builder.setPackageName(parser.getAttributeValue(null, ATTR_PACKAGE_NAME)); 589 String user = parser.getAttributeValue(null, ATTR_USER); 590 builder.setUserId(mInjector.getUserId(mUserManager, Integer.parseInt(user))); 591 String batteryLevel = parser.getAttributeValue(null, ATTR_BATTERY_LEVEL); 592 builder.setBatteryLevel(Float.parseFloat(batteryLevel)); 593 String nightMode = parser.getAttributeValue(null, ATTR_NIGHT_MODE); 594 builder.setNightMode(Boolean.parseBoolean(nightMode)); 595 String colorTemperature = 596 parser.getAttributeValue(null, ATTR_COLOR_TEMPERATURE); 597 builder.setColorTemperature(Integer.parseInt(colorTemperature)); 598 String lastBrightness = parser.getAttributeValue(null, ATTR_LAST_NITS); 599 builder.setLastBrightness(Float.parseFloat(lastBrightness)); 600 601 String luxValue = parser.getAttributeValue(null, ATTR_LUX); 602 String luxTimestamp = parser.getAttributeValue(null, ATTR_LUX_TIMESTAMPS); 603 604 String[] luxValuesStrings = luxValue.split(","); 605 String[] luxTimestampsStrings = luxTimestamp.split(","); 606 if (luxValuesStrings.length != luxTimestampsStrings.length) { 607 continue; 608 } 609 float[] luxValues = new float[luxValuesStrings.length]; 610 long[] luxTimestamps = new long[luxValuesStrings.length]; 611 for (int i = 0; i < luxValues.length; ++i) { 612 luxValues[i] = Float.parseFloat(luxValuesStrings[i]); 613 luxTimestamps[i] = Long.parseLong(luxTimestampsStrings[i]); 614 } 615 builder.setLuxValues(luxValues); 616 builder.setLuxTimestamps(luxTimestamps); 617 618 String defaultConfig = parser.getAttributeValue(null, ATTR_DEFAULT_CONFIG); 619 if (defaultConfig != null) { 620 builder.setIsDefaultBrightnessConfig(Boolean.parseBoolean(defaultConfig)); 621 } 622 String powerSave = parser.getAttributeValue(null, ATTR_POWER_SAVE); 623 if (powerSave != null) { 624 builder.setPowerBrightnessFactor(Float.parseFloat(powerSave)); 625 } else { 626 builder.setPowerBrightnessFactor(1.0f); 627 } 628 String userPoint = parser.getAttributeValue(null, ATTR_USER_POINT); 629 if (userPoint != null) { 630 builder.setUserBrightnessPoint(Boolean.parseBoolean(userPoint)); 631 } 632 633 BrightnessChangeEvent event = builder.build(); 634 if (DEBUG) { 635 Slog.i(TAG, "Read event " + event.brightness 636 + " " + event.packageName); 637 } 638 639 if (event.userId != -1 && event.timeStamp > timeCutOff 640 && event.luxValues.length > 0) { 641 mEvents.append(event); 642 } 643 } 644 } 645 } catch (NullPointerException | NumberFormatException | XmlPullParserException 646 | IOException e) { 647 // Failed to parse something, just start with an empty event log. 648 mEvents = new RingBuffer<>(BrightnessChangeEvent.class, MAX_EVENTS); 649 Slog.e(TAG, "Failed to parse brightness event", e); 650 // Re-throw so we will delete the bad file. 651 throw new IOException("failed to parse file", e); 652 } 653 } 654 655 public void dump(final PrintWriter pw) { 656 pw.println("BrightnessTracker state:"); 657 synchronized (mDataCollectionLock) { 658 pw.println(" mStarted=" + mStarted); 659 pw.println(" mLastBatteryLevel=" + mLastBatteryLevel); 660 pw.println(" mLastBrightness=" + mLastBrightness); 661 pw.println(" mLastSensorReadings.size=" + mLastSensorReadings.size()); 662 if (!mLastSensorReadings.isEmpty()) { 663 pw.println(" mLastSensorReadings time span " 664 + mLastSensorReadings.peekFirst().timestamp + "->" 665 + mLastSensorReadings.peekLast().timestamp); 666 } 667 } 668 synchronized (mEventsLock) { 669 pw.println(" mEventsDirty=" + mEventsDirty); 670 pw.println(" mEvents.size=" + mEvents.size()); 671 BrightnessChangeEvent[] events = mEvents.toArray(); 672 for (int i = 0; i < events.length; ++i) { 673 pw.print(" " + FORMAT.format(new Date(events[i].timeStamp))); 674 pw.print(", userId=" + events[i].userId); 675 pw.print(", " + events[i].lastBrightness + "->" + events[i].brightness); 676 pw.print(", isUserSetBrightness=" + events[i].isUserSetBrightness); 677 pw.print(", powerBrightnessFactor=" + events[i].powerBrightnessFactor); 678 pw.print(", isDefaultBrightnessConfig=" + events[i].isDefaultBrightnessConfig); 679 pw.print(" {"); 680 for (int j = 0; j < events[i].luxValues.length; ++j){ 681 if (j != 0) { 682 pw.print(", "); 683 } 684 pw.print("(" + events[i].luxValues[j] + "," + events[i].luxTimestamps[j] + ")"); 685 } 686 pw.println("}"); 687 } 688 } 689 pw.println(" mWriteBrightnessTrackerStateScheduled=" 690 + mWriteBrightnessTrackerStateScheduled); 691 mBgHandler.runWithScissors(() -> dumpLocal(pw), 1000); 692 if (mAmbientBrightnessStatsTracker != null) { 693 pw.println(); 694 mAmbientBrightnessStatsTracker.dump(pw); 695 } 696 } 697 698 private void dumpLocal(PrintWriter pw) { 699 pw.println(" mSensorRegistered=" + mSensorRegistered); 700 } 701 702 public ParceledListSlice<AmbientBrightnessDayStats> getAmbientBrightnessStats(int userId) { 703 if (mAmbientBrightnessStatsTracker != null) { 704 ArrayList<AmbientBrightnessDayStats> stats = 705 mAmbientBrightnessStatsTracker.getUserStats(userId); 706 if (stats != null) { 707 return new ParceledListSlice<>(stats); 708 } 709 } 710 return ParceledListSlice.emptyList(); 711 } 712 713 // Not allowed to keep the SensorEvent so used to copy the data we care about. 714 private static class LightData { 715 public float lux; 716 // Time in elapsedRealtimeNanos 717 public long timestamp; 718 } 719 720 private void recordSensorEvent(SensorEvent event) { 721 long horizon = mInjector.elapsedRealtimeNanos() - LUX_EVENT_HORIZON; 722 synchronized (mDataCollectionLock) { 723 if (DEBUG) { 724 Slog.v(TAG, "Sensor event " + event); 725 } 726 if (!mLastSensorReadings.isEmpty() 727 && event.timestamp < mLastSensorReadings.getLast().timestamp) { 728 // Ignore event that came out of order. 729 return; 730 } 731 LightData data = null; 732 while (!mLastSensorReadings.isEmpty() 733 && mLastSensorReadings.getFirst().timestamp < horizon) { 734 // Remove data that has fallen out of the window. 735 data = mLastSensorReadings.removeFirst(); 736 } 737 // We put back the last one we removed so we know how long 738 // the first sensor reading was valid for. 739 if (data != null) { 740 mLastSensorReadings.addFirst(data); 741 } 742 743 data = new LightData(); 744 data.timestamp = event.timestamp; 745 data.lux = event.values[0]; 746 mLastSensorReadings.addLast(data); 747 } 748 } 749 750 private void recordAmbientBrightnessStats(SensorEvent event) { 751 mAmbientBrightnessStatsTracker.add(mCurrentUserId, event.values[0]); 752 } 753 754 private void batteryLevelChanged(int level, int scale) { 755 synchronized (mDataCollectionLock) { 756 mLastBatteryLevel = (float) level / (float) scale; 757 } 758 } 759 760 private final class SensorListener implements SensorEventListener { 761 @Override 762 public void onSensorChanged(SensorEvent event) { 763 recordSensorEvent(event); 764 recordAmbientBrightnessStats(event); 765 } 766 767 @Override 768 public void onAccuracyChanged(Sensor sensor, int accuracy) { 769 770 } 771 } 772 773 private final class SettingsObserver extends ContentObserver { 774 public SettingsObserver(Handler handler) { 775 super(handler); 776 } 777 778 @Override 779 public void onChange(boolean selfChange, Uri uri) { 780 if (DEBUG) { 781 Slog.v(TAG, "settings change " + uri); 782 } 783 if (mInjector.isBrightnessModeAutomatic(mContentResolver)) { 784 mBgHandler.obtainMessage(MSG_START_SENSOR_LISTENER).sendToTarget(); 785 } else { 786 mBgHandler.obtainMessage(MSG_STOP_SENSOR_LISTENER).sendToTarget(); 787 } 788 } 789 } 790 791 private final class Receiver extends BroadcastReceiver { 792 @Override 793 public void onReceive(Context context, Intent intent) { 794 if (DEBUG) { 795 Slog.d(TAG, "Received " + intent.getAction()); 796 } 797 String action = intent.getAction(); 798 if (Intent.ACTION_SHUTDOWN.equals(action)) { 799 stop(); 800 scheduleWriteBrightnessTrackerState(); 801 } else if (Intent.ACTION_BATTERY_CHANGED.equals(action)) { 802 int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1); 803 int scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, 0); 804 if (level != -1 && scale != 0) { 805 batteryLevelChanged(level, scale); 806 } 807 } else if (Intent.ACTION_SCREEN_OFF.equals(action)) { 808 mBgHandler.obtainMessage(MSG_STOP_SENSOR_LISTENER).sendToTarget(); 809 } else if (Intent.ACTION_SCREEN_ON.equals(action)) { 810 mBgHandler.obtainMessage(MSG_START_SENSOR_LISTENER).sendToTarget(); 811 } 812 } 813 } 814 815 private final class TrackerHandler extends Handler { 816 public TrackerHandler(Looper looper) { 817 super(looper, null, true /*async*/); 818 } 819 public void handleMessage(Message msg) { 820 switch (msg.what) { 821 case MSG_BACKGROUND_START: 822 backgroundStart((float)msg.obj /*initial brightness*/); 823 break; 824 case MSG_BRIGHTNESS_CHANGED: 825 BrightnessChangeValues values = (BrightnessChangeValues) msg.obj; 826 boolean userInitiatedChange = (msg.arg1 == 1); 827 handleBrightnessChanged(values.brightness, userInitiatedChange, 828 values.powerBrightnessFactor, values.isUserSetBrightness, 829 values.isDefaultBrightnessConfig, values.timestamp); 830 break; 831 case MSG_START_SENSOR_LISTENER: 832 startSensorListener(); 833 break; 834 case MSG_STOP_SENSOR_LISTENER: 835 stopSensorListener(); 836 break; 837 } 838 } 839 } 840 841 private static class BrightnessChangeValues { 842 final float brightness; 843 final float powerBrightnessFactor; 844 final boolean isUserSetBrightness; 845 final boolean isDefaultBrightnessConfig; 846 final long timestamp; 847 848 BrightnessChangeValues(float brightness, float powerBrightnessFactor, 849 boolean isUserSetBrightness, boolean isDefaultBrightnessConfig, 850 long timestamp) { 851 this.brightness = brightness; 852 this.powerBrightnessFactor = powerBrightnessFactor; 853 this.isUserSetBrightness = isUserSetBrightness; 854 this.isDefaultBrightnessConfig = isDefaultBrightnessConfig; 855 this.timestamp = timestamp; 856 } 857 } 858 859 @VisibleForTesting 860 static class Injector { 861 public void registerSensorListener(Context context, 862 SensorEventListener sensorListener, Handler handler) { 863 SensorManager sensorManager = context.getSystemService(SensorManager.class); 864 Sensor lightSensor = sensorManager.getDefaultSensor(Sensor.TYPE_LIGHT); 865 sensorManager.registerListener(sensorListener, 866 lightSensor, SensorManager.SENSOR_DELAY_NORMAL, handler); 867 } 868 869 public void unregisterSensorListener(Context context, SensorEventListener sensorListener) { 870 SensorManager sensorManager = context.getSystemService(SensorManager.class); 871 sensorManager.unregisterListener(sensorListener); 872 } 873 874 public void registerBrightnessModeObserver(ContentResolver resolver, 875 ContentObserver settingsObserver) { 876 resolver.registerContentObserver(Settings.System.getUriFor( 877 Settings.System.SCREEN_BRIGHTNESS_MODE), 878 false, settingsObserver, UserHandle.USER_ALL); 879 } 880 881 public void unregisterBrightnessModeObserver(Context context, 882 ContentObserver settingsObserver) { 883 context.getContentResolver().unregisterContentObserver(settingsObserver); 884 } 885 886 public void registerReceiver(Context context, 887 BroadcastReceiver receiver, IntentFilter filter) { 888 context.registerReceiver(receiver, filter); 889 } 890 891 public void unregisterReceiver(Context context, 892 BroadcastReceiver receiver) { 893 context.unregisterReceiver(receiver); 894 } 895 896 public Handler getBackgroundHandler() { 897 return BackgroundThread.getHandler(); 898 } 899 900 public boolean isBrightnessModeAutomatic(ContentResolver resolver) { 901 return Settings.System.getIntForUser(resolver, Settings.System.SCREEN_BRIGHTNESS_MODE, 902 Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL, UserHandle.USER_CURRENT) 903 == Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC; 904 } 905 906 public int getSecureIntForUser(ContentResolver resolver, String setting, int defaultValue, 907 int userId) { 908 return Settings.Secure.getIntForUser(resolver, setting, defaultValue, userId); 909 } 910 911 public AtomicFile getFile(String filename) { 912 return new AtomicFile(new File(Environment.getDataSystemDeDirectory(), filename)); 913 } 914 915 public long currentTimeMillis() { 916 return System.currentTimeMillis(); 917 } 918 919 public long elapsedRealtimeNanos() { 920 return SystemClock.elapsedRealtimeNanos(); 921 } 922 923 public int getUserSerialNumber(UserManager userManager, int userId) { 924 return userManager.getUserSerialNumber(userId); 925 } 926 927 public int getUserId(UserManager userManager, int userSerialNumber) { 928 return userManager.getUserHandle(userSerialNumber); 929 } 930 931 public int[] getProfileIds(UserManager userManager, int userId) { 932 if (userManager != null) { 933 return userManager.getProfileIds(userId, false); 934 } else { 935 return new int[]{userId}; 936 } 937 } 938 939 public ActivityManager.StackInfo getFocusedStack() throws RemoteException { 940 return ActivityManager.getService().getFocusedStackInfo(); 941 } 942 943 public void scheduleIdleJob(Context context) { 944 BrightnessIdleJob.scheduleJob(context); 945 } 946 947 public void cancelIdleJob(Context context) { 948 BrightnessIdleJob.cancelJob(context); 949 } 950 951 public boolean isInteractive(Context context) { 952 return context.getSystemService(PowerManager.class).isInteractive(); 953 } 954 } 955 } 956