• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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