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