1 /* 2 * Copyright (C) 2022 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.car.power; 18 19 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DEBUGGING_CODE; 20 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DUMP_INFO; 21 22 import android.annotation.IntDef; 23 import android.car.CarOccupantZoneManager; 24 import android.car.CarOccupantZoneManager.OccupantZoneInfo; 25 import android.car.ICarOccupantZoneCallback; 26 import android.car.builtin.util.Slogf; 27 import android.car.builtin.view.DisplayHelper; 28 import android.car.settings.CarSettings; 29 import android.content.Context; 30 import android.database.ContentObserver; 31 import android.hardware.display.DisplayManager; 32 import android.net.Uri; 33 import android.os.Handler; 34 import android.os.Looper; 35 import android.os.Message; 36 import android.os.SystemClock; 37 import android.provider.Settings; 38 import android.text.TextUtils; 39 import android.util.SparseArray; 40 import android.util.SparseIntArray; 41 import android.view.Display; 42 43 import com.android.car.CarLocalServices; 44 import com.android.car.CarLog; 45 import com.android.car.CarOccupantZoneService; 46 import com.android.car.R; 47 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport; 48 import com.android.car.internal.util.IndentingPrintWriter; 49 import com.android.car.systeminterface.SystemInterface; 50 import com.android.internal.annotations.GuardedBy; 51 import com.android.internal.annotations.VisibleForTesting; 52 53 import java.lang.annotation.ElementType; 54 import java.lang.annotation.Retention; 55 import java.lang.annotation.RetentionPolicy; 56 import java.lang.annotation.Target; 57 import java.lang.ref.WeakReference; 58 import java.time.Duration; 59 import java.util.List; 60 61 final class ScreenOffHandler { 62 private static final String TAG = CarLog.tagFor(ScreenOffHandler.class); 63 64 // Minimum and maximum timeout in milliseconds when there is no user. 65 private static final int MIN_NO_USER_SCREEN_OFF_TIMEOUT_MS = 15 * 1000; // 15 seconds 66 private static final int MAX_NO_USER_SCREEN_OFF_TIMEOUT_MS = 10 * 60 * 1000; // 10 minutes 67 68 private static final String DISPLAY_POWER_MODE_SETTING = 69 CarSettings.Global.DISPLAY_POWER_MODE; 70 private static final Uri DISPLAY_POWER_MODE_URI = 71 Settings.Global.getUriFor(DISPLAY_POWER_MODE_SETTING); 72 73 // Constants for display power mode 74 /** 75 * Display power mode is unknown. After initialization, needs to be 76 * replaced with other mode as below. 77 */ 78 @VisibleForTesting 79 static final int DISPLAY_POWER_MODE_NONE = -1; 80 /** 81 * With this mode, screen keeps off. 82 * And user cannot manually turn on the display. 83 */ 84 @VisibleForTesting 85 static final int DISPLAY_POWER_MODE_OFF = 0; 86 /** 87 * With this mode, two kinds of behavior is applied. 88 * When user logged out, screen off timeout involves. 89 * When user logged in, screen keeps on. 90 * And user can manually turn off the display. 91 */ 92 @VisibleForTesting 93 static final int DISPLAY_POWER_MODE_ON = 1; 94 /** 95 * With this mode, screen keeps on. 96 * And user can manually turn off the display. 97 */ 98 @VisibleForTesting 99 static final int DISPLAY_POWER_MODE_ALWAYS_ON = 2; 100 @Retention(RetentionPolicy.SOURCE) 101 @IntDef(prefix = "DISPLAY_POWER_MODE_", value = { 102 DISPLAY_POWER_MODE_NONE, 103 DISPLAY_POWER_MODE_OFF, 104 DISPLAY_POWER_MODE_ON, 105 DISPLAY_POWER_MODE_ALWAYS_ON, 106 }) 107 @Target({ElementType.TYPE_USE}) 108 private @interface DisplayPowerMode {} 109 110 private final Context mContext; 111 private final SystemInterface mSystemInterface; 112 private final CarOccupantZoneService mOccupantZoneService; 113 private final SettingsObserver mSettingsObserver; 114 private final EventHandler mEventHandler; 115 private final ClockInterface mClock; 116 117 private final boolean mIsAutoPowerSaving; 118 private final int mNoUserScreenOffTimeoutMs; 119 private final Object mLock = new Object(); 120 @GuardedBy("mLock") 121 private final SparseArray<DisplayPowerInfo> mDisplayPowerInfos = new SparseArray<>(); 122 123 @GuardedBy("mLock") 124 private boolean mBootCompleted; 125 ScreenOffHandler(Context context, SystemInterface systemInterface, Looper looper)126 ScreenOffHandler(Context context, SystemInterface systemInterface, Looper looper) { 127 this(context, systemInterface, looper, SystemClock::uptimeMillis); 128 } 129 130 @VisibleForTesting ScreenOffHandler(Context context, SystemInterface systemInterface, Looper looper, ClockInterface clock)131 ScreenOffHandler(Context context, SystemInterface systemInterface, Looper looper, 132 ClockInterface clock) { 133 mContext = context; 134 mEventHandler = new EventHandler(looper, this); 135 mSystemInterface = systemInterface; 136 mClock = clock; 137 mSettingsObserver = new SettingsObserver(mEventHandler); 138 mOccupantZoneService = CarLocalServices.getService(CarOccupantZoneService.class); 139 mIsAutoPowerSaving = mContext.getResources().getBoolean( 140 R.bool.config_enablePassengerDisplayPowerSaving); 141 mNoUserScreenOffTimeoutMs = getNoUserScreenOffTimeout(); 142 } 143 init()144 void init() { 145 if (!mIsAutoPowerSaving) { 146 return; 147 } 148 initializeDisplayPowerInfos(); 149 initializeDefaultSettings(); 150 mOccupantZoneService.registerCallback(mOccupantZoneCallback); 151 mContext.getContentResolver().registerContentObserver( 152 DISPLAY_POWER_MODE_URI, /* notifyForDescendants= */ false, mSettingsObserver); 153 mSystemInterface.scheduleActionForBootCompleted(() -> { 154 synchronized (mLock) { 155 mBootCompleted = true; 156 updateSettingsLocked(); 157 long eventTime = mClock.uptimeMillis(); 158 for (int i = 0; i < mDisplayPowerInfos.size(); i++) { 159 int displayId = mDisplayPowerInfos.keyAt(i); 160 updateUserActivityLocked(displayId, eventTime); 161 } 162 } 163 }, Duration.ZERO); 164 } 165 handleDisplayStateChange(int displayId, boolean on)166 void handleDisplayStateChange(int displayId, boolean on) { 167 if (!mIsAutoPowerSaving) { 168 return; 169 } 170 if (on) { 171 synchronized (mLock) { 172 updateUserActivityLocked(displayId, mClock.uptimeMillis()); 173 } 174 } 175 } 176 updateUserActivity(int displayId, long eventTime)177 void updateUserActivity(int displayId, long eventTime) { 178 synchronized (mLock) { 179 updateUserActivityLocked(displayId, eventTime); 180 } 181 } 182 183 @GuardedBy("mLock") updateUserActivityLocked(int displayId, long eventTime)184 private void updateUserActivityLocked(int displayId, long eventTime) { 185 if (!mIsAutoPowerSaving) { 186 return; 187 } 188 if (eventTime > mClock.uptimeMillis()) { 189 throw new IllegalArgumentException("event time must not be in the future"); 190 } 191 DisplayPowerInfo info = mDisplayPowerInfos.get(displayId); 192 if (info == null) { 193 Slogf.w(TAG, "Display(id: %d) is not available", displayId); 194 return; 195 } 196 info.setLastUserActivityTime(eventTime); 197 updateDisplayPowerStateLocked(info); 198 } 199 200 @GuardedBy("mLock") handleSettingsChangedLocked()201 private void handleSettingsChangedLocked() { 202 updateSettingsLocked(); 203 updateAllDisplayPowerStateLocked(); 204 } 205 canTurnOnDisplay(int displayId)206 boolean canTurnOnDisplay(int displayId) { 207 if (!mIsAutoPowerSaving) { 208 return true; 209 } 210 synchronized (mLock) { 211 return canTurnOnDisplayLocked(displayId); 212 } 213 } 214 215 @GuardedBy("mLock") canTurnOnDisplayLocked(int displayId)216 private boolean canTurnOnDisplayLocked(int displayId) { 217 DisplayPowerInfo info = mDisplayPowerInfos.get(displayId); 218 if (info == null) { 219 Slogf.w(TAG, "display(%d) power info is not ready yet.", displayId); 220 return false; 221 } 222 if (info.getMode() == DISPLAY_POWER_MODE_OFF) { 223 return false; 224 } 225 return true; 226 } 227 initializeDefaultSettings()228 private void initializeDefaultSettings() { 229 String setting = Settings.Global.getString(mContext.getContentResolver(), 230 DISPLAY_POWER_MODE_SETTING); 231 if (!TextUtils.isEmpty(setting)) { 232 Slogf.d(TAG, "stored value of %s: %s", DISPLAY_POWER_MODE_SETTING, setting); 233 return; 234 } 235 // At first boot, initialize default setting value 236 StringBuilder sb = new StringBuilder(); 237 synchronized (mLock) { 238 for (int i = 0; i < mDisplayPowerInfos.size(); i++) { 239 int displayId = mDisplayPowerInfos.keyAt(i); 240 DisplayPowerInfo info = mDisplayPowerInfos.valueAt(i); 241 if (info == null) { 242 continue; 243 } 244 int displayPort = getDisplayPort(displayId); 245 if (displayPort == DisplayHelper.INVALID_PORT) { 246 continue; 247 } 248 if (i > 0) { 249 sb.append(','); 250 } 251 sb.append(displayPort); 252 sb.append(':'); 253 if (info.isDriverDisplay()) { 254 // for driver display 255 info.setMode(DISPLAY_POWER_MODE_ALWAYS_ON); 256 sb.append(DISPLAY_POWER_MODE_ALWAYS_ON); 257 } else { 258 // TODO(b/274050716): Restore passenger displays to ON. 259 // for passenger display 260 info.setMode(DISPLAY_POWER_MODE_ALWAYS_ON); 261 sb.append(DISPLAY_POWER_MODE_ALWAYS_ON); 262 } 263 } 264 } 265 Settings.Global.putString( 266 mContext.getContentResolver(), DISPLAY_POWER_MODE_SETTING, sb.toString()); 267 } 268 269 @GuardedBy("mLock") updateSettingsLocked()270 private void updateSettingsLocked() { 271 String setting = Settings.Global.getString(mContext.getContentResolver(), 272 DISPLAY_POWER_MODE_SETTING); 273 SparseIntArray mapping = parseModeAssignmentSettingValue(setting); 274 if (mapping == null) { 275 Slogf.d(TAG, "Failed to parse [%s]", setting); 276 initializeDefaultSettings(); 277 return; 278 } 279 for (int i = 0; i < mapping.size(); i++) { 280 int displayId = mapping.keyAt(i); 281 @DisplayPowerMode int mode = mapping.valueAt(i); 282 DisplayPowerInfo info = mDisplayPowerInfos.get(displayId); 283 if (info != null) { 284 // Check if the mode in the corresponding display power info is the same as current 285 // setting value. 286 if (info.getMode() != mode) { 287 info.setMode(mode); 288 boolean on = mode != DISPLAY_POWER_MODE_OFF; 289 // Update last user activity time due to mode change by driver 290 info.setLastUserActivityTime(mClock.uptimeMillis()); 291 mEventHandler.post(() -> { 292 handleSetDisplayState(displayId, on); 293 }); 294 } 295 } else { 296 Slogf.d(TAG, "No matching DisplayPowerInfo(display=%d)", displayId); 297 } 298 } 299 } 300 301 @GuardedBy("mLock") updateAllDisplayPowerStateLocked()302 private void updateAllDisplayPowerStateLocked() { 303 for (int i = 0; i < mDisplayPowerInfos.size(); i++) { 304 updateDisplayPowerStateLocked(mDisplayPowerInfos.valueAt(i)); 305 } 306 } 307 308 @GuardedBy("mLock") updateDisplayPowerStateLocked(DisplayPowerInfo info)309 private void updateDisplayPowerStateLocked(DisplayPowerInfo info) { 310 int displayId = info.getDisplayId(); 311 mEventHandler.cancelUserActivityTimeout(displayId); 312 313 if (!mBootCompleted 314 || info == null 315 || info.isDriverDisplay() 316 || info.getUserId() != CarOccupantZoneManager.INVALID_USER_ID 317 || info.getMode() == DISPLAY_POWER_MODE_ALWAYS_ON 318 || !mSystemInterface.isDisplayEnabled(displayId)) { 319 return; 320 } 321 322 checkUserActivityTimeout(info); 323 } 324 checkUserActivityTimeout(DisplayPowerInfo info)325 private void checkUserActivityTimeout(DisplayPowerInfo info) { 326 long now = mClock.uptimeMillis(); 327 long nextTimeout = info.getLastUserActivityTime() + mNoUserScreenOffTimeoutMs; 328 if (now < nextTimeout) { 329 mEventHandler.handleUserActivityTimeout(info.getDisplayId(), nextTimeout); 330 } 331 } 332 handleSetDisplayState(int displayId, boolean on)333 private void handleSetDisplayState(int displayId, boolean on) { 334 if (on != mSystemInterface.isDisplayEnabled(displayId)) { 335 mSystemInterface.setDisplayState(displayId, on); 336 } 337 } 338 339 private final ICarOccupantZoneCallback mOccupantZoneCallback = 340 new ICarOccupantZoneCallback.Stub() { 341 @Override 342 public void onOccupantZoneConfigChanged(int flags) { 343 if ((flags & (CarOccupantZoneManager.ZONE_CONFIG_CHANGE_FLAG_DISPLAY 344 | CarOccupantZoneManager.ZONE_CONFIG_CHANGE_FLAG_USER)) != 0) { 345 synchronized (mLock) { 346 handleOccupantZoneConfigChangeLocked(flags); 347 updateAllDisplayPowerStateLocked(); 348 } 349 } 350 } 351 }; 352 353 private final class SettingsObserver extends ContentObserver { SettingsObserver(Handler handler)354 SettingsObserver(Handler handler) { 355 super(handler); 356 } 357 358 @Override onChange(boolean selfChange, Uri uri)359 public void onChange(boolean selfChange, Uri uri) { 360 synchronized (mLock) { 361 handleSettingsChangedLocked(); 362 } 363 } 364 } 365 366 /** 367 * Updates display power info if user occupancy is changed or if display is added or removed. 368 */ 369 @GuardedBy("mLock") handleOccupantZoneConfigChangeLocked(int flags)370 private void handleOccupantZoneConfigChangeLocked(int flags) { 371 List<OccupantZoneInfo> occupantZoneInfos = mOccupantZoneService.getAllOccupantZones(); 372 for (int i = 0; i < occupantZoneInfos.size(); i++) { 373 OccupantZoneInfo zoneInfo = occupantZoneInfos.get(i); 374 int zoneId = zoneInfo.zoneId; 375 int displayId = getMainTypeDisplayId(zoneId); 376 if (displayId == Display.INVALID_DISPLAY) { 377 Slogf.w(TAG, "No main display associated with occupant zone(id: %d)", zoneId); 378 continue; 379 } 380 DisplayPowerInfo info = mDisplayPowerInfos.get(displayId); 381 if ((flags & CarOccupantZoneManager.ZONE_CONFIG_CHANGE_FLAG_USER) != 0 382 && info != null) { 383 int userId = mOccupantZoneService.getUserForOccupant(zoneId); 384 if (info.getUserId() != userId) { 385 if (userId == CarOccupantZoneManager.INVALID_USER_ID) { 386 // User logged out 387 info.setUserId(CarOccupantZoneManager.INVALID_USER_ID); 388 info.setLastUserActivityTime(mClock.uptimeMillis()); 389 } else { 390 // User logged in 391 info.setUserId(userId); 392 } 393 } 394 } 395 if ((flags & CarOccupantZoneManager.ZONE_CONFIG_CHANGE_FLAG_DISPLAY) != 0 396 && info == null) { 397 info = createDisplayPowerInfoLocked(displayId); 398 if (info != null) { 399 // Display added 400 int userId = mOccupantZoneService.getUserForOccupant(zoneId); 401 info.setUserId(userId); 402 } 403 } 404 } 405 if ((flags & CarOccupantZoneManager.ZONE_CONFIG_CHANGE_FLAG_DISPLAY) != 0) { 406 for (int i = 0; i < mDisplayPowerInfos.size(); i++) { 407 DisplayPowerInfo info = mDisplayPowerInfos.valueAt(i); 408 if (info != null 409 && mOccupantZoneService.getDisplayType(info.getDisplayId()) 410 == CarOccupantZoneManager.DISPLAY_TYPE_UNKNOWN) { 411 // Display removed 412 mDisplayPowerInfos.removeAt(i); 413 } 414 } 415 } 416 } 417 initializeDisplayPowerInfos()418 private void initializeDisplayPowerInfos() { 419 List<OccupantZoneInfo> occupantZoneInfos = mOccupantZoneService.getAllOccupantZones(); 420 synchronized (mLock) { 421 for (int i = 0; i < occupantZoneInfos.size(); i++) { 422 OccupantZoneInfo zoneInfo = occupantZoneInfos.get(i); 423 int zoneId = zoneInfo.zoneId; 424 int displayId = getMainTypeDisplayId(zoneId); 425 if (displayId == Display.INVALID_DISPLAY) { 426 continue; 427 } 428 DisplayPowerInfo info = createDisplayPowerInfoLocked(displayId); 429 int userId = mOccupantZoneService.getUserForOccupant(zoneId); 430 info.setUserId(userId); 431 if (zoneInfo.occupantType == CarOccupantZoneManager.OCCUPANT_TYPE_DRIVER) { 432 info.setDriverDisplay(true); 433 } 434 } 435 } 436 } 437 438 @GuardedBy("mLock") createDisplayPowerInfoLocked(int displayId)439 private DisplayPowerInfo createDisplayPowerInfoLocked(int displayId) { 440 DisplayPowerInfo info = new DisplayPowerInfo(displayId); 441 mDisplayPowerInfos.put(displayId, info); 442 return info; 443 } 444 getMainTypeDisplayId(int zoneId)445 private int getMainTypeDisplayId(int zoneId) { 446 return mOccupantZoneService.getDisplayForOccupant(zoneId, 447 CarOccupantZoneManager.DISPLAY_TYPE_MAIN); 448 } 449 450 // value format: comma-separated displayPort:mode 451 @VisibleForTesting parseModeAssignmentSettingValue(String value)452 SparseIntArray parseModeAssignmentSettingValue(String value) { 453 SparseIntArray mapping = new SparseIntArray(); 454 try { 455 String[] entries = value.split(","); 456 for (int i = 0; i < entries.length; i++) { 457 String entry = entries[i]; 458 String[] pair = entry.split(":"); 459 if (pair.length != 2) { 460 return null; 461 } 462 int displayPort = Integer.parseInt(pair[0], /* radix= */ 10); 463 int displayId = getDisplayId(displayPort); 464 if (displayId == Display.INVALID_DISPLAY) { 465 Slogf.w(TAG, "Invalid display port: %d", displayPort); 466 return null; 467 } 468 @DisplayPowerMode int mode = Integer.parseInt(pair[1], /* radix= */ 10); 469 if (mapping.indexOfKey(displayId) >= 0) { 470 Slogf.w(TAG, "Multiple use of display id: %d", displayId); 471 return null; 472 } 473 if (mode < DISPLAY_POWER_MODE_OFF || mode > DISPLAY_POWER_MODE_ALWAYS_ON) { 474 Slogf.w(TAG, "Mode is out of range: %d(%s)", 475 mode, DisplayPowerInfo.displayPowerModeToString(mode)); 476 return null; 477 } 478 mapping.append(displayId, mode); 479 } 480 } catch (Exception e) { 481 Slogf.w(TAG, e, "Setting %s has invalid value: ", value); 482 // Parsing error, ignore all. 483 return null; 484 } 485 return mapping; 486 } 487 getDisplayId(int displayPort)488 private int getDisplayId(int displayPort) { 489 DisplayManager displayManager = mContext.getSystemService(DisplayManager.class); 490 for (Display display : displayManager.getDisplays()) { 491 if (DisplayHelper.getPhysicalPort(display) == displayPort) { 492 return display.getDisplayId(); 493 } 494 } 495 return Display.INVALID_DISPLAY; 496 } 497 getDisplayPort(int displayId)498 private int getDisplayPort(int displayId) { 499 DisplayManager displayManager = mContext.getSystemService(DisplayManager.class); 500 Display display = displayManager.getDisplay(displayId); 501 if (display != null) { 502 return DisplayHelper.getPhysicalPort(display); 503 } 504 return DisplayHelper.INVALID_PORT; 505 } 506 507 private static final class EventHandler extends Handler { 508 private static final int MSG_USER_ACTIVITY_TIMEOUT = 0; 509 510 private final WeakReference<ScreenOffHandler> mScreenOffHandler; 511 EventHandler(Looper looper, ScreenOffHandler screenOffHandler)512 private EventHandler(Looper looper, ScreenOffHandler screenOffHandler) { 513 super(looper); 514 mScreenOffHandler = new WeakReference<ScreenOffHandler>(screenOffHandler); 515 } 516 handleUserActivityTimeout(int displayId, long timeMs)517 private void handleUserActivityTimeout(int displayId, long timeMs) { 518 Message msg = obtainMessage(MSG_USER_ACTIVITY_TIMEOUT, displayId); 519 msg.setAsynchronous(true); 520 sendMessageAtTime(msg, timeMs); 521 } 522 cancelUserActivityTimeout(int displayId)523 private void cancelUserActivityTimeout(int displayId) { 524 removeMessages(MSG_USER_ACTIVITY_TIMEOUT, displayId); 525 } 526 527 @Override handleMessage(Message msg)528 public void handleMessage(Message msg) { 529 ScreenOffHandler screenOffHandler = mScreenOffHandler.get(); 530 if (screenOffHandler == null) { 531 return; 532 } 533 switch (msg.what) { 534 case MSG_USER_ACTIVITY_TIMEOUT: 535 screenOffHandler.handleSetDisplayState(/* displayId= */ (Integer) msg.obj, 536 /* on= */ false); 537 break; 538 default: 539 Slogf.w(TAG, "Invalid message type: %d", msg.what); 540 break; 541 } 542 } 543 } 544 getNoUserScreenOffTimeout()545 private int getNoUserScreenOffTimeout() { 546 int timeout = mContext.getResources().getInteger(R.integer.config_noUserScreenOffTimeout); 547 if (timeout < MIN_NO_USER_SCREEN_OFF_TIMEOUT_MS) { 548 Slogf.w(TAG, "config_noUserScreenOffTimeout(%dms) is shorter than %dms and is reset to " 549 + "%dms", timeout, MIN_NO_USER_SCREEN_OFF_TIMEOUT_MS, 550 MIN_NO_USER_SCREEN_OFF_TIMEOUT_MS); 551 timeout = MIN_NO_USER_SCREEN_OFF_TIMEOUT_MS; 552 } else if (timeout > MAX_NO_USER_SCREEN_OFF_TIMEOUT_MS) { 553 Slogf.w(TAG, "config_noUserScreenOffTimeout(%dms) is longer than %dms and is reset to " 554 + "%dms", timeout, MAX_NO_USER_SCREEN_OFF_TIMEOUT_MS, 555 MAX_NO_USER_SCREEN_OFF_TIMEOUT_MS); 556 timeout = MAX_NO_USER_SCREEN_OFF_TIMEOUT_MS; 557 } 558 return timeout; 559 } 560 561 private static final class DisplayPowerInfo { 562 private final int mDisplayId; 563 564 private int mUserId; 565 private @DisplayPowerMode int mMode; 566 private boolean mIsDriverDisplay; 567 private long mLastUserActivityTime; 568 DisplayPowerInfo(int displayId)569 private DisplayPowerInfo(int displayId) { 570 mDisplayId = displayId; 571 mUserId = CarOccupantZoneManager.INVALID_USER_ID; 572 mMode = DISPLAY_POWER_MODE_NONE; 573 mIsDriverDisplay = false; 574 mLastUserActivityTime = -1; 575 } 576 getDisplayId()577 private int getDisplayId() { 578 return mDisplayId; 579 } 580 setUserId(int userId)581 private void setUserId(int userId) { 582 mUserId = userId; 583 } 584 getUserId()585 private int getUserId() { 586 return mUserId; 587 } 588 setMode(@isplayPowerMode int mode)589 private void setMode(@DisplayPowerMode int mode) { 590 mMode = mode; 591 } 592 getMode()593 private @DisplayPowerMode int getMode() { 594 return mMode; 595 } 596 setDriverDisplay(boolean isDriver)597 private void setDriverDisplay(boolean isDriver) { 598 mIsDriverDisplay = isDriver; 599 } 600 isDriverDisplay()601 private boolean isDriverDisplay() { 602 return mIsDriverDisplay; 603 } 604 getLastUserActivityTime()605 private long getLastUserActivityTime() { 606 return mLastUserActivityTime; 607 } 608 setLastUserActivityTime(long lastUserActivityTime)609 private void setLastUserActivityTime(long lastUserActivityTime) { 610 mLastUserActivityTime = lastUserActivityTime; 611 } 612 613 @Override 614 @ExcludeFromCodeCoverageGeneratedReport(reason = DEBUGGING_CODE) toString()615 public String toString() { 616 StringBuilder b = new StringBuilder(64); 617 b.append(" DisplayPowerInfo{mDisplayId="); 618 b.append(mDisplayId); 619 b.append(" mUserId="); 620 b.append(mUserId); 621 b.append(" mMode="); 622 b.append(displayPowerModeToString(mMode)); 623 b.append(" mIsDriverDisplay="); 624 b.append(mIsDriverDisplay); 625 b.append(" mLastUserActivityTime="); 626 b.append(mLastUserActivityTime); 627 b.append("}"); 628 return b.toString(); 629 } 630 631 @ExcludeFromCodeCoverageGeneratedReport(reason = DEBUGGING_CODE) displayPowerModeToString(@isplayPowerMode int mode)632 private static String displayPowerModeToString(@DisplayPowerMode int mode) { 633 switch (mode) { 634 case DISPLAY_POWER_MODE_NONE: 635 return "NONE"; 636 case DISPLAY_POWER_MODE_ON: 637 return "ON"; 638 case DISPLAY_POWER_MODE_OFF: 639 return "OFF"; 640 case DISPLAY_POWER_MODE_ALWAYS_ON: 641 return "ALWAYS_ON"; 642 default: 643 return "UNKNOWN"; 644 } 645 } 646 } 647 648 @ExcludeFromCodeCoverageGeneratedReport(reason = DUMP_INFO) dump(IndentingPrintWriter writer)649 void dump(IndentingPrintWriter writer) { 650 synchronized (mLock) { 651 writer.println("ScreenOffHandler"); 652 writer.increaseIndent(); 653 writer.println("mIsAutoPowerSaving=" + mIsAutoPowerSaving); 654 writer.println("mBootCompleted=" + mBootCompleted); 655 writer.println("mNoUserScreenOffTimeoutMs=" + mNoUserScreenOffTimeoutMs); 656 writer.decreaseIndent(); 657 for (int i = 0; i < mDisplayPowerInfos.size(); i++) { 658 writer.println(mDisplayPowerInfos.valueAt(i)); 659 } 660 } 661 } 662 663 /** Functional interface for providing time. */ 664 @VisibleForTesting 665 interface ClockInterface { 666 /** 667 * Returns current time in milliseconds since boot, not counting time spent in deep sleep. 668 */ uptimeMillis()669 long uptimeMillis(); 670 } 671 } 672