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