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